Commit 632ae278 authored by tom's avatar tom

refactor Footer

parent 4a508d9d
...@@ -27,7 +27,7 @@ const RESTRICTED_MODULES = { ...@@ -27,7 +27,7 @@ const RESTRICTED_MODULES = {
{ 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: '@chakra-ui/react', name: '@chakra-ui/react',
importNames: [ 'Popover', 'Menu', 'PinInput', 'useToast' ], importNames: [ 'Menu', 'PinInput', 'useToast' ],
message: 'Please use corresponding component or hook from ui/shared/chakra component instead', message: 'Please use corresponding component or hook from ui/shared/chakra component instead',
}, },
{ {
......
...@@ -48,12 +48,24 @@ const ERROR_SCREEN_STYLES: HTMLChakraProps<'div'> = { ...@@ -48,12 +48,24 @@ const ERROR_SCREEN_STYLES: HTMLChakraProps<'div'> = {
}; };
function MyApp({ Component, pageProps }: AppPropsWithLayout) { function MyApp({ Component, pageProps }: AppPropsWithLayout) {
// TODO @tom2drum currently there is hydration mismatch between server and client
// because we use useColorMode hook in the layout component
// not sure how to fix it though
const [ mounted, setMounted ] = React.useState(false);
React.useEffect(() => {
setMounted(true);
}, []);
useLoadFeatures(); useLoadFeatures();
useNotifyOnNavigation(); useNotifyOnNavigation();
const queryClient = useQueryClientConfig(); const queryClient = useQueryClientConfig();
if (!mounted) {
return null;
}
const getLayout = Component.getLayout ?? ((page) => <Layout>{ page }</Layout>); const getLayout = Component.getLayout ?? ((page) => <Layout>{ page }</Layout>);
return ( return (
......
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import PageNextJs from 'nextjs/PageNextJs'; import PageNextJs from 'nextjs/PageNextJs';
const Chakra = dynamic(() => import('ui/pages/Chakra'), { ssr: false }); import Chakra from 'ui/pages/Chakra';
const Page: NextPage = () => { const Page: NextPage = () => {
return ( return (
......
import type {
SkeletonProps as ChakraSkeletonProps,
CircleProps,
} from '@chakra-ui/react';
import { Skeleton as ChakraSkeleton, Circle, Stack } from '@chakra-ui/react';
import * as React from 'react';
export interface SkeletonCircleProps extends ChakraSkeletonProps {
size?: CircleProps['size'];
}
export const SkeletonCircle = React.forwardRef<
HTMLDivElement,
SkeletonCircleProps
>(function SkeletonCircle(props, ref) {
const { size, ...rest } = props;
return (
<Circle size={ size } asChild ref={ ref }>
<ChakraSkeleton { ...rest }/>
</Circle>
);
});
export interface SkeletonTextProps extends ChakraSkeletonProps {
noOfLines?: number;
}
export const SkeletonText = React.forwardRef<HTMLDivElement, SkeletonTextProps>(
function SkeletonText(props, ref) {
const { noOfLines = 3, gap, ...rest } = props;
return (
<Stack gap={ gap } width="full" ref={ ref }>
{ Array.from({ length: noOfLines }).map((_, index) => (
<ChakraSkeleton
height="4"
key={ index }
{ ...props }
_last={{ maxW: '80%' }}
{ ...rest }
/>
)) }
</Stack>
);
},
);
export const Skeleton = React.forwardRef<HTMLDivElement, ChakraSkeletonProps>(
function Skeleton(props, ref) {
const { loading = false, variant = 'shine', ...rest } = props;
return <ChakraSkeleton loading={ loading } variant={ variant } { ...rest } ref={ ref }/>;
},
);
import { Tooltip as ChakraTooltip, Portal } from '@chakra-ui/react'; import { Tooltip as ChakraTooltip, Portal } from '@chakra-ui/react';
import { useClickAway } from '@uidotdev/usehooks';
import * as React from 'react'; import * as React from 'react';
import useIsMobile from 'lib/hooks/useIsMobile';
export interface TooltipProps extends ChakraTooltip.RootProps { export interface TooltipProps extends ChakraTooltip.RootProps {
showArrow?: boolean; showArrow?: boolean;
portalled?: boolean; portalled?: boolean;
...@@ -13,7 +16,7 @@ export interface TooltipProps extends ChakraTooltip.RootProps { ...@@ -13,7 +16,7 @@ export interface TooltipProps extends ChakraTooltip.RootProps {
export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>( export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
function Tooltip(props, ref) { function Tooltip(props, ref) {
const { const {
showArrow, showArrow = true,
children, children,
disabled, disabled,
portalled, portalled,
...@@ -23,14 +26,50 @@ export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>( ...@@ -23,14 +26,50 @@ export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
...rest ...rest
} = props; } = props;
const [ open, setOpen ] = React.useState(false);
const isMobile = useIsMobile();
const triggerRef = useClickAway<HTMLButtonElement>(() => setOpen(false));
const handleOpenChange = React.useCallback(({ open }: { open: boolean }) => {
setOpen(open);
}, []);
const handleTriggerClick = React.useCallback(() => {
// FIXME on mobile tooltip will open and close simultaneously
setOpen((prev) => !prev);
}, [ ]);
if (disabled) return children; if (disabled) return children;
const positioning = {
...rest.positioning,
offset: {
mainAxis: 4,
...rest.positioning?.offset,
},
};
return ( return (
<ChakraTooltip.Root { ...rest }> <ChakraTooltip.Root
<ChakraTooltip.Trigger asChild>{ children }</ChakraTooltip.Trigger> openDelay={ 100 }
closeDelay={ 100 }
open={ open }
onOpenChange={ handleOpenChange }
{ ...rest }
positioning={ positioning }
closeOnClick={ false }
>
<ChakraTooltip.Trigger
ref={ triggerRef }
asChild
onClick={ isMobile ? handleTriggerClick : undefined }
>
{ children }
</ChakraTooltip.Trigger>
<Portal disabled={ !portalled } container={ portalRef }> <Portal disabled={ !portalled } container={ portalRef }>
<ChakraTooltip.Positioner> <ChakraTooltip.Positioner>
<ChakraTooltip.Content ref={ ref } { ...contentProps }> <ChakraTooltip.Content ref={ ref } p={ 2 } { ...contentProps }>
{ showArrow && ( { showArrow && (
<ChakraTooltip.Arrow> <ChakraTooltip.Arrow>
<ChakraTooltip.ArrowTip/> <ChakraTooltip.ArrowTip/>
......
...@@ -13,7 +13,7 @@ const semanticTokens: ThemingConfig['semanticTokens'] = { ...@@ -13,7 +13,7 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
DEFAULT: { value: '{colors.blue.400}' }, DEFAULT: { value: '{colors.blue.400}' },
}, },
}, },
anchor: { dropdown: {
fg: { fg: {
DEFAULT: { value: { base: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } }, DEFAULT: { value: { base: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
selected: { value: { base: '{colors.blue.600}', _dark: '{colors.gray.50}' } }, selected: { value: { base: '{colors.blue.600}', _dark: '{colors.gray.50}' } },
...@@ -27,28 +27,39 @@ const semanticTokens: ThemingConfig['semanticTokens'] = { ...@@ -27,28 +27,39 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
}, },
}, },
}, },
links: {
primary: {
DEFAULT: { value: { base: '{colors.blue.600}', _dark: '{colors.blue.300}' } },
hover: { value: { base: '{colors.blue.400}' } },
},
secondary: {
DEFAULT: { value: { base: '{colors.gray.400}', _dark: '{colors.gray.500}' } },
},
subtle: {
DEFAULT: { value: { base: '{colors.blackAlpha.800}', _dark: '{colors.gray.400}' } },
hover: { value: { base: '{colors.blackAlpha.800}', _dark: '{colors.gray.400}' } },
},
},
text: {
primary: { value: { base: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
secondary: { value: { base: '{colors.gray.500}', _dark: '{colors.gray.400}' } },
},
globals: { globals: {
body: { body: {
bg: { bg: { value: { base: '{colors.white}', _dark: '{colors.black}' } },
DEFAULT: { value: { base: '{colors.white}', _dark: '{colors.black}' } }, fg: { value: '{colors.text.primary}' },
},
}, },
mark: { mark: {
bg: { bg: { value: { base: '{colors.green.100}', _dark: '{colors.green.800}' } },
DEFAULT: { value: { base: '{colors.green.100}', _dark: '{colors.green.800}' } },
},
}, },
}, },
// OLD TOKENS // OLD TOKENS
divider: { divider: { value: { base: '{colors.blackAlpha.200}', _dark: '{colors.whiteAlpha.200}' } },
DEFAULT: { value: '{colors.blackAlpha.200}' }, // text: {
_dark: { value: '{colors.whiteAlpha.200}' }, // DEFAULT: { value: '{colors.blackAlpha.800}' },
}, // _dark: { value: '{colors.whiteAlpha.800}' },
text: { // },
DEFAULT: { value: '{colors.blackAlpha.800}' },
_dark: { value: '{colors.whiteAlpha.800}' },
},
text_secondary: { text_secondary: {
DEFAULT: { value: '{colors.gray.500}' }, DEFAULT: { value: '{colors.gray.500}' },
_dark: { value: '{colors.gray.400}' }, _dark: { value: '{colors.gray.400}' },
......
...@@ -10,6 +10,7 @@ import scrollbar from './globals/scrollbar'; ...@@ -10,6 +10,7 @@ import scrollbar from './globals/scrollbar';
const globalCss: Record<string, SystemStyleObject> = { const globalCss: Record<string, SystemStyleObject> = {
body: { body: {
bg: 'globals.body.bg', bg: 'globals.body.bg',
color: 'globals.body.fg',
'-webkit-tap-highlight-color': 'transparent', '-webkit-tap-highlight-color': 'transparent',
'font-variant-ligatures': 'no-contextual', 'font-variant-ligatures': 'no-contextual',
}, },
......
...@@ -32,41 +32,41 @@ export const recipe = defineRecipe({ ...@@ -32,41 +32,41 @@ export const recipe = defineRecipe({
borderColor: 'buttons.outline.hover', borderColor: 'buttons.outline.hover',
}, },
}, },
anchor: { dropdown: {
borderWidth: '2px', borderWidth: '2px',
borderStyle: 'solid', borderStyle: 'solid',
bg: 'transparent', bg: 'transparent',
color: 'buttons.anchor.fg', color: 'buttons.dropdown.fg',
borderColor: 'buttons.anchor.border', borderColor: 'buttons.dropdown.border',
_hover: { _hover: {
bg: 'transparent', bg: 'transparent',
color: 'buttons.anchor.hover', color: 'buttons.dropdown.hover',
borderColor: 'buttons.anchor.hover', borderColor: 'buttons.dropdown.hover',
}, },
// When the dropdown is open, the button should be active // When the dropdown is open, the button should be active
_active: { _active: {
bg: 'transparent', bg: 'transparent',
color: 'buttons.anchor.hover', color: 'buttons.dropdown.hover',
borderColor: 'buttons.anchor.hover', borderColor: 'buttons.dropdown.hover',
}, },
// We have a special state for this button variant that serves as a popover trigger. // We have a special state for this button variant that serves as a popover trigger.
// When any items (filters) are selected in the popover, the button should change its background and text color. // When any items (filters) are selected in the popover, the button should change its background and text color.
// The last CSS selector is for redefining styles for the TabList component. // The last CSS selector is for redefining styles for the TabList component.
_selected: { _selected: {
bg: 'buttons.anchor.border.selected', bg: 'buttons.dropdown.border.selected',
color: 'buttons.anchor.fg.selected', color: 'buttons.dropdown.fg.selected',
borderColor: 'buttons.anchor.border.selected', borderColor: 'buttons.dropdown.border.selected',
_hover: { _hover: {
bg: 'buttons.anchor.border.selected', bg: 'buttons.dropdown.border.selected',
color: 'buttons.anchor.fg.selected', color: 'buttons.dropdown.fg.selected',
borderColor: 'buttons.anchor.border.selected', borderColor: 'buttons.dropdown.border.selected',
}, },
}, },
}, },
}, },
size: { size: {
xs: { px: 2, h: 6, fontSize: '12px' }, xs: { px: 2, h: 6, fontSize: '12px' },
sm: { px: 3, h: 8, fontSize: '14px' }, sm: { px: 2, h: 8, fontSize: '14px' },
md: { px: 4, h: 10, fontSize: '16px' }, md: { px: 4, h: 10, fontSize: '16px' },
lg: { px: 6, h: 12, fontSize: '20px' }, lg: { px: 6, h: 12, fontSize: '20px' },
}, },
......
export { recipe as button } from './button.recipe'; export { recipe as button } from './button.recipe';
export { recipe as link } from './link.recipe';
import { defineRecipe } from '@chakra-ui/react';
export const recipe = defineRecipe({
variants: {
visual: {
primary: {
color: 'links.primary',
_hover: {
textDecoration: 'none',
color: 'links.primary.hover',
},
},
secondary: {
color: 'links.secondary',
_hover: {
textDecoration: 'none',
color: 'links.primary.hover',
},
},
subtle: {
color: 'links.subtle',
_hover: {
color: 'links.subtle.hover',
textDecorationColor: 'links.subtle.hover',
},
},
},
},
defaultVariants: {
visual: 'primary',
},
});
import { Heading, HStack } from '@chakra-ui/react'; import { Heading, HStack, Link, VStack } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { Button } from 'toolkit/chakra/button'; import { Button } from 'toolkit/chakra/button';
...@@ -15,13 +15,27 @@ const ChakraShowcases = () => { ...@@ -15,13 +15,27 @@ const ChakraShowcases = () => {
<Switch onCheckedChange={ colorMode.toggleColorMode } checked={ colorMode.colorMode === 'dark' } mb={ 4 }> <Switch onCheckedChange={ colorMode.toggleColorMode } checked={ colorMode.colorMode === 'dark' } mb={ 4 }>
Color mode: { colorMode.colorMode } Color mode: { colorMode.colorMode }
</Switch> </Switch>
<Heading textStyle="heading.md" mb={ 4 }>Buttons</Heading>
<VStack align="flex-start" gap={ 6 }>
<section>
<Heading textStyle="heading.md" mb={ 2 }>Buttons</Heading>
<HStack gap={ 4 }> <HStack gap={ 4 }>
<Button>Solid</Button> <Button>Solid</Button>
<Button visual="outline">Outline</Button> <Button visual="outline">Outline</Button>
<Button visual="anchor">Anchor</Button> <Button visual="dropdown">Dropdown</Button>
<Button visual="anchor" selected>Anchor selected</Button> <Button visual="dropdown" selected>Dropdown selected</Button>
</HStack>
</section>
<section>
<Heading textStyle="heading.md" mb={ 2 }>Links</Heading>
<HStack gap={ 4 }>
<Link>Primary</Link>
<Link visual="secondary">Secondary</Link>
<Link visual="subtle">Subtle</Link>
</HStack> </HStack>
</section>
</VStack>
</> </>
); );
}; };
......
import type { HTMLChakraProps } from '@chakra-ui/react'; import type { HTMLChakraProps } from '@chakra-ui/react';
import { Skeleton, chakra } from '@chakra-ui/react'; import { chakra } from '@chakra-ui/react';
import { type IconName } from 'public/icons/name'; import { type IconName } from 'public/icons/name';
import React from 'react'; import React from 'react';
import config from 'configs/app'; import config from 'configs/app';
import { Skeleton } from 'toolkit/chakra/skeleton';
export const href = config.app.spriteHash ? `/icons/sprite.${ config.app.spriteHash }.svg` : '/icons/sprite.svg'; export const href = config.app.spriteHash ? `/icons/sprite.${ config.app.spriteHash }.svg` : '/icons/sprite.svg';
...@@ -14,9 +15,9 @@ interface Props extends HTMLChakraProps<'div'> { ...@@ -14,9 +15,9 @@ interface Props extends HTMLChakraProps<'div'> {
isLoading?: boolean; isLoading?: boolean;
} }
const IconSvg = ({ name, isLoading, ...props }: Props, ref: React.ForwardedRef<HTMLDivElement>) => { const IconSvg = ({ name, isLoading = false, ...props }: Props, ref: React.ForwardedRef<HTMLDivElement>) => {
return ( return (
<Skeleton isLoaded={ !isLoading } display="inline-block" { ...props } ref={ ref }> <Skeleton loading={ isLoading } display="inline-block" { ...props } ref={ ref }>
<chakra.svg w="100%" h="100%"> <chakra.svg w="100%" h="100%">
<use href={ `${ href }#${ name }` }/> <use href={ `${ href }#${ name }` }/>
</chakra.svg> </chakra.svg>
......
import { Button } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { toaster } from 'toolkit/chakra/toaster';
import config from 'configs/app'; import config from 'configs/app';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
import useAddOrSwitchChain from 'lib/web3/useAddOrSwitchChain'; import useAddOrSwitchChain from 'lib/web3/useAddOrSwitchChain';
import useProvider from 'lib/web3/useProvider'; import useProvider from 'lib/web3/useProvider';
import { WALLETS_INFO } from 'lib/web3/wallets'; import { WALLETS_INFO } from 'lib/web3/wallets';
import { Button } from 'toolkit/chakra/button';
import { toaster } from 'toolkit/chakra/toaster';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
const feature = config.features.web3Wallet; const feature = config.features.web3Wallet;
...@@ -46,8 +46,8 @@ const NetworkAddToWallet = () => { ...@@ -46,8 +46,8 @@ const NetworkAddToWallet = () => {
} }
return ( return (
<Button variant="outline" size="sm" onClick={ handleClick }> <Button visual="outline" size="sm" onClick={ handleClick }>
<IconSvg name={ WALLETS_INFO[wallet].icon } boxSize={ 5 } mr={ 2 }/> <IconSvg name={ WALLETS_INFO[wallet].icon } boxSize={ 5 }/>
Add { config.chain.name } Add { config.chain.name }
</Button> </Button>
); );
......
...@@ -28,7 +28,7 @@ const LayoutDefault = ({ children }: Props) => { ...@@ -28,7 +28,7 @@ const LayoutDefault = ({ children }: Props) => {
</AppErrorBoundary> </AppErrorBoundary>
</Layout.MainColumn> </Layout.MainColumn>
</Layout.MainArea> </Layout.MainArea>
{ /* <Layout.Footer/> */ } <Layout.Footer/>
</Layout.Container> </Layout.Container>
); );
}; };
......
import type { GridProps, HTMLChakraProps } from '@chakra-ui/react'; import type { GridProps, HTMLChakraProps } from '@chakra-ui/react';
import { Box, Grid, Flex, Text, Link, VStack, Skeleton, useColorModeValue } from '@chakra-ui/react'; import { Box, Grid, Flex, Text, Link, VStack } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
...@@ -11,6 +11,7 @@ import useApiQuery from 'lib/api/useApiQuery'; ...@@ -11,6 +11,7 @@ import useApiQuery from 'lib/api/useApiQuery';
import useFetch from 'lib/hooks/useFetch'; import useFetch from 'lib/hooks/useFetch';
import useIssueUrl from 'lib/hooks/useIssueUrl'; import useIssueUrl from 'lib/hooks/useIssueUrl';
import { copy } from 'lib/html-entities'; import { copy } from 'lib/html-entities';
import { Skeleton } from 'toolkit/chakra/skeleton';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import { CONTENT_MAX_WIDTH } from 'ui/shared/layout/utils'; import { CONTENT_MAX_WIDTH } from 'ui/shared/layout/utils';
import NetworkAddToWallet from 'ui/shared/NetworkAddToWallet'; import NetworkAddToWallet from 'ui/shared/NetworkAddToWallet';
...@@ -33,7 +34,6 @@ const Footer = () => { ...@@ -33,7 +34,6 @@ const Footer = () => {
}); });
const apiVersionUrl = getApiVersionUrl(backendVersionData?.backend_version); const apiVersionUrl = getApiVersionUrl(backendVersionData?.backend_version);
const issueUrl = useIssueUrl(backendVersionData?.backend_version); const issueUrl = useIssueUrl(backendVersionData?.backend_version);
const logoColor = useColorModeValue('blue.600', 'white');
const BLOCKSCOUT_LINKS = [ const BLOCKSCOUT_LINKS = [
{ {
...@@ -115,11 +115,13 @@ const Footer = () => { ...@@ -115,11 +115,13 @@ const Footer = () => {
}, []); }, []);
const renderProjectInfo = React.useCallback((gridArea?: GridProps['gridArea']) => { const renderProjectInfo = React.useCallback((gridArea?: GridProps['gridArea']) => {
const logoColor = { base: 'blue.600', _dark: 'white' };
return ( return (
<Box gridArea={ gridArea }> <Box gridArea={ gridArea }>
<Flex columnGap={ 2 } fontSize="xs" lineHeight={ 5 } alignItems="center" color="text"> <Flex columnGap={ 2 } textStyle="xs" alignItems="center">
<span>Made with</span> <span>Made with</span>
<Link href="https://www.blockscout.com" isExternal display="inline-flex" color={ logoColor } _hover={{ color: logoColor }}> <Link href="https://www.blockscout.com" target="_blank" display="inline-flex" color={ logoColor } _hover={{ color: logoColor }}>
<IconSvg <IconSvg
name="networks/logo-placeholder" name="networks/logo-placeholder"
width="80px" width="80px"
...@@ -130,7 +132,7 @@ const Footer = () => { ...@@ -130,7 +132,7 @@ const Footer = () => {
<Text mt={ 3 } fontSize="xs"> <Text mt={ 3 } fontSize="xs">
Blockscout is a tool for inspecting and analyzing EVM based blockchains. Blockchain explorer for Ethereum Networks. Blockscout is a tool for inspecting and analyzing EVM based blockchains. Blockchain explorer for Ethereum Networks.
</Text> </Text>
<Box mt={ 6 } alignItems="start" fontSize="xs" lineHeight={ 5 }> <Box mt={ 6 } alignItems="start" textStyle="xs">
{ apiVersionUrl && ( { apiVersionUrl && (
<Text> <Text>
Backend: <Link href={ apiVersionUrl } target="_blank">{ backendVersionData?.backend_version }</Link> Backend: <Link href={ apiVersionUrl } target="_blank">{ backendVersionData?.backend_version }</Link>
...@@ -147,12 +149,12 @@ const Footer = () => { ...@@ -147,12 +149,12 @@ const Footer = () => {
</Box> </Box>
</Box> </Box>
); );
}, [ apiVersionUrl, backendVersionData?.backend_version, frontendLink, logoColor ]); }, [ apiVersionUrl, backendVersionData?.backend_version, frontendLink ]);
const containerProps: HTMLChakraProps<'div'> = { const containerProps: HTMLChakraProps<'div'> = {
as: 'footer', as: 'footer',
borderTopWidth: '1px', borderTopWidth: '1px',
borderTopColor: 'solid', borderTopColor: 'divider',
}; };
const contentProps: GridProps = { const contentProps: GridProps = {
...@@ -170,11 +172,11 @@ const Footer = () => { ...@@ -170,11 +172,11 @@ const Footer = () => {
} }
return ( return (
<Box gridArea={ gridArea } fontSize="xs" lineHeight={ 5 } mt={ 6 } color="text"> <Box gridArea={ gridArea } textStyle="xs" mt={ 6 }>
<span>This site is protected by reCAPTCHA and the Google </span> <span>This site is protected by reCAPTCHA and the Google </span>
<Link href="https://policies.google.com/privacy" isExternal>Privacy Policy</Link> <Link href="https://policies.google.com/privacy" target="_blank" rel="noopener noreferrer">Privacy Policy</Link>
<span> and </span> <span> and </span>
<Link href="https://policies.google.com/terms" isExternal>Terms of Service</Link> <Link href="https://policies.google.com/terms" target="_blank" rel="noopener noreferrer">Terms of Service</Link>
<span> apply.</span> <span> apply.</span>
</Box> </Box>
); );
...@@ -208,8 +210,8 @@ const Footer = () => { ...@@ -208,8 +210,8 @@ const Footer = () => {
.slice(0, colNum) .slice(0, colNum)
.map(linkGroup => ( .map(linkGroup => (
<Box key={ linkGroup.title }> <Box key={ linkGroup.title }>
<Skeleton fontWeight={ 500 } mb={ 3 } display="inline-block" isLoaded={ !isPlaceholderData }>{ linkGroup.title }</Skeleton> <Skeleton fontWeight={ 500 } mb={ 3 } display="inline-block" loading={ isPlaceholderData }>{ linkGroup.title }</Skeleton>
<VStack spacing={ 1 } alignItems="start"> <VStack gap={ 1 } alignItems="start">
{ linkGroup.links.map(link => <FooterLinkItem { ...link } key={ link.text } isLoading={ isPlaceholderData }/>) } { linkGroup.links.map(link => <FooterLinkItem { ...link } key={ link.text } isLoading={ isPlaceholderData }/>) }
</VStack> </VStack>
</Box> </Box>
......
import { Center, Link, Skeleton } from '@chakra-ui/react'; import { Center, Link } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { Skeleton } from 'toolkit/chakra/skeleton';
import type { IconName } from 'ui/shared/IconSvg'; import type { IconName } from 'ui/shared/IconSvg';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
...@@ -18,7 +19,7 @@ const FooterLinkItem = ({ icon, iconSize, text, url, isLoading }: Props) => { ...@@ -18,7 +19,7 @@ const FooterLinkItem = ({ icon, iconSize, text, url, isLoading }: Props) => {
} }
return ( return (
<Link href={ url } display="flex" alignItems="center" h="30px" variant="secondary" target="_blank" fontSize="xs"> <Link href={ url } display="flex" alignItems="center" h="30px" visual="subtle" target="_blank" textStyle="xs">
{ icon && ( { icon && (
<Center minW={ 6 } mr={ 2 }> <Center minW={ 6 } mr={ 2 }>
<IconSvg boxSize={ iconSize || 5 } name={ icon }/> <IconSvg boxSize={ iconSize || 5 } name={ icon }/>
......
import { IconButton, PopoverTrigger, PopoverContent, PopoverBody, Flex, Text, useColorModeValue } from '@chakra-ui/react'; import { Flex, Text } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
...@@ -9,15 +9,14 @@ import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery'; ...@@ -9,15 +9,14 @@ import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery';
import { apos, nbsp, ndash } from 'lib/html-entities'; import { apos, nbsp, ndash } from 'lib/html-entities';
import useSocketChannel from 'lib/socket/useSocketChannel'; import useSocketChannel from 'lib/socket/useSocketChannel';
import useSocketMessage from 'lib/socket/useSocketMessage'; import useSocketMessage from 'lib/socket/useSocketMessage';
import Popover from 'ui/shared/chakra/Popover'; import { Tooltip } from 'toolkit/chakra/tooltip';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
const IntTxsIndexingStatus = () => { const IntTxsIndexingStatus = () => {
const { data, isError, isPending } = useApiQuery('homepage_indexing_status'); const { data, isError, isPending } = useApiQuery('homepage_indexing_status');
const bgColor = useColorModeValue('blackAlpha.100', 'whiteAlpha.100'); const bgColor = { base: 'blackAlpha.100', _dark: 'whiteAlpha.100' };
const hintTextcolor = useColorModeValue('black', 'white');
const queryClient = useQueryClient(); const queryClient = useQueryClient();
...@@ -52,7 +51,7 @@ const IntTxsIndexingStatus = () => { ...@@ -52,7 +51,7 @@ const IntTxsIndexingStatus = () => {
} }
const hint = ( const hint = (
<Text fontSize="xs" color={ hintTextcolor }> <Text textStyle="xs">
{ data.indexed_internal_transactions_ratio && { data.indexed_internal_transactions_ratio &&
`${ Math.floor(Number(data.indexed_internal_transactions_ratio) * 100) }% Blocks With Internal Transactions Indexed${ nbsp }${ ndash } ` } `${ Math.floor(Number(data.indexed_internal_transactions_ratio) * 100) }% Blocks With Internal Transactions Indexed${ nbsp }${ ndash } ` }
We{ apos }re indexing this chain right now. Some of the counts may be inaccurate. We{ apos }re indexing this chain right now. Some of the counts may be inaccurate.
...@@ -67,18 +66,13 @@ const IntTxsIndexingStatus = () => { ...@@ -67,18 +66,13 @@ const IntTxsIndexingStatus = () => {
borderRadius="base" borderRadius="base"
alignItems="center" alignItems="center"
justifyContent="center" justifyContent="center"
columnGap={ 1 }
color="green.400" color="green.400"
_hover={{ color: 'blue.400' }} _hover={{ color: 'blue.400' }}
> >
<IconButton <IconSvg name="info" boxSize={ 5 }/>
colorScheme="none"
aria-label="hint"
icon={ <IconSvg name="info" boxSize={ 5 }/> }
boxSize={ 6 }
variant="simple"
/>
{ data.indexed_internal_transactions_ratio && ( { data.indexed_internal_transactions_ratio && (
<Text fontWeight={ 600 } fontSize="xs" color="inherit"> <Text fontWeight={ 600 } textStyle="xs" color="inherit">
{ Math.floor(Number(data.indexed_internal_transactions_ratio) * 100) + '%' } { Math.floor(Number(data.indexed_internal_transactions_ratio) * 100) + '%' }
</Text> </Text>
) } ) }
...@@ -86,16 +80,9 @@ const IntTxsIndexingStatus = () => { ...@@ -86,16 +80,9 @@ const IntTxsIndexingStatus = () => {
); );
return ( return (
<Popover placement="bottom-start" isLazy trigger="hover"> <Tooltip content={ hint } interactive positioning={{ placement: 'bottom-start' }} lazyMount>
<PopoverTrigger>
{ trigger } { trigger }
</PopoverTrigger> </Tooltip>
<PopoverContent maxH="450px" overflowY="hidden" w="240px">
<PopoverBody p={ 4 } bgColor={ bgColor } boxShadow="2xl">
{ hint }
</PopoverBody>
</PopoverContent>
</Popover>
); );
}; };
......
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