diff options
Diffstat (limited to 'services/nuldoc/nuldoc-src/components/Pagination.ts')
| -rw-r--r-- | services/nuldoc/nuldoc-src/components/Pagination.ts | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/services/nuldoc/nuldoc-src/components/Pagination.ts b/services/nuldoc/nuldoc-src/components/Pagination.ts new file mode 100644 index 0000000..62e796b --- /dev/null +++ b/services/nuldoc/nuldoc-src/components/Pagination.ts @@ -0,0 +1,98 @@ +import { elem, Element } from "../dom.ts"; + +type Props = { + currentPage: number; + totalPages: number; + basePath: string; +}; + +export default function Pagination( + { currentPage, totalPages, basePath }: Props, +): Element { + if (totalPages <= 1) { + return elem("div", {}); + } + + const pages = generatePageNumbers(currentPage, totalPages); + + return elem( + "nav", + { class: "pagination" }, + elem( + "div", + { class: "pagination-prev" }, + currentPage > 1 + ? elem("a", { href: pageUrlAt(basePath, currentPage - 1) }, "前へ") + : null, + ), + ...pages.map((page) => { + if (page === "...") { + return elem("div", { class: "pagination-elipsis" }, "…"); + } else if (page === currentPage) { + return elem( + "div", + { class: "pagination-page pagination-page-current" }, + elem("span", {}, String(page)), + ); + } else { + return elem( + "div", + { class: "pagination-page" }, + elem("a", { href: pageUrlAt(basePath, page) }, String(page)), + ); + } + }), + elem( + "div", + { class: "pagination-next" }, + currentPage < totalPages + ? elem("a", { href: pageUrlAt(basePath, currentPage + 1) }, "次へ") + : null, + ), + ); +} + +type PageItem = number | "..."; + +/** + * Generates page numbers for pagination display. + * + * - Always show the first page + * - Always show the last page + * - Always show the current page + * - Always show the page before and after the current page + * - If there's only one page gap between displayed pages, fill it + * - If there are two or more pages gap between displayed pages, show ellipsis + */ +function generatePageNumbers( + currentPage: number, + totalPages: number, +): PageItem[] { + const pages = new Set<number>(); + pages.add(1); + pages.add(Math.max(1, currentPage - 1)); + pages.add(currentPage); + pages.add(Math.min(totalPages, currentPage + 1)); + pages.add(totalPages); + + const sorted = Array.from(pages).sort((a, b) => a - b); + + const result: PageItem[] = []; + for (let i = 0; i < sorted.length; i++) { + if (i > 0) { + const gap = sorted[i] - sorted[i - 1]; + if (gap === 2) { + result.push(sorted[i - 1] + 1); + } else if (gap > 2) { + result.push("..."); + } + } + result.push(sorted[i]); + } + + return result; +} + +function pageUrlAt(basePath: string, page: number): string { + return page === 1 ? basePath : `${basePath}${page}/`; +} |
