reka-ui
Version:
Vue port for Radix UI Primitives.
94 lines (72 loc) • 3.18 kB
text/typescript
// reference https://github.com/chakra-ui/zag/blob/main/packages/machines/pagination/src/pagination.utils.ts
type Pages = Array<{ type: 'ellipsis' } | { type: 'page', value: number }>
function range(start: number, end: number) {
const length = end - start + 1
return Array.from({ length }, (_, idx) => idx + start)
}
export function transform(items: (string | number)[]): Pages {
return items.map((value) => {
if (typeof value === 'number')
return { type: 'page', value }
return { type: 'ellipsis' }
})
}
const ELLIPSIS = 'ellipsis'
export function getRange(currentPage: number, pageCount: number, siblingCount: number, showEdges: boolean) {
const firstPageIndex = 1
const lastPageIndex = pageCount
const leftSiblingIndex = Math.max(currentPage - siblingCount, firstPageIndex)
const rightSiblingIndex = Math.min(currentPage + siblingCount, lastPageIndex)
if (showEdges) {
/**
* `2 * siblingCount + 5` explanation:
* 2 * siblingCount for left/right siblings
* 5 for 2x left/right ellipsis, 2x first/last page + 1x current page
*
* For some page counts (e.g. totalPages: 8, siblingCount: 2),
* calculated max page is higher than total pages,
* so we need to take the minimum of both.
*/
const totalPageNumbers = Math.min(2 * siblingCount + 5, pageCount)
const itemCount = totalPageNumbers - 2 // 2 stands for one ellipsis and either first or last page
const showLeftEllipsis
// default condition
= leftSiblingIndex > firstPageIndex + 2
// if the current page is towards the end of the list
&& Math.abs(lastPageIndex - itemCount - firstPageIndex + 1) > 2
// if the current page is towards the middle of the list
&& Math.abs(leftSiblingIndex - firstPageIndex) > 2
const showRightEllipsis
// default condition
= rightSiblingIndex < lastPageIndex - 2
// if the current page is towards the start of the list
&& Math.abs(lastPageIndex - itemCount) > 2
// if the current page is towards the middle of the list
&& Math.abs(lastPageIndex - rightSiblingIndex) > 2
if (!showLeftEllipsis && showRightEllipsis) {
const leftRange = range(1, itemCount)
return [...leftRange, ELLIPSIS, lastPageIndex]
}
if (showLeftEllipsis && !showRightEllipsis) {
const rightRange = range(lastPageIndex - itemCount + 1, lastPageIndex)
return [firstPageIndex, ELLIPSIS, ...rightRange]
}
if (showLeftEllipsis && showRightEllipsis) {
const middleRange = range(leftSiblingIndex, rightSiblingIndex)
return [firstPageIndex, ELLIPSIS, ...middleRange, ELLIPSIS, lastPageIndex]
}
const fullRange = range(firstPageIndex, lastPageIndex)
return fullRange
}
else {
const itemCount = siblingCount * 2 + 1
if (pageCount < itemCount)
return range(1, lastPageIndex)
else if (currentPage <= (siblingCount + 1))
return range(firstPageIndex, itemCount)
else if ((pageCount - currentPage) <= siblingCount)
return range(pageCount - itemCount + 1, lastPageIndex)
else
return range(leftSiblingIndex, rightSiblingIndex)
}
}