Feat/526 consolelite design update market list (#635)
* feat: [console-lite] - market list - improve list view * feat: [console-lite] - market list - add column sorting, improve ag-grid styles * feat: [console-lite] - market list - remove unnecessary changes * feat: [console-lite] - market list - fixes for eslint errors * feat: [console-lite] - market list - remove redundant changes * feat: [console-lite] - market list - add resize handler and other small improvements Co-authored-by: maciek <maciek@vegaprotocol.io>
This commit is contained in:
parent
5c57d0e433
commit
51712f4c20
@ -47,6 +47,10 @@ export interface SimpleMarkets_markets_tradableInstrument_instrument_product_set
|
||||
|
||||
export interface SimpleMarkets_markets_tradableInstrument_instrument_product {
|
||||
__typename: "Future";
|
||||
/**
|
||||
* String representing the quote (e.g. BTCUSD -> USD is quote)
|
||||
*/
|
||||
quoteName: string;
|
||||
/**
|
||||
* The name of the asset (string)
|
||||
*/
|
||||
|
@ -1,19 +1,5 @@
|
||||
import { Intent } from '@vegaprotocol/ui-toolkit';
|
||||
import { MarketState } from '@vegaprotocol/types';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
|
||||
export const MARKET_STATUS: Record<MarketState | '', Intent> = {
|
||||
[MarketState.Active]: Intent.Success,
|
||||
[MarketState.Cancelled]: Intent.Primary,
|
||||
[MarketState.Closed]: Intent.None,
|
||||
[MarketState.Pending]: Intent.Warning,
|
||||
[MarketState.Proposed]: Intent.Warning,
|
||||
[MarketState.Rejected]: Intent.Danger,
|
||||
[MarketState.Settled]: Intent.Primary,
|
||||
[MarketState.Suspended]: Intent.Warning,
|
||||
[MarketState.TradingTerminated]: Intent.Danger,
|
||||
'': Intent.Primary,
|
||||
};
|
||||
import { themelite as theme } from '@vegaprotocol/tailwindcss-config';
|
||||
|
||||
export const STATES_FILTER = [
|
||||
{ value: 'all', text: t('All') },
|
||||
@ -27,3 +13,115 @@ export const STATES_FILTER = [
|
||||
{ value: 'Suspended', text: t('Suspended') },
|
||||
{ value: 'TradingTerminated', text: t('TradingTerminated') },
|
||||
];
|
||||
|
||||
export const agGridLightVariables = `
|
||||
.ag-theme-balham {
|
||||
--ag-row-border-color: ${theme.colors.transparent};
|
||||
--ag-row-hover-color: ${theme.colors.transparent};
|
||||
--ag-font-size: 15px;
|
||||
}
|
||||
.ag-theme-balham .ag-row-hover {
|
||||
--ag-row-border-color: ${theme.colors.black[100]};
|
||||
}
|
||||
.ag-theme-balham [col-id="status"] .ag-header-cell-label,
|
||||
.ag-theme-balham [col-id="asset"] .ag-header-cell-label,
|
||||
.ag-theme-balham [col-id="change"] .ag-header-cell-label{
|
||||
justify-content: center;
|
||||
}
|
||||
.ag-theme-balham .ag-header-row .ag-header-cell:first-child{
|
||||
padding-left: 0;
|
||||
}
|
||||
.ag-theme-balham .ag-ltr .ag-header-cell::after, .ag-theme-balham .ag-ltr .ag-header-group-cell::after {
|
||||
right: 0;
|
||||
}
|
||||
.ag-theme-balham .ag-header-cell::after{
|
||||
width: 0;
|
||||
}
|
||||
.ag-theme-balham .ag-header{
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
.ag-theme-balham .ag-has-focus .ag-row.ag-row-focus .ag-cell-focus {
|
||||
outline: none;
|
||||
border-width: 0;
|
||||
}
|
||||
.ag-theme-balham .ag-header-label-icon .ag-icon{
|
||||
position: relative;
|
||||
}
|
||||
.ag-theme-balham .ag-icon::before{
|
||||
font-size: 10px;
|
||||
line-height: 12px;
|
||||
position: absolute;
|
||||
transform: rotate(45deg);
|
||||
top: -6px;
|
||||
right: -14px;
|
||||
content: "◾";
|
||||
background: -webkit-linear-gradient(135deg, rgba(0,0,0,0.54) 0%, rgba(0,0,0,0.54) 40%, rgba(0,0,0,0) 40%, rgba(0,0,0,0) 52%, rgba(0,0,0,0.54) 52%, rgba(0,0,0,0.54) 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
.ag-theme-balham .ag-icon-desc::before{
|
||||
background: -webkit-linear-gradient(135deg, #000 0%, #000 40%, rgba(0,0,0,0) 40%, rgba(0,0,0,0) 52%, rgba(0,0,0,0.54) 52%, rgba(0,0,0,0.54) 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
.ag-theme-balham .ag-icon-asc::before{
|
||||
background: -webkit-linear-gradient(135deg, rgba(0,0,0,0.54) 0%, rgba(0,0,0,0.54) 40%, rgba(0,0,0,0) 40%, rgba(0,0,0,0) 52%, #000 52%, #000 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
`;
|
||||
|
||||
export const agGridDarkVariables = `
|
||||
.ag-theme-balham-dark {
|
||||
--ag-row-border-color: ${theme.colors.transparent};
|
||||
--ag-row-hover-color: ${theme.colors.transparent};
|
||||
--ag-font-size: 15px;
|
||||
}
|
||||
.ag-theme-balham-dark .ag-row-hover {
|
||||
--ag-row-border-color: ${theme.colors.white[100]};
|
||||
}
|
||||
.ag-theme-balham-dark [col-id="status"] .ag-header-cell-label,
|
||||
.ag-theme-balham-dark [col-id="asset"] .ag-header-cell-label,
|
||||
.ag-theme-balham-dark [col-id="change"] .ag-header-cell-label{
|
||||
justify-content: center;
|
||||
}
|
||||
.ag-theme-balham-dark .ag-header-row .ag-header-cell:first-child{
|
||||
padding-left: 0;
|
||||
}
|
||||
.ag-theme-balham-dark .ag-header-cell::after{
|
||||
width: 0;
|
||||
}
|
||||
.ag-theme-balham-dark .ag-header{
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
.ag-theme-balham-dark .ag-has-focus .ag-row.ag-row-focus .ag-cell-focus {
|
||||
outline: none;
|
||||
border-width: 0;
|
||||
}
|
||||
.ag-theme-balham-dark .ag-header-label-icon .ag-icon{
|
||||
position: relative;
|
||||
}
|
||||
.ag-theme-balham-dark .ag-icon::before{
|
||||
font-size: 10px;
|
||||
line-height: 12px;
|
||||
position: absolute;
|
||||
transform: rotate(45deg);
|
||||
top: -6px;
|
||||
right: -14px;
|
||||
content: "◾";
|
||||
background: -webkit-linear-gradient(135deg, rgba(245, 245, 245, 0.64) 0%, rgba(245, 245, 245, 0.64) 40%, rgba(0,0,0,0) 40%, rgba(0,0,0,0) 52%, rgba(245, 245, 245, 0.64) 52%, rgba(245, 245, 245, 0.64) 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-position: center;
|
||||
}
|
||||
.ag-theme-balham-dark .ag-icon-desc::before{
|
||||
background: -webkit-linear-gradient(135deg, #fff 0%, #fff 40%, rgba(0,0,0,0) 40%, rgba(0,0,0,0) 52%, rgba(245, 245, 245, 0.64) 52%, rgba(245, 245, 245, 0.64) 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
.ag-theme-balham-dark .ag-icon-asc::before{
|
||||
background: -webkit-linear-gradient(135deg, rgba(245, 245, 245, 0.64) 0%, rgba(245, 245, 245, 0.64) 40%, rgba(0,0,0,0) 40%, rgba(0,0,0,0) 52%, #fff 52%, #fff 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
`;
|
||||
|
@ -36,6 +36,7 @@ export const MARKETS_QUERY = gql`
|
||||
product {
|
||||
__typename
|
||||
... on Future {
|
||||
quoteName
|
||||
settlementAsset {
|
||||
symbol
|
||||
}
|
||||
|
@ -1,6 +1,12 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import {
|
||||
render,
|
||||
screen,
|
||||
waitFor,
|
||||
cleanup,
|
||||
getAllByRole,
|
||||
} from '@testing-library/react';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
@ -37,6 +43,7 @@ describe('SimpleMarketList', () => {
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it('should be properly renderer as empty', async () => {
|
||||
@ -130,8 +137,12 @@ describe('SimpleMarketList', () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('simple-market-list')).toBeInTheDocument();
|
||||
expect(
|
||||
document.querySelector('.ag-center-cols-container')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByTestId('simple-market-list').children).toHaveLength(2);
|
||||
|
||||
const container = document.querySelector('.ag-center-cols-container');
|
||||
expect(getAllByRole(container as HTMLDivElement, 'row')).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
@ -1,17 +1,29 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import React, {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { subDays } from 'date-fns';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
|
||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { AsyncRenderer, Lozenge, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { ThemeContext } from '@vegaprotocol/react-helpers';
|
||||
import type { MarketState } from '@vegaprotocol/types';
|
||||
import SimpleMarketPercentChange from './simple-market-percent-change';
|
||||
import SimpleMarketExpires from './simple-market-expires';
|
||||
import DataProvider from './data-provider';
|
||||
import { MARKET_STATUS } from './constants';
|
||||
import SimpleMarketToolbar from './simple-market-toolbar';
|
||||
import useMarketsFilterData from '../../hooks/use-markets-filter-data';
|
||||
import useColumnDefinitions from '../../hooks/use-column-definitions';
|
||||
import DataProvider from './data-provider';
|
||||
import * as constants from './constants';
|
||||
import SimpleMarketToolbar from './simple-market-toolbar';
|
||||
import type { SimpleMarkets_markets } from './__generated__/SimpleMarkets';
|
||||
|
||||
export type SimpleMarketsType = SimpleMarkets_markets & {
|
||||
percentChange?: number | '-';
|
||||
};
|
||||
|
||||
export type RouterParams = Partial<{
|
||||
product: string;
|
||||
@ -22,8 +34,9 @@ export type RouterParams = Partial<{
|
||||
const SimpleMarketList = () => {
|
||||
const navigate = useNavigate();
|
||||
const params = useParams<RouterParams>();
|
||||
|
||||
const theme = useContext(ThemeContext);
|
||||
const statusesRef = useRef<Record<string, MarketState | ''>>({});
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
const variables = useMemo(
|
||||
() => ({
|
||||
CandleSince: subDays(Date.now(), 1).toJSON(),
|
||||
@ -40,7 +53,14 @@ const SimpleMarketList = () => {
|
||||
update,
|
||||
variables
|
||||
);
|
||||
const localData = useMarketsFilterData(data || [], params);
|
||||
const localData: Array<SimpleMarketsType> = useMarketsFilterData(
|
||||
data || [],
|
||||
params
|
||||
);
|
||||
|
||||
const handleOnGridReady = useCallback(() => {
|
||||
gridRef.current?.api.sizeColumnsToFit();
|
||||
}, [gridRef]);
|
||||
|
||||
useEffect(() => {
|
||||
const statuses: Record<string, MarketState | ''> = {};
|
||||
@ -50,6 +70,11 @@ const SimpleMarketList = () => {
|
||||
statusesRef.current = statuses;
|
||||
}, [data, statusesRef]);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('resize', handleOnGridReady);
|
||||
return () => window.removeEventListener('resize', handleOnGridReady);
|
||||
}, [handleOnGridReady]);
|
||||
|
||||
const onClick = useCallback(
|
||||
(marketId) => {
|
||||
navigate(`/trading/${marketId}`);
|
||||
@ -57,62 +82,34 @@ const SimpleMarketList = () => {
|
||||
[navigate]
|
||||
);
|
||||
|
||||
const { columnDefs, defaultColDef } = useColumnDefinitions({ onClick });
|
||||
|
||||
const getRowId = useCallback(({ data }) => data.id, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="h-full grid grid-rows-[min-content,1fr]">
|
||||
<SimpleMarketToolbar />
|
||||
<AsyncRenderer loading={loading} error={error} data={localData}>
|
||||
{localData && localData.length > 0 ? (
|
||||
<ul
|
||||
className="list-none relative pt-8 pb-8"
|
||||
data-testid="simple-market-list"
|
||||
>
|
||||
{localData?.map((market) => (
|
||||
<li
|
||||
className="w-full relative flex justify-start items-center no-underline box-border text-left py-8 mb-10"
|
||||
key={market.id}
|
||||
>
|
||||
<div className="w-full grid sm:grid-cols-2">
|
||||
<div className="w-full grid sm:auto-rows-auto">
|
||||
<div className="font-extrabold py-2">{market.name}</div>
|
||||
<SimpleMarketExpires
|
||||
tags={market.tradableInstrument.instrument.metadata.tags}
|
||||
/>
|
||||
<div className="py-2">{`${t('settled in')} ${
|
||||
market.tradableInstrument.instrument.product
|
||||
.settlementAsset.symbol
|
||||
}`}</div>
|
||||
</div>
|
||||
<div className="w-full grid sm:grid-rows-2">
|
||||
<div>
|
||||
<SimpleMarketPercentChange
|
||||
candles={market.candles}
|
||||
marketId={market.id}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Lozenge
|
||||
variant={MARKET_STATUS[market.data?.market.state || '']}
|
||||
>
|
||||
{market.data?.market.state}
|
||||
</Lozenge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute right-16 top-1/2 -translate-y-1/2">
|
||||
<Button
|
||||
onClick={() => onClick(market.id)}
|
||||
variant="inline-link"
|
||||
prependIconName="chevron-right"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<Splash>{t('No data to display')}</Splash>
|
||||
)}
|
||||
<AgGrid
|
||||
className="mb-32 min-h-[300px]"
|
||||
defaultColDef={defaultColDef}
|
||||
columnDefs={columnDefs}
|
||||
rowData={localData}
|
||||
rowHeight={60}
|
||||
customThemeParams={
|
||||
theme === 'dark'
|
||||
? constants.agGridDarkVariables
|
||||
: constants.agGridLightVariables
|
||||
}
|
||||
onGridReady={handleOnGridReady}
|
||||
ref={gridRef}
|
||||
overlayNoRowsTemplate={t('No data to display')}
|
||||
suppressContextMenu
|
||||
getRowId={getRowId}
|
||||
suppressMovableColumns
|
||||
/>
|
||||
</AsyncRenderer>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -7,11 +7,16 @@ import type { SimpleMarkets_markets_candles } from './__generated__/SimpleMarket
|
||||
|
||||
describe('SimpleMarketPercentChange should parse proper change', () => {
|
||||
let candles: (SimpleMarkets_markets_candles | null)[] | null;
|
||||
const setValue = () => undefined;
|
||||
it('empty array', () => {
|
||||
candles = [];
|
||||
render(
|
||||
<MockedProvider>
|
||||
<SimpleMarketPercentChange candles={candles} marketId={'1'} />
|
||||
<SimpleMarketPercentChange
|
||||
candles={candles}
|
||||
marketId={'1'}
|
||||
setValue={setValue}
|
||||
/>
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(screen.getByText('-')).toBeInTheDocument();
|
||||
@ -20,7 +25,11 @@ describe('SimpleMarketPercentChange should parse proper change', () => {
|
||||
candles = null;
|
||||
render(
|
||||
<MockedProvider>
|
||||
<SimpleMarketPercentChange candles={candles} marketId={'1'} />
|
||||
<SimpleMarketPercentChange
|
||||
candles={candles}
|
||||
marketId={'1'}
|
||||
setValue={setValue}
|
||||
/>
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(screen.getByText('-')).toBeInTheDocument();
|
||||
@ -33,7 +42,11 @@ describe('SimpleMarketPercentChange should parse proper change', () => {
|
||||
];
|
||||
render(
|
||||
<MockedProvider>
|
||||
<SimpleMarketPercentChange candles={candles} marketId={'1'} />
|
||||
<SimpleMarketPercentChange
|
||||
candles={candles}
|
||||
marketId={'1'}
|
||||
setValue={setValue}
|
||||
/>
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(screen.getByText('100.000%')).toBeInTheDocument();
|
||||
@ -49,7 +62,11 @@ describe('SimpleMarketPercentChange should parse proper change', () => {
|
||||
];
|
||||
render(
|
||||
<MockedProvider>
|
||||
<SimpleMarketPercentChange candles={candles} marketId={'1'} />
|
||||
<SimpleMarketPercentChange
|
||||
candles={candles}
|
||||
marketId={'1'}
|
||||
setValue={setValue}
|
||||
/>
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(screen.getByText('-50.000%')).toBeInTheDocument();
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { InView } from 'react-intersection-observer';
|
||||
import { useSubscription } from '@apollo/client';
|
||||
import { themelite as theme } from '@vegaprotocol/tailwindcss-config';
|
||||
@ -12,6 +12,7 @@ import { CANDLE_SUB } from './data-provider';
|
||||
interface Props {
|
||||
candles: (SimpleMarkets_markets_candles | null)[] | null;
|
||||
marketId: string;
|
||||
setValue: (arg: unknown) => void;
|
||||
}
|
||||
|
||||
const getChange = (
|
||||
@ -51,20 +52,32 @@ const SimpleMarketPercentChangeWrapper = (props: Props) => {
|
||||
|
||||
return (
|
||||
// @ts-ignore falsy wrong type?
|
||||
<InView onChange={setInView}>
|
||||
<InView
|
||||
onChange={setInView}
|
||||
className="flex h-full items-center justify-center"
|
||||
>
|
||||
{inView && <SimpleMarketPercentChange {...props} />}
|
||||
</InView>
|
||||
);
|
||||
};
|
||||
|
||||
const SimpleMarketPercentChange = ({ candles, marketId }: Props) => {
|
||||
const SimpleMarketPercentChange = ({ candles, marketId, setValue }: Props) => {
|
||||
const { data: { candles: { close = undefined } = {} } = {} } =
|
||||
useSubscription<CandleLive, CandleLiveVariables>(CANDLE_SUB, {
|
||||
variables: { marketId },
|
||||
});
|
||||
const change = getChange(candles, close);
|
||||
const color = getColor(change);
|
||||
return <p style={{ color }}>{change}</p>;
|
||||
useEffect(() => {
|
||||
const value = parseFloat(change);
|
||||
setValue(isNaN(value) ? '-' : value);
|
||||
}, [setValue, change]);
|
||||
|
||||
return (
|
||||
<div className="flex text-center" style={{ color }}>
|
||||
{change}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SimpleMarketPercentChangeWrapper;
|
||||
|
@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import type { SimpleMarkets_markets } from './__generated__/SimpleMarkets';
|
||||
import SimpleMarketExpires from './simple-market-expires';
|
||||
|
||||
interface Props {
|
||||
data: SimpleMarkets_markets;
|
||||
}
|
||||
|
||||
const MarketNameRenderer = ({ data }: Props) => {
|
||||
return (
|
||||
<div className="flex h-full items-center grid grid-rows-2 grid-flow-col gap-x-8 gap-y-0 grid-cols-[min-content,1fr,1fr]">
|
||||
<div className="w-60 row-span-2 bg-pink rounded-full w-44 h-44 bg-gradient-to-br from-white-60 to--white-80 opacity-30" />
|
||||
<div className="col-span-2 uppercase justify-start text-black dark:text-white text-market self-end">
|
||||
{data.name}{' '}
|
||||
<SimpleMarketExpires
|
||||
tags={data.tradableInstrument.instrument.metadata.tags}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-2 ui-small text-deemphasise dark:text-midGrey self-start leading-3">
|
||||
{data.tradableInstrument.instrument.product.quoteName}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MarketNameRenderer;
|
@ -67,7 +67,7 @@ const SimpleMarketToolbar = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="w-max mb-16 font-alpha">
|
||||
<div className="w-max mb-32 font-alpha">
|
||||
<ul
|
||||
ref={slideContRef}
|
||||
className="grid grid-flow-col auto-cols-min gap-8 relative pb-4 mb-16"
|
||||
@ -162,7 +162,7 @@ const SimpleMarketToolbar = () => {
|
||||
</div>
|
||||
{activeNumber > 0 && (
|
||||
<ul
|
||||
className="grid grid-flow-col auto-cols-min md:gap-16 sm:gap-12 pb-4 md:ml-16"
|
||||
className="grid grid-flow-col auto-cols-min md:gap-16 gap-12 pb-4 md:ml-16"
|
||||
data-testid="market-assets-menu"
|
||||
aria-label={t('Asset on the market')}
|
||||
>
|
||||
|
125
apps/simple-trading-app/src/app/hooks/use-column-definitions.tsx
Normal file
125
apps/simple-trading-app/src/app/hooks/use-column-definitions.tsx
Normal file
@ -0,0 +1,125 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import type { SimpleMarkets_markets } from '../components/simple-market-list/__generated__/SimpleMarkets';
|
||||
import MarketNameRenderer from '../components/simple-market-list/simple-market-renderer';
|
||||
import SimpleMarketPercentChange from '../components/simple-market-list/simple-market-percent-change';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import type { ValueSetterParams } from 'ag-grid-community';
|
||||
import type { SimpleMarketsType } from '../components/simple-market-list/simple-market-list';
|
||||
|
||||
interface Props {
|
||||
onClick: (marketId: string) => void;
|
||||
}
|
||||
|
||||
const useColumnDefinitions = ({ onClick }: Props) => {
|
||||
const columnDefs = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
colId: 'market',
|
||||
headerName: t('Markets'),
|
||||
headerClass: 'uppercase',
|
||||
minWidth: 300,
|
||||
field: 'name',
|
||||
cellRenderer: ({ data }: { data: SimpleMarketsType }) => (
|
||||
<MarketNameRenderer data={data} />
|
||||
),
|
||||
},
|
||||
{
|
||||
colId: 'asset',
|
||||
headerName: t('Settlement asset'),
|
||||
headerClass: 'uppercase',
|
||||
minWidth: 100,
|
||||
cellClass: 'uppercase flex h-full items-center',
|
||||
field: 'tradableInstrument.instrument.product.settlementAsset.symbol',
|
||||
cellRenderer: ({ data }: { data: SimpleMarketsType }) => (
|
||||
<div className="flex h-full items-center justify-center">
|
||||
{data.tradableInstrument.instrument.product.settlementAsset.symbol}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
colId: 'change',
|
||||
headerName: t('24h change'),
|
||||
headerClass: 'uppercase',
|
||||
field: 'percentChange',
|
||||
minWidth: 100,
|
||||
valueSetter: (params: ValueSetterParams): boolean => {
|
||||
const { oldValue, newValue, api, data } = params;
|
||||
if (oldValue !== newValue) {
|
||||
const newdata = { percentChange: newValue, ...data };
|
||||
api.applyTransaction({ update: [newdata] });
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
cellRenderer: ({
|
||||
data,
|
||||
setValue,
|
||||
}: {
|
||||
data: SimpleMarketsType;
|
||||
setValue: (arg: unknown) => void;
|
||||
}) => (
|
||||
<SimpleMarketPercentChange
|
||||
candles={data.candles}
|
||||
marketId={data.id}
|
||||
setValue={setValue}
|
||||
/>
|
||||
),
|
||||
comparator: (valueA: number | '-', valueB: number | '-') => {
|
||||
if (valueA === valueB) return 0;
|
||||
if (valueA === '-') {
|
||||
return -1;
|
||||
}
|
||||
if (valueB === '-') {
|
||||
return 1;
|
||||
}
|
||||
return valueA > valueB ? 1 : -1;
|
||||
},
|
||||
},
|
||||
{
|
||||
colId: 'status',
|
||||
headerName: t('Status'),
|
||||
field: 'data.market.state',
|
||||
headerClass: 'uppercase',
|
||||
minWidth: 100,
|
||||
cellRenderer: ({ data }: { data: SimpleMarkets_markets }) => (
|
||||
<div className="uppercase flex h-full items-center justify-center">
|
||||
<div className="border text-center px-8">
|
||||
{data.data?.market.state}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
colId: 'trade',
|
||||
headerName: '',
|
||||
headerClass: 'uppercase',
|
||||
sortable: false,
|
||||
minWidth: 100,
|
||||
cellRenderer: ({ data }: { data: SimpleMarkets_markets }) => (
|
||||
<div className="h-full flex h-full items-center justify-end">
|
||||
<Button
|
||||
onClick={() => onClick(data.id)}
|
||||
variant="inline-link"
|
||||
appendIconName="arrow-top-right"
|
||||
className="uppercase no-underline hover:no-underline"
|
||||
>
|
||||
{t('Trade')}
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
}, [onClick]);
|
||||
|
||||
const defaultColDef = useMemo(() => {
|
||||
return {
|
||||
sortable: true,
|
||||
unSortIcon: true,
|
||||
};
|
||||
}, []);
|
||||
|
||||
return { columnDefs, defaultColDef };
|
||||
};
|
||||
|
||||
export default useColumnDefinitions;
|
@ -12,9 +12,21 @@ module.exports = {
|
||||
mint: '#00F780',
|
||||
pink: '#FF077F',
|
||||
blue: '#2E6DE5',
|
||||
vega: {
|
||||
...theme.colors.vega,
|
||||
'highlight-item': '#000',
|
||||
'highlight-item-dark': '#fff',
|
||||
},
|
||||
'dropdown-bg-dark': theme.colors.black['100'],
|
||||
},
|
||||
fontSize: {
|
||||
...theme.fontSize,
|
||||
capMenu: ['15px', { lineHeight: '24px', letterSpacing: '-0.01em' }],
|
||||
market: ['15px', { lineHeight: '24px' }],
|
||||
},
|
||||
boxShadow: {
|
||||
...theme.boxShadow,
|
||||
'inset-black': '',
|
||||
'inset-white': '',
|
||||
},
|
||||
};
|
||||
|
@ -55,6 +55,8 @@ const colours = {
|
||||
'green-dark': '#008545',
|
||||
red: '#FF261A',
|
||||
'red-dark': '#EB001B',
|
||||
'highlight-item': '#FF077F',
|
||||
'highlight-item-dark': '#DFFF0B',
|
||||
},
|
||||
blue: '#1DA2FB',
|
||||
coral: '#FF6057',
|
||||
@ -65,6 +67,8 @@ const colours = {
|
||||
selected: '#DFFF0B',
|
||||
success: '#00F780',
|
||||
'danger-bg': '#9E0025', // for white text
|
||||
'dropdown-bg': '#FFF',
|
||||
'dropdown-bg-dark': shadeOfGray(100 - 60),
|
||||
};
|
||||
|
||||
const boxShadowPosition = {
|
||||
|
@ -27,9 +27,16 @@ const agGridDarkVariables = `
|
||||
}
|
||||
`;
|
||||
|
||||
export const AgGrid = (props: { children: ReactNode }) => (
|
||||
export const AgGrid = ({
|
||||
children,
|
||||
customThemeParams,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
customThemeParams?: string;
|
||||
}) => (
|
||||
<>
|
||||
<style>{agGridDarkVariables}</style>
|
||||
{props.children}
|
||||
{customThemeParams && <style>{customThemeParams}</style>}
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
|
@ -8,6 +8,7 @@ import 'ag-grid-community/dist/styles/ag-grid.css';
|
||||
|
||||
interface GridProps {
|
||||
children: ReactNode;
|
||||
customThemeParams: string;
|
||||
}
|
||||
|
||||
const AgGridLightTheme = dynamic<GridProps>(
|
||||
@ -24,11 +25,13 @@ export const AgGridThemed = ({
|
||||
style,
|
||||
className,
|
||||
gridRef,
|
||||
customThemeParams = '',
|
||||
...props
|
||||
}: (AgGridReactProps | AgReactUiProps) & {
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
gridRef?: React.ForwardedRef<AgGridReact>;
|
||||
customThemeParams?: string;
|
||||
}) => {
|
||||
const theme = useContext(ThemeContext);
|
||||
const defaultProps = { rowHeight: 20, headerHeight: 22 };
|
||||
@ -40,11 +43,11 @@ export const AgGridThemed = ({
|
||||
style={style}
|
||||
>
|
||||
{theme === 'dark' ? (
|
||||
<AgGridDarkTheme>
|
||||
<AgGridDarkTheme customThemeParams={customThemeParams}>
|
||||
<AgGridReact {...defaultProps} {...props} ref={gridRef} />
|
||||
</AgGridDarkTheme>
|
||||
) : (
|
||||
<AgGridLightTheme>
|
||||
<AgGridLightTheme customThemeParams={customThemeParams}>
|
||||
<AgGridReact {...defaultProps} {...props} ref={gridRef} />
|
||||
</AgGridLightTheme>
|
||||
)}
|
||||
|
@ -11,6 +11,7 @@ type Props = (AgGridReactProps | AgReactUiProps) & {
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
gridRef?: React.Ref<AgGridReact>;
|
||||
customThemeParams?: string;
|
||||
};
|
||||
|
||||
// https://stackoverflow.com/questions/69433673/nextjs-reactdomserver-does-not-yet-support-suspense
|
||||
|
@ -27,9 +27,16 @@ const agGridLightVariables = `
|
||||
}
|
||||
`;
|
||||
|
||||
export const AgGrid = (props: { children: ReactNode }) => (
|
||||
export const AgGrid = ({
|
||||
children,
|
||||
customThemeParams,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
customThemeParams?: string;
|
||||
}) => (
|
||||
<>
|
||||
<style>{agGridLightVariables}</style>
|
||||
{props.children}
|
||||
{customThemeParams && <style>{customThemeParams}</style>}
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
|
@ -13,7 +13,7 @@ const itemClass = classNames(
|
||||
'hover:cursor-pointer',
|
||||
'select-none',
|
||||
'whitespace-nowrap',
|
||||
'focus:bg-vega-pink dark:focus:bg-vega-yellow',
|
||||
'focus:bg-vega-highlight-item dark:focus:bg-vega-highlight-item-dark',
|
||||
'focus:text-white dark:focus:text-black',
|
||||
'focus:outline-none'
|
||||
);
|
||||
@ -23,7 +23,7 @@ function getItemClasses(inset: boolean, checked?: boolean) {
|
||||
itemClass,
|
||||
inset ? 'pl-28' : 'pl-8',
|
||||
checked
|
||||
? 'bg-vega-pink dark:bg-vega-yellow text-white dark:text-black'
|
||||
? 'bg-vega-highlight-item dark:bg-vega-highlight-item-dark text-white dark:text-black'
|
||||
: 'text-black dark:text-white'
|
||||
);
|
||||
}
|
||||
@ -73,7 +73,7 @@ export const DropdownMenuContent = forwardRef<
|
||||
{...contentProps}
|
||||
ref={forwardedRef}
|
||||
className={classNames(
|
||||
'inline-block box-border border-1 border-black bg-white dark:bg-black-60',
|
||||
'inline-block box-border border-1 border-black bg-dropdown-bg dark:bg-dropdown-bg-dark',
|
||||
className
|
||||
)}
|
||||
/>
|
||||
|
Loading…
Reference in New Issue
Block a user