feat(trading): mobile responsiveness tweaks (#5613)
This commit is contained in:
parent
d32f27fcb1
commit
b751bcf17d
@ -137,6 +137,8 @@ const ClosedMarketsDataGrid = ({
|
||||
headerName: t('Market'),
|
||||
field: 'code',
|
||||
cellRenderer: 'MarketCodeCell',
|
||||
width: 150,
|
||||
resizable: true,
|
||||
},
|
||||
{
|
||||
headerName: t('Status'),
|
||||
@ -280,6 +282,7 @@ const ClosedMarketsDataGrid = ({
|
||||
return (
|
||||
<AgGrid
|
||||
rowData={rowData}
|
||||
defaultColDef={COL_DEFS.default}
|
||||
columnDefs={colDefs}
|
||||
getRowId={({ data }) => data.id}
|
||||
overlayNoRowsTemplate={error ? error.message : t('No markets')}
|
||||
|
@ -17,6 +17,7 @@ const defaultColDef = {
|
||||
filter: true,
|
||||
resizable: true,
|
||||
filterParams: { buttons: ['reset'] },
|
||||
minWidth: 120,
|
||||
};
|
||||
|
||||
const components = {
|
||||
|
@ -53,6 +53,7 @@ export const MarketsPage = () => {
|
||||
size="extra-small"
|
||||
data-testid="propose-new-market"
|
||||
href={externalLink}
|
||||
target="_blank"
|
||||
>
|
||||
{t('Propose a new market')}
|
||||
</TradingAnchorButton>
|
||||
|
@ -29,6 +29,7 @@ export const useColumnDefs = () => {
|
||||
{
|
||||
headerName: t('Market'),
|
||||
field: 'tradableInstrument.instrument.code',
|
||||
pinned: true,
|
||||
cellRenderer: ({
|
||||
value,
|
||||
data,
|
||||
|
@ -67,6 +67,7 @@ const defaultColDef = {
|
||||
sortable: true,
|
||||
tooltipComponent: TooltipCellComponent,
|
||||
comparator: accountValuesComparator,
|
||||
minWidth: 150,
|
||||
};
|
||||
export interface GetRowsParams extends Omit<IGetRowsParams, 'successCallback'> {
|
||||
successCallback(rowsThisBlock: AccountFields[], lastRow?: number): void;
|
||||
@ -139,6 +140,8 @@ export const AccountTable = ({
|
||||
{
|
||||
headerName: t('Asset'),
|
||||
field: 'asset.symbol',
|
||||
pinned: true,
|
||||
minWidth: 75,
|
||||
headerTooltip: t(
|
||||
'Asset is the collateral that is deposited into the Vega protocol.'
|
||||
),
|
||||
@ -253,8 +256,8 @@ export const AccountTable = ({
|
||||
colId: 'accounts-actions',
|
||||
field: 'asset.id',
|
||||
...COL_DEFS.actions,
|
||||
minWidth: showDepositButton ? 105 : COL_DEFS.actions.minWidth,
|
||||
maxWidth: showDepositButton ? 105 : COL_DEFS.actions.maxWidth,
|
||||
minWidth: showDepositButton ? 110 : COL_DEFS.actions.minWidth,
|
||||
maxWidth: showDepositButton ? 110 : COL_DEFS.actions.maxWidth,
|
||||
cellRenderer: ({
|
||||
value: assetId,
|
||||
node,
|
||||
|
@ -23,7 +23,9 @@ import { AccountType } from '@vegaprotocol/types';
|
||||
const defaultColDef = {
|
||||
resizable: true,
|
||||
sortable: true,
|
||||
minWidth: 100,
|
||||
};
|
||||
|
||||
interface BreakdownTableProps extends AgGridReactProps {
|
||||
data: AccountFields[] | null;
|
||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||
@ -32,12 +34,13 @@ interface BreakdownTableProps extends AgGridReactProps {
|
||||
const BreakdownTable = forwardRef<AgGridReact, BreakdownTableProps>(
|
||||
({ data }, ref) => {
|
||||
const t = useT();
|
||||
const coldefs = useMemo(() => {
|
||||
const colDefs = useMemo(() => {
|
||||
const defs: ColDef[] = [
|
||||
{
|
||||
headerName: t('Market'),
|
||||
field: 'market.tradableInstrument.instrument.code',
|
||||
minWidth: 200,
|
||||
width: 90,
|
||||
pinned: true,
|
||||
sort: 'desc',
|
||||
cellRenderer: ({
|
||||
value,
|
||||
@ -141,7 +144,7 @@ const BreakdownTable = forwardRef<AgGridReact, BreakdownTableProps>(
|
||||
components={{ PriceCell, ProgressBarCell }}
|
||||
tooltipShowDelay={500}
|
||||
defaultColDef={defaultColDef}
|
||||
columnDefs={coldefs}
|
||||
columnDefs={colDefs}
|
||||
domLayout="autoHeight"
|
||||
/>
|
||||
);
|
||||
|
@ -9,4 +9,7 @@ export const COL_DEFS = {
|
||||
type: 'rightAligned',
|
||||
pinned: 'right' as const,
|
||||
},
|
||||
default: {
|
||||
minWidth: 100,
|
||||
},
|
||||
};
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
isNumeric,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { type ColDef } from 'ag-grid-community';
|
||||
import { AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { AgGrid, COL_DEFS } from '@vegaprotocol/datagrid';
|
||||
import {
|
||||
type VegaICellRendererParams,
|
||||
type VegaValueFormatterParams,
|
||||
@ -21,7 +21,7 @@ export const DepositsTable = (
|
||||
) => {
|
||||
const columnDefs = useMemo<ColDef[]>(
|
||||
() => [
|
||||
{ headerName: 'Asset', field: 'asset.symbol' },
|
||||
{ headerName: 'Asset', field: 'asset.symbol', pinned: true },
|
||||
{
|
||||
headerName: 'Amount',
|
||||
field: 'amount',
|
||||
@ -74,5 +74,11 @@ export const DepositsTable = (
|
||||
],
|
||||
[]
|
||||
);
|
||||
return <AgGrid columnDefs={columnDefs} {...props} />;
|
||||
return (
|
||||
<AgGrid
|
||||
columnDefs={columnDefs}
|
||||
defaultColDef={COL_DEFS.default}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -48,6 +48,7 @@ export const FillsTable = forwardRef<AgGridReact, Props>(
|
||||
field: 'market.tradableInstrument.instrument.code',
|
||||
cellRenderer: 'MarketNameCell',
|
||||
cellRendererParams: { idPath: 'market.id', onMarketClick },
|
||||
pinned: true,
|
||||
},
|
||||
{
|
||||
headerName: t('Size'),
|
||||
@ -143,6 +144,7 @@ export const FillsTable = forwardRef<AgGridReact, Props>(
|
||||
<AgGrid
|
||||
ref={ref}
|
||||
columnDefs={columnDefs}
|
||||
defaultColDef={COL_DEFS.default}
|
||||
overlayNoRowsTemplate={t('No fills')}
|
||||
getRowId={({ data }) => data?.id}
|
||||
tooltipShowDelay={0}
|
||||
|
@ -39,6 +39,7 @@ const defaultColDef = {
|
||||
resizable: true,
|
||||
sortable: true,
|
||||
filterParams: { buttons: ['reset'] },
|
||||
minWidth: 100,
|
||||
};
|
||||
|
||||
export type OrderListTableProps = TypedDataAgGrid<Order> & {
|
||||
@ -82,6 +83,9 @@ export const OrderListTable = memo<
|
||||
field: 'market.tradableInstrument.instrument.code',
|
||||
cellRenderer: 'MarketNameCell',
|
||||
cellRendererParams: { idPath: 'market.id', onMarketClick },
|
||||
pinned: true,
|
||||
width: 130,
|
||||
resizable: true,
|
||||
},
|
||||
{
|
||||
headerName: t('Filled'),
|
||||
|
@ -40,6 +40,7 @@ const defaultColDef = {
|
||||
resizable: true,
|
||||
sortable: true,
|
||||
filterParams: { buttons: ['reset'] },
|
||||
minWidth: 100,
|
||||
};
|
||||
|
||||
export type StopOrdersTableProps = TypedDataAgGrid<StopOrder> & {
|
||||
@ -61,6 +62,7 @@ export const StopOrdersTable = memo(
|
||||
field: 'market.tradableInstrument.instrument.code',
|
||||
cellRenderer: 'MarketNameCell',
|
||||
cellRendererParams: { idPath: 'market.id', onMarketClick },
|
||||
pinned: true,
|
||||
},
|
||||
{
|
||||
headerName: t('Trigger'),
|
||||
|
@ -71,6 +71,7 @@ const defaultColDef = {
|
||||
filterParams: { buttons: ['reset'] },
|
||||
tooltipComponent: TooltipCellComponent,
|
||||
resizable: true,
|
||||
minWidth: 110,
|
||||
};
|
||||
|
||||
export const PositionsTable = ({
|
||||
@ -83,6 +84,330 @@ export const PositionsTable = ({
|
||||
...props
|
||||
}: Props) => {
|
||||
const t = useT();
|
||||
|
||||
const colDefs = useMemo<ColDef[]>(() => {
|
||||
const columnDefs: (ColDef | null)[] = [
|
||||
multipleKeys
|
||||
? {
|
||||
headerName: t('Vega key'),
|
||||
field: 'partyId',
|
||||
valueGetter: ({ data }: VegaValueGetterParams<Position>) =>
|
||||
(data?.partyId &&
|
||||
pubKeys &&
|
||||
pubKeys.find((key) => key.publicKey === data.partyId)?.name) ||
|
||||
data?.partyId,
|
||||
}
|
||||
: null,
|
||||
{
|
||||
headerName: t('Market'),
|
||||
field: 'marketCode',
|
||||
resizable: true,
|
||||
onCellClicked: ({ data }) => {
|
||||
if (!onMarketClick) return;
|
||||
onMarketClick(data.marketId);
|
||||
},
|
||||
pinned: true,
|
||||
cellRenderer: ({
|
||||
value,
|
||||
data,
|
||||
}: VegaICellRendererParams<Position, 'marketCode'>) => {
|
||||
if (!data || !value) return '-';
|
||||
return (
|
||||
<StackedCell
|
||||
primary={value}
|
||||
secondary={
|
||||
<>
|
||||
{data?.assetSymbol}
|
||||
<MarketProductPill productType={data.productType} />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: t('Size / Notional'),
|
||||
field: 'openVolume',
|
||||
type: 'rightAligned',
|
||||
cellClass: 'font-mono text-right',
|
||||
cellClassRules: signedNumberCssClassRules,
|
||||
filter: 'agNumberColumnFilter',
|
||||
valueGetter: ({ data }: { data: Position }) => {
|
||||
return data?.openVolume === undefined
|
||||
? undefined
|
||||
: toBigNum(data?.openVolume, data.positionDecimalPlaces).toNumber();
|
||||
},
|
||||
valueFormatter: ({
|
||||
data,
|
||||
}: VegaValueFormatterParams<Position, 'openVolume'>): string => {
|
||||
if (!data?.openVolume) return '-';
|
||||
|
||||
const vol = volumePrefix(
|
||||
addDecimalsFormatNumber(data.openVolume, data.positionDecimalPlaces)
|
||||
);
|
||||
|
||||
return vol;
|
||||
},
|
||||
cellRenderer: OpenVolumeCell,
|
||||
},
|
||||
{
|
||||
headerName: t('Entry / Mark'),
|
||||
field: 'markPrice',
|
||||
type: 'rightAligned',
|
||||
cellClass: 'font-mono text-right',
|
||||
cellRenderer: ({
|
||||
data,
|
||||
}: VegaICellRendererParams<Position, 'markPrice'>) => {
|
||||
if (
|
||||
!data?.averageEntryPrice ||
|
||||
!data?.markPrice ||
|
||||
!data?.marketDecimalPlaces
|
||||
) {
|
||||
return <>-</>;
|
||||
}
|
||||
|
||||
if (
|
||||
data.marketTradingMode ===
|
||||
MarketTradingMode.TRADING_MODE_OPENING_AUCTION
|
||||
) {
|
||||
return <>-</>;
|
||||
}
|
||||
|
||||
const entry = addDecimalsFormatNumber(
|
||||
data.averageEntryPrice,
|
||||
data.marketDecimalPlaces
|
||||
);
|
||||
const mark = addDecimalsFormatNumber(
|
||||
data.markPrice,
|
||||
data.marketDecimalPlaces
|
||||
);
|
||||
return (
|
||||
<StackedCell
|
||||
primary={entry}
|
||||
secondary={
|
||||
<PriceFlashCell
|
||||
value={Number(data.markPrice)}
|
||||
valueFormatted={mark}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
},
|
||||
filter: 'agNumberColumnFilter',
|
||||
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
|
||||
return !data ||
|
||||
!data.markPrice ||
|
||||
data.marketTradingMode ===
|
||||
MarketTradingMode.TRADING_MODE_OPENING_AUCTION
|
||||
? undefined
|
||||
: toBigNum(data.markPrice, data.marketDecimalPlaces).toNumber();
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: t('Margin / Leverage'),
|
||||
colId: 'margin',
|
||||
type: 'rightAligned',
|
||||
cellClass: 'font-mono text-right',
|
||||
filter: 'agNumberColumnFilter',
|
||||
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
|
||||
return !data
|
||||
? undefined
|
||||
: toBigNum(
|
||||
data.marginAccountBalance,
|
||||
data.assetDecimals
|
||||
).toNumber();
|
||||
},
|
||||
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
|
||||
if (
|
||||
!data ||
|
||||
!data.marginAccountBalance ||
|
||||
!data.marketDecimalPlaces
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
const margin = addDecimalsFormatNumberQuantum(
|
||||
data.marginAccountBalance,
|
||||
data.assetDecimals,
|
||||
data.quantum
|
||||
);
|
||||
|
||||
const lev = data?.currentLeverage ? data.currentLeverage : 1;
|
||||
const leverage = formatNumber(Math.max(1, lev), 1);
|
||||
return <StackedCell primary={margin} secondary={leverage + 'x'} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
colId: 'liquidationPrice',
|
||||
headerName: 'Liquidation',
|
||||
headerTooltip: t('Worst case liquidation price'),
|
||||
cellClass: 'font-mono text-right',
|
||||
type: 'rightAligned',
|
||||
// Cannot be sortable as data is fetched within the cell
|
||||
sortable: false,
|
||||
filter: false,
|
||||
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
|
||||
if (!data) {
|
||||
return '-';
|
||||
}
|
||||
return (
|
||||
<div className="flex h-[45px] items-center">
|
||||
<LiquidationPrice
|
||||
className="block text-right grow"
|
||||
marketId={data.marketId}
|
||||
openVolume={data.openVolume}
|
||||
collateralAvailable={data.totalBalance}
|
||||
decimalPlaces={data.marketDecimalPlaces}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: t('Realised PNL'),
|
||||
field: 'realisedPNL',
|
||||
type: 'rightAligned',
|
||||
cellClassRules: signedNumberCssClassRules,
|
||||
cellClass: 'font-mono text-right',
|
||||
filter: 'agNumberColumnFilter',
|
||||
valueGetter: realisedPNLValueGetter,
|
||||
cellRenderer: (
|
||||
args: VegaICellRendererParams<Position, 'realisedPNL'>
|
||||
) => {
|
||||
const LOSS_SOCIALIZATION_LINK = DocsLinks?.LOSS_SOCIALIZATION ?? '';
|
||||
|
||||
if (!args.data || args.value === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const losses = parseInt(args.data?.lossSocializationAmount ?? '0');
|
||||
|
||||
if (losses <= 0) {
|
||||
// eslint-disable-next-line react/jsx-no-useless-fragment
|
||||
return (
|
||||
<Tooltip description={args.valueFormatted} align="end">
|
||||
<div>
|
||||
<PNLCell {...args} />
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
const lossesFormatted = addDecimalsFormatNumber(
|
||||
args.data.lossSocializationAmount,
|
||||
args.data.assetDecimals
|
||||
);
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
align="end"
|
||||
description={
|
||||
<>
|
||||
<p className="mb-2">
|
||||
{t('Realised PNL: {{value}}', {
|
||||
nsSeparator: '*',
|
||||
replace: { value: args.value },
|
||||
})}
|
||||
</p>
|
||||
<p className="mb-2">
|
||||
{t('Lifetime loss socialisation deductions: {{losses}}', {
|
||||
nsSeparator: '*',
|
||||
replace: {
|
||||
losses: lossesFormatted,
|
||||
},
|
||||
})}
|
||||
</p>
|
||||
<p className="mb-2">
|
||||
{t(
|
||||
`You received less {{assetSymbol}} in gains that you should have when the market moved in your favour. This occurred because one or more other trader(s) were closed out and did not have enough funds to cover their losses, and the market's insurance pool was empty.`,
|
||||
{ assetSymbol: args.data.assetSymbol }
|
||||
)}
|
||||
</p>
|
||||
{LOSS_SOCIALIZATION_LINK && (
|
||||
<ExternalLink href={LOSS_SOCIALIZATION_LINK}>
|
||||
{t('Read more about loss socialisation')}
|
||||
</ExternalLink>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<PNLCell {...args} />
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
valueFormatter: ({
|
||||
data,
|
||||
}: VegaValueFormatterParams<Position, 'realisedPNL'>) => {
|
||||
return !data
|
||||
? ''
|
||||
: addDecimalsFormatNumberQuantum(
|
||||
data.realisedPNL,
|
||||
data.assetDecimals,
|
||||
data.quantum
|
||||
);
|
||||
},
|
||||
headerTooltip: t(
|
||||
'Profit or loss is realised whenever your position is reduced to zero and the margin is released back to your collateral balance. P&L excludes any fees paid.'
|
||||
),
|
||||
},
|
||||
{
|
||||
headerName: t('Unrealised PNL'),
|
||||
field: 'unrealisedPNL',
|
||||
type: 'rightAligned',
|
||||
cellClassRules: signedNumberCssClassRules,
|
||||
cellClass: 'font-mono text-right',
|
||||
filter: 'agNumberColumnFilter',
|
||||
valueGetter: unrealisedPNLValueGetter,
|
||||
// @ts-ignore no type overlap but function can be identical
|
||||
tooltipValueGetter: unrealisedPNLValueGetter,
|
||||
valueFormatter: ({
|
||||
data,
|
||||
}: VegaValueFormatterParams<Position, 'unrealisedPNL'>) =>
|
||||
!data
|
||||
? ''
|
||||
: addDecimalsFormatNumberQuantum(
|
||||
data.unrealisedPNL,
|
||||
data.assetDecimals,
|
||||
data.quantum
|
||||
),
|
||||
headerTooltip: t(
|
||||
'Unrealised profit is the current profit on your open position. Margin is still allocated to your position.'
|
||||
),
|
||||
},
|
||||
onClose && !isReadOnly
|
||||
? {
|
||||
...COL_DEFS.actions,
|
||||
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
|
||||
return (
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
{data?.openVolume &&
|
||||
data?.openVolume !== '0' &&
|
||||
data.partyId === pubKey ? (
|
||||
<ButtonLink
|
||||
data-testid="close-position"
|
||||
onClick={() => data && onClose(data)}
|
||||
title={t('Close position')}
|
||||
>
|
||||
<VegaIcon name={VegaIconNames.CROSS} size={16} />
|
||||
</ButtonLink>
|
||||
) : null}
|
||||
{data?.assetId && (
|
||||
<PositionActionsDropdown assetId={data?.assetId} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
minWidth: 55,
|
||||
maxWidth: 55,
|
||||
}
|
||||
: null,
|
||||
];
|
||||
return columnDefs.filter<ColDef>(
|
||||
(colDef: ColDef | null): colDef is ColDef => colDef !== null
|
||||
);
|
||||
}, [isReadOnly, multipleKeys, onClose, onMarketClick, pubKey, pubKeys, t]);
|
||||
|
||||
return (
|
||||
<AgGrid
|
||||
overlayNoRowsTemplate={t('No positions')}
|
||||
@ -95,349 +420,7 @@ export const PositionsTable = ({
|
||||
MarketNameCell,
|
||||
}}
|
||||
rowHeight={45}
|
||||
columnDefs={useMemo<ColDef[]>(() => {
|
||||
const columnDefs: (ColDef | null)[] = [
|
||||
multipleKeys
|
||||
? {
|
||||
headerName: t('Vega key'),
|
||||
field: 'partyId',
|
||||
valueGetter: ({ data }: VegaValueGetterParams<Position>) =>
|
||||
(data?.partyId &&
|
||||
pubKeys &&
|
||||
pubKeys.find((key) => key.publicKey === data.partyId)
|
||||
?.name) ||
|
||||
data?.partyId,
|
||||
}
|
||||
: null,
|
||||
{
|
||||
headerName: t('Market'),
|
||||
field: 'marketCode',
|
||||
onCellClicked: ({ data }) => {
|
||||
if (!onMarketClick) return;
|
||||
onMarketClick(data.marketId);
|
||||
},
|
||||
cellRenderer: ({
|
||||
value,
|
||||
data,
|
||||
}: VegaICellRendererParams<Position, 'marketCode'>) => {
|
||||
if (!data || !value) return '-';
|
||||
return (
|
||||
<StackedCell
|
||||
primary={value}
|
||||
secondary={
|
||||
<>
|
||||
{data?.assetSymbol}
|
||||
<MarketProductPill productType={data.productType} />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: t('Size / Notional'),
|
||||
field: 'openVolume',
|
||||
type: 'rightAligned',
|
||||
cellClass: 'font-mono text-right',
|
||||
cellClassRules: signedNumberCssClassRules,
|
||||
filter: 'agNumberColumnFilter',
|
||||
valueGetter: ({ data }: { data: Position }) => {
|
||||
return data?.openVolume === undefined
|
||||
? undefined
|
||||
: toBigNum(
|
||||
data?.openVolume,
|
||||
data.positionDecimalPlaces
|
||||
).toNumber();
|
||||
},
|
||||
valueFormatter: ({
|
||||
data,
|
||||
}: VegaValueFormatterParams<Position, 'openVolume'>): string => {
|
||||
if (!data?.openVolume) return '-';
|
||||
|
||||
const vol = volumePrefix(
|
||||
addDecimalsFormatNumber(
|
||||
data.openVolume,
|
||||
data.positionDecimalPlaces
|
||||
)
|
||||
);
|
||||
|
||||
return vol;
|
||||
},
|
||||
cellRenderer: OpenVolumeCell,
|
||||
},
|
||||
{
|
||||
headerName: t('Entry / Mark'),
|
||||
field: 'markPrice',
|
||||
type: 'rightAligned',
|
||||
cellClass: 'font-mono text-right',
|
||||
cellRenderer: ({
|
||||
data,
|
||||
}: VegaICellRendererParams<Position, 'markPrice'>) => {
|
||||
if (
|
||||
!data?.averageEntryPrice ||
|
||||
!data?.markPrice ||
|
||||
!data?.marketDecimalPlaces
|
||||
) {
|
||||
return <>-</>;
|
||||
}
|
||||
|
||||
if (
|
||||
data.marketTradingMode ===
|
||||
MarketTradingMode.TRADING_MODE_OPENING_AUCTION
|
||||
) {
|
||||
return <>-</>;
|
||||
}
|
||||
|
||||
const entry = addDecimalsFormatNumber(
|
||||
data.averageEntryPrice,
|
||||
data.marketDecimalPlaces
|
||||
);
|
||||
const mark = addDecimalsFormatNumber(
|
||||
data.markPrice,
|
||||
data.marketDecimalPlaces
|
||||
);
|
||||
return (
|
||||
<StackedCell
|
||||
primary={entry}
|
||||
secondary={
|
||||
<PriceFlashCell
|
||||
value={Number(data.markPrice)}
|
||||
valueFormatted={mark}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
},
|
||||
filter: 'agNumberColumnFilter',
|
||||
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
|
||||
return !data ||
|
||||
!data.markPrice ||
|
||||
data.marketTradingMode ===
|
||||
MarketTradingMode.TRADING_MODE_OPENING_AUCTION
|
||||
? undefined
|
||||
: toBigNum(data.markPrice, data.marketDecimalPlaces).toNumber();
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: t('Margin / Leverage'),
|
||||
colId: 'margin',
|
||||
type: 'rightAligned',
|
||||
cellClass: 'font-mono text-right',
|
||||
filter: 'agNumberColumnFilter',
|
||||
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
|
||||
return !data
|
||||
? undefined
|
||||
: toBigNum(
|
||||
data.marginAccountBalance,
|
||||
data.assetDecimals
|
||||
).toNumber();
|
||||
},
|
||||
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
|
||||
if (
|
||||
!data ||
|
||||
!data.marginAccountBalance ||
|
||||
!data.marketDecimalPlaces
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
const margin = addDecimalsFormatNumberQuantum(
|
||||
data.marginAccountBalance,
|
||||
data.assetDecimals,
|
||||
data.quantum
|
||||
);
|
||||
|
||||
const lev = data?.currentLeverage ? data.currentLeverage : 1;
|
||||
const leverage = formatNumber(Math.max(1, lev), 1);
|
||||
return (
|
||||
<StackedCell primary={margin} secondary={leverage + 'x'} />
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
colId: 'liquidationPrice',
|
||||
headerName: 'Liquidation',
|
||||
headerTooltip: t('Worst case liquidation price'),
|
||||
cellClass: 'font-mono text-right',
|
||||
type: 'rightAligned',
|
||||
// Cannot be sortable as data is fetched within the cell
|
||||
sortable: false,
|
||||
filter: false,
|
||||
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
|
||||
if (!data) {
|
||||
return '-';
|
||||
}
|
||||
return (
|
||||
<div className="flex h-[45px] items-center">
|
||||
<LiquidationPrice
|
||||
className="block text-right grow"
|
||||
marketId={data.marketId}
|
||||
openVolume={data.openVolume}
|
||||
collateralAvailable={data.totalBalance}
|
||||
decimalPlaces={data.marketDecimalPlaces}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: t('Realised PNL'),
|
||||
field: 'realisedPNL',
|
||||
type: 'rightAligned',
|
||||
cellClassRules: signedNumberCssClassRules,
|
||||
cellClass: 'font-mono text-right',
|
||||
filter: 'agNumberColumnFilter',
|
||||
valueGetter: realisedPNLValueGetter,
|
||||
cellRenderer: (
|
||||
args: VegaICellRendererParams<Position, 'realisedPNL'>
|
||||
) => {
|
||||
const LOSS_SOCIALIZATION_LINK =
|
||||
DocsLinks?.LOSS_SOCIALIZATION ?? '';
|
||||
|
||||
if (!args.data || args.value === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const losses = parseInt(
|
||||
args.data?.lossSocializationAmount ?? '0'
|
||||
);
|
||||
|
||||
if (losses <= 0) {
|
||||
// eslint-disable-next-line react/jsx-no-useless-fragment
|
||||
return (
|
||||
<Tooltip description={args.valueFormatted} align="end">
|
||||
<div>
|
||||
<PNLCell {...args} />
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
const lossesFormatted = addDecimalsFormatNumber(
|
||||
args.data.lossSocializationAmount,
|
||||
args.data.assetDecimals
|
||||
);
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
align="end"
|
||||
description={
|
||||
<>
|
||||
<p className="mb-2">
|
||||
{t('Realised PNL: {{value}}', {
|
||||
nsSeparator: '*',
|
||||
replace: { value: args.value },
|
||||
})}
|
||||
</p>
|
||||
<p className="mb-2">
|
||||
{t(
|
||||
'Lifetime loss socialisation deductions: {{losses}}',
|
||||
{
|
||||
nsSeparator: '*',
|
||||
replace: {
|
||||
losses: lossesFormatted,
|
||||
},
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
<p className="mb-2">
|
||||
{t(
|
||||
`You received less {{assetSymbol}} in gains that you should have when the market moved in your favour. This occurred because one or more other trader(s) were closed out and did not have enough funds to cover their losses, and the market's insurance pool was empty.`,
|
||||
{ assetSymbol: args.data.assetSymbol }
|
||||
)}
|
||||
</p>
|
||||
{LOSS_SOCIALIZATION_LINK && (
|
||||
<ExternalLink href={LOSS_SOCIALIZATION_LINK}>
|
||||
{t('Read more about loss socialisation')}
|
||||
</ExternalLink>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<PNLCell {...args} />
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
valueFormatter: ({
|
||||
data,
|
||||
}: VegaValueFormatterParams<Position, 'realisedPNL'>) => {
|
||||
return !data
|
||||
? ''
|
||||
: addDecimalsFormatNumberQuantum(
|
||||
data.realisedPNL,
|
||||
data.assetDecimals,
|
||||
data.quantum
|
||||
);
|
||||
},
|
||||
headerTooltip: t(
|
||||
'Profit or loss is realised whenever your position is reduced to zero and the margin is released back to your collateral balance. P&L excludes any fees paid.'
|
||||
),
|
||||
},
|
||||
{
|
||||
headerName: t('Unrealised PNL'),
|
||||
field: 'unrealisedPNL',
|
||||
type: 'rightAligned',
|
||||
cellClassRules: signedNumberCssClassRules,
|
||||
cellClass: 'font-mono text-right',
|
||||
filter: 'agNumberColumnFilter',
|
||||
valueGetter: unrealisedPNLValueGetter,
|
||||
// @ts-ignore no type overlap but function can be identical
|
||||
tooltipValueGetter: unrealisedPNLValueGetter,
|
||||
valueFormatter: ({
|
||||
data,
|
||||
}: VegaValueFormatterParams<Position, 'unrealisedPNL'>) =>
|
||||
!data
|
||||
? ''
|
||||
: addDecimalsFormatNumberQuantum(
|
||||
data.unrealisedPNL,
|
||||
data.assetDecimals,
|
||||
data.quantum
|
||||
),
|
||||
headerTooltip: t(
|
||||
'Unrealised profit is the current profit on your open position. Margin is still allocated to your position.'
|
||||
),
|
||||
},
|
||||
onClose && !isReadOnly
|
||||
? {
|
||||
...COL_DEFS.actions,
|
||||
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
|
||||
return (
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
{data?.openVolume &&
|
||||
data?.openVolume !== '0' &&
|
||||
data.partyId === pubKey ? (
|
||||
<ButtonLink
|
||||
data-testid="close-position"
|
||||
onClick={() => data && onClose(data)}
|
||||
title={t('Close position')}
|
||||
>
|
||||
<VegaIcon name={VegaIconNames.CROSS} size={16} />
|
||||
</ButtonLink>
|
||||
) : null}
|
||||
{data?.assetId && (
|
||||
<PositionActionsDropdown assetId={data?.assetId} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
minWidth: 55,
|
||||
maxWidth: 55,
|
||||
}
|
||||
: null,
|
||||
];
|
||||
return columnDefs.filter<ColDef>(
|
||||
(colDef: ColDef | null): colDef is ColDef => colDef !== null
|
||||
);
|
||||
}, [
|
||||
isReadOnly,
|
||||
multipleKeys,
|
||||
onClose,
|
||||
onMarketClick,
|
||||
pubKey,
|
||||
pubKeys,
|
||||
t,
|
||||
])}
|
||||
columnDefs={colDefs}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
@ -29,6 +29,7 @@ export const useColumnDefs = () => {
|
||||
colId: 'market',
|
||||
headerName: t('Market'),
|
||||
field: 'terms.change.instrument.code',
|
||||
pinned: true,
|
||||
cellStyle: { lineHeight: '14px' },
|
||||
cellRenderer: ({
|
||||
value,
|
||||
|
@ -85,19 +85,21 @@ export function Dialog({
|
||||
<VegaIcon name={VegaIconNames.CROSS} size={24} />
|
||||
</DialogPrimitives.Close>
|
||||
)}
|
||||
<div className="flex gap-4 max-w-full">
|
||||
{icon && <div className="fill-current">{icon}</div>}
|
||||
<div data-testid="dialog-content" className="flex-1 max-w-full">
|
||||
{title && (
|
||||
<h1
|
||||
className="text-xl uppercase mb-4 pr-2"
|
||||
data-testid="dialog-title"
|
||||
>
|
||||
{title}
|
||||
</h1>
|
||||
)}
|
||||
<div>{children}</div>
|
||||
</div>
|
||||
<div data-testid="dialog-content" className="flex-1 max-w-full">
|
||||
{title && (
|
||||
<span
|
||||
className="text-xl uppercase flex gap-4"
|
||||
data-testid="dialog-title"
|
||||
>
|
||||
{icon && (
|
||||
<span className="fill-current flex items-center">
|
||||
{icon}
|
||||
</span>
|
||||
)}
|
||||
{title}
|
||||
</span>
|
||||
)}
|
||||
<div>{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -5,15 +5,16 @@ import { ToastPosition, useToastsConfiguration, useToasts } from './use-toasts';
|
||||
import { useCallback } from 'react';
|
||||
import { Intent } from '../../utils/intent';
|
||||
import { useT } from '../../use-t';
|
||||
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||
|
||||
export const ToastPositionSetter = () => {
|
||||
const t = useT();
|
||||
const setPostion = useToastsConfiguration((store) => store.setPosition);
|
||||
const setPosition = useToastsConfiguration((store) => store.setPosition);
|
||||
const position = useToastsConfiguration((store) => store.position);
|
||||
const setToast = useToasts((store) => store.setToast);
|
||||
const handleChange = useCallback(
|
||||
(position: ToastPosition) => {
|
||||
setPostion(position);
|
||||
setPosition(position);
|
||||
setToast({
|
||||
id: 'test-toast',
|
||||
intent: Intent.Primary,
|
||||
@ -21,11 +22,49 @@ export const ToastPositionSetter = () => {
|
||||
onClose: () => useToasts.getState().remove('test-toast'),
|
||||
});
|
||||
},
|
||||
[setToast, setPostion, t]
|
||||
[setToast, setPosition, t]
|
||||
);
|
||||
const buttonCssClasses =
|
||||
'flex items-center px-1 py-1 relative rounded bg-vega-clight-400 dark:bg-vega-cdark-400';
|
||||
const activeIcon = 'fill-vega-clight-900 dark:fill-vega-cdark-900';
|
||||
|
||||
const { screenSize } = useScreenDimensions();
|
||||
|
||||
const isMobileScreen = screenSize === 'xs';
|
||||
|
||||
if (isMobileScreen) {
|
||||
return (
|
||||
<div className="flex justify-between">
|
||||
<div className={classNames('grid grid-cols-1 grid-rows-2 gap-1')}>
|
||||
<button
|
||||
className={buttonCssClasses}
|
||||
onClick={() => handleChange(ToastPosition.TopCenter)}
|
||||
>
|
||||
<Icon
|
||||
className={classNames(
|
||||
position === ToastPosition.TopCenter && activeIcon
|
||||
)}
|
||||
size={3}
|
||||
name={IconNames.ARROW_UP}
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
className={buttonCssClasses}
|
||||
onClick={() => handleChange(ToastPosition.BottomCenter)}
|
||||
>
|
||||
<Icon
|
||||
className={classNames(
|
||||
position === ToastPosition.BottomCenter && activeIcon
|
||||
)}
|
||||
size={3}
|
||||
name={IconNames.ARROW_DOWN}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex justify-between">
|
||||
<div className={classNames('grid grid-cols-3 grid-rows-2 gap-1')}>
|
||||
|
@ -92,7 +92,7 @@ export const ToastsContainer = ({
|
||||
className={classNames(
|
||||
'absolute right-0 top-[-38px] z-20 w-full',
|
||||
'transition-opacity',
|
||||
'opacity-0 hover:!opacity-100 group-hover:opacity-50',
|
||||
'sm:opacity-0 sm:hover:!opacity-100 sm:group-hover:opacity-50',
|
||||
{
|
||||
hidden: validToasts.length === 0,
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ export class ViewConnector implements VegaConnector {
|
||||
});
|
||||
return Promise.resolve([
|
||||
{
|
||||
name: 'View only pubkey',
|
||||
name: 'View only',
|
||||
publicKey: this.pubkey,
|
||||
},
|
||||
]);
|
||||
|
@ -15,6 +15,7 @@ import {
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
COL_DEFS,
|
||||
type TypedDataAgGrid,
|
||||
type VegaICellRendererParams,
|
||||
type VegaValueFormatterParams,
|
||||
@ -46,7 +47,7 @@ export const WithdrawalsTable = ({
|
||||
|
||||
const columnDefs = useMemo<ColDef[]>(
|
||||
() => [
|
||||
{ headerName: t('Asset'), field: 'asset.symbol' },
|
||||
{ headerName: t('Asset'), field: 'asset.symbol', pinned: true },
|
||||
{
|
||||
headerName: t('Amount'),
|
||||
field: 'amount',
|
||||
@ -135,6 +136,7 @@ export const WithdrawalsTable = ({
|
||||
<AgGrid
|
||||
overlayNoRowsTemplate={t('No withdrawals')}
|
||||
columnDefs={columnDefs}
|
||||
defaultColDef={COL_DEFS.default}
|
||||
components={{
|
||||
RecipientCell,
|
||||
StatusCell,
|
||||
|
@ -38,7 +38,7 @@
|
||||
"@radix-ui/react-slider": "^1.1.0",
|
||||
"@radix-ui/react-switch": "^1.0.2",
|
||||
"@radix-ui/react-tabs": "^1.0.2",
|
||||
"@radix-ui/react-tooltip": "^1.0.3",
|
||||
"@radix-ui/react-tooltip": "^1.0.7",
|
||||
"@sentry/nextjs": "^6.19.3",
|
||||
"@sentry/react": "^6.19.2",
|
||||
"@sentry/tracing": "^6.19.2",
|
||||
|
@ -4864,7 +4864,7 @@
|
||||
"@radix-ui/react-separator" "1.0.3"
|
||||
"@radix-ui/react-toggle-group" "1.0.4"
|
||||
|
||||
"@radix-ui/react-tooltip@^1.0.3":
|
||||
"@radix-ui/react-tooltip@^1.0.7":
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.0.7.tgz#8f55070f852e7e7450cc1d9210b793d2e5a7686e"
|
||||
integrity sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw==
|
||||
|
Loading…
Reference in New Issue
Block a user