Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84e82321b8 | ||
|
|
5e3cfbe8b4 | ||
|
|
7de333e1ea | ||
|
|
2123afc4b0 | ||
|
|
ea91fa1723 | ||
|
|
4dcdec7a35 | ||
|
|
196f1914a3 | ||
|
|
ea4c7a4a37 |
@@ -2761,7 +2761,7 @@
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "86666.297",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "68975.3330121062666493861",
|
||||
"locked_amount": "68916.5304132697229014062",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "86666.297",
|
||||
@@ -2827,7 +2827,7 @@
|
||||
"tranche_end": "2023-06-01T00:00:00.000Z",
|
||||
"total_added": "2500",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "1421.61410637973125",
|
||||
"locked_amount": "1418.21231430606425",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "2500",
|
||||
@@ -3215,7 +3215,7 @@
|
||||
"tranche_end": "2023-08-01T00:00:00.000Z",
|
||||
"total_added": "37500",
|
||||
"total_removed": "2836.415649675",
|
||||
"locked_amount": "34080.14646639042375",
|
||||
"locked_amount": "34028.837668815225",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "7500",
|
||||
@@ -3326,7 +3326,7 @@
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "129999.45",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "68912.401699541887736445",
|
||||
"locked_amount": "68853.652750677974505795",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "129999.45",
|
||||
@@ -3392,7 +3392,7 @@
|
||||
"tranche_end": "2023-09-03T00:00:00.000Z",
|
||||
"total_added": "62600",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "33871.4891362252656",
|
||||
"locked_amount": "33829.0153855910767",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "10000",
|
||||
@@ -3585,7 +3585,7 @@
|
||||
"tranche_end": "2023-09-17T00:00:00.000Z",
|
||||
"total_added": "5000",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "2897.1713280060885",
|
||||
"locked_amount": "2893.778855910705",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "5000",
|
||||
@@ -3796,7 +3796,7 @@
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "97499.58",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "10818.8482095714036717828",
|
||||
"locked_amount": "10761.2209634480270840046",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "97499.58",
|
||||
@@ -3829,7 +3829,7 @@
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "135173.4239508",
|
||||
"total_removed": "98230.390980249184455396",
|
||||
"locked_amount": "14787.49770626082669661645614",
|
||||
"locked_amount": "14708.73120973917718086298914",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "135173.4239508",
|
||||
@@ -3875,7 +3875,7 @@
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "32499.86",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "4551.302610251424035468",
|
||||
"locked_amount": "4527.0598229766283023892",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "32499.86",
|
||||
@@ -3908,7 +3908,7 @@
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "10833.29",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "1481.4048348417078526675",
|
||||
"locked_amount": "1473.514042830201860202",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "10833.29",
|
||||
@@ -3941,7 +3941,7 @@
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "22749.93",
|
||||
"total_removed": "4720.860935375",
|
||||
"locked_amount": "5537.8221384325917571011",
|
||||
"locked_amount": "5508.324595517016566601",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "6500",
|
||||
@@ -4093,7 +4093,7 @@
|
||||
"tranche_end": "2023-05-01T00:00:00.000Z",
|
||||
"total_added": "22500",
|
||||
"total_removed": "4373.451408",
|
||||
"locked_amount": "9011.6237914364655",
|
||||
"locked_amount": "8980.8385128913431",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "7500",
|
||||
@@ -4324,7 +4324,7 @@
|
||||
"tranche_end": "2023-06-02T00:00:00.000Z",
|
||||
"total_added": "1939928.38",
|
||||
"total_removed": "928642.9598472029154",
|
||||
"locked_amount": "555369.642869191387312504",
|
||||
"locked_amount": "554053.412289953077578596",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1852091.69",
|
||||
@@ -36616,7 +36616,7 @@
|
||||
"tranche_end": "2023-06-05T00:00:00.000Z",
|
||||
"total_added": "3732368.4671",
|
||||
"total_removed": "592998.0503546212334",
|
||||
"locked_amount": "877911.01211975962832293262",
|
||||
"locked_amount": "875888.518509001519437488391",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1998.95815",
|
||||
@@ -37942,7 +37942,7 @@
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "15870102.715470999700000001",
|
||||
"total_removed": "570447.40018226362274952",
|
||||
"locked_amount": "8412703.667343023478982687330398519288997",
|
||||
"locked_amount": "8405532.0348557766289364422878397935063531",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "16249.93",
|
||||
@@ -43552,7 +43552,7 @@
|
||||
"tranche_end": "2023-05-05T00:00:00.000Z",
|
||||
"total_added": "14597706.0446472999",
|
||||
"total_removed": "3926967.723059737571037032",
|
||||
"locked_amount": "2045109.08378068825804623195557898",
|
||||
"locked_amount": "2038488.278459767293130894768769085",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "129284.449",
|
||||
@@ -50465,7 +50465,7 @@
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "5778205.3912159303",
|
||||
"total_removed": "2749067.463242913023296295",
|
||||
"locked_amount": "491131.564178330845541202121121822",
|
||||
"locked_amount": "488515.643953737443539557205122125",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "552496.6455",
|
||||
@@ -52491,8 +52491,8 @@
|
||||
"tranche_start": "2022-06-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-06-05T00:00:00.000Z",
|
||||
"total_added": "472355.6199999996",
|
||||
"total_removed": "33662.0369387672685",
|
||||
"locked_amount": "139110.018223666781876256383155776",
|
||||
"total_removed": "33803.2676021352685",
|
||||
"locked_amount": "138789.542549975765711077066869624",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "3000",
|
||||
@@ -59181,6 +59181,11 @@
|
||||
"user": "0xD3ec605d078326B0a636ca90d496Ebb5Eb457a27",
|
||||
"tx": "0xa82e451753cbced76b064af62868d3170dff7afca557680e657c27ebba5c7beb"
|
||||
},
|
||||
{
|
||||
"amount": "141.230663368",
|
||||
"user": "0xb7df334217A208B5B35a3824D1CDdBCa85BD352B",
|
||||
"tx": "0x41e9fa9b5003c1d6edf1e9423bd6ae7ce6e4e48fcbce0b47ba17968a67da4d48"
|
||||
},
|
||||
{
|
||||
"amount": "13.1116203702",
|
||||
"user": "0x4cBC0C88d8FE503f62823B42f30a4900292C13bE",
|
||||
@@ -63796,10 +63801,17 @@
|
||||
"tx": "0x1c2c084e3efafb2ffa09c0b259bd229cce0d6da04dd092d911c7e6136a7ac8a9"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "141.230663368",
|
||||
"user": "0xb7df334217A208B5B35a3824D1CDdBCa85BD352B",
|
||||
"tranche_id": 5,
|
||||
"tx": "0x41e9fa9b5003c1d6edf1e9423bd6ae7ce6e4e48fcbce0b47ba17968a67da4d48"
|
||||
}
|
||||
],
|
||||
"total_tokens": "200",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "200"
|
||||
"withdrawn_tokens": "141.230663368",
|
||||
"remaining_tokens": "58.769336632"
|
||||
},
|
||||
{
|
||||
"address": "0x287a34EB03418ad55c2e93d0E4FB1e18DCE607ED",
|
||||
|
||||
@@ -63,6 +63,39 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
||||
.and('have.text', 'Browse, vote, and propose');
|
||||
});
|
||||
});
|
||||
it('should show open or enacted proposals with proposal summary', function () {
|
||||
cy.get('body').then(($body) => {
|
||||
if (!$body.find('[data-testid="proposals-list-item"]').length) {
|
||||
cy.createMarket();
|
||||
cy.reload();
|
||||
cy.wait_for_spinner();
|
||||
}
|
||||
});
|
||||
cy.getByTestId('proposals-list-item')
|
||||
.should('have.length.at.least', 1)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.getByTestId('proposal-title')
|
||||
.invoke('text')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId('proposal-type')
|
||||
.invoke('text')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId('proposal-description')
|
||||
.invoke('text')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId('proposal-details')
|
||||
.invoke('text')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId('proposal-status')
|
||||
.invoke('text')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId('vote-details')
|
||||
.invoke('text')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId('view-proposal-btn').should('be.visible');
|
||||
});
|
||||
});
|
||||
it('should have external link for governance', function () {
|
||||
cy.getByTestId('home-proposals').within(() => {
|
||||
cy.getByTestId('external-link')
|
||||
|
||||
+2
-2
@@ -30,11 +30,11 @@ context('Staking Page - verify elements on page', function () {
|
||||
|
||||
describe('with wallets disconnected', { tags: '@smoke' }, function () {
|
||||
describe('description section', function () {
|
||||
it('Should have staking tab highlighted', function () {
|
||||
it('Should have validators tab highlighted', function () {
|
||||
cy.verify_tab_highlighted('validators');
|
||||
});
|
||||
|
||||
it('Should have STAKING ON VEGA header visible', function () {
|
||||
it('Should have validators ON VEGA header visible', function () {
|
||||
cy.verify_page_header('Validators');
|
||||
});
|
||||
|
||||
@@ -338,8 +338,8 @@ context(
|
||||
.contains(currency.id)
|
||||
.parent()
|
||||
.siblings()
|
||||
.within(() => cy.contains_exactly('10.00').should('be.visible'));
|
||||
|
||||
.invoke('text')
|
||||
.should('not.be.empty');
|
||||
cy.get(vegaWalletCurrencyTitle)
|
||||
.contains(currency.id)
|
||||
.parent()
|
||||
|
||||
@@ -18,7 +18,7 @@ export const NavDropDown = ({ navbarTheme }: { navbarTheme: NavbarTheme }) => {
|
||||
<AppNavLink
|
||||
name={
|
||||
<NavDropdownMenuTrigger
|
||||
className="w-auto flex items-center"
|
||||
className="w-auto flex items-center -m-3 p-3 cursor-pointer"
|
||||
data-testid="state-trigger"
|
||||
onClick={() => setOpen(!isOpen)}
|
||||
>
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { NetworkSwitcher } from '@vegaprotocol/environment';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { TOP_LEVEL_ROUTES } from '../../routes/routes';
|
||||
import { TOP_LEVEL_ROUTES, TOKEN_DROPDOWN_ROUTES } from '../../routes/routes';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import logoWhiteText from '../../images/logo-white-text.png';
|
||||
import logoBlackText from '../../images/logo-black-text.png';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { NavDrawer } from './nav-draw';
|
||||
import { Nav as ToolkitNav } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
Nav as ToolkitNav,
|
||||
Navbar,
|
||||
NavigationContent,
|
||||
NavigationItem,
|
||||
NavigationLink,
|
||||
NavigationList,
|
||||
NavigationTrigger,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { AppNavLink } from './nav-link';
|
||||
import { NavDropDown } from './nav-dropdown';
|
||||
|
||||
@@ -41,7 +49,28 @@ export const Nav = ({ navbarTheme = 'inherit' }: NavbarProps) => {
|
||||
|
||||
const { t } = useTranslation();
|
||||
const isYellow = navbarTheme === 'yellow';
|
||||
|
||||
return (
|
||||
<Navbar>
|
||||
<NavigationList>
|
||||
{TOP_LEVEL_ROUTES.map((r) => (
|
||||
<NavigationItem key={r.path}>
|
||||
<NavigationLink path={r.path}>{t(r.name)}</NavigationLink>
|
||||
</NavigationItem>
|
||||
))}
|
||||
<NavigationItem>
|
||||
<NavigationTrigger>{t('Token')}</NavigationTrigger>
|
||||
<NavigationContent backText={t('Token')}>
|
||||
{TOKEN_DROPDOWN_ROUTES.map((r) => (
|
||||
<NavigationItem>
|
||||
<NavigationLink path={r.path}>{r.name}</NavigationLink>
|
||||
</NavigationItem>
|
||||
))}
|
||||
</NavigationContent>
|
||||
</NavigationItem>
|
||||
</NavigationList>
|
||||
</Navbar>
|
||||
);
|
||||
/*
|
||||
return (
|
||||
<ToolkitNav
|
||||
navbarTheme={navbarTheme}
|
||||
@@ -78,4 +107,5 @@ export const Nav = ({ navbarTheme = 'inherit' }: NavbarProps) => {
|
||||
)}
|
||||
</ToolkitNav>
|
||||
);
|
||||
*/
|
||||
};
|
||||
|
||||
@@ -35,7 +35,7 @@ export const TOP_LEVEL_ROUTES = [
|
||||
|
||||
export const TOKEN_DROPDOWN_ROUTES = [
|
||||
{
|
||||
name: 'Token',
|
||||
name: 'Information',
|
||||
path: Routes.TOKEN,
|
||||
},
|
||||
{
|
||||
|
||||
+111
-89
@@ -50,101 +50,117 @@ export const StandbyPendingValidatorsTable = ({
|
||||
const nodes = useMemo(() => {
|
||||
if (!data) return [];
|
||||
|
||||
return data.map(
|
||||
({
|
||||
id,
|
||||
name,
|
||||
avatarUrl,
|
||||
stakedByDelegates,
|
||||
stakedByOperator,
|
||||
stakedTotal,
|
||||
rankingScore: { stakeScore },
|
||||
pendingStake,
|
||||
}) => {
|
||||
const { rawValidatorScore, performanceScore } =
|
||||
getLastEpochScoreAndPerformance(previousEpochData, id);
|
||||
|
||||
const overstakedAmount = getOverstakedAmount(
|
||||
rawValidatorScore,
|
||||
stakedTotal,
|
||||
totalStake
|
||||
);
|
||||
let individualStakeNeededForPromotion,
|
||||
individualStakeNeededForPromotionDescription;
|
||||
|
||||
if (stakeNeededForPromotion && performanceScore) {
|
||||
const stakedTotalBigNum = new BigNumber(stakedTotal);
|
||||
const stakeNeededBigNum = new BigNumber(stakeNeededForPromotion);
|
||||
const performanceScoreBigNum = new BigNumber(performanceScore);
|
||||
|
||||
const calc = stakeNeededBigNum
|
||||
.dividedBy(performanceScoreBigNum)
|
||||
.minus(stakedTotalBigNum);
|
||||
|
||||
if (calc.isGreaterThan(0)) {
|
||||
individualStakeNeededForPromotion = calc.toString();
|
||||
individualStakeNeededForPromotionDescription = t(
|
||||
stakeNeededForPromotionDescription,
|
||||
{
|
||||
prefix: formatNumber(calc, 2).toString(),
|
||||
}
|
||||
);
|
||||
} else {
|
||||
individualStakeNeededForPromotion = '0';
|
||||
individualStakeNeededForPromotionDescription = t(
|
||||
stakeNeededForPromotionDescription,
|
||||
{
|
||||
prefix: formatNumber(0, 2).toString(),
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
return data
|
||||
.sort((a, b) => {
|
||||
const aVotingPower = new BigNumber(a.rankingScore.votingPower);
|
||||
const bVotingPower = new BigNumber(b.rankingScore.votingPower);
|
||||
return bVotingPower.minus(aVotingPower).toNumber();
|
||||
})
|
||||
.map((node, index) => {
|
||||
const votingPowerRanking = index + 1;
|
||||
|
||||
return {
|
||||
...node,
|
||||
votingPowerRanking,
|
||||
};
|
||||
})
|
||||
.map(
|
||||
({
|
||||
id,
|
||||
[ValidatorFields.VALIDATOR]: {
|
||||
avatarUrl,
|
||||
name,
|
||||
},
|
||||
[ValidatorFields.STAKE]: formatNumber(
|
||||
toBigNum(stakedTotal, decimals),
|
||||
2
|
||||
),
|
||||
[ValidatorFields.STAKE_NEEDED_FOR_PROMOTION]:
|
||||
individualStakeNeededForPromotion || null,
|
||||
[ValidatorFields.STAKE_NEEDED_FOR_PROMOTION_DESCRIPTION]:
|
||||
individualStakeNeededForPromotionDescription || t('n/a'),
|
||||
[ValidatorFields.STAKE_SHARE]: stakedTotalPercentage(stakeScore),
|
||||
[ValidatorFields.STAKED_BY_DELEGATES]: formatNumber(
|
||||
toBigNum(stakedByDelegates, decimals),
|
||||
2
|
||||
),
|
||||
[ValidatorFields.STAKED_BY_OPERATOR]: formatNumber(
|
||||
toBigNum(stakedByOperator, decimals),
|
||||
2
|
||||
),
|
||||
[ValidatorFields.PERFORMANCE_SCORE]:
|
||||
getFormattedPerformanceScore(performanceScore).toString(),
|
||||
[ValidatorFields.PERFORMANCE_PENALTY]:
|
||||
getPerformancePenalty(performanceScore),
|
||||
[ValidatorFields.OVERSTAKED_AMOUNT]: overstakedAmount.toString(),
|
||||
[ValidatorFields.OVERSTAKING_PENALTY]: getOverstakingPenalty(
|
||||
overstakedAmount,
|
||||
totalStake
|
||||
),
|
||||
[ValidatorFields.TOTAL_PENALTIES]: getTotalPenalties(
|
||||
name,
|
||||
avatarUrl,
|
||||
stakedByDelegates,
|
||||
stakedByOperator,
|
||||
stakedTotal,
|
||||
rankingScore: { stakeScore },
|
||||
pendingStake,
|
||||
votingPowerRanking,
|
||||
}) => {
|
||||
const { rawValidatorScore, performanceScore } =
|
||||
getLastEpochScoreAndPerformance(previousEpochData, id);
|
||||
|
||||
const overstakedAmount = getOverstakedAmount(
|
||||
rawValidatorScore,
|
||||
performanceScore,
|
||||
stakedTotal,
|
||||
totalStake
|
||||
),
|
||||
[ValidatorFields.PENDING_STAKE]: formatNumber(
|
||||
toBigNum(pendingStake, decimals),
|
||||
2
|
||||
),
|
||||
};
|
||||
}
|
||||
);
|
||||
);
|
||||
let individualStakeNeededForPromotion,
|
||||
individualStakeNeededForPromotionDescription;
|
||||
|
||||
if (stakeNeededForPromotion && performanceScore) {
|
||||
const stakedTotalBigNum = new BigNumber(stakedTotal);
|
||||
const stakeNeededBigNum = new BigNumber(stakeNeededForPromotion);
|
||||
const performanceScoreBigNum = new BigNumber(performanceScore);
|
||||
|
||||
const calc = stakeNeededBigNum
|
||||
.dividedBy(performanceScoreBigNum)
|
||||
.minus(stakedTotalBigNum);
|
||||
|
||||
if (calc.isGreaterThan(0)) {
|
||||
individualStakeNeededForPromotion = calc.toString();
|
||||
individualStakeNeededForPromotionDescription = t(
|
||||
stakeNeededForPromotionDescription,
|
||||
{
|
||||
prefix: formatNumber(calc, 2).toString(),
|
||||
}
|
||||
);
|
||||
} else {
|
||||
individualStakeNeededForPromotion = '0';
|
||||
individualStakeNeededForPromotionDescription = t(
|
||||
stakeNeededForPromotionDescription,
|
||||
{
|
||||
prefix: formatNumber(0, 2).toString(),
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
[ValidatorFields.RANKING_INDEX]: votingPowerRanking,
|
||||
[ValidatorFields.VALIDATOR]: {
|
||||
avatarUrl,
|
||||
name,
|
||||
},
|
||||
[ValidatorFields.STAKE]: formatNumber(
|
||||
toBigNum(stakedTotal, decimals),
|
||||
2
|
||||
),
|
||||
[ValidatorFields.STAKE_NEEDED_FOR_PROMOTION]:
|
||||
individualStakeNeededForPromotion || null,
|
||||
[ValidatorFields.STAKE_NEEDED_FOR_PROMOTION_DESCRIPTION]:
|
||||
individualStakeNeededForPromotionDescription || t('n/a'),
|
||||
[ValidatorFields.STAKE_SHARE]: stakedTotalPercentage(stakeScore),
|
||||
[ValidatorFields.STAKED_BY_DELEGATES]: formatNumber(
|
||||
toBigNum(stakedByDelegates, decimals),
|
||||
2
|
||||
),
|
||||
[ValidatorFields.STAKED_BY_OPERATOR]: formatNumber(
|
||||
toBigNum(stakedByOperator, decimals),
|
||||
2
|
||||
),
|
||||
[ValidatorFields.PERFORMANCE_SCORE]:
|
||||
getFormattedPerformanceScore(performanceScore).toString(),
|
||||
[ValidatorFields.PERFORMANCE_PENALTY]:
|
||||
getPerformancePenalty(performanceScore),
|
||||
[ValidatorFields.OVERSTAKED_AMOUNT]: overstakedAmount.toString(),
|
||||
[ValidatorFields.OVERSTAKING_PENALTY]: getOverstakingPenalty(
|
||||
overstakedAmount,
|
||||
totalStake
|
||||
),
|
||||
[ValidatorFields.TOTAL_PENALTIES]: getTotalPenalties(
|
||||
rawValidatorScore,
|
||||
performanceScore,
|
||||
stakedTotal,
|
||||
totalStake
|
||||
),
|
||||
[ValidatorFields.PENDING_STAKE]: formatNumber(
|
||||
toBigNum(pendingStake, decimals),
|
||||
2
|
||||
),
|
||||
};
|
||||
}
|
||||
);
|
||||
}, [
|
||||
data,
|
||||
decimals,
|
||||
@@ -158,6 +174,12 @@ export const StandbyPendingValidatorsTable = ({
|
||||
const StandbyPendingTable = forwardRef<AgGridReact>((_, gridRef) => {
|
||||
const colDefs = useMemo<ColDef[]>(
|
||||
() => [
|
||||
{
|
||||
field: ValidatorFields.RANKING_INDEX,
|
||||
headerName: '#',
|
||||
width: 60,
|
||||
pinned: 'left',
|
||||
},
|
||||
{
|
||||
field: ValidatorFields.VALIDATOR,
|
||||
headerName: t(ValidatorFields.VALIDATOR).toString(),
|
||||
|
||||
@@ -59,11 +59,17 @@ export const getOverstakedAmount = (
|
||||
export const getOverstakingPenalty = (
|
||||
overstakedAmount: BigNumber,
|
||||
stakedOnNode: string
|
||||
) =>
|
||||
formatNumberPercentage(
|
||||
) => {
|
||||
// avoid division by zero
|
||||
if (new BigNumber(stakedOnNode).isZero() || overstakedAmount.isZero()) {
|
||||
return '0';
|
||||
}
|
||||
|
||||
return formatNumberPercentage(
|
||||
overstakedAmount.dividedBy(new BigNumber(stakedOnNode)).times(100),
|
||||
2
|
||||
);
|
||||
};
|
||||
|
||||
export const getTotalPenalties = (
|
||||
rawValidatorScore: string | null | undefined,
|
||||
|
||||
@@ -3,6 +3,8 @@ import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import { marketsQuery } from '@vegaprotocol/mock';
|
||||
import { getDateTimeFormat } from '@vegaprotocol/react-helpers';
|
||||
|
||||
const dialogCloseBtn = 'dialog-close';
|
||||
|
||||
describe('markets table', { tags: '@smoke' }, () => {
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage().then(() => {
|
||||
@@ -147,7 +149,7 @@ describe('markets table', { tags: '@smoke' }, () => {
|
||||
});
|
||||
cy.visit('#/markets/market-0');
|
||||
cy.url().should('contain', 'market-0');
|
||||
cy.getByTestId('dialog-close').click();
|
||||
cy.getByTestId(dialogCloseBtn).click();
|
||||
cy.getByTestId('item-value').contains('Opening auction').realHover();
|
||||
cy.getByTestId('opening-auction-sub-status').should(
|
||||
'contain.text',
|
||||
@@ -171,7 +173,8 @@ describe('markets table', { tags: '@smoke' }, () => {
|
||||
});
|
||||
|
||||
function openMarketDropDown() {
|
||||
cy.getByTestId('dialog-close').click();
|
||||
cy.getByTestId(dialogCloseBtn).should('be.visible');
|
||||
cy.getByTestId(dialogCloseBtn).click();
|
||||
cy.getByTestId('popover-trigger').click();
|
||||
cy.contains('Loading market data...').should('not.exist');
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
export * from './navbar';
|
||||
export * from './navbar2';
|
||||
|
||||
@@ -185,7 +185,7 @@ interface AppNavLinkProps {
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
const AppNavLink = ({
|
||||
export const AppNavLink = ({
|
||||
name,
|
||||
path,
|
||||
navbarTheme,
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import { Vega } from '../icons/vega';
|
||||
import { create } from 'zustand';
|
||||
import * as NavigationMenu from '@radix-ui/react-navigation-menu';
|
||||
import type { ReactNode } from 'react';
|
||||
import * as Dialog from '@radix-ui/react-dialog';
|
||||
import { Icon, ThemeSwitcher } from '@vegaprotocol/ui-toolkit';
|
||||
import { NetworkSwitcher } from '@vegaprotocol/environment';
|
||||
import classNames from 'classnames';
|
||||
import { VegaWalletConnectButton } from '../vega-wallet-connect-button';
|
||||
import { animated, useTransition, useSpring, config } from 'react-spring';
|
||||
|
||||
const BREAKPOINT = 760;
|
||||
const useNav = create<{
|
||||
drawerOpen: boolean;
|
||||
value: string;
|
||||
update: (state: Partial<{ drawerOpen: boolean; value: string }>) => void;
|
||||
}>((set) => ({
|
||||
drawerOpen: false,
|
||||
value: '',
|
||||
update: (state) => set(state),
|
||||
}));
|
||||
|
||||
export const Navbar = ({ children }: { children: ReactNode }) => {
|
||||
return (
|
||||
<div className="px-4 border-b border-vega-light-200 dark:border-vega-dark-200">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="py-3 mr-4">
|
||||
<Vega />
|
||||
</div>
|
||||
{window.innerWidth > BREAKPOINT ? (
|
||||
<Menu>{children}</Menu>
|
||||
) : (
|
||||
<Drawer>{children}</Drawer>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Menu = ({ children }: { children: ReactNode }) => {
|
||||
const { value, update } = useNav();
|
||||
return (
|
||||
<>
|
||||
<div className="mr-4">
|
||||
<NetworkSwitcher />
|
||||
</div>
|
||||
<NavigationMenu.Root
|
||||
value={value}
|
||||
onValueChange={(value) => update({ value })}
|
||||
className="relative self-end"
|
||||
>
|
||||
{children}
|
||||
</NavigationMenu.Root>
|
||||
<div className="flex items-center gap-2 ml-auto">
|
||||
<VegaWalletConnectButton />
|
||||
<ThemeSwitcher />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Drawer = ({ children }: { children: ReactNode }) => {
|
||||
const { drawerOpen, value, update } = useNav();
|
||||
const transition = useTransition(drawerOpen, {
|
||||
from: { opacity: 0, transform: 'translateX(100%)' },
|
||||
enter: { opacity: 1, transform: 'translateX(0%)' },
|
||||
leave: { opacity: 0, transform: 'translateX(100%)' },
|
||||
config: {
|
||||
...config.default,
|
||||
duration: 200,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="ml-auto">
|
||||
<MenuButton open={false} onClick={() => update({ drawerOpen: true })} />
|
||||
</div>
|
||||
<Dialog.Root
|
||||
open={drawerOpen}
|
||||
onOpenChange={(open) => update({ drawerOpen: open })}
|
||||
>
|
||||
<Dialog.Portal forceMount={true}>
|
||||
{transition((styles, isOpen) => {
|
||||
return isOpen ? (
|
||||
<>
|
||||
<Dialog.Overlay
|
||||
forceMount={true}
|
||||
asChild={true}
|
||||
className="fixed inset-0 bg-black/50"
|
||||
>
|
||||
<animated.div style={{ opacity: styles.opacity }} />
|
||||
</Dialog.Overlay>
|
||||
<Dialog.Content
|
||||
forceMount={true}
|
||||
asChild={true}
|
||||
className="absolute top-0 right-0 py-4 px-6 w-10/12 border-l border-vega-dark-200 h-screen bg-white dark:bg-black text-black dark:text-white"
|
||||
>
|
||||
<animated.div style={{ transform: styles.transform }}>
|
||||
<div className="absolute top-3 right-3">
|
||||
<MenuButton
|
||||
open={true}
|
||||
onClick={() => update({ drawerOpen: false })}
|
||||
/>
|
||||
</div>
|
||||
<div className="pt-10">
|
||||
<div className="pb-6 mb-6 border-b border-vega-dark-200">
|
||||
<NetworkSwitcher />
|
||||
</div>
|
||||
<NavigationMenu.Root
|
||||
value={value}
|
||||
onValueChange={(value) => update({ value })}
|
||||
>
|
||||
{children}
|
||||
<NavigationMenu.Viewport className="absolute w-full h-full top-0 left-0 bg-black" />
|
||||
</NavigationMenu.Root>
|
||||
<div className="pt-6 mt-6 border-t border-vega-dark-200">
|
||||
<ThemeSwitcher />
|
||||
</div>
|
||||
</div>
|
||||
</animated.div>
|
||||
</Dialog.Content>
|
||||
</>
|
||||
) : null;
|
||||
})}
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const NavigationList = ({ children }: { children: ReactNode }) => {
|
||||
return (
|
||||
<NavigationMenu.List className="flex flex-col md:flex-row gap-2">
|
||||
{children}
|
||||
</NavigationMenu.List>
|
||||
);
|
||||
};
|
||||
|
||||
export const NavigationItem = ({ children }: { children: ReactNode }) => {
|
||||
return (
|
||||
<NavigationMenu.Item className="relative mb-4 md:mb-0 last:mb-0 md:mr-4 last:mr-0 whitespace-nowrap">
|
||||
{children}
|
||||
</NavigationMenu.Item>
|
||||
);
|
||||
};
|
||||
|
||||
export const NavigationLink = ({
|
||||
children,
|
||||
path,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
path: string;
|
||||
}) => {
|
||||
const update = useNav((store) => store.update);
|
||||
const borderClasses = classNames(
|
||||
'absolute h-[2px] w-full bottom-[-1px] left-0',
|
||||
'bg-black dark:bg-vega-yellow'
|
||||
);
|
||||
|
||||
return (
|
||||
<NavigationMenu.Link
|
||||
asChild={true}
|
||||
onClick={() => update({ drawerOpen: false, value: '' })}
|
||||
>
|
||||
<NavLink to={path} className="inline-block md:pt-2 text-lg">
|
||||
{({ isActive }) => {
|
||||
const linkTextClasses = classNames('hover:text-white', {
|
||||
'text-white': isActive,
|
||||
'text-vega-dark-300': !isActive,
|
||||
});
|
||||
return (
|
||||
<span className="block relative pb-1 md:pb-2">
|
||||
<span className={linkTextClasses}>{children}</span>
|
||||
{isActive && <span className={borderClasses} />}
|
||||
</span>
|
||||
);
|
||||
}}
|
||||
</NavLink>
|
||||
</NavigationMenu.Link>
|
||||
);
|
||||
};
|
||||
|
||||
export const NavigationTrigger = ({ children }: { children: ReactNode }) => {
|
||||
// eslint-disable-next-line
|
||||
const preventHover = (event: any) => {
|
||||
const e = event as Event;
|
||||
if (window.innerWidth < BREAKPOINT) e.preventDefault();
|
||||
};
|
||||
return (
|
||||
<NavigationMenu.Trigger
|
||||
onPointerMove={preventHover}
|
||||
onPointerLeave={preventHover}
|
||||
onPointerEnter={preventHover}
|
||||
className="w-full flex items-center justify-between text-vega-dark-300 hover:text-white text-lg md:py-2"
|
||||
>
|
||||
{children}
|
||||
<span className="md:rotate-90 md:ml-3">
|
||||
<Icon name="arrow-right" size={3} />
|
||||
</span>
|
||||
</NavigationMenu.Trigger>
|
||||
);
|
||||
};
|
||||
|
||||
export const NavigationContent = ({
|
||||
children,
|
||||
backText,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
backText: string;
|
||||
}) => {
|
||||
return (
|
||||
<NavigationMenu.Content>
|
||||
<NavigationContentWrapper backText={backText}>
|
||||
{children}
|
||||
</NavigationContentWrapper>
|
||||
</NavigationMenu.Content>
|
||||
);
|
||||
};
|
||||
|
||||
const NavigationContentWrapper = ({
|
||||
backText,
|
||||
children,
|
||||
}: {
|
||||
backText: string;
|
||||
children: ReactNode;
|
||||
}) => {
|
||||
const drawerConfig = {
|
||||
from: { transform: 'translateX(100%)' },
|
||||
to: { transform: 'translateX(0%)' },
|
||||
config: {
|
||||
...config,
|
||||
duration: 200,
|
||||
},
|
||||
};
|
||||
const menuConfig = {
|
||||
from: { opacity: 0, transform: 'translateY(40px)' },
|
||||
to: { opacity: 1, transform: 'translateY(0)' },
|
||||
config: {
|
||||
...config,
|
||||
duration: 100,
|
||||
},
|
||||
};
|
||||
const styles = useSpring(
|
||||
window.innerWidth > BREAKPOINT ? menuConfig : drawerConfig
|
||||
);
|
||||
|
||||
return (
|
||||
<animated.div
|
||||
style={styles}
|
||||
className="pt-20 md:py-2 px-4 z-30 md:absolute md:mt-3 md:border border-vega-dark-200 bg-black dark:md:bg-vega-dark-100 md:rounded-xl"
|
||||
>
|
||||
<div className="flex items-center md:hidden mb-6 text-vega-dark-300 hover:text-white">
|
||||
<Icon name="arrow-left" size={3} />
|
||||
<span className="ml-2">{backText}</span>
|
||||
</div>
|
||||
<ul className="md:min-w-[250px]">{children}</ul>
|
||||
</animated.div>
|
||||
);
|
||||
};
|
||||
|
||||
const MenuButton = ({
|
||||
open,
|
||||
onClick,
|
||||
}: {
|
||||
open: boolean;
|
||||
onClick: () => void;
|
||||
}) => (
|
||||
<button
|
||||
className={classNames(
|
||||
'flex flex-col justify-around gap-3 p-2 relative z-30 h-[34px]',
|
||||
{
|
||||
'z-50': open,
|
||||
}
|
||||
)}
|
||||
onClick={onClick}
|
||||
data-testid="button-menu-drawer"
|
||||
>
|
||||
<div
|
||||
className={classNames('w-[26px] h-[2px] transition-all', {
|
||||
'translate-y-0 rotate-0 bg-white': !open,
|
||||
// 'bg-black': !drawerOpen && navbarTheme === 'yellow',
|
||||
'translate-y-[7.5px] rotate-45 bg-black dark:bg-white': open,
|
||||
})}
|
||||
/>
|
||||
<div
|
||||
className={classNames('w-[26px] h-[2px] transition-all', {
|
||||
'translate-y-0 rotate-0 bg-white': !open,
|
||||
// 'bg-black': !drawerOpen && navbarTheme === 'yellow',
|
||||
'-translate-y-[7.5px] -rotate-45 bg-black dark:bg-white': open,
|
||||
})}
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
@@ -1,6 +1,13 @@
|
||||
import Head from 'next/head';
|
||||
import type { AppProps } from 'next/app';
|
||||
import { Navbar } from '../components/navbar';
|
||||
import {
|
||||
Navbar,
|
||||
NavigationContent,
|
||||
NavigationItem,
|
||||
NavigationLink,
|
||||
NavigationList,
|
||||
NavigationTrigger,
|
||||
} from '../components/navbar';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
useEagerConnect as useVegaEagerConnect,
|
||||
@@ -28,12 +35,19 @@ import { Footer } from '../components/footer';
|
||||
import { useMemo, useState } from 'react';
|
||||
import DialogsContainer from './dialogs-container';
|
||||
import ToastsManager from './toasts-manager';
|
||||
import { HashRouter, useLocation, useSearchParams } from 'react-router-dom';
|
||||
import {
|
||||
HashRouter,
|
||||
Link,
|
||||
NavLink,
|
||||
useLocation,
|
||||
useSearchParams,
|
||||
} from 'react-router-dom';
|
||||
import { Connectors } from '../lib/vega-connectors';
|
||||
import { ViewingBanner } from '../components/viewing-banner';
|
||||
import { Banner } from '../components/banner';
|
||||
import classNames from 'classnames';
|
||||
import { AppLoader } from '../components/app-loader';
|
||||
import { Links, Routes } from './client-router';
|
||||
|
||||
const DEFAULT_TITLE = t('Welcome to Vega trading!');
|
||||
|
||||
@@ -68,6 +82,12 @@ const TransactionsHandler = () => {
|
||||
};
|
||||
|
||||
function AppBody({ Component }: AppProps) {
|
||||
const { marketId } = useGlobalStore((store) => ({
|
||||
marketId: store.marketId,
|
||||
}));
|
||||
const tradingPath = marketId
|
||||
? Links[Routes.MARKET](marketId)
|
||||
: Links[Routes.MARKET]();
|
||||
const location = useLocation();
|
||||
const { VEGA_ENV } = useEnvironment();
|
||||
|
||||
@@ -84,10 +104,66 @@ function AppBody({ Component }: AppProps) {
|
||||
</Head>
|
||||
<Title />
|
||||
<div className={gridClasses}>
|
||||
<Navbar
|
||||
navbarTheme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'dark'}
|
||||
/>
|
||||
<Banner />
|
||||
<Navbar
|
||||
// navbarTheme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'dark'}
|
||||
>
|
||||
<NavigationList>
|
||||
<NavigationItem>
|
||||
<NavigationLink path={Links[Routes.MARKETS]()}>
|
||||
Markets
|
||||
</NavigationLink>
|
||||
</NavigationItem>
|
||||
|
||||
<NavigationItem>
|
||||
<NavigationLink path={tradingPath}>Trading</NavigationLink>
|
||||
</NavigationItem>
|
||||
|
||||
<NavigationItem>
|
||||
<NavigationLink path={Links[Routes.PORTFOLIO]()}>
|
||||
Portfolio
|
||||
</NavigationLink>
|
||||
</NavigationItem>
|
||||
|
||||
<NavigationItem>
|
||||
<NavigationTrigger>Dropdown</NavigationTrigger>
|
||||
<NavigationContent backText="Token">
|
||||
<NavigationItem>
|
||||
<NavigationLink path={tradingPath}>Trading</NavigationLink>
|
||||
</NavigationItem>
|
||||
|
||||
<NavigationItem>
|
||||
<NavigationLink path={Links[Routes.PORTFOLIO]()}>
|
||||
Portfolio
|
||||
</NavigationLink>
|
||||
</NavigationItem>
|
||||
|
||||
<NavigationItem>
|
||||
<NavigationLink path="https://google.com">
|
||||
Redeem Vested Tokens
|
||||
</NavigationLink>
|
||||
</NavigationItem>
|
||||
</NavigationContent>
|
||||
</NavigationItem>
|
||||
|
||||
<NavigationItem>
|
||||
<NavigationTrigger>Another</NavigationTrigger>
|
||||
<NavigationContent backText="Another">
|
||||
<NavigationItem>
|
||||
<NavigationLink path="https://google.com">Foo</NavigationLink>
|
||||
</NavigationItem>
|
||||
|
||||
<NavigationItem>
|
||||
<NavigationLink path="https://google.com">Bar</NavigationLink>
|
||||
</NavigationItem>
|
||||
|
||||
<NavigationItem>
|
||||
<NavigationLink path="https://google.com">Quz</NavigationLink>
|
||||
</NavigationItem>
|
||||
</NavigationContent>
|
||||
</NavigationItem>
|
||||
</NavigationList>
|
||||
</Navbar>
|
||||
<ViewingBanner />
|
||||
<main data-testid={location.pathname}>
|
||||
<Component />
|
||||
|
||||
@@ -8,10 +8,10 @@ export const useNodeHealth = () => {
|
||||
const url = useEnvironment((store) => store.VEGA_URL);
|
||||
const headerStore = useHeaderStore();
|
||||
const headers = url ? headerStore[url] : undefined;
|
||||
const { data, error, loading, stopPolling } = useStatisticsQuery({
|
||||
pollInterval: 1000,
|
||||
fetchPolicy: 'no-cache',
|
||||
});
|
||||
const { data, error, loading, startPolling, stopPolling } =
|
||||
useStatisticsQuery({
|
||||
fetchPolicy: 'no-cache',
|
||||
});
|
||||
|
||||
const blockDiff = useMemo(() => {
|
||||
if (!data?.statistics.blockHeight) {
|
||||
@@ -28,8 +28,13 @@ export const useNodeHealth = () => {
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
stopPolling();
|
||||
return;
|
||||
}
|
||||
}, [error, stopPolling]);
|
||||
|
||||
if (!('Cypress' in window)) {
|
||||
startPolling(1000);
|
||||
}
|
||||
}, [error, startPolling, stopPolling]);
|
||||
|
||||
return {
|
||||
error,
|
||||
|
||||
@@ -53,7 +53,7 @@ export const NavDropdownMenuContent = forwardRef<
|
||||
<DropdownMenuPrimitive.Content
|
||||
{...contentProps}
|
||||
ref={forwardedRef}
|
||||
className="bg-vega-dark-100 mt-4 py-4 rounded-xl border border-vega-dark-200 text-white"
|
||||
className="z-10 bg-vega-dark-100 mt-4 py-4 rounded-xl border border-vega-dark-200 text-white"
|
||||
align="start"
|
||||
sideOffset={10}
|
||||
/>
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from './nav';
|
||||
export * from './nav2';
|
||||
|
||||
@@ -0,0 +1,287 @@
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import { create } from 'zustand';
|
||||
import * as NavigationMenu from '@radix-ui/react-navigation-menu';
|
||||
import type { ReactNode } from 'react';
|
||||
import * as Dialog from '@radix-ui/react-dialog';
|
||||
import { Icon, ThemeSwitcher } from '@vegaprotocol/ui-toolkit';
|
||||
import { NetworkSwitcher } from '@vegaprotocol/environment';
|
||||
import classNames from 'classnames';
|
||||
import { animated, useTransition, useSpring, config } from 'react-spring';
|
||||
|
||||
const BREAKPOINT = 760;
|
||||
const useNav = create<{
|
||||
drawerOpen: boolean;
|
||||
value: string;
|
||||
update: (state: Partial<{ drawerOpen: boolean; value: string }>) => void;
|
||||
}>((set) => ({
|
||||
drawerOpen: false,
|
||||
value: '',
|
||||
update: (state) => set(state),
|
||||
}));
|
||||
|
||||
export const Navbar = ({ children }: { children: ReactNode }) => {
|
||||
return (
|
||||
<div className="px-4 border-b border-vega-light-200 dark:border-vega-dark-200">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="py-3 mr-4">THE ICON</div>
|
||||
{window.innerWidth > BREAKPOINT ? (
|
||||
<Menu>{children}</Menu>
|
||||
) : (
|
||||
<Drawer>{children}</Drawer>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Menu = ({ children }: { children: ReactNode }) => {
|
||||
const { value, update } = useNav();
|
||||
return (
|
||||
<>
|
||||
<div className="mr-4">
|
||||
<NetworkSwitcher />
|
||||
</div>
|
||||
<NavigationMenu.Root
|
||||
value={value}
|
||||
onValueChange={(value) => update({ value })}
|
||||
className="relative self-end"
|
||||
>
|
||||
{children}
|
||||
</NavigationMenu.Root>
|
||||
<div className="flex items-center gap-2 ml-auto">
|
||||
<ThemeSwitcher />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Drawer = ({ children }: { children: ReactNode }) => {
|
||||
const { drawerOpen, value, update } = useNav();
|
||||
const transition = useTransition(drawerOpen, {
|
||||
from: { opacity: 0, transform: 'translateX(100%)' },
|
||||
enter: { opacity: 1, transform: 'translateX(0%)' },
|
||||
leave: { opacity: 0, transform: 'translateX(100%)' },
|
||||
config: {
|
||||
...config.default,
|
||||
duration: 200,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="ml-auto">
|
||||
<MenuButton open={false} onClick={() => update({ drawerOpen: true })} />
|
||||
</div>
|
||||
<Dialog.Root
|
||||
open={drawerOpen}
|
||||
onOpenChange={(open) => update({ drawerOpen: open })}
|
||||
>
|
||||
<Dialog.Portal forceMount={true}>
|
||||
{transition((styles, isOpen) => {
|
||||
return isOpen ? (
|
||||
<>
|
||||
<Dialog.Overlay
|
||||
forceMount={true}
|
||||
asChild={true}
|
||||
className="fixed inset-0 bg-black/50"
|
||||
>
|
||||
<animated.div style={{ opacity: styles.opacity }} />
|
||||
</Dialog.Overlay>
|
||||
<Dialog.Content
|
||||
forceMount={true}
|
||||
asChild={true}
|
||||
className="absolute top-0 right-0 py-4 px-6 w-10/12 border-l border-vega-dark-200 h-screen bg-white dark:bg-black text-black dark:text-white"
|
||||
>
|
||||
<animated.div style={{ transform: styles.transform }}>
|
||||
<div className="absolute top-3 right-3">
|
||||
<MenuButton
|
||||
open={true}
|
||||
onClick={() => update({ drawerOpen: false })}
|
||||
/>
|
||||
</div>
|
||||
<div className="pt-10">
|
||||
<div className="pb-6 mb-6 border-b border-vega-dark-200">
|
||||
<NetworkSwitcher />
|
||||
</div>
|
||||
<NavigationMenu.Root
|
||||
value={value}
|
||||
onValueChange={(value) => update({ value })}
|
||||
>
|
||||
{children}
|
||||
<NavigationMenu.Viewport className="absolute w-full h-full top-0 left-0 bg-black" />
|
||||
</NavigationMenu.Root>
|
||||
<div className="pt-6 mt-6 border-t border-vega-dark-200">
|
||||
<ThemeSwitcher />
|
||||
</div>
|
||||
</div>
|
||||
</animated.div>
|
||||
</Dialog.Content>
|
||||
</>
|
||||
) : null;
|
||||
})}
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const NavigationList = ({ children }: { children: ReactNode }) => {
|
||||
return (
|
||||
<NavigationMenu.List className="flex flex-col md:flex-row gap-2">
|
||||
{children}
|
||||
</NavigationMenu.List>
|
||||
);
|
||||
};
|
||||
|
||||
export const NavigationItem = ({ children }: { children: ReactNode }) => {
|
||||
return (
|
||||
<NavigationMenu.Item className="relative mb-4 md:mb-0 last:mb-0 md:mr-4 last:mr-0 whitespace-nowrap">
|
||||
{children}
|
||||
</NavigationMenu.Item>
|
||||
);
|
||||
};
|
||||
|
||||
export const NavigationLink = ({
|
||||
children,
|
||||
path,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
path: string;
|
||||
}) => {
|
||||
const update = useNav((store) => store.update);
|
||||
const borderClasses = classNames(
|
||||
'absolute h-[2px] w-full bottom-[-1px] left-0',
|
||||
'bg-black dark:bg-vega-yellow'
|
||||
);
|
||||
|
||||
return (
|
||||
<NavigationMenu.Link
|
||||
asChild={true}
|
||||
onClick={() => update({ drawerOpen: false, value: '' })}
|
||||
>
|
||||
<NavLink to={path} className="inline-block md:pt-2 text-lg">
|
||||
{({ isActive }) => {
|
||||
const linkTextClasses = classNames('hover:text-white', {
|
||||
'text-white': isActive,
|
||||
'text-vega-dark-300': !isActive,
|
||||
});
|
||||
return (
|
||||
<span className="block relative pb-1 md:pb-2">
|
||||
<span className={linkTextClasses}>{children}</span>
|
||||
{isActive && <span className={borderClasses} />}
|
||||
</span>
|
||||
);
|
||||
}}
|
||||
</NavLink>
|
||||
</NavigationMenu.Link>
|
||||
);
|
||||
};
|
||||
|
||||
export const NavigationTrigger = ({ children }: { children: ReactNode }) => {
|
||||
// eslint-disable-next-line
|
||||
const preventHover = (event: any) => {
|
||||
const e = event as Event;
|
||||
if (window.innerWidth < BREAKPOINT) e.preventDefault();
|
||||
};
|
||||
return (
|
||||
<NavigationMenu.Trigger
|
||||
onPointerMove={preventHover}
|
||||
onPointerLeave={preventHover}
|
||||
onPointerEnter={preventHover}
|
||||
className="w-full flex items-center justify-between text-vega-dark-300 hover:text-white text-lg md:py-2"
|
||||
>
|
||||
{children}
|
||||
<span className="md:rotate-90 md:ml-3">
|
||||
<Icon name="arrow-right" size={3} />
|
||||
</span>
|
||||
</NavigationMenu.Trigger>
|
||||
);
|
||||
};
|
||||
|
||||
export const NavigationContent = ({
|
||||
children,
|
||||
backText,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
backText: string;
|
||||
}) => {
|
||||
return (
|
||||
<NavigationMenu.Content>
|
||||
<NavigationContentWrapper backText={backText}>
|
||||
{children}
|
||||
</NavigationContentWrapper>
|
||||
</NavigationMenu.Content>
|
||||
);
|
||||
};
|
||||
|
||||
const NavigationContentWrapper = ({
|
||||
backText,
|
||||
children,
|
||||
}: {
|
||||
backText: string;
|
||||
children: ReactNode;
|
||||
}) => {
|
||||
const drawerConfig = {
|
||||
from: { transform: 'translateX(100%)' },
|
||||
to: { transform: 'translateX(0%)' },
|
||||
config: {
|
||||
...config,
|
||||
duration: 200,
|
||||
},
|
||||
};
|
||||
const menuConfig = {
|
||||
from: { opacity: 0, transform: 'translateY(40px)' },
|
||||
to: { opacity: 1, transform: 'translateY(0)' },
|
||||
config: {
|
||||
...config,
|
||||
duration: 100,
|
||||
},
|
||||
};
|
||||
const styles = useSpring(
|
||||
window.innerWidth > BREAKPOINT ? menuConfig : drawerConfig
|
||||
);
|
||||
|
||||
return (
|
||||
<animated.div
|
||||
style={styles}
|
||||
className="pt-20 md:py-2 px-4 z-30 md:absolute md:mt-3 md:border border-vega-dark-200 bg-black dark:md:bg-vega-dark-100 md:rounded-xl"
|
||||
>
|
||||
<div className="flex items-center md:hidden mb-6 text-vega-dark-300 hover:text-white">
|
||||
<Icon name="arrow-left" size={3} />
|
||||
<span className="ml-2">{backText}</span>
|
||||
</div>
|
||||
<ul className="md:min-w-[250px]">{children}</ul>
|
||||
</animated.div>
|
||||
);
|
||||
};
|
||||
|
||||
const MenuButton = ({
|
||||
open,
|
||||
onClick,
|
||||
}: {
|
||||
open: boolean;
|
||||
onClick: () => void;
|
||||
}) => (
|
||||
<button
|
||||
className={classNames(
|
||||
'flex flex-col justify-around gap-3 p-2 relative h-[34px]'
|
||||
)}
|
||||
onClick={onClick}
|
||||
data-testid="button-menu-drawer"
|
||||
>
|
||||
<div
|
||||
className={classNames('w-[26px] h-[2px] transition-all', {
|
||||
'translate-y-0 rotate-0 bg-white': !open,
|
||||
// 'bg-black': !drawerOpen && navbarTheme === 'yellow',
|
||||
'translate-y-[7.5px] rotate-45 bg-black dark:bg-white': open,
|
||||
})}
|
||||
/>
|
||||
<div
|
||||
className={classNames('w-[26px] h-[2px] transition-all', {
|
||||
'translate-y-0 rotate-0 bg-white': !open,
|
||||
// 'bg-black': !drawerOpen && navbarTheme === 'yellow',
|
||||
'-translate-y-[7.5px] -rotate-45 bg-black dark:bg-white': open,
|
||||
})}
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
@@ -27,6 +27,7 @@
|
||||
"@radix-ui/react-dialog": "^1.0.2",
|
||||
"@radix-ui/react-dropdown-menu": "^2.0.2",
|
||||
"@radix-ui/react-icons": "^1.1.1",
|
||||
"@radix-ui/react-navigation-menu": "^1.1.1",
|
||||
"@radix-ui/react-popover": "^1.0.3",
|
||||
"@radix-ui/react-radio-group": "^1.1.1",
|
||||
"@radix-ui/react-select": "^1.2.0",
|
||||
@@ -73,6 +74,7 @@
|
||||
"react-i18next": "^11.11.4",
|
||||
"react-intersection-observer": "^9.2.2",
|
||||
"react-router-dom": "6.3.0",
|
||||
"react-spring": "^9.6.1",
|
||||
"react-syntax-highlighter": "^15.4.5",
|
||||
"react-use-websocket": "^3.0.0",
|
||||
"react-virtualized-auto-sizer": "^1.0.6",
|
||||
|
||||
@@ -4323,6 +4323,27 @@
|
||||
aria-hidden "^1.1.1"
|
||||
react-remove-scroll "2.5.5"
|
||||
|
||||
"@radix-ui/react-navigation-menu@^1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.1.1.tgz#84f24d90e6448a0c83d3431c6eefbea73dc7522e"
|
||||
integrity sha512-Khgf+LwqYfUpbFAHcFPDMj6ZrWxnwCgC96liLYwE48x9YJbXGlutOWzZaSzrgl82xS+PwoPLQunfDe/i4ZITRA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/primitive" "1.0.0"
|
||||
"@radix-ui/react-collection" "1.0.1"
|
||||
"@radix-ui/react-compose-refs" "1.0.0"
|
||||
"@radix-ui/react-context" "1.0.0"
|
||||
"@radix-ui/react-direction" "1.0.0"
|
||||
"@radix-ui/react-dismissable-layer" "1.0.2"
|
||||
"@radix-ui/react-id" "1.0.0"
|
||||
"@radix-ui/react-presence" "1.0.0"
|
||||
"@radix-ui/react-primitive" "1.0.1"
|
||||
"@radix-ui/react-use-callback-ref" "1.0.0"
|
||||
"@radix-ui/react-use-controllable-state" "1.0.0"
|
||||
"@radix-ui/react-use-layout-effect" "1.0.0"
|
||||
"@radix-ui/react-use-previous" "1.0.0"
|
||||
"@radix-ui/react-visually-hidden" "1.0.1"
|
||||
|
||||
"@radix-ui/react-popover@^1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-popover/-/react-popover-1.0.3.tgz#65ae2ee1fca2d7fd750308549eb8e0857c6160fe"
|
||||
@@ -4576,6 +4597,92 @@
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@react-spring/animated@~9.6.1":
|
||||
version "9.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@react-spring/animated/-/animated-9.6.1.tgz#ccc626d847cbe346f5f8815d0928183c647eb425"
|
||||
integrity sha512-ls/rJBrAqiAYozjLo5EPPLLOb1LM0lNVQcXODTC1SMtS6DbuBCPaKco5svFUQFMP2dso3O+qcC4k9FsKc0KxMQ==
|
||||
dependencies:
|
||||
"@react-spring/shared" "~9.6.1"
|
||||
"@react-spring/types" "~9.6.1"
|
||||
|
||||
"@react-spring/core@~9.6.1":
|
||||
version "9.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@react-spring/core/-/core-9.6.1.tgz#ebe07c20682b360b06af116ea24e2b609e778c10"
|
||||
integrity sha512-3HAAinAyCPessyQNNXe5W0OHzRfa8Yo5P748paPcmMowZ/4sMfaZ2ZB6e5x5khQI8NusOHj8nquoutd6FRY5WQ==
|
||||
dependencies:
|
||||
"@react-spring/animated" "~9.6.1"
|
||||
"@react-spring/rafz" "~9.6.1"
|
||||
"@react-spring/shared" "~9.6.1"
|
||||
"@react-spring/types" "~9.6.1"
|
||||
|
||||
"@react-spring/konva@~9.6.1":
|
||||
version "9.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@react-spring/konva/-/konva-9.6.1.tgz#66e63da0e9681e42395e995402a7e73ba6892461"
|
||||
integrity sha512-MevnU+tnG1LPsmMRpfJfevfLtI0ObIvrwYc+Xg+kmYJe00vwMRSdulQOztKANKalFXBewwk72XrQCeRLXFaUIg==
|
||||
dependencies:
|
||||
"@react-spring/animated" "~9.6.1"
|
||||
"@react-spring/core" "~9.6.1"
|
||||
"@react-spring/shared" "~9.6.1"
|
||||
"@react-spring/types" "~9.6.1"
|
||||
|
||||
"@react-spring/native@~9.6.1":
|
||||
version "9.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@react-spring/native/-/native-9.6.1.tgz#b66e237f2faaa4f88569d5a03b6fb0136bcdf2b9"
|
||||
integrity sha512-ZIfSytxFGLw4gYOb8gsmwG0+JZYxuM/Y1XPCXCkhuoMn+RmOYrr0kQ4gLczbmf+TRxth7OT1c8vBYz0+SCGcIQ==
|
||||
dependencies:
|
||||
"@react-spring/animated" "~9.6.1"
|
||||
"@react-spring/core" "~9.6.1"
|
||||
"@react-spring/shared" "~9.6.1"
|
||||
"@react-spring/types" "~9.6.1"
|
||||
|
||||
"@react-spring/rafz@~9.6.1":
|
||||
version "9.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@react-spring/rafz/-/rafz-9.6.1.tgz#d71aafb92b78b24e4ff84639f52745afc285c38d"
|
||||
integrity sha512-v6qbgNRpztJFFfSE3e2W1Uz+g8KnIBs6SmzCzcVVF61GdGfGOuBrbjIcp+nUz301awVmREKi4eMQb2Ab2gGgyQ==
|
||||
|
||||
"@react-spring/shared@~9.6.1":
|
||||
version "9.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@react-spring/shared/-/shared-9.6.1.tgz#4e2e4296910656c02bd9fd54c559702bc836ac4e"
|
||||
integrity sha512-PBFBXabxFEuF8enNLkVqMC9h5uLRBo6GQhRMQT/nRTnemVENimgRd+0ZT4yFnAQ0AxWNiJfX3qux+bW2LbG6Bw==
|
||||
dependencies:
|
||||
"@react-spring/rafz" "~9.6.1"
|
||||
"@react-spring/types" "~9.6.1"
|
||||
|
||||
"@react-spring/three@~9.6.1":
|
||||
version "9.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@react-spring/three/-/three-9.6.1.tgz#095fcd1dc6509127c33c14486d88289b89baeb9d"
|
||||
integrity sha512-Tyw2YhZPKJAX3t2FcqvpLRb71CyTe1GvT3V+i+xJzfALgpk10uPGdGaQQ5Xrzmok1340DAeg2pR/MCfaW7b8AA==
|
||||
dependencies:
|
||||
"@react-spring/animated" "~9.6.1"
|
||||
"@react-spring/core" "~9.6.1"
|
||||
"@react-spring/shared" "~9.6.1"
|
||||
"@react-spring/types" "~9.6.1"
|
||||
|
||||
"@react-spring/types@~9.6.1":
|
||||
version "9.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.6.1.tgz#913d3a68c5cbc1124fdb18eff919432f7b6abdde"
|
||||
integrity sha512-POu8Mk0hIU3lRXB3bGIGe4VHIwwDsQyoD1F394OK7STTiX9w4dG3cTLljjYswkQN+hDSHRrj4O36kuVa7KPU8Q==
|
||||
|
||||
"@react-spring/web@~9.6.1":
|
||||
version "9.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@react-spring/web/-/web-9.6.1.tgz#3e4c03b724d2b545dc2fa2649eb6109318ab9178"
|
||||
integrity sha512-X2zR6q2Z+FjsWfGAmAXlQaoUHbPmfuCaXpuM6TcwXPpLE1ZD4A1eys/wpXboFQmDkjnrlTmKvpVna1MjWpZ5Hw==
|
||||
dependencies:
|
||||
"@react-spring/animated" "~9.6.1"
|
||||
"@react-spring/core" "~9.6.1"
|
||||
"@react-spring/shared" "~9.6.1"
|
||||
"@react-spring/types" "~9.6.1"
|
||||
|
||||
"@react-spring/zdog@~9.6.1":
|
||||
version "9.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@react-spring/zdog/-/zdog-9.6.1.tgz#5292c374e23e3846db3eb9d7557ed5a7bb40dada"
|
||||
integrity sha512-0jSGm2OFW/+/+4dkRp46KzEkcLVfzV2k6DO1om0dLDtQ4q6FpX4dmDTlRc7Apzin6VtfQONMFIGITtbqoS28MQ==
|
||||
dependencies:
|
||||
"@react-spring/animated" "~9.6.1"
|
||||
"@react-spring/core" "~9.6.1"
|
||||
"@react-spring/shared" "~9.6.1"
|
||||
"@react-spring/types" "~9.6.1"
|
||||
|
||||
"@repeaterjs/repeater@^3.0.4":
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@repeaterjs/repeater/-/repeater-3.0.4.tgz#a04d63f4d1bf5540a41b01a921c9a7fddc3bd1ca"
|
||||
@@ -19261,6 +19368,18 @@ react-smooth@^2.0.1:
|
||||
fast-equals "^2.0.0"
|
||||
react-transition-group "2.9.0"
|
||||
|
||||
react-spring@^9.6.1:
|
||||
version "9.6.1"
|
||||
resolved "https://registry.yarnpkg.com/react-spring/-/react-spring-9.6.1.tgz#e715b2fa523c1a3acfdcf1aaa93e081620b8cc8e"
|
||||
integrity sha512-BeP80R4SLb1bZHW/Q62nECoScHw/fH+jzGkD7dc892HNGa+lbGIJXURc6U7N8JfZ8peEO46nPxR57aUMuYzquQ==
|
||||
dependencies:
|
||||
"@react-spring/core" "~9.6.1"
|
||||
"@react-spring/konva" "~9.6.1"
|
||||
"@react-spring/native" "~9.6.1"
|
||||
"@react-spring/three" "~9.6.1"
|
||||
"@react-spring/web" "~9.6.1"
|
||||
"@react-spring/zdog" "~9.6.1"
|
||||
|
||||
react-style-singleton@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.2.1.tgz#f99e420492b2d8f34d38308ff660b60d0b1205b4"
|
||||
|
||||
Reference in New Issue
Block a user