Skip to content

Instantly share code, notes, and snippets.

@kraftdorian
Last active July 9, 2023 19:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kraftdorian/a942e51f391160b8cbc04b0bffb79ee8 to your computer and use it in GitHub Desktop.
Save kraftdorian/a942e51f391160b8cbc04b0bffb79ee8 to your computer and use it in GitHub Desktop.
Static-size items pagination
// inspired by https://www.zacfukuda.com/blog/pagination-algorithm
// especialy, the left/right surrounding items idea
const PaginationItem = {
PreviousPage: Symbol('PaginationItem.PreviousPage'),
NextPage: Symbol('PaginationItem.NextPage'),
};
function paginate(
currentPageNumber,
totalPagesNumber,
maxItemsSize = totalPagesNumber
) {
if (currentPageNumber > totalPagesNumber) {
throw new Error(
'current page number is not supposed to be greater than total pages number!'
);
} else if (maxItemsSize > totalPagesNumber) {
throw new Error(
'max items size is not supposed to be greater than total pages number!'
);
}
const items = Array.from(
{ length: Math.min(totalPagesNumber, maxItemsSize) },
(_, idx) => idx + 1
);
if (totalPagesNumber <= maxItemsSize) {
// if we have enough pages in total it's fine to return it early
return items;
}
let leftPtr = 0,
rightPtr = maxItemsSize - 1;
if (currentPageNumber >= 1 && currentPageNumber < totalPagesNumber) {
// we display next page item starting from the very first page, but not at the last page
items[rightPtr--] = PaginationItem.NextPage;
}
if (currentPageNumber > 1) {
// we display previous page item not at the first page, but until the last page
items[leftPtr++] = PaginationItem.PreviousPage;
}
if (currentPageNumber === 1) {
// start with the first page
items[leftPtr++] = currentPageNumber;
} else if (currentPageNumber > 1 && currentPageNumber < totalPagesNumber) {
const spacingRight = rightPtr - leftPtr; // everything without prev/next pages
// if current page with items displayed to the right overflows the max items,
// then start with first page that still includes the current page and the next/last one.
// otherwise it's OK to start with the current page
items[leftPtr++] =
currentPageNumber + spacingRight > totalPagesNumber
? Math.max(1, currentPageNumber - spacingRight + 1)
: currentPageNumber;
} else {
// start with the first page that still includes the last one
items[leftPtr++] = currentPageNumber - (rightPtr - leftPtr);
}
// once start page is established, just start counting by iterating over numeric items
for (leftPtr; leftPtr <= rightPtr; leftPtr++) {
items[leftPtr] = items[leftPtr - 1] + 1;
}
return items;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment