Generate a sequence of numbers for use in a Pagination Component, the clever way.
npm i @bramus/pagination-sequence
This library comes as an ES Module and exposes a function/algorithm to generate an array of pagination entries.
import { generate } from '@bramus/pagination-sequence';
const sequence = generate(67, 74);
// ~> [1, 2, '…', 65, 66, 67, 68, 69, '…', 73, 74]
Alternatively you can use generateFromObj
which accepts a configuration Object as an argument:
import { generateFromObj } from '@bramus/pagination-sequence';
const sequence = generateFromObj({
curPage: 67,
numPages: 74,
});
// ~> [1, 2, '…', 65, 66, 67, 68, 69, '…', 73, 74]
Note that this is a Framework Agnostic library: the generated array is not rendered in any way but, instead, must be fed into your own Pagination Component for rendering.
💡 Looking for some Pagination Component inspiration? See Integration Examples below to see how to use this with the JavaScript Framework Du Jour™.
The exposed generate
function has the following API:
generate(curPage, numPages, numPagesAtEdges = 2, numPagesAroundCurrent = 2, glue = '…');
Parameters:
curPage
: The current active pagenumPages
: The total number of pagesnumPagesAtEdges
(default: 2): Number of pages to show on the outer edges.numPagesAroundCurrent
(default: 2): Number of pages to show around the active page.glue
(default: '…'): The string to show when there's a gap
The generateFromObj
function accepts one single opts
object. Its members are all of the parameters described above. Default values are set where possible.
const {
curPage = 1,
numPages = 1,
numPagesAtEdges = 2,
numPagesAroundCurrent = 2,
glue = '…',
} = opts;
The algorithm is opinionated and follows these principles:
-
Stable Output
When generating a sequence, it will always generate the same amount of entries, for any
curPage
value. When viewing a page at the edge of a series, this can result innumPagesAtEdges
being ignored.For example: Instead of having
generate(2, 12, 1, 1)
return01-[02]-03-..-12
(5 entries), it will return01-[02]-03-04-05-..-12
(7 entries). This is a deliberate choice becausegenerate(7, 12, 1, 1)
will also return 7 entries:01-..-06-[07]-08-..-12
.With a stable amount of entries being generated, the output will also be visually stable when rendered on screen.
-
Always include links to the edges
The algorithm will always include links to the first and last page.
For Example: when looking at page 25 of 50, the algorithm will include a link to page 1 and page 50.
-
No unnecessary gaps
When the algorithm detects a gap that's only “1 item wide”, it will replace that gap with the actual number.
For Example: A foolish take on
generate(4, 9, 1, 1)
, would generate01-..-03-[04]-05-..-09
. The algorithm corrects the first gap to02
and will return01-02-03-[04]-05-..-09
instead.
🔗 Try it online: https://codepen.io/bramus/pen/NWaxNKQ
import React from "react";
import ReactDOM from "react-dom";
import { generate } from "@bramus/pagination-sequence";
const BASE_URL = '#';
const PaginationEntry = ({ value, onEntryClick = null, label = null, title = null, isCurrent = false, isDisabled = false, ...props }) => {
label ??= value;
title ??= `Go to page ${value}`;
const onClick = (e) => {
e.stopPropagation();
e.preventDefault();
e.target.blur();
if (onEntryClick) {
onEntryClick(value);
}
}
if (value == '…') {
return (
<li data-pagination-ellipsis {...props}><span>{label}</span></li>
);
}
if (isDisabled) {
return (
<li data-pagination-disabled {...props}><span>{label}</span></li>
);
}
if (isCurrent) {
props['data-pagination-current'] = true;
}
return (
<li {...props}>
<a href={`${BASE_URL}/page/${value}`} title={title} onClick={onClick}>{label}</a>
</li>
);
}
const Pagination = ({ curPage, numPages, numPagesAtEdges = 2, numPagesAroundCurrent = 2, onEntryClick = null }) => {
const sequence = generate(curPage, numPages, numPagesAtEdges, numPagesAroundCurrent);
// console.log(sequence);
return (
<ul className="pagination">
<PaginationEntry data-pagination-first onEntryClick={onEntryClick} value={1} title="Go to First Page" label="«" isDisabled={curPage === 1} />
<PaginationEntry data-pagination-prev onEntryClick={onEntryClick} value={curPage-1} title="Go to Previous Page" label="‹" isDisabled={curPage === 1} />
{sequence.map((val, idx) =>
<PaginationEntry key={`page-${(val == '…') ? `…-${idx}` : val}`} onEntryClick={onEntryClick} value={val} isCurrent={val == curPage} />
)}
<PaginationEntry data-pagination-next onEntryClick={onEntryClick} value={curPage+1} title="Go to Next Page" label="›" isDisabled={curPage === numPages} />
<PaginationEntry data-pagination-next onEntryClick={onEntryClick} value={numPages} title="Go to Last Page" label="»" isDisabled={curPage === numPages} />
</ul>
);
}
ReactDOM.render(
<Pagination curPage={25} numPages={50} onEntryClick={(val) => { console.log(val)}} />,
document.getElementById('root')
);
@bramus/pagination-sequence
is released under the MIT public license. See the enclosed LICENSE
for details.
Looking for an implementation in another programming language?
- PHP: https://gist.github.com/bramus/5d8f2e0269e57dff5136 (The original, from 2014)
- (submit a PR to add your own)