From e0a8e1b595dd5a636f49edce7c08b2fd12c1e452 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sat, 21 Jun 2025 15:03:29 +0900 Subject: feat(blog/nuldoc): implement pagination --- vhosts/blog/nuldoc-src/commands/build.ts | 20 ++++++++-- vhosts/blog/nuldoc-src/components/Pagination.tsx | 45 ++++++++++++++++++++++ vhosts/blog/nuldoc-src/config.ts | 1 + vhosts/blog/nuldoc-src/generators/post_list.ts | 48 ++++++++++++++++++++++-- vhosts/blog/nuldoc-src/pages/PostListPage.tsx | 37 ++++++++++++------ 5 files changed, 131 insertions(+), 20 deletions(-) create mode 100644 vhosts/blog/nuldoc-src/components/Pagination.tsx (limited to 'vhosts/blog/nuldoc-src') diff --git a/vhosts/blog/nuldoc-src/commands/build.ts b/vhosts/blog/nuldoc-src/commands/build.ts index 52aca1f7..3f765441 100644 --- a/vhosts/blog/nuldoc-src/commands/build.ts +++ b/vhosts/blog/nuldoc-src/commands/build.ts @@ -14,7 +14,7 @@ import { getPostPublishedDate, PostPage, } from "../generators/post.ts"; -import { generatePostListPage } from "../generators/post_list.ts"; +import { generatePostListPages } from "../generators/post_list.ts"; import { generateSlidePage, SlidePage } from "../generators/slide.ts"; import { generateSlideListPage } from "../generators/slide_list.ts"; import { generateTagPage, TagPage } from "../generators/tag.ts"; @@ -70,10 +70,22 @@ async function parsePosts( } async function buildPostListPage(posts: PostPage[], config: Config) { - const postListPage = await generatePostListPage(posts, config); - await writePage(postListPage, config); + // Sort posts by published date (newest first) + const sortedPosts = [...posts].sort((a, b) => { + const ta = dateToString(getPostPublishedDate(a)); + const tb = dateToString(getPostPublishedDate(b)); + if (ta > tb) return -1; + if (ta < tb) return 1; + return 0; + }); + + const postListPages = await generatePostListPages(sortedPosts, config); + for (const page of postListPages) { + await writePage(page, config); + } + const postFeedPage = await generateFeedPageFromEntries( - postListPage.href, + "/posts/", "posts", `投稿一覧|${config.blog.siteName}`, posts, diff --git a/vhosts/blog/nuldoc-src/components/Pagination.tsx b/vhosts/blog/nuldoc-src/components/Pagination.tsx new file mode 100644 index 00000000..5527c924 --- /dev/null +++ b/vhosts/blog/nuldoc-src/components/Pagination.tsx @@ -0,0 +1,45 @@ +type Props = { + currentPage: number; + totalPages: number; + basePath: string; +}; + +export default function Pagination( + { currentPage, totalPages, basePath }: Props, +) { + if (totalPages <= 1) { + return
; + } + + const prevPage = currentPage > 1 ? currentPage - 1 : null; + const nextPage = currentPage < totalPages ? currentPage + 1 : null; + + const prevHref = prevPage === 1 ? basePath : `${basePath}${prevPage}/`; + const nextHref = `${basePath}${nextPage}/`; + + return ( + + ); +} diff --git a/vhosts/blog/nuldoc-src/config.ts b/vhosts/blog/nuldoc-src/config.ts index 5a07896f..adcb5632 100644 --- a/vhosts/blog/nuldoc-src/config.ts +++ b/vhosts/blog/nuldoc-src/config.ts @@ -18,6 +18,7 @@ const ConfigSchema = z.object({ fqdn: z.string(), siteName: z.string(), siteCopyrightYear: z.number(), + postsPerPage: z.number().default(10), tagLabels: z.record(z.string(), z.string()), }), }); diff --git a/vhosts/blog/nuldoc-src/generators/post_list.ts b/vhosts/blog/nuldoc-src/generators/post_list.ts index 67a4b996..b05f7ee6 100644 --- a/vhosts/blog/nuldoc-src/generators/post_list.ts +++ b/vhosts/blog/nuldoc-src/generators/post_list.ts @@ -6,18 +6,58 @@ import { PostPage } from "./post.ts"; export type PostListPage = Page; -export async function generatePostListPage( +export async function generatePostListPages( posts: PostPage[], config: Config, +): Promise { + const postsPerPage = config.blog.postsPerPage; + const totalPages = Math.ceil(posts.length / postsPerPage); + const pages: PostListPage[] = []; + + for (let pageIndex = 0; pageIndex < totalPages; pageIndex++) { + const pagePosts = posts.slice( + pageIndex * postsPerPage, + (pageIndex + 1) * postsPerPage, + ); + + const page = await generatePostListPage( + pagePosts, + config, + pageIndex + 1, + totalPages, + ); + + pages.push(page); + } + + return pages; +} + +async function generatePostListPage( + posts: PostPage[], + config: Config, + currentPage: number, + totalPages: number, ): Promise { const html = await renderToDOM( - PostListPage(posts, config), + PostListPage( + posts, + config, + currentPage, + totalPages, + ), ); + const destFilePath = currentPage === 1 + ? "/posts/index.html" + : `/posts/${currentPage}/index.html`; + + const href = currentPage === 1 ? "/posts/" : `/posts/${currentPage}/`; + return { root: html, renderer: "html", - destFilePath: "/posts/index.html", - href: "/posts/", + destFilePath, + href, }; } diff --git a/vhosts/blog/nuldoc-src/pages/PostListPage.tsx b/vhosts/blog/nuldoc-src/pages/PostListPage.tsx index c1c5214c..054955e6 100644 --- a/vhosts/blog/nuldoc-src/pages/PostListPage.tsx +++ b/vhosts/blog/nuldoc-src/pages/PostListPage.tsx @@ -1,22 +1,28 @@ import GlobalFooter from "../components/GlobalFooter.tsx"; import GlobalHeader from "../components/GlobalHeader.tsx"; import PageLayout from "../components/PageLayout.tsx"; +import Pagination from "../components/Pagination.tsx"; import PostPageEntry from "../components/PostPageEntry.tsx"; import { Config } from "../config.ts"; -import { dateToString } from "../revision.ts"; -import { getPostPublishedDate, PostPage } from "../generators/post.ts"; +import { PostPage } from "../generators/post.ts"; export default function PostListPage( posts: PostPage[], config: Config, + currentPage: number, + totalPages: number, ) { const pageTitle = "投稿一覧"; + const pageInfoSuffix = ` (${currentPage}ページ目)`; + const metaTitle = `${pageTitle}${pageInfoSuffix}|${config.blog.siteName}`; + const metaDescription = `投稿した記事の一覧${pageInfoSuffix}`; + return ( @@ -24,15 +30,22 @@ export default function PostListPage(
-

{pageTitle}

+

{pageTitle}{pageInfoSuffix}

- {Array.from(posts).sort((a, b) => { - const ta = dateToString(getPostPublishedDate(a)); - const tb = dateToString(getPostPublishedDate(b)); - if (ta > tb) return -1; - if (ta < tb) return 1; - return 0; - }).map((post) => )} + + + + {posts.map((post) => )} + +
-- cgit v1.2.3-70-g09d2