Commit a51725d0 authored by tom's avatar tom

validators and coin balance tabs

parent 8600aeba
...@@ -353,6 +353,12 @@ const semanticTokens: ThemingConfig['semanticTokens'] = { ...@@ -353,6 +353,12 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
}, },
}, },
}, },
stat: {
indicator: {
up: { value: { _light: '{colors.green.500}', _dark: '{colors.green.400}' } },
down: { value: { _light: '{colors.red.600}', _dark: '{colors.red.400}' } },
},
},
heading: { heading: {
DEFAULT: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } }, DEFAULT: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
}, },
......
...@@ -24,6 +24,7 @@ const styles = { ...@@ -24,6 +24,7 @@ const styles = {
'&.address-entity_highlighted': { '&.address-entity_highlighted': {
_before: { _before: {
pr: 2, pr: 2,
width: `calc(100% + 6px + 8px)`,
}, },
}, },
}, },
......
...@@ -20,6 +20,7 @@ import { recipe as radiomark } from './radiomark.recipe'; ...@@ -20,6 +20,7 @@ import { recipe as radiomark } from './radiomark.recipe';
import { recipe as select } from './select.recipe'; import { recipe as select } from './select.recipe';
import { recipe as skeleton } from './skeleton.recipe'; import { recipe as skeleton } from './skeleton.recipe';
import { recipe as spinner } from './spinner.recipe'; import { recipe as spinner } from './spinner.recipe';
import { recipe as stat } from './stat.recipe';
import { recipe as switchRecipe } from './switch.recipe'; import { recipe as switchRecipe } from './switch.recipe';
import { recipe as table } from './table.recipe'; import { recipe as table } from './table.recipe';
import { recipe as tabs } from './tabs.recipe'; import { recipe as tabs } from './tabs.recipe';
...@@ -55,6 +56,7 @@ export const slotRecipes = { ...@@ -55,6 +56,7 @@ export const slotRecipes = {
progressCircle, progressCircle,
radioGroup, radioGroup,
select, select,
stat,
'switch': switchRecipe, 'switch': switchRecipe,
table, table,
tabs, tabs,
......
...@@ -5,7 +5,7 @@ export const recipe = defineSlotRecipe({ ...@@ -5,7 +5,7 @@ export const recipe = defineSlotRecipe({
base: { base: {
content: { content: {
outline: 0, outline: 0,
bg: 'popover,bg', bg: 'popover.bg',
boxShadow: 'popover', boxShadow: 'popover',
color: 'initial', color: 'initial',
maxHeight: 'var(--available-height)', maxHeight: 'var(--available-height)',
......
import { defineSlotRecipe } from '@chakra-ui/react';
export const recipe = defineSlotRecipe({
slots: [ 'root', 'label', 'helpText', 'valueUnit', 'valueText', 'indicator' ],
base: {
root: {
display: 'flex',
flexDirection: 'column',
gap: '1',
position: 'relative',
flex: '1',
},
label: {
display: 'inline-flex',
gap: '1.5',
alignItems: 'center',
color: 'text',
textStyle: 'sm',
},
helpText: {
color: 'text',
textStyle: 'xs',
},
valueUnit: {
color: 'text',
textStyle: 'xs',
fontWeight: 'initial',
letterSpacing: 'initial',
},
valueText: {
verticalAlign: 'baseline',
fontWeight: 'semibold',
letterSpacing: 'normal',
fontFeatureSettings: 'initial',
fontVariantNumeric: 'initial',
display: 'inline-flex',
gap: '1',
},
indicator: {
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
marginEnd: 0,
'& :where(svg)': {
w: '1em',
h: '1em',
},
'&[data-type=up]': {
color: 'stat.indicator.up',
},
'&[data-type=down]': {
color: 'stat.indicator.down',
},
},
},
variants: {
orientation: {
horizontal: {
root: {
flexDirection: 'row',
alignItems: 'center',
},
},
},
positive: {
'true': {
valueText: {
color: 'stat.indicator.up',
},
},
'false': {
valueText: {
color: 'stat.indicator.down',
},
},
},
size: {
sm: {
valueText: {
textStyle: 'sm',
},
},
md: {
valueText: {
textStyle: 'md',
},
},
lg: {
valueText: {
textStyle: 'lg',
},
},
},
},
defaultVariants: {
size: 'md',
orientation: 'horizontal',
},
});
import { Hide, Show, Table, Tbody, Th, Tr } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
...@@ -14,12 +14,12 @@ import useSocketMessage from 'lib/socket/useSocketMessage'; ...@@ -14,12 +14,12 @@ import useSocketMessage from 'lib/socket/useSocketMessage';
import { currencyUnits } from 'lib/units'; import { currencyUnits } from 'lib/units';
import { BLOCK } from 'stubs/block'; import { BLOCK } from 'stubs/block';
import { generateListStub } from 'stubs/utils'; import { generateListStub } from 'stubs/utils';
import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/pagination/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import { default as Thead } from 'ui/shared/TheadSticky';
import AddressBlocksValidatedListItem from './blocksValidated/AddressBlocksValidatedListItem'; import AddressBlocksValidatedListItem from './blocksValidated/AddressBlocksValidatedListItem';
import AddressBlocksValidatedTableItem from './blocksValidated/AddressBlocksValidatedTableItem'; import AddressBlocksValidatedTableItem from './blocksValidated/AddressBlocksValidatedTableItem';
...@@ -104,19 +104,19 @@ const AddressBlocksValidated = ({ scrollRef, shouldRender = true, isQueryEnabled ...@@ -104,19 +104,19 @@ const AddressBlocksValidated = ({ scrollRef, shouldRender = true, isQueryEnabled
const content = query.data?.items ? ( const content = query.data?.items ? (
<> <>
<Hide below="lg" ssr={ false }> <Box hideBelow="lg">
<Table style={{ tableLayout: 'auto' }}> <TableRoot style={{ tableLayout: 'auto' }}>
<Thead top={ query.pagination.isVisible ? ACTION_BAR_HEIGHT_DESKTOP : 0 }> <TableHeaderSticky top={ query.pagination.isVisible ? ACTION_BAR_HEIGHT_DESKTOP : 0 }>
<Tr> <TableRow>
<Th>Block</Th> <TableColumnHeader>Block</TableColumnHeader>
<Th>Age</Th> <TableColumnHeader>Age</TableColumnHeader>
<Th>Txn</Th> <TableColumnHeader>Txn</TableColumnHeader>
<Th>Gas used</Th> <TableColumnHeader>Gas used</TableColumnHeader>
{ !config.UI.views.block.hiddenFields?.total_reward && !config.features.rollup.isEnabled && { !config.UI.views.block.hiddenFields?.total_reward && !config.features.rollup.isEnabled &&
<Th isNumeric>Reward { currencyUnits.ether }</Th> } <TableColumnHeader isNumeric>Reward { currencyUnits.ether }</TableColumnHeader> }
</Tr> </TableRow>
</Thead> </TableHeaderSticky>
<Tbody> <TableBody>
<SocketNewItemsNotice.Desktop <SocketNewItemsNotice.Desktop
url={ window.location.href } url={ window.location.href }
num={ newItemsCount } num={ newItemsCount }
...@@ -132,10 +132,10 @@ const AddressBlocksValidated = ({ scrollRef, shouldRender = true, isQueryEnabled ...@@ -132,10 +132,10 @@ const AddressBlocksValidated = ({ scrollRef, shouldRender = true, isQueryEnabled
isLoading={ query.isPlaceholderData } isLoading={ query.isPlaceholderData }
/> />
)) } )) }
</Tbody> </TableBody>
</Table> </TableRoot>
</Hide> </Box>
<Show below="lg" ssr={ false }> <Box hideFrom="lg">
{ query.pagination.page === 1 && ( { query.pagination.page === 1 && (
<SocketNewItemsNotice.Mobile <SocketNewItemsNotice.Mobile
url={ window.location.href } url={ window.location.href }
...@@ -153,7 +153,7 @@ const AddressBlocksValidated = ({ scrollRef, shouldRender = true, isQueryEnabled ...@@ -153,7 +153,7 @@ const AddressBlocksValidated = ({ scrollRef, shouldRender = true, isQueryEnabled
isLoading={ query.isPlaceholderData } isLoading={ query.isPlaceholderData }
/> />
)) } )) }
</Show> </Box>
</> </>
) : null; ) : null;
...@@ -166,11 +166,12 @@ const AddressBlocksValidated = ({ scrollRef, shouldRender = true, isQueryEnabled ...@@ -166,11 +166,12 @@ const AddressBlocksValidated = ({ scrollRef, shouldRender = true, isQueryEnabled
return ( return (
<DataListDisplay <DataListDisplay
isError={ query.isError } isError={ query.isError }
items={ query.data?.items } itemsNum={ query.data?.items.length }
emptyText="There are no validated blocks for this address." emptyText="There are no validated blocks for this address."
content={ content }
actionBar={ actionBar } actionBar={ actionBar }
/> >
{ content }
</DataListDisplay>
); );
}; };
......
...@@ -187,7 +187,7 @@ const AddressTxs = ({ scrollRef, overloadCount = OVERLOAD_COUNT, shouldRender = ...@@ -187,7 +187,7 @@ const AddressTxs = ({ scrollRef, overloadCount = OVERLOAD_COUNT, shouldRender =
return ( return (
<> <>
{ !isMobile && ( { !isMobile && (
<ActionBar mt={ -6 }> <ActionBar>
{ filter } { filter }
{ currentAddress && csvExportLink } { currentAddress && csvExportLink }
<Pagination { ...addressTxsQuery.pagination } ml={ 8 }/> <Pagination { ...addressTxsQuery.pagination } ml={ 8 }/>
......
...@@ -7,8 +7,8 @@ import type { Block } from 'types/api/block'; ...@@ -7,8 +7,8 @@ import type { Block } from 'types/api/block';
import config from 'configs/app'; import config from 'configs/app';
import getBlockTotalReward from 'lib/block/getBlockTotalReward'; import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import { currencyUnits } from 'lib/units'; import { currencyUnits } from 'lib/units';
import { Skeleton } from 'toolkit/chakra/skeleton';
import BlockGasUsed from 'ui/shared/block/BlockGasUsed'; import BlockGasUsed from 'ui/shared/block/BlockGasUsed';
import Skeleton from 'ui/shared/chakra/Skeleton';
import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
import TimeAgoWithTooltip from 'ui/shared/TimeAgoWithTooltip'; import TimeAgoWithTooltip from 'ui/shared/TimeAgoWithTooltip';
...@@ -22,7 +22,7 @@ const AddressBlocksValidatedListItem = (props: Props) => { ...@@ -22,7 +22,7 @@ const AddressBlocksValidatedListItem = (props: Props) => {
const totalReward = getBlockTotalReward(props); const totalReward = getBlockTotalReward(props);
return ( return (
<ListItemMobile rowGap={ 2 } isAnimated> <ListItemMobile rowGap={ 2 }>
<Flex justifyContent="space-between" w="100%"> <Flex justifyContent="space-between" w="100%">
<BlockEntity <BlockEntity
isLoading={ props.isLoading } isLoading={ props.isLoading }
...@@ -34,32 +34,32 @@ const AddressBlocksValidatedListItem = (props: Props) => { ...@@ -34,32 +34,32 @@ const AddressBlocksValidatedListItem = (props: Props) => {
timestamp={ props.timestamp } timestamp={ props.timestamp }
enableIncrement={ props.page === 1 } enableIncrement={ props.page === 1 }
isLoading={ props.isLoading } isLoading={ props.isLoading }
color="text_secondary" color="text.secondary"
display="inline-block" display="inline-block"
/> />
</Flex> </Flex>
<Flex columnGap={ 2 } w="100%"> <Flex columnGap={ 2 } w="100%">
<Skeleton isLoaded={ !props.isLoading } fontWeight={ 500 } flexShrink={ 0 }>Txn</Skeleton> <Skeleton loading={ props.isLoading } fontWeight={ 500 } flexShrink={ 0 }>Txn</Skeleton>
<Skeleton isLoaded={ !props.isLoading } display="inline-block" color="Skeleton_secondary"> <Skeleton loading={ props.isLoading } display="inline-block" color="Skeleton_secondary">
<span>{ props.transaction_count }</span> <span>{ props.transaction_count }</span>
</Skeleton> </Skeleton>
</Flex> </Flex>
<Flex columnGap={ 2 } w="100%"> <Flex columnGap={ 2 } w="100%">
<Skeleton isLoaded={ !props.isLoading } fontWeight={ 500 } flexShrink={ 0 }>Gas used</Skeleton> <Skeleton loading={ props.isLoading } fontWeight={ 500 } flexShrink={ 0 }>Gas used</Skeleton>
<Skeleton isLoaded={ !props.isLoading }> <Skeleton loading={ props.isLoading }>
<Text color="text_secondary">{ BigNumber(props.gas_used || 0).toFormat() }</Text> <Text color="text.secondary">{ BigNumber(props.gas_used || 0).toFormat() }</Text>
</Skeleton> </Skeleton>
<BlockGasUsed <BlockGasUsed
gasUsed={ props.gas_used } gasUsed={ props.gas_used || undefined }
gasLimit={ props.gas_limit } gasLimit={ props.gas_limit }
isLoading={ props.isLoading } isLoading={ props.isLoading }
/> />
</Flex> </Flex>
{ !config.UI.views.block.hiddenFields?.total_reward && !config.features.rollup.isEnabled && ( { !config.UI.views.block.hiddenFields?.total_reward && !config.features.rollup.isEnabled && (
<Flex columnGap={ 2 } w="100%"> <Flex columnGap={ 2 } w="100%">
<Skeleton isLoaded={ !props.isLoading } fontWeight={ 500 } flexShrink={ 0 }>Reward { currencyUnits.ether }</Skeleton> <Skeleton loading={ props.isLoading } fontWeight={ 500 } flexShrink={ 0 }>Reward { currencyUnits.ether }</Skeleton>
<Skeleton isLoaded={ !props.isLoading }> <Skeleton loading={ props.isLoading }>
<Text color="text_secondary">{ totalReward.toFixed() }</Text> <Text color="text.secondary">{ totalReward.toFixed() }</Text>
</Skeleton> </Skeleton>
</Flex> </Flex>
) } ) }
......
import { Td, Tr, Flex } 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';
...@@ -6,8 +6,9 @@ import type { Block } from 'types/api/block'; ...@@ -6,8 +6,9 @@ import type { Block } from 'types/api/block';
import config from 'configs/app'; import config from 'configs/app';
import getBlockTotalReward from 'lib/block/getBlockTotalReward'; import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { TableCell, TableRow } from 'toolkit/chakra/table';
import BlockGasUsed from 'ui/shared/block/BlockGasUsed'; import BlockGasUsed from 'ui/shared/block/BlockGasUsed';
import Skeleton from 'ui/shared/chakra/Skeleton';
import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import TimeAgoWithTooltip from 'ui/shared/TimeAgoWithTooltip'; import TimeAgoWithTooltip from 'ui/shared/TimeAgoWithTooltip';
...@@ -20,51 +21,50 @@ const AddressBlocksValidatedTableItem = (props: Props) => { ...@@ -20,51 +21,50 @@ const AddressBlocksValidatedTableItem = (props: Props) => {
const totalReward = getBlockTotalReward(props); const totalReward = getBlockTotalReward(props);
return ( return (
<Tr> <TableRow>
<Td> <TableCell>
<BlockEntity <BlockEntity
isLoading={ props.isLoading } isLoading={ props.isLoading }
number={ props.height } number={ props.height }
noIcon noIcon
fontSize="sm" textStyle="sm"
lineHeight={ 5 }
fontWeight={ 700 } fontWeight={ 700 }
/> />
</Td> </TableCell>
<Td> <TableCell>
<TimeAgoWithTooltip <TimeAgoWithTooltip
timestamp={ props.timestamp } timestamp={ props.timestamp }
enableIncrement={ props.page === 1 } enableIncrement={ props.page === 1 }
isLoading={ props.isLoading } isLoading={ props.isLoading }
color="text_secondary" color="text.secondary"
display="inline-block" display="inline-block"
/> />
</Td> </TableCell>
<Td> <TableCell>
<Skeleton isLoaded={ !props.isLoading } display="inline-block" fontWeight="500"> <Skeleton loading={ props.isLoading } display="inline-block" fontWeight="500">
<span>{ props.transaction_count }</span> <span>{ props.transaction_count }</span>
</Skeleton> </Skeleton>
</Td> </TableCell>
<Td> <TableCell>
<Flex alignItems="center" columnGap={ 2 }> <Flex alignItems="center" columnGap={ 2 }>
<Skeleton isLoaded={ !props.isLoading } flexBasis="80px"> <Skeleton loading={ props.isLoading } flexBasis="80px">
{ BigNumber(props.gas_used || 0).toFormat() } { BigNumber(props.gas_used || 0).toFormat() }
</Skeleton> </Skeleton>
<BlockGasUsed <BlockGasUsed
gasUsed={ props.gas_used } gasUsed={ props.gas_used || undefined }
gasLimit={ props.gas_limit } gasLimit={ props.gas_limit }
isLoading={ props.isLoading } isLoading={ props.isLoading }
/> />
</Flex> </Flex>
</Td> </TableCell>
{ !config.UI.views.block.hiddenFields?.total_reward && !config.features.rollup.isEnabled && ( { !config.UI.views.block.hiddenFields?.total_reward && !config.features.rollup.isEnabled && (
<Td isNumeric> <TableCell isNumeric>
<Skeleton isLoaded={ !props.isLoading } display="inline-block"> <Skeleton loading={ props.isLoading } display="inline-block">
<span>{ totalReward.toFixed() }</span> <span>{ totalReward.toFixed() }</span>
</Skeleton> </Skeleton>
</Td> </TableCell>
) } ) }
</Tr> </TableRow>
); );
}; };
......
...@@ -34,7 +34,7 @@ const AddressCoinBalanceChart = ({ addressHash }: Props) => { ...@@ -34,7 +34,7 @@ const AddressCoinBalanceChart = ({ addressHash }: Props) => {
isLoading={ isPending } isLoading={ isPending }
h="300px" h="300px"
units={ currencyUnits.ether } units={ currencyUnits.ether }
emptyText={ data?.days && `Insufficient data for the past ${ data.days } days` } emptyText={ data?.days ? `Insufficient data for the past ${ data.days } days` : undefined }
/> />
); );
}; };
......
import { Hide, Show, Table, Tbody, Th, Tr } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query'; import type { UseQueryResult } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
...@@ -7,10 +7,10 @@ import type { PaginationParams } from 'ui/shared/pagination/types'; ...@@ -7,10 +7,10 @@ import type { PaginationParams } from 'ui/shared/pagination/types';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
import { currencyUnits } from 'lib/units'; import { currencyUnits } from 'lib/units';
import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay'; import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/pagination/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import { default as Thead } from 'ui/shared/TheadSticky';
import AddressCoinBalanceListItem from './AddressCoinBalanceListItem'; import AddressCoinBalanceListItem from './AddressCoinBalanceListItem';
import AddressCoinBalanceTableItem from './AddressCoinBalanceTableItem'; import AddressCoinBalanceTableItem from './AddressCoinBalanceTableItem';
...@@ -25,18 +25,18 @@ const AddressCoinBalanceHistory = ({ query }: Props) => { ...@@ -25,18 +25,18 @@ const AddressCoinBalanceHistory = ({ query }: Props) => {
const content = query.data?.items ? ( const content = query.data?.items ? (
<> <>
<Hide below="lg" ssr={ false }> <Box hideBelow="lg">
<Table> <TableRoot>
<Thead top={ query.pagination.isVisible ? ACTION_BAR_HEIGHT_DESKTOP : 0 }> <TableHeaderSticky top={ query.pagination.isVisible ? ACTION_BAR_HEIGHT_DESKTOP : 0 }>
<Tr> <TableRow>
<Th width="20%">Block</Th> <TableColumnHeader width="20%">Block</TableColumnHeader>
<Th width="20%">Txn</Th> <TableColumnHeader width="20%">Txn</TableColumnHeader>
<Th width="20%">Age</Th> <TableColumnHeader width="20%">Age</TableColumnHeader>
<Th width="20%" isNumeric pr={ 1 }>Balance { currencyUnits.ether }</Th> <TableColumnHeader width="20%" isNumeric pr={ 1 }>Balance { currencyUnits.ether }</TableColumnHeader>
<Th width="20%" isNumeric>Delta</Th> <TableColumnHeader width="20%" isNumeric>Delta</TableColumnHeader>
</Tr> </TableRow>
</Thead> </TableHeaderSticky>
<Tbody> <TableBody>
{ query.data.items.map((item, index) => ( { query.data.items.map((item, index) => (
<AddressCoinBalanceTableItem <AddressCoinBalanceTableItem
key={ item.block_number + (query.isPlaceholderData ? String(index) : '') } key={ item.block_number + (query.isPlaceholderData ? String(index) : '') }
...@@ -45,10 +45,10 @@ const AddressCoinBalanceHistory = ({ query }: Props) => { ...@@ -45,10 +45,10 @@ const AddressCoinBalanceHistory = ({ query }: Props) => {
isLoading={ query.isPlaceholderData } isLoading={ query.isPlaceholderData }
/> />
)) } )) }
</Tbody> </TableBody>
</Table> </TableRoot>
</Hide> </Box>
<Show below="lg" ssr={ false }> <Box hideFrom="lg">
{ query.data.items.map((item, index) => ( { query.data.items.map((item, index) => (
<AddressCoinBalanceListItem <AddressCoinBalanceListItem
key={ item.block_number + (query.isPlaceholderData ? String(index) : '') } key={ item.block_number + (query.isPlaceholderData ? String(index) : '') }
...@@ -57,7 +57,7 @@ const AddressCoinBalanceHistory = ({ query }: Props) => { ...@@ -57,7 +57,7 @@ const AddressCoinBalanceHistory = ({ query }: Props) => {
isLoading={ query.isPlaceholderData } isLoading={ query.isPlaceholderData }
/> />
)) } )) }
</Show> </Box>
</> </>
) : null; ) : null;
...@@ -71,11 +71,12 @@ const AddressCoinBalanceHistory = ({ query }: Props) => { ...@@ -71,11 +71,12 @@ const AddressCoinBalanceHistory = ({ query }: Props) => {
<DataListDisplay <DataListDisplay
mt={ 8 } mt={ 8 }
isError={ query.isError } isError={ query.isError }
items={ query.data?.items } itemsNum={ query.data?.items.length }
emptyText="There is no coin balance history for this address." emptyText="There is no coin balance history for this address."
content={ content }
actionBar={ actionBar } actionBar={ actionBar }
/> >
{ content }
</DataListDisplay>
); );
}; };
......
import { Text, Stat, StatHelpText, StatArrow, Flex } from '@chakra-ui/react'; import { Stat, Flex } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import React from 'react'; import React from 'react';
...@@ -6,7 +6,7 @@ import type { AddressCoinBalanceHistoryItem } from 'types/api/address'; ...@@ -6,7 +6,7 @@ import type { AddressCoinBalanceHistoryItem } from 'types/api/address';
import { WEI, ZERO } from 'lib/consts'; import { WEI, ZERO } from 'lib/consts';
import { currencyUnits } from 'lib/units'; import { currencyUnits } from 'lib/units';
import Skeleton from 'ui/shared/chakra/Skeleton'; import { Skeleton } from 'toolkit/chakra/skeleton';
import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
...@@ -22,24 +22,22 @@ const AddressCoinBalanceListItem = (props: Props) => { ...@@ -22,24 +22,22 @@ const AddressCoinBalanceListItem = (props: Props) => {
const isPositiveDelta = deltaBn.gte(ZERO); const isPositiveDelta = deltaBn.gte(ZERO);
return ( return (
<ListItemMobile rowGap={ 2 } isAnimated> <ListItemMobile rowGap={ 2 }>
<Flex justifyContent="space-between" w="100%"> <Flex justifyContent="space-between" w="100%">
<Skeleton isLoaded={ !props.isLoading } fontWeight={ 600 }> <Skeleton loading={ props.isLoading } fontWeight={ 600 }>
{ BigNumber(props.value).div(WEI).dp(8).toFormat() } { currencyUnits.ether } { BigNumber(props.value).div(WEI).dp(8).toFormat() } { currencyUnits.ether }
</Skeleton> </Skeleton>
<Skeleton isLoaded={ !props.isLoading }> <Skeleton loading={ props.isLoading }>
<Stat flexGrow="0"> <Stat.Root flexGrow="0" positive={ isPositiveDelta } size="sm">
<StatHelpText display="flex" mb={ 0 } alignItems="center"> { isPositiveDelta ? <Stat.UpIndicator/> : <Stat.DownIndicator/> }
<StatArrow type={ isPositiveDelta ? 'increase' : 'decrease' } mr={ 2 }/> <Stat.ValueText fontWeight={ 600 }>
<Text as="span" color={ isPositiveDelta ? 'green.500' : 'red.500' } fontWeight={ 600 }>
{ deltaBn.dp(8).toFormat() } { deltaBn.dp(8).toFormat() }
</Text> </Stat.ValueText>
</StatHelpText> </Stat.Root>
</Stat>
</Skeleton> </Skeleton>
</Flex> </Flex>
<Flex columnGap={ 2 } w="100%"> <Flex columnGap={ 2 } w="100%">
<Skeleton isLoaded={ !props.isLoading } fontWeight={ 500 } flexShrink={ 0 }>Block</Skeleton> <Skeleton loading={ props.isLoading } fontWeight={ 500 } flexShrink={ 0 }>Block</Skeleton>
<BlockEntity <BlockEntity
isLoading={ props.isLoading } isLoading={ props.isLoading }
number={ props.block_number } number={ props.block_number }
...@@ -49,7 +47,7 @@ const AddressCoinBalanceListItem = (props: Props) => { ...@@ -49,7 +47,7 @@ const AddressCoinBalanceListItem = (props: Props) => {
</Flex> </Flex>
{ props.transaction_hash && ( { props.transaction_hash && (
<Flex columnGap={ 2 } w="100%"> <Flex columnGap={ 2 } w="100%">
<Skeleton isLoaded={ !props.isLoading } fontWeight={ 500 } flexShrink={ 0 }>Txs</Skeleton> <Skeleton loading={ props.isLoading } fontWeight={ 500 } flexShrink={ 0 }>Txs</Skeleton>
<TxEntity <TxEntity
hash={ props.transaction_hash } hash={ props.transaction_hash }
isLoading={ props.isLoading } isLoading={ props.isLoading }
...@@ -60,7 +58,7 @@ const AddressCoinBalanceListItem = (props: Props) => { ...@@ -60,7 +58,7 @@ const AddressCoinBalanceListItem = (props: Props) => {
</Flex> </Flex>
) } ) }
<Flex columnGap={ 2 } w="100%"> <Flex columnGap={ 2 } w="100%">
<Skeleton isLoaded={ !props.isLoading } fontWeight={ 500 } flexShrink={ 0 }>Age</Skeleton> <Skeleton loading={ props.isLoading } fontWeight={ 500 } flexShrink={ 0 }>Age</Skeleton>
<TimeAgoWithTooltip <TimeAgoWithTooltip
timestamp={ props.block_timestamp } timestamp={ props.block_timestamp }
enableIncrement={ props.page === 1 } enableIncrement={ props.page === 1 }
......
import { Td, Tr, Text, Stat, StatHelpText, StatArrow } from '@chakra-ui/react'; import { Stat } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import React from 'react'; import React from 'react';
import type { AddressCoinBalanceHistoryItem } from 'types/api/address'; import type { AddressCoinBalanceHistoryItem } from 'types/api/address';
import { WEI, ZERO } from 'lib/consts'; import { WEI, ZERO } from 'lib/consts';
import Skeleton from 'ui/shared/chakra/Skeleton'; import { Skeleton } from 'toolkit/chakra/skeleton';
import { TableCell, TableRow } from 'toolkit/chakra/table';
import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity'; import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TimeAgoWithTooltip from 'ui/shared/TimeAgoWithTooltip'; import TimeAgoWithTooltip from 'ui/shared/TimeAgoWithTooltip';
...@@ -20,18 +21,16 @@ const AddressCoinBalanceTableItem = (props: Props) => { ...@@ -20,18 +21,16 @@ const AddressCoinBalanceTableItem = (props: Props) => {
const isPositiveDelta = deltaBn.gte(ZERO); const isPositiveDelta = deltaBn.gte(ZERO);
return ( return (
<Tr> <TableRow>
<Td> <TableCell>
<BlockEntity <BlockEntity
isLoading={ props.isLoading } isLoading={ props.isLoading }
number={ props.block_number } number={ props.block_number }
noIcon noIcon
fontSize="sm"
lineHeight={ 5 }
fontWeight={ 700 } fontWeight={ 700 }
/> />
</Td> </TableCell>
<Td> <TableCell>
{ props.transaction_hash && ( { props.transaction_hash && (
<TxEntity <TxEntity
hash={ props.transaction_hash } hash={ props.transaction_hash }
...@@ -41,34 +40,32 @@ const AddressCoinBalanceTableItem = (props: Props) => { ...@@ -41,34 +40,32 @@ const AddressCoinBalanceTableItem = (props: Props) => {
maxW="150px" maxW="150px"
/> />
) } ) }
</Td> </TableCell>
<Td> <TableCell>
<TimeAgoWithTooltip <TimeAgoWithTooltip
timestamp={ props.block_timestamp } timestamp={ props.block_timestamp }
enableIncrement={ props.page === 1 } enableIncrement={ props.page === 1 }
isLoading={ props.isLoading } isLoading={ props.isLoading }
color="text_secondary" color="text.secondary"
display="inline-block" display="inline-block"
/> />
</Td> </TableCell>
<Td isNumeric pr={ 1 }> <TableCell isNumeric pr={ 1 }>
<Skeleton isLoaded={ !props.isLoading } color="text_secondary" display="inline-block"> <Skeleton loading={ props.isLoading } color="text.secondary" display="inline-block">
<span>{ BigNumber(props.value).div(WEI).dp(8).toFormat() }</span> <span>{ BigNumber(props.value).div(WEI).dp(8).toFormat() }</span>
</Skeleton> </Skeleton>
</Td> </TableCell>
<Td isNumeric display="flex" justifyContent="end"> <TableCell isNumeric display="flex" justifyContent="end">
<Skeleton isLoaded={ !props.isLoading }> <Skeleton loading={ props.isLoading }>
<Stat flexGrow="0" lineHeight={ 5 }> <Stat.Root flexGrow="0" size="sm" positive={ isPositiveDelta }>
<StatHelpText display="flex" mb={ 0 } alignItems="center"> { isPositiveDelta ? <Stat.UpIndicator/> : <Stat.DownIndicator/> }
<StatArrow type={ isPositiveDelta ? 'increase' : 'decrease' } mr={ 2 }/> <Stat.ValueText fontWeight={ 600 }>
<Text as="span" color={ isPositiveDelta ? 'green.500' : 'red.500' } fontWeight={ 600 }>
{ deltaBn.dp(8).toFormat() } { deltaBn.dp(8).toFormat() }
</Text> </Stat.ValueText>
</StatHelpText> </Stat.Root>
</Stat>
</Skeleton> </Skeleton>
</Td> </TableCell>
</Tr> </TableRow>
); );
}; };
......
...@@ -2,8 +2,8 @@ import { Box, Flex, HStack } from '@chakra-ui/react'; ...@@ -2,8 +2,8 @@ import { Box, Flex, HStack } 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 { EntityTag } from 'ui/shared/EntityTags/types'; import type { EntityTag } from 'ui/shared/EntityTags/types';
import type { RoutedTab } from 'ui/shared/Tabs/types';
import config from 'configs/app'; import config from 'configs/app';
import getCheckedSummedAddress from 'lib/address/getCheckedSummedAddress'; import getCheckedSummedAddress from 'lib/address/getCheckedSummedAddress';
...@@ -151,7 +151,7 @@ const AddressPageContent = () => { ...@@ -151,7 +151,7 @@ const AddressPageContent = () => {
Boolean(config.features.mudFramework.isEnabled && mudTablesCountQuery.data && mudTablesCountQuery.data > 0), Boolean(config.features.mudFramework.isEnabled && mudTablesCountQuery.data && mudTablesCountQuery.data > 0),
); );
const tabs: Array<RoutedTab> = React.useMemo(() => { const tabs: Array<TabItemRegular> = React.useMemo(() => {
return [ return [
// config.features.mudFramework.isEnabled && mudTablesCountQuery.data && mudTablesCountQuery.data > 0 && { // config.features.mudFramework.isEnabled && mudTablesCountQuery.data && mudTablesCountQuery.data > 0 && {
// id: 'mud', // id: 'mud',
...@@ -213,19 +213,19 @@ const AddressPageContent = () => { ...@@ -213,19 +213,19 @@ const AddressPageContent = () => {
// count: addressTabsCountersQuery.data?.celo_election_rewards_count, // count: addressTabsCountersQuery.data?.celo_election_rewards_count,
// component: <AddressEpochRewards scrollRef={ tabsScrollRef } shouldRender={ !isTabsLoading } isQueryEnabled={ areQueriesEnabled }/>, // component: <AddressEpochRewards scrollRef={ tabsScrollRef } shouldRender={ !isTabsLoading } isQueryEnabled={ areQueriesEnabled }/>,
// } : undefined, // } : undefined,
// { {
// id: 'coin_balance_history', id: 'coin_balance_history',
// title: 'Coin balance history', title: 'Coin balance history',
// component: <AddressCoinBalance shouldRender={ !isTabsLoading } isQueryEnabled={ areQueriesEnabled }/>, component: <AddressCoinBalance shouldRender={ !isTabsLoading } isQueryEnabled={ areQueriesEnabled }/>,
// }, },
// addressTabsCountersQuery.data?.validations_count ? addressTabsCountersQuery.data?.validations_count ?
// { {
// id: 'blocks_validated', id: 'blocks_validated',
// title: `Blocks ${ getNetworkValidationActionText() }`, title: `Blocks ${ getNetworkValidationActionText() }`,
// count: addressTabsCountersQuery.data?.validations_count, count: addressTabsCountersQuery.data?.validations_count,
// component: <AddressBlocksValidated scrollRef={ tabsScrollRef } shouldRender={ !isTabsLoading } isQueryEnabled={ areQueriesEnabled }/>, component: <AddressBlocksValidated scrollRef={ tabsScrollRef } shouldRender={ !isTabsLoading } isQueryEnabled={ areQueriesEnabled }/>,
// } : } :
// undefined, undefined,
// addressTabsCountersQuery.data?.logs_count ? // addressTabsCountersQuery.data?.logs_count ?
// { // {
// id: 'logs', // id: 'logs',
......
import { useColorModeValue, useToken } from '@chakra-ui/react'; import { useToken } from '@chakra-ui/react';
import * as d3 from 'd3'; import * as d3 from 'd3';
import React from 'react'; import React from 'react';
import { useColorModeValue } from 'toolkit/chakra/color-mode';
interface Props extends Omit<React.SVGProps<SVGGElement>, 'scale'> { interface Props extends Omit<React.SVGProps<SVGGElement>, 'scale'> {
type: 'left' | 'bottom'; type: 'left' | 'bottom';
scale: d3.ScaleTime<number, number> | d3.ScaleLinear<number, number>; scale: d3.ScaleTime<number, number> | d3.ScaleLinear<number, number>;
...@@ -14,8 +16,7 @@ interface Props extends Omit<React.SVGProps<SVGGElement>, 'scale'> { ...@@ -14,8 +16,7 @@ interface Props extends Omit<React.SVGProps<SVGGElement>, 'scale'> {
const ChartAxis = ({ type, scale, ticks, tickFormatGenerator, noAnimation, anchorEl, ...props }: Props) => { const ChartAxis = ({ type, scale, ticks, tickFormatGenerator, noAnimation, anchorEl, ...props }: Props) => {
const ref = React.useRef<SVGGElement>(null); const ref = React.useRef<SVGGElement>(null);
const textColorToken = useColorModeValue('blackAlpha.600', 'whiteAlpha.500'); const textColor = useToken('colors', useColorModeValue('blackAlpha.600', 'whiteAlpha.500'));
const textColor = useToken('colors', textColorToken);
React.useEffect(() => { React.useEffect(() => {
if (!ref.current) { if (!ref.current) {
...@@ -41,7 +42,7 @@ const ChartAxis = ({ type, scale, ticks, tickFormatGenerator, noAnimation, ancho ...@@ -41,7 +42,7 @@ const ChartAxis = ({ type, scale, ticks, tickFormatGenerator, noAnimation, ancho
axisGroup.selectAll('text') axisGroup.selectAll('text')
.attr('opacity', 1) .attr('opacity', 1)
.attr('color', textColor) .attr('color', textColor)
.attr('font-size', '0.75rem'); .style('font-size', '12px');
}, [ scale, ticks, tickFormatGenerator, noAnimation, type, textColor ]); }, [ scale, ticks, tickFormatGenerator, noAnimation, type, textColor ]);
React.useEffect(() => { React.useEffect(() => {
......
import { import { useCopyToClipboard } from '@uidotdev/usehooks';
IconButton,
MenuButton,
MenuItem,
MenuList,
useClipboard,
useColorModeValue,
VisuallyHidden,
} from '@chakra-ui/react';
import domToImage from 'dom-to-image'; import domToImage from 'dom-to-image';
import React from 'react'; import React from 'react';
...@@ -16,8 +8,10 @@ import type { Resolution } from '@blockscout/stats-types'; ...@@ -16,8 +8,10 @@ import type { Resolution } from '@blockscout/stats-types';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import isBrowser from 'lib/isBrowser'; import isBrowser from 'lib/isBrowser';
import saveAsCSV from 'lib/saveAsCSV'; import saveAsCSV from 'lib/saveAsCSV';
import Menu from 'ui/shared/chakra/Menu'; import { useColorModeValue } from 'toolkit/chakra/color-mode';
import Skeleton from 'ui/shared/chakra/Skeleton'; import { IconButton } from 'toolkit/chakra/icon-button';
import { MenuContent, MenuItem, MenuRoot, MenuTrigger } from 'toolkit/chakra/menu';
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import FullscreenChartModal from './FullscreenChartModal'; import FullscreenChartModal from './FullscreenChartModal';
...@@ -52,19 +46,15 @@ const ChartMenu = ({ ...@@ -52,19 +46,15 @@ const ChartMenu = ({
handleZoomReset, handleZoomReset,
}: Props) => { }: Props) => {
const pngBackgroundColor = useColorModeValue('white', 'black'); const pngBackgroundColor = useColorModeValue('white', 'black');
const [ isFullscreen, setIsFullscreen ] = React.useState(false); const fullscreenDialog = useDisclosure();
const { onCopy } = useClipboard(chartUrl ?? ''); const [ , copyToClipboard ] = useCopyToClipboard();
const isInBrowser = isBrowser(); const isInBrowser = isBrowser();
const showChartFullscreen = React.useCallback(() => { const showChartFullscreen = React.useCallback(() => {
setIsFullscreen(true); fullscreenDialog.onOpenChange({ open: true });
}, []); }, [ fullscreenDialog ]);
const clearFullscreenChart = React.useCallback(() => {
setIsFullscreen(false);
}, []);
const handleFileSaveClick = React.useCallback(() => { const handleFileSaveClick = React.useCallback(() => {
// wait for context menu to close // wait for context menu to close
...@@ -111,6 +101,10 @@ const ChartMenu = ({ ...@@ -111,6 +101,10 @@ const ChartMenu = ({
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const hasShare = isInBrowser && (window.navigator.share as any); const hasShare = isInBrowser && (window.navigator.share as any);
const handleCopy = React.useCallback(() => {
copyToClipboard(chartUrl ?? '');
}, [ chartUrl, copyToClipboard ]);
const handleShare = React.useCallback(async() => { const handleShare = React.useCallback(async() => {
try { try {
await window.navigator.share({ await window.navigator.share({
...@@ -123,27 +117,17 @@ const ChartMenu = ({ ...@@ -123,27 +117,17 @@ const ChartMenu = ({
return ( return (
<> <>
<Menu> <MenuRoot>
<Skeleton isLoaded={ !isLoading } borderRadius="base"> <MenuTrigger asChild>
<MenuButton <IconButton variant="dropdown" size="sm" aria-label="Open chart options menu" loading={ isLoading } borderRadius="base" borderWidth="0">
w="36px" <IconSvg name="dots" boxSize={ 4 } transform="rotate(-90deg)"/>
h="32px" </IconButton>
icon={ <IconSvg name="dots" boxSize={ 4 } transform="rotate(-90deg)"/> } </MenuTrigger>
colorScheme="gray" <MenuContent>
variant="simple"
as={ IconButton }
>
<VisuallyHidden>
Open chart options menu
</VisuallyHidden>
</MenuButton>
</Skeleton>
<MenuList>
{ chartUrl && ( { chartUrl && (
<MenuItem <MenuItem
display="flex" value={ hasShare ? 'share' : 'copy' }
alignItems="center" onClick={ hasShare ? handleShare : handleCopy }
onClick={ hasShare ? handleShare : onCopy }
closeOnSelect={ hasShare ? false : true } closeOnSelect={ hasShare ? false : true }
> >
<IconSvg name={ hasShare ? 'share' : 'copy' } boxSize={ 5 } mr={ 3 }/> <IconSvg name={ hasShare ? 'share' : 'copy' } boxSize={ 5 } mr={ 3 }/>
...@@ -151,38 +135,35 @@ const ChartMenu = ({ ...@@ -151,38 +135,35 @@ const ChartMenu = ({
</MenuItem> </MenuItem>
) } ) }
<MenuItem <MenuItem
display="flex" value="fullscreen"
alignItems="center"
onClick={ showChartFullscreen } onClick={ showChartFullscreen }
> >
<IconSvg name="scope" boxSize={ 5 } mr={ 3 }/> <IconSvg name="scope" boxSize={ 5 } mr={ 3 }/>
View fullscreen View fullscreen
</MenuItem> </MenuItem>
<MenuItem <MenuItem
display="flex" value="save-png"
alignItems="center"
onClick={ handleFileSaveClick } onClick={ handleFileSaveClick }
> >
<IconSvg name="files/image" boxSize={ 5 } mr={ 3 }/> <IconSvg name="files/image" boxSize={ 5 } mr={ 3 }/>
Save as PNG Save as PNG
</MenuItem> </MenuItem>
<MenuItem <MenuItem
display="flex" value="save-csv"
alignItems="center"
onClick={ handleSVGSavingClick } onClick={ handleSVGSavingClick }
> >
<IconSvg name="files/csv" boxSize={ 5 } mr={ 3 }/> <IconSvg name="files/csv" boxSize={ 5 } mr={ 3 }/>
Save as CSV Save as CSV
</MenuItem> </MenuItem>
</MenuList> </MenuContent>
</Menu> </MenuRoot>
{ items && isFullscreen && ( { items && (
<FullscreenChartModal <FullscreenChartModal
isOpen open={ fullscreenDialog.open }
onOpenChange={ fullscreenDialog.onOpenChange }
items={ items } items={ items }
title={ title } title={ title }
description={ description } description={ description }
onClose={ clearFullscreenChart }
units={ units } units={ units }
resolution={ resolution } resolution={ resolution }
zoomRange={ zoomRange } zoomRange={ zoomRange }
......
import type { IconProps } from '@chakra-ui/react'; import type { IconProps } from '@chakra-ui/react';
import { Icon, useColorModeValue } from '@chakra-ui/react'; import { Icon } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
// eslint-disable-next-line no-restricted-imports // eslint-disable-next-line no-restricted-imports
import logoIcon from 'icons/networks/logo-placeholder.svg'; import logoIcon from 'icons/networks/logo-placeholder.svg';
const ChartWatermarkIcon = (props: IconProps) => { const ChartWatermarkIcon = (props: IconProps) => {
const watermarkColor = useColorModeValue('link', 'white');
return ( return (
<Icon <Icon
{ ...props } { ...props }
...@@ -18,7 +17,7 @@ const ChartWatermarkIcon = (props: IconProps) => { ...@@ -18,7 +17,7 @@ const ChartWatermarkIcon = (props: IconProps) => {
transform="translate(-50%, -50%)" transform="translate(-50%, -50%)"
pointerEvents="none" pointerEvents="none"
viewBox="0 0 114 20" viewBox="0 0 114 20"
color={ watermarkColor } color={{ _light: 'link', _dark: 'white' }}
/> />
); );
}; };
......
import { import { chakra, Flex } from '@chakra-ui/react';
chakra,
Flex,
IconButton,
Tooltip,
useColorModeValue,
} from '@chakra-ui/react';
import NextLink from 'next/link'; import NextLink from 'next/link';
import React, { useRef } from 'react'; import React, { useRef } from 'react';
...@@ -13,7 +7,9 @@ import type { TimeChartItem } from './types'; ...@@ -13,7 +7,9 @@ import type { TimeChartItem } from './types';
import { route, type Route } from 'nextjs-routes'; import { route, type Route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import Skeleton from 'ui/shared/chakra/Skeleton'; import { IconButton } from 'toolkit/chakra/icon-button';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { Tooltip } from 'toolkit/chakra/tooltip';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import ChartMenu from './ChartMenu'; import ChartMenu from './ChartMenu';
...@@ -48,8 +44,6 @@ const ChartWidget = ({ ...@@ -48,8 +44,6 @@ const ChartWidget = ({
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
const { zoomRange, handleZoom, handleZoomReset } = useZoom(); const { zoomRange, handleZoom, handleZoomReset } = useZoom();
const borderColor = useColorModeValue('gray.200', 'gray.600');
const hasItems = items && items.length > 2; const hasItems = items && items.length > 2;
const content = ( const content = (
...@@ -72,24 +66,22 @@ const ChartWidget = ({ ...@@ -72,24 +66,22 @@ const ChartWidget = ({
flexDir="column" flexDir="column"
alignItems="flex-start" alignItems="flex-start"
cursor={ href ? 'pointer' : 'default' } cursor={ href ? 'pointer' : 'default' }
_hover={ href ? { color: 'link_hovered' } : {} } _hover={ href ? { color: 'link.primary.hovered' } : {} }
> >
<Skeleton <Skeleton
isLoaded={ !isLoading } loading={ isLoading }
fontWeight={ 600 } fontWeight={ 600 }
size={{ base: 'xs', lg: 'sm' }} textStyle="md"
> >
<span>{ title }</span> <span>{ title }</span>
</Skeleton> </Skeleton>
{ description && ( { description && (
<Skeleton <Skeleton
isLoaded={ !isLoading } loading={ isLoading }
color="text_secondary" color="text_secondary"
fontSize="xs" fontSize="xs"
mt={ 1 } mt={ 1 }
> >
<span>{ description }</span> <span>{ description }</span>
</Skeleton> </Skeleton>
...@@ -104,8 +96,8 @@ const ChartWidget = ({ ...@@ -104,8 +96,8 @@ const ChartWidget = ({
flexDir="column" flexDir="column"
padding={{ base: 3, lg: 4 }} padding={{ base: 3, lg: 4 }}
borderRadius="lg" borderRadius="lg"
border="1px" borderWidth="1px"
borderColor={ borderColor } borderColor={{ _light: 'gray.200', _dark: 'gray.600' }}
className={ className } className={ className }
> >
<Flex columnGap={ 6 } mb={ 2 } alignItems="flex-start"> <Flex columnGap={ 6 } mb={ 2 } alignItems="flex-start">
...@@ -115,18 +107,18 @@ const ChartWidget = ({ ...@@ -115,18 +107,18 @@ const ChartWidget = ({
</NextLink> </NextLink>
) : chartHeader } ) : chartHeader }
<Flex ml="auto" columnGap={ 2 }> <Flex ml="auto" columnGap={ 2 }>
<Tooltip label="Reset zoom"> <Tooltip content="Reset zoom">
<IconButton <IconButton
hidden={ !zoomRange } hidden={ !zoomRange }
aria-label="Reset zoom" aria-label="Reset zoom"
colorScheme="blue"
w={ 9 } w={ 9 }
h={ 8 } h={ 8 }
size="sm" size="sm"
variant="outline" variant="outline"
onClick={ handleZoomReset } onClick={ handleZoomReset }
icon={ <IconSvg name="repeat" w={ 4 } h={ 4 }/> } >
/> <IconSvg name="repeat" boxSize={ 4 }/>
</IconButton>
</Tooltip> </Tooltip>
{ hasItems && ( { hasItems && (
......
import { Box, Center, Flex, Link, Text } from '@chakra-ui/react'; import { Box, Center, Flex, Text } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { TimeChartItem } from './types'; import type { TimeChartItem } from './types';
import type { Resolution } from '@blockscout/stats-types'; import type { Resolution } from '@blockscout/stats-types';
import { apos } from 'lib/html-entities'; import { apos } from 'lib/html-entities';
import Skeleton from 'ui/shared/chakra/Skeleton'; import { Link } from 'toolkit/chakra/link';
import { Skeleton } from 'toolkit/chakra/skeleton';
import ChartWatermarkIcon from './ChartWatermarkIcon'; import ChartWatermarkIcon from './ChartWatermarkIcon';
import ChartWidgetGraph from './ChartWidgetGraph'; import ChartWidgetGraph from './ChartWidgetGraph';
...@@ -48,7 +49,7 @@ const ChartWidgetContent = ({ ...@@ -48,7 +49,7 @@ const ChartWidgetContent = ({
py={ 4 } py={ 4 }
> >
<Text <Text
variant="secondary" color="text.secondary"
fontSize="sm" fontSize="sm"
textAlign="center" textAlign="center"
> >
...@@ -60,13 +61,13 @@ const ChartWidgetContent = ({ ...@@ -60,13 +61,13 @@ const ChartWidgetContent = ({
} }
if (isLoading) { if (isLoading) {
return <Skeleton flexGrow={ 1 } w="100%"/>; return <Skeleton loading flexGrow={ 1 } w="100%"/>;
} }
if (!hasItems) { if (!hasItems) {
return ( return (
<Center flexGrow={ 1 }> <Center flexGrow={ 1 }>
<Text variant="secondary" fontSize="sm">{ emptyText || 'No data' }</Text> <Text color="text.secondary" fontSize="sm">{ emptyText || 'No data' }</Text>
</Center> </Center>
); );
} }
......
...@@ -41,7 +41,7 @@ const ChartWidgetGraph = ({ ...@@ -41,7 +41,7 @@ const ChartWidgetGraph = ({
zoomRange, zoomRange,
}: Props) => { }: Props) => {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const color = useToken('colors', 'blue.200'); const [ color ] = useToken('colors', 'blue.200');
const chartId = `chart-${ title.split(' ').join('') }-${ isEnlarged ? 'fullscreen' : 'small' }`; const chartId = `chart-${ title.split(' ').join('') }-${ isEnlarged ? 'fullscreen' : 'small' }`;
const overlayRef = React.useRef<SVGRectElement>(null); const overlayRef = React.useRef<SVGRectElement>(null);
......
import { Box, Button, Grid, Heading, Modal, ModalBody, ModalCloseButton, ModalContent, ModalOverlay, Text } from '@chakra-ui/react'; import { Grid, Text } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { TimeChartItem } from './types'; import type { TimeChartItem } from './types';
import type { Resolution } from '@blockscout/stats-types'; import type { Resolution } from '@blockscout/stats-types';
import { Button } from 'toolkit/chakra/button';
import { DialogBody, DialogContent, DialogHeader, DialogRoot } from 'toolkit/chakra/dialog';
import { Heading } from 'toolkit/chakra/heading';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import ChartWidgetContent from './ChartWidgetContent'; import ChartWidgetContent from './ChartWidgetContent';
type Props = { type Props = {
isOpen: boolean; open: boolean;
onOpenChange: ({ open }: { open: boolean }) => void;
title: string; title: string;
description?: string; description?: string;
items: Array<TimeChartItem>; items: Array<TimeChartItem>;
onClose: () => void;
units?: string; units?: string;
resolution?: Resolution; resolution?: Resolution;
zoomRange?: [ Date, Date ]; zoomRange?: [ Date, Date ];
...@@ -22,46 +25,35 @@ type Props = { ...@@ -22,46 +25,35 @@ type Props = {
}; };
const FullscreenChartModal = ({ const FullscreenChartModal = ({
isOpen, open,
onOpenChange,
title, title,
description, description,
items, items,
units, units,
onClose,
resolution, resolution,
zoomRange, zoomRange,
handleZoom, handleZoom,
handleZoomReset, handleZoomReset,
}: Props) => { }: Props) => {
return ( return (
<Modal <DialogRoot
isOpen={ isOpen } open={ open }
onClose={ onClose } onOpenChange={ onOpenChange }
size="full" size="full"
isCentered
>
<ModalOverlay/>
<ModalContent>
<Box
mb={ 1 }
>
<Grid
gridColumnGap={ 2 }
>
<Heading
mb={ 1 }
size={{ base: 'xs', sm: 'md' }}
> >
<DialogContent>
<DialogHeader/>
<DialogBody pt={ 6 } display="flex" flexDir="column">
<Grid gridColumnGap={ 2 } >
<Heading mb={ 1 } level="2">
{ title } { title }
</Heading> </Heading>
{ description && ( { description && (
<Text <Text
gridColumn={ 1 } gridColumn={ 1 }
as="p" color="text.secondary"
variant="secondary"
fontSize="xs" fontSize="xs"
> >
{ description } { description }
...@@ -70,8 +62,6 @@ const FullscreenChartModal = ({ ...@@ -70,8 +62,6 @@ const FullscreenChartModal = ({
{ Boolean(zoomRange) && ( { Boolean(zoomRange) && (
<Button <Button
leftIcon={ <IconSvg name="repeat" w={ 4 } h={ 4 }/> }
colorScheme="blue"
gridColumn={ 2 } gridColumn={ 2 }
justifySelf="end" justifySelf="end"
alignSelf="top" alignSelf="top"
...@@ -80,18 +70,11 @@ const FullscreenChartModal = ({ ...@@ -80,18 +70,11 @@ const FullscreenChartModal = ({
variant="outline" variant="outline"
onClick={ handleZoomReset } onClick={ handleZoomReset }
> >
<IconSvg name="repeat" w={ 4 } h={ 4 }/>
Reset zoom Reset zoom
</Button> </Button>
) } ) }
</Grid> </Grid>
</Box>
<ModalCloseButton/>
<ModalBody
h="100%"
margin={{ bottom: 60 }}
>
<ChartWidgetContent <ChartWidgetContent
isEnlarged isEnlarged
items={ items } items={ items }
...@@ -101,9 +84,9 @@ const FullscreenChartModal = ({ ...@@ -101,9 +84,9 @@ const FullscreenChartModal = ({
title={ title } title={ title }
resolution={ resolution } resolution={ resolution }
/> />
</ModalBody> </DialogBody>
</ModalContent> </DialogContent>
</Modal> </DialogRoot>
); );
}; };
......
...@@ -71,7 +71,7 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement, ...@@ -71,7 +71,7 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement,
</TableCell> </TableCell>
<TableCell whiteSpace="nowrap"> <TableCell whiteSpace="nowrap">
{ tx.method && ( { tx.method && (
<Badge colorScheme={ tx.method === 'Multicall' ? 'teal' : 'gray' } loading={ isLoading } truncated> <Badge colorPalette={ tx.method === 'Multicall' ? 'teal' : 'gray' } loading={ isLoading } truncated>
<span>{ tx.method }</span> <span>{ tx.method }</span>
</Badge> </Badge>
) } ) }
......
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