Commit 083fd33f authored by tom's avatar tom

home page refactoring: first part

parent 62423eec
......@@ -32,6 +32,8 @@ const RESTRICTED_MODULES = {
'Menu', 'useToast', 'useDisclosure', 'useClipboard', 'Tooltip', 'Skeleton', 'IconButton', 'Button',
'Image', 'Popover', 'PopoverTrigger', 'PopoverContent', 'PopoverBody', 'PopoverFooter',
'DrawerRoot', 'DrawerBody', 'DrawerContent', 'DrawerOverlay', 'DrawerBackdrop', 'DrawerTrigger', 'Drawer',
'Alert', 'AlertIcon', 'AlertTitle', 'AlertDescription',
'Heading', 'Badge',
],
message: 'Please use corresponding component or hook from ui/shared/chakra component instead',
},
......@@ -441,4 +443,13 @@ export default tseslint.config(
'no-restricted-properties': 'off',
},
},
{
files: [
'toolkit/chakra/**',
],
rules: {
// for toolkit components allow to import @chakra-ui/react directly
'no-restricted-imports': 'off',
},
},
);
......@@ -35,7 +35,7 @@ export const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
{ children ? (
<ChakraAlert.Content>
{ title && <ChakraAlert.Title>{ title }</ChakraAlert.Title> }
<ChakraAlert.Description>{ children }</ChakraAlert.Description>
<ChakraAlert.Description display="inline-flex">{ children }</ChakraAlert.Description>
</ChakraAlert.Content>
) : (
<ChakraAlert.Title flex="1">{ title }</ChakraAlert.Title>
......
import type { BadgeProps as ChakraBadgeProps } from '@chakra-ui/react';
import { Badge as ChakraBadge } from '@chakra-ui/react';
import React from 'react';
import { Skeleton } from './skeleton';
export interface BadgeProps extends ChakraBadgeProps {
loading?: boolean;
}
export const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(
function Badge(props, ref) {
const { loading, ...rest } = props;
return (
<Skeleton loading={ loading }>
<ChakraBadge ref={ ref } { ...rest }/>
</Skeleton>
);
});
/* eslint-disable no-restricted-imports */
import { Drawer as ChakraDrawer, Portal } from '@chakra-ui/react';
import * as React from 'react';
......@@ -50,7 +49,11 @@ export const DrawerRoot = (props: ChakraDrawer.RootProps) => {
return <ChakraDrawer.Root { ...rest } initialFocusEl={ initialFocusEl }/>;
};
export const DrawerTrigger = ChakraDrawer.Trigger;
export const DrawerTrigger = (props: ChakraDrawer.TriggerProps) => {
const { asChild = true, ...rest } = props;
return <ChakraDrawer.Trigger asChild={ asChild } { ...rest }/>;
};
export const DrawerFooter = ChakraDrawer.Footer;
export const DrawerHeader = ChakraDrawer.Header;
export const DrawerBody = ChakraDrawer.Body;
......
import type { HeadingProps as ChakraHeadingProps } from '@chakra-ui/react';
import { Heading as ChakraHeading } from '@chakra-ui/react';
import React from 'react';
export interface HeadingProps extends ChakraHeadingProps {
level?: '1' | '2' | '3';
}
export const Heading = React.forwardRef<HTMLHeadingElement, HeadingProps>(
function Heading(props, ref) {
const { level, ...rest } = props;
const textStyle = (() => {
if (level === '1') {
return { base: 'heading.md', lg: 'heading.xl' };
}
if (level === '2') {
return { base: 'heading.sm', lg: 'heading.lg' };
}
return { base: 'heading.xs', lg: 'heading.md' };
})();
const as = (() => {
if (level === '1') {
return 'h1';
}
if (level === '2') {
return 'h2';
}
return 'h3';
})();
return <ChakraHeading ref={ ref } color="heading" textStyle={ textStyle } as={ as } { ...rest }/>;
});
import { forwardRef } from 'react';
import React from 'react';
import { Button, type ButtonProps } from './button';
import { Skeleton } from './skeleton';
export interface IconButtonProps extends ButtonProps {}
export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
export const IconButton = React.forwardRef<HTMLDivElement, IconButtonProps>(
function IconButton(props, ref) {
const { loading, ...rest } = props;
return (
<Button
display="inline-flex"
px="0"
py="0"
height="auto"
minW="auto"
flexShrink="0"
ref={ ref }
{ ...props }
visual={ props.visual ?? 'plain' }
/>
<Skeleton loading={ loading } asChild ref={ ref }>
<Button
display="inline-flex"
px="0"
py="0"
height="auto"
minW="auto"
flexShrink="0"
{ ...rest }
visual={ props.visual ?? 'plain' }
/>
</Skeleton>
);
},
);
/* eslint-disable no-restricted-imports */
import { Popover as ChakraPopover, Portal } from '@chakra-ui/react';
import * as React from 'react';
......@@ -61,11 +60,14 @@ export const PopoverRoot = (props: ChakraPopover.RootProps) => {
...props.positioning?.offset,
},
};
const { lazyMount = true, unmountOnExit = true, ...rest } = props;
return (
<ChakraPopover.Root
autoFocus={ false }
{ ...props }
lazyMount={ lazyMount }
unmountOnExit={ unmountOnExit }
{ ...rest }
positioning={ positioning }
/>
);
......@@ -75,7 +77,7 @@ export const PopoverTrigger = React.forwardRef<
HTMLButtonElement,
ChakraPopover.TriggerProps
>(function PopoverTrigger(props, ref) {
return <ChakraPopover.Trigger as="div" display="flex" asChild ref={ ref } { ...props }/>;
return <ChakraPopover.Trigger asChild ref={ ref } { ...props }/>;
});
export const PopoverTitle = ChakraPopover.Title;
......
/* eslint-disable no-restricted-imports */
import { Tooltip as ChakraTooltip, Portal } from '@chakra-ui/react';
import { useClickAway } from '@uidotdev/usehooks';
import * as React from 'react';
......@@ -24,14 +23,17 @@ export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
selected,
children,
disabled,
portalled,
portalled = true,
content,
contentProps,
portalRef,
defaultOpen = false,
lazyMount = true,
unmountOnExit = true,
...rest
} = props;
const [ open, setOpen ] = React.useState(false);
const [ open, setOpen ] = React.useState(defaultOpen);
const isMobile = useIsMobile();
const triggerRef = useClickAway<HTMLButtonElement>(() => setOpen(false));
......@@ -66,12 +68,13 @@ export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
onOpenChange={ isMobile ? undefined : handleOpenChange }
closeOnClick={ false }
visual={ visual }
lazyMount={ lazyMount }
unmountOnExit={ unmountOnExit }
{ ...rest }
positioning={ positioning }
>
<ChakraTooltip.Trigger
ref={ triggerRef }
as="div"
asChild
onClick={ isMobile ? handleTriggerClick : undefined }
>
......
......@@ -179,6 +179,7 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
bg: {
info: { value: { _light: '{colors.blue.100}', _dark: '{colors.blue.900}' } },
warning: { value: { _light: '{colors.orange.100}', _dark: '{colors.orange.800/60}' } },
warning_table: { value: { _light: '{colors.orange.50}', _dark: '{colors.orange.800/60}' } },
success: { value: { _light: '{colors.green.100}', _dark: '{colors.green.900}' } },
error: { value: { _light: '{colors.red.100}', _dark: '{colors.red.900}' } },
neutral: { value: { _light: '{colors.blackAlpha.50}', _dark: '{colors.whiteAlpha.100}' } },
......@@ -239,6 +240,43 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
DEFAULT: { value: { _light: '{colors.blackAlpha.200}', _dark: '{colors.whiteAlpha.200}' } },
},
},
badge: {
gray: {
bg: { value: { _light: '{colors.blackAlpha.50}', _dark: '{colors.whiteAlpha.100}' } },
fg: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
},
green: {
bg: { value: { _light: '{colors.green.50}', _dark: '{colors.green.800}' } },
fg: { value: { _light: '{colors.green.500}', _dark: '{colors.green.200}' } },
},
red: {
bg: { value: { _light: '{colors.red.50}', _dark: '{colors.red.800}' } },
fg: { value: { _light: '{colors.red.500}', _dark: '{colors.red.200}' } },
},
purple: {
bg: { value: { _light: '{colors.purple.50}', _dark: '{colors.purple.800}' } },
fg: { value: { _light: '{colors.purple.500}', _dark: '{colors.purple.100}' } },
},
orange: {
bg: { value: { _light: '{colors.orange.50}', _dark: '{colors.orange.800}' } },
fg: { value: { _light: '{colors.orange.500}', _dark: '{colors.orange.100}' } },
},
blue: {
bg: { value: { _light: '{colors.blue.50}', _dark: '{colors.blue.800}' } },
fg: { value: { _light: '{colors.blue.500}', _dark: '{colors.blue.100}' } },
},
yellow: {
bg: { value: { _light: '{colors.yellow.50}', _dark: '{colors.yellow.800}' } },
fg: { value: { _light: '{colors.yellow.500}', _dark: '{colors.yellow.100}' } },
},
teal: {
bg: { value: { _light: '{colors.teal.50}', _dark: '{colors.teal.800}' } },
fg: { value: { _light: '{colors.teal.500}', _dark: '{colors.teal.100}' } },
},
},
heading: {
DEFAULT: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
},
text: {
primary: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
secondary: { value: { _light: '{colors.gray.500}', _dark: '{colors.gray.400}' } },
......
......@@ -16,38 +16,45 @@ export const textStyles: ThemingConfig['textStyles'] = {
heading: {
xl: {
value: {
fontSize: '40px',
lineHeight: '48px',
fontSize: '32px',
lineHeight: '40px',
fontWeight: '500',
letterSpacing: '-1px',
letterSpacing: '-0.5px',
fontFamily: 'heading',
},
},
lg: {
value: {
fontSize: '32px',
lineHeight: '40px',
fontSize: '24px',
lineHeight: '32px',
fontWeight: '500',
letterSpacing: '-0.5px',
fontFamily: 'heading',
},
},
md: {
value: {
fontSize: '24px',
lineHeight: '32px',
fontSize: '18px',
lineHeight: '24px',
fontWeight: '500',
fontFamily: 'heading',
},
},
sm: {
value: {
fontSize: '18px',
fontSize: '16px',
lineHeight: '24px',
fontWeight: '500',
fontFamily: 'heading',
},
},
xs: {
value: {
fontSize: '14px',
lineHeight: '20px',
fontWeight: '600',
fontFamily: 'heading',
},
},
},
text: {
md: {
......
......@@ -14,6 +14,7 @@ export const zIndex: TokenDefinition['zIndex'] = {
modal: { value: 1400 },
popover: { value: 1500 },
tooltip: { value: 1550 }, // otherwise tooltips will not be visible in modals
tooltip2: { value: 1551 }, // for tooltips in tooltips
skipLink: { value: 1600 },
toast: { value: 1700 },
};
......
......@@ -42,6 +42,9 @@ export const recipe = defineSlotRecipe({
warning: {
root: { bg: 'alert.bg.warning', color: 'alert.fg' },
},
warning_table: {
root: { bg: 'alert.bg.warning_table', color: 'alert.fg' },
},
success: {
root: { bg: 'alert.bg.success', color: 'alert.fg' },
},
......@@ -82,7 +85,7 @@ export const recipe = defineSlotRecipe({
},
indicator: {
boxSize: '5',
mt: '2px',
my: '2px',
},
},
},
......
import { defineRecipe } from '@chakra-ui/react';
export const recipe = defineRecipe({
base: {
display: 'inline-flex',
alignItems: 'center',
borderRadius: 'sm',
gap: '1',
fontWeight: '500',
whiteSpace: 'nowrap',
userSelect: 'none',
},
variants: {
variant: {
subtle: {},
},
colorPalette: {
gray: {
bg: 'badge.gray.bg',
color: 'badge.gray.fg',
},
green: {
bg: 'badge.green.bg',
color: 'badge.green.fg',
},
red: {
bg: 'badge.red.bg',
color: 'badge.red.fg',
},
purple: {
bg: 'badge.purple.bg',
color: 'badge.purple.fg',
},
orange: {
bg: 'badge.orange.bg',
color: 'badge.orange.fg',
},
blue: {
bg: 'badge.blue.bg',
color: 'badge.blue.fg',
},
yellow: {
bg: 'badge.yellow.bg',
color: 'badge.yellow.fg',
},
teal: {
bg: 'badge.teal.bg',
color: 'badge.teal.fg',
},
},
size: {
md: {
textStyle: 'sm',
px: '1',
py: '0.5',
minH: '6',
},
},
},
defaultVariants: {
variant: 'subtle',
colorPalette: 'gray',
size: 'md',
},
});
......@@ -124,7 +124,6 @@ export const recipe = defineSlotRecipe({
// anyway it will override the minH from the "full" size variant
// minH: 'auto',
maxH: 'calc(100% - 7.5rem)',
borderRadius: 'xl',
},
body: {
overflow: 'auto',
......
import { recipe as alert } from './alert.recipe';
import { recipe as badge } from './badge.recipe';
import { recipe as button } from './button.recipe';
import { recipe as closeButton } from './close-button.recipe';
import { recipe as dialog } from './dialog.recipe';
......@@ -20,6 +21,7 @@ import { recipe as toast } from './toast.recipe';
import { recipe as tooltip } from './tooltip.recipe';
export const recipes = {
badge,
button,
closeButton,
input,
......
// we use custom heading size for hero banner
// eslint-disable-next-line no-restricted-imports
import { Box, Flex, Heading } from '@chakra-ui/react';
import React from 'react';
......
......@@ -12,8 +12,8 @@ import type { Transaction } from 'types/api/transaction';
import config from 'configs/app';
import getValueWithUnit from 'lib/getValueWithUnit';
import { currencyUnits } from 'lib/units';
import { Skeleton } from 'toolkit/chakra/skeleton';
import AddressFromTo from 'ui/shared/address/AddressFromTo';
import Skeleton from 'ui/shared/chakra/Skeleton';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TxStatus from 'ui/shared/statusTag/TxStatus';
import TimeAgoWithTooltip from 'ui/shared/TimeAgoWithTooltip';
......@@ -68,9 +68,8 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => {
timestamp={ tx.timestamp }
enableIncrement
isLoading={ isLoading }
color="text_secondary"
fontWeight="400"
fontSize="sm"
color="text.secondary"
textStyle="sm"
flexShrink={ 0 }
ml={ 2 }
/>
......@@ -83,17 +82,17 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => {
isLoading={ isLoading }
mode="compact"
/>
<Flex flexDir="column">
<Flex flexDir="column" rowGap={ 3 }>
{ !config.UI.views.tx.hiddenFields?.value && (
<Skeleton isLoaded={ !isLoading } my="3px">
<Skeleton loading={ isLoading } textStyle="md">
<Text as="span" whiteSpace="pre">Value </Text>
<Text as="span" variant="secondary">{ getValueWithUnit(tx.value).dp(5).toFormat() } { currencyUnits.ether }</Text>
<Text as="span" color="text.secondary">{ getValueWithUnit(tx.value).dp(5).toFormat() } { currencyUnits.ether }</Text>
</Skeleton>
) }
{ !config.UI.views.tx.hiddenFields?.tx_fee && (
<Skeleton isLoaded={ !isLoading } display="flex" whiteSpace="pre" my="3px">
<Skeleton loading={ isLoading } display="flex" whiteSpace="pre" textStyle="md">
<Text as="span">Fee </Text>
<TxFee tx={ tx } accuracy={ 5 } color="text_secondary"/>
<TxFee tx={ tx } accuracy={ 5 } color="text.secondary"/>
</Skeleton>
) }
</Flex>
......
......@@ -11,8 +11,8 @@ import type { Transaction } from 'types/api/transaction';
import config from 'configs/app';
import getValueWithUnit from 'lib/getValueWithUnit';
import { currencyUnits } from 'lib/units';
import { Skeleton } from 'toolkit/chakra/skeleton';
import AddressFromTo from 'ui/shared/address/AddressFromTo';
import Skeleton from 'ui/shared/chakra/Skeleton';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import TxStatus from 'ui/shared/statusTag/TxStatus';
import TimeAgoWithTooltip from 'ui/shared/TimeAgoWithTooltip';
......@@ -78,15 +78,15 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => {
mb={ 3 }
/>
{ !config.UI.views.tx.hiddenFields?.value && (
<Skeleton isLoaded={ !isLoading } mb={ 2 } fontSize="sm" w="fit-content">
<Skeleton loading={ isLoading } mb={ 2 } fontSize="sm" w="fit-content">
<Text as="span">Value </Text>
<Text as="span" variant="secondary">{ getValueWithUnit(tx.value).dp(5).toFormat() } { currencyUnits.ether }</Text>
<Text as="span" color="text.secondary">{ getValueWithUnit(tx.value).dp(5).toFormat() } { currencyUnits.ether }</Text>
</Skeleton>
) }
{ !config.UI.views.tx.hiddenFields?.tx_fee && (
<Skeleton isLoaded={ !isLoading } fontSize="sm" w="fit-content" display="flex" whiteSpace="pre">
<Skeleton loading={ isLoading } fontSize="sm" w="fit-content" display="flex" whiteSpace="pre">
<Text as="span">Fee </Text>
<TxFee tx={ tx } accuracy={ 5 } color="text_secondary"/>
<TxFee tx={ tx } accuracy={ 5 } color="text.secondary"/>
</Skeleton>
) }
</Box>
......
import { Heading } from '@chakra-ui/react';
import React from 'react';
import config from 'configs/app';
import { Heading } from 'toolkit/chakra/heading';
import LatestOptimisticDeposits from 'ui/home/latestDeposits/LatestOptimisticDeposits';
import LatestTxs from 'ui/home/LatestTxs';
import LatestWatchlistTxs from 'ui/home/LatestWatchlistTxs';
......@@ -29,7 +29,7 @@ const TransactionsHome = () => {
].filter(Boolean);
return (
<>
<Heading as="h4" size="sm" mb={ 3 }>Transactions</Heading>
<Heading level="3" mb={ 3 }>Transactions</Heading>
<TabsWithScroll tabs={ tabs } lazyBehavior="keepMounted" tabListProps={ TAB_LIST_PROPS }/>
</>
);
......@@ -37,7 +37,7 @@ const TransactionsHome = () => {
return (
<>
<Heading as="h4" size="sm" mb={ 3 }>Latest transactions</Heading>
<Heading level="3" mb={ 3 }>Latest transactions</Heading>
<LatestTxs/>
</>
);
......
......@@ -45,7 +45,7 @@ const ChainIndicatorChart = ({ data }: Props) => {
data={ data[0].items }
xScale={ axes.x.scale }
yScale={ axes.y.scale }
stroke={ lineColor }
stroke={ lineColor[0] }
animation="left"
strokeWidth={ 3 }
/>
......
import { Text, Flex, Box, useColorModeValue } from '@chakra-ui/react';
import { Text, Flex, Box } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query';
import React from 'react';
......@@ -6,7 +6,7 @@ import type { HomeStats } from 'types/api/stats';
import type { ChainIndicatorId } from 'types/homepage';
import type { ResourceError } from 'lib/api/resources';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Skeleton } from 'toolkit/chakra/skeleton';
interface Props {
id: ChainIndicatorId;
......@@ -20,20 +20,17 @@ interface Props {
}
const ChainIndicatorItem = ({ id, title, value, valueDiff, icon, isSelected, onClick, stats }: Props) => {
const activeColor = useColorModeValue('gray.500', 'gray.400');
const activeBgColor = useColorModeValue('white', 'black');
const handleClick = React.useCallback(() => {
onClick(id);
}, [ id, onClick ]);
const valueContent = (() => {
if (!stats.data) {
return <Text variant="secondary" fontWeight={ 400 }>no data</Text>;
return <Text color="text.secondary" fontWeight={ 400 }>no data</Text>;
}
return (
<Skeleton isLoaded={ !stats.isPlaceholderData } variant="secondary" fontWeight={ 600 } minW="30px">
<Skeleton loading={ stats.isPlaceholderData } fontWeight={ 600 } minW="30px">
{ value(stats.data) }
</Skeleton>
);
......@@ -51,7 +48,7 @@ const ChainIndicatorItem = ({ id, title, value, valueDiff, icon, isSelected, onC
const diffColor = diff >= 0 ? 'green.500' : 'red.500';
return (
<Skeleton isLoaded={ !stats.isPlaceholderData } ml={ 1 } display="flex" alignItems="center" color={ diffColor }>
<Skeleton loading={ stats.isPlaceholderData } ml={ 1 } display="flex" alignItems="center" color={ diffColor }>
<span>{ diff >= 0 ? '+' : '-' }</span>
<Text color={ diffColor } fontWeight={ 600 }>{ Math.abs(diff) }%</Text>
</Skeleton>
......@@ -68,14 +65,14 @@ const ChainIndicatorItem = ({ id, title, value, valueDiff, icon, isSelected, onC
as="li"
borderRadius="base"
cursor="pointer"
color={ isSelected ? activeColor : 'link' }
bgColor={ isSelected ? activeBgColor : undefined }
color={ isSelected ? { _light: 'gray.500', _dark: 'gray.400' } : 'link' }
bgColor={ isSelected ? { _light: 'white', _dark: 'black' } : undefined }
onClick={ handleClick }
fontSize="xs"
fontWeight={ 500 }
_hover={{
bgColor: activeBgColor,
color: isSelected ? activeColor : 'link_hovered',
bgColor: { _light: 'white', _dark: 'black' },
color: isSelected ? { _light: 'gray.500', _dark: 'gray.400' } : 'link.primary.hover',
zIndex: 1,
}}
>
......
import { Flex, Text, useColorModeValue } from '@chakra-ui/react';
import { Flex, Text } from '@chakra-ui/react';
import React from 'react';
import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery';
import { HOMEPAGE_STATS } from 'stubs/stats';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Skeleton } from 'toolkit/chakra/skeleton';
import Hint from 'ui/shared/Hint';
import IconSvg from 'ui/shared/IconSvg';
......@@ -39,8 +39,6 @@ const ChainIndicators = () => {
},
});
const bgColor = useColorModeValue('gray.50', 'whiteAlpha.100');
if (indicators.length === 0) {
return null;
}
......@@ -74,7 +72,7 @@ const ChainIndicators = () => {
const diffColor = diff >= 0 ? 'green.500' : 'red.500';
return (
<Skeleton isLoaded={ !statsQueryResult.isPlaceholderData } display="flex" alignItems="center" color={ diffColor } ml={ 2 }>
<Skeleton loading={ statsQueryResult.isPlaceholderData } display="flex" alignItems="center" color={ diffColor } ml={ 2 }>
<IconSvg name="arrows/up-head" boxSize={ 5 } mr={ 1 } transform={ diff < 0 ? 'rotate(180deg)' : 'rotate(0)' }/>
<Text color={ diffColor } fontWeight={ 600 }>{ diff }%</Text>
</Skeleton>
......@@ -86,7 +84,7 @@ const ChainIndicators = () => {
px={{ base: 3, lg: 4 }}
py={ 3 }
borderRadius="base"
bgColor={ bgColor }
bgColor={{ _light: 'gray.50', _dark: 'whiteAlpha.100' }}
columnGap={{ base: 3, lg: 4 }}
rowGap={ 0 }
flexBasis="50%"
......
/* eslint-disable max-len */
/* eslint-disable react/jsx-no-bind */
import { createListCollection, Heading, HStack, Link, Spinner, Tabs, VStack } from '@chakra-ui/react';
import { createListCollection, HStack, Link, Spinner, Tabs, VStack } from '@chakra-ui/react';
import React from 'react';
import { Alert } from 'toolkit/chakra/alert';
import { Badge } from 'toolkit/chakra/badge';
import { Button } from 'toolkit/chakra/button';
import { useColorMode } from 'toolkit/chakra/color-mode';
import { Field } from 'toolkit/chakra/field';
import { Heading } from 'toolkit/chakra/heading';
import { Input } from 'toolkit/chakra/input';
import { InputGroup } from 'toolkit/chakra/input-group';
import { NativeSelectField, NativeSelectRoot } from 'toolkit/chakra/native-select';
......@@ -224,6 +226,20 @@ const ChakraShowcases = () => {
</HStack>
</section>
<section>
<Heading textStyle="heading.md" mb={ 2 }>Badges</Heading>
<HStack gap={ 4 } whiteSpace="nowrap" flexWrap="wrap">
<Badge colorPalette="gray">Gray</Badge>
<Badge colorPalette="green">Green</Badge>
<Badge colorPalette="red">Red</Badge>
<Badge colorPalette="purple">Purple</Badge>
<Badge colorPalette="orange">Orange</Badge>
<Badge colorPalette="blue">Blue</Badge>
<Badge colorPalette="yellow">Yellow</Badge>
<Badge colorPalette="teal">Teal</Badge>
</HStack>
</section>
<section>
<Heading textStyle="heading.md" mb={ 2 }>Toasts</Heading>
<HStack gap={ 4 } whiteSpace="nowrap">
......
......@@ -31,17 +31,17 @@ const Home = () => {
return (
<Box as="main">
<HeroBanner/>
{ /* <Flex flexDir={{ base: 'column', lg: 'row' }} columnGap={ 2 } rowGap={ 1 } mt={ 3 } _empty={{ mt: 0 }}>
<Flex flexDir={{ base: 'column', lg: 'row' }} columnGap={ 2 } rowGap={ 1 } mt={ 3 } _empty={{ mt: 0 }}>
<Stats/>
<ChainIndicators/>
</Flex>
<AdBanner mt={ 6 } mx="auto" display={{ base: 'flex', lg: 'none' }} justifyContent="center"/>
<Flex mt={ 8 } direction={{ base: 'column', lg: 'row' }} columnGap={ 12 } rowGap={ 6 }>
{ leftWidget }
{ /* { leftWidget } */ }
<Box flexGrow={ 1 }>
<Transactions/>
</Box>
</Flex> */ }
</Flex>
</Box>
);
};
......
import {
useColorModeValue,
chakra,
Button,
} from '@chakra-ui/react';
import { chakra } from '@chakra-ui/react';
import React from 'react';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { IconButton } from 'toolkit/chakra/icon-button';
import IconSvg from 'ui/shared/IconSvg';
interface Props {
isOpen?: boolean;
isLoading?: boolean;
loading?: boolean;
className?: string;
onClick?: () => void;
}
const AdditionalInfoButton = ({ isOpen, onClick, className, isLoading }: Props, ref: React.ForwardedRef<HTMLButtonElement>) => {
const infoBgColor = useColorModeValue('blue.50', 'gray.600');
if (isLoading) {
return <Skeleton boxSize={ 6 } borderRadius="sm" flexShrink={ 0 }/>;
}
const AdditionalInfoButton = (props: Props, ref: React.ForwardedRef<HTMLButtonElement>) => {
return (
<Button
variant="unstyled"
display="inline-flex"
alignItems="center"
className={ className }
<IconButton
ref={ ref }
background={ isOpen ? infoBgColor : 'unset' }
borderRadius="8px"
w="24px"
h="24px"
onClick={ onClick }
cursor="pointer"
flexShrink={ 0 }
color="icon_info"
_hover={{ color: 'link.primary.hover' }}
_open={{
bgColor: { _light: 'blue.50', _dark: 'gray.600' },
color: 'link.primary.hover',
}}
borderRadius="base"
aria-label="Transaction info"
boxSize={ 6 }
{ ...props }
>
<IconSvg
name="info"
boxSize={ 5 }
color={ isOpen ? 'link_hovered' : 'icon_info' }
_hover={{ color: 'link_hovered' }}
/>
</Button>
<IconSvg name="info" boxSize={ 5 }/>
</IconButton>
);
};
......
......@@ -54,6 +54,9 @@ const CopyToClipboard = ({ text, className, isLoading, onClick, size = 5, type,
return (
<Tooltip
content={ hasCopied ? 'Copied' : `Copy${ type === 'link' ? ' link ' : ' ' }to clipboard` }
contentProps={{
zIndex: 'tooltip2',
}}
// open={ hasCopied }
>
<IconButton
......
......@@ -2,10 +2,10 @@ import { chakra } from '@chakra-ui/react';
import React from 'react';
import getCurrencyValue from 'lib/getCurrencyValue';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Skeleton } from 'toolkit/chakra/skeleton';
interface Props {
value: string;
value: string | null;
currency?: string;
exchangeRate?: string | null;
className?: string;
......
import { Alert, AlertDescription, chakra } from '@chakra-ui/react';
import { chakra } from '@chakra-ui/react';
import React from 'react';
import { Alert } from 'toolkit/chakra/alert';
const DataFetchAlert = ({ className }: { className?: string }) => {
return (
<Alert status="warning" width="fit-content" className={ className }>
<AlertDescription>
Something went wrong. Try refreshing the page or come back later.
</AlertDescription>
Something went wrong. Try refreshing the page or come back later.
</Alert>
);
};
......
......@@ -37,7 +37,6 @@ const Hint = ({ label, className, tooltipProps, isLoading }: Props) => {
<IconButton
aria-label="hint"
boxSize={ 5 }
display="inline-block"
className={ className }
// onMouseEnter={ onOpen }
// onMouseLeave={ onClose }
......
import { Heading, Flex, Link, chakra } from '@chakra-ui/react';
import { Flex, Link, chakra } from '@chakra-ui/react';
import { debounce } from 'es-toolkit';
import React from 'react';
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import { Tooltip } from 'toolkit/chakra/tooltip';
import useIsMobile from 'lib/hooks/useIsMobile';
import { Heading } from 'toolkit/chakra/heading';
import { Tooltip } from 'toolkit/chakra/tooltip';
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import TextAd from 'ui/shared/ad/TextAd';
import Skeleton from 'ui/shared/chakra/Skeleton';
import IconSvg from 'ui/shared/IconSvg';
......@@ -139,7 +140,7 @@ const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoa
<Heading
ref={ headingRef }
as="h1"
textStyle="heading.lg"
level="1"
whiteSpace="normal"
wordBreak="break-all"
style={{
......
import { Alert, Link, Text, chakra, useTheme, useColorModeValue, Tr, Td } from '@chakra-ui/react';
import { transparentize } from '@chakra-ui/theme-tools';
import { Link, Text, chakra, Table } from '@chakra-ui/react';
import React from 'react';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Alert } from 'toolkit/chakra/alert';
import { Skeleton } from 'toolkit/chakra/skeleton';
interface InjectedProps {
content: React.ReactNode;
......@@ -19,8 +19,6 @@ interface Props {
}
const SocketNewItemsNotice = chakra(({ children, className, url, num, alert, type = 'transaction', isLoading }: Props) => {
const theme = useTheme();
const alertContent = (() => {
if (alert) {
return alert;
......@@ -55,24 +53,18 @@ const SocketNewItemsNotice = chakra(({ children, className, url, num, alert, typ
);
})();
const color = useColorModeValue('blackAlpha.800', 'whiteAlpha.800');
const bgColor = useColorModeValue('orange.50', transparentize('orange.200', 0.16)(theme));
const content = !isLoading ? (
<Alert
className={ className }
status="warning"
visual="warning_table"
px={ 4 }
py="6px"
fontWeight={ 400 }
fontSize="sm"
lineHeight={ 5 }
bgColor={ bgColor }
color={ color }
startElement={ null }
>
{ alertContent }
</Alert>
) : <Skeleton className={ className } h="33px"/>;
) : <Skeleton className={ className } h="36px" loading/>;
return children ? children({ content }) : content;
});
......@@ -90,7 +82,7 @@ export const Desktop = ({ ...props }: Props) => {
my={ props.isLoading ? '6px' : 0 }
{ ...props }
>
{ ({ content }) => <Tr><Td colSpan={ 100 } p={ 0 } _first={{ p: 0 }} _last={{ p: 0 }}>{ content }</Td></Tr> }
{ ({ content }) => <Table.Row><Table.Cell colSpan={ 100 } p={ 0 } _first={{ p: 0 }} _last={{ p: 0 }}>{ content }</Table.Cell></Table.Row> }
</SocketNewItemsNotice>
);
};
......
import { Tooltip, chakra } from '@chakra-ui/react';
import { chakra } from '@chakra-ui/react';
import React from 'react';
import dayjs from 'lib/date/dayjs';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { Tooltip } from 'toolkit/chakra/tooltip';
type Props = {
timestamp?: string | number | null;
......@@ -20,11 +21,11 @@ const TimeAgoWithTooltip = ({ timestamp, fallbackText, isLoading, enableIncremen
}
const content = timestamp ?
<Tooltip label={ dayjs(timestamp).format('llll') }><span>{ timeAgo }</span></Tooltip> :
<Tooltip content={ dayjs(timestamp).format('llll') }><span>{ timeAgo }</span></Tooltip> :
<span>{ fallbackText }</span>;
return (
<Skeleton isLoaded={ !isLoading } className={ className }>
<Skeleton loading={ isLoading } className={ className }>
{ content }
</Skeleton>
);
......
import { Box, Flex, chakra, useColorModeValue } from '@chakra-ui/react';
import { Box, Flex, chakra } from '@chakra-ui/react';
import { clamp } from 'es-toolkit';
import React from 'react';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Skeleton } from 'toolkit/chakra/skeleton';
interface Props {
className?: string;
value: number;
......@@ -14,17 +15,17 @@ const WIDTH = 50;
const Utilization = ({ className, value, colorScheme = 'green', isLoading }: Props) => {
const valueString = (clamp(value * 100 || 0, 0, 100)).toLocaleString(undefined, { maximumFractionDigits: 2 }) + '%';
const colorGrayScheme = useColorModeValue('gray.500', 'gray.400');
const colorGrayScheme = { _light: 'gray.500', _dark: 'gray.400' };
const color = colorScheme === 'gray' ? colorGrayScheme : 'green.500';
return (
<Flex className={ className } alignItems="center" columnGap={ 2 }>
<Skeleton isLoaded={ !isLoading } w={ `${ WIDTH }px` } h="4px" borderRadius="full" overflow="hidden">
<Box bg={ useColorModeValue('blackAlpha.200', 'whiteAlpha.200') } h="100%">
<Skeleton loading={ isLoading } w={ `${ WIDTH }px` } h="4px" borderRadius="full" overflow="hidden">
<Box bg={{ _light: 'blackAlpha.200', _dark: 'whiteAlpha.200' }} h="100%">
<Box bg={ color } w={ valueString } h="100%"/>
</Box>
</Skeleton>
<Skeleton isLoaded={ !isLoading } color={ color } fontWeight="bold">
<Skeleton loading={ isLoading } color={ color } fontWeight="bold">
<span>
{ valueString }
</span>
......
import type { ThemeTypings } from '@chakra-ui/react';
import type { ConditionalValue } from '@chakra-ui/react';
import { Flex, Grid, chakra, useBreakpointValue } from '@chakra-ui/react';
import React from 'react';
......@@ -17,7 +17,7 @@ interface Props {
from: AddressParam;
to: AddressParam | null;
current?: string;
mode?: Mode | Partial<Record<ThemeTypings['breakpoints'], Mode>>;
mode?: Mode | ConditionalValue<Mode>;
className?: string;
isLoading?: boolean;
tokenHash?: string;
......@@ -28,9 +28,9 @@ interface Props {
const AddressFromTo = ({ from, to, current, mode: modeProp, className, isLoading, tokenHash = '', noIcon }: Props) => {
const mode = useBreakpointValue(
{
base: (typeof modeProp === 'object' ? modeProp.base : modeProp),
lg: (typeof modeProp === 'object' ? modeProp.lg : modeProp),
xl: (typeof modeProp === 'object' ? modeProp.xl : modeProp),
base: (typeof modeProp === 'object' && 'base' in modeProp ? modeProp.base : modeProp),
lg: (typeof modeProp === 'object' && 'lg' in modeProp ? modeProp.lg : modeProp),
xl: (typeof modeProp === 'object' && 'xl' in modeProp ? modeProp.xl : modeProp),
},
) ?? 'long';
......
import { Tooltip, chakra, useColorModeValue } from '@chakra-ui/react';
import { chakra } from '@chakra-ui/react';
import React from 'react';
import { Tooltip } from 'toolkit/chakra/tooltip';
import IconSvg from 'ui/shared/IconSvg';
import type { TxCourseType } from './utils';
......@@ -14,19 +15,19 @@ interface Props {
const AddressFromToIcon = ({ isLoading, type, className }: Props) => {
const styles = {
'in': {
color: useColorModeValue('green.500', 'green.200'),
bgColor: useColorModeValue('green.50', 'green.800'),
color: { _light: 'green.500', _dark: 'green.200' },
bgColor: { _light: 'green.50', _dark: 'green.800' },
},
out: {
color: useColorModeValue('yellow.600', 'yellow.500'),
bgColor: useColorModeValue('orange.50', 'yellow.900'),
color: { _light: 'yellow.600', _dark: 'yellow.500' },
bgColor: { _light: 'orange.50', _dark: 'yellow.900' },
},
self: {
color: useColorModeValue('blackAlpha.400', 'whiteAlpha.400'),
bgColor: useColorModeValue('blackAlpha.50', 'whiteAlpha.50'),
color: { _light: 'blackAlpha.400', _dark: 'whiteAlpha.400' },
bgColor: { _light: 'blackAlpha.50', _dark: 'whiteAlpha.50' },
},
unspecified: {
color: useColorModeValue('gray.500', 'gray.300'),
color: { _light: 'gray.500', _dark: 'gray.300' },
bgColor: 'transparent',
},
};
......@@ -53,7 +54,7 @@ const AddressFromToIcon = ({ isLoading, type, className }: Props) => {
}
return (
<Tooltip label={ labels[type] }>
<Tooltip content={ labels[type] }>
{ icon }
</Tooltip>
);
......
import { useColorModeValue, useToken, useTheme } from '@chakra-ui/react';
import { transparentize } from '@chakra-ui/theme-tools';
import { useToken } from '@chakra-ui/react';
import * as d3 from 'd3';
import React from 'react';
import type { TimeChartItem } from 'ui/shared/chart/types';
import { useColorModeValue } from 'toolkit/chakra/color-mode';
interface Props extends React.SVGProps<SVGPathElement> {
id?: string;
xScale: d3.ScaleTime<number, number> | d3.ScaleLinear<number, number>;
......@@ -16,13 +17,12 @@ interface Props extends React.SVGProps<SVGPathElement> {
const ChartArea = ({ id, xScale, yScale, color, data, noAnimation, ...props }: Props) => {
const ref = React.useRef(null);
const theme = useTheme();
const gradientColorId = `${ id || 'gradient' }-${ color }-color`;
const gradientStopColor = useToken('colors', useColorModeValue('whiteAlpha.200', 'blackAlpha.100'));
const defaultGradient = {
startColor: useToken('colors', useColorModeValue('blue.100', 'blue.400')),
stopColor: useToken('colors', transparentize(useColorModeValue('blue.100', 'blue.400'), 0)(theme)),
stopColor: useToken('colors', useColorModeValue('rgba(190, 227, 248, 0)', 'rgba(66, 153, 225, 0)')), // blue.100 - blue.400 with 0 opacity
};
React.useEffect(() => {
......@@ -60,14 +60,14 @@ const ChartArea = ({ id, xScale, yScale, color, data, noAnimation, ...props }: P
<defs>
<linearGradient id={ `${ gradientColorId }` } x1="0%" x2="0%" y1="0%" y2="100%">
<stop offset="0%" stopColor={ color }/>
<stop offset="100%" stopColor={ gradientStopColor }/>
<stop offset="100%" stopColor={ gradientStopColor[0] }/>
</linearGradient>
</defs>
) : (
<defs>
<linearGradient id="gradient-chart-area-default" x1="0%" x2="0%" y1="0%" y2="100%">
<stop offset="0%" stopColor={ defaultGradient.startColor }/>
<stop offset="100%" stopColor={ defaultGradient.stopColor }/>
<stop offset="0%" stopColor={ defaultGradient.startColor[0] }/>
<stop offset="100%" stopColor={ defaultGradient.stopColor[0] }/>
</linearGradient>
</defs>
) }
......
......@@ -155,8 +155,10 @@ const ChartTooltip = ({
<g
ref={ ref }
opacity={ 0 }
fontSize="12px"
fontWeight="500"
style={{
fontWeight: '500',
fontSize: '12px',
}}
{ ...props }
>
<ChartTooltipLine/>
......
......@@ -12,7 +12,7 @@ const ChartTooltipBackdrop = () => {
className="ChartTooltip__backdrop"
rx={ 12 }
ry={ 12 }
fill={ bgColor }
fill={ bgColor[0] }
/>
);
};
......
......@@ -4,7 +4,7 @@ import React from 'react';
const ChartTooltipLine = () => {
const lineColor = useToken('colors', 'gray.400');
return <line className="ChartTooltip__line" stroke={ lineColor } strokeDasharray="3"/>;
return <line className="ChartTooltip__line" stroke={ lineColor[0] } strokeDasharray="3"/>;
};
export default React.memo(ChartTooltipLine);
......
import { useColorModeValue, useToken } from '@chakra-ui/react';
import { useToken } from '@chakra-ui/react';
import * as d3 from 'd3';
import React from 'react';
import type { TimeChartData, TimeChartItem } from 'ui/shared/chart/types';
import { useColorModeValue } from 'toolkit/chakra/color-mode';
import { POINT_SIZE } from './utils';
const ChartTooltipPoint = () => {
......@@ -15,8 +17,8 @@ const ChartTooltipPoint = () => {
className="ChartTooltip__point"
r={ POINT_SIZE / 2 }
opacity={ 1 }
fill={ bgColor }
stroke={ borderColor }
fill={ bgColor[0] }
stroke={ borderColor[0] }
strokeWidth={ 4 }
/>
);
......
......@@ -23,7 +23,7 @@ const ChartTooltipRow = ({ label, lineNum, children }: Props) => {
className="ChartTooltip__label"
transform="translate(0,0)"
dominantBaseline="hanging"
fill={ labelColor }
fill={ labelColor[0] }
>
{ label }
</text>
......@@ -31,7 +31,7 @@ const ChartTooltipRow = ({ label, lineNum, children }: Props) => {
className="ChartTooltip__value"
transform={ `translate(${ LABEL_WIDTH },0)` }
dominantBaseline="hanging"
fill={ textColor }
fill={ textColor[0] }
/>
</>
) }
......
......@@ -17,7 +17,7 @@ const ChartTooltipTitle = ({ resolution = Resolution.DAY }: { resolution?: Resol
<text
className="ChartTooltip__title"
transform="translate(0,0)"
fill={ titleColor }
fill={ titleColor[0] }
opacity={ 0 }
dominantBaseline="hanging"
>
......
......@@ -115,7 +115,7 @@ const Content = chakra((props: ContentProps) => {
return (
<Tooltip content={ label } contentProps={{ maxW: { base: 'calc(100vw - 8px)', lg: '400px' } }}>
<Skeleton loading={ props.isLoading } overflow="hidden" textOverflow="ellipsis" whiteSpace="nowrap" as="span">
<Skeleton loading={ props.isLoading } overflow="hidden" textOverflow="ellipsis" whiteSpace="nowrap">
{ nameText }
</Skeleton>
</Tooltip>
......@@ -160,6 +160,8 @@ const AddressEntry = (props: EntityProps) => {
const settingsContext = useSettingsContext();
const altHash = !props.noAltHash && settingsContext?.addressFormat === 'bech32' ? toBech32Address(props.address.hash) : undefined;
const content = <Content { ...partsProps.content } altHash={ altHash }/>;
return (
<Container
// we have to use the global classnames here, see theme/global.ts
......@@ -172,9 +174,7 @@ const AddressEntry = (props: EntityProps) => {
zIndex={ 0 }
>
<Icon { ...partsProps.icon }/>
<Link { ...partsProps.link }>
<Content { ...partsProps.content } altHash={ altHash }/>
</Link>
{ props.noLink ? content : (<Link { ...partsProps.link }> { content } </Link>) }
<Copy { ...partsProps.copy } altHash={ altHash }/>
</Container>
);
......
import { Box, DarkMode, PopoverBody, PopoverContent, PopoverTrigger, Portal, useColorModeValue, Flex, PopoverArrow } from '@chakra-ui/react';
import { Box, Flex } from '@chakra-ui/react';
import React from 'react';
import Popover from 'ui/shared/chakra/Popover';
import { Tooltip } from 'toolkit/chakra/tooltip';
import * as EntityBase from 'ui/shared/entities/base/components';
import type { ContentProps } from './AddressEntity';
import AddressEntity from './AddressEntity';
const AddressEntityContentProxy = (props: ContentProps) => {
const bgColor = useColorModeValue('gray.700', 'gray.900');
const implementations = props.address.implementations;
const handleClick = React.useCallback((event: React.MouseEvent) => {
event.stopPropagation();
}, []);
if (!implementations || implementations.length === 0) {
return null;
}
......@@ -25,51 +19,46 @@ const AddressEntityContentProxy = (props: ContentProps) => {
const implementationName = implementations.length === 1 && implementations[0].name ? implementations[0].name : undefined;
return (
<Popover trigger="hover" isLazy gutter={ 8 }>
<PopoverTrigger>
<Box display="inline-flex" w="100%">
<EntityBase.Content
{ ...props }
truncation={ nameTag || implementationName || props.address.name ? 'tail' : props.truncation }
text={ nameTag || implementationName || props.address.name || props.altHash || props.address.hash }
const content = (
<>
<Box fontWeight={ 600 }>
Proxy contract
{ props.address.name ? ` (${ props.address.name })` : '' }
</Box>
<AddressEntity address={{ hash: props.address.hash, filecoin: props.address.filecoin }} noLink noIcon noHighlight justifyContent="center"/>
<Box fontWeight={ 600 } mt={ 2 }>
Implementation{ implementations.length > 1 ? 's' : '' }
{ implementationName ? ` (${ implementationName })` : '' }
</Box>
<Flex flexWrap="wrap" columnGap={ 3 }>
{ implementations.map((item) => (
<AddressEntity
key={ item.address }
address={{ hash: item.address, filecoin: { robust: item.filecoin_robust_address } }}
noLink
noIcon
noHighlight
minW={ `calc((100% - ${ colNum - 1 } * 12px) / ${ colNum })` }
flex={ 1 }
justifyContent={ colNum === 1 ? 'center' : undefined }
isTooltipDisabled
/>
</Box>
</PopoverTrigger>
<Portal>
<DarkMode>
<PopoverContent bgColor={ bgColor } w="fit-content" borderRadius="sm" maxW={{ base: '100vw', lg: '410px' }} onClick={ handleClick }>
<PopoverArrow bgColor={ bgColor }/>
<PopoverBody color="white" p={ 2 } fontSize="sm" lineHeight={ 5 } textAlign="center">
<Box fontWeight={ 600 }>
Proxy contract
{ props.address.name ? ` (${ props.address.name })` : '' }
</Box>
<AddressEntity address={{ hash: props.address.hash, filecoin: props.address.filecoin }} noLink noIcon noHighlight justifyContent="center"/>
<Box fontWeight={ 600 } mt={ 2 }>
Implementation{ implementations.length > 1 ? 's' : '' }
{ implementationName ? ` (${ implementationName })` : '' }
</Box>
<Flex flexWrap="wrap" columnGap={ 3 }>
{ implementations.map((item) => (
<AddressEntity
key={ item.address }
address={{ hash: item.address, filecoin: { robust: item.filecoin_robust_address } }}
noLink
noIcon
noHighlight
minW={ `calc((100% - ${ colNum - 1 } * 12px) / ${ colNum })` }
flex={ 1 }
justifyContent={ colNum === 1 ? 'center' : undefined }
/>
)) }
</Flex>
</PopoverBody>
</PopoverContent>
</DarkMode>
</Portal>
</Popover>
)) }
</Flex>
</>
);
return (
<Tooltip content={ content } interactive contentProps={{ maxW: { base: '100vw', lg: '410px' } }}>
<Box display="inline-flex" w="100%">
<EntityBase.Content
{ ...props }
truncation={ nameTag || implementationName || props.address.name ? 'tail' : props.truncation }
text={ nameTag || implementationName || props.address.name || props.altHash || props.address.hash }
isTooltipDisabled
/>
</Box>
</Tooltip>
);
};
......
......@@ -64,7 +64,7 @@ const Link = chakra(({ isLoading, children, isExternal, onClick, href, noLink }:
};
if (noLink) {
return <Skeleton loading={ isLoading } { ...styles }>{ children }</Skeleton>;
return null;
}
const Component = isExternal ? LinkExternal : LinkInternal;
......@@ -109,7 +109,7 @@ const Icon = ({ isLoading, noIcon, size, name, color, borderRadius, marginRight,
);
};
export interface ContentBaseProps extends Pick<EntityBaseProps, 'className' | 'isLoading' | 'truncation' | 'tailLength'> {
export interface ContentBaseProps extends Pick<EntityBaseProps, 'className' | 'isLoading' | 'truncation' | 'tailLength' | 'isTooltipDisabled'> {
asProp?: React.ElementType;
text: string;
}
......@@ -157,6 +157,7 @@ const Content = chakra(({ className, isLoading, asProp, text, truncation = 'dyna
overflow="hidden"
whiteSpace="nowrap"
textOverflow={ truncation === 'tail' ? 'ellipsis' : undefined }
w="100%"
>
{ children }
</Skeleton>
......
import type { As } from '@chakra-ui/react';
import { chakra } from '@chakra-ui/react';
import React from 'react';
......@@ -77,7 +76,7 @@ const TxEntity = (props: EntityProps) => {
);
};
export default React.memo(chakra<As, EntityProps>(TxEntity));
export default React.memo(chakra(TxEntity));
export {
Container,
......
import { Box, Flex, Text, useColorModeValue, chakra } from '@chakra-ui/react';
import { Box, Flex, Text, chakra } from '@chakra-ui/react';
import NextLink from 'next/link';
import React from 'react';
import type { Route } from 'nextjs-routes';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Skeleton } from 'toolkit/chakra/skeleton';
import Hint from 'ui/shared/Hint';
import IconSvg, { type IconName } from 'ui/shared/IconSvg';
import TruncatedValue from 'ui/shared/TruncatedValue';
......@@ -52,16 +52,12 @@ const StatsWidget = ({
period,
href,
}: Props) => {
const bgColor = useColorModeValue('gray.50', 'whiteAlpha.100');
const skeletonBgColor = useColorModeValue('blackAlpha.50', 'whiteAlpha.50');
const hintColor = useColorModeValue('gray.600', 'gray.400');
return (
<Container href={ !isLoading ? href : undefined }>
<Flex
className={ className }
alignItems="center"
bgColor={ isLoading ? skeletonBgColor : bgColor }
bgColor={ isLoading ? { _light: 'blackAlpha.50', _dark: 'whiteAlpha.50' } : { _light: 'gray.50', _dark: 'whiteAlpha.100' } }
p={ 3 }
borderRadius="base"
justifyContent="space-between"
......@@ -84,21 +80,19 @@ const StatsWidget = ({
) }
<Box w={{ base: '100%', lg: icon ? 'calc(100% - 48px)' : '100%' }}>
<Skeleton
isLoaded={ !isLoading }
loading={ isLoading }
color="text_secondary"
fontSize="xs"
lineHeight="16px"
textStyle="xs"
w="fit-content"
>
<h2>{ label }</h2>
</Skeleton>
<Skeleton
isLoaded={ !isLoading }
loading={ isLoading }
display="flex"
alignItems="baseline"
fontWeight={ 500 }
fontSize="lg"
lineHeight={ 6 }
textStyle="lg"
>
{ valuePrefix && <chakra.span whiteSpace="pre">{ valuePrefix }</chakra.span> }
{ typeof value === 'string' ? (
......@@ -112,15 +106,15 @@ const StatsWidget = ({
<Text ml={ 2 } mr={ 1 } color="green.500">
+{ diffFormatted || Number(diff).toLocaleString() }
</Text>
<Text variant="secondary" fontSize="sm">({ diffPeriod })</Text>
<Text color="text.secondary" textStyle="sm">({ diffPeriod })</Text>
</>
) }
{ period && <Text variant="secondary" fontSize="xs" fontWeight={ 400 } ml={ 1 }>({ period })</Text> }
{ period && <Text color="text.secondary" textStyle="xs" fontWeight={ 400 } ml={ 1 }>({ period })</Text> }
</Skeleton>
</Box>
{ typeof hint === 'string' ? (
<Skeleton isLoaded={ !isLoading } alignSelf="center" borderRadius="base">
<Hint label={ hint } boxSize={ 6 } color={ hintColor }/>
<Skeleton loading={ isLoading } alignSelf="center" borderRadius="base">
<Hint label={ hint } boxSize={ 6 } color={{ _light: 'gray.600', _dark: 'gray.400' }}/>
</Skeleton>
) : hint }
</Flex>
......
import { TagLabel, Tooltip, chakra } from '@chakra-ui/react';
import { chakra } from '@chakra-ui/react';
import React from 'react';
import capitalizeFirstLetter from 'lib/capitalizeFirstLetter';
import Tag from 'ui/shared/chakra/Tag';
import type { BadgeProps } from 'toolkit/chakra/badge';
import { Badge } from 'toolkit/chakra/badge';
import { Tooltip } from 'toolkit/chakra/tooltip';
import type { IconName } from 'ui/shared/IconSvg';
import IconSvg from 'ui/shared/IconSvg';
......@@ -18,31 +20,31 @@ export interface Props {
const StatusTag = ({ type, text, errorText, isLoading, className }: Props) => {
let icon: IconName;
let colorScheme;
let colorPalette: BadgeProps['colorPalette'];
const capitalizedText = capitalizeFirstLetter(text);
switch (type) {
case 'ok':
icon = 'status/success';
colorScheme = 'green';
colorPalette = 'green';
break;
case 'error':
icon = 'status/error';
colorScheme = 'red';
colorPalette = 'red';
break;
case 'pending':
icon = 'status/pending';
colorScheme = 'gray';
colorPalette = 'gray';
break;
}
return (
<Tooltip label={ errorText }>
<Tag colorScheme={ colorScheme } display="flex" isLoading={ isLoading } className={ className }>
<Tooltip content={ errorText } disabled={ !errorText }>
<Badge colorPalette={ colorPalette } loading={ isLoading } className={ className }>
<IconSvg boxSize={ 2.5 } name={ icon } mr={ 1 } flexShrink={ 0 }/>
<TagLabel display="block">{ capitalizedText }</TagLabel>
</Tag>
<span>{ capitalizedText }</span>
</Badge>
</Tooltip>
);
};
......
......@@ -6,7 +6,7 @@ import type { Transaction } from 'types/api/transaction';
import config from 'configs/app';
import getCurrencyValue from 'lib/getCurrencyValue';
import { currencyUnits } from 'lib/units';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Skeleton } from 'toolkit/chakra/skeleton';
import CurrencyValue from 'ui/shared/CurrencyValue';
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
......@@ -32,7 +32,7 @@ const TxFee = ({ className, tx, accuracy, accuracyUsd, isLoading, withCurrency =
accuracyUsd,
});
return (
<Skeleton whiteSpace="pre-wrap" wordBreak="break-word" isLoaded={ !isLoading } display="flex" flexWrap="wrap" className={ className }>
<Skeleton whiteSpace="pre-wrap" wordBreak="break-word" loading={ isLoading } display="flex" flexWrap="wrap" className={ className }>
<span>{ valueStr } </span>
<TokenEntity token={ token } noCopy onlySymbol w="auto" ml={ 1 }/>
{ usd && withUsd && <chakra.span color="text_secondary"> (${ usd })</chakra.span> }
......@@ -51,7 +51,7 @@ const TxFee = ({ className, tx, accuracy, accuracyUsd, isLoading, withCurrency =
});
return (
<Skeleton whiteSpace="pre" isLoaded={ !isLoading } display="flex" className={ className }>
<Skeleton whiteSpace="pre" loading={ isLoading } display="flex" className={ className }>
<span>{ valueStr } </span>
{ valueStr !== '0' && <TokenEntity token={ token } noCopy onlySymbol w="auto" ml={ 1 }/> }
{ usd && withUsd && <chakra.span color="text_secondary"> (${ usd })</chakra.span> }
......
......@@ -3,7 +3,7 @@ import React from 'react';
import type { Transaction } from 'types/api/transaction';
import Tag from 'ui/shared/chakra/Tag';
import { Badge } from 'toolkit/chakra/badge';
interface Props {
tx: Transaction;
......@@ -23,16 +23,16 @@ const TxWatchListTags = ({ tx, isLoading }: Props) => {
return (
<Flex columnGap={ 2 } rowGap={ 2 } flexWrap="wrap" overflow="hidden" maxW="100%">
{ tags.map((tag) => (
<Tag
<Badge
key={ tag.label }
isLoading={ isLoading }
isTruncated
loading={ isLoading }
truncate
// TODO @tom2drum check these styles
// maxW={{ base: '115px', lg: 'initial' }}
colorScheme="gray"
variant="subtle"
colorPalette="gray"
>
{ tag.display_name }
</Tag>
</Badge>
)) }
</Flex>
);
......
......@@ -10,7 +10,7 @@ interface Props {
className?: string;
}
const NetworkMenuButton = ({ isActive, onClick, className }: Props, ref: React.ForwardedRef<HTMLButtonElement>) => {
const NetworkMenuButton = ({ isActive, onClick, className, ...rest }: Props, ref: React.ForwardedRef<HTMLDivElement>) => {
return (
<IconButton
className={ className }
......@@ -24,6 +24,7 @@ const NetworkMenuButton = ({ isActive, onClick, className }: Props, ref: React.F
onClick={ onClick }
aria-label="Network menu"
aria-roledescription="menu"
{ ...rest }
>
<IconSvg
name="networks"
......
import {
chakra,
Modal,
ModalContent,
ModalCloseButton,
PopoverTrigger,
PopoverContent,
PopoverBody,
useDisclosure,
} from '@chakra-ui/react';
import { chakra, DialogBody } from '@chakra-ui/react';
import React from 'react';
import type { Transaction } from 'types/api/transaction';
import { DialogContent, DialogHeader, DialogRoot, DialogTrigger } from 'toolkit/chakra/dialog';
import { Heading } from 'toolkit/chakra/heading';
import { PopoverBody, PopoverContent, PopoverRoot, PopoverTrigger } from 'toolkit/chakra/popover';
import AdditionalInfoButton from 'ui/shared/AdditionalInfoButton';
import Popover from 'ui/shared/chakra/Popover';
import TxAdditionalInfoContainer from './TxAdditionalInfoContainer';
import TxAdditionalInfoContent from './TxAdditionalInfoContent';
......@@ -32,39 +25,39 @@ type Props =
className?: string;
};
// TODO @tom2drum fix other popovers
const TxAdditionalInfo = ({ hash, tx, isMobile, isLoading, className }: Props) => {
const { isOpen, onOpen, onClose } = useDisclosure();
const content = hash !== undefined ? <TxAdditionalInfoContainer hash={ hash }/> : <TxAdditionalInfoContent tx={ tx }/>;
if (isMobile) {
return (
<>
<AdditionalInfoButton onClick={ onOpen } isLoading={ isLoading } className={ className }/>
<Modal isOpen={ isOpen } onClose={ onClose } size="full">
<ModalContent paddingTop={ 4 }>
<ModalCloseButton/>
<DialogRoot size="full">
<DialogTrigger>
<AdditionalInfoButton loading={ isLoading } className={ className }/>
</DialogTrigger>
<DialogContent>
<DialogHeader>
Additional info
</DialogHeader>
<DialogBody>
{ content }
</ModalContent>
</Modal>
</>
</DialogBody>
</DialogContent>
</DialogRoot>
);
}
return (
<Popover placement="right-start" openDelay={ 300 } isLazy>
{ ({ isOpen }) => (
<>
<PopoverTrigger>
<AdditionalInfoButton isOpen={ isOpen } isLoading={ isLoading } className={ className }/>
</PopoverTrigger>
<PopoverContent border="1px solid" borderColor="border.divider">
<PopoverBody fontWeight={ 400 } fontSize="sm">
{ content }
</PopoverBody>
</PopoverContent>
</>
) }
</Popover>
<PopoverRoot positioning={{ placement: 'right-start' }}>
<PopoverTrigger>
<AdditionalInfoButton loading={ isLoading } className={ className }/>
</PopoverTrigger>
<PopoverContent w="330px">
<PopoverBody textStyle="sm">
<Heading level="3" mb={ 6 }>Additional info </Heading>
{ content }
</PopoverBody>
</PopoverContent>
</PopoverRoot>
);
};
......
import { Box, Divider } from '@chakra-ui/react';
import { Box, Separator } from '@chakra-ui/react';
import React from 'react';
import useApiQuery from 'lib/api/useApiQuery';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Skeleton } from 'toolkit/chakra/skeleton';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import TxAdditionalInfoContent from './TxAdditionalInfoContent';
......@@ -22,30 +22,29 @@ const TxAdditionalInfoContainer = ({ hash }: Props) => {
if (isPending) {
return (
<Box>
<Skeleton w="130px" h="24px" borderRadius="full" mb={ 6 }/>
<Box>
<Skeleton w="110px" h="16px" borderRadius="full" mb={ 3 }/>
<Skeleton w="100%" h="16px" borderRadius="full"/>
<Skeleton loading w="110px" h="16px" borderRadius="full" mb={ 3 }/>
<Skeleton loading w="100%" h="16px" borderRadius="full"/>
</Box>
<Divider my={ 4 }/>
<Separator my={ 4 }/>
<Box>
<Skeleton w="110px" h="16px" borderRadius="full" mb={ 3 }/>
<Skeleton w="100%" h="16px" borderRadius="full"/>
<Skeleton loading w="110px" h="16px" borderRadius="full" mb={ 3 }/>
<Skeleton loading w="100%" h="16px" borderRadius="full"/>
</Box>
<Divider my={ 4 }/>
<Separator my={ 4 }/>
<Box>
<Skeleton w="110px" h="16px" borderRadius="full" mb={ 3 }/>
<Skeleton w="100%" h="16px" borderRadius="full"/>
<Skeleton loading w="110px" h="16px" borderRadius="full" mb={ 3 }/>
<Skeleton loading w="100%" h="16px" borderRadius="full"/>
</Box>
<Divider my={ 4 }/>
<Separator my={ 4 }/>
<Box>
<Skeleton w="110px" h="16px" borderRadius="full" mb={ 3 }/>
<Skeleton w="75%" h="16px" borderRadius="full"/>
<Skeleton w="75%" h="16px" borderRadius="full" mt={ 1 }/>
<Skeleton w="75%" h="16px" borderRadius="full" mt={ 1 }/>
<Skeleton loading w="110px" h="16px" borderRadius="full" mb={ 3 }/>
<Skeleton loading w="75%" h="16px" borderRadius="full"/>
<Skeleton loading w="75%" h="16px" borderRadius="full" mt={ 1 }/>
<Skeleton loading w="75%" h="16px" borderRadius="full" mt={ 1 }/>
</Box>
<Divider my={ 4 }/>
<Skeleton w="80px" h="16px" borderRadius="full"/>
<Separator my={ 4 }/>
<Skeleton loading w="80px" h="16px" borderRadius="full"/>
</Box>
);
}
......
import { Box, Heading, Text, Flex } from '@chakra-ui/react';
import { Box, Text, Flex } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';
......@@ -30,7 +30,6 @@ const TxAdditionalInfoContent = ({ tx }: { tx: Transaction }) => {
return (
<>
<Heading as="h4" size="sm" mb={ 6 }>Additional info </Heading>
{ tx.blob_versioned_hashes && tx.blob_versioned_hashes.length > 0 && (
<Box { ...sectionProps } mb={ 4 }>
<Flex alignItems="center" justifyContent="space-between">
......
......@@ -2,7 +2,8 @@ import React from 'react';
import type { TransactionType } from 'types/api/transaction';
import Tag from 'ui/shared/chakra/Tag';
import type { BadgeProps } from 'toolkit/chakra/badge';
import { Badge } from 'toolkit/chakra/badge';
export interface Props {
types: Array<TransactionType>;
......@@ -24,51 +25,51 @@ const TxType = ({ types, isLoading }: Props) => {
const typeToShow = types.sort((t1, t2) => TYPES_ORDER.indexOf(t1) - TYPES_ORDER.indexOf(t2))[0];
let label;
let colorScheme;
let colorPalette: BadgeProps['colorPalette'];
switch (typeToShow) {
case 'contract_call':
label = 'Contract call';
colorScheme = 'blue';
colorPalette = 'blue';
break;
case 'blob_transaction':
label = 'Blob txn';
colorScheme = 'yellow';
colorPalette = 'yellow';
break;
case 'contract_creation':
label = 'Contract creation';
colorScheme = 'blue';
colorPalette = 'blue';
break;
case 'token_transfer':
label = 'Token transfer';
colorScheme = 'orange';
colorPalette = 'orange';
break;
case 'token_creation':
label = 'Token creation';
colorScheme = 'orange';
colorPalette = 'orange';
break;
case 'coin_transfer':
label = 'Coin transfer';
colorScheme = 'orange';
colorPalette = 'orange';
break;
case 'rootstock_remasc':
label = 'REMASC';
colorScheme = 'blue';
colorPalette = 'blue';
break;
case 'rootstock_bridge':
label = 'Bridge';
colorScheme = 'blue';
colorPalette = 'blue';
break;
default:
label = 'Transaction';
colorScheme = 'purple';
colorPalette = 'purple';
}
return (
<Tag colorScheme={ colorScheme } isLoading={ isLoading }>
<Badge colorPalette={ colorPalette } loading={ isLoading }>
{ label }
</Tag>
</Badge>
);
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment