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:
macqbat 2022-06-28 11:57:47 +02:00 committed by GitHub
parent 5c57d0e433
commit 51712f4c20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 425 additions and 99 deletions

View File

@ -47,6 +47,10 @@ export interface SimpleMarkets_markets_tradableInstrument_instrument_product_set
export interface SimpleMarkets_markets_tradableInstrument_instrument_product { export interface SimpleMarkets_markets_tradableInstrument_instrument_product {
__typename: "Future"; __typename: "Future";
/**
* String representing the quote (e.g. BTCUSD -> USD is quote)
*/
quoteName: string;
/** /**
* The name of the asset (string) * The name of the asset (string)
*/ */

View File

@ -1,19 +1,5 @@
import { Intent } from '@vegaprotocol/ui-toolkit';
import { MarketState } from '@vegaprotocol/types';
import { t } from '@vegaprotocol/react-helpers'; import { t } from '@vegaprotocol/react-helpers';
import { themelite as theme } from '@vegaprotocol/tailwindcss-config';
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,
};
export const STATES_FILTER = [ export const STATES_FILTER = [
{ value: 'all', text: t('All') }, { value: 'all', text: t('All') },
@ -27,3 +13,115 @@ export const STATES_FILTER = [
{ value: 'Suspended', text: t('Suspended') }, { value: 'Suspended', text: t('Suspended') },
{ value: 'TradingTerminated', text: t('TradingTerminated') }, { 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;
}
`;

View File

@ -36,6 +36,7 @@ export const MARKETS_QUERY = gql`
product { product {
__typename __typename
... on Future { ... on Future {
quoteName
settlementAsset { settlementAsset {
symbol symbol
} }

View File

@ -1,6 +1,12 @@
import React from 'react'; import React from 'react';
import { act } from 'react-dom/test-utils'; 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 { MockedProvider } from '@apollo/client/testing';
import type { MockedResponse } from '@apollo/client/testing'; import type { MockedResponse } from '@apollo/client/testing';
import { BrowserRouter } from 'react-router-dom'; import { BrowserRouter } from 'react-router-dom';
@ -37,6 +43,7 @@ describe('SimpleMarketList', () => {
afterEach(() => { afterEach(() => {
jest.clearAllMocks(); jest.clearAllMocks();
cleanup();
}); });
it('should be properly renderer as empty', async () => { it('should be properly renderer as empty', async () => {
@ -130,8 +137,12 @@ describe('SimpleMarketList', () => {
await new Promise((resolve) => setTimeout(resolve, 0)); await new Promise((resolve) => setTimeout(resolve, 0));
}); });
await waitFor(() => { 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);
}); });
}); });

View File

@ -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 { useNavigate, useParams } from 'react-router-dom';
import { subDays } from 'date-fns'; 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 { useDataProvider } from '@vegaprotocol/react-helpers';
import { t } from '@vegaprotocol/react-helpers'; import { t } from '@vegaprotocol/react-helpers';
import { AsyncRenderer, Lozenge, Splash } from '@vegaprotocol/ui-toolkit'; import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { Button } from '@vegaprotocol/ui-toolkit'; import { ThemeContext } from '@vegaprotocol/react-helpers';
import type { MarketState } from '@vegaprotocol/types'; 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 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<{ export type RouterParams = Partial<{
product: string; product: string;
@ -22,8 +34,9 @@ export type RouterParams = Partial<{
const SimpleMarketList = () => { const SimpleMarketList = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const params = useParams<RouterParams>(); const params = useParams<RouterParams>();
const theme = useContext(ThemeContext);
const statusesRef = useRef<Record<string, MarketState | ''>>({}); const statusesRef = useRef<Record<string, MarketState | ''>>({});
const gridRef = useRef<AgGridReact | null>(null);
const variables = useMemo( const variables = useMemo(
() => ({ () => ({
CandleSince: subDays(Date.now(), 1).toJSON(), CandleSince: subDays(Date.now(), 1).toJSON(),
@ -40,7 +53,14 @@ const SimpleMarketList = () => {
update, update,
variables variables
); );
const localData = useMarketsFilterData(data || [], params); const localData: Array<SimpleMarketsType> = useMarketsFilterData(
data || [],
params
);
const handleOnGridReady = useCallback(() => {
gridRef.current?.api.sizeColumnsToFit();
}, [gridRef]);
useEffect(() => { useEffect(() => {
const statuses: Record<string, MarketState | ''> = {}; const statuses: Record<string, MarketState | ''> = {};
@ -50,6 +70,11 @@ const SimpleMarketList = () => {
statusesRef.current = statuses; statusesRef.current = statuses;
}, [data, statusesRef]); }, [data, statusesRef]);
useEffect(() => {
window.addEventListener('resize', handleOnGridReady);
return () => window.removeEventListener('resize', handleOnGridReady);
}, [handleOnGridReady]);
const onClick = useCallback( const onClick = useCallback(
(marketId) => { (marketId) => {
navigate(`/trading/${marketId}`); navigate(`/trading/${marketId}`);
@ -57,62 +82,34 @@ const SimpleMarketList = () => {
[navigate] [navigate]
); );
const { columnDefs, defaultColDef } = useColumnDefinitions({ onClick });
const getRowId = useCallback(({ data }) => data.id, []);
return ( return (
<> <div className="h-full grid grid-rows-[min-content,1fr]">
<SimpleMarketToolbar /> <SimpleMarketToolbar />
<AsyncRenderer loading={loading} error={error} data={localData}> <AsyncRenderer loading={loading} error={error} data={localData}>
{localData && localData.length > 0 ? ( <AgGrid
<ul className="mb-32 min-h-[300px]"
className="list-none relative pt-8 pb-8" defaultColDef={defaultColDef}
data-testid="simple-market-list" columnDefs={columnDefs}
> rowData={localData}
{localData?.map((market) => ( rowHeight={60}
<li customThemeParams={
className="w-full relative flex justify-start items-center no-underline box-border text-left py-8 mb-10" theme === 'dark'
key={market.id} ? constants.agGridDarkVariables
> : constants.agGridLightVariables
<div className="w-full grid sm:grid-cols-2"> }
<div className="w-full grid sm:auto-rows-auto"> onGridReady={handleOnGridReady}
<div className="font-extrabold py-2">{market.name}</div> ref={gridRef}
<SimpleMarketExpires overlayNoRowsTemplate={t('No data to display')}
tags={market.tradableInstrument.instrument.metadata.tags} suppressContextMenu
/> getRowId={getRowId}
<div className="py-2">{`${t('settled in')} ${ suppressMovableColumns
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>
)}
</AsyncRenderer> </AsyncRenderer>
</> </div>
); );
}; };

View File

@ -7,11 +7,16 @@ import type { SimpleMarkets_markets_candles } from './__generated__/SimpleMarket
describe('SimpleMarketPercentChange should parse proper change', () => { describe('SimpleMarketPercentChange should parse proper change', () => {
let candles: (SimpleMarkets_markets_candles | null)[] | null; let candles: (SimpleMarkets_markets_candles | null)[] | null;
const setValue = () => undefined;
it('empty array', () => { it('empty array', () => {
candles = []; candles = [];
render( render(
<MockedProvider> <MockedProvider>
<SimpleMarketPercentChange candles={candles} marketId={'1'} /> <SimpleMarketPercentChange
candles={candles}
marketId={'1'}
setValue={setValue}
/>
</MockedProvider> </MockedProvider>
); );
expect(screen.getByText('-')).toBeInTheDocument(); expect(screen.getByText('-')).toBeInTheDocument();
@ -20,7 +25,11 @@ describe('SimpleMarketPercentChange should parse proper change', () => {
candles = null; candles = null;
render( render(
<MockedProvider> <MockedProvider>
<SimpleMarketPercentChange candles={candles} marketId={'1'} /> <SimpleMarketPercentChange
candles={candles}
marketId={'1'}
setValue={setValue}
/>
</MockedProvider> </MockedProvider>
); );
expect(screen.getByText('-')).toBeInTheDocument(); expect(screen.getByText('-')).toBeInTheDocument();
@ -33,7 +42,11 @@ describe('SimpleMarketPercentChange should parse proper change', () => {
]; ];
render( render(
<MockedProvider> <MockedProvider>
<SimpleMarketPercentChange candles={candles} marketId={'1'} /> <SimpleMarketPercentChange
candles={candles}
marketId={'1'}
setValue={setValue}
/>
</MockedProvider> </MockedProvider>
); );
expect(screen.getByText('100.000%')).toBeInTheDocument(); expect(screen.getByText('100.000%')).toBeInTheDocument();
@ -49,7 +62,11 @@ describe('SimpleMarketPercentChange should parse proper change', () => {
]; ];
render( render(
<MockedProvider> <MockedProvider>
<SimpleMarketPercentChange candles={candles} marketId={'1'} /> <SimpleMarketPercentChange
candles={candles}
marketId={'1'}
setValue={setValue}
/>
</MockedProvider> </MockedProvider>
); );
expect(screen.getByText('-50.000%')).toBeInTheDocument(); expect(screen.getByText('-50.000%')).toBeInTheDocument();

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react'; import React, { useEffect, useState } from 'react';
import { InView } from 'react-intersection-observer'; import { InView } from 'react-intersection-observer';
import { useSubscription } from '@apollo/client'; import { useSubscription } from '@apollo/client';
import { themelite as theme } from '@vegaprotocol/tailwindcss-config'; import { themelite as theme } from '@vegaprotocol/tailwindcss-config';
@ -12,6 +12,7 @@ import { CANDLE_SUB } from './data-provider';
interface Props { interface Props {
candles: (SimpleMarkets_markets_candles | null)[] | null; candles: (SimpleMarkets_markets_candles | null)[] | null;
marketId: string; marketId: string;
setValue: (arg: unknown) => void;
} }
const getChange = ( const getChange = (
@ -51,20 +52,32 @@ const SimpleMarketPercentChangeWrapper = (props: Props) => {
return ( return (
// @ts-ignore falsy wrong type? // @ts-ignore falsy wrong type?
<InView onChange={setInView}> <InView
onChange={setInView}
className="flex h-full items-center justify-center"
>
{inView && <SimpleMarketPercentChange {...props} />} {inView && <SimpleMarketPercentChange {...props} />}
</InView> </InView>
); );
}; };
const SimpleMarketPercentChange = ({ candles, marketId }: Props) => { const SimpleMarketPercentChange = ({ candles, marketId, setValue }: Props) => {
const { data: { candles: { close = undefined } = {} } = {} } = const { data: { candles: { close = undefined } = {} } = {} } =
useSubscription<CandleLive, CandleLiveVariables>(CANDLE_SUB, { useSubscription<CandleLive, CandleLiveVariables>(CANDLE_SUB, {
variables: { marketId }, variables: { marketId },
}); });
const change = getChange(candles, close); const change = getChange(candles, close);
const color = getColor(change); 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; export default SimpleMarketPercentChangeWrapper;

View File

@ -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;

View File

@ -67,7 +67,7 @@ const SimpleMarketToolbar = () => {
); );
return ( return (
<div className="w-max mb-16 font-alpha"> <div className="w-max mb-32 font-alpha">
<ul <ul
ref={slideContRef} ref={slideContRef}
className="grid grid-flow-col auto-cols-min gap-8 relative pb-4 mb-16" className="grid grid-flow-col auto-cols-min gap-8 relative pb-4 mb-16"
@ -162,7 +162,7 @@ const SimpleMarketToolbar = () => {
</div> </div>
{activeNumber > 0 && ( {activeNumber > 0 && (
<ul <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" data-testid="market-assets-menu"
aria-label={t('Asset on the market')} aria-label={t('Asset on the market')}
> >

View 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;

View File

@ -12,9 +12,21 @@ module.exports = {
mint: '#00F780', mint: '#00F780',
pink: '#FF077F', pink: '#FF077F',
blue: '#2E6DE5', blue: '#2E6DE5',
vega: {
...theme.colors.vega,
'highlight-item': '#000',
'highlight-item-dark': '#fff',
},
'dropdown-bg-dark': theme.colors.black['100'],
}, },
fontSize: { fontSize: {
...theme.fontSize, ...theme.fontSize,
capMenu: ['15px', { lineHeight: '24px', letterSpacing: '-0.01em' }], capMenu: ['15px', { lineHeight: '24px', letterSpacing: '-0.01em' }],
market: ['15px', { lineHeight: '24px' }],
},
boxShadow: {
...theme.boxShadow,
'inset-black': '',
'inset-white': '',
}, },
}; };

View File

@ -55,6 +55,8 @@ const colours = {
'green-dark': '#008545', 'green-dark': '#008545',
red: '#FF261A', red: '#FF261A',
'red-dark': '#EB001B', 'red-dark': '#EB001B',
'highlight-item': '#FF077F',
'highlight-item-dark': '#DFFF0B',
}, },
blue: '#1DA2FB', blue: '#1DA2FB',
coral: '#FF6057', coral: '#FF6057',
@ -65,6 +67,8 @@ const colours = {
selected: '#DFFF0B', selected: '#DFFF0B',
success: '#00F780', success: '#00F780',
'danger-bg': '#9E0025', // for white text 'danger-bg': '#9E0025', // for white text
'dropdown-bg': '#FFF',
'dropdown-bg-dark': shadeOfGray(100 - 60),
}; };
const boxShadowPosition = { const boxShadowPosition = {

View File

@ -27,9 +27,16 @@ const agGridDarkVariables = `
} }
`; `;
export const AgGrid = (props: { children: ReactNode }) => ( export const AgGrid = ({
children,
customThemeParams,
}: {
children: ReactNode;
customThemeParams?: string;
}) => (
<> <>
<style>{agGridDarkVariables}</style> <style>{agGridDarkVariables}</style>
{props.children} {customThemeParams && <style>{customThemeParams}</style>}
{children}
</> </>
); );

View File

@ -8,6 +8,7 @@ import 'ag-grid-community/dist/styles/ag-grid.css';
interface GridProps { interface GridProps {
children: ReactNode; children: ReactNode;
customThemeParams: string;
} }
const AgGridLightTheme = dynamic<GridProps>( const AgGridLightTheme = dynamic<GridProps>(
@ -24,11 +25,13 @@ export const AgGridThemed = ({
style, style,
className, className,
gridRef, gridRef,
customThemeParams = '',
...props ...props
}: (AgGridReactProps | AgReactUiProps) & { }: (AgGridReactProps | AgReactUiProps) & {
style?: React.CSSProperties; style?: React.CSSProperties;
className?: string; className?: string;
gridRef?: React.ForwardedRef<AgGridReact>; gridRef?: React.ForwardedRef<AgGridReact>;
customThemeParams?: string;
}) => { }) => {
const theme = useContext(ThemeContext); const theme = useContext(ThemeContext);
const defaultProps = { rowHeight: 20, headerHeight: 22 }; const defaultProps = { rowHeight: 20, headerHeight: 22 };
@ -40,11 +43,11 @@ export const AgGridThemed = ({
style={style} style={style}
> >
{theme === 'dark' ? ( {theme === 'dark' ? (
<AgGridDarkTheme> <AgGridDarkTheme customThemeParams={customThemeParams}>
<AgGridReact {...defaultProps} {...props} ref={gridRef} /> <AgGridReact {...defaultProps} {...props} ref={gridRef} />
</AgGridDarkTheme> </AgGridDarkTheme>
) : ( ) : (
<AgGridLightTheme> <AgGridLightTheme customThemeParams={customThemeParams}>
<AgGridReact {...defaultProps} {...props} ref={gridRef} /> <AgGridReact {...defaultProps} {...props} ref={gridRef} />
</AgGridLightTheme> </AgGridLightTheme>
)} )}

View File

@ -11,6 +11,7 @@ type Props = (AgGridReactProps | AgReactUiProps) & {
style?: React.CSSProperties; style?: React.CSSProperties;
className?: string; className?: string;
gridRef?: React.Ref<AgGridReact>; gridRef?: React.Ref<AgGridReact>;
customThemeParams?: string;
}; };
// https://stackoverflow.com/questions/69433673/nextjs-reactdomserver-does-not-yet-support-suspense // https://stackoverflow.com/questions/69433673/nextjs-reactdomserver-does-not-yet-support-suspense

View File

@ -27,9 +27,16 @@ const agGridLightVariables = `
} }
`; `;
export const AgGrid = (props: { children: ReactNode }) => ( export const AgGrid = ({
children,
customThemeParams,
}: {
children: ReactNode;
customThemeParams?: string;
}) => (
<> <>
<style>{agGridLightVariables}</style> <style>{agGridLightVariables}</style>
{props.children} {customThemeParams && <style>{customThemeParams}</style>}
{children}
</> </>
); );

View File

@ -13,7 +13,7 @@ const itemClass = classNames(
'hover:cursor-pointer', 'hover:cursor-pointer',
'select-none', 'select-none',
'whitespace-nowrap', '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:text-white dark:focus:text-black',
'focus:outline-none' 'focus:outline-none'
); );
@ -23,7 +23,7 @@ function getItemClasses(inset: boolean, checked?: boolean) {
itemClass, itemClass,
inset ? 'pl-28' : 'pl-8', inset ? 'pl-28' : 'pl-8',
checked 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' : 'text-black dark:text-white'
); );
} }
@ -73,7 +73,7 @@ export const DropdownMenuContent = forwardRef<
{...contentProps} {...contentProps}
ref={forwardedRef} ref={forwardedRef}
className={classNames( 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 className
)} )}
/> />