refactor(ui-toolkit): move healthbar to ui-toolkit (#3121)
This commit is contained in:
parent
4cbd7a4928
commit
74a3aa8566
@ -18,6 +18,7 @@ import type * as Schema from '@vegaprotocol/types';
|
||||
import {
|
||||
AsyncRenderer,
|
||||
Icon,
|
||||
HealthBar,
|
||||
TooltipCellComponent,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import type { GetRowIdParams, RowClickedEvent } from 'ag-grid-community';
|
||||
@ -27,9 +28,9 @@ import { AgGridColumn } from 'ag-grid-react';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
import { Grid } from '../../grid';
|
||||
import { HealthBar } from '../../health-bar';
|
||||
import { HealthDialog } from '../../health-dialog';
|
||||
import { Status } from '../../status';
|
||||
import { intentForStatus } from '../../../lib/utils';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
|
||||
export const MarketList = () => {
|
||||
@ -273,13 +274,13 @@ export const MarketList = () => {
|
||||
data: Market;
|
||||
}) => (
|
||||
<HealthBar
|
||||
status={value}
|
||||
target={data.target}
|
||||
decimals={
|
||||
data.tradableInstrument.instrument.product.settlementAsset
|
||||
.decimals
|
||||
}
|
||||
levels={data.feeLevels}
|
||||
intent={intentForStatus(value)}
|
||||
/>
|
||||
)}
|
||||
sortable={false}
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { useState } from 'react';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Icon } from '@vegaprotocol/ui-toolkit';
|
||||
import { Icon, HealthBar } from '@vegaprotocol/ui-toolkit';
|
||||
import { formatWithAsset } from '@vegaprotocol/liquidity';
|
||||
|
||||
import type * as Schema from '@vegaprotocol/types';
|
||||
import { HealthBar } from '../../health-bar';
|
||||
import { HealthDialog } from '../../health-dialog';
|
||||
import { Last24hVolume } from '../last-24h-volume';
|
||||
import { Status } from '../../status';
|
||||
import { intentForStatus } from '../../../lib/utils';
|
||||
|
||||
interface Levels {
|
||||
fee: string;
|
||||
@ -92,10 +92,10 @@ export const Market = ({
|
||||
<td className="px-4">
|
||||
{tradingMode && settlementAsset?.decimals && feeLevels && (
|
||||
<HealthBar
|
||||
status={tradingMode}
|
||||
target={targetStake}
|
||||
decimals={settlementAsset.decimals}
|
||||
levels={feeLevels}
|
||||
intent={intentForStatus(tradingMode)}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
|
@ -1,213 +0,0 @@
|
||||
import classNames from 'classnames';
|
||||
import type * as Schema from '@vegaprotocol/types';
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { BigNumber } from 'bignumber.js';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
import { getColorForStatus } from '../../lib/utils';
|
||||
|
||||
import { Indicator } from '../indicator';
|
||||
|
||||
const Remainder = () => (
|
||||
<div className="bg-greys-light-200 h-[inherit] relative flex-1"></div>
|
||||
);
|
||||
|
||||
const COPY_CLASS =
|
||||
'text-sm font-medium whitespace-nowrap text-white font-alpha calt';
|
||||
|
||||
const Tooltip = ({
|
||||
children,
|
||||
isExpanded,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
isExpanded: boolean;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'absolute top-0 left-1/2 -translate-x-2/4 -translate-y-[80%] p-2 z-10 bg-greys-light-400 group-hover:flex rounded',
|
||||
{
|
||||
flex: isExpanded,
|
||||
hidden: !isExpanded,
|
||||
}
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Target = ({
|
||||
targetPercent,
|
||||
isLarge,
|
||||
children,
|
||||
}: {
|
||||
targetPercent: number;
|
||||
isLarge: boolean;
|
||||
children: ReactNode;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'absolute top-1/2 left-1/2 -translate-x-2/4 -translate-y-1/2 px-1.5 group'
|
||||
)}
|
||||
style={{ left: `${targetPercent}%` }}
|
||||
>
|
||||
<div
|
||||
className={classNames(
|
||||
'health-target w-0.5 bg-black group-hover:scale-x-150 group-hover:scale-y-108',
|
||||
{
|
||||
'h-6': !isLarge,
|
||||
'h-12': isLarge,
|
||||
}
|
||||
)}
|
||||
></div>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Level = ({
|
||||
children,
|
||||
commitmentAmount,
|
||||
total,
|
||||
backgroundColor,
|
||||
opacity,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
commitmentAmount: number;
|
||||
total: number;
|
||||
backgroundColor: string;
|
||||
opacity: number;
|
||||
}) => {
|
||||
const width = new BigNumber(commitmentAmount)
|
||||
.div(total)
|
||||
.multipliedBy(100)
|
||||
.toNumber();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(`relative h-[inherit] w-full group min-w-[1px]`)}
|
||||
style={{
|
||||
width: `${width}%`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="relative w-full h-[inherit] group-hover:scale-y-150"
|
||||
style={{
|
||||
opacity,
|
||||
backgroundColor,
|
||||
}}
|
||||
></div>
|
||||
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Full = () => (
|
||||
<div className="bg-transparent w-full h-[inherit] absolute bottom-0 left-0"></div>
|
||||
);
|
||||
|
||||
interface Levels {
|
||||
fee: string;
|
||||
commitmentAmount: number;
|
||||
}
|
||||
|
||||
export const HealthBar = ({
|
||||
status,
|
||||
target = '0',
|
||||
decimals,
|
||||
levels,
|
||||
size = 'small',
|
||||
isExpanded = false,
|
||||
}: {
|
||||
status: Schema.MarketTradingMode;
|
||||
target: string;
|
||||
decimals: number;
|
||||
levels: Levels[];
|
||||
isExpanded?: boolean;
|
||||
size?: 'small' | 'large';
|
||||
}) => {
|
||||
const targetNumber = parseInt(target, 10);
|
||||
|
||||
const committedNumber = levels
|
||||
.reduce((total, current) => {
|
||||
return total.plus(current.commitmentAmount);
|
||||
}, new BigNumber(0))
|
||||
.toNumber();
|
||||
|
||||
const total =
|
||||
targetNumber * 2 >= committedNumber ? targetNumber * 2 : committedNumber;
|
||||
const targetPercent = (targetNumber / total) * 100;
|
||||
const isLarge = size === 'large';
|
||||
const backgroundColor = getColorForStatus(status);
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div
|
||||
className={classNames('health-wrapper relative', {
|
||||
'py-2': !isLarge,
|
||||
'py-5': isLarge,
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className={classNames('health-inner relative w-full flex', {
|
||||
'h-4': !isLarge,
|
||||
'h-8': isLarge,
|
||||
})}
|
||||
>
|
||||
<Full />
|
||||
|
||||
<div className="health-bars h-[inherit] flex w-full gap-0.5">
|
||||
{levels.map((p, index) => {
|
||||
const { commitmentAmount, fee } = p;
|
||||
const prevLevel = levels[index - 1]?.commitmentAmount;
|
||||
const opacity = 1 - 0.2 * index;
|
||||
return (
|
||||
<Level
|
||||
commitmentAmount={commitmentAmount}
|
||||
total={total}
|
||||
backgroundColor={backgroundColor}
|
||||
opacity={opacity}
|
||||
>
|
||||
<Tooltip isExpanded={isExpanded}>
|
||||
<div className="mt-1.5 inline-flex">
|
||||
<Indicator status={status} opacity={opacity} />
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className={COPY_CLASS}>
|
||||
{fee}% {t('Fee')}
|
||||
</span>
|
||||
<span className={classNames(COPY_CLASS, 'opacity-60')}>
|
||||
{prevLevel
|
||||
? addDecimalsFormatNumber(prevLevel, decimals)
|
||||
: '0'}{' '}
|
||||
- {addDecimalsFormatNumber(commitmentAmount, decimals)}
|
||||
</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</Level>
|
||||
);
|
||||
})}
|
||||
{(total !== committedNumber || levels.length === 0) && (
|
||||
<Remainder />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Target targetPercent={targetPercent} isLarge={isLarge}>
|
||||
<Tooltip isExpanded={isExpanded}>
|
||||
<div className="mt-1.5 inline-flex">
|
||||
<Indicator />
|
||||
</div>
|
||||
<span className={COPY_CLASS}>
|
||||
{t('Target stake')} {addDecimalsFormatNumber(target, decimals)}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</Target>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1 +0,0 @@
|
||||
export * from './health-bar';
|
@ -1,9 +1,8 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Dialog } from '@vegaprotocol/ui-toolkit';
|
||||
import { Dialog, HealthBar } from '@vegaprotocol/ui-toolkit';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { HealthBar } from '../health-bar';
|
||||
import { intentForStatus } from '../../lib/utils';
|
||||
|
||||
interface HealthDialogProps {
|
||||
isOpen: boolean;
|
||||
@ -96,9 +95,9 @@ export const HealthDialog = ({ onChange, isOpen }: HealthDialogProps) => {
|
||||
<HealthBar
|
||||
size="large"
|
||||
levels={r.data.levels}
|
||||
status={r.data.status}
|
||||
target={r.data.target}
|
||||
decimals={r.data.decimals}
|
||||
intent={intentForStatus(r.data.status)}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -1,4 +1,5 @@
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { Intent } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
const marketTradingModeStyle = {
|
||||
[Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS]: '#00D46E',
|
||||
@ -10,3 +11,15 @@ const marketTradingModeStyle = {
|
||||
|
||||
export const getColorForStatus = (status: Schema.MarketTradingMode) =>
|
||||
marketTradingModeStyle[status];
|
||||
|
||||
const marketTradingModeIntent = {
|
||||
[Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS]: Intent.Success,
|
||||
[Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION]: Intent.Danger,
|
||||
[Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION]: Intent.Primary,
|
||||
[Schema.MarketTradingMode.TRADING_MODE_BATCH_AUCTION]: Intent.Danger,
|
||||
[Schema.MarketTradingMode.TRADING_MODE_NO_TRADING]: Intent.Danger,
|
||||
};
|
||||
|
||||
export const intentForStatus = (status: Schema.MarketTradingMode) => {
|
||||
return marketTradingModeIntent[status];
|
||||
};
|
||||
|
203
libs/ui-toolkit/src/components/healthbar/healthbar.tsx
Normal file
203
libs/ui-toolkit/src/components/healthbar/healthbar.tsx
Normal file
@ -0,0 +1,203 @@
|
||||
import classNames from 'classnames';
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { BigNumber } from 'bignumber.js';
|
||||
import { getIntentBackground, Intent } from '../../utils/intent';
|
||||
import { Indicator } from '../indicator';
|
||||
import { Tooltip } from '../tooltip';
|
||||
|
||||
const Remainder = () => (
|
||||
<div className="bg-greys-light-200 h-[inherit] relative flex-1" />
|
||||
);
|
||||
|
||||
const Target = ({
|
||||
target,
|
||||
decimals,
|
||||
isLarge,
|
||||
}: {
|
||||
isLarge: boolean;
|
||||
target: string;
|
||||
decimals: number;
|
||||
}) => {
|
||||
return (
|
||||
<Tooltip
|
||||
description={
|
||||
<>
|
||||
<div className="mt-1.5 inline-flex">
|
||||
<Indicator variant={Intent.None} />
|
||||
</div>
|
||||
<span>
|
||||
{t('Target stake')} {addDecimalsFormatNumber(target, decimals)}
|
||||
</span>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={classNames(
|
||||
'absolute top-1/2 left-1/2 -translate-x-2/4 -translate-y-1/2 px-1.5 group'
|
||||
)}
|
||||
style={{ left: '50%' }}
|
||||
>
|
||||
<div
|
||||
className={classNames(
|
||||
'health-target w-0.5 bg-black group-hover:scale-x-150 group-hover:scale-y-108',
|
||||
{
|
||||
'h-6': !isLarge,
|
||||
'h-12': isLarge,
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const Level = ({
|
||||
commitmentAmount,
|
||||
rangeLimit,
|
||||
opacity,
|
||||
fee,
|
||||
prevLevel,
|
||||
decimals,
|
||||
intent,
|
||||
}: {
|
||||
commitmentAmount: number;
|
||||
rangeLimit: number;
|
||||
opacity: number;
|
||||
fee: string;
|
||||
prevLevel: number;
|
||||
decimals: number;
|
||||
intent: Intent;
|
||||
}) => {
|
||||
const width = new BigNumber(commitmentAmount)
|
||||
.div(rangeLimit)
|
||||
.multipliedBy(100)
|
||||
.toNumber();
|
||||
|
||||
const tooltipContent = (
|
||||
<>
|
||||
<div className="mt-1.5 inline-flex">
|
||||
<Indicator variant={intent} />
|
||||
</div>
|
||||
<span>
|
||||
{fee}% {t('Fee')}
|
||||
</span>
|
||||
<div className="flex flex-col">
|
||||
<span>
|
||||
{prevLevel ? addDecimalsFormatNumber(prevLevel, decimals) : '0'} -{' '}
|
||||
{addDecimalsFormatNumber(commitmentAmount, decimals)}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<Tooltip description={tooltipContent}>
|
||||
<div
|
||||
className={classNames(`relative h-[inherit] w-full group min-w-[1px]`)}
|
||||
style={{
|
||||
width: `${width}%`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={classNames(
|
||||
'relative w-full h-[inherit] group-hover:scale-y-150',
|
||||
getIntentBackground(intent)
|
||||
)}
|
||||
style={{ opacity }}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const Full = () => (
|
||||
<div className="bg-transparent w-full h-[inherit] absolute bottom-0 left-0" />
|
||||
);
|
||||
|
||||
interface Levels {
|
||||
fee: string;
|
||||
commitmentAmount: number;
|
||||
}
|
||||
|
||||
export const HealthBar = ({
|
||||
target = '0',
|
||||
decimals,
|
||||
levels,
|
||||
size = 'small',
|
||||
intent,
|
||||
}: {
|
||||
target: string;
|
||||
decimals: number;
|
||||
levels: Levels[];
|
||||
size?: 'small' | 'large';
|
||||
intent: Intent;
|
||||
}) => {
|
||||
const targetNumber = parseInt(target, 10);
|
||||
const rangeLimit = targetNumber * 2;
|
||||
|
||||
let lastVisibleLevel = 0;
|
||||
const committedNumber = levels
|
||||
.reduce((total, current, index) => {
|
||||
const newTotal = total.plus(current.commitmentAmount);
|
||||
if (total.isLessThan(rangeLimit) && newTotal.isGreaterThan(rangeLimit)) {
|
||||
lastVisibleLevel = index;
|
||||
}
|
||||
return newTotal;
|
||||
}, new BigNumber(0))
|
||||
.toNumber();
|
||||
|
||||
const isLarge = size === 'large';
|
||||
const showRemainder = committedNumber < rangeLimit || levels.length === 0;
|
||||
const showOverflow = !showRemainder && lastVisibleLevel < levels.length - 1;
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div
|
||||
className={classNames('health-wrapper relative', {
|
||||
'py-2': !isLarge,
|
||||
'py-5': isLarge,
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className={classNames('health-inner relative w-full flex', {
|
||||
'h-4': !isLarge,
|
||||
'h-8': isLarge,
|
||||
})}
|
||||
>
|
||||
<Full />
|
||||
|
||||
<div className="health-bars h-[inherit] flex w-full gap-0.5">
|
||||
{levels.map((p, index) => {
|
||||
const { commitmentAmount, fee } = p;
|
||||
const prevLevel = levels[index - 1]?.commitmentAmount;
|
||||
const opacity = 1 - 0.2 * index;
|
||||
return index <= lastVisibleLevel ? (
|
||||
<Level
|
||||
commitmentAmount={commitmentAmount}
|
||||
rangeLimit={rangeLimit}
|
||||
opacity={opacity}
|
||||
fee={fee}
|
||||
prevLevel={prevLevel}
|
||||
decimals={0}
|
||||
intent={intent}
|
||||
/>
|
||||
) : null;
|
||||
})}
|
||||
{showRemainder && <Remainder />}
|
||||
{showOverflow && (
|
||||
<Tooltip
|
||||
description={t(
|
||||
'Providers greater than 2x target stake not shown'
|
||||
)}
|
||||
>
|
||||
<div className="h-[inherit] relative flex-1 leading-4">...</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<Target isLarge={isLarge} target={target} decimals={decimals} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
1
libs/ui-toolkit/src/components/healthbar/index.ts
Normal file
1
libs/ui-toolkit/src/components/healthbar/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './healthbar';
|
@ -46,3 +46,4 @@ export * from './traffic-light';
|
||||
export * from './vega-icons';
|
||||
export * from './vega-logo';
|
||||
export * from './viewing-as-user';
|
||||
export * from './healthbar';
|
||||
|
Loading…
Reference in New Issue
Block a user