nhb-toolbox
Version:
A versatile collection of smart, efficient, and reusable utility functions, classes and types for everyday development needs.
123 lines (122 loc) • 4.23 kB
JavaScript
export class Paginator {
#totalItems;
#perPage;
#currentPage;
constructor(options) {
const { totalItems, itemsPerPage = 10, currentPage = 1 } = options ?? {};
this.#totalItems = Math.max(0, Number(totalItems));
this.#perPage = Math.max(1, Number(itemsPerPage));
this.#currentPage = Math.max(1, Number(currentPage));
}
withPage(page) {
const safePage = Math.min(Math.max(1, page), this.totalPages());
return new Paginator({
totalItems: this.#totalItems,
itemsPerPage: this.#perPage,
currentPage: safePage,
});
}
withPerPage(perPage) {
const safePerPage = Math.max(1, perPage);
return new Paginator({
totalItems: this.#totalItems,
itemsPerPage: safePerPage,
currentPage: this.#currentPage,
});
}
withTotalItems(totalItems) {
const safeTotalItems = Math.max(0, totalItems);
return new Paginator({
totalItems: safeTotalItems,
itemsPerPage: this.#perPage,
currentPage: this.#currentPage,
});
}
withOptions(options) {
const newTotalItems = Math.max(0, options.totalItems ? Number(options.totalItems) : this.#totalItems);
const newItemsPerPage = Math.max(1, options.itemsPerPage ? Number(options.itemsPerPage) : this.#perPage);
const totalPages = Math.ceil(newTotalItems / newItemsPerPage);
const newCurrentPage = Math.min(Math.max(1, options.currentPage ? Number(options.currentPage) : this.#currentPage), totalPages);
return new Paginator({
totalItems: newTotalItems,
itemsPerPage: newItemsPerPage,
currentPage: newCurrentPage,
});
}
offset() {
return (this.#currentPage - 1) * this.#perPage;
}
getOffset() {
return this.offset();
}
skipCount() {
return this.offset();
}
totalPages() {
return Math.ceil(this.#totalItems / this.#perPage);
}
getMeta() {
const totalPages = this.totalPages();
return {
totalItems: this.#totalItems,
currentPage: this.#currentPage,
itemsPerPage: this.#perPage,
totalPages,
hasPrev: this.hasPrevPage(),
hasNext: this.hasNextPage(),
isFirst: this.isFirstPage(),
isLast: this.isLastPage(),
offset: this.offset(),
};
}
nextPage() {
return this.#currentPage < this.totalPages() ? this.#currentPage + 1 : null;
}
prevPage() {
return this.#currentPage > 1 ? this.#currentPage - 1 : null;
}
isFirstPage() {
return this.#currentPage === 1;
}
isLastPage() {
return this.#currentPage === this.totalPages();
}
hasPrevPage() {
return this.#currentPage > 1;
}
hasNextPage() {
return this.#currentPage < this.totalPages();
}
pageList(options = {}) {
const total = this.totalPages();
const edgeCount = Math.max(0, options.edgeCount ?? 1);
const siblingCount = Math.max(0, options.siblingCount ?? 1);
const start = Math.max(this.#currentPage - siblingCount, edgeCount + 1);
const end = Math.min(this.#currentPage + siblingCount, total - edgeCount);
const _getRange = (from, to) => {
return from > to ? [] : Array.from({ length: to - from + 1 }, (_, i) => from + i);
};
const startPages = _getRange(1, edgeCount);
const middlePages = _getRange(start, end);
const endPages = _getRange(total - edgeCount + 1, total);
const pages = new Set([...startPages, ...middlePages, ...endPages]);
return Array.from(pages).sort((a, b) => a - b);
}
firstPage() {
return 1;
}
lastPage() {
return this.totalPages();
}
isPageValid(page) {
const p = Math.floor(page);
return p >= 1 && p <= this.totalPages();
}
static fromMeta(meta) {
return new Paginator({
totalItems: meta.totalItems,
itemsPerPage: meta.itemsPerPage,
currentPage: meta.currentPage,
});
}
}