Commit a75ee466 authored by tom's avatar tom

list pagination

parent ed2e5189
...@@ -7,19 +7,18 @@ export interface IconButtonProps extends ButtonProps {} ...@@ -7,19 +7,18 @@ export interface IconButtonProps extends ButtonProps {}
export const IconButton = React.forwardRef<HTMLDivElement, IconButtonProps>( export const IconButton = React.forwardRef<HTMLDivElement, IconButtonProps>(
function IconButton(props, ref) { function IconButton(props, ref) {
const { loading, ...rest } = props; const { loading, size, variant = 'plain', ...rest } = props;
return ( return (
<Skeleton loading={ loading } asChild ref={ ref }> <Skeleton loading={ loading } ref={ ref }>
<Button <Button
display="inline-flex" display="inline-flex"
px="0" px="0"
py="0" py="0"
height="auto" { ...(size ? { size } : { height: 'auto', minW: 'auto' }) }
minW="auto"
flexShrink="0" flexShrink="0"
variant={ variant }
{ ...rest } { ...rest }
variant={ props.variant ?? 'plain' }
/> />
</Skeleton> </Skeleton>
); );
......
'use client';
import type { HTMLChakraProps, RecipeProps } from '@chakra-ui/react';
import { createRecipeContext } from '@chakra-ui/react';
export interface LinkButtonProps
extends HTMLChakraProps<'a', RecipeProps<'button'>> {}
const { withContext } = createRecipeContext({ key: 'button' });
// TODO @tom2drum style and use this component
// Replace "a" with your framework's link component
export const LinkButton = withContext<HTMLAnchorElement, LinkButtonProps>('a');
...@@ -8,7 +8,7 @@ import { route } from 'nextjs-routes'; ...@@ -8,7 +8,7 @@ import { route } from 'nextjs-routes';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { nbsp } from 'lib/html-entities'; import { nbsp } from 'lib/html-entities';
import { HOMEPAGE_STATS } from 'stubs/stats'; import { HOMEPAGE_STATS } from 'stubs/stats';
import Skeleton from 'ui/shared/chakra/Skeleton'; import { Skeleton } from 'toolkit/chakra/skeleton';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import LinkInternal from 'ui/shared/links/LinkInternal'; import LinkInternal from 'ui/shared/links/LinkInternal';
import Pagination from 'ui/shared/pagination/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
...@@ -31,7 +31,7 @@ const BlocksTabSlot = ({ pagination }: Props) => { ...@@ -31,7 +31,7 @@ const BlocksTabSlot = ({ pagination }: Props) => {
<Text as="span" fontSize="sm"> <Text as="span" fontSize="sm">
Network utilization (last 50 blocks):{ nbsp } Network utilization (last 50 blocks):{ nbsp }
</Text> </Text>
<Skeleton display="inline-block" fontSize="sm" color="blue.400" fontWeight={ 600 } isLoaded={ !statsQuery.isPlaceholderData }> <Skeleton display="inline-block" fontSize="sm" color="blue.400" fontWeight={ 600 } loading={ statsQuery.isPlaceholderData }>
<span>{ statsQuery.data.network_utilization_percentage.toFixed(2) }%</span> <span>{ statsQuery.data.network_utilization_percentage.toFixed(2) }%</span>
</Skeleton> </Skeleton>
</Box> </Box>
......
...@@ -25,6 +25,7 @@ import PageTitle from 'ui/shared/Page/PageTitle'; ...@@ -25,6 +25,7 @@ import PageTitle from 'ui/shared/Page/PageTitle';
import AlertsShowcase from 'ui/showcases/Alerts'; import AlertsShowcase from 'ui/showcases/Alerts';
import BadgesShowcase from 'ui/showcases/Badges'; import BadgesShowcase from 'ui/showcases/Badges';
import ButtonShowcase from 'ui/showcases/Button'; import ButtonShowcase from 'ui/showcases/Button';
import PaginationShowcase from 'ui/showcases/Pagination';
import TabsShowcase from 'ui/showcases/Tabs'; import TabsShowcase from 'ui/showcases/Tabs';
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 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.';
...@@ -53,6 +54,7 @@ const ChakraShowcases = () => { ...@@ -53,6 +54,7 @@ const ChakraShowcases = () => {
<TabsTrigger value="alerts">Alerts</TabsTrigger> <TabsTrigger value="alerts">Alerts</TabsTrigger>
<TabsTrigger value="badges">Badges</TabsTrigger> <TabsTrigger value="badges">Badges</TabsTrigger>
<TabsTrigger value="buttons">Buttons</TabsTrigger> <TabsTrigger value="buttons">Buttons</TabsTrigger>
<TabsTrigger value="pagination">Pagination</TabsTrigger>
<TabsTrigger value="tabs">Tabs</TabsTrigger> <TabsTrigger value="tabs">Tabs</TabsTrigger>
<TabsTrigger value="unsorted">Unsorted</TabsTrigger> <TabsTrigger value="unsorted">Unsorted</TabsTrigger>
</TabsList> </TabsList>
...@@ -60,6 +62,7 @@ const ChakraShowcases = () => { ...@@ -60,6 +62,7 @@ const ChakraShowcases = () => {
<BadgesShowcase/> <BadgesShowcase/>
<ButtonShowcase/> <ButtonShowcase/>
<TabsShowcase/> <TabsShowcase/>
<PaginationShowcase/>
<TabsContent value="unsorted"> <TabsContent value="unsorted">
<VStack align="flex-start" gap={ 6 }> <VStack align="flex-start" gap={ 6 }>
......
import { Button, Flex, IconButton, chakra } from '@chakra-ui/react'; import type { HTMLChakraProps } from '@chakra-ui/react';
import { Center, Flex } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { PaginationParams } from './types'; import type { PaginationParams } from './types';
import Skeleton from 'ui/shared/chakra/Skeleton'; import { Button } from 'toolkit/chakra/button';
import { IconButton } from 'toolkit/chakra/icon-button';
import { Skeleton } from 'toolkit/chakra/skeleton';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
interface Props extends PaginationParams { interface Props extends PaginationParams, Omit<HTMLChakraProps<'div'>, 'page' | 'direction'> {}
className?: string;
}
const Pagination = ({ page, onNextPageClick, onPrevPageClick, resetPage, hasPages, hasNextPage, className, canGoBackwards, isLoading, isVisible }: Props) => { const Pagination = (props: Props) => {
const { page, onNextPageClick, onPrevPageClick, resetPage, hasPages, hasNextPage, canGoBackwards, isLoading, isVisible, ...rest } = props;
if (!isVisible) { if (!isVisible) {
return null; return null;
...@@ -20,63 +22,62 @@ const Pagination = ({ page, onNextPageClick, onPrevPageClick, resetPage, hasPage ...@@ -20,63 +22,62 @@ const Pagination = ({ page, onNextPageClick, onPrevPageClick, resetPage, hasPage
return ( return (
<Flex <Flex
className={ className } as="nav"
fontSize="sm"
alignItems="center" alignItems="center"
{ ...rest }
> >
<Skeleton isLoaded={ !showSkeleton } display="inline-block" mr={ 4 } borderRadius="base"> <Skeleton loading={ showSkeleton } mr={ 4 }>
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
onClick={ resetPage } onClick={ resetPage }
isDisabled={ page === 1 || isLoading } disabled={ page === 1 || isLoading }
> >
First First
</Button> </Button>
</Skeleton> </Skeleton>
<Skeleton isLoaded={ !showSkeleton } display="inline-block" mr={ 3 } borderRadius="base"> <IconButton
<IconButton aria-label="Prev page"
variant="outline" variant="outline"
onClick={ onPrevPageClick } w={ 9 }
size="sm" size="sm"
aria-label="Prev page" onClick={ onPrevPageClick }
w="36px" disabled={ !canGoBackwards || isLoading || page === 1 }
icon={ <IconSvg name="arrows/east-mini" w={ 5 } h={ 5 }/> } loading={ showSkeleton }
isDisabled={ !canGoBackwards || isLoading } >
/> <IconSvg name="arrows/east-mini" boxSize={ 5 }/>
</Skeleton> </IconButton>
<Skeleton isLoaded={ !showSkeleton } display="inline-block" borderRadius="base"> <Skeleton loading={ showSkeleton } mx={ 2 } >
<Button <Center
variant="outline" display="flex"
size="sm" alignItems="center"
data-selected={ true } justifyContent="center"
borderWidth="1px"
fontWeight={ 400 }
h={ 8 } h={ 8 }
minW="36px" minW={ 9 }
cursor="unset" px={ 2 }
bgColor={{ _light: 'blue.50', _dark: 'whiteAlpha.100' }}
color={{ _light: 'blackAlpha.800', _dark: 'gray.50' }}
borderRadius="base"
textStyle="sm"
> >
{ page } { page }
</Button> </Center>
</Skeleton>
<Skeleton isLoaded={ !showSkeleton } display="inline-block" ml={ 3 } borderRadius="base">
<IconButton
variant="outline"
onClick={ onNextPageClick }
size="sm"
aria-label="Next page"
w="36px"
icon={ <IconSvg name="arrows/east-mini" w={ 5 } h={ 5 } transform="rotate(180deg)"/> }
isDisabled={ !hasNextPage || isLoading }
/>
</Skeleton> </Skeleton>
{ /* not implemented yet */ } <IconButton
{ /* <Flex alignItems="center" width="132px" ml={ 16 } display={{ base: 'none', lg: 'flex' }}> aria-label="Next page"
Go to <Input w="84px" size="xs" ml={ 2 }/> variant="outline"
</Flex> */ } w={ 9 }
size="sm"
onClick={ onNextPageClick }
disabled={ !hasNextPage || isLoading }
loading={ showSkeleton }
>
<IconSvg name="arrows/east-mini" boxSize={ 5 } transform="rotate(180deg)"/>
</IconButton>
</Flex> </Flex>
); );
}; };
export default chakra(Pagination); export default React.memo(Pagination);
import type { UseQueryResult } from '@tanstack/react-query'; import type { UseQueryResult } from '@tanstack/react-query';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import { omit } from 'es-toolkit'; import { clamp, omit } from 'es-toolkit';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import { animateScroll } from 'react-scroll'; import { animateScroll } from 'react-scroll';
...@@ -131,7 +131,7 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({ ...@@ -131,7 +131,7 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({
scrollToTop(); scrollToTop();
router.push({ pathname: router.pathname, query: nextPageQuery }, undefined, { shallow: true }) router.push({ pathname: router.pathname, query: nextPageQuery }, undefined, { shallow: true })
.then(() => { .then(() => {
setPage(prev => prev - 1); setPage(prev => clamp(prev - 1, 1, Infinity));
page === 2 && queryClient.removeQueries({ queryKey: [ resourceName ] }); page === 2 && queryClient.removeQueries({ queryKey: [ resourceName ] });
}); });
}, [ router, page, pageParams, scrollToTop, queryClient, resourceName ]); }, [ router, page, pageParams, scrollToTop, queryClient, resourceName ]);
......
import React from 'react';
import Pagination from 'ui/shared/pagination/Pagination';
import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts';
const PaginationShowcase = () => {
const [ page, setPage ] = React.useState(1);
const handleNextPageClick = () => {
setPage(page + 1);
};
const handlePrevPageClick = () => {
setPage(page - 1);
};
const props = {
page,
onNextPageClick: handleNextPageClick,
onPrevPageClick: handlePrevPageClick,
resetPage: () => setPage(1),
hasNextPage: page < 10,
hasPages: true,
isVisible: true,
canGoBackwards: true,
isLoading: false,
};
return (
<Container value="pagination">
<Section>
<SectionHeader>Examples</SectionHeader>
<SectionSubHeader>List pagination</SectionSubHeader>
<SamplesStack>
<Sample label="Loaded state">
<Pagination { ...props }/>
</Sample>
<Sample label="Initial loading state">
<Pagination { ...props } hasPages={ false } page={ 1 } isLoading/>
</Sample>
<Sample label="Next page loading state">
<Pagination { ...props } isLoading/>
</Sample>
</SamplesStack>
</Section>
</Container>
);
};
export default React.memo(PaginationShowcase);
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