gitscope
https://dev.kronoz.win/r/marzneshin.git
app/dashboard/src/components/table/pagination.tsx · v0.1.3
Source
1 a47eb4c Dawsh 2y ago import {
2 a47eb4c Dawsh 2y ago Box,
3 a47eb4c Dawsh 2y ago Button,
4 a47eb4c Dawsh 2y ago ButtonGroup,
5 a47eb4c Dawsh 2y ago chakra,
6 a47eb4c Dawsh 2y ago HStack,
7 a47eb4c Dawsh 2y ago Select,
8 a47eb4c Dawsh 2y ago Text,
9 bf7c131 Mardin 2y ago } from '@chakra-ui/react';
10 a47eb4c Dawsh 2y ago import {
11 a47eb4c Dawsh 2y ago ArrowLongLeftIcon,
12 a47eb4c Dawsh 2y ago ArrowLongRightIcon,
13 bf7c131 Mardin 2y ago } from '@heroicons/react/24/outline';
14 bf7c131 Mardin 2y ago import { ChangeEvent, FC } from 'react';
15 bf7c131 Mardin 2y ago import { useTranslation } from 'react-i18next';
16 89f8f93 Mardin 2y ago import { FilterType } from 'types';
17 6aab6f0 Mrdn 2y ago import { PageSizeManager } from 'utils/userPreferenceStorage';
18 a47eb4c Dawsh 2y ago
19 a47eb4c Dawsh 2y ago const PrevIcon = chakra(ArrowLongLeftIcon, {
20 a47eb4c Dawsh 2y ago baseStyle: {
21 a47eb4c Dawsh 2y ago w: 4,
22 a47eb4c Dawsh 2y ago h: 4,
23 a47eb4c Dawsh 2y ago },
24 a47eb4c Dawsh 2y ago });
25 a47eb4c Dawsh 2y ago const NextIcon = chakra(ArrowLongRightIcon, {
26 a47eb4c Dawsh 2y ago baseStyle: {
27 a47eb4c Dawsh 2y ago w: 4,
28 a47eb4c Dawsh 2y ago h: 4,
29 a47eb4c Dawsh 2y ago },
30 a47eb4c Dawsh 2y ago });
31 a47eb4c Dawsh 2y ago
32 a47eb4c Dawsh 2y ago export type PaginationType = {
33 a47eb4c Dawsh 2y ago count: number;
34 a47eb4c Dawsh 2y ago perPage: number;
35 a47eb4c Dawsh 2y ago page: number;
36 a47eb4c Dawsh 2y ago onChange?: (page: number) => void;
37 a47eb4c Dawsh 2y ago };
38 a47eb4c Dawsh 2y ago
39 a47eb4c Dawsh 2y ago const MINIMAL_PAGE_ITEM_COUNT = 5;
40 a47eb4c Dawsh 2y ago
41 a47eb4c Dawsh 2y ago /**
42 a47eb4c Dawsh 2y ago * Generate numeric page items around current page.
43 a47eb4c Dawsh 2y ago * - Always include first and last page
44 a47eb4c Dawsh 2y ago * - Add ellipsis if needed
45 a47eb4c Dawsh 2y ago */
46 6aab6f0 Mrdn 2y ago interface PageItem {
47 6aab6f0 Mrdn 2y ago value: string | number;
48 6aab6f0 Mrdn 2y ago isPlaceholder?: boolean;
49 6aab6f0 Mrdn 2y ago }
50 6aab6f0 Mrdn 2y ago
51 6aab6f0 Mrdn 2y ago function validateWidth(width: number): void {
52 6aab6f0 Mrdn 2y ago if (width < MINIMAL_PAGE_ITEM_COUNT || width % 2 === 0) {
53 6aab6f0 Mrdn 2y ago throw new Error(`Must allow at least ${MINIMAL_PAGE_ITEM_COUNT} odd page items`);
54 a47eb4c Dawsh 2y ago }
55 6aab6f0 Mrdn 2y ago }
56 6aab6f0 Mrdn 2y ago
57 6aab6f0 Mrdn 2y ago function generatePageItems(total: number, current: number, width: number): PageItem[] {
58 6aab6f0 Mrdn 2y ago validateWidth(width);
59 6aab6f0 Mrdn 2y ago
60 a47eb4c Dawsh 2y ago if (total < width) {
61 6aab6f0 Mrdn 2y ago return Array.from({ length: total }, (_, index) => ({ value: index }));
62 a47eb4c Dawsh 2y ago }
63 6aab6f0 Mrdn 2y ago
64 6aab6f0 Mrdn 2y ago const left = Math.max(0, Math.min(total - width, current - Math.floor(width / 2)));
65 6aab6f0 Mrdn 2y ago const right = Math.min(left + width, total);
66 6aab6f0 Mrdn 2y ago
67 6aab6f0 Mrdn 2y ago const items: PageItem[] = [];
68 6aab6f0 Mrdn 2y ago
69 6aab6f0 Mrdn 2y ago if (left > 0) {
70 6aab6f0 Mrdn 2y ago items.push({ value: 0 }, { value: 'prev-more', isPlaceholder: true });
71 a47eb4c Dawsh 2y ago }
72 6aab6f0 Mrdn 2y ago
73 6aab6f0 Mrdn 2y ago for (let i = left; i < right; i++) {
74 6aab6f0 Mrdn 2y ago items.push({ value: i });
75 a47eb4c Dawsh 2y ago }
76 6aab6f0 Mrdn 2y ago
77 6aab6f0 Mrdn 2y ago if (right < total) {
78 6aab6f0 Mrdn 2y ago items.push({ value: 'next-more', isPlaceholder: true }, { value: total - 1 });
79 a47eb4c Dawsh 2y ago }
80 6aab6f0 Mrdn 2y ago
81 a47eb4c Dawsh 2y ago return items;
82 a47eb4c Dawsh 2y ago }
83 a47eb4c Dawsh 2y ago
84 4205669 Mardin 2y ago type PaginationProps = {
85 4205669 Mardin 2y ago total: number;
86 4205669 Mardin 2y ago onFilterChange: (filters: Partial<FilterType>) => void;
87 4205669 Mardin 2y ago filters: FilterType;
88 6aab6f0 Mrdn 2y ago pageSizeManager: PageSizeManager;
89 6aab6f0 Mrdn 2y ago pageListsNumbers?: number[];
90 4205669 Mardin 2y ago }
91 4205669 Mardin 2y ago
92 6aab6f0 Mrdn 2y ago export const Pagination: FC<PaginationProps> = ({
93 6aab6f0 Mrdn 2y ago filters,
94 6aab6f0 Mrdn 2y ago total,
95 6aab6f0 Mrdn 2y ago onFilterChange,
96 6aab6f0 Mrdn 2y ago pageSizeManager,
97 6aab6f0 Mrdn 2y ago pageListsNumbers = [10, 20, 30],
98 6aab6f0 Mrdn 2y ago }) => {
99 a47eb4c Dawsh 2y ago const { limit: perPage, offset } = filters;
100 a47eb4c Dawsh 2y ago
101 a47eb4c Dawsh 2y ago const page = (offset || 0) / (perPage || 1);
102 a47eb4c Dawsh 2y ago const noPages = Math.ceil(total / (perPage || 1));
103 a47eb4c Dawsh 2y ago const pages = generatePageItems(noPages, page, 7);
104 a47eb4c Dawsh 2y ago
105 a47eb4c Dawsh 2y ago const changePage = (page: number) => {
106 a47eb4c Dawsh 2y ago onFilterChange({
107 a47eb4c Dawsh 2y ago ...filters,
108 a47eb4c Dawsh 2y ago offset: page * (perPage as number),
109 a47eb4c Dawsh 2y ago });
110 a47eb4c Dawsh 2y ago };
111 a47eb4c Dawsh 2y ago
112 a47eb4c Dawsh 2y ago const handlePageSizeChange = (e: ChangeEvent<HTMLSelectElement>) => {
113 a47eb4c Dawsh 2y ago onFilterChange({
114 a47eb4c Dawsh 2y ago ...filters,
115 a47eb4c Dawsh 2y ago limit: parseInt(e.target.value),
116 a47eb4c Dawsh 2y ago });
117 6aab6f0 Mrdn 2y ago pageSizeManager.setPageSize(e.target.value);
118 a47eb4c Dawsh 2y ago };
119 a47eb4c Dawsh 2y ago
120 a47eb4c Dawsh 2y ago const { t } = useTranslation();
121 a47eb4c Dawsh 2y ago
122 a47eb4c Dawsh 2y ago return (
123 a47eb4c Dawsh 2y ago <HStack
124 a47eb4c Dawsh 2y ago justifyContent="space-between"
125 a47eb4c Dawsh 2y ago mt={4}
126 a47eb4c Dawsh 2y ago w="full"
127 a47eb4c Dawsh 2y ago display="flex"
128 a47eb4c Dawsh 2y ago columnGap={{ lg: 4, md: 0 }}
129 a47eb4c Dawsh 2y ago rowGap={{ md: 0, base: 4 }}
130 bf7c131 Mardin 2y ago flexDirection={{ md: 'row', base: 'column' }}
131 a47eb4c Dawsh 2y ago >
132 a47eb4c Dawsh 2y ago <Box order={{ base: 2, md: 1 }}>
133 a47eb4c Dawsh 2y ago <HStack>
134 a47eb4c Dawsh 2y ago <Select
135 a47eb4c Dawsh 2y ago minW="60px"
136 a47eb4c Dawsh 2y ago value={perPage}
137 a47eb4c Dawsh 2y ago onChange={handlePageSizeChange}
138 a47eb4c Dawsh 2y ago size="sm"
139 a47eb4c Dawsh 2y ago rounded="md"
140 a47eb4c Dawsh 2y ago >
141 6aab6f0 Mrdn 2y ago {pageListsNumbers.map((list, i) => {
142 6aab6f0 Mrdn 2y ago return (<option key={i}>{list}</option>)
143 6aab6f0 Mrdn 2y ago })}
144 a47eb4c Dawsh 2y ago </Select>
145 bf7c131 Mardin 2y ago <Text whiteSpace={'nowrap'} fontSize="sm">
146 bf7c131 Mardin 2y ago {t('itemsPerPage')}
147 a47eb4c Dawsh 2y ago </Text>
148 a47eb4c Dawsh 2y ago </HStack>
149 a47eb4c Dawsh 2y ago </Box>
150 a47eb4c Dawsh 2y ago
151 a47eb4c Dawsh 2y ago <ButtonGroup size="sm" isAttached variant="outline" order={{ base: 1, md: 2 }}>
152 a47eb4c Dawsh 2y ago <Button
153 a47eb4c Dawsh 2y ago leftIcon={<PrevIcon />}
154 a47eb4c Dawsh 2y ago onClick={changePage.bind(null, page - 1)}
155 a47eb4c Dawsh 2y ago isDisabled={page === 0 || noPages === 0}
156 a47eb4c Dawsh 2y ago >
157 bf7c131 Mardin 2y ago {t('previous')}
158 a47eb4c Dawsh 2y ago </Button>
159 6aab6f0 Mrdn 2y ago {pages.map((pageItem) => (
160 6aab6f0 Mrdn 2y ago <Button
161 6aab6f0 Mrdn 2y ago key={String(pageItem.value)}
162 6aab6f0 Mrdn 2y ago variant={String(pageItem.value) === String(page) ? 'solid' : 'outline'}
163 6aab6f0 Mrdn 2y ago onClick={() =>
164 6aab6f0 Mrdn 2y ago typeof pageItem.value === 'number' && changePage(pageItem.value)
165 6aab6f0 Mrdn 2y ago }
166 6aab6f0 Mrdn 2y ago >
167 6aab6f0 Mrdn 2y ago {String(pageItem.value) === 'prev-more' || String(pageItem.value) === 'next-more'
168 6aab6f0 Mrdn 2y ago ? '...'
169 6aab6f0 Mrdn 2y ago : (pageItem.value as number) + 1}
170 6aab6f0 Mrdn 2y ago </Button>
171 6aab6f0 Mrdn 2y ago ))}
172 a47eb4c Dawsh 2y ago <Button
173 a47eb4c Dawsh 2y ago rightIcon={<NextIcon />}
174 a47eb4c Dawsh 2y ago onClick={changePage.bind(null, page + 1)}
175 a47eb4c Dawsh 2y ago isDisabled={page + 1 === noPages || noPages === 0}
176 a47eb4c Dawsh 2y ago >
177 bf7c131 Mardin 2y ago {t('next')}
178 a47eb4c Dawsh 2y ago </Button>
179 a47eb4c Dawsh 2y ago </ButtonGroup>
180 a47eb4c Dawsh 2y ago </HStack>
181 a47eb4c Dawsh 2y ago );
182 a47eb4c Dawsh 2y ago };