Commit b8eb137f authored by tom's avatar tom

collapse long tags row

parent 963aaf40
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
<g fill="currentColor" clip-path="url(#explorer_svg__a)">
<path d="M14.192 5.808a.624.624 0 0 0-.64-.15L7.927 7.531a.625.625 0 0 0-.395.395l-1.875 5.625a.625.625 0 0 0 .79.791l5.626-1.875a.626.626 0 0 0 .395-.395l1.875-5.625a.625.625 0 0 0-.151-.64Zm-6.954 6.954L8.62 8.619l2.762 2.762-4.143 1.38Z"/>
<path d="M10 18.75A8.75 8.75 0 1 1 18.75 10 8.76 8.76 0 0 1 10 18.75ZM10 2.5a7.5 7.5 0 1 0 7.5 7.5A7.509 7.509 0 0 0 10 2.5Z"/>
</g>
<defs>
<clipPath id="explorer_svg__a">
<path fill="currentColor" d="M0 0h20v20H0z"/>
</clipPath>
</defs>
</svg>
...@@ -10,6 +10,7 @@ import iconSuccess from 'icons/status/success.svg'; ...@@ -10,6 +10,7 @@ import iconSuccess from 'icons/status/success.svg';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/appContext'; import { useAppContext } from 'lib/appContext';
import useContractTabs from 'lib/hooks/useContractTabs'; import useContractTabs from 'lib/hooks/useContractTabs';
import useIsMobile from 'lib/hooks/useIsMobile';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import AddressBlocksValidated from 'ui/address/AddressBlocksValidated'; import AddressBlocksValidated from 'ui/address/AddressBlocksValidated';
import AddressCoinBalance from 'ui/address/AddressCoinBalance'; import AddressCoinBalance from 'ui/address/AddressCoinBalance';
...@@ -39,7 +40,7 @@ const TOKEN_TABS = Object.values(tokenTabsByType); ...@@ -39,7 +40,7 @@ const TOKEN_TABS = Object.values(tokenTabsByType);
const AddressPageContent = () => { const AddressPageContent = () => {
const router = useRouter(); const router = useRouter();
const isMobile = useIsMobile();
const appProps = useAppContext(); const appProps = useAppContext();
const tabsScrollRef = React.useRef<HTMLDivElement>(null); const tabsScrollRef = React.useRef<HTMLDivElement>(null);
...@@ -98,7 +99,7 @@ const AddressPageContent = () => { ...@@ -98,7 +99,7 @@ const AddressPageContent = () => {
addressQuery.data?.token ? { label: 'token', display_name: 'Token' } : undefined, addressQuery.data?.token ? { label: 'token', display_name: 'Token' } : undefined,
] } ] }
contentAfter={ contentAfter={
<NetworkExplorers type="address" pathParam={ hash } ml="auto"/> <NetworkExplorers type="address" pathParam={ hash } ml="auto" hideText={ isMobile }/>
} }
/> />
); );
......
...@@ -106,7 +106,6 @@ const BlockPageContent = () => { ...@@ -106,7 +106,6 @@ const BlockPageContent = () => {
title={ `Block #${ blockQuery.data?.height }` } title={ `Block #${ blockQuery.data?.height }` }
backLink={ backLink } backLink={ backLink }
contentAfter={ <NetworkExplorers type="block" pathParam={ height } ml={{ base: 'initial', lg: 'auto' }}/> } contentAfter={ <NetworkExplorers type="block" pathParam={ height } ml={{ base: 'initial', lg: 'auto' }}/> }
withTextAd
/> />
) } ) }
{ blockQuery.isLoading ? <SkeletonTabs/> : ( { blockQuery.isLoading ? <SkeletonTabs/> : (
......
...@@ -236,7 +236,7 @@ const TokenPageContent = () => { ...@@ -236,7 +236,7 @@ const TokenPageContent = () => {
tokenQuery.data ? { label: tokenQuery.data?.type, display_name: tokenQuery.data?.type } : undefined, tokenQuery.data ? { label: tokenQuery.data?.type, display_name: tokenQuery.data?.type } : undefined,
] } ] }
contentAfter={ contentAfter={
<NetworkExplorers type="token" pathParam={ hashString } ml="auto"/> <NetworkExplorers type="token" pathParam={ hashString } ml="auto" hideText={ isMobile }/>
} }
flexGrow={ 1 } flexGrow={ 1 }
/> />
......
...@@ -5,6 +5,7 @@ import type { RoutedTab } from 'ui/shared/Tabs/types'; ...@@ -5,6 +5,7 @@ import type { RoutedTab } from 'ui/shared/Tabs/types';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/appContext'; import { useAppContext } from 'lib/appContext';
import useIsMobile from 'lib/hooks/useIsMobile';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import TextAd from 'ui/shared/ad/TextAd'; import TextAd from 'ui/shared/ad/TextAd';
import EntityTags from 'ui/shared/EntityTags'; import EntityTags from 'ui/shared/EntityTags';
...@@ -31,6 +32,7 @@ const TABS: Array<RoutedTab> = [ ...@@ -31,6 +32,7 @@ const TABS: Array<RoutedTab> = [
const TransactionPageContent = () => { const TransactionPageContent = () => {
const router = useRouter(); const router = useRouter();
const appProps = useAppContext(); const appProps = useAppContext();
const isMobile = useIsMobile();
const hash = getQueryParamString(router.query.hash); const hash = getQueryParamString(router.query.hash);
...@@ -44,7 +46,7 @@ const TransactionPageContent = () => { ...@@ -44,7 +46,7 @@ const TransactionPageContent = () => {
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
tagsBefore={ [ data?.tx_tag ? { label: data.tx_tag, display_name: data.tx_tag } : undefined ] } tagsBefore={ [ data?.tx_tag ? { label: data.tx_tag, display_name: data.tx_tag } : undefined ] }
contentAfter={ contentAfter={
<NetworkExplorers type="tx" pathParam={ hash } ml={{ base: 'initial', lg: 'auto' }}/> <NetworkExplorers type="tx" pathParam={ hash } ml={{ base: 'initial', lg: 'auto' }} hideText={ isMobile && Boolean(data?.tx_tag) }/>
} }
/> />
); );
......
import { Flex, chakra } from '@chakra-ui/react'; import { Flex, chakra, useDisclosure, Popover, PopoverTrigger, PopoverContent, PopoverBody } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { UserTags } from 'types/api/addressParams'; import type { UserTags } from 'types/api/addressParams';
import useIsMobile from 'lib/hooks/useIsMobile';
import Tag from 'ui/shared/chakra/Tag'; import Tag from 'ui/shared/chakra/Tag';
interface TagData { interface TagData {
...@@ -20,6 +21,9 @@ interface Props { ...@@ -20,6 +21,9 @@ interface Props {
} }
const EntityTags = ({ className, data, tagsBefore = [], tagsAfter = [], isLoading, contentAfter }: Props) => { const EntityTags = ({ className, data, tagsBefore = [], tagsAfter = [], isLoading, contentAfter }: Props) => {
const isMobile = useIsMobile();
const { isOpen, onToggle, onClose } = useDisclosure();
const tags = [ const tags = [
...tagsBefore, ...tagsBefore,
...(data?.private_tags || []), ...(data?.private_tags || []),
...@@ -27,16 +31,55 @@ const EntityTags = ({ className, data, tagsBefore = [], tagsAfter = [], isLoadin ...@@ -27,16 +31,55 @@ const EntityTags = ({ className, data, tagsBefore = [], tagsAfter = [], isLoadin
...(data?.watchlist_names || []), ...(data?.watchlist_names || []),
...tagsAfter, ...tagsAfter,
] ]
.filter(Boolean) .filter(Boolean);
.map((tag) => <Tag key={ tag.label } isLoading={ isLoading }>{ tag.display_name }</Tag>);
if (tags.length === 0) { if (tags.length === 0) {
return null; return null;
} }
const content = (() => {
if (isMobile && tags.length > 2) {
return (
<>
{
tags
.slice(0, 2)
.map((tag) => (
<Tag key={ tag.label } isLoading={ isLoading } isTruncated maxW={{ base: '115px', lg: 'initial' }}>
{ tag.display_name }
</Tag>
))
}
<Popover isOpen={ isOpen } onClose={ onClose } placement="bottom-start" isLazy>
<PopoverTrigger>
<Tag onClick={ onToggle }>+{ tags.length - 1 }</Tag>
</PopoverTrigger>
<PopoverContent w="240px">
<PopoverBody >
<Flex columnGap={ 2 } rowGap={ 2 } flexWrap="wrap">
{
tags
.slice(2)
.map((tag) => <Tag key={ tag.label }>{ tag.display_name }</Tag>)
}
</Flex>
</PopoverBody>
</PopoverContent>
</Popover>
</>
);
}
return tags.map((tag) => (
<Tag key={ tag.label } isLoading={ isLoading } isTruncated maxW={{ base: '115px', lg: 'initial' }}>
{ tag.display_name }
</Tag>
));
})();
return ( return (
<Flex className={ className } columnGap={ 2 } rowGap={ 2 } flexWrap="wrap" alignItems="center"> <Flex className={ className } columnGap={ 2 } rowGap={ 2 } flexWrap="wrap" alignItems="center">
{ tags } { content }
{ contentAfter } { contentAfter }
</Flex> </Flex>
); );
......
...@@ -5,16 +5,17 @@ import type { NetworkExplorer as TNetworkExplorer } from 'types/networks'; ...@@ -5,16 +5,17 @@ import type { NetworkExplorer as TNetworkExplorer } from 'types/networks';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import arrowIcon from 'icons/arrows/east-mini.svg'; import arrowIcon from 'icons/arrows/east-mini.svg';
import searchIcon from 'icons/search.svg'; import explorerIcon from 'icons/explorer.svg';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/LinkExternal';
interface Props { interface Props {
className?: string; className?: string;
type: keyof TNetworkExplorer['paths']; type: keyof TNetworkExplorer['paths'];
pathParam: string; pathParam: string;
hideText?: boolean;
} }
const NetworkExplorers = ({ className, type, pathParam }: Props) => { const NetworkExplorers = ({ className, type, pathParam, hideText }: Props) => {
const { isOpen, onToggle, onClose } = useDisclosure(); const { isOpen, onToggle, onClose } = useDisclosure();
const explorersLinks = appConfig.network.explorers const explorersLinks = appConfig.network.explorers
...@@ -42,8 +43,8 @@ const NetworkExplorers = ({ className, type, pathParam }: Props) => { ...@@ -42,8 +43,8 @@ const NetworkExplorers = ({ className, type, pathParam }: Props) => {
px={ 2 } px={ 2 }
h="30px" h="30px"
> >
<Icon as={ searchIcon } boxSize={ 4 } mr={ 1 }/> <Icon as={ explorerIcon } boxSize={ 5 } mr={ hideText ? 0 : 1 }/>
<span>Explorers</span> { !hideText && <span>Explorers</span> }
<Icon as={ arrowIcon } transform={ isOpen ? 'rotate(90deg)' : 'rotate(-90deg)' } transitionDuration="faster" boxSize={ 5 } ml={ 1 }/> <Icon as={ arrowIcon } transform={ isOpen ? 'rotate(90deg)' : 'rotate(-90deg)' } transitionDuration="faster" boxSize={ 5 } ml={ 1 }/>
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
......
...@@ -8,7 +8,7 @@ interface Props extends TagProps { ...@@ -8,7 +8,7 @@ interface Props extends TagProps {
isLoading?: boolean; isLoading?: boolean;
} }
const Tag = ({ isLoading, ...props }: Props) => { const Tag = ({ isLoading, ...props }: Props, ref: React.ForwardedRef<HTMLButtonElement>) => {
if (props.isTruncated && typeof props.children === 'string') { if (props.isTruncated && typeof props.children === 'string') {
if (!props.children) { if (!props.children) {
...@@ -18,16 +18,16 @@ const Tag = ({ isLoading, ...props }: Props) => { ...@@ -18,16 +18,16 @@ const Tag = ({ isLoading, ...props }: Props) => {
return ( return (
<Skeleton isLoaded={ !isLoading } display="inline-block" borderRadius="sm" maxW="100%"> <Skeleton isLoaded={ !isLoading } display="inline-block" borderRadius="sm" maxW="100%">
<TruncatedTextTooltip label={ props.children }> <TruncatedTextTooltip label={ props.children }>
<ChakraTag { ...props }/> <ChakraTag { ...props } ref={ ref }/>
</TruncatedTextTooltip> </TruncatedTextTooltip>
</Skeleton> </Skeleton>
); );
} }
return ( return (
<Skeleton isLoaded={ !isLoading } display="inline-block" borderRadius="sm" maxW="100%"> <Skeleton isLoaded={ !isLoading } display="inline-block" borderRadius="sm" maxW="100%">
<ChakraTag { ...props }/> <ChakraTag { ...props } ref={ ref }/>
</Skeleton> </Skeleton>
); );
}; };
export default React.memo(Tag); export default React.memo(React.forwardRef(Tag));
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