Commit 2b22bfc2 authored by tom's avatar tom

links refactoring and showcases

parent b740a53f
......@@ -26,6 +26,8 @@ const RESTRICTED_MODULES = {
{ 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: 'ui/shared/chakra/Skeleton', message: 'Please use Skeleton component from toolkit/chakra instead' },
{ name: 'ui/shared/links/LinkInternal', message: 'Please use Link component from toolkit/chakra instead' },
{ name: 'ui/shared/links/LinkExternal', message: 'Please use Link component from toolkit/chakra instead' },
{
name: '@chakra-ui/react',
importNames: [
......
import type { LinkProps as ChakraLinkProps } from '@chakra-ui/react';
import { Link as ChakraLink } from '@chakra-ui/react';
import NextLink from 'next/link';
import type { LinkProps as NextLinkProps } from 'next/link';
import React from 'react';
import IconSvg from 'ui/shared/IconSvg';
import { Skeleton } from './skeleton';
export interface LinkProps extends ChakraLinkProps {
loading?: boolean;
external?: boolean;
scroll?: boolean;
iconColor?: ChakraLinkProps['color'];
}
export const Link = React.forwardRef<HTMLAnchorElement, LinkProps>(
function Link(props, ref) {
const { external, loading, href, children, scroll = true, iconColor, ...rest } = props;
if (external) {
return (
<Skeleton loading={ loading } asChild>
<ChakraLink
ref={ ref }
href={ href }
className="group"
target="_blank"
rel="noopener noreferrer" { ...rest }
>
{ children }
<IconSvg
name="link_external"
boxSize={ 3 }
verticalAlign="middle"
color={ iconColor ?? 'icon.externalLink' }
_groupHover={{
color: 'inherit',
}}
flexShrink={ 0 }
/>
</ChakraLink>
</Skeleton>
);
}
return (
<Skeleton loading={ loading } asChild>
<ChakraLink asChild ref={ ref } { ...rest }>
{ href ? <NextLink href={ href as NextLinkProps['href'] } scroll={ scroll }>{ children }</NextLink> : <span>{ children }</span> }
</ChakraLink>
</Skeleton>
);
},
);
......@@ -47,6 +47,6 @@ export const SkeletonText = React.forwardRef<HTMLDivElement, SkeletonTextProps>(
export const Skeleton = React.forwardRef<HTMLDivElement, ChakraSkeletonProps>(
function Skeleton(props, ref) {
const { loading = false, ...rest } = props;
return <ChakraSkeleton loading={ loading } { ...rest } ref={ ref }/>;
return <ChakraSkeleton loading={ loading } { ...(loading ? { 'data-loading': true } : {}) } { ...rest } ref={ ref }/>;
},
);
import React from 'react';
import { scroller, Element } from 'react-scroll';
import { Link } from 'toolkit/chakra/link';
interface Props {
children: React.ReactNode;
id?: string;
onClick?: () => void;
isLoading?: boolean;
}
const ID = 'CutLink';
const CutLink = (props: Props) => {
const { children, id = ID, onClick, isLoading } = props;
const [ isExpanded, setIsExpanded ] = React.useState(false);
const handleClick = React.useCallback(() => {
setIsExpanded((flag) => !flag);
scroller.scrollTo(id, {
duration: 500,
smooth: true,
});
onClick?.();
}, [ id, onClick ]);
return (
<>
<Element name={ id }>
<Link
textStyle="sm"
textDecorationLine="underline"
textDecorationStyle="dashed"
onClick={ handleClick }
loading={ isLoading }
>
{ isExpanded ? 'Hide details' : 'View details' }
</Link>
</Element>
{ isExpanded && children }
</>
);
};
export default React.memo(CutLink);
......@@ -89,6 +89,9 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
secondary: {
DEFAULT: { value: { _light: '{colors.gray.400}', _dark: '{colors.gray.500}' } },
},
underlaid: {
bg: { value: { _light: '{colors.gray.100}', _dark: '{colors.gray.800}' } },
},
subtle: {
DEFAULT: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.gray.400}' } },
hover: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.gray.400}' } },
......@@ -306,6 +309,7 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
},
icon: {
backTo: { value: '{colors.gray.400}' },
externalLink: { value: { _light: '{colors.gray.400}', _dark: '{colors.gray.400}' } },
},
global: {
body: {
......
......@@ -5,7 +5,7 @@ export const recipe = defineRecipe({
gap: 0,
},
variants: {
visual: {
variant: {
primary: {
color: 'link.primary',
_hover: {
......@@ -24,9 +24,27 @@ export const recipe = defineRecipe({
color: 'link.subtle',
_hover: {
color: 'link.subtle.hover',
textDecorationLine: 'underline',
textDecorationColor: 'link.subtle.hover',
},
},
underlaid: {
color: 'link.primary',
// css-var to override bg property on loaded skeleton
'--layer-bg': '{colors.link.underlaid.bg}',
bgColor: 'link.underlaid.bg',
px: '8px',
py: '6px',
borderRadius: 'base',
textStyle: 'sm',
_hover: {
color: 'link.primary.hover',
textDecoration: 'none',
},
_loading: {
bgColor: 'transparent',
},
},
navigation: {
color: 'link.navigation.fg',
bg: 'link.navigation.bg',
......@@ -47,6 +65,6 @@ export const recipe = defineRecipe({
},
},
defaultVariants: {
visual: 'primary',
variant: 'primary',
},
});
......@@ -19,7 +19,7 @@ export const recipe = defineRecipe({
},
},
'false': {
background: 'unset',
background: 'var(--layer-bg)',
animation: 'fade-in var(--fade-duration, 0.1s) ease-out !important',
},
},
......
......@@ -72,6 +72,8 @@ export const recipe = defineSlotRecipe({
boxShadow: 'popover',
boxShadowColor: 'popover.shadow',
borderRadius: 'md',
textAlign: 'left',
fontWeight: 'normal',
},
},
},
......
......@@ -31,6 +31,12 @@ const customConfig = defineConfig({
fonts,
shadows,
zIndex,
fontWeights: {
normal: { value: '400' },
medium: { value: '500' },
semibold: { value: '600' },
bold: { value: '700' },
},
},
},
// components,
......
......@@ -25,6 +25,7 @@ import PageTitle from 'ui/shared/Page/PageTitle';
import AlertsShowcase from 'ui/showcases/Alerts';
import BadgesShowcase from 'ui/showcases/Badges';
import ButtonShowcase from 'ui/showcases/Button';
import LinksShowcase from 'ui/showcases/Links';
import PaginationShowcase from 'ui/showcases/Pagination';
import TabsShowcase from 'ui/showcases/Tabs';
......@@ -54,6 +55,7 @@ const ChakraShowcases = () => {
<TabsTrigger value="alerts">Alerts</TabsTrigger>
<TabsTrigger value="badges">Badges</TabsTrigger>
<TabsTrigger value="buttons">Buttons</TabsTrigger>
<TabsTrigger value="links">Links</TabsTrigger>
<TabsTrigger value="pagination">Pagination</TabsTrigger>
<TabsTrigger value="tabs">Tabs</TabsTrigger>
<TabsTrigger value="unsorted">Unsorted</TabsTrigger>
......@@ -61,6 +63,7 @@ const ChakraShowcases = () => {
<AlertsShowcase/>
<BadgesShowcase/>
<ButtonShowcase/>
<LinksShowcase/>
<TabsShowcase/>
<PaginationShowcase/>
......
......@@ -48,7 +48,7 @@ const CopyToClipboard = ({ text, className, isLoading, onClick, size = 5, type,
}, [ onClick, copy ]);
if (isLoading) {
return <Skeleton boxSize={ size } className={ className } borderRadius="sm" flexShrink={ 0 } ml={ 2 } display="inline-block"/>;
return <Skeleton boxSize={ size } className={ className } borderRadius="sm" flexShrink={ 0 } ml={ 2 } display="inline-block" loading/>;
}
return (
......
......@@ -48,7 +48,7 @@ const Icon = (props: IconProps) => {
};
if (props.isLoading) {
return <Skeleton { ...styles } borderRadius="full" flexShrink={ 0 }/>;
return <Skeleton { ...styles } loading borderRadius="full" flexShrink={ 0 }/>;
}
if (props.address.is_contract) {
......
import { Box } from '@chakra-ui/react';
import { Box, Flex } from '@chakra-ui/react';
import dynamic from 'next/dynamic';
import React from 'react';
......@@ -53,7 +53,7 @@ const Icon = dynamic(
return (props: IconProps) => {
const svg = GradientAvatar(props.hash, props.size, 'circle');
return <div dangerouslySetInnerHTML={{ __html: svg }}/>;
return <Flex dangerouslySetInnerHTML={{ __html: svg }}/>;
};
}
......
......@@ -31,6 +31,7 @@ export interface EntityBaseProps {
tailLength?: number;
target?: React.HTMLAttributeAnchorTarget;
truncation?: Truncation;
size?: 'md' | 'lg';
}
export interface ContainerBaseProps extends Pick<EntityBaseProps, 'className'> {
......@@ -165,9 +166,9 @@ const Content = chakra(({ className, isLoading, asProp, text, truncation = 'dyna
);
});
export type CopyBaseProps = Pick<CopyToClipboardProps, 'isLoading' | 'text'> & Pick<EntityBaseProps, 'noCopy'>;
export type CopyBaseProps = Pick<CopyToClipboardProps, 'isLoading' | 'text'> & Pick<EntityBaseProps, 'noCopy' | 'size'>;
const Copy = (props: CopyBaseProps) => {
const Copy = ({ size, ...props }: CopyBaseProps) => {
if (props.noCopy) {
return null;
}
......
import type { As } from '@chakra-ui/react';
import { chakra } from '@chakra-ui/react';
import React from 'react';
......@@ -76,7 +75,7 @@ const BlobEntity = (props: EntityProps) => {
);
};
export default React.memo(chakra<As, EntityProps>(BlobEntity));
export default React.memo(chakra(BlobEntity));
export {
Container,
......
import type { As } from '@chakra-ui/react';
import { Box, chakra, Flex, Image, PopoverBody, PopoverContent, PopoverTrigger, Portal, Text } from '@chakra-ui/react';
import { chakra, Flex, Text } from '@chakra-ui/react';
import React from 'react';
import type * as bens from '@blockscout/bens-types';
import { route } from 'nextjs-routes';
import Popover from 'ui/shared/chakra/Popover';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Image } from 'toolkit/chakra/image';
import { Link as LinkToolkit } from 'toolkit/chakra/link';
import { Skeleton } from 'toolkit/chakra/skeleton';
import { Tooltip } from 'toolkit/chakra/tooltip';
import * as EntityBase from 'ui/shared/entities/base/components';
import IconSvg from 'ui/shared/IconSvg';
import LinkExternal from 'ui/shared/links/LinkExternal';
import TruncatedValue from 'ui/shared/TruncatedValue';
import { distributeEntityProps, getIconProps } from '../base/utils';
......@@ -39,58 +39,71 @@ const Icon = (props: IconProps) => {
const styles = getIconProps(props.size);
if (props.isLoading) {
return <Skeleton boxSize={ styles.boxSize } borderRadius="sm" mr={ 2 }/>;
return <Skeleton loading boxSize={ styles.boxSize } borderRadius="sm" mr={ 2 }/>;
}
const content = (
<>
<Flex alignItems="center" textStyle="md">
<Image
src={ props.protocol.icon_url }
boxSize={ 5 }
borderRadius="sm"
mr={ 2 }
alt={ `${ props.protocol.title } protocol icon` }
fallback={ icon }
// fallbackStrategy={ props.protocol.icon_url ? 'onError' : 'beforeLoadOrError' }
/>
<div>
<span>{ props.protocol.short_name }</span>
<chakra.span color="text_secondary" whiteSpace="pre"> { props.protocol.tld_list.map((tld) => `.${ tld }`).join((' ')) }</chakra.span>
</div>
</Flex>
<Text>{ props.protocol.description }</Text>
{ props.protocol.docs_url && (
<LinkToolkit
href={ props.protocol.docs_url }
display="inline-flex"
alignItems="center"
external
>
{ /* TODO @tom2drum maybe refactor this to pass icon as a prop */ }
<IconSvg name="docs" boxSize={ 5 } color="text.secondary" mr={ 2 }/>
<span>Documentation</span>
</LinkToolkit>
) }
</>
);
return (
<Popover trigger="hover" isLazy placement="bottom-start">
<PopoverTrigger>
<Box flexShrink={ 0 }>
<Image
src={ props.protocol.icon_url }
boxSize={ styles.boxSize }
borderRadius="sm"
mr={ 2 }
alt={ `${ props.protocol.title } protocol icon` }
fallback={ icon }
fallbackStrategy={ props.protocol.icon_url ? 'onError' : 'beforeLoadOrError' }
/>
</Box>
</PopoverTrigger>
<Portal>
<PopoverContent maxW={{ base: '100vw', lg: '440px' }} minW="250px" w="fit-content">
<PopoverBody display="flex" flexDir="column" rowGap={ 3 }>
<Flex alignItems="center">
<Image
src={ props.protocol.icon_url }
boxSize={ 5 }
borderRadius="sm"
mr={ 2 }
alt={ `${ props.protocol.title } protocol icon` }
fallback={ icon }
fallbackStrategy={ props.protocol.icon_url ? 'onError' : 'beforeLoadOrError' }
/>
<div>
<span>{ props.protocol.short_name }</span>
<chakra.span color="text_secondary" whiteSpace="pre"> { props.protocol.tld_list.map((tld) => `.${ tld }`).join((' ')) }</chakra.span>
</div>
</Flex>
<Text fontSize="sm">{ props.protocol.description }</Text>
{ props.protocol.docs_url && (
<LinkExternal
href={ props.protocol.docs_url }
display="inline-flex"
alignItems="center"
fontSize="sm"
>
<IconSvg name="docs" boxSize={ 5 } color="text_secondary" mr={ 2 }/>
<span>Documentation</span>
</LinkExternal>
) }
</PopoverBody>
</PopoverContent>
</Portal>
</Popover>
<Tooltip
content={ content }
visual="popover"
positioning={{
placement: 'bottom-start',
}}
contentProps={{
maxW: { base: '100vw', lg: '440px' },
minW: '250px',
w: 'fit-content',
display: 'flex',
flexDir: 'column',
rowGap: 3,
alignItems: 'flex-start',
}}
interactive
>
<Image
src={ props.protocol.icon_url }
boxSize={ styles.boxSize }
borderRadius="sm"
mr={ 2 }
flexShrink={ 0 }
alt={ `${ props.protocol.title } protocol icon` }
fallback={ icon }
// fallbackStrategy={ props.protocol.icon_url ? 'onError' : 'beforeLoadOrError' }
/>
</Tooltip>
);
}
......@@ -140,7 +153,7 @@ const EnsEntity = (props: EntityProps) => {
);
};
export default React.memo(chakra<As, EntityProps>(EnsEntity));
export default React.memo(chakra(EnsEntity));
export {
Container,
......
import type { As } from '@chakra-ui/react';
import { chakra } from '@chakra-ui/react';
import React from 'react';
......@@ -69,7 +68,7 @@ const NftEntity = (props: EntityProps) => {
);
};
export default React.memo(chakra<As, EntityProps>(NftEntity));
export default React.memo(chakra(NftEntity));
export {
Container,
......
import type { As } from '@chakra-ui/react';
import { Flex, chakra, useColorModeValue } from '@chakra-ui/react';
import { Flex, chakra } from '@chakra-ui/react';
import React from 'react';
import type { Pool } from 'types/api/pools';
......@@ -7,7 +6,7 @@ import type { Pool } from 'types/api/pools';
import { route } from 'nextjs-routes';
import { getPoolTitle } from 'lib/pools/getPoolTitle';
import Skeleton from 'ui/shared/chakra/Skeleton';
import { Skeleton } from 'toolkit/chakra/skeleton';
import * as EntityBase from 'ui/shared/entities/base/components';
import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
......@@ -32,8 +31,8 @@ const Link = chakra((props: LinkProps) => {
type IconProps = Pick<EntityProps, 'pool' | 'className'> & EntityBase.IconBaseProps;
const Icon = (props: IconProps) => {
const bgColor = useColorModeValue('white', 'black');
const borderColor = useColorModeValue('whiteAlpha.800', 'blackAlpha.800');
const bgColor = { _light: 'white', _dark: 'black' };
const borderColor = { _light: 'whiteAlpha.800', _dark: 'blackAlpha.800' };
return (
<Flex>
<Flex
......@@ -87,7 +86,7 @@ const Content = chakra((props: ContentProps) => {
return (
<TruncatedTextTooltip label={ nameString }>
<Skeleton
isLoaded={ !props.isLoading }
loading={ props.isLoading }
display="inline-block"
whiteSpace="nowrap"
overflow="hidden"
......@@ -119,7 +118,7 @@ const PoolEntity = (props: EntityProps) => {
);
};
export default React.memo(chakra<As, EntityProps>(PoolEntity));
export default React.memo(chakra(PoolEntity));
export {
Container,
......
......@@ -43,7 +43,7 @@ const Icon = (props: IconProps) => {
};
if (props.isLoading) {
return <Skeleton { ...styles } className={ props.className }/>;
return <Skeleton { ...styles } loading className={ props.className }/>;
}
return (
......
import type { As } from '@chakra-ui/react';
import { chakra } from '@chakra-ui/react';
import React from 'react';
......@@ -76,7 +75,7 @@ const UserOpEntity = (props: EntityProps) => {
);
};
export default React.memo(chakra<As, EntityProps>(UserOpEntity));
export default React.memo(chakra(UserOpEntity));
export {
Container,
......
......@@ -14,12 +14,11 @@ interface Props {
children: React.ReactNode;
isLoading?: boolean;
variant?: Variants;
visual?: LinkProps['visual'];
iconColor?: LinkProps['color'];
onClick?: LinkProps['onClick'];
}
const LinkExternal = ({ href, children, className, isLoading, variant, visual, iconColor, onClick }: Props) => {
const LinkExternal = ({ href, children, className, isLoading, variant, iconColor, onClick }: Props) => {
const commonProps = {
display: 'inline-block',
alignItems: 'center',
......@@ -46,7 +45,7 @@ const LinkExternal = ({ href, children, className, isLoading, variant, visual, i
}
return (
<Link className={ className } { ...styleProps } target="_blank" href={ href } onClick={ onClick } visual={ visual }>
<Link className={ className } { ...styleProps } target="_blank" href={ href } onClick={ onClick } variant={ variant }>
{ children }
<IconSvg name="link_external" boxSize={ 3 } verticalAlign="middle" color={ iconColor ?? 'icon_link_external' } flexShrink={ 0 }/>
</Link>
......
/* eslint-disable max-len */
import { Box } from '@chakra-ui/react';
import React from 'react';
import * as addressMock from 'mocks/address/address';
import * as implementationsMock from 'mocks/address/implementations';
import * as blobsMock from 'mocks/blobs/blobs';
import * as blockMock from 'mocks/blocks/block';
import * as ensMock from 'mocks/ens/domain';
import * as poolMock from 'mocks/pools/pool';
import * as txMock from 'mocks/txs/tx';
import { Link } from 'toolkit/chakra/link';
import CutLink from 'toolkit/components/CutLink/CutLink';
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import BlobEntity from 'ui/shared/entities/blob/BlobEntity';
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
import EnsEntity from 'ui/shared/entities/ens/EnsEntity';
import NftEntity from 'ui/shared/entities/nft/NftEntity';
import PoolEntity from 'ui/shared/entities/pool/PoolEntity';
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
import TxEntity from 'ui/shared/entities/tx/TxEntity';
import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts';
const TEXT = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.';
const TOKEN = {
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
circulating_market_cap: '139446916652.6728',
decimals: '6',
exchange_rate: '0.999921',
holders: '8348277',
icon_url: 'https://assets.coingecko.com/coins/images/325/small/Tether.png?1696501661',
name: 'Tether',
symbol: 'USDT',
total_supply: '76923002799740785',
type: 'ERC-20' as const,
volume_24h: '82069586622.4918',
};
const LinksShowcase = () => {
return (
<Container value="links">
<Section>
<SectionHeader>Variants</SectionHeader>
<SamplesStack>
<Sample label="variant: primary">
<Link href="/blocks" variant="primary">Default</Link>
<Link href="/" variant="primary" data-hover>Hover</Link>
</Sample>
<Sample label="variant: secondary">
<Link href="/" variant="secondary">Default</Link>
<Link href="/" variant="secondary" data-hover>Hover</Link>
</Sample>
<Sample label="variant: subtle">
<Link href="/" variant="subtle">Default</Link>
<Link href="/" variant="subtle" data-hover>Hover</Link>
</Sample>
<Sample label="variant: navigation">
<Link href="/" variant="navigation" p={ 2 } borderRadius="base">Default</Link>
<Link href="/" variant="navigation" p={ 2 } borderRadius="base" data-hover>Hover</Link>
<Link href="/" variant="navigation" p={ 2 } borderRadius="base" data-selected>Selected</Link>
<Link href="/" variant="navigation" p={ 2 } borderRadius="base" data-active>Active</Link>
</Sample>
<Sample label="variant: underlaid">
<Link href="/" variant="underlaid" external>Default</Link>
<Link href="/" variant="underlaid" external data-hover>Hover</Link>
</Sample>
</SamplesStack>
</Section>
<Section>
<SectionHeader>Loading</SectionHeader>
<SamplesStack>
<Sample label="loading: true, variant: primary">
<Link href="/" loading>hello hello hello</Link>
<span>Within <Link loading>hello</Link> text</span>
</Sample>
<Sample label="loading: true, variant: underlaid">
<Link href="/" loading variant="underlaid">hello hello hello</Link>
<span>Within <Link loading variant="underlaid">hello hello</Link> text</span>
</Sample>
</SamplesStack>
</Section>
<Section>
<SectionHeader>Examples</SectionHeader>
<SectionSubHeader>Cut link</SectionSubHeader>
<SamplesStack>
<Sample label="Default" flexDirection="column" alignItems="flex-start">
<CutLink id="CutLink_1">
<Box maxW="500px">{ TEXT }</Box>
</CutLink>
</Sample>
<Sample label="Loading" flexDirection="column" alignItems="flex-start">
<CutLink id="CutLink_2" isLoading>
<Box maxW="500px">{ TEXT }</Box>
</CutLink>
</Sample>
</SamplesStack>
<SectionSubHeader>Address link</SectionSubHeader>
<SamplesStack>
<Sample label="Without name" vertical>
<AddressEntity address={ addressMock.withoutName }/>
<AddressEntity address={ addressMock.withoutName } isExternal/>
<AddressEntity address={{ ...addressMock.filecoin, name: null }}/>
<Box maxW="200px">
<AddressEntity address={ addressMock.withoutName }/>
</Box>
<AddressEntity address={ addressMock.withoutName } isLoading/>
</Sample>
<Sample label="Size: md, lg??? (heading)" vertical>
<AddressEntity address={ addressMock.withoutName } size="md"/>
<AddressEntity address={ addressMock.withoutName } size="lg" textStyle="heading.md"/>
</Sample>
<Sample label="With name" vertical>
<AddressEntity address={ addressMock.withName }/>
<AddressEntity address={ addressMock.withNameTag }/>
<AddressEntity address={ addressMock.withEns }/>
<Box maxW="150px">
<AddressEntity address={ addressMock.withEns }/>
</Box>
</Sample>
<Sample label="Contract" vertical>
<AddressEntity address={{ ...addressMock.contract, is_verified: false, name: null, implementations: [] }}/>
<AddressEntity address={{ ...addressMock.contract, implementations: [] }}/>
<AddressEntity address={{ ...addressMock.contract, implementations: implementationsMock.multiple }}/>
<AddressEntity address={ addressMock.contract }/>
</Sample>
</SamplesStack>
<SectionSubHeader>Token link</SectionSubHeader>
<SamplesStack>
<Sample label="With info" vertical w="100%">
<TokenEntity token={ TOKEN }/>
<Box maxW="200px">
<TokenEntity token={{ ...TOKEN, name: 'Very looooooooong name' }} noSymbol/>
</Box>
<Box maxW="300px">
<TokenEntity token={{ ...TOKEN, symbol: 'Very looooooooong symbol' }}/>
</Box>
<TokenEntity token={ TOKEN } jointSymbol/>
<TokenEntity token={ TOKEN } onlySymbol/>
<TokenEntity token={ TOKEN } isLoading/>
</Sample>
<Sample label="With partial info" vertical w="100%">
<TokenEntity token={{ ...TOKEN, icon_url: null }}/>
<TokenEntity token={{ ...TOKEN, icon_url: null, name: null, symbol: null }}/>
</Sample>
<Sample label="Size: md, lg??? (heading)" vertical w="100%">
<TokenEntity token={ TOKEN } size="md"/>
<TokenEntity token={ TOKEN } size="lg" textStyle="heading.md" jointSymbol/>
</Sample>
</SamplesStack>
<SectionSubHeader>Transaction link</SectionSubHeader>
<SamplesStack>
<Sample label="Default" vertical w="100%">
<TxEntity hash={ txMock.base.hash }/>
<TxEntity hash={ txMock.base.hash } isExternal/>
<Box maxW="200px">
<TxEntity hash={ txMock.base.hash } noCopy={ false }/>
</Box>
<TxEntity hash={ txMock.base.hash } isLoading noCopy={ false }/>
</Sample>
</SamplesStack>
<SectionSubHeader>Block link</SectionSubHeader>
<SamplesStack>
<Sample label="Default" vertical w="100%">
<BlockEntity number={ blockMock.base.height }/>
<BlockEntity number={ blockMock.base.height } isExternal icon={{ name: 'txn_batches_slim' }}/>
<Box maxW="150px">
<BlockEntity number={ 1234567890123456 }/>
</Box>
<BlockEntity number={ blockMock.base.height } isLoading/>
</Sample>
</SamplesStack>
<SectionSubHeader>NFT link</SectionSubHeader>
<SamplesStack>
<Sample label="Default" vertical w="100%">
<NftEntity id="42" hash={ TOKEN.address }/>
<Box maxW="250px">
<NftEntity id="32925298983216553915666621415831103694597106215670571463977478984525997408266" hash={ TOKEN.address }/>
</Box>
<NftEntity id="4200000" hash={ TOKEN.address } isLoading/>
</Sample>
</SamplesStack>
<SectionSubHeader>ENS link</SectionSubHeader>
<SamplesStack>
<Sample label="Default" vertical w="100%">
<EnsEntity domain={ ensMock.ensDomainA.name } protocol={ ensMock.ensDomainA.protocol }/>
<Box maxW="150px">
<EnsEntity domain={ ensMock.ensDomainB.name } protocol={ ensMock.ensDomainB.protocol }/>
</Box>
<EnsEntity domain={ ensMock.ensDomainA.name } protocol={ ensMock.ensDomainA.protocol } isLoading/>
</Sample>
<Sample label="Size: md, lg??? (heading)" vertical w="100%">
<EnsEntity domain={ ensMock.ensDomainA.name } protocol={ ensMock.ensDomainA.protocol } size="md"/>
<EnsEntity domain={ ensMock.ensDomainA.name } protocol={ ensMock.ensDomainA.protocol } size="lg" textStyle="heading.md"/>
</Sample>
</SamplesStack>
<SectionSubHeader>Blob link</SectionSubHeader>
<SamplesStack>
<Sample label="Default" vertical w="100%">
<BlobEntity hash={ blobsMock.base1.hash }/>
<Box maxW="200px">
<BlobEntity hash={ blobsMock.base1.hash } isExternal/>
</Box>
<BlobEntity hash={ blobsMock.base1.hash } isLoading/>
</Sample>
</SamplesStack>
<SectionSubHeader>Pool link</SectionSubHeader>
<SamplesStack>
<Sample label="Default" vertical w="100%">
<PoolEntity pool={{
...poolMock.base,
base_token_icon_url: 'https://coin-images.coingecko.com/coins/images/39926/large/usds.webp?1726666683',
quote_token_icon_url: 'https://coin-images.coingecko.com/coins/images/39925/large/sky.jpg?1724827980',
}}/>
<Box maxW="150px">
<PoolEntity pool={ poolMock.noIcons } isExternal/>
</Box>
<PoolEntity pool={ poolMock.noIcons } isLoading/>
</Sample>
</SamplesStack>
</Section>
</Container>
);
};
export default React.memo(LinksShowcase);
import type { StackProps, TabsContentProps } from '@chakra-ui/react';
import { Code, Grid, HStack } from '@chakra-ui/react';
import { Code, Grid, HStack, VStack } from '@chakra-ui/react';
import React from 'react';
import { Heading } from 'toolkit/chakra/heading';
......@@ -13,16 +13,28 @@ export const SamplesStack = ({ children }: { children: React.ReactNode }) => (
<Grid
rowGap={ 4 }
columnGap={ 8 }
gridTemplateColumns="fit-content(100%) fit-content(100%)"
gridTemplateColumns="fit-content(100%) 1fr"
justifyItems="flex-start"
alignItems="flex-start"
>
{ children }
</Grid>
);
export const Sample = ({ children, label, ...props }: { children: React.ReactNode; label?: string } & StackProps) => (
<>
{ label && <Code w="fit-content">{ label }</Code> }
<HStack gap={ 3 } whiteSpace="pre-wrap" flexWrap="wrap" columnSpan={ label ? '1' : '2' } { ...props }>{ children }</HStack>
</>
);
export const Sample = ({ children, label, vertical, ...props }: { children: React.ReactNode; vertical?: boolean; label?: string } & StackProps) => {
const Stack = vertical ? VStack : HStack;
return (
<>
{ label && <Code w="fit-content">{ label }</Code> }
<Stack
gap={ 3 }
whiteSpace="pre-wrap"
flexWrap="wrap"
columnSpan={ label ? '1' : '2' }
alignItems={ vertical ? 'flex-start' : 'center' }
{ ...props }
>
{ children }
</Stack>
</>
);
};
......@@ -19,7 +19,7 @@ const FooterLinkItem = ({ icon, iconSize, text, url, isLoading }: Props) => {
}
return (
<Link href={ url } display="flex" alignItems="center" h="30px" visual="subtle" target="_blank" textStyle="xs">
<Link href={ url } display="flex" alignItems="center" h="30px" variant="subtle" target="_blank" textStyle="xs">
{ icon && (
<Center minW={ 6 } mr={ 2 }>
<IconSvg boxSize={ iconSize || 5 } name={ icon }/>
......
......@@ -37,7 +37,7 @@ const NavLink = ({ className, item, noIcon }: Props) => {
href={ href }
display="flex"
alignItems="center"
visual="navigation"
variant="navigation"
{ ...(isActive ? { 'data-selected': true } : {}) }
w="224px"
px={ 2 }
......
......@@ -50,7 +50,7 @@ const NavLinkGroup = ({ item }: Props) => {
visual="popover"
content={ content }
onOpenChange={ onOpenChange }
lazyMount
lazyMount={ false }
positioning={{
placement: 'bottom',
offset: { mainAxis: 8 },
......@@ -66,7 +66,7 @@ const NavLinkGroup = ({ item }: Props) => {
py={ 1.5 }
textStyle="sm"
fontWeight={ 500 }
visual="navigation"
variant="navigation"
{ ...(item.isActive ? { 'data-selected': true } : {}) }
{ ...(open ? { 'data-active': true } : {}) }
borderRadius="base"
......
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