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