Commit a44d803f authored by isstuev's avatar isstuev

mud mobile views

parent 0891adbd
import { Grid, Box, useColorModeValue, chakra, HStack } from '@chakra-ui/react';
import { Box, useColorModeValue, chakra, HStack } from '@chakra-ui/react';
import React from 'react';
import { route } from 'nextjs-routes';
import useIsMobile from 'lib/hooks/useIsMobile';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/links/LinkInternal';
......@@ -70,8 +71,18 @@ const BreadcrumbItem = ({ text, href, isLast, scrollRef }: BreadcrumbItemProps)
const AddressMudBreadcrumbs = (props: TableViewProps | RecordViewProps) => {
const queryParams = { tab: 'mud', hash: props.hash };
const isMobile = useIsMobile();
return (
<Grid templateColumns="20px auto auto auto" gap={ 2 } alignItems="center" className={ props.className }>
<Box
display={ isMobile ? 'flex' : 'grid' }
flexWrap="wrap"
gridTemplateColumns="20px auto auto auto"
gap={ 2 }
alignItems="center"
className={ props.className }
width="fit-content"
>
<IconSvg name="MUD" boxSize={ 5 } color="green.500"/>
<BreadcrumbItem
text="MUD World"
......@@ -93,7 +104,7 @@ const AddressMudBreadcrumbs = (props: TableViewProps | RecordViewProps) => {
/>
) }
</Grid>
</Box>
);
};
......
import { Box, Td, Tr, Flex, useColorModeValue, Table } from '@chakra-ui/react';
import { Box, Td, Tr, Flex, Text, Table, Show, Hide, Divider, VStack } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
......@@ -9,6 +9,7 @@ import ContentLoader from 'ui/shared/ContentLoader';
import TruncatedValue from 'ui/shared/TruncatedValue';
import AddressMudBreadcrumbs from './AddressMudBreadcrumbs';
import AddressMudRecordValues from './AddressMudRecordValues';
type Props ={
scrollRef?: React.RefObject<HTMLDivElement>;
......@@ -22,8 +23,6 @@ const AddressMudRecord = ({ tableId, recordId, isQueryEnabled = true, scrollRef
const hash = getQueryParamString(router.query.hash);
const valuesBgColor = useColorModeValue('blackAlpha.50', 'whiteAlpha.50');
const { data, isLoading, isError } = useApiQuery('address_mud_record', {
pathParams: { hash, table_id: tableId, record_id: recordId },
queryOptions: {
......@@ -52,42 +51,41 @@ const AddressMudRecord = ({ tableId, recordId, isQueryEnabled = true, scrollRef
scrollRef={ scrollRef }
/>
) }
<Table borderRadius="8px" style={{ tableLayout: 'auto' }} width="100%">
{ data?.schema.key_names.length && data?.schema.key_names.map((keyName, index) => (
<Tr key={ keyName } borderBottomStyle={ index === data.schema.key_names.length - 1 ? 'hidden' : 'solid' }>
<Td fontWeight={ 600 } whiteSpace="nowrap">
{ keyName } ({ data.schema.key_types[index] })
</Td>
<Td colSpan={ 2 }>
<Flex justifyContent="space-between">
<TruncatedValue value={ data.record.decoded[keyName] } mr={ 2 }/>
{ index === 0 && <Box color="text_secondary">{ dayjs(data.record.timestamp).format('lll') }</Box> }
</Flex>
</Td>
</Tr>
)) }
{ data?.schema.value_names.length && (
<>
<Tr backgroundColor={ valuesBgColor } borderBottomStyle="hidden">
<Td fontWeight={ 600 }>Field</Td>
<Td fontWeight={ 600 }>Type</Td>
<Td fontWeight={ 600 } w="100%" wordBreak="break-all">Value</Td>
<Show above="lg" ssr={ false }>
<Table borderRadius="8px" style={{ tableLayout: 'auto' }} width="100%" overflow="hidden">
{ data?.schema.key_names.length && data?.schema.key_names.map((keyName, index) => (
<Tr key={ keyName } borderBottomStyle={ index === data.schema.key_names.length - 1 ? 'hidden' : 'solid' }>
<Td fontWeight={ 600 } whiteSpace="nowrap">
{ keyName } ({ data.schema.key_types[index] })
</Td>
<Td colSpan={ 2 }>
<Flex justifyContent="space-between">
<TruncatedValue value={ data.record.decoded[keyName] } mr={ 2 }/>
{ index === 0 && <Box color="text_secondary">{ dayjs(data.record.timestamp).format('lll') }</Box> }
</Flex>
</Td>
</Tr>
{ data?.schema.value_names.map((valName, index) => (
<Tr key={ valName } backgroundColor={ valuesBgColor } borderBottomStyle="hidden">
<Td whiteSpace="nowrap">{ valName }</Td>
<Td>{ data.schema.value_types[index] }</Td>
<Td w="100%" wordBreak="break-all">
<Box>
{ data.record.decoded[valName] }
</Box>
</Td>
</Tr>
)) }
</>
) }
</Table>
)) }
<AddressMudRecordValues data={ data }/>
</Table>
</Show>
<Hide above="lg" ssr={ false }>
<>
{ data?.schema.key_names.length && data?.schema.key_names.map((keyName, index) => (
<VStack gap={ 1 } key={ keyName } alignItems="start">
<Divider/>
<Text fontWeight={ 600 } whiteSpace="nowrap">
{ keyName } ({ data.schema.key_types[index] })
</Text>
<Text wordBreak="break-all">{ data.record.decoded[keyName] }</Text>
{ index === 0 && <Box color="text_secondary">{ dayjs(data.record.timestamp).format('lll') }</Box> }
</VStack>
)) }
<Table borderRadius="8px" style={{ tableLayout: 'auto' }} width="100%" mt={ 2 } overflow="hidden">
<AddressMudRecordValues data={ data }/>
</Table>
</>
</Hide>
</>
);
};
......
import { Box, Td, Tr, useColorModeValue } from '@chakra-ui/react';
import React from 'react';
import type { AddressMudRecord } from 'types/api/address';
type Props ={
data?: AddressMudRecord;
}
const AddressMudRecordValues = ({ data }: Props) => {
const valuesBgColor = useColorModeValue('blackAlpha.50', 'whiteAlpha.50');
if (!data?.schema.value_names.length) {
return null;
}
return (
<>
<Tr backgroundColor={ valuesBgColor } borderBottomStyle="hidden">
<Td fontWeight={ 600 }>Field</Td>
<Td fontWeight={ 600 }>Type</Td>
<Td fontWeight={ 600 } w="100%" wordBreak="break-all">Value</Td>
</Tr>
{
data?.schema.value_names.map((valName, index) => (
<Tr key={ valName } backgroundColor={ valuesBgColor } borderBottomStyle="hidden">
<Td whiteSpace="nowrap" py={ 0 } pb={ 4 }>{ valName }</Td>
<Td py={ 0 } pb={ 4 }>{ data.schema.value_types[index] }</Td>
<Td w="100%" wordBreak="break-all" py={ 0 } pb={ 4 }>
<Box>
{ data.record.decoded[valName] }
</Box>
</Td>
</Tr>
))
}
</>
);
};
export default AddressMudRecordValues;
......@@ -7,6 +7,7 @@ import type { AddressMudRecords, AddressMudRecordsFilter, AddressMudRecordsSorti
import capitalizeFirstLetter from 'lib/capitalizeFirstLetter';
import dayjs from 'lib/date/dayjs';
import useIsMobile from 'lib/hooks/useIsMobile';
import IconSvg from 'ui/shared/IconSvg';
import { default as Thead } from 'ui/shared/TheadSticky';
......@@ -14,7 +15,9 @@ import AddressMudRecordsKeyFilter from './AddressMudRecordsKeyFilter';
import { getNameTypeText } from './utils';
const COL_MIN_WIDTH = 180;
const COL_MIN_WIDTH_MOBILE = 140;
const CUT_COL_WIDTH = 36;
const MIN_CUT_COUNT = 2;
type Props = {
data: AddressMudRecords;
......@@ -37,9 +40,11 @@ const AddressMudRecordsTable = ({
toggleTableHasHorisontalScroll,
scrollRef,
}: Props) => {
const [ colsCutCount, setColsCutCount ] = React.useState<number>(0);
const totalColsCut = data.schema.key_names.length + data.schema.value_names.length;
const isMobile = useIsMobile(false);
const [ colsCutCount, setColsCutCount ] = React.useState<number>(isMobile ? 2 : 0);
const [ isOpened, setIsOpened ] = useBoolean(false);
const [ hasCut, setHasCut ] = useBoolean(true);
const [ hasCut, setHasCut ] = useBoolean(isMobile ? totalColsCut > MIN_CUT_COUNT : true);
const tableRef = React.useRef<HTMLTableElement>(null);
......@@ -56,10 +61,7 @@ const AddressMudRecordsTable = ({
record_id: e.currentTarget.getAttribute('data-id') as string,
};
router.push({ pathname: router.pathname, query: newQuery }, undefined, { shallow: true });
window.setTimeout(() => {
// cannot do scroll instantly, have to wait a little
scrollRef?.current?.scrollIntoView({ behavior: 'smooth' });
}, 500);
scrollRef?.current?.scrollIntoView();
}, [ router, scrollRef ]);
const handleFilterChange = React.useCallback((field: keyof AddressMudRecordsFilter) => (val: string) => {
......@@ -80,39 +82,42 @@ const AddressMudRecordsTable = ({
React.useEffect(() => {
if (hasCut && !colsCutCount && tableRef.current) {
const count = Math.floor((tableRef.current.getBoundingClientRect().width - CUT_COL_WIDTH) / COL_MIN_WIDTH);
const total = data.schema.key_names.length + data.schema.value_names.length;
if (total > 2 && count - 1 < total) {
if (totalColsCut > 2 && count - 1 < totalColsCut) {
setColsCutCount(count - 1);
} else {
setHasCut.off();
}
}
}, [ colsCutCount, data.schema, hasCut, setHasCut ]);
}, [ colsCutCount, data.schema, hasCut, setHasCut, totalColsCut ]);
const cutWidth = `${ CUT_COL_WIDTH }px `;
const colW = isMobile ? COL_MIN_WIDTH_MOBILE : COL_MIN_WIDTH;
const tdStyles: StyleProps = {
wordBreak: 'break-all',
whiteSpace: 'normal',
minW: `${ COL_MIN_WIDTH }px`,
w: `${ COL_MIN_WIDTH }px`,
minW: `${ colW }px`,
w: `${ colW }px`,
};
const thStyles: StyleProps = {
wordBreak: 'break-word',
whiteSpace: 'normal',
minW: `${ COL_MIN_WIDTH }px`,
w: `${ COL_MIN_WIDTH }px`,
minW: `${ colW }px`,
w: `${ colW }px`,
};
const keys = (isOpened || !hasCut) ? data.schema.key_names : data.schema.key_names.slice(0, colsCutCount);
const values = (isOpened || !hasCut) ? data.schema.value_names : data.schema.value_names.slice(0, colsCutCount - data.schema.key_names.length);
const hasHorizontalScroll = isMobile || isOpened;
return (
// can't implement both horisontal table scroll and sticky header
<Box maxW="100%" overflowX={ isOpened ? 'scroll' : 'unset' } whiteSpace="nowrap">
<Box maxW="100%" overflowX={ hasHorizontalScroll ? 'scroll' : 'unset' } whiteSpace="nowrap">
<Table variant="simple" size="sm" style={{ tableLayout: 'fixed' }} ref={ tableRef }>
<Thead top={ isOpened ? 0 : top } display={ isOpened ? 'table' : 'table-header-group' } w="100%">
<Thead top={ hasHorizontalScroll ? 0 : top } display={ hasHorizontalScroll ? 'table' : 'table-header-group' } w="100%">
<Tr >
{ keys.map((keyName, index) => {
const text = getNameTypeText(keyName, data.schema.key_types[index]);
......@@ -147,7 +152,7 @@ const AddressMudRecordsTable = ({
{ hasCut && isOpened && <Th width={ cutWidth }><Link onClick={ toggleIsOpen }>...</Link></Th> }
</Tr>
</Thead>
<Tbody display={ isOpened ? 'table' : 'table-row-group' } w="100%">
<Tbody display={ hasHorizontalScroll ? 'table' : 'table-row-group' } w="100%">
{ data.items.map((item) => (
<Tr key={ item.id }>
{ keys.map((keyName, index) => (
......
import { Box, HStack, Hide, Show, Tag, TagCloseButton, chakra, useBoolean } from '@chakra-ui/react';
import { Box, HStack, Tag, TagCloseButton, chakra, useBoolean } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
import type { AddressMudRecordsFilter, AddressMudRecordsSorting } from 'types/api/address';
import useIsMobile from 'lib/hooks/useIsMobile';
import { apos, nbsp } from 'lib/html-entities';
import getQueryParamString from 'lib/router/getQueryParamString';
import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar';
......@@ -31,7 +32,8 @@ const AddressMudTable = ({ scrollRef, tableId, isQueryEnabled = true }: Props) =
const [ sorting, setSorting ] =
React.useState<AddressMudRecordsSorting | undefined>(getSortParamsFromQuery<AddressMudRecordsSorting>(router.query, SORT_SEQUENCE));
const [ filters, setFilters ] = React.useState<AddressMudRecordsFilter>({});
const [ tableHasHorisontalScroll, setTableHasHorisontalScroll ] = useBoolean(false);
const isMobile = useIsMobile();
const [ tableHasHorisontalScroll, setTableHasHorisontalScroll ] = useBoolean(isMobile);
const hash = getQueryParamString(router.query.hash);
......@@ -93,18 +95,20 @@ const AddressMudTable = ({ scrollRef, tableId, isQueryEnabled = true }: Props) =
</HStack>
) : null;
const actionBar = (
const breadcrumbs = data ? (
<AddressMudBreadcrumbs
hash={ hash }
tableId={ tableId }
tableName={ data?.table.table_full_name }
scrollRef={ scrollRef }
mb={ hasActiveFilters ? 4 : 0 }
/>
) : null;
const actionBar = (!isMobile || hasActiveFilters || pagination.isVisible) && (
<ActionBar mt={ -6 } showShadow={ tableHasHorisontalScroll } justifyContent="space-between" alignItems={ hasActiveFilters ? 'start' : 'center' }>
<Box>
{ data && (
<AddressMudBreadcrumbs
hash={ hash }
tableId={ tableId }
tableName={ data?.table.table_full_name }
scrollRef={ scrollRef }
mb={ hasActiveFilters ? 4 : 0 }
/>
) }
{ !isMobile && breadcrumbs }
{ filtersTags }
</Box>
<Pagination ml={{ base: 0, lg: 8 }} { ...pagination }/>
......@@ -112,45 +116,36 @@ const AddressMudTable = ({ scrollRef, tableId, isQueryEnabled = true }: Props) =
);
const content = data?.items ? (
<>
<Hide below="lg" ssr={ false }>
<AddressMudRecordsTable
data={ data }
top={ actionBatHeight }
sorting={ sorting }
toggleSorting={ toggleSorting }
setFilters={ setFilters }
filters={ filters }
toggleTableHasHorisontalScroll={ setTableHasHorisontalScroll.toggle }
scrollRef={ scrollRef }
/>
</Hide>
<Show below="lg" ssr={ false }>
waiting for mobile mockup
{ /* { data.items.map((item, index) => (
<AddressMudListItem
key={ item.table.table_id + (isPlaceholderData ? String(index) : '') }
item={ item }
isLoading={ isPlaceholderData }
/>
)) } */ }
</Show>
</>
<AddressMudRecordsTable
data={ data }
top={ actionBatHeight }
sorting={ sorting }
toggleSorting={ toggleSorting }
setFilters={ setFilters }
filters={ filters }
toggleTableHasHorisontalScroll={ setTableHasHorisontalScroll.toggle }
scrollRef={ scrollRef }
/>
) : null;
return (
<DataListDisplay
isError={ isError }
items={ data?.items }
emptyText="There are no records for this table."
filterProps={{
emptyFilteredText: `Couldn${ apos }t find records that match your filter query.`,
hasActiveFilters: Object.values(filters).some(Boolean),
}}
content={ content }
actionBar={ actionBar }
showActionBarIfEmpty
/>
<>
{ isMobile && (
<Box mb={ 6 }>{ breadcrumbs }</Box>
) }
<DataListDisplay
isError={ isError }
items={ data?.items }
emptyText="There are no records for this table."
filterProps={{
emptyFilteredText: `Couldn${ apos }t find records that match your filter query.`,
hasActiveFilters: Object.values(filters).some(Boolean),
}}
content={ content }
actionBar={ actionBar }
showActionBarIfEmpty={ !isMobile }
/>
</>
);
};
......
......@@ -13,6 +13,7 @@ import FilterInput from 'ui/shared/filters/FilterInput';
import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import AddressMudTablesListItem from './AddressMudTablesListItem';
import AddressMudTablesTable from './AddressMudTablesTable';
type Props ={
......@@ -73,14 +74,13 @@ const AddressMudTables = ({ scrollRef, isQueryEnabled = true }: Props) => {
/>
</Hide>
<Show below="lg" ssr={ false }>
waiting for mobile mockup
{ /* { data.items.map((item, index) => (
<AddressMudListItem
{ data.items.map((item, index) => (
<AddressMudTablesListItem
key={ item.table.table_id + (isPlaceholderData ? String(index) : '') }
item={ item }
isLoading={ isPlaceholderData }
/>
)) } */ }
)) }
</Show>
</>
) : null;
......
import { Divider, Text, Skeleton, useBoolean, Flex, Link, VStack, chakra, Box, Grid, GridItem } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
import type { AddressMudTableItem } from 'types/api/address';
import Tag from 'ui/shared/chakra/Tag';
import HashStringShorten from 'ui/shared/HashStringShorten';
import IconSvg from 'ui/shared/IconSvg';
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
type Props = {
item: AddressMudTableItem;
isLoading: boolean;
scrollRef?: React.RefObject<HTMLDivElement>;
};
const AddressMudTablesListItem = ({ item, isLoading, scrollRef }: Props) => {
const [ isOpened, setIsOpened ] = useBoolean(false);
const router = useRouter();
const onTableClick = React.useCallback((e: React.MouseEvent) => {
const newQuery = {
...router.query,
table_id: e.currentTarget.getAttribute('data-id') as string,
};
router.push({ pathname: router.pathname, query: newQuery }, undefined, { shallow: true });
scrollRef?.current?.scrollIntoView();
}, [ router, scrollRef ]);
return (
<ListItemMobile rowGap={ 3 } fontSize="sm" py={ 3 }>
<Flex w="100%">
<Skeleton isLoaded={ !isLoading }>
<Link display="block">
<IconSvg
name="arrows/east-mini"
transform={ isOpened ? 'rotate(270deg)' : 'rotate(180deg)' }
boxSize={ 6 }
cursor="pointer"
onClick={ setIsOpened.toggle }
transitionDuration="faster"
/>
</Link>
</Skeleton>
<Box flexGrow="1">
<Flex justifyContent="space-between" height={ 6 } alignItems="center" mb={ 3 }>
<Skeleton isLoaded={ !isLoading }>
<Link onClick={ onTableClick } data-id={ item.table.table_id } fontWeight={ 500 }>
{ item.table.table_full_name }
</Link>
</Skeleton>
<Skeleton isLoaded={ !isLoading } color="text_secondary">
{ item.table.table_type }
</Skeleton>
</Flex>
<Skeleton isLoaded={ !isLoading } color="text_secondary">
<HashStringShorten hash={ item.table.table_id } type="long"/>
</Skeleton>
</Box>
</Flex>
{ isOpened && (
<Grid templateColumns="48px 1fr" gap="8px 24px" fontWeight={ 500 } w="100%">
{ Boolean(item.schema.key_names.length) && (
<>
<Text lineHeight="24px">Key</Text>
<VStack gap={ 1 } alignItems="start">
{ item.schema.key_names.map((name, index) => (
<Tag key={ name }>
<chakra.span fontWeight={ 700 }>{ item.schema.key_types[index] }</chakra.span> { name }
</Tag>
)) }
</VStack>
</>
) }
<GridItem colSpan={ 2 }><Divider/></GridItem>
<Text lineHeight="24px">Value</Text>
<VStack gap={ 1 } alignItems="start">
{ item.schema.value_names.map((name, index) => (
<Text key={ name }>
<chakra.span fontWeight={ 700 }>{ item.schema.value_types[index] }</chakra.span> { name }
</Text>
)) }
</VStack>
</Grid>
) }
</ListItemMobile>
);
};
export default React.memo(AddressMudTablesListItem);
......@@ -24,10 +24,7 @@ const AddressMudTablesTableItem = ({ item, isLoading, scrollRef }: Props) => {
table_id: e.currentTarget.getAttribute('data-id') as string,
};
router.push({ pathname: router.pathname, query: newQuery }, undefined, { shallow: true });
window.setTimeout(() => {
// cannot do scroll instantly, have to wait a little
scrollRef?.current?.scrollIntoView({ behavior: 'smooth' });
}, 500);
scrollRef?.current?.scrollIntoView();
}, [ router, scrollRef ]);
return (
......
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