Commit 695a0322 authored by tom's avatar tom

tokens page

parent b9417e2e
...@@ -4,12 +4,12 @@ import React from 'react'; ...@@ -4,12 +4,12 @@ import React from 'react';
import PageNextJs from 'nextjs/PageNextJs'; import PageNextJs from 'nextjs/PageNextJs';
// const Tokens = dynamic(() => import('ui/pages/Tokens'), { ssr: false }); const Tokens = dynamic(() => import('ui/pages/Tokens'), { ssr: false });
const Page: NextPage = () => { const Page: NextPage = () => {
return ( return (
<PageNextJs pathname="/tokens"> <PageNextJs pathname="/tokens">
{ /* <Tokens/> */ } <Tokens/>
</PageNextJs> </PageNextJs>
); );
}; };
......
...@@ -34,4 +34,4 @@ export interface TokensSorting { ...@@ -34,4 +34,4 @@ export interface TokensSorting {
export type TokensSortingField = TokensSorting['sort']; export type TokensSortingField = TokensSorting['sort'];
export type TokensSortingValue = `${ TokensSortingField }-${ TokensSorting['order'] }`; export type TokensSortingValue = `${ TokensSortingField }-${ TokensSorting['order'] }` | 'default';
...@@ -2,9 +2,9 @@ import { Box } from '@chakra-ui/react'; ...@@ -2,9 +2,9 @@ import { Box } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import type { TabItemRegular } from 'toolkit/components/AdaptiveTabs/types';
import type { TokenType } from 'types/api/token'; import type { TokenType } from 'types/api/token';
import type { TokensSortingValue, TokensSortingField, TokensSorting } from 'types/api/tokens'; import type { TokensSortingValue, TokensSortingField, TokensSorting } from 'types/api/tokens';
import type { RoutedTab } from 'ui/shared/Tabs/types';
import config from 'configs/app'; import config from 'configs/app';
import useDebounce from 'lib/hooks/useDebounce'; import useDebounce from 'lib/hooks/useDebounce';
...@@ -12,13 +12,13 @@ import useIsMobile from 'lib/hooks/useIsMobile'; ...@@ -12,13 +12,13 @@ import useIsMobile from 'lib/hooks/useIsMobile';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import { TOKEN_INFO_ERC_20 } from 'stubs/token'; import { TOKEN_INFO_ERC_20 } from 'stubs/token';
import { generateListStub } from 'stubs/utils'; import { generateListStub } from 'stubs/utils';
import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs';
import PopoverFilter from 'ui/shared/filters/PopoverFilter'; import PopoverFilter from 'ui/shared/filters/PopoverFilter';
import TokenTypeFilter from 'ui/shared/filters/TokenTypeFilter'; import TokenTypeFilter from 'ui/shared/filters/TokenTypeFilter';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import getSortParamsFromValue from 'ui/shared/sort/getSortParamsFromValue'; import getSortParamsFromValue from 'ui/shared/sort/getSortParamsFromValue';
import getSortValueFromQuery from 'ui/shared/sort/getSortValueFromQuery'; import getSortValueFromQuery from 'ui/shared/sort/getSortValueFromQuery';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import TokensList from 'ui/tokens/Tokens'; import TokensList from 'ui/tokens/Tokens';
import TokensActionBar from 'ui/tokens/TokensActionBar'; import TokensActionBar from 'ui/tokens/TokensActionBar';
import TokensBridgedChainsFilter from 'ui/tokens/TokensBridgedChainsFilter'; import TokensBridgedChainsFilter from 'ui/tokens/TokensBridgedChainsFilter';
...@@ -48,7 +48,7 @@ const Tokens = () => { ...@@ -48,7 +48,7 @@ const Tokens = () => {
const q = getQueryParamString(router.query.q); const q = getQueryParamString(router.query.q);
const [ searchTerm, setSearchTerm ] = React.useState<string>(q ?? ''); const [ searchTerm, setSearchTerm ] = React.useState<string>(q ?? '');
const [ sort, setSort ] = React.useState<TokensSortingValue | undefined>(getSortValueFromQuery<TokensSortingValue>(router.query, SORT_OPTIONS)); const [ sort, setSort ] = React.useState<TokensSortingValue>(getSortValueFromQuery<TokensSortingValue>(router.query, SORT_OPTIONS) ?? 'default');
const [ tokenTypes, setTokenTypes ] = React.useState<Array<TokenType> | undefined>(getTokenFilterValue(router.query.type)); const [ tokenTypes, setTokenTypes ] = React.useState<Array<TokenType> | undefined>(getTokenFilterValue(router.query.type));
const [ bridgeChains, setBridgeChains ] = React.useState<Array<string> | undefined>(getBridgedChainsFilterValue(router.query.chain_ids)); const [ bridgeChains, setBridgeChains ] = React.useState<Array<string> | undefined>(getBridgedChainsFilterValue(router.query.chain_ids));
...@@ -91,14 +91,14 @@ const Tokens = () => { ...@@ -91,14 +91,14 @@ const Tokens = () => {
setBridgeChains(value); setBridgeChains(value);
}, [ debouncedSearchTerm, tokensQuery ]); }, [ debouncedSearchTerm, tokensQuery ]);
const handleSortChange = React.useCallback((value?: TokensSortingValue) => { const handleSortChange = React.useCallback((value: TokensSortingValue) => {
setSort(value); setSort(value);
tokensQuery.onSortingChange(getSortParamsFromValue(value)); tokensQuery.onSortingChange(getSortParamsFromValue(value));
}, [ tokensQuery ]); }, [ tokensQuery ]);
const handleTabChange = React.useCallback(() => { const handleTabChange = React.useCallback(() => {
setSearchTerm(''); setSearchTerm('');
setSort(undefined); setSort('default');
setTokenTypes(undefined); setTokenTypes(undefined);
setBridgeChains(undefined); setBridgeChains(undefined);
}, []); }, []);
...@@ -144,7 +144,7 @@ const Tokens = () => { ...@@ -144,7 +144,7 @@ const Tokens = () => {
); );
})(); })();
const tabs: Array<RoutedTab> = [ const tabs: Array<TabItemRegular> = [
{ {
id: 'all', id: 'all',
title: 'All', title: 'All',
...@@ -185,11 +185,11 @@ const Tokens = () => { ...@@ -185,11 +185,11 @@ const Tokens = () => {
{ !hasMultipleTabs && !isMobile && actionBar } { !hasMultipleTabs && !isMobile && actionBar }
<RoutedTabs <RoutedTabs
tabs={ tabs } tabs={ tabs }
tabListProps={ isMobile ? undefined : TAB_LIST_PROPS } listProps={ isMobile ? undefined : TAB_LIST_PROPS }
rightSlot={ hasMultipleTabs && !isMobile ? actionBar : null } rightSlot={ hasMultipleTabs && !isMobile ? actionBar : null }
rightSlotProps={ !isMobile ? TABS_RIGHT_SLOT_PROPS : undefined } rightSlotProps={ !isMobile ? TABS_RIGHT_SLOT_PROPS : undefined }
stickyEnabled={ !isMobile } stickyEnabled={ !isMobile }
onTabChange={ handleTabChange } onValueChange={ handleTabChange }
/> />
</> </>
); );
......
export default function getSortParamsFromValue<SortValue extends string, SortField extends string, SortOrder extends string>(val?: SortValue) { export default function getSortParamsFromValue<SortValue extends string, SortField extends string, SortOrder extends string>(val?: SortValue) {
if (!val) { if (!val || val === 'default') {
return undefined; return undefined;
} }
......
...@@ -22,6 +22,8 @@ const txSortingOptions = createListCollection({ ...@@ -22,6 +22,8 @@ const txSortingOptions = createListCollection({
items: SORT_OPTIONS, items: SORT_OPTIONS,
}); });
// TODO @tom2drum + tanya: select with search
const SelectShowcase = () => { const SelectShowcase = () => {
const [ hasActiveFilter, setHasActiveFilter ] = React.useState(false); const [ hasActiveFilter, setHasActiveFilter ] = React.useState(false);
......
import { Hide, Show } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { TokensSortingValue } from 'types/api/tokens'; import type { TokensSortingValue } from 'types/api/tokens';
...@@ -13,8 +13,8 @@ import TokensTable from './TokensTable'; ...@@ -13,8 +13,8 @@ import TokensTable from './TokensTable';
interface Props { interface Props {
query: QueryWithPagesResult<'tokens'> | QueryWithPagesResult<'tokens_bridged'>; query: QueryWithPagesResult<'tokens'> | QueryWithPagesResult<'tokens_bridged'>;
onSortChange: () => void; onSortChange: (value: TokensSortingValue) => void;
sort: TokensSortingValue | undefined; sort: TokensSortingValue;
actionBar?: React.ReactNode; actionBar?: React.ReactNode;
hasActiveFilters: boolean; hasActiveFilters: boolean;
description?: React.ReactNode; description?: React.ReactNode;
...@@ -31,7 +31,7 @@ const Tokens = ({ query, onSortChange, sort, actionBar, description, hasActiveFi ...@@ -31,7 +31,7 @@ const Tokens = ({ query, onSortChange, sort, actionBar, description, hasActiveFi
const content = data?.items ? ( const content = data?.items ? (
<> <>
<Show below="lg" ssr={ false }> <Box hideFrom="lg">
{ description } { description }
{ data.items.map((item, index) => ( { data.items.map((item, index) => (
<TokensListItem <TokensListItem
...@@ -42,8 +42,8 @@ const Tokens = ({ query, onSortChange, sort, actionBar, description, hasActiveFi ...@@ -42,8 +42,8 @@ const Tokens = ({ query, onSortChange, sort, actionBar, description, hasActiveFi
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
/> />
)) } )) }
</Show> </Box>
<Hide below="lg" ssr={ false }> <Box hideBelow="lg">
{ description } { description }
<TokensTable <TokensTable
items={ data.items } items={ data.items }
...@@ -53,22 +53,23 @@ const Tokens = ({ query, onSortChange, sort, actionBar, description, hasActiveFi ...@@ -53,22 +53,23 @@ const Tokens = ({ query, onSortChange, sort, actionBar, description, hasActiveFi
sorting={ sort } sorting={ sort }
top={ tableTop } top={ tableTop }
/> />
</Hide> </Box>
</> </>
) : null; ) : null;
return ( return (
<DataListDisplay <DataListDisplay
isError={ isError } isError={ isError }
items={ data?.items } itemsNum={ data?.items.length }
emptyText="There are no tokens." emptyText="There are no tokens."
filterProps={{ filterProps={{
emptyFilteredText: `Couldn${ apos }t find token that matches your filter query.`, emptyFilteredText: `Couldn${ apos }t find token that matches your filter query.`,
hasActiveFilters, hasActiveFilters,
}} }}
content={ content }
actionBar={ query.pagination.isVisible || hasActiveFilters ? actionBar : null } actionBar={ query.pagination.isVisible || hasActiveFilters ? actionBar : null }
/> >
{ content }
</DataListDisplay>
); );
}; };
......
import { HStack } from '@chakra-ui/react'; import { createListCollection, HStack } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { TokensSortingValue } from 'types/api/tokens'; import type { TokensSortingValue } from 'types/api/tokens';
...@@ -10,12 +10,16 @@ import Pagination from 'ui/shared/pagination/Pagination'; ...@@ -10,12 +10,16 @@ import Pagination from 'ui/shared/pagination/Pagination';
import Sort from 'ui/shared/sort/Sort'; import Sort from 'ui/shared/sort/Sort';
import { SORT_OPTIONS } from 'ui/tokens/utils'; import { SORT_OPTIONS } from 'ui/tokens/utils';
const sortCollection = createListCollection({
items: SORT_OPTIONS,
});
interface Props { interface Props {
pagination: PaginationParams; pagination: PaginationParams;
searchTerm: string | undefined; searchTerm: string | undefined;
onSearchChange: (value: string) => void; onSearchChange: (value: string) => void;
sort: TokensSortingValue | undefined; sort: TokensSortingValue;
onSortChange: () => void; onSortChange: (value: TokensSortingValue) => void;
filter: React.ReactNode; filter: React.ReactNode;
inTabsSlot?: boolean; inTabsSlot?: boolean;
} }
...@@ -30,10 +34,14 @@ const TokensActionBar = ({ ...@@ -30,10 +34,14 @@ const TokensActionBar = ({
inTabsSlot, inTabsSlot,
}: Props) => { }: Props) => {
const handleSortChange = React.useCallback(({ value }: { value: Array<string> }) => {
onSortChange(value[0] as TokensSortingValue);
}, [ onSortChange ]);
const searchInput = ( const searchInput = (
<FilterInput <FilterInput
w={{ base: '100%', lg: '360px' }} w={{ base: '100%', lg: '360px' }}
size="xs" size="sm"
onChange={ onSearchChange } onChange={ onSearchChange }
placeholder="Token name or symbol" placeholder="Token name or symbol"
initialValue={ searchTerm } initialValue={ searchTerm }
...@@ -42,13 +50,13 @@ const TokensActionBar = ({ ...@@ -42,13 +50,13 @@ const TokensActionBar = ({
return ( return (
<> <>
<HStack spacing={ 3 } mb={ 6 } display={{ base: 'flex', lg: 'none' }}> <HStack gap={ 3 } mb={ 6 } display={{ base: 'flex', lg: 'none' }}>
{ filter } { filter }
<Sort <Sort
name="tokens_sorting" name="tokens_sorting"
defaultValue={ sort } defaultValue={ [ sort ] }
options={ SORT_OPTIONS } collection={ sortCollection }
onChange={ onSortChange } onValueChange={ handleSortChange }
/> />
{ searchInput } { searchInput }
</HStack> </HStack>
...@@ -58,7 +66,7 @@ const TokensActionBar = ({ ...@@ -58,7 +66,7 @@ const TokensActionBar = ({
justifyContent={ inTabsSlot ? 'space-between' : undefined } justifyContent={ inTabsSlot ? 'space-between' : undefined }
display={{ base: pagination.isVisible ? 'flex' : 'none', lg: 'flex' }} display={{ base: pagination.isVisible ? 'flex' : 'none', lg: 'flex' }}
> >
<HStack spacing={ 3 } display={{ base: 'none', lg: 'flex' }}> <HStack gap={ 3 } display={{ base: 'none', lg: 'flex' }}>
{ filter } { filter }
{ searchInput } { searchInput }
</HStack> </HStack>
......
import { CheckboxGroup, Checkbox, Text, Flex, Link, useCheckboxGroup, chakra } from '@chakra-ui/react'; import { CheckboxGroup, Text, Flex, useCheckboxGroup, chakra, Fieldset } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import config from 'configs/app'; import config from 'configs/app';
import { Checkbox } from 'toolkit/chakra/checkbox';
import { Link } from 'toolkit/chakra/link';
const feature = config.features.bridgedTokens; const feature = config.features.bridgedTokens;
...@@ -33,7 +35,7 @@ const TokensBridgedChainsFilter = ({ onChange, defaultValue }: Props) => { ...@@ -33,7 +35,7 @@ const TokensBridgedChainsFilter = ({ onChange, defaultValue }: Props) => {
return ( return (
<> <>
<Flex justifyContent="space-between" fontSize="sm"> <Flex justifyContent="space-between" fontSize="sm">
<Text fontWeight={ 600 } variant="secondary">Show bridged tokens from</Text> <Text fontWeight={ 600 } color="text.secondary">Show bridged tokens from</Text>
<Link <Link
onClick={ handleReset } onClick={ handleReset }
color={ value.length > 0 ? 'link' : 'text_secondary' } color={ value.length > 0 ? 'link' : 'text_secondary' }
...@@ -44,14 +46,18 @@ const TokensBridgedChainsFilter = ({ onChange, defaultValue }: Props) => { ...@@ -44,14 +46,18 @@ const TokensBridgedChainsFilter = ({ onChange, defaultValue }: Props) => {
Reset Reset
</Link> </Link>
</Flex> </Flex>
<CheckboxGroup size="lg" onChange={ handleChange } value={ value }> <Fieldset.Root>
<CheckboxGroup defaultValue={ defaultValue } onValueChange={ handleChange } value={ value } name="bridged_token_chain">
<Fieldset.Content>
{ feature.chains.map(({ title, id, short_title: shortTitle }) => ( { feature.chains.map(({ title, id, short_title: shortTitle }) => (
<Checkbox key={ id } value={ id } fontSize="md" whiteSpace="pre-wrap"> <Checkbox key={ id } value={ id } textStyle="md" whiteSpace="pre-wrap">
<span>{ title }</span> <span>{ title }</span>
<chakra.span color="text_secondary"> ({ shortTitle })</chakra.span> <chakra.span color="text_secondary"> ({ shortTitle })</chakra.span>
</Checkbox> </Checkbox>
)) } )) }
</Fieldset.Content>
</CheckboxGroup> </CheckboxGroup>
</Fieldset.Root>
</> </>
); );
}; };
......
...@@ -7,9 +7,9 @@ import type { TokenInfo } from 'types/api/token'; ...@@ -7,9 +7,9 @@ import type { TokenInfo } from 'types/api/token';
import config from 'configs/app'; import config from 'configs/app';
import getItemIndex from 'lib/getItemIndex'; import getItemIndex from 'lib/getItemIndex';
import { getTokenTypeName } from 'lib/token/tokenTypes'; import { getTokenTypeName } from 'lib/token/tokenTypes';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { Tag } from 'toolkit/chakra/tag';
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet'; import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
import Skeleton from 'ui/shared/chakra/Skeleton';
import Tag from 'ui/shared/chakra/Tag';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TokenEntity from 'ui/shared/entities/token/TokenEntity'; import TokenEntity from 'ui/shared/entities/token/TokenEntity';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
...@@ -57,14 +57,14 @@ const TokensTableItem = ({ ...@@ -57,14 +57,14 @@ const TokensTableItem = ({
jointSymbol jointSymbol
noCopy noCopy
w="auto" w="auto"
fontSize="sm" textStyle="sm"
fontWeight="700" fontWeight="700"
/> />
<Flex ml={ 3 } flexShrink={ 0 } columnGap={ 1 }> <Flex ml={ 3 } flexShrink={ 0 } columnGap={ 1 }>
<Tag isLoading={ isLoading }>{ getTokenTypeName(type) }</Tag> <Tag loading={ isLoading }>{ getTokenTypeName(type) }</Tag>
{ bridgedChainTag && <Tag isLoading={ isLoading }>{ bridgedChainTag }</Tag> } { bridgedChainTag && <Tag loading={ isLoading }>{ bridgedChainTag }</Tag> }
</Flex> </Flex>
<Skeleton isLoaded={ !isLoading } fontSize="sm" ml="auto" color="text_secondary" minW="24px" textAlign="right" lineHeight={ 6 }> <Skeleton loading={ isLoading } textStyle="sm" ml="auto" color="text.secondary" minW="24px" textAlign="right">
<span>{ getItemIndex(index, page) }</span> <span>{ getItemIndex(index, page) }</span>
</Skeleton> </Skeleton>
</GridItem> </GridItem>
...@@ -79,22 +79,22 @@ const TokensTableItem = ({ ...@@ -79,22 +79,22 @@ const TokensTableItem = ({
<AddressAddToWallet token={ token } isLoading={ isLoading }/> <AddressAddToWallet token={ token } isLoading={ isLoading }/>
</Flex> </Flex>
{ exchangeRate && ( { exchangeRate && (
<HStack spacing={ 3 }> <HStack gap={ 3 }>
<Skeleton isLoaded={ !isLoading } fontSize="sm" fontWeight={ 500 }>Price</Skeleton> <Skeleton loading={ isLoading } textStyle="sm" fontWeight={ 500 }>Price</Skeleton>
<Skeleton isLoaded={ !isLoading } fontSize="sm" color="text_secondary"> <Skeleton loading={ isLoading } textStyle="sm" color="text.secondary">
<span>${ Number(exchangeRate).toLocaleString(undefined, { minimumSignificantDigits: 4 }) }</span> <span>${ Number(exchangeRate).toLocaleString(undefined, { minimumSignificantDigits: 4 }) }</span>
</Skeleton> </Skeleton>
</HStack> </HStack>
) } ) }
{ marketCap && ( { marketCap && (
<HStack spacing={ 3 }> <HStack gap={ 3 }>
<Skeleton isLoaded={ !isLoading } fontSize="sm" fontWeight={ 500 }>On-chain market cap</Skeleton> <Skeleton loading={ isLoading } textStyle="sm" fontWeight={ 500 }>On-chain market cap</Skeleton>
<Skeleton isLoaded={ !isLoading } fontSize="sm" color="text_secondary"><span>{ BigNumber(marketCap).toFormat() }</span></Skeleton> <Skeleton loading={ isLoading } textStyle="sm" color="text.secondary"><span>{ BigNumber(marketCap).toFormat() }</span></Skeleton>
</HStack> </HStack>
) } ) }
<HStack spacing={ 3 }> <HStack gap={ 3 }>
<Skeleton isLoaded={ !isLoading } fontSize="sm" fontWeight={ 500 }>Holders</Skeleton> <Skeleton loading={ isLoading } textStyle="sm" fontWeight={ 500 }>Holders</Skeleton>
<Skeleton isLoaded={ !isLoading } fontSize="sm" color="text_secondary"><span>{ Number(holders).toLocaleString() }</span></Skeleton> <Skeleton loading={ isLoading } textStyle="sm" color="text.secondary"><span>{ Number(holders).toLocaleString() }</span></Skeleton>
</HStack> </HStack>
</ListItemMobile> </ListItemMobile>
); );
......
import { Link, Table, Tbody, Th, Tr } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { TokenInfo } from 'types/api/token'; import type { TokenInfo } from 'types/api/token';
import type { TokensSortingField, TokensSortingValue } from 'types/api/tokens'; import type { TokensSortingField, TokensSortingValue } from 'types/api/tokens';
import { Link } from 'toolkit/chakra/link';
import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import { default as getNextSortValueShared } from 'ui/shared/sort/getNextSortValue'; import { default as getNextSortValueShared } from 'ui/shared/sort/getNextSortValue';
import { default as Thead } from 'ui/shared/TheadSticky';
import TokensTableItem from './TokensTableItem'; import TokensTableItem from './TokensTableItem';
const SORT_SEQUENCE: Record<TokensSortingField, Array<TokensSortingValue | undefined>> = { const SORT_SEQUENCE: Record<TokensSortingField, Array<TokensSortingValue>> = {
fiat_value: [ 'fiat_value-desc', 'fiat_value-asc', undefined ], fiat_value: [ 'fiat_value-desc', 'fiat_value-asc', 'default' ],
holder_count: [ 'holder_count-desc', 'holder_count-asc', undefined ], holder_count: [ 'holder_count-desc', 'holder_count-asc', 'default' ],
circulating_market_cap: [ 'circulating_market_cap-desc', 'circulating_market_cap-asc', undefined ], circulating_market_cap: [ 'circulating_market_cap-desc', 'circulating_market_cap-asc', 'default' ],
}; };
const getNextSortValue = (getNextSortValueShared<TokensSortingField, TokensSortingValue>).bind(undefined, SORT_SEQUENCE); const getNextSortValue = (getNextSortValueShared<TokensSortingField, TokensSortingValue>).bind(undefined, SORT_SEQUENCE);
...@@ -22,8 +22,8 @@ const getNextSortValue = (getNextSortValueShared<TokensSortingField, TokensSorti ...@@ -22,8 +22,8 @@ const getNextSortValue = (getNextSortValueShared<TokensSortingField, TokensSorti
type Props = { type Props = {
items: Array<TokenInfo>; items: Array<TokenInfo>;
page: number; page: number;
sorting?: TokensSortingValue; sorting: TokensSortingValue;
setSorting: (val?: TokensSortingValue) => void; setSorting: (value: TokensSortingValue) => void;
isLoading?: boolean; isLoading?: boolean;
top?: number; top?: number;
}; };
...@@ -37,36 +37,36 @@ const TokensTable = ({ items, page, isLoading, sorting, setSorting, top }: Props ...@@ -37,36 +37,36 @@ const TokensTable = ({ items, page, isLoading, sorting, setSorting, top }: Props
}, [ sorting, setSorting ]); }, [ sorting, setSorting ]);
return ( return (
<Table> <TableRoot>
<Thead top={ top ?? ACTION_BAR_HEIGHT_DESKTOP }> <TableHeaderSticky top={ top ?? ACTION_BAR_HEIGHT_DESKTOP }>
<Tr> <TableRow>
<Th w="50%">Token</Th> <TableColumnHeader w="50%">Token</TableColumnHeader>
<Th isNumeric w="15%"> <TableColumnHeader isNumeric w="15%">
<Link onClick={ sort('fiat_value') } display="flex" justifyContent="end"> <Link onClick={ sort('fiat_value') } display="flex" justifyContent="end">
{ sorting?.includes('fiat_value') && <IconSvg name="arrows/east-mini" boxSize={ 4 } transform={ sortIconTransform }/> } { sorting?.includes('fiat_value') && <IconSvg name="arrows/east-mini" boxSize={ 4 } transform={ sortIconTransform }/> }
Price Price
</Link> </Link>
</Th> </TableColumnHeader>
<Th isNumeric w="20%"> <TableColumnHeader isNumeric w="20%">
<Link onClick={ sort('circulating_market_cap') } display="flex" justifyContent="end"> <Link onClick={ sort('circulating_market_cap') } display="flex" justifyContent="end">
{ sorting?.includes('circulating_market_cap') && <IconSvg name="arrows/east-mini" boxSize={ 4 } transform={ sortIconTransform }/> } { sorting?.includes('circulating_market_cap') && <IconSvg name="arrows/east-mini" boxSize={ 4 } transform={ sortIconTransform }/> }
On-chain market cap On-chain market cap
</Link> </Link>
</Th> </TableColumnHeader>
<Th isNumeric w="15%"> <TableColumnHeader isNumeric w="15%">
<Link onClick={ sort('holder_count') } display="flex" justifyContent="end"> <Link onClick={ sort('holder_count') } display="flex" justifyContent="end">
{ sorting?.includes('holder_count') && <IconSvg name="arrows/east-mini" boxSize={ 4 } transform={ sortIconTransform }/> } { sorting?.includes('holder_count') && <IconSvg name="arrows/east-mini" boxSize={ 4 } transform={ sortIconTransform }/> }
Holders Holders
</Link> </Link>
</Th> </TableColumnHeader>
</Tr> </TableRow>
</Thead> </TableHeaderSticky>
<Tbody> <TableBody>
{ items.map((item, index) => ( { items.map((item, index) => (
<TokensTableItem key={ item.address + (isLoading ? index : '') } token={ item } index={ index } page={ page } isLoading={ isLoading }/> <TokensTableItem key={ item.address + (isLoading ? index : '') } token={ item } index={ index } page={ page } isLoading={ isLoading }/>
)) } )) }
</Tbody> </TableBody>
</Table> </TableRoot>
); );
}; };
......
import { Flex, Td, Tr } from '@chakra-ui/react'; import { Flex } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import React from 'react'; import React from 'react';
...@@ -7,9 +7,10 @@ import type { TokenInfo } from 'types/api/token'; ...@@ -7,9 +7,10 @@ import type { TokenInfo } from 'types/api/token';
import config from 'configs/app'; import config from 'configs/app';
import getItemIndex from 'lib/getItemIndex'; import getItemIndex from 'lib/getItemIndex';
import { getTokenTypeName } from 'lib/token/tokenTypes'; import { getTokenTypeName } from 'lib/token/tokenTypes';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { TableCell, TableRow } from 'toolkit/chakra/table';
import { Tag } from 'toolkit/chakra/tag';
import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet'; import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet';
import Skeleton from 'ui/shared/chakra/Skeleton';
import Tag from 'ui/shared/chakra/Tag';
import type { EntityProps as AddressEntityProps } from 'ui/shared/entities/address/AddressEntity'; import type { EntityProps as AddressEntityProps } from 'ui/shared/entities/address/AddressEntity';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import TokenEntity from 'ui/shared/entities/token/TokenEntity'; import TokenEntity from 'ui/shared/entities/token/TokenEntity';
...@@ -57,15 +58,12 @@ const TokensTableItem = ({ ...@@ -57,15 +58,12 @@ const TokensTableItem = ({
}; };
return ( return (
<Tr <TableRow className="group">
role="group" <TableCell>
>
<Td>
<Flex alignItems="flex-start"> <Flex alignItems="flex-start">
<Skeleton <Skeleton
isLoaded={ !isLoading } loading={ isLoading }
fontSize="sm" textStyle="sm"
lineHeight="20px"
fontWeight={ 600 } fontWeight={ 600 }
mr={ 3 } mr={ 3 }
minW="28px" minW="28px"
...@@ -78,7 +76,7 @@ const TokensTableItem = ({ ...@@ -78,7 +76,7 @@ const TokensTableItem = ({
isLoading={ isLoading } isLoading={ isLoading }
jointSymbol jointSymbol
noCopy noCopy
fontSize="sm" textStyle="sm"
fontWeight="700" fontWeight="700"
/> />
<Flex columnGap={ 2 } py="5px" alignItems="center"> <Flex columnGap={ 2 } py="5px" alignItems="center">
...@@ -86,7 +84,7 @@ const TokensTableItem = ({ ...@@ -86,7 +84,7 @@ const TokensTableItem = ({
address={ tokenAddress } address={ tokenAddress }
isLoading={ isLoading } isLoading={ isLoading }
noIcon noIcon
fontSize="sm" textStyle="sm"
fontWeight={ 500 } fontWeight={ 500 }
/> />
<AddressAddToWallet <AddressAddToWallet
...@@ -98,34 +96,33 @@ const TokensTableItem = ({ ...@@ -98,34 +96,33 @@ const TokensTableItem = ({
/> />
</Flex> </Flex>
<Flex columnGap={ 1 }> <Flex columnGap={ 1 }>
<Tag isLoading={ isLoading }>{ getTokenTypeName(type) }</Tag> <Tag loading={ isLoading }>{ getTokenTypeName(type) }</Tag>
{ bridgedChainTag && <Tag isLoading={ isLoading }>{ bridgedChainTag }</Tag> } { bridgedChainTag && <Tag loading={ isLoading }>{ bridgedChainTag }</Tag> }
</Flex> </Flex>
</Flex> </Flex>
</Flex> </Flex>
</Td> </TableCell>
<Td isNumeric> <TableCell isNumeric>
<Skeleton isLoaded={ !isLoading } fontSize="sm" lineHeight="24px" fontWeight={ 500 } display="inline-block"> <Skeleton loading={ isLoading } textStyle="sm" fontWeight={ 500 } display="inline-block">
{ exchangeRate && `$${ Number(exchangeRate).toLocaleString(undefined, { minimumSignificantDigits: 4 }) }` } { exchangeRate && `$${ Number(exchangeRate).toLocaleString(undefined, { minimumSignificantDigits: 4 }) }` }
</Skeleton> </Skeleton>
</Td> </TableCell>
<Td isNumeric maxWidth="300px" width="300px"> <TableCell isNumeric maxWidth="300px" width="300px">
<Skeleton isLoaded={ !isLoading } fontSize="sm" lineHeight="24px" fontWeight={ 500 } display="inline-block"> <Skeleton loading={ isLoading } textStyle="sm" fontWeight={ 500 } display="inline-block">
{ marketCap && `$${ BigNumber(marketCap).toFormat() }` } { marketCap && `$${ BigNumber(marketCap).toFormat() }` }
</Skeleton> </Skeleton>
</Td> </TableCell>
<Td isNumeric> <TableCell isNumeric>
<Skeleton <Skeleton
isLoaded={ !isLoading } loading={ isLoading }
fontSize="sm" textStyle="sm"
lineHeight="24px"
fontWeight={ 500 } fontWeight={ 500 }
display="inline-block" display="inline-block"
> >
{ Number(holders).toLocaleString() } { Number(holders).toLocaleString() }
</Skeleton> </Skeleton>
</Td> </TableCell>
</Tr> </TableRow>
); );
}; };
......
...@@ -7,7 +7,7 @@ import { TOKEN_TYPE_IDS } from 'lib/token/tokenTypes'; ...@@ -7,7 +7,7 @@ import { TOKEN_TYPE_IDS } from 'lib/token/tokenTypes';
import type { SelectOption } from 'toolkit/chakra/select'; import type { SelectOption } from 'toolkit/chakra/select';
export const SORT_OPTIONS: Array<SelectOption<TokensSortingValue>> = [ export const SORT_OPTIONS: Array<SelectOption<TokensSortingValue>> = [
{ label: 'Default', value: undefined }, { label: 'Default', value: 'default' },
{ label: 'Price ascending', value: 'fiat_value-asc' }, { label: 'Price ascending', value: 'fiat_value-asc' },
{ label: 'Price descending', value: 'fiat_value-desc' }, { label: 'Price descending', value: 'fiat_value-desc' },
{ label: 'Holders ascending', value: 'holder_count-asc' }, { label: 'Holders ascending', value: 'holder_count-asc' },
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment