@spaced-out/ui-design-system
Version:
Sense UI components library
156 lines (139 loc) • 3.95 kB
Flow
// @flow strict
import type {
PaginationBaseProps,
PaginationItemProps,
} from '../../components/Pagination';
import {range} from '../../utils/helpers';
export const usePagination = (
props: PaginationBaseProps,
): {
items: ?(PaginationItemProps[]),
} => {
const {
boundaryCount = 1,
totalPages = 1,
disabled = false,
hideNextButton = false,
hidePrevButton = false,
onChange: handleChange,
currentPage = 1,
showFirstButton = false,
showLastButton = false,
siblingCount = 1,
style = 'primary',
} = props;
let itemList = [];
const handleClick = (event, value) => {
if (handleChange) {
handleChange(value, event);
}
};
const startPages = range(1, Math.min(boundaryCount, totalPages));
const endPages = range(
Math.max(totalPages - boundaryCount + 1, boundaryCount + 1),
totalPages,
);
const siblingsStart = Math.max(
Math.min(
// Natural start
currentPage - siblingCount,
// Lower boundary when currentPage is high
totalPages - boundaryCount - siblingCount * 2 - 1,
),
// Greater than startPages
boundaryCount + 2,
);
const siblingsEnd = Math.min(
Math.max(
// Natural end
currentPage + siblingCount,
// Upper boundary when currentPage is low
boundaryCount + siblingCount * 2 + 2,
),
// Less than endPages
endPages.length > 0 ? endPages[0] - 2 : totalPages - 1,
);
// Basic list of items to render
// For primary style: itemList = ['first', 'previous', 1, 'start-ellipsis', 4, 5, 6, 'end-ellipsis', 10, 'next', 'last']
// For secondary style: itemList = ['first', 'previous', 'next', 'last']
if (style === 'primary') {
itemList = [
...(showFirstButton ? ['first'] : []),
...(hidePrevButton ? [] : ['previous']),
...startPages,
// Start ellipsis
// eslint-disable-next-line no-nested-ternary
...(siblingsStart > boundaryCount + 2
? ['start-ellipsis']
: boundaryCount + 1 < totalPages - boundaryCount
? [boundaryCount + 1]
: []),
// Sibling pages
...range(siblingsStart, siblingsEnd),
// End ellipsis
// eslint-disable-next-line no-nested-ternary
...(siblingsEnd < totalPages - boundaryCount - 1
? ['end-ellipsis']
: totalPages - boundaryCount > boundaryCount
? [totalPages - boundaryCount]
: []),
...endPages,
...(hideNextButton ? [] : ['next']),
...(showLastButton ? ['last'] : []),
];
} else if (style === 'secondary') {
itemList = [
...(showFirstButton ? ['first'] : []),
...(hidePrevButton ? [] : ['previous']),
...(hideNextButton ? [] : ['next']),
...(showLastButton ? ['last'] : []),
];
}
// Map the button type to its page number
const buttonPage = (type) => {
switch (type) {
case 'first':
return 1;
case 'previous':
return currentPage - 1;
case 'next':
return currentPage + 1;
case 'last':
return totalPages;
default:
return null;
}
};
// Convert the basic item list to contain PaginationItem props objects
const items = itemList.map((item, idx) =>
typeof item === 'number'
? {
id: idx.toString(),
onClick: (event) => {
handleClick(event, item);
},
type: 'page',
page: item,
selected: item === currentPage,
disabled,
}
: {
id: idx.toString(),
onClick: (event) => {
handleClick(event, buttonPage(item));
},
type: item,
page: buttonPage(item),
selected: false,
disabled:
disabled ||
(item.indexOf('ellipsis') === -1 &&
(item === 'next' || item === 'last'
? currentPage >= totalPages
: currentPage <= 1)),
},
);
return {
items,
};
};