Commit b740a53f authored by tom's avatar tom

refactor block list

parent a75ee466
...@@ -34,6 +34,7 @@ const RESTRICTED_MODULES = { ...@@ -34,6 +34,7 @@ const RESTRICTED_MODULES = {
'DrawerRoot', 'DrawerBody', 'DrawerContent', 'DrawerOverlay', 'DrawerBackdrop', 'DrawerTrigger', 'Drawer', 'DrawerRoot', 'DrawerBody', 'DrawerContent', 'DrawerOverlay', 'DrawerBackdrop', 'DrawerTrigger', 'Drawer',
'Alert', 'AlertIcon', 'AlertTitle', 'AlertDescription', 'Alert', 'AlertIcon', 'AlertTitle', 'AlertDescription',
'Heading', 'Badge', 'Tabs', 'Heading', 'Badge', 'Tabs',
'Table', 'TableRoot', 'TableBody', 'TableHeader', 'TableRow', 'TableCell',
], ],
message: 'Please use corresponding component or hook from ui/shared/chakra component instead', message: 'Please use corresponding component or hook from ui/shared/chakra component instead',
}, },
......
...@@ -2,7 +2,7 @@ import React from 'react'; ...@@ -2,7 +2,7 @@ import React from 'react';
type Id = string | number; type Id = string | number;
interface Params<T> { export interface Params<T> {
data: Array<T>; data: Array<T>;
idFn: (item: T) => Id; idFn: (item: T) => Id;
enabled: boolean; enabled: boolean;
...@@ -22,10 +22,15 @@ export default function useInitialList<T>({ data, idFn, enabled }: Params<T>) { ...@@ -22,10 +22,15 @@ export default function useInitialList<T>({ data, idFn, enabled }: Params<T>) {
return !list.includes(idFn(data)); return !list.includes(idFn(data));
}, [ list, idFn ]); }, [ list, idFn ]);
const getAnimationProp = React.useCallback((data: T) => {
return isNew(data) ? 'fade-in 500ms linear' : undefined;
}, [ isNew ]);
return React.useMemo(() => { return React.useMemo(() => {
return { return {
list, list,
isNew, isNew,
getAnimationProp,
}; };
}, [ list, isNew ]); }, [ list, isNew, getAnimationProp ]);
} }
import { Table as ChakraTable } from '@chakra-ui/react';
import { throttle } from 'es-toolkit';
import * as React from 'react';
export const TableRoot = ChakraTable.Root;
export const TableBody = ChakraTable.Body;
export const TableHeader = ChakraTable.Header;
export const TableRow = ChakraTable.Row;
export interface TableCellProps extends ChakraTable.CellProps {
isNumeric?: boolean;
}
export const TableCell = (props: TableCellProps) => {
const { isNumeric, ...rest } = props;
return <ChakraTable.Cell textAlign={ isNumeric ? 'right' : undefined } { ...rest }/>;
};
export interface TableColumnHeaderProps extends ChakraTable.ColumnHeaderProps {
isNumeric?: boolean;
}
export const TableColumnHeader = (props: TableColumnHeaderProps) => {
const { isNumeric, ...rest } = props;
return <ChakraTable.ColumnHeader textAlign={ isNumeric ? 'right' : undefined } { ...rest }/>;
};
export interface TableHeaderProps extends ChakraTable.HeaderProps {
top?: number;
}
export const TableHeaderSticky = (props: TableHeaderProps) => {
const { top, children, ...rest } = props;
const ref = React.useRef<HTMLTableSectionElement>(null);
const [ isStuck, setIsStuck ] = React.useState(false);
const handleScroll = React.useCallback(() => {
if (Number(ref.current?.getBoundingClientRect().y) <= (top || 0)) {
setIsStuck(true);
} else {
setIsStuck(false);
}
}, [ top ]);
React.useEffect(() => {
const throttledHandleScroll = throttle(handleScroll, 300);
window.addEventListener('scroll', throttledHandleScroll);
return () => {
window.removeEventListener('scroll', throttledHandleScroll);
};
}, [ handleScroll ]);
return (
<TableHeader
ref={ ref }
position="sticky"
top={ top ? `${ top }px` : 0 }
backgroundColor={{ _light: 'white', _dark: 'black' }}
boxShadow={ isStuck ? 'action_bar' : 'none' }
zIndex="1"
{ ...rest }
>
{ children }
</TableHeader>
);
};
...@@ -286,6 +286,12 @@ const semanticTokens: ThemingConfig['semanticTokens'] = { ...@@ -286,6 +286,12 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
fg: { value: { _light: '{colors.cyan.500}', _dark: '{colors.cyan.100}' } }, fg: { value: { _light: '{colors.cyan.500}', _dark: '{colors.cyan.100}' } },
}, },
}, },
table: {
header: {
bg: { value: { _light: '{colors.blackAlpha.100}', _dark: '{colors.whiteAlpha.200}' } },
fg: { value: { _light: '{colors.blackAlpha.700}', _dark: '{colors.whiteAlpha.700}' } },
},
},
heading: { heading: {
DEFAULT: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } }, DEFAULT: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
}, },
......
...@@ -10,7 +10,7 @@ const globalCss: Record<string, SystemStyleObject> = { ...@@ -10,7 +10,7 @@ const globalCss: Record<string, SystemStyleObject> = {
bg: 'global.body.bg', bg: 'global.body.bg',
color: 'global.body.fg', color: 'global.body.fg',
'-webkit-tap-highlight-color': 'transparent', '-webkit-tap-highlight-color': 'transparent',
'font-variant-ligatures': 'no-contextual', fontVariantLigatures: 'no-contextual',
}, },
mark: { mark: {
bg: 'global.mark.bg', bg: 'global.mark.bg',
......
...@@ -15,6 +15,7 @@ import { recipe as select } from './select.recipe'; ...@@ -15,6 +15,7 @@ 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 switchRecipe } from './switch.recipe'; import { recipe as switchRecipe } from './switch.recipe';
import { recipe as table } from './table.recipe';
import { recipe as tabs } from './tabs.recipe'; import { recipe as tabs } from './tabs.recipe';
import { recipe as textarea } from './textarea.recipe'; import { recipe as textarea } from './textarea.recipe';
import { recipe as toast } from './toast.recipe'; import { recipe as toast } from './toast.recipe';
...@@ -42,6 +43,7 @@ export const slotRecipes = { ...@@ -42,6 +43,7 @@ export const slotRecipes = {
progressCircle, progressCircle,
select, select,
'switch': switchRecipe, 'switch': switchRecipe,
table,
tabs, tabs,
toast, toast,
tooltip, tooltip,
......
import { defineSlotRecipe } from '@chakra-ui/react';
export const recipe = defineSlotRecipe({
slots: [ 'root', 'row', 'cell', 'columnHeader', 'caption', 'footer', 'body', 'header' ],
base: {
root: {
tableLayout: 'fixed',
fontVariant: 'normal',
fontVariantLigatures: 'no-contextual',
borderCollapse: 'collapse',
width: 'full',
textAlign: 'start',
verticalAlign: 'top',
overflow: 'unset',
},
cell: {
textAlign: 'start',
alignItems: 'center',
verticalAlign: 'top',
},
columnHeader: {
fontWeight: '500',
textAlign: 'start',
},
},
variants: {
variant: {
line: {
columnHeader: {
color: 'table.header.fg',
backgroundColor: 'table.header.bg',
_first: {
borderTopLeftRadius: '8px',
},
_last: {
borderTopRightRadius: '8px',
},
},
cell: {
borderBottomWidth: '1px',
},
row: {
bg: 'bg',
},
},
},
size: {
md: {
root: {
fontSize: 'sm',
},
columnHeader: {
px: '6px',
py: '10px',
_first: {
pl: 3,
},
_last: {
pr: 3,
},
},
cell: {
px: '6px',
py: 4,
_first: {
pl: 3,
},
_last: {
pr: 3,
},
},
},
},
},
defaultVariants: {
variant: 'line',
size: 'md',
},
});
...@@ -126,11 +126,12 @@ const BlocksContent = ({ type, query, enableSocket = true, top }: Props) => { ...@@ -126,11 +126,12 @@ const BlocksContent = ({ type, query, enableSocket = true, top }: Props) => {
return ( return (
<DataListDisplay <DataListDisplay
isError={ query.isError } isError={ query.isError }
items={ query.data?.items } itemsNum={ query.data?.items?.length }
emptyText="There are no blocks." emptyText="There are no blocks."
content={ content }
actionBar={ actionBar } actionBar={ actionBar }
/> >
{ content }
</DataListDisplay>
); );
}; };
......
import { Box } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import { AnimatePresence } from 'framer-motion';
import React from 'react'; import React from 'react';
import type { Block } from 'types/api/block'; import type { Block } from 'types/api/block';
import useInitialList from 'lib/hooks/useInitialList';
import BlocksListItem from 'ui/blocks/BlocksListItem'; import BlocksListItem from 'ui/blocks/BlocksListItem';
interface Props { interface Props {
...@@ -13,18 +13,23 @@ interface Props { ...@@ -13,18 +13,23 @@ interface Props {
} }
const BlocksList = ({ data, isLoading, page }: Props) => { const BlocksList = ({ data, isLoading, page }: Props) => {
const initialList = useInitialList({
data: data ?? [],
idFn: (item) => item.height,
enabled: !isLoading,
});
return ( return (
<Box> <Box>
<AnimatePresence initial={ false }>
{ data.map((item, index) => ( { data.map((item, index) => (
<BlocksListItem <BlocksListItem
key={ item.height + (isLoading ? String(index) : '') } key={ item.height + (isLoading ? String(index) : '') }
data={ item } data={ item }
isLoading={ isLoading } isLoading={ isLoading }
enableTimeIncrement={ page === 1 && !isLoading } enableTimeIncrement={ page === 1 && !isLoading }
animation={ initialList.getAnimationProp(item) }
/> />
)) } )) }
</AnimatePresence>
</Box> </Box>
); );
}; };
......
import { Flex, Text, Box, Tooltip } from '@chakra-ui/react'; import { Flex, Text, Box } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { capitalize } from 'es-toolkit'; import { capitalize } from 'es-toolkit';
import React from 'react'; import React from 'react';
...@@ -12,8 +12,9 @@ import getBlockTotalReward from 'lib/block/getBlockTotalReward'; ...@@ -12,8 +12,9 @@ import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import { WEI } from 'lib/consts'; import { WEI } from 'lib/consts';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle'; import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
import { currencyUnits } from 'lib/units'; import { currencyUnits } from 'lib/units';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { Tooltip } from 'toolkit/chakra/tooltip';
import BlockGasUsed from 'ui/shared/block/BlockGasUsed'; import BlockGasUsed from 'ui/shared/block/BlockGasUsed';
import Skeleton from 'ui/shared/chakra/Skeleton';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
...@@ -28,18 +29,19 @@ interface Props { ...@@ -28,18 +29,19 @@ interface Props {
data: Block; data: Block;
isLoading?: boolean; isLoading?: boolean;
enableTimeIncrement?: boolean; enableTimeIncrement?: boolean;
animation?: string;
} }
const isRollup = config.features.rollup.isEnabled; const isRollup = config.features.rollup.isEnabled;
const BlocksListItem = ({ data, isLoading, enableTimeIncrement }: Props) => { const BlocksListItem = ({ data, isLoading, enableTimeIncrement, animation }: Props) => {
const totalReward = getBlockTotalReward(data); const totalReward = getBlockTotalReward(data);
const burntFees = BigNumber(data.burnt_fees || 0); const burntFees = BigNumber(data.burnt_fees || 0);
const txFees = BigNumber(data.transaction_fees || 0); const txFees = BigNumber(data.transaction_fees || 0);
const baseFeeValue = getBaseFeeValue(data.base_fee_per_gas); const baseFeeValue = getBaseFeeValue(data.base_fee_per_gas);
return ( return (
<ListItemMobile rowGap={ 3 } key={ String(data.height) } isAnimated> <ListItemMobile rowGap={ 3 } key={ String(data.height) } animation={ animation }>
<Flex justifyContent="space-between" w="100%"> <Flex justifyContent="space-between" w="100%">
<Flex columnGap={ 2 } alignItems="center"> <Flex columnGap={ 2 } alignItems="center">
<BlockEntity <BlockEntity
...@@ -50,7 +52,7 @@ const BlocksListItem = ({ data, isLoading, enableTimeIncrement }: Props) => { ...@@ -50,7 +52,7 @@ const BlocksListItem = ({ data, isLoading, enableTimeIncrement }: Props) => {
fontWeight={ 600 } fontWeight={ 600 }
/> />
{ data.celo?.is_epoch_block && ( { data.celo?.is_epoch_block && (
<Tooltip label={ `Finalized epoch #${ data.celo.epoch_number }` }> <Tooltip content={ `Finalized epoch #${ data.celo.epoch_number }` } disabled={ isLoading }>
<IconSvg name="checkered_flag" boxSize={ 5 } p="1px" isLoading={ isLoading } flexShrink={ 0 }/> <IconSvg name="checkered_flag" boxSize={ 5 } p="1px" isLoading={ isLoading } flexShrink={ 0 }/>
</Tooltip> </Tooltip>
) } ) }
...@@ -66,7 +68,7 @@ const BlocksListItem = ({ data, isLoading, enableTimeIncrement }: Props) => { ...@@ -66,7 +68,7 @@ const BlocksListItem = ({ data, isLoading, enableTimeIncrement }: Props) => {
</Flex> </Flex>
<Flex columnGap={ 2 }> <Flex columnGap={ 2 }>
<Text fontWeight={ 500 }>Size</Text> <Text fontWeight={ 500 }>Size</Text>
<Skeleton isLoaded={ !isLoading } display="inline-block" color="text_secondary"> <Skeleton loading={ isLoading } display="inline-block" color="text_secondary">
<span>{ data.size.toLocaleString() } bytes</span> <span>{ data.size.toLocaleString() } bytes</span>
</Skeleton> </Skeleton>
</Flex> </Flex>
...@@ -83,33 +85,33 @@ const BlocksListItem = ({ data, isLoading, enableTimeIncrement }: Props) => { ...@@ -83,33 +85,33 @@ const BlocksListItem = ({ data, isLoading, enableTimeIncrement }: Props) => {
<Flex columnGap={ 2 }> <Flex columnGap={ 2 }>
<Text fontWeight={ 500 }>Txn</Text> <Text fontWeight={ 500 }>Txn</Text>
{ data.transaction_count > 0 ? ( { data.transaction_count > 0 ? (
<Skeleton isLoaded={ !isLoading } display="inline-block"> <Skeleton loading={ isLoading } display="inline-block">
<LinkInternal href={ route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: String(data.height), tab: 'txs' } }) }> <LinkInternal href={ route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: String(data.height), tab: 'txs' } }) }>
{ data.transaction_count } { data.transaction_count }
</LinkInternal> </LinkInternal>
</Skeleton> </Skeleton>
) : ) :
<Text variant="secondary">{ data.transaction_count }</Text> <Text color="text.secondary">{ data.transaction_count }</Text>
} }
</Flex> </Flex>
<Box> <Box>
<Text fontWeight={ 500 }>Gas used</Text> <Text fontWeight={ 500 }>Gas used</Text>
<Flex mt={ 2 }> <Flex mt={ 2 }>
<Skeleton isLoaded={ !isLoading } display="inline-block" color="text_secondary" mr={ 4 }> <Skeleton loading={ isLoading } display="inline-block" color="text_secondary" mr={ 4 }>
<span>{ BigNumber(data.gas_used || 0).toFormat() }</span> <span>{ BigNumber(data.gas_used || 0).toFormat() }</span>
</Skeleton> </Skeleton>
<BlockGasUsed <BlockGasUsed
gasUsed={ data.gas_used } gasUsed={ data.gas_used || undefined }
gasLimit={ data.gas_limit } gasLimit={ data.gas_limit }
isLoading={ isLoading } isLoading={ isLoading }
gasTarget={ data.gas_target_percentage } gasTarget={ data.gas_target_percentage || undefined }
/> />
</Flex> </Flex>
</Box> </Box>
{ !isRollup && !config.UI.views.block.hiddenFields?.total_reward && ( { !isRollup && !config.UI.views.block.hiddenFields?.total_reward && (
<Flex columnGap={ 2 }> <Flex columnGap={ 2 }>
<Text fontWeight={ 500 }>Reward { currencyUnits.ether }</Text> <Text fontWeight={ 500 }>Reward { currencyUnits.ether }</Text>
<Skeleton isLoaded={ !isLoading } display="inline-block" color="text_secondary"> <Skeleton loading={ isLoading } display="inline-block" color="text_secondary">
<span>{ totalReward.toFixed() }</span> <span>{ totalReward.toFixed() }</span>
</Skeleton> </Skeleton>
</Flex> </Flex>
...@@ -120,7 +122,7 @@ const BlocksListItem = ({ data, isLoading, enableTimeIncrement }: Props) => { ...@@ -120,7 +122,7 @@ const BlocksListItem = ({ data, isLoading, enableTimeIncrement }: Props) => {
<Flex columnGap={ 4 } mt={ 2 }> <Flex columnGap={ 4 } mt={ 2 }>
<Flex> <Flex>
<IconSvg name="flame" boxSize={ 5 } color="gray.500" isLoading={ isLoading }/> <IconSvg name="flame" boxSize={ 5 } color="gray.500" isLoading={ isLoading }/>
<Skeleton isLoaded={ !isLoading } display="inline-block" color="text_secondary" ml={ 2 }> <Skeleton loading={ isLoading } display="inline-block" color="text_secondary" ml={ 2 }>
<span>{ burntFees.div(WEI).toFixed() }</span> <span>{ burntFees.div(WEI).toFixed() }</span>
</Skeleton> </Skeleton>
</Flex> </Flex>
...@@ -131,7 +133,7 @@ const BlocksListItem = ({ data, isLoading, enableTimeIncrement }: Props) => { ...@@ -131,7 +133,7 @@ const BlocksListItem = ({ data, isLoading, enableTimeIncrement }: Props) => {
{ !isRollup && !config.UI.views.block.hiddenFields?.base_fee && baseFeeValue && ( { !isRollup && !config.UI.views.block.hiddenFields?.base_fee && baseFeeValue && (
<Flex columnGap={ 2 }> <Flex columnGap={ 2 }>
<Text fontWeight={ 500 }>Base fee</Text> <Text fontWeight={ 500 }>Base fee</Text>
<Skeleton isLoaded={ !isLoading } display="inline-block" color="text_secondary"> <Skeleton loading={ isLoading } display="inline-block" color="text_secondary">
<span>{ baseFeeValue }</span> <span>{ baseFeeValue }</span>
</Skeleton> </Skeleton>
</Flex> </Flex>
......
import { Table, Tbody, Tr, Th } from '@chakra-ui/react';
import { capitalize } from 'es-toolkit'; import { capitalize } from 'es-toolkit';
import { AnimatePresence } from 'framer-motion';
import React from 'react'; import React from 'react';
import type { Block } from 'types/api/block'; import type { Block } from 'types/api/block';
import config from 'configs/app'; import config from 'configs/app';
import { AddressHighlightProvider } from 'lib/contexts/addressHighlight'; import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
import useInitialList from 'lib/hooks/useInitialList';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle'; import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
import { currencyUnits } from 'lib/units'; import { currencyUnits } from 'lib/units';
import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import BlocksTableItem from 'ui/blocks/BlocksTableItem'; import BlocksTableItem from 'ui/blocks/BlocksTableItem';
import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import { default as Thead } from 'ui/shared/TheadSticky';
interface Props { interface Props {
data: Array<Block>; data: Array<Block>;
...@@ -31,6 +30,11 @@ const FEES_COL_WEIGHT = 22; ...@@ -31,6 +30,11 @@ const FEES_COL_WEIGHT = 22;
const isRollup = config.features.rollup.isEnabled; const isRollup = config.features.rollup.isEnabled;
const BlocksTable = ({ data, isLoading, top, page, showSocketInfo, socketInfoNum, socketInfoAlert }: Props) => { const BlocksTable = ({ data, isLoading, top, page, showSocketInfo, socketInfoNum, socketInfoAlert }: Props) => {
const initialList = useInitialList({
data: data ?? [],
idFn: (item) => item.height,
enabled: !isLoading,
});
const widthBase = const widthBase =
(!config.UI.views.block.hiddenFields?.miner ? VALIDATOR_COL_WEIGHT : 0) + (!config.UI.views.block.hiddenFields?.miner ? VALIDATOR_COL_WEIGHT : 0) +
...@@ -40,24 +44,27 @@ const BlocksTable = ({ data, isLoading, top, page, showSocketInfo, socketInfoNum ...@@ -40,24 +44,27 @@ const BlocksTable = ({ data, isLoading, top, page, showSocketInfo, socketInfoNum
return ( return (
<AddressHighlightProvider> <AddressHighlightProvider>
<Table minWidth="1040px" fontWeight={ 500 }> <TableRoot minWidth="1040px" fontWeight={ 500 }>
<Thead top={ top }> <TableHeaderSticky top={ top }>
<Tr> <TableRow>
<Th width="150px">Block</Th> <TableColumnHeader width="150px">Block</TableColumnHeader>
<Th width="120px">Size, bytes</Th> <TableColumnHeader width="120px">Size, bytes</TableColumnHeader>
{ !config.UI.views.block.hiddenFields?.miner && { !config.UI.views.block.hiddenFields?.miner && (
<Th width={ `${ VALIDATOR_COL_WEIGHT / widthBase * 100 }%` } minW="160px">{ capitalize(getNetworkValidatorTitle()) }</Th> } <TableColumnHeader width={ `${ VALIDATOR_COL_WEIGHT / widthBase * 100 }%` } minW="160px">
<Th width="64px" isNumeric>Txn</Th> { capitalize(getNetworkValidatorTitle()) }
<Th width={ `${ GAS_COL_WEIGHT / widthBase * 100 }%` }>Gas used</Th> </TableColumnHeader>
) }
<TableColumnHeader width="64px" isNumeric>Txn</TableColumnHeader>
<TableColumnHeader width={ `${ GAS_COL_WEIGHT / widthBase * 100 }%` }>Gas used</TableColumnHeader>
{ !isRollup && !config.UI.views.block.hiddenFields?.total_reward && { !isRollup && !config.UI.views.block.hiddenFields?.total_reward &&
<Th width={ `${ REWARD_COL_WEIGHT / widthBase * 100 }%` }>Reward { currencyUnits.ether }</Th> } <TableColumnHeader width={ `${ REWARD_COL_WEIGHT / widthBase * 100 }%` }>Reward { currencyUnits.ether }</TableColumnHeader> }
{ !isRollup && !config.UI.views.block.hiddenFields?.burnt_fees && { !isRollup && !config.UI.views.block.hiddenFields?.burnt_fees &&
<Th width={ `${ FEES_COL_WEIGHT / widthBase * 100 }%` }>Burnt fees { currencyUnits.ether }</Th> } <TableColumnHeader width={ `${ FEES_COL_WEIGHT / widthBase * 100 }%` }>Burnt fees { currencyUnits.ether }</TableColumnHeader> }
{ !isRollup && !config.UI.views.block.hiddenFields?.base_fee && { !isRollup && !config.UI.views.block.hiddenFields?.base_fee &&
<Th width="150px" isNumeric>Base fee</Th> } <TableColumnHeader width="150px" isNumeric>Base fee</TableColumnHeader> }
</Tr> </TableRow>
</Thead> </TableHeaderSticky>
<Tbody> <TableBody>
{ showSocketInfo && ( { showSocketInfo && (
<SocketNewItemsNotice.Desktop <SocketNewItemsNotice.Desktop
url={ window.location.href } url={ window.location.href }
...@@ -67,18 +74,17 @@ const BlocksTable = ({ data, isLoading, top, page, showSocketInfo, socketInfoNum ...@@ -67,18 +74,17 @@ const BlocksTable = ({ data, isLoading, top, page, showSocketInfo, socketInfoNum
isLoading={ isLoading } isLoading={ isLoading }
/> />
) } ) }
<AnimatePresence initial={ false }>
{ data.map((item, index) => ( { data.map((item, index) => (
<BlocksTableItem <BlocksTableItem
key={ item.height + (isLoading ? `${ index }_${ page }` : '') } key={ item.height + (isLoading ? `${ index }_${ page }` : '') }
data={ item } data={ item }
enableTimeIncrement={ page === 1 && !isLoading } enableTimeIncrement={ page === 1 && !isLoading }
isLoading={ isLoading } isLoading={ isLoading }
animation={ initialList.getAnimationProp(item) }
/> />
)) } )) }
</AnimatePresence> </TableBody>
</Tbody> </TableRoot>
</Table>
</AddressHighlightProvider> </AddressHighlightProvider>
); );
}; };
......
import { Tr, Td, Flex, Box, Tooltip, useColorModeValue } from '@chakra-ui/react'; import { Flex, Box } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { motion } from 'framer-motion';
import React from 'react'; import React from 'react';
import type { Block } from 'types/api/block'; import type { Block } from 'types/api/block';
...@@ -10,8 +9,10 @@ import { route } from 'nextjs-routes'; ...@@ -10,8 +9,10 @@ import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import getBlockTotalReward from 'lib/block/getBlockTotalReward'; import getBlockTotalReward from 'lib/block/getBlockTotalReward';
import { WEI } from 'lib/consts'; import { WEI } from 'lib/consts';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { TableCell, TableRow } from 'toolkit/chakra/table';
import { Tooltip } from 'toolkit/chakra/tooltip';
import BlockGasUsed from 'ui/shared/block/BlockGasUsed'; import BlockGasUsed from 'ui/shared/block/BlockGasUsed';
import Skeleton from 'ui/shared/chakra/Skeleton';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity'; import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
...@@ -24,44 +25,34 @@ import { getBaseFeeValue } from './utils'; ...@@ -24,44 +25,34 @@ import { getBaseFeeValue } from './utils';
interface Props { interface Props {
data: Block; data: Block;
isLoading?: boolean; isLoading?: boolean;
animation?: string;
enableTimeIncrement?: boolean; enableTimeIncrement?: boolean;
} }
const isRollup = config.features.rollup.isEnabled; const isRollup = config.features.rollup.isEnabled;
const BlocksTableItem = ({ data, isLoading, enableTimeIncrement }: Props) => { const BlocksTableItem = ({ data, isLoading, enableTimeIncrement, animation }: Props) => {
const totalReward = getBlockTotalReward(data); const totalReward = getBlockTotalReward(data);
const burntFees = BigNumber(data.burnt_fees || 0); const burntFees = BigNumber(data.burnt_fees || 0);
const txFees = BigNumber(data.transaction_fees || 0); const txFees = BigNumber(data.transaction_fees || 0);
const burntFeesIconColor = useColorModeValue('gray.500', 'inherit');
const baseFeeValue = getBaseFeeValue(data.base_fee_per_gas); const baseFeeValue = getBaseFeeValue(data.base_fee_per_gas);
return ( return (
<Tr <TableRow animation={ animation }>
as={ motion.tr } <TableCell >
initial={{ opacity: 0, scale: 0.97 }}
animate={{ opacity: 1, scale: 1 }}
transitionDuration="normal"
transitionTimingFunction="linear"
key={ data.height }
>
<Td fontSize="sm">
<Flex columnGap={ 2 } alignItems="center" mb={ 2 }> <Flex columnGap={ 2 } alignItems="center" mb={ 2 }>
{ data.celo?.is_epoch_block && ( { data.celo?.is_epoch_block && (
<Tooltip label={ `Finalized epoch #${ data.celo.epoch_number }` }> <Tooltip content={ `Finalized epoch #${ data.celo.epoch_number }` }>
<IconSvg name="checkered_flag" boxSize={ 5 } p="1px" isLoading={ isLoading } flexShrink={ 0 }/> <IconSvg name="checkered_flag" boxSize={ 5 } p="1px" isLoading={ isLoading } flexShrink={ 0 }/>
</Tooltip> </Tooltip>
) } ) }
<Tooltip isDisabled={ data.type !== 'reorg' } label="Chain reorganizations"> { /* TODO @tom2drum fix tooltip */ }
<Tooltip disabled={ data.type !== 'reorg' } content="Chain reorganizations">
<BlockEntity <BlockEntity
isLoading={ isLoading } isLoading={ isLoading }
number={ data.height } number={ data.height }
hash={ data.type !== 'block' ? data.hash : undefined } hash={ data.type !== 'block' ? data.hash : undefined }
noIcon noIcon
fontSize="sm"
lineHeight={ 5 }
fontWeight={ 600 } fontWeight={ 600 }
/> />
</Tooltip> </Tooltip>
...@@ -74,25 +65,25 @@ const BlocksTableItem = ({ data, isLoading, enableTimeIncrement }: Props) => { ...@@ -74,25 +65,25 @@ const BlocksTableItem = ({ data, isLoading, enableTimeIncrement }: Props) => {
fontWeight={ 400 } fontWeight={ 400 }
display="inline-block" display="inline-block"
/> />
</Td> </TableCell>
<Td fontSize="sm"> <TableCell >
<Skeleton isLoaded={ !isLoading } display="inline-block"> <Skeleton loading={ isLoading } display="inline-block">
{ data.size.toLocaleString() } { data.size.toLocaleString() }
</Skeleton> </Skeleton>
</Td> </TableCell>
{ !config.UI.views.block.hiddenFields?.miner && ( { !config.UI.views.block.hiddenFields?.miner && (
<Td fontSize="sm"> <TableCell >
<AddressEntity <AddressEntity
address={ data.miner } address={ data.miner }
isLoading={ isLoading } isLoading={ isLoading }
truncation="constant" truncation="constant"
maxW="min-content" maxW="min-content"
/> />
</Td> </TableCell>
) } ) }
<Td isNumeric fontSize="sm"> <TableCell isNumeric >
{ data.transaction_count > 0 ? ( { data.transaction_count > 0 ? (
<Skeleton isLoaded={ !isLoading } display="inline-block"> <Skeleton loading={ isLoading } display="inline-block">
<LinkInternal href={ route({ <LinkInternal href={ route({
pathname: '/block/[height_or_hash]', pathname: '/block/[height_or_hash]',
query: { height_or_hash: String(data.height), tab: 'txs' }, query: { height_or_hash: String(data.height), tab: 'txs' },
...@@ -101,48 +92,48 @@ const BlocksTableItem = ({ data, isLoading, enableTimeIncrement }: Props) => { ...@@ -101,48 +92,48 @@ const BlocksTableItem = ({ data, isLoading, enableTimeIncrement }: Props) => {
</LinkInternal> </LinkInternal>
</Skeleton> </Skeleton>
) : data.transaction_count } ) : data.transaction_count }
</Td> </TableCell>
<Td fontSize="sm"> <TableCell >
<Skeleton isLoaded={ !isLoading } display="inline-block">{ BigNumber(data.gas_used || 0).toFormat() }</Skeleton> <Skeleton loading={ isLoading } display="inline-block">{ BigNumber(data.gas_used || 0).toFormat() }</Skeleton>
<Flex mt={ 2 }> <Flex mt={ 2 }>
<BlockGasUsed <BlockGasUsed
gasUsed={ data.gas_used } gasUsed={ data.gas_used || undefined }
gasLimit={ data.gas_limit } gasLimit={ data.gas_limit }
isLoading={ isLoading } isLoading={ isLoading }
gasTarget={ data.gas_target_percentage } gasTarget={ data.gas_target_percentage || undefined }
/> />
</Flex> </Flex>
</Td> </TableCell>
{ !isRollup && !config.UI.views.block.hiddenFields?.total_reward && ( { !isRollup && !config.UI.views.block.hiddenFields?.total_reward && (
<Td fontSize="sm"> <TableCell >
<Skeleton isLoaded={ !isLoading } display="inline-block"> <Skeleton loading={ isLoading } display="inline-block">
{ totalReward.toFixed(8) } { totalReward.toFixed(8) }
</Skeleton> </Skeleton>
</Td> </TableCell>
) } ) }
{ !isRollup && !config.UI.views.block.hiddenFields?.burnt_fees && ( { !isRollup && !config.UI.views.block.hiddenFields?.burnt_fees && (
<Td fontSize="sm"> <TableCell >
<Flex alignItems="center" columnGap={ 2 }> <Flex alignItems="center" columnGap={ 2 }>
<IconSvg name="flame" boxSize={ 5 } color={ burntFeesIconColor } isLoading={ isLoading }/> <IconSvg name="flame" boxSize={ 5 } color={{ _light: 'gray.500', _dark: 'inherit' }} isLoading={ isLoading }/>
<Skeleton isLoaded={ !isLoading } display="inline-block"> <Skeleton loading={ isLoading } display="inline-block">
{ burntFees.dividedBy(WEI).toFixed(8) } { burntFees.dividedBy(WEI).toFixed(8) }
</Skeleton> </Skeleton>
</Flex> </Flex>
<Tooltip label={ isLoading ? undefined : 'Burnt fees / Txn fees * 100%' }> <Tooltip content="Burnt fees / Txn fees * 100%" disabled={ isLoading }>
<Box w="min-content"> <Box w="min-content">
<Utilization mt={ 2 } value={ burntFees.div(txFees).toNumber() } isLoading={ isLoading }/> <Utilization mt={ 2 } value={ burntFees.div(txFees).toNumber() } isLoading={ isLoading }/>
</Box> </Box>
</Tooltip> </Tooltip>
</Td> </TableCell>
) } ) }
{ !isRollup && !config.UI.views.block.hiddenFields?.base_fee && Boolean(baseFeeValue) && ( { !isRollup && !config.UI.views.block.hiddenFields?.base_fee && Boolean(baseFeeValue) && (
<Td fontSize="sm" isNumeric> <TableCell isNumeric>
<Skeleton isLoaded={ !isLoading } display="inline-block" whiteSpace="pre-wrap" wordBreak="break-word"> <Skeleton loading={ isLoading } display="inline-block" whiteSpace="pre-wrap" wordBreak="break-word">
{ baseFeeValue } { baseFeeValue }
</Skeleton> </Skeleton>
</Td> </TableCell>
) } ) }
</Tr> </TableRow>
); );
}; };
......
...@@ -90,7 +90,7 @@ const LatestBlocks = () => { ...@@ -90,7 +90,7 @@ const LatestBlocks = () => {
key={ block.height + (isPlaceholderData ? String(index) : '') } key={ block.height + (isPlaceholderData ? String(index) : '') }
block={ block } block={ block }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
isNew={ initialList.isNew(block) } animation={ initialList.getAnimationProp(block) }
/> />
))) } ))) }
</VStack> </VStack>
......
...@@ -16,14 +16,14 @@ import TimeAgoWithTooltip from 'ui/shared/TimeAgoWithTooltip'; ...@@ -16,14 +16,14 @@ import TimeAgoWithTooltip from 'ui/shared/TimeAgoWithTooltip';
type Props = { type Props = {
block: Block; block: Block;
isLoading?: boolean; isLoading?: boolean;
isNew?: boolean; animation?: string;
}; };
const LatestBlocksItem = ({ block, isLoading, isNew }: Props) => { const LatestBlocksItem = ({ block, isLoading, animation }: Props) => {
const totalReward = getBlockTotalReward(block); const totalReward = getBlockTotalReward(block);
return ( return (
<Box <Box
animation={ isNew ? 'fade-in 500ms linear' : undefined } animation={ animation }
borderRadius="md" borderRadius="md"
border="1px solid" border="1px solid"
borderColor="border.divider" borderColor="border.divider"
......
...@@ -77,7 +77,7 @@ const LatestArbitrumL2Batches = () => { ...@@ -77,7 +77,7 @@ const LatestArbitrumL2Batches = () => {
timestamp={ batch.commitment_transaction.timestamp } timestamp={ batch.commitment_transaction.timestamp }
txCount={ batch.transactions_count } txCount={ batch.transactions_count }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
isNew={ initialList.isNew(batch) } animation={ initialList.getAnimationProp(batch) }
/> />
))) } ))) }
</VStack> </VStack>
......
...@@ -14,13 +14,13 @@ type Props = { ...@@ -14,13 +14,13 @@ type Props = {
txCount: number; txCount: number;
status?: React.ReactNode; status?: React.ReactNode;
isLoading: boolean; isLoading: boolean;
isNew?: boolean; animation?: string;
}; };
const LatestBatchItem = ({ number, timestamp, txCount, status, isLoading, isNew }: Props) => { const LatestBatchItem = ({ number, timestamp, txCount, status, isLoading, animation }: Props) => {
return ( return (
<Box <Box
animation={ isNew ? 'fade-in 500ms linear' : undefined } animation={ animation }
borderRadius="md" borderRadius="md"
border="1px solid" border="1px solid"
borderColor="border.divider" borderColor="border.divider"
......
...@@ -80,7 +80,7 @@ const LatestZkEvmL2Batches = () => { ...@@ -80,7 +80,7 @@ const LatestZkEvmL2Batches = () => {
timestamp={ batch.timestamp } timestamp={ batch.timestamp }
status={ status } status={ status }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
isNew={ initialList.isNew(batch) } animation={ initialList.getAnimationProp(batch) }
/> />
); );
})) } })) }
......
...@@ -70,15 +70,10 @@ const BlocksPageContent = () => { ...@@ -70,15 +70,10 @@ const BlocksPageContent = () => {
})(); })();
const tabs: Array<RoutedTab> = [ const tabs: Array<RoutedTab> = [
{ id: 'blocks', title: 'All', component: <div>All</div> }, { id: 'blocks', title: 'All', component: <BlocksContent type="block" query={ blocksQuery }/> },
{ id: 'reorgs', title: 'Forked', component: <div>Forked</div> }, { id: 'reorgs', title: 'Forked', component: <BlocksContent type="reorg" query={ reorgsQuery }/> },
{ id: 'uncles', title: 'Uncles', component: <div>Uncles</div> }, { id: 'uncles', title: 'Uncles', component: <BlocksContent type="uncle" query={ unclesQuery }/> },
]; ];
// const tabs: Array<RoutedTab> = [
// { id: 'blocks', title: 'All', component: <BlocksContent type="block" query={ blocksQuery }/> },
// { id: 'reorgs', title: 'Forked', component: <BlocksContent type="reorg" query={ reorgsQuery }/> },
// { id: 'uncles', title: 'Uncles', component: <BlocksContent type="uncle" query={ unclesQuery }/> },
// ];
return ( return (
<> <>
......
import { Flex, useColorModeValue, chakra } from '@chakra-ui/react'; import { Flex, chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { useScrollDirection } from 'lib/contexts/scrollDirection'; import { useScrollDirection } from 'lib/contexts/scrollDirection';
...@@ -19,7 +19,6 @@ const ActionBar = ({ children, className, showShadow }: Props) => { ...@@ -19,7 +19,6 @@ const ActionBar = ({ children, className, showShadow }: Props) => {
const ref = React.useRef<HTMLDivElement>(null); const ref = React.useRef<HTMLDivElement>(null);
const scrollDirection = useScrollDirection(); const scrollDirection = useScrollDirection();
const isSticky = useIsSticky(ref, TOP_UP + 5); const isSticky = useIsSticky(ref, TOP_UP + 5);
const bgColor = useColorModeValue('white', 'black');
if (!React.Children.toArray(children).filter(Boolean).length) { if (!React.Children.toArray(children).filter(Boolean).length) {
return null; return null;
...@@ -28,7 +27,7 @@ const ActionBar = ({ children, className, showShadow }: Props) => { ...@@ -28,7 +27,7 @@ const ActionBar = ({ children, className, showShadow }: Props) => {
return ( return (
<Flex <Flex
className={ className } className={ className }
backgroundColor={ bgColor } backgroundColor={{ _light: 'white', _dark: 'black' }}
pt={ 6 } pt={ 6 }
pb={{ base: 6, lg: 3 }} pb={{ base: 6, lg: 3 }}
mx={{ base: -3, lg: 0 }} mx={{ base: -3, lg: 0 }}
......
...@@ -12,11 +12,11 @@ type FilterProps = { ...@@ -12,11 +12,11 @@ type FilterProps = {
type Props = { type Props = {
isError: boolean; isError: boolean;
items?: Array<unknown>; itemsNum?: number;
emptyText: React.ReactNode; emptyText: React.ReactNode;
actionBar?: React.ReactNode; actionBar?: React.ReactNode;
showActionBarIfEmpty?: boolean; showActionBarIfEmpty?: boolean;
content: React.ReactNode; children: React.ReactNode;
className?: string; className?: string;
filterProps?: FilterProps; filterProps?: FilterProps;
}; };
...@@ -26,7 +26,7 @@ const DataListDisplay = (props: Props) => { ...@@ -26,7 +26,7 @@ const DataListDisplay = (props: Props) => {
return <DataFetchAlert className={ props.className }/>; return <DataFetchAlert className={ props.className }/>;
} }
if (props.filterProps?.hasActiveFilters && !props.items?.length) { if (props.filterProps?.hasActiveFilters && !props.itemsNum) {
return ( return (
<Box className={ props.className }> <Box className={ props.className }>
{ props.actionBar } { props.actionBar }
...@@ -35,7 +35,7 @@ const DataListDisplay = (props: Props) => { ...@@ -35,7 +35,7 @@ const DataListDisplay = (props: Props) => {
); );
} }
if (!props.items?.length) { if (!props.itemsNum) {
return ( return (
<> <>
{ props.showActionBarIfEmpty && props.actionBar } { props.showActionBarIfEmpty && props.actionBar }
...@@ -47,7 +47,7 @@ const DataListDisplay = (props: Props) => { ...@@ -47,7 +47,7 @@ const DataListDisplay = (props: Props) => {
return ( return (
<Box className={ props.className }> <Box className={ props.className }>
{ props.actionBar } { props.actionBar }
{ props.content } { props.children }
</Box> </Box>
); );
}; };
......
import { Tooltip } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import Skeleton from 'ui/shared/chakra/Skeleton'; import { Skeleton } from 'toolkit/chakra/skeleton';
import { Tooltip } from 'toolkit/chakra/tooltip';
type Props = { type Props = {
value: number; value: number;
...@@ -10,8 +10,8 @@ type Props = { ...@@ -10,8 +10,8 @@ type Props = {
const GasUsedToTargetRatio = ({ value, isLoading }: Props) => { const GasUsedToTargetRatio = ({ value, isLoading }: Props) => {
return ( return (
<Tooltip label="% of Gas Target"> <Tooltip content="% of Gas Target">
<Skeleton color="text_secondary" isLoaded={ !isLoading }> <Skeleton color="text_secondary" loading={ isLoading }>
<span>{ (value > 0 ? '+' : '') + value.toLocaleString(undefined, { maximumFractionDigits: 2 }) }%</span> <span>{ (value > 0 ? '+' : '') + value.toLocaleString(undefined, { maximumFractionDigits: 2 }) }%</span>
</Skeleton> </Skeleton>
</Tooltip> </Tooltip>
......
import { Flex, chakra } from '@chakra-ui/react'; import { Flex, chakra } from '@chakra-ui/react';
import { motion } from 'framer-motion';
import React from 'react'; import React from 'react';
interface Props { interface Props {
children: React.ReactNode; children: React.ReactNode;
className?: string; className?: string;
isAnimated?: boolean; animation?: string;
} }
const ListItemMobile = ({ children, className, isAnimated }: Props) => { const ListItemMobile = ({ children, className, animation }: Props) => {
return ( return (
<Flex <Flex
as={ motion.div } animation={ animation }
initial={ isAnimated ? { opacity: 0, scale: 0.97 } : { opacity: 1, scale: 1 } }
animate={{ opacity: 1, scale: 1 }}
transitionDuration="normal"
transitionTimingFunction="linear"
rowGap={ 6 } rowGap={ 6 }
alignItems="flex-start" alignItems="flex-start"
flexDirection="column" flexDirection="column"
......
import { Thead, useColorModeValue } from '@chakra-ui/react';
import type { TableHeadProps, PositionProps } from '@chakra-ui/react';
import { throttle } from 'es-toolkit';
import React from 'react';
interface Props extends TableHeadProps {
top?: number;
children?: React.ReactNode;
}
const TheadSticky = ({ top, children, ...restProps }: Props) => {
const ref = React.useRef<HTMLTableSectionElement>(null);
const [ isSticky, setIsSticky ] = React.useState(false);
const handleScroll = React.useCallback(() => {
if (Number(ref.current?.getBoundingClientRect().y) <= (top || 0)) {
setIsSticky(true);
} else {
setIsSticky(false);
}
}, [ top ]);
React.useEffect(() => {
const throttledHandleScroll = throttle(handleScroll, 300);
window.addEventListener('scroll', throttledHandleScroll);
return () => {
window.removeEventListener('scroll', throttledHandleScroll);
};
}, [ handleScroll ]);
const props = {
...restProps,
position: 'sticky' as PositionProps['position'],
top: top ? `${ top }px` : 0,
backgroundColor: useColorModeValue('white', 'black'),
boxShadow: isSticky ? 'md' : 'none',
zIndex: '1',
};
return (
<Thead { ...props } ref={ ref }>
{ children }
</Thead>
);
};
export default TheadSticky;
import { chakra, Tooltip, Box, useColorModeValue } from '@chakra-ui/react'; import { chakra, Box } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import React from 'react'; import React from 'react';
import config from 'configs/app'; import config from 'configs/app';
import { Tooltip } from 'toolkit/chakra/tooltip';
import GasUsedToTargetRatio from '../GasUsedToTargetRatio'; import GasUsedToTargetRatio from '../GasUsedToTargetRatio';
import TextSeparator from '../TextSeparator'; import TextSeparator from '../TextSeparator';
...@@ -23,15 +24,13 @@ const BlockGasUsed = ({ className, gasUsed, gasLimit, gasTarget, isLoading }: Pr ...@@ -23,15 +24,13 @@ const BlockGasUsed = ({ className, gasUsed, gasLimit, gasTarget, isLoading }: Pr
gasUsed && gasUsed !== '0' && gasUsed && gasUsed !== '0' &&
(!rollupFeature.isEnabled || rollupFeature.type === 'optimistic' || rollupFeature.type === 'shibarium'); (!rollupFeature.isEnabled || rollupFeature.type === 'optimistic' || rollupFeature.type === 'shibarium');
const separatorColor = useColorModeValue('gray.200', 'gray.700');
if (!hasGasUtilization) { if (!hasGasUtilization) {
return null; return null;
} }
return ( return (
<> <>
<Tooltip label={ isLoading ? undefined : 'Gas Used %' }> <Tooltip content="Gas Used %" disabled={ isLoading }>
<Box> <Box>
<Utilization <Utilization
colorScheme="gray" colorScheme="gray"
...@@ -43,7 +42,7 @@ const BlockGasUsed = ({ className, gasUsed, gasLimit, gasTarget, isLoading }: Pr ...@@ -43,7 +42,7 @@ const BlockGasUsed = ({ className, gasUsed, gasLimit, gasTarget, isLoading }: Pr
</Tooltip> </Tooltip>
{ gasTarget && ( { gasTarget && (
<> <>
<TextSeparator color={ separatorColor } mx={ 1 }/> <TextSeparator color={{ _light: 'gray.200', _dark: 'gray.700' }} mx={ 1 }/>
<GasUsedToTargetRatio value={ gasTarget } isLoading={ isLoading }/> <GasUsedToTargetRatio value={ gasTarget } isLoading={ isLoading }/>
</> </>
) } ) }
......
...@@ -39,9 +39,10 @@ export interface ContainerBaseProps extends Pick<EntityBaseProps, 'className'> { ...@@ -39,9 +39,10 @@ export interface ContainerBaseProps extends Pick<EntityBaseProps, 'className'> {
onMouseLeave?: (event: React.MouseEvent) => void; onMouseLeave?: (event: React.MouseEvent) => void;
} }
const Container = chakra(({ className, children, ...props }: ContainerBaseProps) => { const Container = chakra(React.forwardRef(({ className, children, ...props }: ContainerBaseProps, ref: React.Ref<HTMLDivElement>) => {
return ( return (
<Flex <Flex
ref={ ref }
className={ className } className={ className }
alignItems="center" alignItems="center"
minWidth={ 0 } // for content truncation - https://css-tricks.com/flexbox-truncated-text/ minWidth={ 0 } // for content truncation - https://css-tricks.com/flexbox-truncated-text/
...@@ -50,7 +51,7 @@ const Container = chakra(({ className, children, ...props }: ContainerBaseProps) ...@@ -50,7 +51,7 @@ const Container = chakra(({ className, children, ...props }: ContainerBaseProps)
{ children } { children }
</Flex> </Flex>
); );
}); }));
export interface LinkBaseProps extends Pick<EntityBaseProps, 'className' | 'onClick' | 'isLoading' | 'isExternal' | 'href' | 'noLink' | 'query'> { export interface LinkBaseProps extends Pick<EntityBaseProps, 'className' | 'onClick' | 'isLoading' | 'isExternal' | 'href' | 'noLink' | 'query'> {
children: React.ReactNode; children: React.ReactNode;
......
...@@ -51,20 +51,20 @@ export interface EntityProps extends EntityBase.EntityBaseProps { ...@@ -51,20 +51,20 @@ export interface EntityProps extends EntityBase.EntityBaseProps {
hash?: string; hash?: string;
} }
const BlockEntity = (props: EntityProps) => { const BlockEntity = (props: EntityProps, ref: React.Ref<HTMLDivElement>) => {
const partsProps = distributeEntityProps(props); const partsProps = distributeEntityProps(props);
const content = <Content { ...partsProps.content }/>; const content = <Content { ...partsProps.content }/>;
return ( return (
<Container { ...partsProps.container }> <Container { ...partsProps.container } ref={ ref }>
<Icon { ...partsProps.icon }/> <Icon { ...partsProps.icon }/>
{ props.noLink ? content : <Link { ...partsProps.link }>{ content }</Link> } { props.noLink ? content : <Link { ...partsProps.link }>{ content }</Link> }
</Container> </Container>
); );
}; };
export default React.memo(chakra(BlockEntity)); export default React.memo(chakra(React.forwardRef(BlockEntity)));
export { export {
Container, Container,
......
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