/
use-pagination.ts
98 lines (81 loc) · 2.62 KB
/
use-pagination.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import { useMemo } from 'react';
import { useUncontrolled } from '../use-uncontrolled/use-uncontrolled';
import { range } from '../utils';
export const DOTS = 'dots';
export interface PaginationParams {
/** Page selected on initial render, defaults to 1 */
initialPage?: number;
/** Controlled active page number */
page?: number;
/** Total amount of pages */
total: number;
/** Siblings amount on left/right side of selected page, defaults to 1 */
siblings?: number;
/** Amount of elements visible on left/right edges, defaults to 1 */
boundaries?: number;
/** Callback fired after change of each page */
onChange?: (page: number) => void;
}
export function usePagination({
total,
siblings = 1,
boundaries = 1,
page,
initialPage = 1,
onChange,
}: PaginationParams) {
const _total = Math.max(Math.trunc(total), 0);
const [activePage, setActivePage] = useUncontrolled({
value: page,
onChange,
defaultValue: initialPage,
finalValue: initialPage,
});
const setPage = (pageNumber: number) => {
if (pageNumber <= 0) {
setActivePage(1);
} else if (pageNumber > _total) {
setActivePage(_total);
} else {
setActivePage(pageNumber);
}
};
const next = () => setPage(activePage + 1);
const previous = () => setPage(activePage - 1);
const first = () => setPage(1);
const last = () => setPage(_total);
const paginationRange = useMemo((): (number | 'dots')[] => {
const totalPageNumbers = siblings * 2 + 3 + boundaries * 2;
if (totalPageNumbers >= _total) {
return range(1, _total);
}
const leftSiblingIndex = Math.max(activePage - siblings, boundaries);
const rightSiblingIndex = Math.min(activePage + siblings, _total - boundaries);
const shouldShowLeftDots = leftSiblingIndex > boundaries + 2;
const shouldShowRightDots = rightSiblingIndex < _total - (boundaries + 1);
if (!shouldShowLeftDots && shouldShowRightDots) {
const leftItemCount = siblings * 2 + boundaries + 2;
return [...range(1, leftItemCount), DOTS, ...range(_total - (boundaries - 1), _total)];
}
if (shouldShowLeftDots && !shouldShowRightDots) {
const rightItemCount = boundaries + 1 + 2 * siblings;
return [...range(1, boundaries), DOTS, ...range(_total - rightItemCount, _total)];
}
return [
...range(1, boundaries),
DOTS,
...range(leftSiblingIndex, rightSiblingIndex),
DOTS,
...range(_total - boundaries + 1, _total),
];
}, [_total, siblings, activePage]);
return {
range: paginationRange,
active: activePage,
setPage,
next,
previous,
first,
last,
};
}