UNPKG

3.44 kBJavaScriptView Raw
1'use client';
2
3import useControlled from '@mui/utils/useControlled';
4export default function usePagination(props = {}) {
5 // keep default values in sync with @default tags in Pagination.propTypes
6 const {
7 boundaryCount = 1,
8 componentName = 'usePagination',
9 count = 1,
10 defaultPage = 1,
11 disabled = false,
12 hideNextButton = false,
13 hidePrevButton = false,
14 onChange: handleChange,
15 page: pageProp,
16 showFirstButton = false,
17 showLastButton = false,
18 siblingCount = 1,
19 ...other
20 } = props;
21 const [page, setPageState] = useControlled({
22 controlled: pageProp,
23 default: defaultPage,
24 name: componentName,
25 state: 'page'
26 });
27 const handleClick = (event, value) => {
28 if (!pageProp) {
29 setPageState(value);
30 }
31 if (handleChange) {
32 handleChange(event, value);
33 }
34 };
35
36 // https://dev.to/namirsab/comment/2050
37 const range = (start, end) => {
38 const length = end - start + 1;
39 return Array.from({
40 length
41 }, (_, i) => start + i);
42 };
43 const startPages = range(1, Math.min(boundaryCount, count));
44 const endPages = range(Math.max(count - boundaryCount + 1, boundaryCount + 1), count);
45 const siblingsStart = Math.max(Math.min(
46 // Natural start
47 page - siblingCount,
48 // Lower boundary when page is high
49 count - boundaryCount - siblingCount * 2 - 1),
50 // Greater than startPages
51 boundaryCount + 2);
52 const siblingsEnd = Math.min(Math.max(
53 // Natural end
54 page + siblingCount,
55 // Upper boundary when page is low
56 boundaryCount + siblingCount * 2 + 2),
57 // Less than endPages
58 count - boundaryCount - 1);
59
60 // Basic list of items to render
61 // for example itemList = ['first', 'previous', 1, 'ellipsis', 4, 5, 6, 'ellipsis', 10, 'next', 'last']
62 const itemList = [...(showFirstButton ? ['first'] : []), ...(hidePrevButton ? [] : ['previous']), ...startPages,
63 // Start ellipsis
64 // eslint-disable-next-line no-nested-ternary
65 ...(siblingsStart > boundaryCount + 2 ? ['start-ellipsis'] : boundaryCount + 1 < count - boundaryCount ? [boundaryCount + 1] : []),
66 // Sibling pages
67 ...range(siblingsStart, siblingsEnd),
68 // End ellipsis
69 // eslint-disable-next-line no-nested-ternary
70 ...(siblingsEnd < count - boundaryCount - 1 ? ['end-ellipsis'] : count - boundaryCount > boundaryCount ? [count - boundaryCount] : []), ...endPages, ...(hideNextButton ? [] : ['next']), ...(showLastButton ? ['last'] : [])];
71
72 // Map the button type to its page number
73 const buttonPage = type => {
74 switch (type) {
75 case 'first':
76 return 1;
77 case 'previous':
78 return page - 1;
79 case 'next':
80 return page + 1;
81 case 'last':
82 return count;
83 default:
84 return null;
85 }
86 };
87
88 // Convert the basic item list to PaginationItem props objects
89 const items = itemList.map(item => {
90 return typeof item === 'number' ? {
91 onClick: event => {
92 handleClick(event, item);
93 },
94 type: 'page',
95 page: item,
96 selected: item === page,
97 disabled,
98 'aria-current': item === page ? 'true' : undefined
99 } : {
100 onClick: event => {
101 handleClick(event, buttonPage(item));
102 },
103 type: item,
104 page: buttonPage(item),
105 selected: false,
106 disabled: disabled || !item.includes('ellipsis') && (item === 'next' || item === 'last' ? page >= count : page <= 1)
107 };
108 });
109 return {
110 items,
111 ...other
112 };
113}
\No newline at end of file