Commit 730d6ddb authored by tom's avatar tom

block details tab

parent 34f281d3
...@@ -9,18 +9,21 @@ NEXT_PUBLIC_APP_PORT=3000 ...@@ -9,18 +9,21 @@ NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_ENV=development NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws
NEXT_PUBLIC_CELO_ENABLED=true
NEXT_PUBLIC_CELO_L2_UPGRADE_BLOCK=26369280
# Instance ENVs # Instance ENVs
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/ NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_API_HOST=celo-alfajores.blockscout.com NEXT_PUBLIC_API_HOST=celo-alfajores.blockscout.com
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
NEXT_PUBLIC_CELO_ENABLED=true
NEXT_PUBLIC_CELO_L2_UPGRADE_BLOCK=26369280
NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.ethereum.org/?address={hash}&blockscout={domain}','icon_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/ide-icons/remix.png'}] NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.ethereum.org/?address={hash}&blockscout={domain}','icon_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/ide-icons/remix.png'}]
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/celo-alfajores-testnet.json
NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/sherblockHolmesBadge
NEXT_PUBLIC_GAS_TRACKER_ENABLED=false NEXT_PUBLIC_GAS_TRACKER_ENABLED=false
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x9767ce30754afad2a3279b9df2d13257f467c3dad4e0e601271e66d16dfd1641
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs'] NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=rgba(252, 255, 82, 1) NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['rgba(252, 255, 82, 1)'],'text_color':['rgba(0, 0, 0, 1)']}
NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR=rgba(0, 0, 0, 1)
NEXT_PUBLIC_IS_TESTNET=true NEXT_PUBLIC_IS_TESTNET=true
NEXT_PUBLIC_MARKETPLACE_ENABLED=false NEXT_PUBLIC_MARKETPLACE_ENABLED=false
NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com
...@@ -32,12 +35,14 @@ NEXT_PUBLIC_NETWORK_ICON_DARK=https://raw.githubusercontent.com/blockscout/front ...@@ -32,12 +35,14 @@ NEXT_PUBLIC_NETWORK_ICON_DARK=https://raw.githubusercontent.com/blockscout/front
NEXT_PUBLIC_NETWORK_ID=44787 NEXT_PUBLIC_NETWORK_ID=44787
NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/celo-logo-light.svg NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/celo-logo-light.svg
NEXT_PUBLIC_NETWORK_LOGO_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/celo-logo-dark.svg NEXT_PUBLIC_NETWORK_LOGO_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/celo-logo-dark.svg
NEXT_PUBLIC_NETWORK_MULTIPLE_GAS_CURRENCIES=true
NEXT_PUBLIC_NETWORK_NAME=Celo Alfajores NEXT_PUBLIC_NETWORK_NAME=Celo Alfajores
NEXT_PUBLIC_NETWORK_RPC_URL=https://alfajores-forno.celo-testnet.org NEXT_PUBLIC_NETWORK_RPC_URL=https://alfajores-forno.celo-testnet.org
NEXT_PUBLIC_NETWORK_SHORT_NAME=Alfajores NEXT_PUBLIC_NETWORK_SHORT_NAME=Alfajores
NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true
NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/celo.png NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/celo.png
NEXT_PUBLIC_STATS_API_HOST=https://stats-alfajores-testnet.k8s-prod-1.blockscout.com
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
NEXT_PUBLIC_VIEWS_BLOCK_HIDDEN_FIELDS=['burnt_fees']
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_HOMEPAGE_STATS=['total_blocks','average_block_time','total_txs','wallet_addresses','gas_tracker','current_epoch'] \ No newline at end of file
\ No newline at end of file
...@@ -26,10 +26,11 @@ const RESTRICTED_MODULES = { ...@@ -26,10 +26,11 @@ const RESTRICTED_MODULES = {
{ name: '@metamask/post-message-stream', message: 'Please lazy-load @metamask/post-message-stream or use useProvider hook instead' }, { name: '@metamask/post-message-stream', message: 'Please lazy-load @metamask/post-message-stream or use useProvider hook instead' },
{ name: 'playwright/TestApp', message: 'Please use render() fixture from test() function of playwright/lib module' }, { name: 'playwright/TestApp', message: 'Please use render() fixture from test() function of playwright/lib module' },
{ name: 'ui/shared/chakra/Skeleton', message: 'Please use Skeleton component from toolkit/chakra instead' }, { name: 'ui/shared/chakra/Skeleton', message: 'Please use Skeleton component from toolkit/chakra instead' },
{ name: 'ui/shared/Tabs/RoutedTabs', message: 'Please use RoutedTabs component from toolkit/components/RoutedTabs instead' },
{ {
name: '@chakra-ui/react', name: '@chakra-ui/react',
importNames: [ importNames: [
'Menu', 'useToast', 'useDisclosure', 'useClipboard', 'Tooltip', 'Skeleton', 'IconButton', 'Button', 'Link', 'Menu', 'useToast', 'useDisclosure', 'useClipboard', 'Tooltip', 'Skeleton', 'IconButton', 'Button', 'Link', 'Tag',
'Image', 'Popover', 'PopoverTrigger', 'PopoverContent', 'PopoverBody', 'PopoverFooter', 'Image', 'Popover', 'PopoverTrigger', 'PopoverContent', 'PopoverBody', 'PopoverFooter',
'DrawerRoot', 'DrawerBody', 'DrawerContent', 'DrawerOverlay', 'DrawerBackdrop', 'DrawerTrigger', 'Drawer', 'DrawerRoot', 'DrawerBody', 'DrawerContent', 'DrawerOverlay', 'DrawerBackdrop', 'DrawerTrigger', 'Drawer',
'Alert', 'AlertIcon', 'AlertTitle', 'AlertDescription', 'Alert', 'AlertIcon', 'AlertTitle', 'AlertDescription',
......
...@@ -5,12 +5,12 @@ import React from 'react'; ...@@ -5,12 +5,12 @@ import React from 'react';
import type { Props } from 'nextjs/getServerSideProps'; import type { Props } from 'nextjs/getServerSideProps';
import PageNextJs from 'nextjs/PageNextJs'; import PageNextJs from 'nextjs/PageNextJs';
// const Block = dynamic(() => import('ui/pages/Block'), { ssr: false }); const Block = dynamic(() => import('ui/pages/Block'), { ssr: false });
const Page: NextPage<Props> = (props: Props) => { const Page: NextPage<Props> = (props: Props) => {
return ( return (
<PageNextJs pathname="/block/[height_or_hash]" query={ props.query }> <PageNextJs pathname="/block/[height_or_hash]" query={ props.query }>
{ /* <Block/> */ } <Block/>
</PageNextJs> </PageNextJs>
); );
}; };
......
...@@ -4,6 +4,7 @@ import * as React from 'react'; ...@@ -4,6 +4,7 @@ import * as React from 'react';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import { CloseButton } from './close-button'; import { CloseButton } from './close-button';
import { Skeleton } from './skeleton';
export interface AlertProps extends Omit<ChakraAlert.RootProps, 'title'> { export interface AlertProps extends Omit<ChakraAlert.RootProps, 'title'> {
startElement?: React.ReactNode; startElement?: React.ReactNode;
...@@ -12,6 +13,7 @@ export interface AlertProps extends Omit<ChakraAlert.RootProps, 'title'> { ...@@ -12,6 +13,7 @@ export interface AlertProps extends Omit<ChakraAlert.RootProps, 'title'> {
icon?: React.ReactElement; icon?: React.ReactElement;
closable?: boolean; closable?: boolean;
onClose?: () => void; onClose?: () => void;
loading?: boolean;
} }
export const Alert = React.forwardRef<HTMLDivElement, AlertProps>( export const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
...@@ -24,33 +26,36 @@ export const Alert = React.forwardRef<HTMLDivElement, AlertProps>( ...@@ -24,33 +26,36 @@ export const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
onClose, onClose,
startElement, startElement,
endElement, endElement,
loading,
...rest ...rest
} = props; } = props;
const defaultIcon = <IconSvg name="info_filled"/>; const defaultIcon = <IconSvg name="info_filled" w="100%" h="100%"/>;
return ( return (
<ChakraAlert.Root ref={ ref } { ...rest }> <Skeleton loading={ loading } asChild>
{ startElement !== undefined || icon !== undefined ? startElement : <ChakraAlert.Indicator>{ icon || defaultIcon }</ChakraAlert.Indicator> } <ChakraAlert.Root ref={ ref } { ...rest }>
{ children ? ( { startElement !== undefined || icon !== undefined ? startElement : <ChakraAlert.Indicator>{ icon || defaultIcon }</ChakraAlert.Indicator> }
<ChakraAlert.Content> { children ? (
{ title && <ChakraAlert.Title>{ title }</ChakraAlert.Title> } <ChakraAlert.Content>
<ChakraAlert.Description display="inline-flex">{ children }</ChakraAlert.Description> { title && <ChakraAlert.Title>{ title }</ChakraAlert.Title> }
</ChakraAlert.Content> <ChakraAlert.Description display="inline-flex">{ children }</ChakraAlert.Description>
) : ( </ChakraAlert.Content>
<ChakraAlert.Title flex="1">{ title }</ChakraAlert.Title> ) : (
) } <ChakraAlert.Title flex="1">{ title }</ChakraAlert.Title>
{ endElement } ) }
{ closable && ( { endElement }
<CloseButton { closable && (
pos="relative" <CloseButton
top="-2" pos="relative"
insetEnd="-2" top="-2"
alignSelf="flex-start" insetEnd="-2"
onClick={ onClose } alignSelf="flex-start"
/> onClick={ onClose }
) } />
</ChakraAlert.Root> ) }
</ChakraAlert.Root>
</Skeleton>
); );
}, },
); );
...@@ -14,10 +14,10 @@ export interface BadgeProps extends ChakraBadgeProps { ...@@ -14,10 +14,10 @@ export interface BadgeProps extends ChakraBadgeProps {
export const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>( export const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(
function Badge(props, ref) { function Badge(props, ref) {
const { loading, iconStart, children, ...rest } = props; const { loading, iconStart, children, asChild = true, ...rest } = props;
return ( return (
<Skeleton loading={ loading }> <Skeleton loading={ loading } asChild={ asChild }>
<ChakraBadge ref={ ref } display="flex" alignItems="center" gap={ 1 } { ...rest }> <ChakraBadge ref={ ref } display="flex" alignItems="center" gap={ 1 } { ...rest }>
{ iconStart && <IconSvg name={ iconStart } boxSize="10px"/> } { iconStart && <IconSvg name={ iconStart } boxSize="10px"/> }
{ children } { children }
......
...@@ -5,12 +5,14 @@ import { Skeleton } from './skeleton'; ...@@ -5,12 +5,14 @@ import { Skeleton } from './skeleton';
export interface IconButtonProps extends ButtonProps {} export interface IconButtonProps extends ButtonProps {}
// TODO @tom2drum variants for icon buttons: prev-next, top-bar, copy-to-clipboard
export const IconButton = React.forwardRef<HTMLDivElement, IconButtonProps>( export const IconButton = React.forwardRef<HTMLDivElement, IconButtonProps>(
function IconButton(props, ref) { function IconButton(props, ref) {
const { loading, size, variant = 'plain', ...rest } = props; const { loading, size, variant = 'plain', ...rest } = props;
return ( return (
<Skeleton loading={ loading } ref={ ref }> <Skeleton loading={ loading } ref={ ref } asChild>
<Button <Button
display="inline-flex" display="inline-flex"
px="0" px="0"
......
...@@ -12,6 +12,7 @@ export interface TooltipProps extends ChakraTooltip.RootProps { ...@@ -12,6 +12,7 @@ export interface TooltipProps extends ChakraTooltip.RootProps {
content: React.ReactNode; content: React.ReactNode;
contentProps?: ChakraTooltip.ContentProps; contentProps?: ChakraTooltip.ContentProps;
disabled?: boolean; disabled?: boolean;
disableOnMobile?: boolean;
} }
export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>( export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
...@@ -23,6 +24,7 @@ export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>( ...@@ -23,6 +24,7 @@ export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
selected, selected,
children, children,
disabled, disabled,
disableOnMobile,
portalled = true, portalled = true,
content, content,
contentProps, contentProps,
...@@ -36,6 +38,7 @@ export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>( ...@@ -36,6 +38,7 @@ export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
const [ open, setOpen ] = React.useState(defaultOpen); const [ open, setOpen ] = React.useState(defaultOpen);
const isMobile = useIsMobile(); const isMobile = useIsMobile();
// TODO @tom2drum merge refs
const triggerRef = useClickAway<HTMLButtonElement>(() => setOpen(false)); const triggerRef = useClickAway<HTMLButtonElement>(() => setOpen(false));
const handleOpenChange = React.useCallback((details: { open: boolean }) => { const handleOpenChange = React.useCallback((details: { open: boolean }) => {
...@@ -47,7 +50,7 @@ export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>( ...@@ -47,7 +50,7 @@ export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
setOpen((prev) => !prev); setOpen((prev) => !prev);
}, [ ]); }, [ ]);
if (disabled) return children; if (disabled || (disableOnMobile && isMobile)) return children;
const defaultShowArrow = visual === 'popover' ? false : true; const defaultShowArrow = visual === 'popover' ? false : true;
const showArrow = showArrowProp !== undefined ? showArrowProp : defaultShowArrow; const showArrow = showArrowProp !== undefined ? showArrowProp : defaultShowArrow;
......
import React from 'react'; import React from 'react';
import { scroller, Element } from 'react-scroll'; import { scroller, Element } from 'react-scroll';
import type { LinkProps } from 'toolkit/chakra/link';
import { Link } from 'toolkit/chakra/link'; import { Link } from 'toolkit/chakra/link';
interface Props { interface Props extends LinkProps {
children: React.ReactNode; children: React.ReactNode;
id?: string; id?: string;
onClick?: () => void; onClick?: () => void;
isLoading?: boolean;
} }
const ID = 'CutLink'; const ID = 'CutLink';
const CutLink = (props: Props) => { const CutLink = (props: Props) => {
const { children, id = ID, onClick, isLoading } = props; const { children, id = ID, onClick, ...rest } = props;
const [ isExpanded, setIsExpanded ] = React.useState(false); const [ isExpanded, setIsExpanded ] = React.useState(false);
...@@ -27,19 +26,20 @@ const CutLink = (props: Props) => { ...@@ -27,19 +26,20 @@ const CutLink = (props: Props) => {
onClick?.(); onClick?.();
}, [ id, onClick ]); }, [ id, onClick ]);
const text = isExpanded ? 'Hide details' : 'View details';
return ( return (
<> <>
<Element name={ id }> <Link
<Link textStyle="sm"
textStyle="sm" textDecorationLine="underline"
textDecorationLine="underline" textDecorationStyle="dashed"
textDecorationStyle="dashed" onClick={ handleClick }
onClick={ handleClick } asChild
loading={ isLoading } { ...rest }
> >
{ isExpanded ? 'Hide details' : 'View details' } <Element name={ id }>{ text }</Element>
</Link> </Link>
</Element>
{ isExpanded && children } { isExpanded && children }
</> </>
); );
......
import { Flex, chakra, Box, useColorModeValue } from '@chakra-ui/react'; import { Flex, chakra, Box } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { RoutedTab } from '../Tabs/types'; import type { TabItemRegular } from '../AdaptiveTabs/types';
import Skeleton from 'ui/shared/chakra/Skeleton'; import { Skeleton } from 'toolkit/chakra/skeleton';
import useTabIndexFromQuery from 'ui/shared/Tabs/useTabIndexFromQuery'; import type { TabsProps } from 'toolkit/chakra/tabs';
type TabSize = 'sm' | 'md'; import useActiveTabFromQuery from './useActiveTabFromQuery';
const SkeletonTabText = ({ size, title }: { size: TabSize; title: RoutedTab['title'] }) => ( const SkeletonTabText = ({ size, title }: { size: TabsProps['size']; title: TabItemRegular['title'] }) => (
<Skeleton <Skeleton
borderRadius="base" borderRadius="base"
borderWidth={ size === 'sm' ? '2px' : 0 } borderWidth={ size === 'sm' ? '2px' : 0 }
...@@ -22,18 +22,19 @@ const SkeletonTabText = ({ size, title }: { size: TabSize; title: RoutedTab['tit ...@@ -22,18 +22,19 @@ const SkeletonTabText = ({ size, title }: { size: TabSize; title: RoutedTab['tit
interface Props { interface Props {
className?: string; className?: string;
tabs: Array<RoutedTab>; tabs: Array<TabItemRegular>;
size?: 'sm' | 'md'; size?: 'sm' | 'md';
} }
const TabsSkeleton = ({ className, tabs, size = 'md' }: Props) => { const RoutedTabsSkeleton = ({ className, tabs, size = 'md' }: Props) => {
const bgColor = useColorModeValue('blackAlpha.50', 'whiteAlpha.50'); const activeTab = useActiveTabFromQuery(tabs);
const tabIndex = useTabIndexFromQuery(tabs || []);
if (tabs.length === 1) { if (tabs.length === 1) {
return null; return null;
} }
const tabIndex = activeTab ? tabs.findIndex((tab) => tab.id === activeTab.id) : 0;
return ( return (
<Flex className={ className } my={ 8 } alignItems="center" overflow="hidden"> <Flex className={ className } my={ 8 } alignItems="center" overflow="hidden">
{ tabs.slice(0, tabIndex).map(({ title, id }) => ( { tabs.slice(0, tabIndex).map(({ title, id }) => (
...@@ -44,7 +45,13 @@ const TabsSkeleton = ({ className, tabs, size = 'md' }: Props) => { ...@@ -44,7 +45,13 @@ const TabsSkeleton = ({ className, tabs, size = 'md' }: Props) => {
/> />
)) } )) }
{ tabs.slice(tabIndex, tabIndex + 1).map(({ title, id }) => ( { tabs.slice(tabIndex, tabIndex + 1).map(({ title, id }) => (
<Box key={ id.toString() } bgColor={ bgColor } py={ size === 'sm' ? 1 : 2 } borderRadius="base" flexShrink={ 0 }> <Box
key={ id.toString() }
bgColor={{ _light: 'blackAlpha.50', _dark: 'whiteAlpha.50' }}
py={ size === 'sm' ? 1 : 2 }
borderRadius="base"
flexShrink={ 0 }
>
<SkeletonTabText <SkeletonTabText
key={ id.toString() } key={ id.toString() }
title={ title } title={ title }
...@@ -63,4 +70,4 @@ const TabsSkeleton = ({ className, tabs, size = 'md' }: Props) => { ...@@ -63,4 +70,4 @@ const TabsSkeleton = ({ className, tabs, size = 'md' }: Props) => {
); );
}; };
export default chakra(TabsSkeleton); export default chakra(RoutedTabsSkeleton);
...@@ -27,6 +27,7 @@ export const recipe = defineSlotRecipe({ ...@@ -27,6 +27,7 @@ export const recipe = defineSlotRecipe({
height: '5', height: '5',
_icon: { boxSize: 'full' }, _icon: { boxSize: 'full' },
color: 'alert.fg', color: 'alert.fg',
'--layer-bg': 'transparent',
}, },
content: { content: {
display: 'flex', display: 'flex',
...@@ -35,24 +36,48 @@ export const recipe = defineSlotRecipe({ ...@@ -35,24 +36,48 @@ export const recipe = defineSlotRecipe({
}, },
variants: { variants: {
visual: { status: {
info: { info: {
root: { bg: 'alert.bg.info', color: 'alert.fg' }, root: {
bg: 'alert.bg.info',
'--layer-bg': '{colors.alert.bg.info}',
color: 'alert.fg',
},
}, },
warning: { warning: {
root: { bg: 'alert.bg.warning', color: 'alert.fg' }, root: {
bg: 'alert.bg.warning',
'--layer-bg': '{colors.alert.bg.warning}',
color: 'alert.fg',
},
}, },
warning_table: { warning_table: {
root: { bg: 'alert.bg.warning_table', color: 'alert.fg' }, root: {
bg: 'alert.bg.warning_table',
'--layer-bg': '{colors.alert.bg.warning_table}',
color: 'alert.fg',
},
}, },
success: { success: {
root: { bg: 'alert.bg.success', color: 'alert.fg' }, root: {
bg: 'alert.bg.success',
'--layer-bg': '{colors.alert.bg.success}',
color: 'alert.fg',
},
}, },
error: { error: {
root: { bg: 'alert.bg.error', color: 'alert.fg' }, root: {
bg: 'alert.bg.error',
'--layer-bg': '{colors.alert.bg.error}',
color: 'alert.fg',
},
}, },
neutral: { neutral: {
root: { bg: 'alert.bg.neutral', color: 'alert.fg' }, root: {
bg: 'alert.bg.neutral',
'--layer-bg': '{colors.alert.bg.neutral}',
color: 'alert.fg',
},
}, },
}, },
...@@ -92,7 +117,7 @@ export const recipe = defineSlotRecipe({ ...@@ -92,7 +117,7 @@ export const recipe = defineSlotRecipe({
}, },
defaultVariants: { defaultVariants: {
visual: 'neutral', status: 'neutral',
size: 'md', size: 'md',
inline: true, inline: true,
}, },
......
...@@ -17,46 +17,57 @@ export const recipe = defineRecipe({ ...@@ -17,46 +17,57 @@ export const recipe = defineRecipe({
colorPalette: { colorPalette: {
gray: { gray: {
bg: 'badge.gray.bg', bg: 'badge.gray.bg',
'--layer-bg': '{colors.badge.gray.bg}',
color: 'badge.gray.fg', color: 'badge.gray.fg',
}, },
green: { green: {
bg: 'badge.green.bg', bg: 'badge.green.bg',
'--layer-bg': '{colors.badge.green.bg}',
color: 'badge.green.fg', color: 'badge.green.fg',
}, },
red: { red: {
bg: 'badge.red.bg', bg: 'badge.red.bg',
'--layer-bg': '{colors.badge.red.bg}',
color: 'badge.red.fg', color: 'badge.red.fg',
}, },
purple: { purple: {
bg: 'badge.purple.bg', bg: 'badge.purple.bg',
'--layer-bg': '{colors.badge.purple.bg}',
color: 'badge.purple.fg', color: 'badge.purple.fg',
}, },
orange: { orange: {
bg: 'badge.orange.bg', bg: 'badge.orange.bg',
'--layer-bg': '{colors.badge.orange.bg}',
color: 'badge.orange.fg', color: 'badge.orange.fg',
}, },
blue: { blue: {
bg: 'badge.blue.bg', bg: 'badge.blue.bg',
'--layer-bg': '{colors.badge.blue.bg}',
color: 'badge.blue.fg', color: 'badge.blue.fg',
}, },
yellow: { yellow: {
bg: 'badge.yellow.bg', bg: 'badge.yellow.bg',
'--layer-bg': '{colors.badge.yellow.bg}',
color: 'badge.yellow.fg', color: 'badge.yellow.fg',
}, },
teal: { teal: {
bg: 'badge.teal.bg', bg: 'badge.teal.bg',
'--layer-bg': '{colors.badge.teal.bg}',
color: 'badge.teal.fg', color: 'badge.teal.fg',
}, },
cyan: { cyan: {
bg: 'badge.cyan.bg', bg: 'badge.cyan.bg',
'--layer-bg': '{colors.badge.cyan.bg}',
color: 'badge.cyan.fg', color: 'badge.cyan.fg',
}, },
purple_alt: { purple_alt: {
bg: 'badge.purple_alt.bg', bg: 'badge.purple_alt.bg',
'--layer-bg': '{colors.badge.purple_alt.bg}',
color: 'badge.purple_alt.fg', color: 'badge.purple_alt.fg',
}, },
blue_alt: { blue_alt: {
bg: 'badge.blue_alt.bg', bg: 'badge.blue_alt.bg',
'--layer-bg': '{colors.badge.blue_alt.bg}',
color: 'badge.blue_alt.fg', color: 'badge.blue_alt.fg',
}, },
}, },
......
import { chakra, Tooltip, Hide, Flex } from '@chakra-ui/react'; import { chakra, Flex } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { CsvExportParams } from 'types/client/address'; import type { CsvExportParams } from 'types/client/address';
...@@ -8,9 +8,10 @@ import { route } from 'nextjs-routes'; ...@@ -8,9 +8,10 @@ import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import useIsInitialLoading from 'lib/hooks/useIsInitialLoading'; import useIsInitialLoading from 'lib/hooks/useIsInitialLoading';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import Skeleton from 'ui/shared/chakra/Skeleton'; import { Link } from 'toolkit/chakra/link';
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 LinkInternal from 'ui/shared/links/LinkInternal';
interface Props { interface Props {
address: string; address: string;
...@@ -30,27 +31,23 @@ const AddressCsvExportLink = ({ className, address, params, isLoading }: Props) ...@@ -30,27 +31,23 @@ const AddressCsvExportLink = ({ className, address, params, isLoading }: Props)
if (isInitialLoading) { if (isInitialLoading) {
return ( return (
<Flex className={ className } flexShrink={ 0 } alignItems="center"> <Flex className={ className } flexShrink={ 0 } alignItems="center">
<Skeleton boxSize={{ base: '32px', lg: 6 }} borderRadius="base"/> <Skeleton boxSize={{ base: 8, lg: 6 }}/>
<Hide ssr={ false } below="lg"> <Skeleton loading hideBelow="lg" w="112px" h={ 6 } ml={ 1 }/>
<Skeleton w="112px" h={ 6 } ml={ 1 }/>
</Hide>
</Flex> </Flex>
); );
} }
return ( return (
<Tooltip isDisabled={ !isMobile } label="Download CSV"> <Tooltip disabled={ !isMobile } content="Download CSV">
<LinkInternal <Link
className={ className } className={ className }
display="inline-flex"
alignItems="center"
whiteSpace="nowrap" whiteSpace="nowrap"
href={ route({ pathname: '/csv-export', query: { ...params, address } }) } href={ route({ pathname: '/csv-export', query: { ...params, address } }) }
flexShrink={ 0 } flexShrink={ 0 }
> >
<IconSvg name="files/csv" boxSize={{ base: '30px', lg: 6 }}/> <IconSvg name="files/csv" boxSize={{ base: '30px', lg: 6 }}/>
<Hide ssr={ false } below="lg"><chakra.span ml={ 1 }>Download CSV</chakra.span></Hide> <chakra.span ml={ 1 } hideBelow="lg">Download CSV</chakra.span>
</LinkInternal> </Link>
</Tooltip> </Tooltip>
); );
}; };
......
import { Tag, Tooltip, useDisclosure } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import LinkInternal from 'ui/shared/links/LinkInternal'; import { Link } from 'toolkit/chakra/link';
import { Tag } from 'toolkit/chakra/tag';
import { Tooltip } from 'toolkit/chakra/tooltip';
import type { BlockQuery } from './useBlockQuery'; import type { BlockQuery } from './useBlockQuery';
...@@ -13,9 +14,6 @@ interface Props { ...@@ -13,9 +14,6 @@ interface Props {
} }
const BlockCeloEpochTag = ({ blockQuery }: Props) => { const BlockCeloEpochTag = ({ blockQuery }: Props) => {
// have to implement controlled tooltip because of the issue - https://github.com/chakra-ui/chakra-ui/issues/7107
const { isOpen, onOpen, onToggle, onClose } = useDisclosure();
if (!blockQuery.data?.celo) { if (!blockQuery.data?.celo) {
return null; return null;
} }
...@@ -26,28 +24,19 @@ const BlockCeloEpochTag = ({ blockQuery }: Props) => { ...@@ -26,28 +24,19 @@ const BlockCeloEpochTag = ({ blockQuery }: Props) => {
blockQuery.data.celo.epoch_number * celoConfig.BLOCKS_PER_EPOCH : blockQuery.data.celo.epoch_number * celoConfig.BLOCKS_PER_EPOCH :
undefined; undefined;
const tag = ( const tag = (
<Tag // TODO @tom2drum revise tag color scheme
colorScheme={ epochBlockNumber ? 'gray-blue' : 'gray' } <Tag colorScheme={ epochBlockNumber ? 'gray-blue' : 'gray' } >Epoch #{ blockQuery.data.celo.epoch_number }</Tag>
onClick={ epochBlockNumber ? undefined : onToggle }
onMouseEnter={ onOpen }
onMouseLeave={ onClose }
>
Epoch #{ blockQuery.data.celo.epoch_number }
</Tag>
); );
const content = epochBlockNumber ? ( const content = epochBlockNumber ? (
<LinkInternal href={ route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: String(epochBlockNumber) } }) }> <Link href={ route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: String(epochBlockNumber) } }) }>
{ tag } { tag }
</LinkInternal> </Link>
) : tag; ) : tag;
return ( return (
<Tooltip <Tooltip
label="Displays the epoch this block belongs to before the epoch is finalized" key="epoch-tag-before-finalized"
maxW="280px" content="Displays the epoch this block belongs to before the epoch is finalized"
textAlign="center"
isOpen={ isOpen }
onClose={ onClose }
> >
{ content } { content }
</Tooltip> </Tooltip>
...@@ -56,15 +45,10 @@ const BlockCeloEpochTag = ({ blockQuery }: Props) => { ...@@ -56,15 +45,10 @@ const BlockCeloEpochTag = ({ blockQuery }: Props) => {
return ( return (
<Tooltip <Tooltip
label="Displays the epoch finalized by this block" key="epoch-tag"
maxW="280px" content="Displays the epoch finalized by this block"
textAlign="center"
isOpen={ isOpen }
onClose={ onClose }
> >
<Tag bgColor="celo" color="blackAlpha.800" onClick={ onToggle } onMouseEnter={ onOpen } onMouseLeave={ onClose }> <Tag bgColor="celo" color="blackAlpha.800" > Finalized epoch #{ blockQuery.data.celo.epoch_number } </Tag>
Finalized epoch #{ blockQuery.data.celo.epoch_number }
</Tag>
</Tooltip> </Tooltip>
); );
}; };
......
import { Grid, GridItem, Text, Link, Box, Tooltip } from '@chakra-ui/react'; import { Grid, GridItem, 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 { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import { scroller, Element } from 'react-scroll';
import { ZKSYNC_L2_TX_BATCH_STATUSES } from 'types/api/zkSyncL2'; import { ZKSYNC_L2_TX_BATCH_STATUSES } from 'types/api/zkSyncL2';
...@@ -18,9 +17,12 @@ import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle'; ...@@ -18,9 +17,12 @@ import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
import * as arbitrum from 'lib/rollups/arbitrum'; import * as arbitrum from 'lib/rollups/arbitrum';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import { currencyUnits } from 'lib/units'; import { currencyUnits } from 'lib/units';
import { Link } from 'toolkit/chakra/link';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { Tooltip } from 'toolkit/chakra/tooltip';
import CutLink from 'toolkit/components/CutLink/CutLink';
import OptimisticL2TxnBatchDA from 'ui/shared/batch/OptimisticL2TxnBatchDA'; import OptimisticL2TxnBatchDA from 'ui/shared/batch/OptimisticL2TxnBatchDA';
import BlockGasUsed from 'ui/shared/block/BlockGasUsed'; import BlockGasUsed from 'ui/shared/block/BlockGasUsed';
import Skeleton from 'ui/shared/chakra/Skeleton';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import CopyToClipboard from 'ui/shared/CopyToClipboard';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem'; import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider'; import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
...@@ -31,7 +33,6 @@ import BlockEntityL1 from 'ui/shared/entities/block/BlockEntityL1'; ...@@ -31,7 +33,6 @@ import BlockEntityL1 from 'ui/shared/entities/block/BlockEntityL1';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/links/LinkInternal';
import PrevNext from 'ui/shared/PrevNext'; import PrevNext from 'ui/shared/PrevNext';
import RawDataSnippet from 'ui/shared/RawDataSnippet'; import RawDataSnippet from 'ui/shared/RawDataSnippet';
import StatusTag from 'ui/shared/statusTag/StatusTag'; import StatusTag from 'ui/shared/statusTag/StatusTag';
...@@ -51,20 +52,11 @@ interface Props { ...@@ -51,20 +52,11 @@ interface Props {
const rollupFeature = config.features.rollup; const rollupFeature = config.features.rollup;
const BlockDetails = ({ query }: Props) => { const BlockDetails = ({ query }: Props) => {
const [ isExpanded, setIsExpanded ] = React.useState(false);
const router = useRouter(); const router = useRouter();
const heightOrHash = getQueryParamString(router.query.height_or_hash); const heightOrHash = getQueryParamString(router.query.height_or_hash);
const { data, isPlaceholderData } = query; const { data, isPlaceholderData } = query;
const handleCutClick = React.useCallback(() => {
setIsExpanded((flag) => !flag);
scroller.scrollTo('BlockDetails__cutLink', {
duration: 500,
smooth: true,
});
}, []);
const handlePrevNextClick = React.useCallback((direction: 'prev' | 'next') => { const handlePrevNextClick = React.useCallback((direction: 'prev' | 'next') => {
if (!data) { if (!data) {
return; return;
...@@ -90,18 +82,18 @@ const BlockDetails = ({ query }: Props) => { ...@@ -90,18 +82,18 @@ const BlockDetails = ({ query }: Props) => {
} }
if (isPlaceholderData) { if (isPlaceholderData) {
return <Skeleton w="525px" h="20px"/>; return <Skeleton loading w="525px" h="20px"/>;
} }
return ( return (
<Text variant="secondary" whiteSpace="break-spaces"> <Text color="text.secondary" whiteSpace="break-spaces">
<Tooltip label="Static block reward"> <Tooltip content="Static block reward">
<span>{ staticReward.dividedBy(WEI).toFixed() }</span> <span>{ staticReward.dividedBy(WEI).toFixed() }</span>
</Tooltip> </Tooltip>
{ !txFees.isEqualTo(ZERO) && ( { !txFees.isEqualTo(ZERO) && (
<> <>
{ space }+{ space } { space }+{ space }
<Tooltip label="Txn fees"> <Tooltip content="Txn fees">
<span>{ txFees.dividedBy(WEI).toFixed() }</span> <span>{ txFees.dividedBy(WEI).toFixed() }</span>
</Tooltip> </Tooltip>
</> </>
...@@ -109,7 +101,7 @@ const BlockDetails = ({ query }: Props) => { ...@@ -109,7 +101,7 @@ const BlockDetails = ({ query }: Props) => {
{ !burntFees.isEqualTo(ZERO) && ( { !burntFees.isEqualTo(ZERO) && (
<> <>
{ space }-{ space } { space }-{ space }
<Tooltip label="Burnt fees"> <Tooltip content="Burnt fees">
<span>{ burntFees.dividedBy(WEI).toFixed() }</span> <span>{ burntFees.dividedBy(WEI).toFixed() }</span>
</Tooltip> </Tooltip>
</> </>
...@@ -122,17 +114,17 @@ const BlockDetails = ({ query }: Props) => { ...@@ -122,17 +114,17 @@ const BlockDetails = ({ query }: Props) => {
const txsNum = (() => { const txsNum = (() => {
const blockTxsNum = ( const blockTxsNum = (
<LinkInternal href={ route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: heightOrHash, tab: 'txs' } }) }> <Link href={ route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: heightOrHash, tab: 'txs' } }) }>
{ data.transaction_count } txn{ data.transaction_count === 1 ? '' : 's' } { data.transaction_count } txn{ data.transaction_count === 1 ? '' : 's' }
</LinkInternal> </Link>
); );
const blockBlobTxsNum = (config.features.dataAvailability.isEnabled && data.blob_transaction_count) ? ( const blockBlobTxsNum = (config.features.dataAvailability.isEnabled && data.blob_transaction_count) ? (
<> <>
<span> including </span> <span> including </span>
<LinkInternal href={ route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: heightOrHash, tab: 'blob_txs' } }) }> <Link href={ route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: heightOrHash, tab: 'blob_txs' } }) }>
{ data.blob_transaction_count } blob txn{ data.blob_transaction_count === 1 ? '' : 's' } { data.blob_transaction_count } blob txn{ data.blob_transaction_count === 1 ? '' : 's' }
</LinkInternal> </Link>
</> </>
) : null; ) : null;
...@@ -170,7 +162,7 @@ const BlockDetails = ({ query }: Props) => { ...@@ -170,7 +162,7 @@ const BlockDetails = ({ query }: Props) => {
{ blockTypeLabel } height { blockTypeLabel } height
</DetailsInfoItem.Label> </DetailsInfoItem.Label>
<DetailsInfoItem.Value> <DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton loading={ isPlaceholderData }>
{ data.height } { data.height }
</Skeleton> </Skeleton>
{ data.height === 0 && <Text whiteSpace="pre"> - Genesis Block</Text> } { data.height === 0 && <Text whiteSpace="pre"> - Genesis Block</Text> }
...@@ -209,7 +201,7 @@ const BlockDetails = ({ query }: Props) => { ...@@ -209,7 +201,7 @@ const BlockDetails = ({ query }: Props) => {
<DetailsInfoItem.Value> <DetailsInfoItem.Value>
{ data.arbitrum.batch_number ? { data.arbitrum.batch_number ?
<BatchEntityL2 isLoading={ isPlaceholderData } number={ data.arbitrum.batch_number }/> : <BatchEntityL2 isLoading={ isPlaceholderData } number={ data.arbitrum.batch_number }/> :
<Skeleton isLoaded={ !isPlaceholderData }>Pending</Skeleton> } <Skeleton loading={ isPlaceholderData }>Pending</Skeleton> }
</DetailsInfoItem.Value> </DetailsInfoItem.Value>
</> </>
) } ) }
...@@ -225,7 +217,7 @@ const BlockDetails = ({ query }: Props) => { ...@@ -225,7 +217,7 @@ const BlockDetails = ({ query }: Props) => {
<DetailsInfoItem.Value columnGap={ 3 }> <DetailsInfoItem.Value columnGap={ 3 }>
{ data.optimism.internal_id ? { data.optimism.internal_id ?
<BatchEntityL2 isLoading={ isPlaceholderData } number={ data.optimism.internal_id }/> : <BatchEntityL2 isLoading={ isPlaceholderData } number={ data.optimism.internal_id }/> :
<Skeleton isLoaded={ !isPlaceholderData }>Pending</Skeleton> } <Skeleton loading={ isPlaceholderData }>Pending</Skeleton> }
{ data.optimism.batch_data_container && ( { data.optimism.batch_data_container && (
<OptimisticL2TxnBatchDA <OptimisticL2TxnBatchDA
container={ data.optimism.batch_data_container } container={ data.optimism.batch_data_container }
...@@ -243,7 +235,7 @@ const BlockDetails = ({ query }: Props) => { ...@@ -243,7 +235,7 @@ const BlockDetails = ({ query }: Props) => {
Size Size
</DetailsInfoItem.Label> </DetailsInfoItem.Label>
<DetailsInfoItem.Value> <DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton loading={ isPlaceholderData }>
{ data.size.toLocaleString() } { data.size.toLocaleString() }
</Skeleton> </Skeleton>
</DetailsInfoItem.Value> </DetailsInfoItem.Value>
...@@ -265,7 +257,7 @@ const BlockDetails = ({ query }: Props) => { ...@@ -265,7 +257,7 @@ const BlockDetails = ({ query }: Props) => {
Transactions Transactions
</DetailsInfoItem.Label> </DetailsInfoItem.Label>
<DetailsInfoItem.Value> <DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton loading={ isPlaceholderData }>
{ txsNum } { txsNum }
</Skeleton> </Skeleton>
</DetailsInfoItem.Value> </DetailsInfoItem.Value>
...@@ -279,10 +271,10 @@ const BlockDetails = ({ query }: Props) => { ...@@ -279,10 +271,10 @@ const BlockDetails = ({ query }: Props) => {
Withdrawals Withdrawals
</DetailsInfoItem.Label> </DetailsInfoItem.Label>
<DetailsInfoItem.Value> <DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton loading={ isPlaceholderData }>
<LinkInternal href={ route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: heightOrHash, tab: 'withdrawals' } }) }> <Link href={ route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: heightOrHash, tab: 'withdrawals' } }) }>
{ data.withdrawals_count } withdrawal{ data.withdrawals_count === 1 ? '' : 's' } { data.withdrawals_count } withdrawal{ data.withdrawals_count === 1 ? '' : 's' }
</LinkInternal> </Link>
</Skeleton> </Skeleton>
</DetailsInfoItem.Value> </DetailsInfoItem.Value>
</> </>
...@@ -299,7 +291,7 @@ const BlockDetails = ({ query }: Props) => { ...@@ -299,7 +291,7 @@ const BlockDetails = ({ query }: Props) => {
<DetailsInfoItem.Value> <DetailsInfoItem.Value>
{ data.zksync.batch_number ? { data.zksync.batch_number ?
<BatchEntityL2 isLoading={ isPlaceholderData } number={ data.zksync.batch_number }/> : <BatchEntityL2 isLoading={ isPlaceholderData } number={ data.zksync.batch_number }/> :
<Skeleton isLoaded={ !isPlaceholderData }>Pending</Skeleton> } <Skeleton loading={ isPlaceholderData }>Pending</Skeleton> }
</DetailsInfoItem.Value> </DetailsInfoItem.Value>
</> </>
) } ) }
...@@ -393,7 +385,7 @@ const BlockDetails = ({ query }: Props) => { ...@@ -393,7 +385,7 @@ const BlockDetails = ({ query }: Props) => {
Block reward Block reward
</DetailsInfoItem.Label> </DetailsInfoItem.Label>
<DetailsInfoItem.Value columnGap={ 1 }> <DetailsInfoItem.Value columnGap={ 1 }>
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton loading={ isPlaceholderData }>
{ totalReward.dividedBy(WEI).toFixed() } { currencyUnits.ether } { totalReward.dividedBy(WEI).toFixed() } { currencyUnits.ether }
</Skeleton> </Skeleton>
{ rewardBreakDown } { rewardBreakDown }
...@@ -426,7 +418,7 @@ const BlockDetails = ({ query }: Props) => { ...@@ -426,7 +418,7 @@ const BlockDetails = ({ query }: Props) => {
View View
</DetailsInfoItem.Label> </DetailsInfoItem.Label>
<DetailsInfoItem.Value> <DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton loading={ isPlaceholderData }>
{ data.zilliqa.view } { data.zilliqa.view }
</Skeleton> </Skeleton>
</DetailsInfoItem.Value> </DetailsInfoItem.Value>
...@@ -444,15 +436,15 @@ const BlockDetails = ({ query }: Props) => { ...@@ -444,15 +436,15 @@ const BlockDetails = ({ query }: Props) => {
Gas used Gas used
</DetailsInfoItem.Label> </DetailsInfoItem.Label>
<DetailsInfoItem.Value> <DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton loading={ isPlaceholderData }>
{ BigNumber(data.gas_used || 0).toFormat() } { BigNumber(data.gas_used || 0).toFormat() }
</Skeleton> </Skeleton>
<BlockGasUsed <BlockGasUsed
gasUsed={ data.gas_used } gasUsed={ data.gas_used || undefined }
gasLimit={ data.gas_limit } gasLimit={ data.gas_limit }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
ml={ 4 } ml={ 4 }
gasTarget={ data.gas_target_percentage } gasTarget={ data.gas_target_percentage || undefined }
/> />
</DetailsInfoItem.Value> </DetailsInfoItem.Value>
...@@ -463,7 +455,7 @@ const BlockDetails = ({ query }: Props) => { ...@@ -463,7 +455,7 @@ const BlockDetails = ({ query }: Props) => {
Gas limit Gas limit
</DetailsInfoItem.Label> </DetailsInfoItem.Label>
<DetailsInfoItem.Value> <DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton loading={ isPlaceholderData }>
{ BigNumber(data.gas_limit).toFormat() } { BigNumber(data.gas_limit).toFormat() }
</Skeleton> </Skeleton>
</DetailsInfoItem.Value> </DetailsInfoItem.Value>
...@@ -477,7 +469,7 @@ const BlockDetails = ({ query }: Props) => { ...@@ -477,7 +469,7 @@ const BlockDetails = ({ query }: Props) => {
Minimum gas price Minimum gas price
</DetailsInfoItem.Label> </DetailsInfoItem.Label>
<DetailsInfoItem.Value> <DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton loading={ isPlaceholderData }>
{ BigNumber(data.minimum_gas_price).dividedBy(GWEI).toFormat() } { currencyUnits.gwei } { BigNumber(data.minimum_gas_price).dividedBy(GWEI).toFormat() } { currencyUnits.gwei }
</Skeleton> </Skeleton>
</DetailsInfoItem.Value> </DetailsInfoItem.Value>
...@@ -494,11 +486,11 @@ const BlockDetails = ({ query }: Props) => { ...@@ -494,11 +486,11 @@ const BlockDetails = ({ query }: Props) => {
</DetailsInfoItem.Label> </DetailsInfoItem.Label>
<DetailsInfoItem.Value> <DetailsInfoItem.Value>
{ isPlaceholderData ? ( { isPlaceholderData ? (
<Skeleton isLoaded={ !isPlaceholderData } h="20px" maxW="380px" w="100%"/> <Skeleton loading={ isPlaceholderData } h="20px" maxW="380px" w="100%"/>
) : ( ) : (
<> <>
<Text>{ BigNumber(data.base_fee_per_gas).dividedBy(WEI).toFixed() } { currencyUnits.ether } </Text> <Text>{ BigNumber(data.base_fee_per_gas).dividedBy(WEI).toFixed() } { currencyUnits.ether } </Text>
<Text variant="secondary" whiteSpace="pre"> <Text color="text.secondary" whiteSpace="pre">
{ space }({ BigNumber(data.base_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei }) { space }({ BigNumber(data.base_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })
</Text> </Text>
</> </>
...@@ -520,11 +512,11 @@ const BlockDetails = ({ query }: Props) => { ...@@ -520,11 +512,11 @@ const BlockDetails = ({ query }: Props) => {
</DetailsInfoItem.Label> </DetailsInfoItem.Label>
<DetailsInfoItem.Value> <DetailsInfoItem.Value>
<IconSvg name="flame" boxSize={ 5 } color="gray.500" isLoading={ isPlaceholderData }/> <IconSvg name="flame" boxSize={ 5 } color="gray.500" isLoading={ isPlaceholderData }/>
<Skeleton isLoaded={ !isPlaceholderData } ml={ 2 }> <Skeleton loading={ isPlaceholderData } ml={ 2 }>
{ burntFees.dividedBy(WEI).toFixed() } { currencyUnits.ether } { burntFees.dividedBy(WEI).toFixed() } { currencyUnits.ether }
</Skeleton> </Skeleton>
{ !txFees.isEqualTo(ZERO) && ( { !txFees.isEqualTo(ZERO) && (
<Tooltip label="Burnt fees / Txn fees * 100%"> <Tooltip content="Burnt fees / Txn fees * 100%">
<Box> <Box>
<Utilization <Utilization
ml={ 4 } ml={ 4 }
...@@ -547,232 +539,215 @@ const BlockDetails = ({ query }: Props) => { ...@@ -547,232 +539,215 @@ const BlockDetails = ({ query }: Props) => {
Priority fee / Tip Priority fee / Tip
</DetailsInfoItem.Label> </DetailsInfoItem.Label>
<DetailsInfoItem.Value> <DetailsInfoItem.Value>
<Skeleton isLoaded={ !isPlaceholderData }> <Skeleton loading={ isPlaceholderData }>
{ BigNumber(data.priority_fee).dividedBy(WEI).toFixed() } { currencyUnits.ether } { BigNumber(data.priority_fee).dividedBy(WEI).toFixed() } { currencyUnits.ether }
</Skeleton> </Skeleton>
</DetailsInfoItem.Value> </DetailsInfoItem.Value>
</> </>
) } ) }
{ /* CUT */ }
<GridItem colSpan={{ base: undefined, lg: 2 }}>
<Element name="BlockDetails__cutLink">
<Skeleton isLoaded={ !isPlaceholderData } mt={ 6 } display="inline-block">
<Link
fontSize="sm"
textDecorationLine="underline"
textDecorationStyle="dashed"
onClick={ handleCutClick }
>
{ isExpanded ? 'Hide details' : 'View details' }
</Link>
</Skeleton>
</Element>
</GridItem>
{ /* ADDITIONAL INFO */ } { /* ADDITIONAL INFO */ }
{ isExpanded && !isPlaceholderData && ( <CutLink loading={ isPlaceholderData } mt={ 6 } gridColumn={{ base: undefined, lg: '1 / 3' }}>
<> <GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 1, lg: 4 }}/>
<GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 1, lg: 4 }}/>
{ rollupFeature.isEnabled && rollupFeature.type === 'zkSync' && data.zksync &&
<ZkSyncL2TxnBatchHashesInfo data={ data.zksync } isLoading={ isPlaceholderData }/> }
{ !isPlaceholderData && <BlockDetailsBlobInfo data={ data }/> }
{ data.bitcoin_merged_mining_header && ( { rollupFeature.isEnabled && rollupFeature.type === 'zkSync' && data.zksync &&
<> <ZkSyncL2TxnBatchHashesInfo data={ data.zksync } isLoading={ isPlaceholderData }/> }
<DetailsInfoItem.Label
hint="Merged-mining field: Bitcoin header"
>
Bitcoin merged mining header
</DetailsInfoItem.Label>
<DetailsInfoItem.Value
flexWrap="nowrap"
alignSelf="flex-start"
>
<Box whiteSpace="nowrap" overflow="hidden">
<HashStringShortenDynamic hash={ data.bitcoin_merged_mining_header }/>
</Box>
<CopyToClipboard text={ data.bitcoin_merged_mining_header }/>
</DetailsInfoItem.Value>
</>
) }
{ data.bitcoin_merged_mining_coinbase_transaction && ( { !isPlaceholderData && <BlockDetailsBlobInfo data={ data }/> }
<>
<DetailsInfoItem.Label
hint="Merged-mining field: Coinbase transaction"
>
Bitcoin merged mining coinbase transaction
</DetailsInfoItem.Label>
<DetailsInfoItem.Value>
<RawDataSnippet
data={ data.bitcoin_merged_mining_coinbase_transaction }
isLoading={ isPlaceholderData }
showCopy={ false }
textareaMaxHeight="100px"
/>
</DetailsInfoItem.Value>
</>
) }
{ data.bitcoin_merged_mining_merkle_proof && ( { data.bitcoin_merged_mining_header && (
<> <>
<DetailsInfoItem.Label <DetailsInfoItem.Label
hint="Merged-mining field: Merkle proof" hint="Merged-mining field: Bitcoin header"
> >
Bitcoin merged mining Merkle proof Bitcoin merged mining header
</DetailsInfoItem.Label> </DetailsInfoItem.Label>
<DetailsInfoItem.Value> <DetailsInfoItem.Value
<RawDataSnippet flexWrap="nowrap"
data={ data.bitcoin_merged_mining_merkle_proof } alignSelf="flex-start"
isLoading={ isPlaceholderData } >
showCopy={ false } <Box whiteSpace="nowrap" overflow="hidden">
textareaMaxHeight="100px" <HashStringShortenDynamic hash={ data.bitcoin_merged_mining_header }/>
/> </Box>
</DetailsInfoItem.Value> <CopyToClipboard text={ data.bitcoin_merged_mining_header }/>
</> </DetailsInfoItem.Value>
) } </>
) }
{ data.hash_for_merged_mining && ( { data.bitcoin_merged_mining_coinbase_transaction && (
<> <>
<DetailsInfoItem.Label <DetailsInfoItem.Label
hint="Merged-mining field: Rootstock block header hash" hint="Merged-mining field: Coinbase transaction"
> >
Hash for merged mining Bitcoin merged mining coinbase transaction
</DetailsInfoItem.Label> </DetailsInfoItem.Label>
<DetailsInfoItem.Value <DetailsInfoItem.Value>
flexWrap="nowrap" <RawDataSnippet
alignSelf="flex-start" data={ data.bitcoin_merged_mining_coinbase_transaction }
> isLoading={ isPlaceholderData }
<Box whiteSpace="nowrap" overflow="hidden"> showCopy={ false }
<HashStringShortenDynamic hash={ data.hash_for_merged_mining }/> textareaMaxHeight="100px"
</Box> />
<CopyToClipboard text={ data.hash_for_merged_mining }/> </DetailsInfoItem.Value>
</DetailsInfoItem.Value> </>
</> ) }
) }
<DetailsInfoItem.Label { data.bitcoin_merged_mining_merkle_proof && (
hint={ `Block difficulty for ${ validatorTitle }, used to calibrate block generation time` } <>
> <DetailsInfoItem.Label
Difficulty hint="Merged-mining field: Merkle proof"
</DetailsInfoItem.Label> >
<DetailsInfoItem.Value overflow="hidden"> Bitcoin merged mining Merkle proof
<HashStringShortenDynamic hash={ BigNumber(data.difficulty).toFormat() }/> </DetailsInfoItem.Label>
</DetailsInfoItem.Value> <DetailsInfoItem.Value>
<RawDataSnippet
data={ data.bitcoin_merged_mining_merkle_proof }
isLoading={ isPlaceholderData }
showCopy={ false }
textareaMaxHeight="100px"
/>
</DetailsInfoItem.Value>
</>
) }
{ data.total_difficulty && ( { data.hash_for_merged_mining && (
<> <>
<DetailsInfoItem.Label <DetailsInfoItem.Label
hint="Total difficulty of the chain until this block" hint="Merged-mining field: Rootstock block header hash"
> >
Total difficulty Hash for merged mining
</DetailsInfoItem.Label> </DetailsInfoItem.Label>
<DetailsInfoItem.Value overflow="hidden"> <DetailsInfoItem.Value
<HashStringShortenDynamic hash={ BigNumber(data.total_difficulty).toFormat() }/> flexWrap="nowrap"
</DetailsInfoItem.Value> alignSelf="flex-start"
</> >
) } <Box whiteSpace="nowrap" overflow="hidden">
<HashStringShortenDynamic hash={ data.hash_for_merged_mining }/>
</Box>
<CopyToClipboard text={ data.hash_for_merged_mining }/>
</DetailsInfoItem.Value>
</>
) }
<DetailsInfoItemDivider/> <DetailsInfoItem.Label
hint={ `Block difficulty for ${ validatorTitle }, used to calibrate block generation time` }
>
Difficulty
</DetailsInfoItem.Label>
<DetailsInfoItem.Value overflow="hidden">
<HashStringShortenDynamic hash={ BigNumber(data.difficulty).toFormat() }/>
</DetailsInfoItem.Value>
<DetailsInfoItem.Label { data.total_difficulty && (
hint="The SHA256 hash of the block" <>
> <DetailsInfoItem.Label
Hash hint="Total difficulty of the chain until this block"
</DetailsInfoItem.Label> >
<DetailsInfoItem.Value flexWrap="nowrap"> Total difficulty
<Box overflow="hidden" > </DetailsInfoItem.Label>
<HashStringShortenDynamic hash={ data.hash }/> <DetailsInfoItem.Value overflow="hidden">
</Box> <HashStringShortenDynamic hash={ BigNumber(data.total_difficulty).toFormat() }/>
<CopyToClipboard text={ data.hash }/> </DetailsInfoItem.Value>
</DetailsInfoItem.Value> </>
) }
{ data.height > 0 && ( <DetailsInfoItemDivider/>
<>
<DetailsInfoItem.Label <DetailsInfoItem.Label
hint="The hash of the block from which this block was generated" hint="The SHA256 hash of the block"
>
Hash
</DetailsInfoItem.Label>
<DetailsInfoItem.Value flexWrap="nowrap">
<Box overflow="hidden" >
<HashStringShortenDynamic hash={ data.hash }/>
</Box>
<CopyToClipboard text={ data.hash }/>
</DetailsInfoItem.Value>
{ data.height > 0 && (
<>
<DetailsInfoItem.Label
hint="The hash of the block from which this block was generated"
>
Parent hash
</DetailsInfoItem.Label>
<DetailsInfoItem.Value flexWrap="nowrap">
<Link
href={ route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: String(data.height - 1) } }) }
overflow="hidden"
whiteSpace="nowrap"
> >
Parent hash <HashStringShortenDynamic
</DetailsInfoItem.Label> hash={ data.parent_hash }
<DetailsInfoItem.Value flexWrap="nowrap"> />
<LinkInternal </Link>
href={ route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: String(data.height - 1) } }) } <CopyToClipboard text={ data.parent_hash }/>
overflow="hidden" </DetailsInfoItem.Value>
whiteSpace="nowrap" </>
> ) }
<HashStringShortenDynamic
hash={ data.parent_hash }
/>
</LinkInternal>
<CopyToClipboard text={ data.parent_hash }/>
</DetailsInfoItem.Value>
</>
) }
{ rollupFeature.isEnabled && rollupFeature.type === 'arbitrum' && data.arbitrum && ( { rollupFeature.isEnabled && rollupFeature.type === 'arbitrum' && data.arbitrum && (
<> <>
<DetailsInfoItem.Label <DetailsInfoItem.Label
hint="The cumulative number of L2 to L1 transactions as of this block" hint="The cumulative number of L2 to L1 transactions as of this block"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
Send count Send count
</DetailsInfoItem.Label> </DetailsInfoItem.Label>
<DetailsInfoItem.Value> <DetailsInfoItem.Value>
{ data.arbitrum.send_count.toLocaleString() } { data.arbitrum.send_count.toLocaleString() }
</DetailsInfoItem.Value> </DetailsInfoItem.Value>
<DetailsInfoItem.Label <DetailsInfoItem.Label
hint="The root of the Merkle accumulator representing all L2 to L1 transactions as of this block" hint="The root of the Merkle accumulator representing all L2 to L1 transactions as of this block"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
Send root Send root
</DetailsInfoItem.Label> </DetailsInfoItem.Label>
<DetailsInfoItem.Value> <DetailsInfoItem.Value>
{ data.arbitrum.send_root } { data.arbitrum.send_root }
</DetailsInfoItem.Value> </DetailsInfoItem.Value>
<DetailsInfoItem.Label <DetailsInfoItem.Label
hint="The number of delayed L1 to L2 messages read as of this block" hint="The number of delayed L1 to L2 messages read as of this block"
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
> >
Delayed messages Delayed messages
</DetailsInfoItem.Label> </DetailsInfoItem.Label>
<DetailsInfoItem.Value> <DetailsInfoItem.Value>
{ data.arbitrum.delayed_messages.toLocaleString() } { data.arbitrum.delayed_messages.toLocaleString() }
</DetailsInfoItem.Value> </DetailsInfoItem.Value>
</> </>
) } ) }
{ !config.UI.views.block.hiddenFields?.nonce && ( { !config.UI.views.block.hiddenFields?.nonce && (
<> <>
<DetailsInfoItem.Label <DetailsInfoItem.Label
hint="Block nonce is a value used during mining to demonstrate proof of work for a block" hint="Block nonce is a value used during mining to demonstrate proof of work for a block"
> >
Nonce Nonce
</DetailsInfoItem.Label> </DetailsInfoItem.Label>
<DetailsInfoItem.Value> <DetailsInfoItem.Value>
{ data.nonce } { data.nonce }
</DetailsInfoItem.Value> </DetailsInfoItem.Value>
</> </>
) } ) }
{ data.zilliqa && (
<>
<DetailsInfoItemDivider/>
<BlockDetailsZilliqaQuorumCertificate data={ data.zilliqa?.quorum_certificate }/>
{ data.zilliqa?.aggregate_quorum_certificate && (
<>
<GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 1, lg: 2 }}/>
<BlockDetailsZilliqaQuorumCertificate data={ data.zilliqa?.aggregate_quorum_certificate }/>
</>
) }
</>
) }
</CutLink>
{ data.zilliqa && (
<>
<DetailsInfoItemDivider/>
<BlockDetailsZilliqaQuorumCertificate data={ data.zilliqa?.quorum_certificate }/>
{ data.zilliqa?.aggregate_quorum_certificate && (
<>
<GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 1, lg: 2 }}/>
<BlockDetailsZilliqaQuorumCertificate data={ data.zilliqa?.aggregate_quorum_certificate }/>
</>
) }
</>
) }
</>
) }
</Grid> </Grid>
); );
}; };
......
import { Box, Flex, Link } from '@chakra-ui/react'; import { Box, Flex } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import React from 'react'; import React from 'react';
...@@ -7,6 +7,7 @@ import type { BlockBaseFeeCelo } from 'types/api/block'; ...@@ -7,6 +7,7 @@ import type { BlockBaseFeeCelo } from 'types/api/block';
import type { TokenInfo } from 'types/api/token'; import type { TokenInfo } from 'types/api/token';
import { WEI, ZERO_ADDRESS } from 'lib/consts'; import { WEI, ZERO_ADDRESS } from 'lib/consts';
import { Link } from 'toolkit/chakra/link';
import AddressFromTo from 'ui/shared/address/AddressFromTo'; import AddressFromTo from 'ui/shared/address/AddressFromTo';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem'; import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider'; import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
...@@ -50,7 +51,7 @@ const BlockDetailsBaseFeeCelo = ({ data }: Props) => { ...@@ -50,7 +51,7 @@ const BlockDetailsBaseFeeCelo = ({ data }: Props) => {
const totalFeeLabel = ( const totalFeeLabel = (
<Box whiteSpace="pre-wrap"> <Box whiteSpace="pre-wrap">
<span>The FeeHandler regularly burns 80% of its tokens. Non-CELO tokens are swapped to CELO beforehand. The remaining 20% are sent to the </span> <span>The FeeHandler regularly burns 80% of its tokens. Non-CELO tokens are swapped to CELO beforehand. The remaining 20% are sent to the </span>
<Link isExternal href="https://www.ultragreen.money">Green Fund</Link> <Link external href="https://www.ultragreen.money">Green Fund</Link>
<span>.</span> <span>.</span>
</Box> </Box>
); );
...@@ -65,10 +66,7 @@ const BlockDetailsBaseFeeCelo = ({ data }: Props) => { ...@@ -65,10 +66,7 @@ const BlockDetailsBaseFeeCelo = ({ data }: Props) => {
<DetailsInfoItem.Value> <DetailsInfoItem.Value>
<AddressEntity address={ data.recipient }/> <AddressEntity address={ data.recipient }/>
</DetailsInfoItem.Value> </DetailsInfoItem.Value>
<DetailsInfoItem.Label <DetailsInfoItem.Label hint={ totalFeeLabel }>
hint={ totalFeeLabel }
type="popover"
>
Base fee total Base fee total
</DetailsInfoItem.Label> </DetailsInfoItem.Label>
<DetailsInfoItem.Value display="block"> <DetailsInfoItem.Value display="block">
......
import { Text, Tooltip } from '@chakra-ui/react'; import { Text } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import React from 'react'; import React from 'react';
...@@ -7,6 +7,7 @@ import type { Block } from 'types/api/block'; ...@@ -7,6 +7,7 @@ import type { Block } from 'types/api/block';
import { WEI, WEI_IN_GWEI, ZERO } from 'lib/consts'; import { WEI, WEI_IN_GWEI, ZERO } from 'lib/consts';
import { space } from 'lib/html-entities'; import { space } from 'lib/html-entities';
import { currencyUnits } from 'lib/units'; import { currencyUnits } from 'lib/units';
import { Tooltip } from 'toolkit/chakra/tooltip';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem'; import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider'; import DetailsInfoItemDivider from 'ui/shared/DetailsInfoItemDivider';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
...@@ -42,7 +43,7 @@ const BlockDetailsBlobInfo = ({ data }: Props) => { ...@@ -42,7 +43,7 @@ const BlockDetailsBlobInfo = ({ data }: Props) => {
</DetailsInfoItem.Label> </DetailsInfoItem.Label>
<DetailsInfoItem.Value> <DetailsInfoItem.Value>
<Text>{ BigNumber(data.blob_gas_price).dividedBy(WEI).toFixed() } { currencyUnits.ether } </Text> <Text>{ BigNumber(data.blob_gas_price).dividedBy(WEI).toFixed() } { currencyUnits.ether } </Text>
<Text variant="secondary" whiteSpace="pre"> <Text color="text.secondary" whiteSpace="pre">
{ space }({ BigNumber(data.blob_gas_price).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei }) { space }({ BigNumber(data.blob_gas_price).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })
</Text> </Text>
</DetailsInfoItem.Value> </DetailsInfoItem.Value>
...@@ -71,7 +72,7 @@ const BlockDetailsBlobInfo = ({ data }: Props) => { ...@@ -71,7 +72,7 @@ const BlockDetailsBlobInfo = ({ data }: Props) => {
<IconSvg name="flame" boxSize={ 5 } color="gray.500" mr={ 2 }/> <IconSvg name="flame" boxSize={ 5 } color="gray.500" mr={ 2 }/>
{ burntBlobFees.dividedBy(WEI).toFixed() } { currencyUnits.ether } { burntBlobFees.dividedBy(WEI).toFixed() } { currencyUnits.ether }
{ !blobFees.isEqualTo(ZERO) && ( { !blobFees.isEqualTo(ZERO) && (
<Tooltip label="Blob burnt fees / Txn fees * 100%"> <Tooltip content="Blob burnt fees / Txn fees * 100%">
<div> <div>
<Utilization ml={ 4 } value={ burntBlobFees.dividedBy(blobFees).toNumber() }/> <Utilization ml={ 4 } value={ burntBlobFees.dividedBy(blobFees).toNumber() }/>
</div> </div>
...@@ -89,7 +90,7 @@ const BlockDetailsBlobInfo = ({ data }: Props) => { ...@@ -89,7 +90,7 @@ const BlockDetailsBlobInfo = ({ data }: Props) => {
</DetailsInfoItem.Label> </DetailsInfoItem.Label>
<DetailsInfoItem.Value> <DetailsInfoItem.Value>
<Text>{ BigNumber(data.excess_blob_gas).dividedBy(WEI).toFixed() } { currencyUnits.ether } </Text> <Text>{ BigNumber(data.excess_blob_gas).dividedBy(WEI).toFixed() } { currencyUnits.ether } </Text>
<Text variant="secondary" whiteSpace="pre"> <Text color="text.secondary" whiteSpace="pre">
{ space }({ BigNumber(data.excess_blob_gas).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei }) { space }({ BigNumber(data.excess_blob_gas).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })
</Text> </Text>
</DetailsInfoItem.Value> </DetailsInfoItem.Value>
......
...@@ -13,6 +13,9 @@ import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError'; ...@@ -13,6 +13,9 @@ import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import getNetworkValidationActionText from 'lib/networks/getNetworkValidationActionText'; import getNetworkValidationActionText from 'lib/networks/getNetworkValidationActionText';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import { Skeleton } from 'toolkit/chakra/skeleton';
import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs';
import RoutedTabsSkeleton from 'toolkit/components/RoutedTabs/RoutedTabsSkeleton';
import BlockCeloEpochTag from 'ui/block/BlockCeloEpochTag'; import BlockCeloEpochTag from 'ui/block/BlockCeloEpochTag';
import BlockDetails from 'ui/block/BlockDetails'; import BlockDetails from 'ui/block/BlockDetails';
import BlockEpochRewards from 'ui/block/BlockEpochRewards'; import BlockEpochRewards from 'ui/block/BlockEpochRewards';
...@@ -23,13 +26,10 @@ import useBlockTxsQuery from 'ui/block/useBlockTxsQuery'; ...@@ -23,13 +26,10 @@ import useBlockTxsQuery from 'ui/block/useBlockTxsQuery';
import useBlockWithdrawalsQuery from 'ui/block/useBlockWithdrawalsQuery'; import useBlockWithdrawalsQuery from 'ui/block/useBlockWithdrawalsQuery';
import TextAd from 'ui/shared/ad/TextAd'; import TextAd from 'ui/shared/ad/TextAd';
import ServiceDegradationWarning from 'ui/shared/alerts/ServiceDegradationWarning'; import ServiceDegradationWarning from 'ui/shared/alerts/ServiceDegradationWarning';
import Skeleton from 'ui/shared/chakra/Skeleton';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import NetworkExplorers from 'ui/shared/NetworkExplorers'; import NetworkExplorers from 'ui/shared/NetworkExplorers';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/pagination/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import TabsSkeleton from 'ui/shared/Tabs/TabsSkeleton';
import TxsWithFrontendSorting from 'ui/txs/TxsWithFrontendSorting'; import TxsWithFrontendSorting from 'ui/txs/TxsWithFrontendSorting';
const TAB_LIST_PROPS = { const TAB_LIST_PROPS = {
...@@ -68,40 +68,40 @@ const BlockPageContent = () => { ...@@ -68,40 +68,40 @@ const BlockPageContent = () => {
</> </>
), ),
}, },
{ // {
id: 'txs', // id: 'txs',
title: 'Transactions', // title: 'Transactions',
component: ( // component: (
<> // <>
{ blockTxsQuery.isDegradedData && <ServiceDegradationWarning isLoading={ blockTxsQuery.isPlaceholderData } mb={ 6 }/> } // { blockTxsQuery.isDegradedData && <ServiceDegradationWarning isLoading={ blockTxsQuery.isPlaceholderData } mb={ 6 }/> }
<TxsWithFrontendSorting query={ blockTxsQuery } showBlockInfo={ false } showSocketInfo={ false } top={ hasPagination ? TABS_HEIGHT : 0 }/> // <TxsWithFrontendSorting query={ blockTxsQuery } showBlockInfo={ false } showSocketInfo={ false } top={ hasPagination ? TABS_HEIGHT : 0 }/>
</> // </>
), // ),
}, // },
config.features.dataAvailability.isEnabled && blockQuery.data?.blob_transaction_count ? // config.features.dataAvailability.isEnabled && blockQuery.data?.blob_transaction_count ?
{ // {
id: 'blob_txs', // id: 'blob_txs',
title: 'Blob txns', // title: 'Blob txns',
component: ( // component: (
<TxsWithFrontendSorting query={ blockBlobTxsQuery } showBlockInfo={ false } showSocketInfo={ false }/> // <TxsWithFrontendSorting query={ blockBlobTxsQuery } showBlockInfo={ false } showSocketInfo={ false }/>
), // ),
} : null, // } : null,
config.features.beaconChain.isEnabled && Boolean(blockQuery.data?.withdrawals_count) ? // config.features.beaconChain.isEnabled && Boolean(blockQuery.data?.withdrawals_count) ?
{ // {
id: 'withdrawals', // id: 'withdrawals',
title: 'Withdrawals', // title: 'Withdrawals',
component: ( // component: (
<> // <>
{ blockWithdrawalsQuery.isDegradedData && <ServiceDegradationWarning isLoading={ blockWithdrawalsQuery.isPlaceholderData } mb={ 6 }/> } // { blockWithdrawalsQuery.isDegradedData && <ServiceDegradationWarning isLoading={ blockWithdrawalsQuery.isPlaceholderData } mb={ 6 }/> }
<BlockWithdrawals blockWithdrawalsQuery={ blockWithdrawalsQuery }/> // <BlockWithdrawals blockWithdrawalsQuery={ blockWithdrawalsQuery }/>
</> // </>
), // ),
} : null, // } : null,
blockQuery.data?.celo?.is_epoch_block ? { // blockQuery.data?.celo?.is_epoch_block ? {
id: 'epoch_rewards', // id: 'epoch_rewards',
title: 'Epoch rewards', // title: 'Epoch rewards',
component: <BlockEpochRewards heightOrHash={ heightOrHash }/>, // component: <BlockEpochRewards heightOrHash={ heightOrHash }/>,
} : null, // } : null,
].filter(Boolean)), [ blockBlobTxsQuery, blockQuery, blockTxsQuery, blockWithdrawalsQuery, hasPagination, heightOrHash ]); ].filter(Boolean)), [ blockBlobTxsQuery, blockQuery, blockTxsQuery, blockWithdrawalsQuery, hasPagination, heightOrHash ]);
let pagination; let pagination;
...@@ -152,7 +152,7 @@ const BlockPageContent = () => { ...@@ -152,7 +152,7 @@ const BlockPageContent = () => {
<> <>
{ !config.UI.views.block.hiddenFields?.miner && blockQuery.data?.miner && ( { !config.UI.views.block.hiddenFields?.miner && blockQuery.data?.miner && (
<Skeleton <Skeleton
isLoaded={ !blockQuery.isPlaceholderData } loading={ blockQuery.isPlaceholderData }
fontFamily="heading" fontFamily="heading"
display="flex" display="flex"
minW={ 0 } minW={ 0 }
...@@ -165,7 +165,11 @@ const BlockPageContent = () => { ...@@ -165,7 +165,11 @@ const BlockPageContent = () => {
<AddressEntity address={ blockQuery.data.miner }/> <AddressEntity address={ blockQuery.data.miner }/>
</Skeleton> </Skeleton>
) } ) }
<NetworkExplorers type="block" pathParam={ heightOrHash } ml={{ base: config.UI.views.block.hiddenFields?.miner ? 0 : 3, lg: 'auto' }}/> <NetworkExplorers
type="block"
pathParam={ heightOrHash }
ml={{ base: config.UI.views.block.hiddenFields?.miner ? 0 : 3, lg: 'auto' }}
/>
</> </>
); );
...@@ -179,10 +183,10 @@ const BlockPageContent = () => { ...@@ -179,10 +183,10 @@ const BlockPageContent = () => {
secondRow={ titleSecondRow } secondRow={ titleSecondRow }
isLoading={ blockQuery.isPlaceholderData } isLoading={ blockQuery.isPlaceholderData }
/> />
{ blockQuery.isPlaceholderData ? <TabsSkeleton tabs={ tabs }/> : ( { blockQuery.isPlaceholderData ? <RoutedTabsSkeleton tabs={ tabs }/> : (
<RoutedTabs <RoutedTabs
tabs={ tabs } tabs={ tabs }
tabListProps={ isMobile ? undefined : TAB_LIST_PROPS } listProps={ isMobile ? undefined : TAB_LIST_PROPS }
rightSlot={ hasPagination ? <Pagination { ...(pagination as PaginationParams) }/> : null } rightSlot={ hasPagination ? <Pagination { ...(pagination as PaginationParams) }/> : null }
stickyEnabled={ hasPagination } stickyEnabled={ hasPagination }
/> />
......
import { chakra, GridItem, Flex, Text } from '@chakra-ui/react'; import { chakra, GridItem, Flex, Text } 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 * as ContainerWithScrollY from 'ui/shared/ContainerWithScrollY'; import * as ContainerWithScrollY from 'ui/shared/ContainerWithScrollY';
import Hint from 'ui/shared/Hint'; import Hint from 'ui/shared/Hint';
import HintPopover from 'ui/shared/HintPopover';
const LabelScrollText = () => ( const LabelScrollText = () => (
<Text fontWeight={ 500 } variant="secondary" fontSize="xs" className="note" align="right"> <Text fontWeight={ 500 } color="text.secondary" fontSize="xs" className="note" textAlign="right">
Scroll to see more Scroll to see more
</Text> </Text>
); );
...@@ -19,23 +18,20 @@ interface LabelProps { ...@@ -19,23 +18,20 @@ interface LabelProps {
className?: string; className?: string;
id?: string; id?: string;
hasScroll?: boolean; hasScroll?: boolean;
type?: 'tooltip' | 'popover';
} }
const Label = chakra(({ hint, children, isLoading, id, className, hasScroll, type }: LabelProps) => { const Label = chakra(({ hint, children, isLoading, id, className, hasScroll }: LabelProps) => {
return ( return (
<GridItem <GridItem
id={ id } id={ id }
className={ className } className={ className }
py={ 1 } py={ 1 }
lineHeight={{ base: 5, lg: 6 }} textStyle="md"
_notFirst={{ mt: { base: 3, lg: 0 } }} _notFirst={{ mt: { base: 3, lg: 0 } }}
> >
<Flex columnGap={ 2 } alignItems="flex-start"> <Flex columnGap={ 2 } alignItems="flex-start">
{ hint && (type === 'popover' ? { hint && <Hint label={ hint } isLoading={ isLoading } my={{ lg: '2px' }}/> }
<HintPopover label={ hint } isLoading={ isLoading } my={{ lg: '2px' }}/> : <Skeleton loading={ isLoading } fontWeight={{ base: 700, lg: 500 }}>
<Hint label={ hint } isLoading={ isLoading } my={{ lg: '2px' }}/>) }
<Skeleton isLoaded={ !isLoading } fontWeight={{ base: 700, lg: 500 }}>
{ children } { children }
{ hasScroll && <LabelScrollText/> } { hasScroll && <LabelScrollText/> }
</Skeleton> </Skeleton>
...@@ -59,7 +55,7 @@ const Value = chakra(({ children, className }: ValueProps) => { ...@@ -59,7 +55,7 @@ const Value = chakra(({ children, className }: ValueProps) => {
rowGap={ 3 } rowGap={ 3 }
pl={{ base: 7, lg: 0 }} pl={{ base: 7, lg: 0 }}
py={ 1 } py={ 1 }
lineHeight={{ base: 5, lg: 6 }} textStyle="md"
whiteSpace="nowrap" whiteSpace="nowrap"
> >
{ children } { children }
......
import React from 'react'; import React from 'react';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import Skeleton from 'ui/shared/chakra/Skeleton'; import { Skeleton } from 'toolkit/chakra/skeleton';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import TextSeparator from 'ui/shared/TextSeparator'; import TextSeparator from 'ui/shared/TextSeparator';
...@@ -15,11 +15,11 @@ const DetailsTimestamp = ({ timestamp, isLoading }: Props) => { ...@@ -15,11 +15,11 @@ const DetailsTimestamp = ({ timestamp, isLoading }: Props) => {
return ( return (
<> <>
<IconSvg name="clock" boxSize={ 5 } color="gray.500" isLoading={ isLoading }/> <IconSvg name="clock" boxSize={ 5 } color="gray.500" isLoading={ isLoading }/>
<Skeleton isLoaded={ !isLoading } ml={ 2 }> <Skeleton loading={ isLoading } ml={ 2 }>
{ dayjs(timestamp).fromNow() } { dayjs(timestamp).fromNow() }
</Skeleton> </Skeleton>
<TextSeparator color="gray.500"/> <TextSeparator color="gray.500"/>
<Skeleton isLoaded={ !isLoading } whiteSpace="normal"> <Skeleton loading={ isLoading } whiteSpace="normal">
{ dayjs(timestamp).format('llll') } { dayjs(timestamp).format('llll') }
</Skeleton> </Skeleton>
</> </>
......
...@@ -32,6 +32,7 @@ const Hint = ({ label, className, tooltipProps, isLoading }: Props) => { ...@@ -32,6 +32,7 @@ const Hint = ({ label, className, tooltipProps, isLoading }: Props) => {
<Tooltip <Tooltip
content={ label } content={ label }
positioning={{ placement: 'top' }} positioning={{ placement: 'top' }}
interactive
{ ...tooltipProps } { ...tooltipProps }
> >
<IconButton <IconButton
......
import type {
PopoverBodyProps,
PopoverContentProps,
PopoverProps } from '@chakra-ui/react';
import {
DarkMode,
PopoverArrow,
PopoverBody,
PopoverContent,
PopoverTrigger,
Portal,
chakra,
useColorModeValue,
} from '@chakra-ui/react';
import React from 'react';
import Popover from 'ui/shared/chakra/Popover';
import Skeleton from 'ui/shared/chakra/Skeleton';
import IconSvg from './IconSvg';
interface Props {
label: React.ReactNode;
className?: string;
isLoading?: boolean;
popoverProps?: Partial<PopoverProps>;
popoverContentProps?: Partial<PopoverContentProps>;
popoverBodyProps?: Partial<PopoverBodyProps>;
}
const HintPopover = ({ label, isLoading, className, popoverProps, popoverContentProps, popoverBodyProps }: Props) => {
const bgColor = useColorModeValue('gray.700', 'gray.900');
if (isLoading) {
return <Skeleton className={ className } boxSize={ 5 } borderRadius="sm"/>;
}
return (
<Popover trigger="hover" isLazy placement="top" { ...popoverProps }>
<PopoverTrigger>
<IconSvg className={ className } name="info" boxSize={ 5 } color="icon_info" _hover={{ color: 'link_hovered' }} cursor="pointer"/>
</PopoverTrigger>
<Portal>
<PopoverContent bgColor={ bgColor } maxW={{ base: 'calc(100vw - 8px)', lg: '320px' }} borderRadius="sm" { ...popoverContentProps }>
<PopoverArrow bgColor={ bgColor }/>
<PopoverBody color="white" fontSize="sm" lineHeight="20px" px={ 2 } py="2px" { ...popoverBodyProps }>
<DarkMode>
{ label }
</DarkMode>
</PopoverBody>
</PopoverContent>
</Portal>
</Popover>
);
};
export default React.memo(chakra(HintPopover));
import { Grid, chakra, GridItem } from '@chakra-ui/react'; import { Grid, chakra, GridItem } from '@chakra-ui/react';
import { motion } from 'framer-motion';
import React from 'react'; import React from 'react';
import Skeleton from 'ui/shared/chakra/Skeleton'; import { Skeleton } from 'toolkit/chakra/skeleton';
interface ContainerProps { interface ContainerProps {
className?: string; className?: string;
isAnimated?: boolean; animation?: string;
children: React.ReactNode; children: React.ReactNode;
} }
const Container = chakra(({ isAnimated, children, className }: ContainerProps) => { const Container = chakra(({ animation, children, className }: ContainerProps) => {
return ( return (
<Grid <Grid
as={ motion.div }
w="100%" w="100%"
initial={ isAnimated ? { opacity: 0, scale: 0.97 } : { opacity: 1, scale: 1 } } animation={ animation }
animate={{ opacity: 1, scale: 1 }}
transitionDuration="normal"
transitionTimingFunction="linear"
rowGap={ 2 } rowGap={ 2 }
columnGap={ 2 } columnGap={ 2 }
gridTemplateColumns="86px auto" gridTemplateColumns="86px auto"
...@@ -48,7 +43,7 @@ const Label = chakra(({ children, className, isLoading }: LabelProps) => { ...@@ -48,7 +43,7 @@ const Label = chakra(({ children, className, isLoading }: LabelProps) => {
return ( return (
<Skeleton <Skeleton
className={ className } className={ className }
isLoaded={ !isLoading } loading={ isLoading }
fontWeight={ 500 } fontWeight={ 500 }
my="5px" my="5px"
justifySelf="start" justifySelf="start"
......
import { import { chakra } from '@chakra-ui/react';
Image,
useColorModeValue,
chakra,
} from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { NetworkExplorer as TNetworkExplorer } from 'types/networks'; import type { NetworkExplorer as TNetworkExplorer } from 'types/networks';
import config from 'configs/app'; import config from 'configs/app';
import stripTrailingSlash from 'lib/stripTrailingSlash'; import stripTrailingSlash from 'lib/stripTrailingSlash';
import { Image } from 'toolkit/chakra/image';
import { Link } from 'toolkit/chakra/link';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import LinkExternal from 'ui/shared/links/LinkExternal';
import VerifyWith from 'ui/shared/VerifyWith'; import VerifyWith from 'ui/shared/VerifyWith';
interface Props { interface Props {
...@@ -20,24 +17,22 @@ interface Props { ...@@ -20,24 +17,22 @@ interface Props {
} }
const NetworkExplorers = ({ className, type, pathParam }: Props) => { const NetworkExplorers = ({ className, type, pathParam }: Props) => {
const defaultIconColor = useColorModeValue('gray.400', 'gray.500');
const explorersLinks = React.useMemo(() => { const explorersLinks = React.useMemo(() => {
return config.UI.explorers.items return config.UI.explorers.items
.filter((explorer) => typeof explorer.paths[type] === 'string') .filter((explorer) => typeof explorer.paths[type] === 'string')
.map((explorer) => { .map((explorer) => {
const url = new URL(stripTrailingSlash(explorer.paths[type] || '') + '/' + pathParam, explorer.baseUrl); const url = new URL(stripTrailingSlash(explorer.paths[type] || '') + '/' + pathParam, explorer.baseUrl);
return ( return (
<LinkExternal h="34px" key={ explorer.baseUrl } href={ url.toString() } alignItems="center" display="inline-flex" minW="120px"> <Link external h="34px" key={ explorer.baseUrl } href={ url.toString() } alignItems="center" display="inline-flex" minW="120px">
{ explorer.logo ? { explorer.logo ?
<Image boxSize={ 5 } mr={ 2 } src={ explorer.logo } alt={ `${ explorer.title } icon` }/> : <Image boxSize={ 5 } mr={ 2 } src={ explorer.logo } alt={ `${ explorer.title } icon` }/> :
<IconSvg name="explorer" boxSize={ 5 } color={ defaultIconColor } mr={ 2 }/> <IconSvg name="explorer" boxSize={ 5 } color={{ _light: 'gray.400', _dark: 'gray.500' }} mr={ 2 }/>
} }
{ explorer.title } { explorer.title }
</LinkExternal> </Link>
); );
}); });
}, [ pathParam, type, defaultIconColor ]); }, [ pathParam, type ]);
if (explorersLinks.length === 0) { if (explorersLinks.length === 0) {
return null; return null;
......
import { Tooltip, chakra } from '@chakra-ui/react';
import React from 'react';
import useIsMobile from 'lib/hooks/useIsMobile';
import Skeleton from 'ui/shared/chakra/Skeleton';
type Props = {
label: string;
isLoading?: boolean;
className?: string;
children: React.ReactNode;
};
const PopoverTriggerTooltip = ({ label, isLoading, className, children }: Props, ref: React.ForwardedRef<HTMLDivElement>) => {
const isMobile = useIsMobile();
return (
// tooltip need to be wrapped in div for proper popover positioning
<Skeleton isLoaded={ !isLoading } borderRadius="base" ref={ ref } className={ className }>
<Tooltip
label={ label }
isDisabled={ isMobile }
// need a delay to avoid flickering when closing the popover
openDelay={ 100 }
>
{ children }
</Tooltip>
</Skeleton>
);
};
export default chakra(React.forwardRef(PopoverTriggerTooltip));
import { Box, IconButton, chakra, Tooltip, Flex } from '@chakra-ui/react'; import { Box, chakra, Flex } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
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';
interface Props { interface Props {
...@@ -26,38 +28,44 @@ const PrevNext = ({ className, onClick, prevLabel, nextLabel, isPrevDisabled, is ...@@ -26,38 +28,44 @@ const PrevNext = ({ className, onClick, prevLabel, nextLabel, isPrevDisabled, is
if (isLoading) { if (isLoading) {
return ( return (
<Flex columnGap="10px" className={ className }> <Flex columnGap="10px" className={ className }>
<Skeleton boxSize={ 6 } borderRadius="sm"/> <Skeleton loading boxSize={ 6 } borderRadius="sm"/>
<Skeleton boxSize={ 6 } borderRadius="sm"/> <Skeleton loading boxSize={ 6 } borderRadius="sm"/>
</Flex> </Flex>
); );
} }
return ( return (
<Box className={ className } display="flex"> <Box className={ className } display="flex">
<Tooltip label={ prevLabel }> <Tooltip content={ prevLabel }>
<IconButton <IconButton
aria-label="prev" aria-label="prev"
icon={ <IconSvg name="arrows/east-mini" boxSize={ 6 }/> }
h={ 6 }
borderRadius="sm" borderRadius="sm"
variant="subtle" bg="gray.100"
colorScheme="gray" color="gray.600"
_hover={{
color: 'link.primary.hover',
}}
onClick={ handelPrevClick } onClick={ handelPrevClick }
isDisabled={ isPrevDisabled } disabled={ isPrevDisabled }
/> >
<IconSvg name="arrows/east-mini" boxSize={ 6 }/>
</IconButton>
</Tooltip> </Tooltip>
<Tooltip label={ nextLabel }> <Tooltip content={ nextLabel }>
<IconButton <IconButton
aria-label="next" aria-label="next"
icon={ <IconSvg name="arrows/east-mini" boxSize={ 6 } transform="rotate(180deg)"/> }
h={ 6 }
borderRadius="sm" borderRadius="sm"
variant="subtle" bg="gray.100"
colorScheme="gray" color="gray.600"
_hover={{
color: 'link.primary.hover',
}}
ml="10px" ml="10px"
onClick={ handelNextClick } onClick={ handelNextClick }
isDisabled={ isNextDisabled } disabled={ isNextDisabled }
/> >
<IconSvg name="arrows/east-mini" boxSize={ 6 } transform="rotate(180deg)"/>
</IconButton>
</Tooltip> </Tooltip>
</Box> </Box>
); );
......
import type { ChakraProps } from '@chakra-ui/react'; import type { HTMLChakraProps } from '@chakra-ui/react';
import { Box, Flex, chakra, useColorModeValue } from '@chakra-ui/react'; import { Box, Flex, chakra } 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 CopyToClipboard from './CopyToClipboard'; import CopyToClipboard from './CopyToClipboard';
...@@ -16,7 +16,7 @@ interface Props { ...@@ -16,7 +16,7 @@ interface Props {
textareaMinHeight?: string; textareaMinHeight?: string;
showCopy?: boolean; showCopy?: boolean;
isLoading?: boolean; isLoading?: boolean;
contentProps?: ChakraProps; contentProps?: HTMLChakraProps<'div'>;
} }
const RawDataSnippet = ({ const RawDataSnippet = ({
...@@ -36,12 +36,12 @@ const RawDataSnippet = ({ ...@@ -36,12 +36,12 @@ const RawDataSnippet = ({
// so blackAlpha.50 here is replaced with #f5f5f6 // so blackAlpha.50 here is replaced with #f5f5f6
// and whiteAlpha.50 is replaced with #1a1b1b // and whiteAlpha.50 is replaced with #1a1b1b
// const bgColor = useColorModeValue('blackAlpha.50', 'whiteAlpha.50'); // const bgColor = useColorModeValue('blackAlpha.50', 'whiteAlpha.50');
const bgColor = useColorModeValue('#f5f5f6', '#1a1b1b'); const bgColor = { _light: '#f5f5f6', _dark: '#1a1b1b' };
return ( return (
<Box className={ className } as="section" title={ title }> <Box className={ className } as="section" title={ title }>
{ (title || rightSlot || showCopy) && ( { (title || rightSlot || showCopy) && (
<Flex justifyContent={ title ? 'space-between' : 'flex-end' } alignItems="center" mb={ 3 }> <Flex justifyContent={ title ? 'space-between' : 'flex-end' } alignItems="center" mb={ 3 }>
{ title && <Skeleton fontWeight={ 500 } isLoaded={ !isLoading }>{ title }</Skeleton> } { title && <Skeleton fontWeight={ 500 } loading={ isLoading }>{ title }</Skeleton> }
{ rightSlot } { rightSlot }
{ typeof data === 'string' && showCopy && <CopyToClipboard text={ data } isLoading={ isLoading }/> } { typeof data === 'string' && showCopy && <CopyToClipboard text={ data } isLoading={ isLoading }/> }
</Flex> </Flex>
...@@ -58,7 +58,7 @@ const RawDataSnippet = ({ ...@@ -58,7 +58,7 @@ const RawDataSnippet = ({
whiteSpace="pre-wrap" whiteSpace="pre-wrap"
overflowX="hidden" overflowX="hidden"
overflowY="auto" overflowY="auto"
isLoaded={ !isLoading } loading={ isLoading }
{ ...contentProps } { ...contentProps }
> >
{ data } { data }
......
import { import { Box, chakra, Grid } from '@chakra-ui/react';
Button,
PopoverTrigger,
PopoverBody,
PopoverContent,
Show,
Hide,
chakra,
useDisclosure,
Grid,
} from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import Popover from 'ui/shared/chakra/Popover'; import { Button } from 'toolkit/chakra/button';
import { PopoverBody, PopoverContent, PopoverRoot, PopoverTrigger } from 'toolkit/chakra/popover';
import { Tooltip } from 'toolkit/chakra/tooltip';
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import PopoverTriggerTooltip from 'ui/shared/PopoverTriggerTooltip';
interface Props { interface Props {
className?: string; className?: string;
...@@ -24,39 +16,34 @@ interface Props { ...@@ -24,39 +16,34 @@ interface Props {
} }
const VerifyWith = ({ className, links, label, longText, shortText }: Props) => { const VerifyWith = ({ className, links, label, longText, shortText }: Props) => {
const { isOpen, onToggle, onClose } = useDisclosure();
const { open, onOpenChange } = useDisclosure();
return ( return (
<Popover isOpen={ isOpen } onClose={ onClose } placement="bottom-start" isLazy> <PopoverRoot positioning={{ placement: 'bottom-start' }} open={ open } onOpenChange={ onOpenChange }>
<PopoverTrigger> <PopoverTrigger>
<PopoverTriggerTooltip label={ label } className={ className }> <Box className={ className }>
<Button <Tooltip content={ label } disableOnMobile disabled={ open }>
size="sm" <Button
variant="outline" size="sm"
colorScheme="gray" variant="dropdown"
onClick={ onToggle } expanded={ open }
isActive={ isOpen } aria-label={ label }
aria-label={ label } fontWeight={ 500 }
fontWeight={ 500 } px={ shortText ? 2 : 1 }
px={ shortText ? 2 : 1 } flexShrink={ 0 }
h="32px" columnGap={ 1 }
flexShrink={ 0 } >
> <IconSvg name="explorer" boxSize={ 5 }/>
<IconSvg name="explorer" boxSize={ 5 }/> <chakra.span hideBelow="xl">{ longText }</chakra.span>
<Show above="xl"> { shortText && <chakra.span hideFrom="xl">{ shortText }</chakra.span> }
<chakra.span ml={ 1 }>{ longText }</chakra.span> </Button>
</Show> </Tooltip>
{ shortText && ( </Box>
<Hide above="xl">
<chakra.span ml={ 1 }>{ shortText }</chakra.span>
</Hide>
) }
</Button>
</PopoverTriggerTooltip>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent w="auto"> <PopoverContent w="auto">
<PopoverBody > <PopoverBody >
<chakra.span color="text_secondary" fontSize="xs">{ label }</chakra.span> <chakra.span color="text.secondary" textStyle="xs">{ label }</chakra.span>
<Grid <Grid
alignItems="center" alignItems="center"
templateColumns={ links.length > 1 ? 'auto auto' : '1fr' } templateColumns={ links.length > 1 ? 'auto auto' : '1fr' }
...@@ -68,7 +55,7 @@ const VerifyWith = ({ className, links, label, longText, shortText }: Props) => ...@@ -68,7 +55,7 @@ const VerifyWith = ({ className, links, label, longText, shortText }: Props) =>
</Grid> </Grid>
</PopoverBody> </PopoverBody>
</PopoverContent> </PopoverContent>
</Popover> </PopoverRoot>
); );
}; };
......
import { Alert, Spinner, chakra } from '@chakra-ui/react'; import { Spinner, chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import Skeleton from 'ui/shared/chakra/Skeleton'; import { Alert } from 'toolkit/chakra/alert';
interface Props { interface Props {
isLoading?: boolean; isLoading?: boolean;
...@@ -10,12 +10,14 @@ interface Props { ...@@ -10,12 +10,14 @@ interface Props {
const ServiceDegradationWarning = ({ isLoading, className }: Props) => { const ServiceDegradationWarning = ({ isLoading, className }: Props) => {
return ( return (
<Skeleton className={ className } isLoaded={ !isLoading }> <Alert
<Alert status="warning" colorScheme="gray" alignItems={{ base: 'flex-start', lg: 'center' }}> loading={ isLoading }
<Spinner size="sm" mr={ 2 } my={{ base: '3px', lg: 0 }} flexShrink={ 0 }/> status="neutral"
Data sync in progress... page will refresh automatically once data is available className={ className }
</Alert> startElement={ <Spinner size="sm" my="3px" flexShrink={ 0 }/> }
</Skeleton> >
Data sync in progress... page will refresh automatically once data is available
</Alert>
); );
}; };
......
...@@ -3,7 +3,7 @@ import React from 'react'; ...@@ -3,7 +3,7 @@ import React from 'react';
import type { OptimisticL2TxnBatchesItem } from 'types/api/optimisticL2'; import type { OptimisticL2TxnBatchesItem } from 'types/api/optimisticL2';
import type { ExcludeUndefined } from 'types/utils'; import type { ExcludeUndefined } from 'types/utils';
import Tag from 'ui/shared/chakra/Tag'; import { Badge } from 'toolkit/chakra/badge';
export interface Props { export interface Props {
container: ExcludeUndefined<OptimisticL2TxnBatchesItem['batch_data_container']>; container: ExcludeUndefined<OptimisticL2TxnBatchesItem['batch_data_container']>;
...@@ -24,9 +24,9 @@ const OptimisticL2TxnBatchDA = ({ container, isLoading }: Props) => { ...@@ -24,9 +24,9 @@ const OptimisticL2TxnBatchDA = ({ container, isLoading }: Props) => {
})(); })();
return ( return (
<Tag colorScheme="yellow" isLoading={ isLoading }> <Badge colorScheme="yellow" loading={ isLoading }>
{ text } { text }
</Tag> </Badge>
); );
}; };
......
...@@ -3,7 +3,7 @@ import React from 'react'; ...@@ -3,7 +3,7 @@ import React from 'react';
import type { Step } from './types'; import type { Step } from './types';
import Skeleton from 'ui/shared/chakra/Skeleton'; import { Skeleton } from 'toolkit/chakra/skeleton';
import VerificationStep from './VerificationStep'; import VerificationStep from './VerificationStep';
...@@ -25,7 +25,7 @@ const VerificationSteps = ({ currentStep, currentStepPending, steps, isLoading, ...@@ -25,7 +25,7 @@ const VerificationSteps = ({ currentStep, currentStepPending, steps, isLoading,
return ( return (
<Skeleton <Skeleton
className={ className } className={ className }
isLoaded={ !isLoading } loading={ isLoading }
display="flex" display="flex"
gap={ 2 } gap={ 2 }
alignItems="center" alignItems="center"
......
import { Box, Table } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { Alert } from 'toolkit/chakra/alert'; import { Alert } from 'toolkit/chakra/alert';
import { TableBody, TableColumnHeader, TableHeader, TableRoot, TableRow } from 'toolkit/chakra/table';
import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice'; import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts'; import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts';
...@@ -12,20 +13,20 @@ const AlertsShowcase = () => { ...@@ -12,20 +13,20 @@ const AlertsShowcase = () => {
<Section> <Section>
<SectionHeader>Status</SectionHeader> <SectionHeader>Status</SectionHeader>
<SamplesStack> <SamplesStack>
<Sample label="visual: info"> <Sample label="status: info">
<Alert visual="info" title="Info"> Alert content </Alert> <Alert status="info" title="Info"> Alert content </Alert>
</Sample> </Sample>
<Sample label="visual: neutral"> <Sample label="status: neutral">
<Alert visual="neutral" title="Neutral"> Alert content </Alert> <Alert status="neutral" title="Neutral"> Alert content </Alert>
</Sample> </Sample>
<Sample label="visual: warning"> <Sample label="status: warning">
<Alert visual="warning" title="Warning"> Alert content </Alert> <Alert status="warning" title="Warning"> Alert content </Alert>
</Sample> </Sample>
<Sample label="visual: success"> <Sample label="status: success">
<Alert visual="success" title="Success"> Alert content </Alert> <Alert status="success" title="Success"> Alert content </Alert>
</Sample> </Sample>
<Sample label="visual: error"> <Sample label="status: error">
<Alert visual="error" title="Error"> Alert content </Alert> <Alert status="error" title="Error"> Alert content </Alert>
</Sample> </Sample>
</SamplesStack> </SamplesStack>
</Section> </Section>
...@@ -33,7 +34,7 @@ const AlertsShowcase = () => { ...@@ -33,7 +34,7 @@ const AlertsShowcase = () => {
<SectionHeader>Variant</SectionHeader> <SectionHeader>Variant</SectionHeader>
<SamplesStack> <SamplesStack>
<Sample label="variant: subtle"> <Sample label="variant: subtle">
<Alert visual="info" title="Info"> Alert content </Alert> <Alert status="info" title="Info"> Alert content </Alert>
</Sample> </Sample>
</SamplesStack> </SamplesStack>
</Section> </Section>
...@@ -42,56 +43,56 @@ const AlertsShowcase = () => { ...@@ -42,56 +43,56 @@ const AlertsShowcase = () => {
<SectionSubHeader>Inside table (SocketNewItemsNotice)</SectionSubHeader> <SectionSubHeader>Inside table (SocketNewItemsNotice)</SectionSubHeader>
<SamplesStack> <SamplesStack>
<Sample label="loading"> <Sample label="loading">
<Table.Root> <TableRoot tableLayout="auto">
<Table.Header> <TableHeader>
<Table.Row> <TableRow>
<Table.ColumnHeader w="100px">Block</Table.ColumnHeader> <TableColumnHeader w="100px">Block</TableColumnHeader>
<Table.ColumnHeader w="100px">Age</Table.ColumnHeader> <TableColumnHeader w="100px">Age</TableColumnHeader>
<Table.ColumnHeader w="100px">Gas used</Table.ColumnHeader> <TableColumnHeader w="100px">Gas used</TableColumnHeader>
</Table.Row> </TableRow>
</Table.Header> </TableHeader>
<Table.Body> <TableBody>
<SocketNewItemsNotice.Desktop <SocketNewItemsNotice.Desktop
url={ window.location.href } url={ window.location.href }
num={ 1234 } num={ 1234 }
type="block" type="block"
isLoading isLoading
/> />
</Table.Body> </TableBody>
</Table.Root> </TableRoot>
</Sample> </Sample>
<Sample label="success"> <Sample label="success">
<Table.Root> <TableRoot tableLayout="auto">
<Table.Header> <TableHeader>
<Table.Row> <TableRow>
<Table.ColumnHeader w="100px">Block</Table.ColumnHeader> <TableColumnHeader w="100px">Block</TableColumnHeader>
<Table.ColumnHeader w="100px">Age</Table.ColumnHeader> <TableColumnHeader w="100px">Age</TableColumnHeader>
<Table.ColumnHeader w="100px">Gas used</Table.ColumnHeader> <TableColumnHeader w="100px">Gas used</TableColumnHeader>
</Table.Row> </TableRow>
</Table.Header> </TableHeader>
<Table.Body> <TableBody>
<SocketNewItemsNotice.Desktop <SocketNewItemsNotice.Desktop
url={ window.location.href } url={ window.location.href }
num={ 1234 } num={ 1234 }
type="block" type="block"
isLoading={ false } isLoading={ false }
/> />
</Table.Body> </TableBody>
</Table.Root> </TableRoot>
</Sample> </Sample>
</SamplesStack> </SamplesStack>
<SectionSubHeader>Multiple lines</SectionSubHeader> <SectionSubHeader>Multiple lines</SectionSubHeader>
<SamplesStack> <SamplesStack>
<Sample label="multiple lines, with title, inline=false"> <Sample label="multiple lines, with title, inline=false">
<Alert visual="warning" title="Warning" inline={ false } maxWidth="500px"> <Alert status="warning" title="Warning" inline={ false } maxWidth="500px">
<Box> <Box>
Participated in our recent Blockscout activities? Check your eligibility and claim your NFT Scout badges. More exciting things are coming soon! Participated in our recent Blockscout activities? Check your eligibility and claim your NFT Scout badges. More exciting things are coming soon!
</Box> </Box>
</Alert> </Alert>
</Sample> </Sample>
<Sample label="multiple lines, no title"> <Sample label="multiple lines, no title">
<Alert visual="warning" maxWidth="500px"> <Alert status="warning" maxWidth="500px">
<Box> <Box>
Participated in our recent Blockscout activities? Check your eligibility and claim your NFT Scout badges. More exciting things are coming soon! Participated in our recent Blockscout activities? Check your eligibility and claim your NFT Scout badges. More exciting things are coming soon!
</Box> </Box>
......
...@@ -102,7 +102,7 @@ const LinksShowcase = () => { ...@@ -102,7 +102,7 @@ const LinksShowcase = () => {
</CutLink> </CutLink>
</Sample> </Sample>
<Sample label="Loading" flexDirection="column" alignItems="flex-start"> <Sample label="Loading" flexDirection="column" alignItems="flex-start">
<CutLink id="CutLink_2" isLoading> <CutLink id="CutLink_2" loading>
<Box maxW="500px">{ TEXT }</Box> <Box maxW="500px">{ TEXT }</Box>
</CutLink> </CutLink>
</Sample> </Sample>
......
...@@ -3,7 +3,7 @@ import React from 'react'; ...@@ -3,7 +3,7 @@ import React from 'react';
import type { ZkSyncBatch } from 'types/api/zkSyncL2'; import type { ZkSyncBatch } from 'types/api/zkSyncL2';
import Skeleton from 'ui/shared/chakra/Skeleton'; import { Skeleton } from 'toolkit/chakra/skeleton';
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem'; import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
import DetailsTimestamp from 'ui/shared/DetailsTimestamp'; import DetailsTimestamp from 'ui/shared/DetailsTimestamp';
import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1'; import TxEntityL1 from 'ui/shared/entities/tx/TxEntityL1';
...@@ -48,7 +48,7 @@ const ZkSyncL2TxnBatchHashesInfo = ({ isLoading, data }: Props) => { ...@@ -48,7 +48,7 @@ const ZkSyncL2TxnBatchHashesInfo = ({ isLoading, data }: Props) => {
</Flex> </Flex>
) } ) }
</> </>
) : <Skeleton isLoaded={ !isLoading }>Pending</Skeleton> } ) : <Skeleton loading={ isLoading }>Pending</Skeleton> }
</DetailsInfoItem.Value> </DetailsInfoItem.Value>
<DetailsInfoItem.Label <DetailsInfoItem.Label
...@@ -75,7 +75,7 @@ const ZkSyncL2TxnBatchHashesInfo = ({ isLoading, data }: Props) => { ...@@ -75,7 +75,7 @@ const ZkSyncL2TxnBatchHashesInfo = ({ isLoading, data }: Props) => {
</Flex> </Flex>
) } ) }
</> </>
) : <Skeleton isLoaded={ !isLoading }>Pending</Skeleton> } ) : <Skeleton loading={ isLoading }>Pending</Skeleton> }
</DetailsInfoItem.Value> </DetailsInfoItem.Value>
<DetailsInfoItem.Label <DetailsInfoItem.Label
...@@ -102,7 +102,7 @@ const ZkSyncL2TxnBatchHashesInfo = ({ isLoading, data }: Props) => { ...@@ -102,7 +102,7 @@ const ZkSyncL2TxnBatchHashesInfo = ({ isLoading, data }: Props) => {
</Flex> </Flex>
) } ) }
</> </>
) : <Skeleton isLoaded={ !isLoading }>Pending</Skeleton> } ) : <Skeleton loading={ isLoading }>Pending</Skeleton> }
</DetailsInfoItem.Value> </DetailsInfoItem.Value>
</> </>
); );
......
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