summaryrefslogtreecommitdiffhomepage
path: root/vhosts/blog/nuldoc-src
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2025-06-21 15:03:29 +0900
committernsfisis <nsfisis@gmail.com>2025-06-21 15:48:56 +0900
commite0a8e1b595dd5a636f49edce7c08b2fd12c1e452 (patch)
tree2bb9a635b6144273772c692b939750b71f7a7332 /vhosts/blog/nuldoc-src
parentbe5d20ba8b6988c6107a6066774f3d7b994c48f5 (diff)
downloadnsfisis.dev-e0a8e1b595dd5a636f49edce7c08b2fd12c1e452.tar.gz
nsfisis.dev-e0a8e1b595dd5a636f49edce7c08b2fd12c1e452.tar.zst
nsfisis.dev-e0a8e1b595dd5a636f49edce7c08b2fd12c1e452.zip
feat(blog/nuldoc): implement pagination
Diffstat (limited to 'vhosts/blog/nuldoc-src')
-rw-r--r--vhosts/blog/nuldoc-src/commands/build.ts20
-rw-r--r--vhosts/blog/nuldoc-src/components/Pagination.tsx45
-rw-r--r--vhosts/blog/nuldoc-src/config.ts1
-rw-r--r--vhosts/blog/nuldoc-src/generators/post_list.ts48
-rw-r--r--vhosts/blog/nuldoc-src/pages/PostListPage.tsx37
5 files changed, 131 insertions, 20 deletions
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 <div></div>;
+ }
+
+ 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 (
+ <nav className="pagination">
+ <div className="pagination-prev">
+ {prevPage
+ ? (
+ <a href={prevHref}>
+ 前のページ
+ </a>
+ )
+ : null}
+ </div>
+ <div className="pagination-info">
+ {String(currentPage)} / {String(totalPages)}
+ </div>
+ <div className="pagination-next">
+ {nextPage
+ ? (
+ <a href={nextHref}>
+ 次のページ
+ </a>
+ )
+ : null}
+ </div>
+ </nav>
+ );
+}
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<PostListPage[]> {
+ 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<PostListPage> {
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 (
<PageLayout
metaCopyrightYear={config.blog.siteCopyrightYear}
- metaDescription="投稿した記事の一覧"
- metaTitle={`${pageTitle}|${config.blog.siteName}`}
+ metaDescription={metaDescription}
+ metaTitle={metaTitle}
metaAtomFeedHref={`https://${config.blog.fqdn}/posts/atom.xml`}
config={config}
>
@@ -24,15 +30,22 @@ export default function PostListPage(
<GlobalHeader config={config} />
<main className="main">
<header className="page-header">
- <h1>{pageTitle}</h1>
+ <h1>{pageTitle}{pageInfoSuffix}</h1>
</header>
- {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) => <PostPageEntry post={post} key={post.uuid} />)}
+
+ <Pagination
+ currentPage={currentPage}
+ totalPages={totalPages}
+ basePath="/posts/"
+ />
+
+ {posts.map((post) => <PostPageEntry post={post} key={post.uuid} />)}
+
+ <Pagination
+ currentPage={currentPage}
+ totalPages={totalPages}
+ basePath="/posts/"
+ />
</main>
<GlobalFooter config={config} />
</body>