chore(trading): remove unnecessary forwarding of grid refs (#4486)

This commit is contained in:
Matthew Russell 2023-08-04 11:08:28 +01:00 committed by GitHub
parent 015e0188ec
commit 1041864ad8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 1082 additions and 1181 deletions

View File

@ -4,12 +4,9 @@ import { depositsProvider } from '@vegaprotocol/deposits';
import { t } from '@vegaprotocol/i18n';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useRef } from 'react';
import type { AgGridReact } from 'ag-grid-react';
import { useSidebar, ViewType } from '../../components/sidebar';
export const DepositsContainer = () => {
const gridRef = useRef<AgGridReact | null>(null);
const { pubKey, isReadOnly } = useVegaWallet();
const { data, error } = useDataProvider({
dataProvider: depositsProvider,
@ -24,7 +21,6 @@ export const DepositsContainer = () => {
<div className="h-full">
<DepositsTable
rowData={data}
ref={gridRef}
overlayNoRowsTemplate={error ? error.message : t('No deposits')}
/>
{!isReadOnly && (

View File

@ -118,7 +118,6 @@ export const AccountManager = ({
onMarketClick,
gridProps,
}: AccountManagerProps) => {
const gridRef = useRef<AgGridReact | null>(null);
const [breakdownAssetId, setBreakdownAssetId] = useState<string>();
const { data, error } = useDataProvider({
dataProvider: aggregatedAccountsDataProvider,
@ -138,7 +137,6 @@ export const AccountManager = ({
return (
<div className="relative h-full">
<AccountTable
ref={gridRef}
rowData={data}
onClickAsset={onClickAsset}
onClickDeposit={onClickDeposit}

View File

@ -1,4 +1,4 @@
import { forwardRef, useMemo, useCallback } from 'react';
import { useMemo, useCallback } from 'react';
import {
addDecimalsFormatNumber,
addDecimalsFormatNumberQuantum,
@ -21,7 +21,7 @@ import type {
RowHeightParams,
ColDef,
} from 'ag-grid-community';
import type { AgGridReact, AgGridReactProps } from 'ag-grid-react';
import type { AgGridReactProps } from 'ag-grid-react';
import type { AccountFields } from './accounts-data-provider';
import type { Asset } from '@vegaprotocol/types';
import { CenteredGridCellWrapper } from '@vegaprotocol/datagrid';
@ -75,256 +75,250 @@ export interface AccountTableProps extends AgGridReactProps {
pinnedAsset?: PinnedAsset;
}
export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
(
{
onClickAsset,
onClickWithdraw,
onClickDeposit,
onClickBreakdown,
onClickTransfer,
rowData,
isReadOnly,
pinnedAsset,
...props
export const AccountTable = ({
onClickAsset,
onClickWithdraw,
onClickDeposit,
onClickBreakdown,
onClickTransfer,
rowData,
isReadOnly,
pinnedAsset,
...props
}: AccountTableProps) => {
const pinnedRow = useMemo(() => {
if (!pinnedAsset) {
return;
}
const currentPinnedAssetRow = rowData?.find(
(row) => row.asset.id === pinnedAsset?.id
);
if (!currentPinnedAssetRow) {
return {
asset: pinnedAsset,
available: '0',
used: '0',
total: '0',
balance: '0',
};
}
return currentPinnedAssetRow;
}, [pinnedAsset, rowData]);
const { getRowHeight } = props;
const getPinnedAssetRowHeight = useCallback(
(params: RowHeightParams) => {
if (
params.node.rowPinned &&
params.data.asset.id === pinnedAsset?.id &&
new BigNumber(params.data.total).isLessThanOrEqualTo(0)
) {
return 32;
}
return getRowHeight ? getRowHeight(params) : undefined;
},
ref
) => {
const pinnedRow = useMemo(() => {
if (!pinnedAsset) {
return;
}
const currentPinnedAssetRow = rowData?.find(
(row) => row.asset.id === pinnedAsset?.id
);
if (!currentPinnedAssetRow) {
return {
asset: pinnedAsset,
available: '0',
used: '0',
total: '0',
balance: '0',
};
}
return currentPinnedAssetRow;
}, [pinnedAsset, rowData]);
[pinnedAsset?.id, getRowHeight]
);
const { getRowHeight } = props;
const showDepositButton = pinnedRow?.balance === '0';
const getPinnedAssetRowHeight = useCallback(
(params: RowHeightParams) => {
if (
params.node.rowPinned &&
params.data.asset.id === pinnedAsset?.id &&
new BigNumber(params.data.total).isLessThanOrEqualTo(0)
) {
return 32;
}
return getRowHeight ? getRowHeight(params) : undefined;
const colDefs = useMemo(() => {
const defs: ColDef[] = [
{
headerName: t('Asset'),
field: 'asset.symbol',
headerTooltip: t(
'Asset is the collateral that is deposited into the Vega protocol.'
),
cellClass: 'underline',
onCellClicked: ({ data }) => {
if (data) {
onClickAsset(data.asset.id);
}
},
},
[pinnedAsset?.id, getRowHeight]
);
const showDepositButton = pinnedRow?.balance === '0';
const colDefs = useMemo(() => {
const defs: ColDef[] = [
{
headerName: t('Asset'),
field: 'asset.symbol',
headerTooltip: t(
'Asset is the collateral that is deposited into the Vega protocol.'
),
cellClass: 'underline',
onCellClicked: ({ data }) => {
if (data) {
onClickAsset(data.asset.id);
}
},
{
headerName: t('Used'),
type: 'rightAligned',
field: 'used',
headerTooltip: t(
'Currently allocated to a market as margin or bond. Check the breakdown for details.'
),
tooltipValueGetter: ({ value, data }) => {
if (!value || !data) return null;
return addDecimalsFormatNumber(value, data.asset.decimals);
},
{
headerName: t('Used'),
type: 'rightAligned',
field: 'used',
headerTooltip: t(
'Currently allocated to a market as margin or bond. Check the breakdown for details.'
),
tooltipValueGetter: ({ value, data }) => {
if (!value || !data) return null;
return addDecimalsFormatNumber(value, data.asset.decimals);
},
onCellClicked: ({ data }) => {
if (!data || !onClickBreakdown) return;
onClickBreakdown(data.asset.id);
},
cellRenderer: ({
data,
onCellClicked: ({ data }) => {
if (!data || !onClickBreakdown) return;
onClickBreakdown(data.asset.id);
},
cellRenderer: ({
data,
value,
}: VegaICellRendererParams<AccountFields, 'used'>) => {
if (!value || !data) return '-';
const percentageUsed = percentageValue(value, data.total);
const valueFormatted = addDecimalsFormatNumberQuantum(
value,
}: VegaICellRendererParams<AccountFields, 'used'>) => {
if (!value || !data) return '-';
const percentageUsed = percentageValue(value, data.total);
const valueFormatted = addDecimalsFormatNumberQuantum(
value,
data.asset.decimals,
data.asset.quantum
);
data.asset.decimals,
data.asset.quantum
);
return data.breakdown ? (
<>
<span className="underline">{valueFormatted}</span>
<span
className={classNames(
colorClass(percentageUsed),
'ml-1 inline-block w-14'
)}
return data.breakdown ? (
<>
<span className="underline">{valueFormatted}</span>
<span
className={classNames(
colorClass(percentageUsed),
'ml-1 inline-block w-14'
)}
>
{percentageUsed.toFixed(2)}%
</span>
</>
) : (
<>
<span className="underline">{valueFormatted}</span>
<span className="ml-2 inline-block w-14 text-muted">
{t('0.00%')}
</span>
</>
);
},
},
{
headerName: t('Available'),
field: 'available',
type: 'rightAligned',
headerTooltip: t(
'Deposited on the network, but not allocated to a market. Free to use for placing orders or providing liquidity.'
),
tooltipValueGetter: ({ value, data }) => {
if (!value || !data) return null;
return addDecimalsFormatNumber(value, data.asset.decimals);
},
cellClass: ({ data }) => {
const percentageUsed = percentageValue(data?.used, data?.total);
return colorClass(percentageUsed, true);
},
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<AccountFields, 'available'>) => {
if (!value || !data) return '-';
return addDecimalsFormatNumberQuantum(
value,
data.asset.decimals,
data.asset.quantum
);
},
},
{
headerName: t('Total'),
type: 'rightAligned',
field: 'total',
headerTooltip: t(
'The total amount of each asset on this key. Includes used and available collateral.'
),
tooltipValueGetter: ({ value, data }) => {
if (!value || !data) return null;
return addDecimalsFormatNumber(value, data.asset.decimals);
},
valueFormatter: ({
data,
value,
}: VegaValueFormatterParams<AccountFields, 'total'>) => {
if (!data || !value) return '-';
return addDecimalsFormatNumberQuantum(
value,
data.asset.decimals,
data.asset.quantum
);
},
},
{
colId: 'accounts-actions',
field: 'asset.id',
...COL_DEFS.actions,
minWidth: showDepositButton ? 130 : COL_DEFS.actions.minWidth,
maxWidth: showDepositButton ? 130 : COL_DEFS.actions.maxWidth,
cellRenderer: ({
value: assetId,
node,
}: VegaICellRendererParams<AccountFields, 'asset.id'>) => {
if (!assetId) return null;
if (node.rowPinned && node.data?.total === '0') {
return (
<CenteredGridCellWrapper className="h-[30px] justify-end py-1">
<Button
size="xs"
variant="primary"
data-testid="deposit"
onClick={() => {
onClickDeposit && onClickDeposit(assetId);
}}
>
{percentageUsed.toFixed(2)}%
</span>
</>
) : (
<>
<span className="underline">{valueFormatted}</span>
<span className="ml-2 inline-block w-14 text-muted">
{t('0.00%')}
</span>
</>
<VegaIcon name={VegaIconNames.DEPOSIT} /> {t('Deposit')}
</Button>
</CenteredGridCellWrapper>
);
},
},
{
headerName: t('Available'),
field: 'available',
type: 'rightAligned',
headerTooltip: t(
'Deposited on the network, but not allocated to a market. Free to use for placing orders or providing liquidity.'
),
tooltipValueGetter: ({ value, data }) => {
if (!value || !data) return null;
return addDecimalsFormatNumber(value, data.asset.decimals);
},
cellClass: ({ data }) => {
const percentageUsed = percentageValue(data?.used, data?.total);
return colorClass(percentageUsed, true);
},
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<AccountFields, 'available'>) => {
if (!value || !data) return '-';
return addDecimalsFormatNumberQuantum(
value,
data.asset.decimals,
data.asset.quantum
);
},
}
return isReadOnly ? null : (
<AccountsActionsDropdown
assetId={assetId}
assetContractAddress={
node.data?.asset.source?.__typename === 'ERC20'
? node.data.asset.source.contractAddress
: undefined
}
onClickDeposit={() => {
onClickDeposit && onClickDeposit(assetId);
}}
onClickWithdraw={() => {
onClickWithdraw && onClickWithdraw(assetId);
}}
onClickBreakdown={() => {
onClickBreakdown && onClickBreakdown(assetId);
}}
onClickTransfer={() => {
onClickTransfer && onClickTransfer(assetId);
}}
/>
);
},
},
];
return defs;
}, [
onClickAsset,
onClickBreakdown,
onClickDeposit,
onClickWithdraw,
onClickTransfer,
isReadOnly,
showDepositButton,
]);
{
headerName: t('Total'),
type: 'rightAligned',
field: 'total',
headerTooltip: t(
'The total amount of each asset on this key. Includes used and available collateral.'
),
tooltipValueGetter: ({ value, data }) => {
if (!value || !data) return null;
return addDecimalsFormatNumber(value, data.asset.decimals);
},
valueFormatter: ({
data,
value,
}: VegaValueFormatterParams<AccountFields, 'total'>) => {
if (!data || !value) return '-';
return addDecimalsFormatNumberQuantum(
value,
data.asset.decimals,
data.asset.quantum
);
},
},
{
colId: 'accounts-actions',
field: 'asset.id',
...COL_DEFS.actions,
minWidth: showDepositButton ? 130 : COL_DEFS.actions.minWidth,
maxWidth: showDepositButton ? 130 : COL_DEFS.actions.maxWidth,
cellRenderer: ({
value: assetId,
node,
}: VegaICellRendererParams<AccountFields, 'asset.id'>) => {
if (!assetId) return null;
if (node.rowPinned && node.data?.total === '0') {
return (
<CenteredGridCellWrapper className="h-[30px] justify-end py-1">
<Button
size="xs"
variant="primary"
data-testid="deposit"
onClick={() => {
onClickDeposit && onClickDeposit(assetId);
}}
>
<VegaIcon name={VegaIconNames.DEPOSIT} /> {t('Deposit')}
</Button>
</CenteredGridCellWrapper>
);
}
return isReadOnly ? null : (
<AccountsActionsDropdown
assetId={assetId}
assetContractAddress={
node.data?.asset.source?.__typename === 'ERC20'
? node.data.asset.source.contractAddress
: undefined
}
onClickDeposit={() => {
onClickDeposit && onClickDeposit(assetId);
}}
onClickWithdraw={() => {
onClickWithdraw && onClickWithdraw(assetId);
}}
onClickBreakdown={() => {
onClickBreakdown && onClickBreakdown(assetId);
}}
onClickTransfer={() => {
onClickTransfer && onClickTransfer(assetId);
}}
/>
);
},
},
];
return defs;
}, [
onClickAsset,
onClickBreakdown,
onClickDeposit,
onClickWithdraw,
onClickTransfer,
isReadOnly,
showDepositButton,
]);
const data = rowData?.filter((data) => data.asset.id !== pinnedAsset?.id);
const data = rowData?.filter((data) => data.asset.id !== pinnedAsset?.id);
return (
<AgGrid
{...props}
style={{ width: '100%', height: '100%' }}
getRowId={({ data }: { data: AccountFields }) => data.asset.id}
ref={ref}
tooltipShowDelay={500}
rowData={data}
defaultColDef={{
resizable: true,
tooltipComponent: TooltipCellComponent,
sortable: true,
comparator: accountValuesComparator,
}}
columnDefs={colDefs}
getRowHeight={getPinnedAssetRowHeight}
pinnedTopRowData={pinnedRow ? [pinnedRow] : undefined}
/>
);
}
);
return (
<AgGrid
{...props}
style={{ width: '100%', height: '100%' }}
getRowId={({ data }: { data: AccountFields }) => data.asset.id}
tooltipShowDelay={500}
rowData={data}
defaultColDef={{
resizable: true,
tooltipComponent: TooltipCellComponent,
sortable: true,
comparator: accountValuesComparator,
}}
columnDefs={colDefs}
getRowHeight={getPinnedAssetRowHeight}
pinnedTopRowData={pinnedRow ? [pinnedRow] : undefined}
/>
);
};

View File

@ -21,5 +21,4 @@ export * from './lib/type-helpers';
export * from './lib/cells/grid-progress-bar';
export * from './lib/ag-grid-update';
export * from './lib/use-datagrid-events';

View File

@ -1,20 +0,0 @@
import type { MutableRefObject, RefObject } from 'react';
import type { AgGridReact } from 'ag-grid-react';
type AnyArray = Array<any> | null; // eslint-disable-line @typescript-eslint/no-explicit-any
export const isXOrWasEmpty = (prev?: AnyArray, curr?: AnyArray) =>
Boolean(Number(!!prev?.length) ^ Number(!!curr?.length));
export const updateGridData = (
dataRef: MutableRefObject<AnyArray>,
data: AnyArray,
gridRef: RefObject<AgGridReact>
) => {
const rerender = isXOrWasEmpty(dataRef.current, data);
dataRef.current = data;
if (gridRef.current?.api?.getModel().getType() === 'infinite') {
gridRef.current?.api?.refreshInfiniteCache();
}
return !rerender;
};

View File

@ -1,4 +1,4 @@
import { forwardRef, useMemo } from 'react';
import { useMemo } from 'react';
import {
addDecimalsFormatNumber,
getDateTimeFormat,
@ -6,7 +6,6 @@ import {
isNumeric,
} from '@vegaprotocol/utils';
import type { ColDef } from 'ag-grid-community';
import type { AgGridReact } from 'ag-grid-react';
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
import type {
VegaICellRendererParams,
@ -17,10 +16,9 @@ import type { DepositFieldsFragment } from './__generated__/Deposit';
import { EtherscanLink } from '@vegaprotocol/environment';
import { DepositStatusMapping } from '@vegaprotocol/types';
export const DepositsTable = forwardRef<
AgGridReact,
TypedDataAgGrid<DepositFieldsFragment>
>((props, ref) => {
export const DepositsTable = (
props: TypedDataAgGrid<DepositFieldsFragment>
) => {
const columnDefs = useMemo<ColDef[]>(
() => [
{ headerName: 'Asset', field: 'asset.symbol' },
@ -79,11 +77,10 @@ export const DepositsTable = forwardRef<
);
return (
<AgGrid
ref={ref}
defaultColDef={{ flex: 1 }}
columnDefs={columnDefs}
style={{ width: '100%', height: '100%' }}
{...props}
/>
);
});
};

View File

@ -1,8 +1,7 @@
import { t } from '@vegaprotocol/i18n';
import type * as Schema from '@vegaprotocol/types';
import type { FilterChangedEvent } from 'ag-grid-community';
import type { AgGridReact } from 'ag-grid-react';
import { useCallback, useRef, useState, useMemo } from 'react';
import { useCallback, useState, useMemo } from 'react';
import { subDays, formatRFC3339 } from 'date-fns';
import { ledgerEntriesProvider } from './ledger-entries-data-provider';
import type { LedgerEntriesQueryVariables } from './__generated__/LedgerEntries';
@ -32,7 +31,6 @@ export const LedgerManager = ({
partyId: string;
gridProps: ReturnType<typeof useDataGridEvents>;
}) => {
const gridRef = useRef<AgGridReact | null>(null);
const [filter, setFilter] = useState<Filter>(defaultFilter);
const variables = useMemo<LedgerEntriesQueryVariables>(
@ -64,7 +62,6 @@ export const LedgerManager = ({
return (
<div className="h-full relative">
<LedgerTable
ref={gridRef}
rowData={data}
overlayNoRowsTemplate={error ? error.message : t('No entries')}
{...gridProps}

View File

@ -14,7 +14,6 @@ import {
DateRangeFilter,
SetFilter,
} from '@vegaprotocol/datagrid';
import type { AgGridReact } from 'ag-grid-react';
import type * as Types from '@vegaprotocol/types';
import type { ColDef } from 'ag-grid-community';
import {
@ -23,7 +22,7 @@ import {
TransferTypeMapping,
} from '@vegaprotocol/types';
import type { LedgerEntry } from './ledger-entries-data-provider';
import { forwardRef, useMemo } from 'react';
import { useMemo } from 'react';
import { formatRFC3339, subDays } from 'date-fns';
export const TransferTooltipCellComponent = ({
@ -45,162 +44,159 @@ const dateRangeFilterParams = {
};
type LedgerEntryProps = TypedDataAgGrid<LedgerEntry>;
export const LedgerTable = forwardRef<AgGridReact, LedgerEntryProps>(
(props, ref) => {
const columnDefs = useMemo<ColDef[]>(
() => [
{
headerName: t('Sender'),
field: 'fromAccountPartyId',
cellRenderer: ({
value,
}: VegaValueFormatterParams<LedgerEntry, 'fromAccountPartyId'>) =>
truncateByChars(value || ''),
export const LedgerTable = (props: LedgerEntryProps) => {
const columnDefs = useMemo<ColDef[]>(
() => [
{
headerName: t('Sender'),
field: 'fromAccountPartyId',
cellRenderer: ({
value,
}: VegaValueFormatterParams<LedgerEntry, 'fromAccountPartyId'>) =>
truncateByChars(value || ''),
},
{
headerName: t('Account type'),
filter: SetFilter,
filterParams: {
set: AccountTypeMapping,
},
{
headerName: t('Account type'),
filter: SetFilter,
filterParams: {
set: AccountTypeMapping,
},
field: 'fromAccountType',
cellRenderer: ({
value,
}: VegaValueFormatterParams<LedgerEntry, 'fromAccountType'>) =>
value ? AccountTypeMapping[value] : '-',
field: 'fromAccountType',
cellRenderer: ({
value,
}: VegaValueFormatterParams<LedgerEntry, 'fromAccountType'>) =>
value ? AccountTypeMapping[value] : '-',
},
{
headerName: t('Market'),
field: 'marketSender.tradableInstrument.instrument.code',
cellRenderer: ({
value,
}: VegaValueFormatterParams<
LedgerEntry,
'marketSender.tradableInstrument.instrument.code'
>) => value || '-',
},
{
headerName: t('Receiver'),
field: 'toAccountPartyId',
cellRenderer: ({
value,
}: VegaValueFormatterParams<LedgerEntry, 'toAccountPartyId'>) =>
truncateByChars(value || ''),
},
{
headerName: t('Account type'),
filter: SetFilter,
filterParams: {
set: AccountTypeMapping,
},
{
headerName: t('Market'),
field: 'marketSender.tradableInstrument.instrument.code',
cellRenderer: ({
value,
}: VegaValueFormatterParams<
LedgerEntry,
'marketSender.tradableInstrument.instrument.code'
>) => value || '-',
field: 'toAccountType',
cellRenderer: ({
value,
}: VegaValueFormatterParams<LedgerEntry, 'toAccountType'>) =>
value ? AccountTypeMapping[value] : '-',
},
{
headerName: t('Market'),
field: 'marketReceiver.tradableInstrument.instrument.code',
cellRenderer: ({
value,
}: VegaValueFormatterParams<
LedgerEntry,
'marketReceiver.tradableInstrument.instrument.code'
>) => value || '-',
},
{
headerName: t('Transfer type'),
field: 'transferType',
tooltipField: 'transferType',
filter: SetFilter,
filterParams: {
set: TransferTypeMapping,
},
{
headerName: t('Receiver'),
field: 'toAccountPartyId',
cellRenderer: ({
value,
}: VegaValueFormatterParams<LedgerEntry, 'toAccountPartyId'>) =>
truncateByChars(value || ''),
valueFormatter: ({
value,
}: VegaValueFormatterParams<LedgerEntry, 'transferType'>) =>
value ? TransferTypeMapping[value] : '',
},
{
headerName: t('Quantity'),
field: 'quantity',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<LedgerEntry, 'quantity'>) => {
const assetDecimalPlaces = data?.asset?.decimals || 0;
return value
? addDecimalsFormatNumber(value, assetDecimalPlaces)
: '';
},
{
headerName: t('Account type'),
filter: SetFilter,
filterParams: {
set: AccountTypeMapping,
},
field: 'toAccountType',
cellRenderer: ({
value,
}: VegaValueFormatterParams<LedgerEntry, 'toAccountType'>) =>
value ? AccountTypeMapping[value] : '-',
},
{
headerName: t('Asset'),
field: 'assetId',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<LedgerEntry, 'asset'>) =>
data?.asset?.symbol || '',
},
{
headerName: t('Sender account balance'),
field: 'fromAccountBalance',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<LedgerEntry, 'fromAccountBalance'>) => {
const assetDecimalPlaces = data?.asset?.decimals || 0;
return value
? addDecimalsFormatNumber(value, assetDecimalPlaces)
: '';
},
{
headerName: t('Market'),
field: 'marketReceiver.tradableInstrument.instrument.code',
cellRenderer: ({
value,
}: VegaValueFormatterParams<
LedgerEntry,
'marketReceiver.tradableInstrument.instrument.code'
>) => value || '-',
},
{
headerName: t('Receiver account balance'),
field: 'toAccountBalance',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<LedgerEntry, 'toAccountBalance'>) => {
const assetDecimalPlaces = data?.asset?.decimals || 0;
return value
? addDecimalsFormatNumber(value, assetDecimalPlaces)
: '';
},
{
headerName: t('Transfer type'),
field: 'transferType',
tooltipField: 'transferType',
filter: SetFilter,
filterParams: {
set: TransferTypeMapping,
},
valueFormatter: ({
value,
}: VegaValueFormatterParams<LedgerEntry, 'transferType'>) =>
value ? TransferTypeMapping[value] : '',
},
{
headerName: t('Vega time'),
field: 'vegaTime',
valueFormatter: ({
value,
}: VegaValueFormatterParams<LedgerEntry, 'vegaTime'>) =>
value ? getDateTimeFormat().format(fromNanoSeconds(value)) : '-',
filterParams: dateRangeFilterParams,
filter: DateRangeFilter,
flex: 1,
},
],
[]
);
return (
<AgGrid
style={{ width: '100%', height: '100%' }}
tooltipShowDelay={500}
defaultColDef={{
resizable: true,
sortable: true,
tooltipComponent: TransferTooltipCellComponent,
filterParams: {
...dateRangeFilterParams,
buttons: ['reset'],
},
{
headerName: t('Quantity'),
field: 'quantity',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<LedgerEntry, 'quantity'>) => {
const assetDecimalPlaces = data?.asset?.decimals || 0;
return value
? addDecimalsFormatNumber(value, assetDecimalPlaces)
: '';
},
},
{
headerName: t('Asset'),
field: 'assetId',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<LedgerEntry, 'asset'>) =>
data?.asset?.symbol || '',
},
{
headerName: t('Sender account balance'),
field: 'fromAccountBalance',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<LedgerEntry, 'fromAccountBalance'>) => {
const assetDecimalPlaces = data?.asset?.decimals || 0;
return value
? addDecimalsFormatNumber(value, assetDecimalPlaces)
: '';
},
},
{
headerName: t('Receiver account balance'),
field: 'toAccountBalance',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<LedgerEntry, 'toAccountBalance'>) => {
const assetDecimalPlaces = data?.asset?.decimals || 0;
return value
? addDecimalsFormatNumber(value, assetDecimalPlaces)
: '';
},
},
{
headerName: t('Vega time'),
field: 'vegaTime',
valueFormatter: ({
value,
}: VegaValueFormatterParams<LedgerEntry, 'vegaTime'>) =>
value ? getDateTimeFormat().format(fromNanoSeconds(value)) : '-',
filterParams: dateRangeFilterParams,
filter: DateRangeFilter,
flex: 1,
},
],
[]
);
return (
<AgGrid
style={{ width: '100%', height: '100%' }}
ref={ref}
tooltipShowDelay={500}
defaultColDef={{
resizable: true,
sortable: true,
tooltipComponent: TransferTooltipCellComponent,
filterParams: {
...dateRangeFilterParams,
buttons: ['reset'],
},
}}
columnDefs={columnDefs}
{...props}
/>
);
}
);
}}
columnDefs={columnDefs}
{...props}
/>
);
};

View File

@ -1,11 +1,9 @@
import React, { forwardRef } from 'react';
import type { TypedDataAgGrid } from '@vegaprotocol/datagrid';
import {
AgGridLazy as AgGrid,
PriceFlashCell,
MarketNameCell,
} from '@vegaprotocol/datagrid';
import type { AgGridReact } from 'ag-grid-react';
import type { MarketMaybeWithData } from '../../markets-provider';
import { OracleStatus } from './oracle-status';
import { useColumnDefs } from './use-column-defs';
@ -43,14 +41,15 @@ const defaultColDef = {
filterParams: { buttons: ['reset'] },
minWidth: 100,
};
export const MarketListTable = forwardRef<
AgGridReact,
TypedDataAgGrid<MarketMaybeWithData> & {
onMarketClick: (marketId: string, metaKey?: boolean) => void;
SuccessorMarketRenderer?: React.FC<{ value: string }>;
}
>(({ onMarketClick, SuccessorMarketRenderer, ...props }, ref) => {
type Props = TypedDataAgGrid<MarketMaybeWithData> & {
onMarketClick: (marketId: string, metaKey?: boolean) => void;
SuccessorMarketRenderer?: React.FC<{ value: string }>;
};
export const MarketListTable = ({
onMarketClick,
SuccessorMarketRenderer,
...props
}: Props) => {
const columnDefs = useColumnDefs({ onMarketClick });
const components = {
PriceFlashCell,
@ -61,7 +60,6 @@ export const MarketListTable = forwardRef<
<AgGrid
style={{ width: '100%', height: '100%' }}
getRowId={getRowId}
ref={ref}
defaultColDef={defaultColDef}
columnDefs={columnDefs}
suppressCellFocus
@ -69,6 +67,6 @@ export const MarketListTable = forwardRef<
{...props}
/>
);
});
};
export default MarketListTable;

View File

@ -1,6 +1,5 @@
import type { MouseEvent } from 'react';
import React, { useEffect, useRef } from 'react';
import type { AgGridReact } from 'ag-grid-react';
import React, { useEffect } from 'react';
import type { CellClickedEvent } from 'ag-grid-community';
import { t } from '@vegaprotocol/i18n';
import { MarketListTable } from './market-list-table';
@ -18,8 +17,6 @@ export const MarketsContainer = ({
onSelect,
SuccessorMarketRenderer,
}: MarketsContainerProps) => {
const gridRef = useRef<AgGridReact | null>(null);
const { data, error, reload } = useDataProvider({
dataProvider,
variables: undefined,
@ -37,7 +34,6 @@ export const MarketsContainer = ({
return (
<div className="h-full relative">
<MarketListTable
ref={gridRef}
rowData={data}
onCellClicked={(cellEvent: CellClickedEvent) => {
const { data, column, event } = cellEvent;

View File

@ -2,17 +2,13 @@ import { render, screen, act } from '@testing-library/react';
import { StopOrdersManager } from './stop-orders-manager';
import * as useDataProviderHook from '@vegaprotocol/data-provider';
import type { StopOrder } from '../order-data-provider/stop-orders-data-provider';
import * as stopOrdersTableMock from '../stop-orders-table/stop-orders-table';
import { forwardRef } from 'react';
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
import { VegaWalletContext } from '@vegaprotocol/wallet';
import { MockedProvider } from '@apollo/client/testing';
// @ts-ignore StopOrdersTable is read only but we need to override with the forwardRef to
// avoid warnings about padding refs
stopOrdersTableMock.StopOrdersTable = forwardRef(() => (
<div>StopOrdersTable</div>
));
jest.mock('../stop-orders-table/stop-orders-table', () => ({
StopOrdersTable: () => <div>StopOrdersTable</div>,
}));
const generateJsx = () => {
const pubKey = '0x123';

View File

@ -8,7 +8,7 @@ import { t } from '@vegaprotocol/i18n';
import * as Schema from '@vegaprotocol/types';
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
import type { ForwardedRef } from 'react';
import { memo, forwardRef, useMemo } from 'react';
import { memo, useMemo } from 'react';
import {
AgGridLazy as AgGrid,
SetFilter,
@ -36,265 +36,252 @@ export type StopOrdersTableProps = TypedDataAgGrid<StopOrder> & {
export const StopOrdersTable = memo<
StopOrdersTableProps & { ref?: ForwardedRef<AgGridReact> }
>(
forwardRef<AgGridReact, StopOrdersTableProps>(
({ onCancel, onMarketClick, ...props }, ref) => {
const showAllActions = !props.isReadOnly;
const columnDefs: ColDef[] = useMemo(
() => [
{
headerName: t('Market'),
field: 'market.tradableInstrument.instrument.code',
cellRenderer: 'MarketNameCell',
cellRendererParams: { idPath: 'market.id', onMarketClick },
minWidth: 150,
},
{
headerName: t('Trigger'),
field: 'trigger',
cellClass: 'font-mono text-right',
type: 'rightAligned',
sortable: false,
valueFormatter: ({
data,
value,
}: VegaValueFormatterParams<StopOrder, 'trigger'>): string => {
if (data && value?.__typename === 'StopOrderPrice') {
return `${t('Mark')} ${
data?.triggerDirection ===
Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_FALLS_BELOW
? '<'
: '>'
} ${addDecimalsFormatNumber(
value.price,
data.market.decimalPlaces
)}`;
}
if (
data &&
value?.__typename === 'StopOrderTrailingPercentOffset'
) {
return `${t('Mark')} ${
data?.triggerDirection ===
Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_FALLS_BELOW
? '+'
: '-'
}${(Number(value.trailingPercentOffset) * 100).toFixed(1)}%`;
}
return '-';
},
minWidth: 100,
},
{
field: 'expiresAt',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<StopOrder, 'expiresAt'>) => {
if (
data &&
value &&
data?.expiryStrategy !==
Schema.StopOrderExpiryStrategy.EXPIRY_STRATEGY_UNSPECIFIED
) {
const expiresAt = getDateTimeFormat().format(new Date(value));
const expiryStrategy =
data.expiryStrategy ===
Schema.StopOrderExpiryStrategy.EXPIRY_STRATEGY_SUBMIT
? t('Submit')
: t('Cancels');
return `${expiryStrategy} ${expiresAt}`;
}
return '';
},
minWidth: 150,
},
{
headerName: t('Size'),
field: 'submission.size',
cellClass: 'font-mono text-right',
type: 'rightAligned',
cellClassRules: {
[positiveClassNames]: ({ data }: { data: StopOrder }) =>
data?.submission.size === Schema.Side.SIDE_BUY,
[negativeClassNames]: ({ data }: { data: StopOrder }) =>
data?.submission.size === Schema.Side.SIDE_SELL,
},
valueGetter: ({ data }: VegaValueGetterParams<StopOrder>) => {
return data?.submission.size && data.market
? toBigNum(
data.submission.size,
data.market.positionDecimalPlaces ?? 0
)
.multipliedBy(
data.submission.side === Schema.Side.SIDE_SELL ? -1 : 1
)
.toNumber()
: undefined;
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<StopOrder, 'size'>) => {
if (!data) {
return '';
}
if (!data?.market || !isNumeric(data.submission.size)) {
return '-';
}
const prefix = data
? data.submission.side === Schema.Side.SIDE_BUY
? '+'
: '-'
: '';
return (
prefix +
addDecimalsFormatNumber(
data.submission.size,
data.market.positionDecimalPlaces
>(({ onCancel, onMarketClick, ...props }: StopOrdersTableProps) => {
const showAllActions = !props.isReadOnly;
const columnDefs: ColDef[] = useMemo(
() => [
{
headerName: t('Market'),
field: 'market.tradableInstrument.instrument.code',
cellRenderer: 'MarketNameCell',
cellRendererParams: { idPath: 'market.id', onMarketClick },
minWidth: 150,
},
{
headerName: t('Trigger'),
field: 'trigger',
cellClass: 'font-mono text-right',
type: 'rightAligned',
sortable: false,
valueFormatter: ({
data,
value,
}: VegaValueFormatterParams<StopOrder, 'trigger'>): string => {
if (data && value?.__typename === 'StopOrderPrice') {
return `${t('Mark')} ${
data?.triggerDirection ===
Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_FALLS_BELOW
? '<'
: '>'
} ${addDecimalsFormatNumber(
value.price,
data.market.decimalPlaces
)}`;
}
if (data && value?.__typename === 'StopOrderTrailingPercentOffset') {
return `${t('Mark')} ${
data?.triggerDirection ===
Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_FALLS_BELOW
? '+'
: '-'
}${(Number(value.trailingPercentOffset) * 100).toFixed(1)}%`;
}
return '-';
},
minWidth: 100,
},
{
field: 'expiresAt',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<StopOrder, 'expiresAt'>) => {
if (
data &&
value &&
data?.expiryStrategy !==
Schema.StopOrderExpiryStrategy.EXPIRY_STRATEGY_UNSPECIFIED
) {
const expiresAt = getDateTimeFormat().format(new Date(value));
const expiryStrategy =
data.expiryStrategy ===
Schema.StopOrderExpiryStrategy.EXPIRY_STRATEGY_SUBMIT
? t('Submit')
: t('Cancels');
return `${expiryStrategy} ${expiresAt}`;
}
return '';
},
minWidth: 150,
},
{
headerName: t('Size'),
field: 'submission.size',
cellClass: 'font-mono text-right',
type: 'rightAligned',
cellClassRules: {
[positiveClassNames]: ({ data }: { data: StopOrder }) =>
data?.submission.size === Schema.Side.SIDE_BUY,
[negativeClassNames]: ({ data }: { data: StopOrder }) =>
data?.submission.size === Schema.Side.SIDE_SELL,
},
valueGetter: ({ data }: VegaValueGetterParams<StopOrder>) => {
return data?.submission.size && data.market
? toBigNum(
data.submission.size,
data.market.positionDecimalPlaces ?? 0
)
.multipliedBy(
data.submission.side === Schema.Side.SIDE_SELL ? -1 : 1
)
);
},
minWidth: 80,
},
{
field: 'submission.type',
filter: SetFilter,
filterParams: {
set: Schema.OrderTypeMapping,
},
cellRenderer: ({
value,
}: VegaICellRendererParams<StopOrder, 'submission.type'>) =>
value ? Schema.OrderTypeMapping[value] : '',
minWidth: 80,
},
{
field: 'status',
filter: SetFilter,
filterParams: {
set: Schema.StopOrderStatusMapping,
},
valueFormatter: ({
value,
}: VegaValueFormatterParams<StopOrder, 'status'>) => {
return value ? Schema.StopOrderStatusMapping[value] : '';
},
cellRenderer: ({
valueFormatted,
data,
}: {
valueFormatted: string;
data: StopOrder;
}) => (
<span data-testid={`order-status-${data?.id}`}>
{valueFormatted}
</span>
),
minWidth: 100,
},
{
field: 'submission.price',
type: 'rightAligned',
cellClass: 'font-mono text-right',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<StopOrder, 'submission.price'>) => {
if (!data) {
return '';
}
if (
!data?.market ||
data.submission.type === Schema.OrderType.TYPE_MARKET ||
!isNumeric(value)
) {
return '-';
}
return addDecimalsFormatNumber(value, data.market.decimalPlaces);
},
minWidth: 100,
},
{
field: 'submission.timeInForce',
filter: SetFilter,
filterParams: {
set: Schema.OrderTimeInForceMapping,
},
valueFormatter: ({
value,
}: VegaValueFormatterParams<
StopOrder,
'submission.timeInForce'
>) => {
return value ? Schema.OrderTimeInForceCode[value] : '';
},
minWidth: 150,
},
{
field: 'updatedAt',
filter: DateRangeFilter,
valueGetter: ({ data }: VegaValueGetterParams<StopOrder>) =>
data?.updatedAt || data?.createdAt,
cellRenderer: ({
data,
}: VegaICellRendererParams<StopOrder, 'createdAt'>) => {
if (!data) {
return undefined;
}
const value = data.updatedAt || data.createdAt;
return (
<span data-value={value}>
{value ? getDateTimeFormat().format(new Date(value)) : '-'}
</span>
);
},
minWidth: 150,
},
{
colId: 'actions',
...COL_DEFS.actions,
minWidth: showAllActions ? 120 : COL_DEFS.actions.minWidth,
maxWidth: showAllActions ? 120 : COL_DEFS.actions.minWidth,
cellRenderer: ({ data }: { data?: StopOrder }) => {
if (!data) return null;
.toNumber()
: undefined;
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<StopOrder, 'size'>) => {
if (!data) {
return '';
}
if (!data?.market || !isNumeric(data.submission.size)) {
return '-';
}
const prefix = data
? data.submission.side === Schema.Side.SIDE_BUY
? '+'
: '-'
: '';
return (
prefix +
addDecimalsFormatNumber(
data.submission.size,
data.market.positionDecimalPlaces
)
);
},
minWidth: 80,
},
{
field: 'submission.type',
filter: SetFilter,
filterParams: {
set: Schema.OrderTypeMapping,
},
cellRenderer: ({
value,
}: VegaICellRendererParams<StopOrder, 'submission.type'>) =>
value ? Schema.OrderTypeMapping[value] : '',
minWidth: 80,
},
{
field: 'status',
filter: SetFilter,
filterParams: {
set: Schema.StopOrderStatusMapping,
},
valueFormatter: ({
value,
}: VegaValueFormatterParams<StopOrder, 'status'>) => {
return value ? Schema.StopOrderStatusMapping[value] : '';
},
cellRenderer: ({
valueFormatted,
data,
}: {
valueFormatted: string;
data: StopOrder;
}) => (
<span data-testid={`order-status-${data?.id}`}>{valueFormatted}</span>
),
minWidth: 100,
},
{
field: 'submission.price',
type: 'rightAligned',
cellClass: 'font-mono text-right',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<StopOrder, 'submission.price'>) => {
if (!data) {
return '';
}
if (
!data?.market ||
data.submission.type === Schema.OrderType.TYPE_MARKET ||
!isNumeric(value)
) {
return '-';
}
return addDecimalsFormatNumber(value, data.market.decimalPlaces);
},
minWidth: 100,
},
{
field: 'submission.timeInForce',
filter: SetFilter,
filterParams: {
set: Schema.OrderTimeInForceMapping,
},
valueFormatter: ({
value,
}: VegaValueFormatterParams<StopOrder, 'submission.timeInForce'>) => {
return value ? Schema.OrderTimeInForceCode[value] : '';
},
minWidth: 150,
},
{
field: 'updatedAt',
filter: DateRangeFilter,
valueGetter: ({ data }: VegaValueGetterParams<StopOrder>) =>
data?.updatedAt || data?.createdAt,
cellRenderer: ({
data,
}: VegaICellRendererParams<StopOrder, 'createdAt'>) => {
if (!data) {
return undefined;
}
const value = data.updatedAt || data.createdAt;
return (
<span data-value={value}>
{value ? getDateTimeFormat().format(new Date(value)) : '-'}
</span>
);
},
minWidth: 150,
},
{
colId: 'actions',
...COL_DEFS.actions,
minWidth: showAllActions ? 120 : COL_DEFS.actions.minWidth,
maxWidth: showAllActions ? 120 : COL_DEFS.actions.minWidth,
cellRenderer: ({ data }: { data?: StopOrder }) => {
if (!data) return null;
return (
<div className="flex gap-2 items-center justify-end">
{data.status === Schema.StopOrderStatus.STATUS_PENDING &&
!props.isReadOnly && (
<ButtonLink
data-testid="cancel"
onClick={() => onCancel(data)}
>
{t('Cancel')}
</ButtonLink>
)}
</div>
);
},
},
],
[onCancel, onMarketClick, props.isReadOnly, showAllActions]
);
return (
<div className="flex gap-2 items-center justify-end">
{data.status === Schema.StopOrderStatus.STATUS_PENDING &&
!props.isReadOnly && (
<ButtonLink
data-testid="cancel"
onClick={() => onCancel(data)}
>
{t('Cancel')}
</ButtonLink>
)}
</div>
);
},
},
],
[onCancel, onMarketClick, props.isReadOnly, showAllActions]
);
return (
<AgGrid
ref={ref}
defaultColDef={{
resizable: true,
sortable: true,
filterParams: { buttons: ['reset'] },
}}
columnDefs={columnDefs}
style={{
width: '100%',
height: '100%',
}}
getRowId={({ data }) => data.id}
components={{ MarketNameCell }}
{...props}
/>
);
}
)
);
return (
<AgGrid
defaultColDef={{
resizable: true,
sortable: true,
filterParams: { buttons: ['reset'] },
}}
columnDefs={columnDefs}
style={{
width: '100%',
height: '100%',
}}
getRowId={({ data }) => data.id}
components={{ MarketNameCell }}
{...props}
/>
);
});

View File

@ -1,5 +1,5 @@
import classNames from 'classnames';
import { forwardRef, useMemo } from 'react';
import { useMemo } from 'react';
import type { CSSProperties, ReactNode } from 'react';
import type { ColDef } from 'ag-grid-community';
import type {
@ -33,7 +33,6 @@ import {
addDecimalsFormatNumber,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import type { AgGridReact } from 'ag-grid-react';
import type { Position } from './positions-data-providers';
import * as Schema from '@vegaprotocol/types';
import { PositionStatus, PositionStatusMapping } from '@vegaprotocol/types';
@ -87,383 +86,367 @@ AmountCell.displayName = 'AmountCell';
export const getRowId = ({ data }: { data: Position }) =>
`${data.partyId}-${data.marketId}`;
export const PositionsTable = forwardRef<AgGridReact, Props>(
(
{
onClose,
onMarketClick,
multipleKeys,
isReadOnly,
pubKeys,
pubKey,
...props
},
ref
) => {
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
return (
<AgGrid
style={{ width: '100%', height: '100%' }}
overlayNoRowsTemplate={t('No positions')}
getRowId={getRowId}
ref={ref}
tooltipShowDelay={500}
defaultColDef={{
resizable: true,
sortable: true,
filter: true,
filterParams: { buttons: ['reset'] },
tooltipComponent: TooltipCellComponent,
}}
components={{
AmountCell,
PriceFlashCell,
ProgressBarCell,
MarketNameCell,
}}
{...props}
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,
minWidth: 190,
}
: null,
{
headerName: t('Market'),
field: 'marketName',
cellRenderer: 'MarketNameCell',
cellRendererParams: { idPath: 'marketId', onMarketClick },
minWidth: 190,
export const PositionsTable = ({
onClose,
onMarketClick,
multipleKeys,
isReadOnly,
pubKeys,
pubKey,
...props
}: Props) => {
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
return (
<AgGrid
style={{ width: '100%', height: '100%' }}
overlayNoRowsTemplate={t('No positions')}
getRowId={getRowId}
tooltipShowDelay={500}
defaultColDef={{
resizable: true,
sortable: true,
filter: true,
filterParams: { buttons: ['reset'] },
tooltipComponent: TooltipCellComponent,
}}
components={{
AmountCell,
PriceFlashCell,
ProgressBarCell,
MarketNameCell,
}}
{...props}
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,
minWidth: 190,
}
: null,
{
headerName: t('Market'),
field: 'marketName',
cellRenderer: 'MarketNameCell',
cellRendererParams: { idPath: 'marketId', onMarketClick },
minWidth: 190,
},
{
headerName: t('Notional'),
headerTooltip: t('Mark price x open volume.'),
field: 'notional',
type: 'rightAligned',
cellClass: 'font-mono text-right',
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return !data?.notional
? undefined
: toBigNum(data.notional, data.marketDecimalPlaces).toNumber();
},
{
headerName: t('Notional'),
headerTooltip: t('Mark price x open volume.'),
field: 'notional',
type: 'rightAligned',
cellClass: 'font-mono text-right',
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return !data?.notional
? undefined
: toBigNum(
data.notional,
data.marketDecimalPlaces
).toNumber();
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'notional'>) => {
return !data || !data.notional
? '-'
: addDecimalsFormatNumber(
data.notional,
data.marketDecimalPlaces
);
},
minWidth: 80,
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'notional'>) => {
return !data || !data.notional
? '-'
: addDecimalsFormatNumber(
data.notional,
data.marketDecimalPlaces
);
},
{
headerName: t('Open volume'),
field: 'openVolume',
type: 'rightAligned',
cellClass: 'font-mono text-right',
cellClassRules: signedNumberCssClassRules,
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return data?.openVolume === undefined
? undefined
: toBigNum(
data?.openVolume,
minWidth: 80,
},
{
headerName: t('Open volume'),
field: 'openVolume',
type: 'rightAligned',
cellClass: 'font-mono text-right',
cellClassRules: signedNumberCssClassRules,
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return data?.openVolume === undefined
? undefined
: toBigNum(
data?.openVolume,
data.positionDecimalPlaces
).toNumber();
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'openVolume'>): string => {
return data?.openVolume === undefined
? ''
: volumePrefix(
addDecimalsFormatNumber(
data.openVolume,
data.positionDecimalPlaces
).toNumber();
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'openVolume'>): string => {
return data?.openVolume === undefined
? ''
: volumePrefix(
addDecimalsFormatNumber(
data.openVolume,
data.positionDecimalPlaces
)
)
);
},
cellRenderer: OpenVolumeCell,
minWidth: 100,
},
{
headerName: t('Mark price'),
field: 'markPrice',
type: 'rightAligned',
cellRenderer: PriceFlashCell,
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return !data ||
!data.markPrice ||
data.marketTradingMode ===
Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION
? undefined
: toBigNum(data.markPrice, data.marketDecimalPlaces).toNumber();
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'markPrice'>) => {
if (!data) {
return '';
}
if (
!data.markPrice ||
data.marketTradingMode ===
Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION
) {
return '-';
}
return addDecimalsFormatNumber(
data.markPrice,
data.marketDecimalPlaces
);
},
minWidth: 100,
},
{
headerName: t('Liquidation price'),
colId: 'liquidationPrice',
type: 'rightAligned',
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
if (!data) return null;
return (
<LiquidationPrice
marketId={data.marketId}
openVolume={data.openVolume}
collateralAvailable={data.totalBalance}
decimalPlaces={data.decimals}
formatDecimals={data.marketDecimalPlaces}
/>
);
},
},
{
headerName: t('Settlement asset'),
field: 'assetSymbol',
colId: 'asset',
minWidth: 100,
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
if (!data) return null;
return (
<ButtonLink
title={t('View settlement asset details')}
onClick={(e) => {
openAssetDetailsDialog(
data.assetId,
e.target as HTMLElement
);
},
cellRenderer: OpenVolumeCell,
minWidth: 100,
}}
>
{data?.assetSymbol}
</ButtonLink>
);
},
{
headerName: t('Mark price'),
field: 'markPrice',
type: 'rightAligned',
cellRenderer: PriceFlashCell,
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return !data ||
!data.markPrice ||
data.marketTradingMode ===
Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION
? undefined
: toBigNum(
data.markPrice,
data.marketDecimalPlaces
).toNumber();
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'markPrice'>) => {
if (!data) {
return '';
}
if (
!data.markPrice ||
data.marketTradingMode ===
Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION
) {
return '-';
}
return addDecimalsFormatNumber(
data.markPrice,
data.marketDecimalPlaces
);
},
minWidth: 100,
},
{
headerName: t('Entry price'),
field: 'averageEntryPrice',
type: 'rightAligned',
cellRenderer: PriceFlashCell,
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return data?.markPrice === undefined || !data
? undefined
: toBigNum(
data.averageEntryPrice,
data.marketDecimalPlaces
).toNumber();
},
{
headerName: t('Liquidation price'),
colId: 'liquidationPrice',
type: 'rightAligned',
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
if (!data) return null;
return (
<LiquidationPrice
marketId={data.marketId}
openVolume={data.openVolume}
collateralAvailable={data.totalBalance}
decimalPlaces={data.decimals}
formatDecimals={data.marketDecimalPlaces}
/>
);
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<
Position,
'averageEntryPrice'
>): string => {
if (!data) {
return '';
}
return addDecimalsFormatNumber(
data.averageEntryPrice,
data.marketDecimalPlaces
);
},
{
headerName: t('Settlement asset'),
field: 'assetSymbol',
colId: 'asset',
minWidth: 100,
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
if (!data) return null;
return (
<ButtonLink
title={t('View settlement asset details')}
onClick={(e) => {
openAssetDetailsDialog(
data.assetId,
e.target as HTMLElement
);
}}
>
{data?.assetSymbol}
</ButtonLink>
);
minWidth: 100,
},
multipleKeys
? null
: {
headerName: t('Leverage'),
field: 'currentLeverage',
type: 'rightAligned',
filter: 'agNumberColumnFilter',
cellRenderer: PriceFlashCell,
valueFormatter: ({
value,
}: VegaValueFormatterParams<Position, 'currentLeverage'>) =>
value === undefined ? '' : formatNumber(value.toString(), 1),
minWidth: 100,
},
},
{
headerName: t('Entry price'),
field: 'averageEntryPrice',
type: 'rightAligned',
cellRenderer: PriceFlashCell,
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return data?.markPrice === undefined || !data
? undefined
: toBigNum(
data.averageEntryPrice,
data.marketDecimalPlaces
).toNumber();
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<
Position,
'averageEntryPrice'
>): string => {
if (!data) {
return '';
}
return addDecimalsFormatNumber(
data.averageEntryPrice,
data.marketDecimalPlaces
);
},
minWidth: 100,
},
multipleKeys
? null
: {
headerName: t('Leverage'),
field: 'currentLeverage',
type: 'rightAligned',
filter: 'agNumberColumnFilter',
cellRenderer: PriceFlashCell,
valueFormatter: ({
value,
}: VegaValueFormatterParams<Position, 'currentLeverage'>) =>
value === undefined
? ''
: formatNumber(value.toString(), 1),
minWidth: 100,
multipleKeys
? null
: {
headerName: t('Margin allocated'),
field: 'marginAccountBalance',
type: 'rightAligned',
filter: 'agNumberColumnFilter',
cellRenderer: PriceFlashCell,
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return !data
? undefined
: toBigNum(
data.marginAccountBalance,
data.decimals
).toNumber();
},
multipleKeys
? null
: {
headerName: t('Margin allocated'),
field: 'marginAccountBalance',
type: 'rightAligned',
filter: 'agNumberColumnFilter',
cellRenderer: PriceFlashCell,
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return !data
? undefined
: toBigNum(
data.marginAccountBalance,
data.decimals
).toNumber();
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<
Position,
'marginAccountBalance'
>): string => {
if (!data) {
return '';
}
return addDecimalsFormatNumber(
data.marginAccountBalance,
data.decimals
);
},
minWidth: 100,
valueFormatter: ({
data,
}: VegaValueFormatterParams<
Position,
'marginAccountBalance'
>): string => {
if (!data) {
return '';
}
return addDecimalsFormatNumber(
data.marginAccountBalance,
data.decimals
);
},
{
headerName: t('Realised PNL'),
field: 'realisedPNL',
type: 'rightAligned',
cellClassRules: signedNumberCssClassRules,
cellClass: 'font-mono text-right',
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return !data
? undefined
: toBigNum(data.realisedPNL, data.decimals).toNumber();
minWidth: 100,
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'realisedPNL'>) => {
return !data
? ''
: addDecimalsFormatNumber(data.realisedPNL, data.decimals);
},
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.'
),
cellRenderer: PNLCell,
minWidth: 100,
{
headerName: t('Realised PNL'),
field: 'realisedPNL',
type: 'rightAligned',
cellClassRules: signedNumberCssClassRules,
cellClass: 'font-mono text-right',
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return !data
? undefined
: toBigNum(data.realisedPNL, data.decimals).toNumber();
},
{
headerName: t('Unrealised PNL'),
field: 'unrealisedPNL',
type: 'rightAligned',
cellClassRules: signedNumberCssClassRules,
cellClass: 'font-mono text-right',
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return !data
? undefined
: toBigNum(data.unrealisedPNL, data.decimals).toNumber();
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'unrealisedPNL'>) =>
!data
? ''
: addDecimalsFormatNumber(data.unrealisedPNL, data.decimals),
headerTooltip: t(
'Unrealised profit is the current profit on your open position. Margin is still allocated to your position.'
),
cellRenderer: PNLCell,
minWidth: 100,
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'realisedPNL'>) => {
return !data
? ''
: addDecimalsFormatNumber(data.realisedPNL, data.decimals);
},
{
headerName: t('Updated'),
field: 'updatedAt',
type: 'rightAligned',
filter: DateRangeFilter,
valueFormatter: ({
value,
}: VegaValueFormatterParams<Position, 'updatedAt'>) => {
if (!value) {
return '';
}
return getDateTimeFormat().format(new Date(value));
},
minWidth: 150,
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.'
),
cellRenderer: PNLCell,
minWidth: 100,
},
{
headerName: t('Unrealised PNL'),
field: 'unrealisedPNL',
type: 'rightAligned',
cellClassRules: signedNumberCssClassRules,
cellClass: 'font-mono text-right',
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return !data
? undefined
: toBigNum(data.unrealisedPNL, data.decimals).toNumber();
},
onClose && !isReadOnly
? {
...COL_DEFS.actions,
cellRenderer: ({
data,
}: VegaICellRendererParams<Position>) => {
return (
<div className="flex gap-2 items-center justify-end">
{data?.openVolume &&
data?.openVolume !== '0' &&
data.partyId === pubKey ? (
<ButtonLink
data-testid="close-position"
onClick={() => data && onClose(data)}
>
{t('Close')}
</ButtonLink>
) : null}
{data?.assetId && (
<PositionActionsDropdown assetId={data?.assetId} />
)}
</div>
);
},
minWidth: 90,
maxWidth: 90,
}
: null,
];
return columnDefs.filter<ColDef>(
(colDef: ColDef | null): colDef is ColDef => colDef !== null
);
}, [
isReadOnly,
multipleKeys,
onClose,
onMarketClick,
openAssetDetailsDialog,
pubKey,
pubKeys,
])}
/>
);
}
);
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'unrealisedPNL'>) =>
!data
? ''
: addDecimalsFormatNumber(data.unrealisedPNL, data.decimals),
headerTooltip: t(
'Unrealised profit is the current profit on your open position. Margin is still allocated to your position.'
),
cellRenderer: PNLCell,
minWidth: 100,
},
{
headerName: t('Updated'),
field: 'updatedAt',
type: 'rightAligned',
filter: DateRangeFilter,
valueFormatter: ({
value,
}: VegaValueFormatterParams<Position, 'updatedAt'>) => {
if (!value) {
return '';
}
return getDateTimeFormat().format(new Date(value));
},
minWidth: 150,
},
onClose && !isReadOnly
? {
...COL_DEFS.actions,
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
return (
<div className="flex gap-2 items-center justify-end">
{data?.openVolume &&
data?.openVolume !== '0' &&
data.partyId === pubKey ? (
<ButtonLink
data-testid="close-position"
onClick={() => data && onClose(data)}
>
{t('Close')}
</ButtonLink>
) : null}
{data?.assetId && (
<PositionActionsDropdown assetId={data?.assetId} />
)}
</div>
);
},
minWidth: 90,
maxWidth: 90,
}
: null,
];
return columnDefs.filter<ColDef>(
(colDef: ColDef | null): colDef is ColDef => colDef !== null
);
}, [
isReadOnly,
multipleKeys,
onClose,
onMarketClick,
openAssetDetailsDialog,
pubKey,
pubKeys,
])}
/>
);
};
export default PositionsTable;

View File

@ -1,9 +1,7 @@
import React from 'react';
import { useRef } from 'react';
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
import { t } from '@vegaprotocol/i18n';
import * as Types from '@vegaprotocol/types';
import type { AgGridReact } from 'ag-grid-react';
import { useColumnDefs } from './use-column-defs';
import type { ProposalListFieldsFragment } from '../../lib/proposals-data-provider/__generated__/Proposals';
import { useProposalsListQuery } from '../../lib/proposals-data-provider/__generated__/Proposals';
@ -25,7 +23,6 @@ interface ProposalListProps {
export const ProposalsList = ({
SuccessorMarketRenderer,
}: ProposalListProps) => {
const gridRef = useRef<AgGridReact | null>(null);
const { data } = useProposalsListQuery({
variables: {
proposalType: Types.ProposalType.TYPE_NEW_MARKET,
@ -40,7 +37,6 @@ export const ProposalsList = ({
return (
<div className="relative h-full">
<AgGrid
ref={gridRef}
className="w-full h-full"
columnDefs={columnDefs}
rowData={filteredData}

View File

@ -1,6 +1,4 @@
import { useDataProvider } from '@vegaprotocol/data-provider';
import type { AgGridReact } from 'ag-grid-react';
import { useRef } from 'react';
import { tradesWithMarketProvider } from './trades-data-provider';
import { TradesTable } from './trades-table';
import { useCreateOrderStore } from '@vegaprotocol/orders';
@ -11,7 +9,6 @@ interface TradesContainerProps {
}
export const TradesContainer = ({ marketId }: TradesContainerProps) => {
const gridRef = useRef<AgGridReact | null>(null);
const useOrderStoreRef = useCreateOrderStore();
const updateOrder = useOrderStoreRef((store) => store.update);
@ -22,7 +19,6 @@ export const TradesContainer = ({ marketId }: TradesContainerProps) => {
return (
<TradesTable
ref={gridRef}
rowData={data}
onClick={(price?: string) => {
if (price) {

View File

@ -1,7 +1,5 @@
import type { AgGridReact } from 'ag-grid-react';
import { useMemo } from 'react';
import type { ColDef } from 'ag-grid-community';
import { forwardRef } from 'react';
import type {
VegaICellRendererParams,
VegaValueFormatterParams,
@ -48,90 +46,87 @@ interface Props extends AgGridReactProps {
onClick?: (price?: string) => void;
}
export const TradesTable = forwardRef<AgGridReact, Props>(
({ onClick, ...props }, ref) => {
const columnDefs = useMemo<ColDef[]>(
() => [
{
headerName: t('Price'),
field: 'price',
type: 'rightAligned',
width: 130,
cellClass: changeCellClass,
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<Trade, 'price'>) => {
if (!value || !data?.market) {
return '';
}
return addDecimalsFormatNumber(value, data.market.decimalPlaces);
},
cellRenderer: ({
value,
data,
}: VegaICellRendererParams<Trade, 'price'>) => {
if (!data?.market || !value) {
return '';
}
return (
<button
onClick={() =>
onClick &&
onClick(addDecimal(value, data.market?.decimalPlaces || 0))
}
className="hover:dark:bg-vega-cdark-800 hover:bg-vega-clight-800"
>
{addDecimalsFormatNumber(value, data.market.decimalPlaces)}
</button>
);
},
export const TradesTable = ({ onClick, ...props }: Props) => {
const columnDefs = useMemo<ColDef[]>(
() => [
{
headerName: t('Price'),
field: 'price',
type: 'rightAligned',
width: 130,
cellClass: changeCellClass,
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<Trade, 'price'>) => {
if (!value || !data?.market) {
return '';
}
return addDecimalsFormatNumber(value, data.market.decimalPlaces);
},
{
headerName: t('Size'),
field: 'size',
width: 125,
type: 'rightAligned',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<Trade, 'size'>) => {
if (!value || !data?.market) {
return '';
}
return addDecimalsFormatNumber(
value,
data.market.positionDecimalPlaces
);
},
cellRenderer: NumericCell,
cellRenderer: ({
value,
data,
}: VegaICellRendererParams<Trade, 'price'>) => {
if (!data?.market || !value) {
return '';
}
return (
<button
onClick={() =>
onClick &&
onClick(addDecimal(value, data.market?.decimalPlaces || 0))
}
className="hover:dark:bg-vega-cdark-800 hover:bg-vega-clight-800"
>
{addDecimalsFormatNumber(value, data.market.decimalPlaces)}
</button>
);
},
{
headerName: t('Created at'),
field: 'createdAt',
type: 'rightAligned',
width: 170,
cellClass: 'text-right',
valueFormatter: ({
},
{
headerName: t('Size'),
field: 'size',
width: 125,
type: 'rightAligned',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<Trade, 'size'>) => {
if (!value || !data?.market) {
return '';
}
return addDecimalsFormatNumber(
value,
}: VegaValueFormatterParams<Trade, 'createdAt'>) => {
return value && getDateTimeFormat().format(new Date(value));
},
data.market.positionDecimalPlaces
);
},
],
[onClick]
);
return (
<AgGrid
style={{ width: '100%', height: '100%' }}
getRowId={({ data }) => data.id}
ref={ref}
defaultColDef={{
flex: 1,
}}
columnDefs={columnDefs}
{...props}
/>
);
}
);
cellRenderer: NumericCell,
},
{
headerName: t('Created at'),
field: 'createdAt',
type: 'rightAligned',
width: 170,
cellClass: 'text-right',
valueFormatter: ({
value,
}: VegaValueFormatterParams<Trade, 'createdAt'>) => {
return value && getDateTimeFormat().format(new Date(value));
},
},
],
[onClick]
);
return (
<AgGrid
style={{ width: '100%', height: '100%' }}
getRowId={({ data }) => data.id}
defaultColDef={{
flex: 1,
}}
columnDefs={columnDefs}
{...props}
/>
);
};

View File

@ -1,5 +1,4 @@
import { useEffect, useRef, useState, useMemo } from 'react';
import type { AgGridReact } from 'ag-grid-react';
import type { ColDef } from 'ag-grid-community';
import {
addDecimalsFormatNumber,
@ -40,7 +39,6 @@ export const WithdrawalsTable = ({
ready?: TimestampedWithdrawals;
delayed?: TimestampedWithdrawals;
}) => {
const gridRef = useRef<AgGridReact | null>(null);
const createWithdrawApproval = useEthWithdrawApprovalsStore(
(store) => store.create
);
@ -146,7 +144,6 @@ export const WithdrawalsTable = ({
CompleteCell,
}}
suppressCellFocus
ref={gridRef}
{...props}
/>
);