Feat/324 sparkline component (#381)
* move colors in ui-toolkit/config * fixing tests and add stories * fixing some tests and edit story * update sparkline story * rename story templates for sparkline * use tailwind colors and rename to *.spec.tsx * use tailwind only * remove test.tsx * Update libs/tailwindcss-config/src/theme.js Co-authored-by: Matthew Russell <mattrussell36@gmail.com> * remove gray from tailwind and use black/40 and white/40 for strokeCurrent Co-authored-by: madalinaraicu <“madalina@raygroup.uk”> Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
This commit is contained in:
parent
19a9e9adb0
commit
319e14164d
@ -5,7 +5,6 @@ import React from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import { Colors } from '../../config';
|
|
||||||
import {
|
import {
|
||||||
AppStateActionType,
|
AppStateActionType,
|
||||||
useAppState,
|
useAppState,
|
||||||
@ -26,6 +25,9 @@ import {
|
|||||||
WalletCardRow,
|
WalletCardRow,
|
||||||
} from '../wallet-card';
|
} from '../wallet-card';
|
||||||
import { Button, Loader } from '@vegaprotocol/ui-toolkit';
|
import { Button, Loader } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { theme } from '@vegaprotocol/tailwindcss-config';
|
||||||
|
|
||||||
|
const Colors = theme.colors;
|
||||||
|
|
||||||
const removeLeadingAddressSymbol = (key: string) => {
|
const removeLeadingAddressSymbol = (key: string) => {
|
||||||
if (key && key.length > 2 && key.slice(0, 2) === '0x') {
|
if (key && key.length > 2 && key.slice(0, 2) === '0x') {
|
||||||
@ -68,13 +70,13 @@ const AssociatedAmounts = ({
|
|||||||
total={associationAmounts.total}
|
total={associationAmounts.total}
|
||||||
leftLabel={t('associated')}
|
leftLabel={t('associated')}
|
||||||
rightLabel={t('notAssociated')}
|
rightLabel={t('notAssociated')}
|
||||||
leftColor={Colors.WHITE}
|
leftColor={Colors.white.DEFAULT}
|
||||||
rightColor={Colors.BLACK}
|
rightColor={Colors.black.DEFAULT}
|
||||||
light={true}
|
light={true}
|
||||||
/>
|
/>
|
||||||
{vestingAssociationByVegaKey.length ? (
|
{vestingAssociationByVegaKey.length ? (
|
||||||
<>
|
<>
|
||||||
<hr style={{ borderStyle: 'dashed', color: Colors.TEXT }} />
|
<hr style={{ borderStyle: 'dashed', color: Colors.text }} />
|
||||||
<WalletCardRow label="Associated with Vega keys" bold={true} />
|
<WalletCardRow label="Associated with Vega keys" bold={true} />
|
||||||
{vestingAssociationByVegaKey.map(([key, amount]) => {
|
{vestingAssociationByVegaKey.map(([key, amount]) => {
|
||||||
return (
|
return (
|
||||||
|
@ -2,9 +2,11 @@ import './locked-progress.scss';
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { Colors } from '../../config';
|
|
||||||
import { formatNumber } from '../../lib/format-number';
|
import { formatNumber } from '../../lib/format-number';
|
||||||
import type { BigNumber } from '../../lib/bignumber';
|
import type { BigNumber } from '../../lib/bignumber';
|
||||||
|
import { theme } from '@vegaprotocol/tailwindcss-config';
|
||||||
|
|
||||||
|
const Colors = theme.colors;
|
||||||
|
|
||||||
export interface LockedProgressProps {
|
export interface LockedProgressProps {
|
||||||
total: BigNumber;
|
total: BigNumber;
|
||||||
@ -23,8 +25,8 @@ export const LockedProgress = ({
|
|||||||
unlocked,
|
unlocked,
|
||||||
leftLabel,
|
leftLabel,
|
||||||
rightLabel,
|
rightLabel,
|
||||||
leftColor = Colors.PINK,
|
leftColor = Colors.pink,
|
||||||
rightColor = Colors.GREEN,
|
rightColor = Colors.green.DEFAULT,
|
||||||
light = false,
|
light = false,
|
||||||
}: LockedProgressProps) => {
|
}: LockedProgressProps) => {
|
||||||
const lockedPercentage = React.useMemo(() => {
|
const lockedPercentage = React.useMemo(() => {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Links } from '../../config';
|
||||||
import { Colors, Links } from '../../config';
|
|
||||||
|
|
||||||
export const DownloadWalletPrompt = () => {
|
export const DownloadWalletPrompt = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -9,7 +8,7 @@ export const DownloadWalletPrompt = () => {
|
|||||||
<h3>{t('getWallet')}</h3>
|
<h3>{t('getWallet')}</h3>
|
||||||
<p style={{ margin: 0 }}>
|
<p style={{ margin: 0 }}>
|
||||||
<a
|
<a
|
||||||
style={{ color: Colors.DEEMPHASISE }}
|
className={'text-deemphasise'}
|
||||||
href={Links.WALLET_GUIDE}
|
href={Links.WALLET_GUIDE}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
@ -19,7 +18,7 @@ export const DownloadWalletPrompt = () => {
|
|||||||
</p>
|
</p>
|
||||||
<p style={{ margin: 0 }}>
|
<p style={{ margin: 0 }}>
|
||||||
<a
|
<a
|
||||||
style={{ color: Colors.DEEMPHASISE }}
|
className={'text-deemphasise'}
|
||||||
href={Links.WALLET_RELEASES}
|
href={Links.WALLET_RELEASES}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
export const Colors = {
|
|
||||||
WHITE: '#FFF',
|
|
||||||
RED: '#ED1515',
|
|
||||||
PINK: '#ff2d5e',
|
|
||||||
GREEN: '#26ff8a',
|
|
||||||
TEXT: '#c7c7c7',
|
|
||||||
|
|
||||||
BLACK: '#000',
|
|
||||||
DEEMPHASISE: '#8a9ba8',
|
|
||||||
GRAY_DARK_1: '#292929',
|
|
||||||
GRAY_DARK_2: '#333',
|
|
||||||
GRAY: '#494949',
|
|
||||||
GRAY_LIGHT: '#ccc',
|
|
||||||
GRAY_LIGHT_1: '#6e6e6e',
|
|
||||||
GREEN_DARK: '#246340', // Same as GREEN_TRANSPARENT given a background of #1f1f1f
|
|
||||||
GREEN_TRANSPARENT: 'rgba(38, 255, 138, 0.3)',
|
|
||||||
RED_TRANSPARENT: 'rgba(255, 38, 65, 0.3)',
|
|
||||||
TRANSPARENT: 'rgba(0,0,0,0)',
|
|
||||||
|
|
||||||
VEGA_RED: '#ff261a',
|
|
||||||
VEGA_ORANGE: '#d9822b',
|
|
||||||
VEGA_GREEN: '#26ff8a',
|
|
||||||
VEGA_YELLOW: '#daff0d',
|
|
||||||
};
|
|
@ -1,4 +1,3 @@
|
|||||||
export * from './colors';
|
|
||||||
export * from './flags';
|
export * from './flags';
|
||||||
export * from './ethereum';
|
export * from './ethereum';
|
||||||
export * from './links';
|
export * from './links';
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Colors } from '../config';
|
|
||||||
import { usePrevious } from './use-previous';
|
import { usePrevious } from './use-previous';
|
||||||
import type { BigNumber } from '../lib/bignumber';
|
import type { BigNumber } from '../lib/bignumber';
|
||||||
|
import { theme as tailwindcss } from '@vegaprotocol/tailwindcss-config';
|
||||||
|
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||||
|
const Colors = tailwindcss.colors;
|
||||||
|
|
||||||
const FLASH_DURATION = 1200; // Duration of flash animation in milliseconds
|
const FLASH_DURATION = 1200; // Duration of flash animation in milliseconds
|
||||||
|
|
||||||
@ -11,6 +13,7 @@ export function useAnimateValue(
|
|||||||
) {
|
) {
|
||||||
const shouldAnimate = React.useRef(false);
|
const shouldAnimate = React.useRef(false);
|
||||||
const previous = usePrevious(value);
|
const previous = usePrevious(value);
|
||||||
|
const [theme] = useThemeSwitcher();
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
@ -28,9 +31,17 @@ export function useAnimateValue(
|
|||||||
) {
|
) {
|
||||||
elRef.current?.animate(
|
elRef.current?.animate(
|
||||||
[
|
[
|
||||||
{ backgroundColor: Colors.VEGA_RED, color: Colors.WHITE },
|
{ backgroundColor: Colors.red.vega, color: Colors.white.DEFAULT },
|
||||||
{ backgroundColor: Colors.VEGA_RED, color: Colors.WHITE, offset: 0.8 },
|
{
|
||||||
{ backgroundColor: Colors.GRAY_LIGHT, color: Colors.WHITE },
|
backgroundColor: Colors.red.vega,
|
||||||
|
color: Colors.white.DEFAULT,
|
||||||
|
offset: 0.8,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor:
|
||||||
|
theme === 'dark' ? Colors.white[60] : Colors.black[60],
|
||||||
|
color: Colors.white.DEFAULT,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
FLASH_DURATION
|
FLASH_DURATION
|
||||||
);
|
);
|
||||||
@ -43,13 +54,20 @@ export function useAnimateValue(
|
|||||||
) {
|
) {
|
||||||
elRef.current?.animate(
|
elRef.current?.animate(
|
||||||
[
|
[
|
||||||
{ backgroundColor: Colors.VEGA_GREEN, color: Colors.WHITE },
|
|
||||||
{
|
{
|
||||||
backgroundColor: Colors.VEGA_GREEN,
|
backgroundColor: Colors.green.vega,
|
||||||
color: Colors.WHITE,
|
color: Colors.white.DEFAULT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: Colors.green.vega,
|
||||||
|
color: Colors.white.DEFAULT,
|
||||||
offset: 0.8,
|
offset: 0.8,
|
||||||
},
|
},
|
||||||
{ backgroundColor: Colors.GRAY_LIGHT, color: Colors.WHITE },
|
{
|
||||||
|
backgroundColor:
|
||||||
|
theme === 'dark' ? Colors.white[60] : Colors.black[60],
|
||||||
|
color: Colors.white.DEFAULT,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
FLASH_DURATION
|
FLASH_DURATION
|
||||||
);
|
);
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import { Trans, useTranslation } from 'react-i18next';
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { Colors } from '../../config';
|
|
||||||
|
|
||||||
interface TargetAddressMismatchProps {
|
interface TargetAddressMismatchProps {
|
||||||
connectedAddress: string;
|
connectedAddress: string;
|
||||||
expectedAddress: string;
|
expectedAddress: string;
|
||||||
@ -27,7 +25,7 @@ export const TargetAddressMismatch = ({
|
|||||||
}}
|
}}
|
||||||
components={{
|
components={{
|
||||||
bold: <strong />,
|
bold: <strong />,
|
||||||
red: <span style={{ color: Colors.RED }} />,
|
red: <span className={'text-red'} />,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
|
@ -3,7 +3,6 @@ import { Trans, useTranslation } from 'react-i18next';
|
|||||||
import { Link, useParams, useOutletContext } from 'react-router-dom';
|
import { Link, useParams, useOutletContext } from 'react-router-dom';
|
||||||
|
|
||||||
import { TransactionCallout } from '../../../components/transaction-callout';
|
import { TransactionCallout } from '../../../components/transaction-callout';
|
||||||
import { Colors } from '../../../config';
|
|
||||||
import { ADDRESSES } from '../../../config';
|
import { ADDRESSES } from '../../../config';
|
||||||
import { useAppState } from '../../../contexts/app-state/app-state-context';
|
import { useAppState } from '../../../contexts/app-state/app-state-context';
|
||||||
import { useContracts } from '../../../contexts/contracts/contracts-context';
|
import { useContracts } from '../../../contexts/contracts/contracts-context';
|
||||||
@ -88,7 +87,7 @@ export const RedeemFromTranche = () => {
|
|||||||
{txState.txState !== TxState.Default ? (
|
{txState.txState !== TxState.Default ? (
|
||||||
<TransactionCallout
|
<TransactionCallout
|
||||||
completeHeading={
|
completeHeading={
|
||||||
<strong style={{ color: Colors.WHITE }}>
|
<strong className={'text-white'}>
|
||||||
{t('Tokens from this Tranche have been redeemed')}
|
{t('Tokens from this Tranche have been redeemed')}
|
||||||
</strong>
|
</strong>
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { TokenInput } from '../../components/token-input';
|
import { TokenInput } from '../../components/token-input';
|
||||||
import { Colors, NetworkParams } from '../../config';
|
import { NetworkParams } from '../../config';
|
||||||
import { useAppState } from '../../contexts/app-state/app-state-context';
|
import { useAppState } from '../../contexts/app-state/app-state-context';
|
||||||
import { useNetworkParam } from '../../hooks/use-network-param';
|
import { useNetworkParam } from '../../hooks/use-network-param';
|
||||||
import { useSearchParams } from '../../hooks/use-search-params';
|
import { useSearchParams } from '../../hooks/use-search-params';
|
||||||
@ -203,11 +203,9 @@ export const StakingForm = ({
|
|||||||
availableStakeToRemove.isEqualTo(0)
|
availableStakeToRemove.isEqualTo(0)
|
||||||
) {
|
) {
|
||||||
if (appState.lien.isGreaterThan(0)) {
|
if (appState.lien.isGreaterThan(0)) {
|
||||||
return (
|
return <span className={'text-red'}>{t('stakeNodeWrongVegaKey')}</span>;
|
||||||
<span style={{ color: Colors.RED }}>{t('stakeNodeWrongVegaKey')}</span>
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
return <span style={{ color: Colors.RED }}>{t('stakeNodeNone')}</span>;
|
return <span className={'text-red'}>{t('stakeNodeNone')}</span>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { EpochCountdown } from '../../components/epoch-countdown';
|
import { EpochCountdown } from '../../components/epoch-countdown';
|
||||||
import { Colors } from '../../config';
|
|
||||||
import type { VegaKeyExtended } from '@vegaprotocol/wallet';
|
import type { VegaKeyExtended } from '@vegaprotocol/wallet';
|
||||||
import { BigNumber } from '../../lib/bignumber';
|
import { BigNumber } from '../../lib/bignumber';
|
||||||
import type { Staking as StakingQueryResult } from './__generated__/Staking';
|
import type { Staking as StakingQueryResult } from './__generated__/Staking';
|
||||||
@ -88,9 +87,7 @@ export const StakingNode = ({ vegaKey, data }: StakingNodeProps) => {
|
|||||||
|
|
||||||
if (!nodeInfo) {
|
if (!nodeInfo) {
|
||||||
return (
|
return (
|
||||||
<span style={{ color: Colors.RED }}>
|
<span className={'text-red'}>{t('stakingNodeNotFound', { node })}</span>
|
||||||
{t('stakingNodeNotFound', { node })}
|
|
||||||
</span>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Colors } from '../../config';
|
|
||||||
import { getAbbreviatedNumber } from '../../lib/abbreviate-number';
|
import { getAbbreviatedNumber } from '../../lib/abbreviate-number';
|
||||||
import { ProgressBar } from './progress-bar';
|
import { ProgressBar } from './progress-bar';
|
||||||
import type { BigNumber } from '../../lib/bignumber';
|
import type { BigNumber } from '../../lib/bignumber';
|
||||||
|
import { theme } from '@vegaprotocol/tailwindcss-config';
|
||||||
|
const Colors = theme.colors;
|
||||||
|
|
||||||
interface TrancheProgressProps {
|
interface TrancheProgressProps {
|
||||||
locked: BigNumber;
|
locked: BigNumber;
|
||||||
@ -28,7 +29,7 @@ export const TrancheProgress = ({
|
|||||||
<span className="tranches__progress-title">{t('Locked')}</span>
|
<span className="tranches__progress-title">{t('Locked')}</span>
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
width={220}
|
width={220}
|
||||||
color={Colors.PINK}
|
color={Colors.pink}
|
||||||
percentage={lockedPercentage}
|
percentage={lockedPercentage}
|
||||||
/>
|
/>
|
||||||
<span className="tranches__progress-numbers">
|
<span className="tranches__progress-numbers">
|
||||||
@ -39,7 +40,7 @@ export const TrancheProgress = ({
|
|||||||
<span className="tranches__progress-title">{t('Redeemed')}</span>
|
<span className="tranches__progress-title">{t('Redeemed')}</span>
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
width={220}
|
width={220}
|
||||||
color={Colors.GREEN}
|
color={Colors.green.DEFAULT}
|
||||||
percentage={removedPercentage}
|
percentage={removedPercentage}
|
||||||
/>
|
/>
|
||||||
<span className="tranches__progress-numbers">
|
<span className="tranches__progress-numbers">
|
||||||
|
@ -13,10 +13,12 @@ import {
|
|||||||
YAxis,
|
YAxis,
|
||||||
} from 'recharts';
|
} from 'recharts';
|
||||||
|
|
||||||
import { Colors } from '../../config';
|
|
||||||
import { DATE_FORMAT_LONG } from '../../lib/date-formats';
|
import { DATE_FORMAT_LONG } from '../../lib/date-formats';
|
||||||
import data from './data.json';
|
import data from './data.json';
|
||||||
|
|
||||||
|
import { theme } from '@vegaprotocol/tailwindcss-config';
|
||||||
|
const Colors = theme.colors;
|
||||||
|
|
||||||
const ORDER = ['community', 'publicSale', 'earlyInvestors', 'team'];
|
const ORDER = ['community', 'publicSale', 'earlyInvestors', 'team'];
|
||||||
|
|
||||||
export const VestingChart = () => {
|
export const VestingChart = () => {
|
||||||
@ -31,10 +33,10 @@ export const VestingChart = () => {
|
|||||||
<AreaChart data={data}>
|
<AreaChart data={data}>
|
||||||
<defs>
|
<defs>
|
||||||
{[
|
{[
|
||||||
['pink', Colors.PINK],
|
['pink', Colors.pink],
|
||||||
['green', Colors.VEGA_GREEN],
|
['green', Colors.green.vega],
|
||||||
['orange', Colors.VEGA_ORANGE],
|
['orange', Colors.orange],
|
||||||
['yellow', Colors.VEGA_YELLOW],
|
['yellow', Colors.yellow.DEFAULT],
|
||||||
].map(([key, color]) => (
|
].map(([key, color]) => (
|
||||||
<linearGradient id={key} x1="0" y1="0" x2="0" y2="1">
|
<linearGradient id={key} x1="0" y1="0" x2="0" y2="1">
|
||||||
<stop offset="0%" stopColor={color} stopOpacity={0.85} />
|
<stop offset="0%" stopColor={color} stopOpacity={0.85} />
|
||||||
@ -43,7 +45,7 @@ export const VestingChart = () => {
|
|||||||
))}
|
))}
|
||||||
</defs>
|
</defs>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
contentStyle={{ backgroundColor: Colors.BLACK }}
|
contentStyle={{ backgroundColor: Colors.black.DEFAULT }}
|
||||||
separator=":"
|
separator=":"
|
||||||
formatter={(value: number) => {
|
formatter={(value: number) => {
|
||||||
return (
|
return (
|
||||||
@ -68,7 +70,7 @@ export const VestingChart = () => {
|
|||||||
value={t('VEGA').toString()}
|
value={t('VEGA').toString()}
|
||||||
position="left"
|
position="left"
|
||||||
offset={-5}
|
offset={-5}
|
||||||
fill={Colors.WHITE}
|
fill={Colors.white.DEFAULT}
|
||||||
/>
|
/>
|
||||||
</YAxis>
|
</YAxis>
|
||||||
<XAxis dataKey="date">
|
<XAxis dataKey="date">
|
||||||
@ -76,24 +78,24 @@ export const VestingChart = () => {
|
|||||||
value={t('date').toString()}
|
value={t('date').toString()}
|
||||||
position="bottom"
|
position="bottom"
|
||||||
offset={5}
|
offset={5}
|
||||||
fill={Colors.WHITE}
|
fill={Colors.white.DEFAULT}
|
||||||
/>
|
/>
|
||||||
</XAxis>
|
</XAxis>
|
||||||
<ReferenceLine
|
<ReferenceLine
|
||||||
x={currentDate}
|
x={currentDate}
|
||||||
stroke={Colors.WHITE}
|
stroke={Colors.white.DEFAULT}
|
||||||
strokeWidth={2}
|
strokeWidth={2}
|
||||||
label={{
|
label={{
|
||||||
position: 'right',
|
position: 'right',
|
||||||
value: currentDate,
|
value: currentDate,
|
||||||
fill: Colors.WHITE,
|
fill: Colors.white.DEFAULT,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Area
|
<Area
|
||||||
dot={false}
|
dot={false}
|
||||||
type="linear"
|
type="linear"
|
||||||
dataKey="team"
|
dataKey="team"
|
||||||
stroke={Colors.PINK}
|
stroke={Colors.pink}
|
||||||
fill="url(#pink)"
|
fill="url(#pink)"
|
||||||
yAxisId={0}
|
yAxisId={0}
|
||||||
strokeWidth={2}
|
strokeWidth={2}
|
||||||
@ -105,7 +107,7 @@ export const VestingChart = () => {
|
|||||||
dot={false}
|
dot={false}
|
||||||
type="monotone"
|
type="monotone"
|
||||||
dataKey="earlyInvestors"
|
dataKey="earlyInvestors"
|
||||||
stroke={Colors.VEGA_GREEN}
|
stroke={Colors.green.vega}
|
||||||
fill="url(#green)"
|
fill="url(#green)"
|
||||||
yAxisId={0}
|
yAxisId={0}
|
||||||
strokeWidth={2}
|
strokeWidth={2}
|
||||||
@ -117,7 +119,7 @@ export const VestingChart = () => {
|
|||||||
dot={false}
|
dot={false}
|
||||||
type="monotone"
|
type="monotone"
|
||||||
dataKey="publicSale"
|
dataKey="publicSale"
|
||||||
stroke={Colors.VEGA_YELLOW}
|
stroke={Colors.yellow.DEFAULT}
|
||||||
fill="url(#yellow)"
|
fill="url(#yellow)"
|
||||||
yAxisId={0}
|
yAxisId={0}
|
||||||
strokeWidth={2}
|
strokeWidth={2}
|
||||||
@ -129,7 +131,7 @@ export const VestingChart = () => {
|
|||||||
dot={false}
|
dot={false}
|
||||||
type="monotone"
|
type="monotone"
|
||||||
dataKey="community"
|
dataKey="community"
|
||||||
stroke={Colors.VEGA_ORANGE}
|
stroke={Colors.orange}
|
||||||
fill="url(#orange)"
|
fill="url(#orange)"
|
||||||
yAxisId={0}
|
yAxisId={0}
|
||||||
strokeWidth={2}
|
strokeWidth={2}
|
||||||
|
@ -71,8 +71,8 @@ export const FlashCell = memo(({ children, value }: FlashCellProps) => {
|
|||||||
if (value < previousValue) {
|
if (value < previousValue) {
|
||||||
ref.current?.animate(
|
ref.current?.animate(
|
||||||
[
|
[
|
||||||
{ color: theme.colors.vega.pink },
|
{ color: theme.colors.pink },
|
||||||
{ color: theme.colors.vega.pink, offset: 0.8 },
|
{ color: theme.colors.pink, offset: 0.8 },
|
||||||
{ color: 'inherit' },
|
{ color: 'inherit' },
|
||||||
],
|
],
|
||||||
FLASH_DURATION
|
FLASH_DURATION
|
||||||
@ -80,8 +80,8 @@ export const FlashCell = memo(({ children, value }: FlashCellProps) => {
|
|||||||
} else if (value > previousValue) {
|
} else if (value > previousValue) {
|
||||||
ref.current?.animate(
|
ref.current?.animate(
|
||||||
[
|
[
|
||||||
{ color: theme.colors.vega.green },
|
{ color: theme.colors.green.vega },
|
||||||
{ color: theme.colors.vega.green, offset: 0.8 },
|
{ color: theme.colors.green.vega, offset: 0.8 },
|
||||||
{ color: 'inherit' },
|
{ color: 'inherit' },
|
||||||
],
|
],
|
||||||
FLASH_DURATION
|
FLASH_DURATION
|
||||||
|
@ -10,6 +10,26 @@ module.exports = {
|
|||||||
colors: {
|
colors: {
|
||||||
transparent: 'transparent',
|
transparent: 'transparent',
|
||||||
current: 'currentColor',
|
current: 'currentColor',
|
||||||
|
bullish: '#26FF8A',
|
||||||
|
bearish: '#ED1515',
|
||||||
|
vega: {
|
||||||
|
yellow: '#EDFF22',
|
||||||
|
pink: '#FF2D5E',
|
||||||
|
green: '#00F780',
|
||||||
|
},
|
||||||
|
red: {
|
||||||
|
DEFAULT: '#ED1515',
|
||||||
|
transparent: 'rgba(255, 38, 65, 0.3)',
|
||||||
|
vega: '#FF261A',
|
||||||
|
},
|
||||||
|
green: {
|
||||||
|
DEFAULT: '#26FF8A',
|
||||||
|
transparent: 'rgba(38, 255, 138, 0.3)',
|
||||||
|
dark: '#246340',
|
||||||
|
vega: '#00F780',
|
||||||
|
},
|
||||||
|
text: '#C7C7C7',
|
||||||
|
deemphasise: '#8A9BA8',
|
||||||
white: {
|
white: {
|
||||||
DEFAULT: '#FFF',
|
DEFAULT: '#FFF',
|
||||||
'02': 'rgba(255, 255, 255, 0.02)',
|
'02': 'rgba(255, 255, 255, 0.02)',
|
||||||
@ -36,12 +56,12 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
blue: '#1DA2FB',
|
blue: '#1DA2FB',
|
||||||
coral: '#FF6057',
|
coral: '#FF6057',
|
||||||
vega: {
|
pink: '#FF2D5E',
|
||||||
yellow: '#EDFF22',
|
orange: '#D9822B',
|
||||||
pink: '#FF2D5E',
|
yellow: {
|
||||||
green: '#00F780',
|
DEFAULT: '#EDFF22',
|
||||||
|
dark: '#474B0A', // yellow 0.3 opacity on black
|
||||||
},
|
},
|
||||||
'vega-yellow-dark': '#474B0A', // yellow 0.3 opacity on black
|
|
||||||
intent: {
|
intent: {
|
||||||
danger: '#FF261A',
|
danger: '#FF261A',
|
||||||
warning: '#FF7A1A',
|
warning: '#FF7A1A',
|
||||||
|
@ -18,7 +18,7 @@ const vegaCustomClasses = plugin(function ({ addUtilities }) {
|
|||||||
},
|
},
|
||||||
'.dark .syntax-highlighter-wrapper .hljs': {
|
'.dark .syntax-highlighter-wrapper .hljs': {
|
||||||
background: '#2C2C2C',
|
background: '#2C2C2C',
|
||||||
color: theme.colors.vega.green,
|
color: theme.colors.vega.green.DEFAULT,
|
||||||
},
|
},
|
||||||
'.syntax-highlighter-wrapper .hljs-literal': {
|
'.syntax-highlighter-wrapper .hljs-literal': {
|
||||||
color: theme.colors.vega.pink,
|
color: theme.colors.vega.pink,
|
||||||
|
@ -9,7 +9,7 @@ const agGridLightVariables = `
|
|||||||
--ag-header-background-color: ${theme.colors.white[100]};
|
--ag-header-background-color: ${theme.colors.white[100]};
|
||||||
--ag-odd-row-background-color: ${theme.colors.white[100]};
|
--ag-odd-row-background-color: ${theme.colors.white[100]};
|
||||||
--ag-row-border-color: ${theme.colors.white[100]};
|
--ag-row-border-color: ${theme.colors.white[100]};
|
||||||
--ag-row-hover-color: ${theme.colors.vega.yellow};
|
--ag-row-hover-color: ${theme.colors.yellow.DEFAULT};
|
||||||
--ag-font-size: 12px;
|
--ag-font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ const getClasses = (
|
|||||||
standardButtonPaddingRight,
|
standardButtonPaddingRight,
|
||||||
standardButtonBorderWidth,
|
standardButtonBorderWidth,
|
||||||
buttonHeight,
|
buttonHeight,
|
||||||
'bg-vega-yellow dark:bg-vega-yellow hover:bg-vega-yellow-dark dark:hover:bg-vega-yellow/30 active:bg-white dark:active:bg-black',
|
'bg-vega-yellow dark:bg-vega-yellow hover:bg-yellow/dark dark:hover:bg-vega-yellow/30 active:bg-white dark:active:bg-black',
|
||||||
'text-ui uppercase text-black dark:text-black hover:text-white dark:hover:text-white active:text-black dark:active:text-white',
|
'text-ui uppercase text-black dark:text-black hover:text-white dark:hover:text-white active:text-black dark:active:text-white',
|
||||||
'border-transparent dark:border-transparent',
|
'border-transparent dark:border-transparent',
|
||||||
];
|
];
|
||||||
|
@ -22,3 +22,4 @@ export * from './toggle';
|
|||||||
export * from './tooltip';
|
export * from './tooltip';
|
||||||
export * from './vega-logo';
|
export * from './vega-logo';
|
||||||
export * from './syntax-highlighter';
|
export * from './syntax-highlighter';
|
||||||
|
export * from './sparkline';
|
||||||
|
1
libs/ui-toolkit/src/components/sparkline/index.ts
Normal file
1
libs/ui-toolkit/src/components/sparkline/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './sparkline';
|
73
libs/ui-toolkit/src/components/sparkline/sparkline.spec.tsx
Normal file
73
libs/ui-toolkit/src/components/sparkline/sparkline.spec.tsx
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import { theme } from '@vegaprotocol/tailwindcss-config';
|
||||||
|
|
||||||
|
import { Sparkline } from './sparkline';
|
||||||
|
export const Colors = theme.colors;
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
data: [
|
||||||
|
1, 2, 3, 4, 5, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 6, 7, 8, 9,
|
||||||
|
10, 11, 12,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
it('Renders an svg with a single path', () => {
|
||||||
|
render(<Sparkline {...props} />);
|
||||||
|
expect(screen.getByTestId('sparkline-svg')).toBeInTheDocument();
|
||||||
|
const paths = screen.getAllByTestId('sparkline-path');
|
||||||
|
const path = paths[0];
|
||||||
|
expect(path).toBeInTheDocument();
|
||||||
|
expect(path).toHaveAttribute('d', expect.any(String));
|
||||||
|
expect(path).toHaveAttribute('stroke', expect.any(String));
|
||||||
|
expect(path).toHaveAttribute('stroke-width', '2');
|
||||||
|
expect(path).toHaveAttribute('fill', 'transparent');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Requires a data prop but width and height are optional', () => {
|
||||||
|
render(<Sparkline {...props} />);
|
||||||
|
const svg = screen.getByTestId('sparkline-svg');
|
||||||
|
expect(svg).toHaveAttribute('width', '60');
|
||||||
|
expect(svg).toHaveAttribute('height', '15');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders a red line if the last value is less than the first', () => {
|
||||||
|
props.data[0] = 10;
|
||||||
|
props.data[props.data.length - 1] = 5;
|
||||||
|
render(<Sparkline {...props} />);
|
||||||
|
const paths = screen.getAllByTestId('sparkline-path');
|
||||||
|
const path = paths[0];
|
||||||
|
expect(path).toHaveClass('stroke-bearish');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders a green line if the last value is greater than the first', () => {
|
||||||
|
props.data[0] = 5;
|
||||||
|
props.data[props.data.length - 1] = 10;
|
||||||
|
render(<Sparkline {...props} />);
|
||||||
|
const paths = screen.getAllByTestId('sparkline-path');
|
||||||
|
const path = paths[0];
|
||||||
|
expect(path).toHaveClass('stroke-bullish');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders a white line if the first and last values are equal', () => {
|
||||||
|
props.data[0] = 5;
|
||||||
|
props.data[props.data.length - 1] = 5;
|
||||||
|
render(<Sparkline {...props} />);
|
||||||
|
const paths = screen.getAllByTestId('sparkline-path');
|
||||||
|
const path = paths[0];
|
||||||
|
expect(path).toHaveClass(
|
||||||
|
'[vector-effect:non-scaling-stroke] stroke-black/40 dark:stroke-white/40'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders a gray line if there are not 24 values', () => {
|
||||||
|
props.data = [
|
||||||
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
|
||||||
|
22, 23,
|
||||||
|
];
|
||||||
|
render(<Sparkline {...props} />);
|
||||||
|
const paths = screen.queryAllByTestId('sparkline-path');
|
||||||
|
expect(paths).toHaveLength(2);
|
||||||
|
expect(paths[0]).toHaveClass(
|
||||||
|
'[vector-effect:non-scaling-stroke] stroke-black/40 dark:stroke-white/40'
|
||||||
|
);
|
||||||
|
});
|
@ -0,0 +1,56 @@
|
|||||||
|
import type { Story, Meta } from '@storybook/react';
|
||||||
|
import { Sparkline } from './sparkline';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
component: Sparkline,
|
||||||
|
title: 'Sparkline',
|
||||||
|
} as Meta;
|
||||||
|
|
||||||
|
const Template: Story = (args) => <Sparkline data={args['data']} {...args} />;
|
||||||
|
|
||||||
|
export const Grey = Template.bind({});
|
||||||
|
Grey.args = {
|
||||||
|
data: [
|
||||||
|
1, 2, 3, 4, 5, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 6, 7, 8,
|
||||||
|
],
|
||||||
|
width: 60,
|
||||||
|
height: 30,
|
||||||
|
points: 25,
|
||||||
|
className: 'w-[113px]',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Equal = Template.bind({});
|
||||||
|
Equal.args = {
|
||||||
|
data: [
|
||||||
|
12, 2, 3, 4, 5, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 6, 7, 8, 9,
|
||||||
|
10, 11, 12,
|
||||||
|
],
|
||||||
|
width: 60,
|
||||||
|
height: 30,
|
||||||
|
points: 25,
|
||||||
|
className: 'w-[113px]',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Increase = Template.bind({});
|
||||||
|
Increase.args = {
|
||||||
|
data: [
|
||||||
|
1, 2, 3, 4, 5, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 6, 7, 8, 9,
|
||||||
|
10, 11, 12,
|
||||||
|
],
|
||||||
|
width: 60,
|
||||||
|
height: 30,
|
||||||
|
points: 25,
|
||||||
|
className: 'w-[113px]',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Decrease = Template.bind({});
|
||||||
|
Decrease.args = {
|
||||||
|
data: [
|
||||||
|
12, 2, 3, 4, 5, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 6, 7, 8, 9,
|
||||||
|
10, 11, 1,
|
||||||
|
],
|
||||||
|
width: 60,
|
||||||
|
height: 30,
|
||||||
|
points: 25,
|
||||||
|
className: 'w-[113px]',
|
||||||
|
};
|
132
libs/ui-toolkit/src/components/sparkline/sparkline.tsx
Normal file
132
libs/ui-toolkit/src/components/sparkline/sparkline.tsx
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
import { extent } from 'd3-array';
|
||||||
|
import { scaleLinear } from 'd3-scale';
|
||||||
|
import { line } from 'd3-shape';
|
||||||
|
import isEqual from 'lodash/isEqual';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function colorByChange(a: number, b: number) {
|
||||||
|
return a === b
|
||||||
|
? 'stroke-black/40 dark:stroke-white/40'
|
||||||
|
: a < b
|
||||||
|
? 'stroke-bullish'
|
||||||
|
: 'stroke-bearish';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SparklineProps {
|
||||||
|
data: number[];
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
points?: number;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SparklineView = ({
|
||||||
|
data,
|
||||||
|
width = 60,
|
||||||
|
height = 15,
|
||||||
|
points = 25,
|
||||||
|
className,
|
||||||
|
}: SparklineProps) => {
|
||||||
|
// How many points are missing. If market is 12 hours old the 25 - 12
|
||||||
|
const preMarketLength = points - data.length;
|
||||||
|
|
||||||
|
// Create two dimensional array for sparkline points [x, y]
|
||||||
|
const marketData: [number, number][] = data.map((d, i) => [
|
||||||
|
preMarketLength + i,
|
||||||
|
d,
|
||||||
|
]);
|
||||||
|
// Empty two dimensional array for gray, 'no data' line
|
||||||
|
let preMarketData: [number, number][] = [];
|
||||||
|
|
||||||
|
// Get the extent for our y value
|
||||||
|
const [min, max] = extent(marketData, (d) => d[1]);
|
||||||
|
|
||||||
|
if (typeof min !== 'number' || typeof max !== 'number') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a second set of data to render a gray line for any
|
||||||
|
// missing points if the market is less than 24 hours old
|
||||||
|
if (marketData.length < points) {
|
||||||
|
// Populate preMarketData with the average of our extents
|
||||||
|
// so that the line renders centered vertically
|
||||||
|
const fillValue = (min + max) / 2;
|
||||||
|
preMarketData = new Array(points - marketData.length)
|
||||||
|
.fill(fillValue)
|
||||||
|
.map((d: number, i) => [i, d] as [number, number]);
|
||||||
|
|
||||||
|
// Add the first point of or market data so that the two
|
||||||
|
// lines join up
|
||||||
|
preMarketData.push(marketData[0] as [number, number]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const xScale = scaleLinear().domain([0, points]).range([0, 100]);
|
||||||
|
const yScale = scaleLinear().domain([min, max]).range([100, 0]);
|
||||||
|
|
||||||
|
const lineSeries = line()
|
||||||
|
.x((d) => xScale(d[0]))
|
||||||
|
.y((d) => yScale(d[1]));
|
||||||
|
|
||||||
|
// Get the color of the marketData line
|
||||||
|
const [firstVal, lastVal] = [data[0], data[data.length - 1]];
|
||||||
|
const strokeClassName =
|
||||||
|
data.length >= 24
|
||||||
|
? colorByChange(firstVal, lastVal)
|
||||||
|
: 'stroke-black/40 dark:stroke-white/40';
|
||||||
|
|
||||||
|
// Create paths
|
||||||
|
const preMarketCreationPath = lineSeries(preMarketData);
|
||||||
|
const mainPath = lineSeries(marketData);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
data-testid="sparkline-svg"
|
||||||
|
className={`pt-px pr-0 w-full overflow-visible ${className}`}
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
viewBox="0 0 100 100"
|
||||||
|
preserveAspectRatio="none"
|
||||||
|
>
|
||||||
|
{preMarketCreationPath && (
|
||||||
|
<path
|
||||||
|
data-testid="sparkline-path"
|
||||||
|
className={`[vector-effect:non-scaling-stroke] ${strokeClassName}`}
|
||||||
|
d={preMarketCreationPath}
|
||||||
|
stroke="strokeCurrent"
|
||||||
|
strokeWidth={2}
|
||||||
|
fill="transparent"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{mainPath && (
|
||||||
|
<path
|
||||||
|
data-testid="sparkline-path"
|
||||||
|
d={mainPath}
|
||||||
|
className={strokeClassName}
|
||||||
|
stroke="strokeCurrent"
|
||||||
|
strokeWidth={1}
|
||||||
|
fill="transparent"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
SparklineView.displayName = 'SparklineView';
|
||||||
|
|
||||||
|
// Use react memo to only re-render if props change
|
||||||
|
export const Sparkline = React.memo(
|
||||||
|
SparklineView,
|
||||||
|
function (prevProps, nextProps) {
|
||||||
|
// Warning! The return value here is the opposite of shouldComponentUpdate.
|
||||||
|
// Return true if you DON NOT want a re-render
|
||||||
|
if (
|
||||||
|
prevProps.width !== nextProps.width ||
|
||||||
|
prevProps.height !== nextProps.height
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return isEqual(prevProps.data, nextProps.data);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Sparkline.displayName = 'Sparkline';
|
@ -3,7 +3,7 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "../../dist/out-tsc",
|
"outDir": "../../dist/out-tsc",
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"types": ["jest", "node", "@testing-libary/jest-dom"]
|
"types": ["jest", "node", "@testing-library/jest-dom"]
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"**/*.test.ts",
|
"**/*.test.ts",
|
||||||
|
Loading…
Reference in New Issue
Block a user