Commit 011cd479 authored by tom's avatar tom

display object types in nested accordions

parent a3ff9bb2
import { Box, Flex } from '@chakra-ui/react'; import { Accordion } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { TokenInstance } from 'types/api/token'; import TokenInstanceMetadataAccordionItem from './TokenInstanceMetadataAccordionItem';
import type { ExcludeNull } from 'types/utils/ExcludeNull';
import LinkExternal from 'ui/shared/LinkExternal';
import { formatName } from './utils';
interface Props { interface Props {
data: ExcludeNull<TokenInstance['metadata']>; data: Record<string, unknown>;
level?: number;
} }
const TokenInstanceMetadataAccordion = ({ data }: Props) => { const TokenInstanceMetadataAccordion = ({ data, level = 0 }: Props) => {
const renderContent = React.useCallback((value: unknown) => {
switch (typeof value) {
case 'string': {
try {
if (!value.includes('http')) {
throw new Error();
}
const url = new URL(value);
return <LinkExternal href={ url.toString() }>{ value }</LinkExternal>;
} catch (error) {
return value;
}
}
case 'number': const ml = (() => {
case 'boolean': { if (level === 0) {
return value; return 0;
} }
default: { if (level === 1) {
return '-'; return 126;
}
} }
}, []);
return 24;
})();
return ( return (
<ul> <Accordion allowMultiple fontSize="sm" ml={ `${ ml }px` } defaultIndex={ level === 0 ? [ 0 ] : undefined }>
{ Object.entries(data).map(([ key, value ]) => { { Object.entries(data).map(([ key, value ]) => {
return ( return <TokenInstanceMetadataAccordionItem key={ key } name={ key } value={ value } level={ level }/>;
<Flex
as="li"
key={ key }
alignItems="flex-start"
py={ 2 }
pl={{ base: 0, lg: 6 }}
columnGap={ 3 }
borderTopWidth="1px"
borderColor="divider"
_last={{
borderBottomWidth: '1px',
}}
wordBreak="break-word"
fontSize="sm"
>
<Box w="90px" flexShrink={ 0 } fontWeight={ 600 }>{ formatName(key) }</Box>
<Box>{ renderContent(value) }</Box>
</Flex>
);
}) } }) }
</ul> </Accordion>
); );
}; };
export default TokenInstanceMetadataAccordion; export default React.memo(TokenInstanceMetadataAccordion);
import type { ChakraProps } from '@chakra-ui/react';
import { Box, AccordionItem, AccordionIcon, AccordionPanel, AccordionButton } from '@chakra-ui/react';
import React from 'react';
import LinkExternal from 'ui/shared/LinkExternal';
import TokenInstanceMetadataAccordion from './TokenInstanceMetadataAccordion';
import { formatName } from './utils';
interface Props {
name: string;
value: unknown;
level: number;
}
const TokenInstanceMetadataAccordionItem = ({ name, value, level }: Props) => {
const title = <Box w="90px" flexShrink={ 0 } fontWeight={ 600 } wordBreak="break-word">{ formatName(name) }</Box>;
const commonProps: ChakraProps = {
display: 'flex',
alignItems: 'flex-start',
py: 2,
pl: { base: 0, lg: 6 },
columnGap: 3,
borderTopWidth: '1px',
borderColor: 'divider',
wordBreak: 'break-all',
_last: {
borderBottomWidth: level === 0 ? '1px' : '0px',
},
_first: {
borderTopWidth: level === 0 ? '1px' : '0px',
},
};
switch (typeof value) {
case 'string': {
try {
if (!value.includes('http')) {
throw new Error();
}
const url = new URL(value);
return (
<AccordionItem { ...commonProps }>
{ title }
<LinkExternal href={ url.toString() }>{ value }</LinkExternal>
</AccordionItem>
);
} catch (error) {
return (
<AccordionItem { ...commonProps }>
{ title }
<Box>{ value }</Box>
</AccordionItem>
);
}
}
case 'number':
case 'boolean': {
return (
<AccordionItem { ...commonProps }>
{ title }
<Box>{ value }</Box>
</AccordionItem>
);
}
case 'object': {
if (!value) {
break;
}
if (Array.isArray(value)) {
break;
}
if (Object.keys(value).length === 0) {
break;
}
if (level >= 4) {
return (
<AccordionItem { ...commonProps }>
{ title }
<Box whiteSpace="pre-wrap">{ JSON.stringify(value, undefined, 2) }</Box>
</AccordionItem>
);
}
return (
<AccordionItem
{ ...commonProps }
flexDir="column"
alignItems="stretch"
pl={ 0 }
py={ 0 }
>
<AccordionButton
px={ 0 }
py={ 2 }
_hover={{ bgColor: 'inherit' }}
fontSize="sm"
textAlign="left"
_expanded={{
borderColor: 'divider',
borderBottomWidth: '1px',
}}
>
<AccordionIcon boxSize={ 6 } p={ 1 }/>
{ title }
</AccordionButton>
<AccordionPanel p={ 0 }>
<TokenInstanceMetadataAccordion data={ value as Record<string, unknown> } level={ level + 1 }/>
</AccordionPanel>
</AccordionItem>
);
}
}
return (
<AccordionItem { ...commonProps }>
{ title }
<Box>-</Box>
</AccordionItem>
);
};
export default React.memo(TokenInstanceMetadataAccordionItem);
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