131 lines
4.4 KiB
JavaScript
131 lines
4.4 KiB
JavaScript
// Shared helpers for building paginated list response state.
|
|
|
|
function normalizePageNumber(value) {
|
|
const pageNumber = Math.floor(Number(value) || 1);
|
|
return Math.max(1, pageNumber);
|
|
}
|
|
|
|
function buildQueryString(query) {
|
|
const searchParams = new URLSearchParams();
|
|
Object.keys(query || {}).forEach(function (key) {
|
|
const value = query[key];
|
|
if (value === undefined || value === null || value === '') {
|
|
return;
|
|
}
|
|
searchParams.set(key, String(value));
|
|
});
|
|
|
|
const queryString = searchParams.toString();
|
|
return queryString ? `?${queryString}` : '';
|
|
}
|
|
|
|
function createPageEntry(pageNumber, currentPage, pageParam, queryState) {
|
|
const nextQuery = Object.assign({}, queryState, { [pageParam]: pageNumber });
|
|
|
|
return {
|
|
number: pageNumber,
|
|
active: pageNumber === currentPage,
|
|
url: buildQueryString(nextQuery)
|
|
};
|
|
}
|
|
|
|
function createEllipsisEntry() {
|
|
return {
|
|
ellipsis: true
|
|
};
|
|
}
|
|
|
|
function buildPaginationPages(totalPages, currentPage, pageParam, queryState, maxVisiblePages) {
|
|
const pages = [];
|
|
const visiblePageCount = Math.max(1, Math.min(Number(maxVisiblePages) || 7, totalPages));
|
|
const firstPage = 1;
|
|
const lastPage = totalPages;
|
|
|
|
if (totalPages <= visiblePageCount) {
|
|
for (let pageNumber = firstPage; pageNumber <= lastPage; pageNumber += 1) {
|
|
pages.push(createPageEntry(pageNumber, currentPage, pageParam, queryState));
|
|
}
|
|
|
|
return pages;
|
|
}
|
|
|
|
const innerPageCount = Math.max(1, visiblePageCount - 2);
|
|
let startPage = Math.max(2, currentPage - Math.floor(innerPageCount / 2));
|
|
let endPage = startPage + innerPageCount - 1;
|
|
|
|
if (endPage > lastPage - 1) {
|
|
endPage = lastPage - 1;
|
|
startPage = Math.max(2, endPage - innerPageCount + 1);
|
|
}
|
|
|
|
pages.push(createPageEntry(firstPage, currentPage, pageParam, queryState));
|
|
|
|
if (startPage > 2) {
|
|
pages.push(createEllipsisEntry());
|
|
}
|
|
|
|
for (let pageNumber = startPage; pageNumber <= endPage; pageNumber += 1) {
|
|
pages.push(createPageEntry(pageNumber, currentPage, pageParam, queryState));
|
|
}
|
|
|
|
if (endPage < lastPage - 1) {
|
|
pages.push(createEllipsisEntry());
|
|
}
|
|
|
|
pages.push(createPageEntry(lastPage, currentPage, pageParam, queryState));
|
|
|
|
return pages;
|
|
}
|
|
|
|
function buildPagination(totalItems, currentPage, pageParam, queryState, pageSize, itemLabel, ariaLabel) {
|
|
const normalizedPageParam = String(pageParam || 'page').trim() || 'page';
|
|
const normalizedPageSize = Math.max(1, Number(pageSize) || 10);
|
|
const totalPages = Math.max(1, Math.ceil(Number(totalItems) / normalizedPageSize));
|
|
const safeCurrentPage = Math.min(normalizePageNumber(currentPage), totalPages);
|
|
const startIndex = totalItems <= 0 ? 0 : (safeCurrentPage - 1) * normalizedPageSize;
|
|
const endIndex = totalItems <= 0 ? 0 : Math.min(totalItems, startIndex + normalizedPageSize);
|
|
const pages = buildPaginationPages(totalPages, safeCurrentPage, normalizedPageParam, queryState, 7);
|
|
|
|
const previousQuery = Object.assign({}, queryState, { [normalizedPageParam]: safeCurrentPage - 1 });
|
|
const nextQuery = Object.assign({}, queryState, { [normalizedPageParam]: safeCurrentPage + 1 });
|
|
const firstQuery = Object.assign({}, queryState, { [normalizedPageParam]: 1 });
|
|
const lastQuery = Object.assign({}, queryState, { [normalizedPageParam]: totalPages });
|
|
|
|
let normalizedItemLabel = String(itemLabel || 'items');
|
|
const total = Number(totalItems) || 0;
|
|
|
|
if (total === 1 && normalizedItemLabel.endsWith('s')) {
|
|
normalizedItemLabel = normalizedItemLabel.slice(0, -1);
|
|
} else if (total > 1 && !normalizedItemLabel.endsWith('s')) {
|
|
normalizedItemLabel += 's';
|
|
}
|
|
|
|
return {
|
|
currentPage: safeCurrentPage,
|
|
totalPages: totalPages,
|
|
totalItems: Number(totalItems) || 0,
|
|
hasMultiplePages: totalPages > 1,
|
|
startItem: startIndex + 1,
|
|
endItem: endIndex,
|
|
hasFirst: safeCurrentPage > 1,
|
|
hasPrevious: safeCurrentPage > 1,
|
|
hasNext: safeCurrentPage < totalPages,
|
|
hasLast: safeCurrentPage < totalPages,
|
|
firstUrl: buildQueryString(firstQuery),
|
|
lastUrl: buildQueryString(lastQuery),
|
|
previousUrl: buildQueryString(previousQuery),
|
|
nextUrl: buildQueryString(nextQuery),
|
|
pages: pages,
|
|
pageSize: normalizedPageSize,
|
|
pageParam: normalizedPageParam,
|
|
itemLabel: String(normalizedItemLabel || 'item'),
|
|
ariaLabel: String(ariaLabel || 'Pagination')
|
|
};
|
|
}
|
|
|
|
module.exports = {
|
|
normalizePageNumber,
|
|
buildQueryString,
|
|
buildPaginationPages,
|
|
buildPagination
|
|
}; |