diff --git a/app/blog/page.tsx b/app/blog/page.tsx index f261033..f3fe372 100644 --- a/app/blog/page.tsx +++ b/app/blog/page.tsx @@ -1,5 +1,5 @@ import { getAllPostsSorted } from '@/lib/posts'; -import { PostListItem } from '@/components/post-list-item'; +import { PostListWithControls } from '@/components/post-list-with-controls'; export const metadata = { title: '所有文章' @@ -13,11 +13,7 @@ export default function BlogIndexPage() {

所有文章

- + ); } diff --git a/app/tags/[tag]/page.tsx b/app/tags/[tag]/page.tsx index 6aefb07..38781c1 100644 --- a/app/tags/[tag]/page.tsx +++ b/app/tags/[tag]/page.tsx @@ -1,6 +1,6 @@ import type { Metadata } from 'next'; import { allPosts } from 'contentlayer/generated'; -import { PostListItem } from '@/components/post-list-item'; +import { PostListWithControls } from '@/components/post-list-with-controls'; import { getTagSlug } from '@/lib/posts'; export function generateStaticParams() { @@ -47,11 +47,7 @@ export default function TagPage({ params }: Props) {

標籤:{tagLabel}

- + ); } diff --git a/components/post-list-with-controls.tsx b/components/post-list-with-controls.tsx new file mode 100644 index 0000000..c06f34b --- /dev/null +++ b/components/post-list-with-controls.tsx @@ -0,0 +1,132 @@ +'use client'; + +import { useMemo, useState } from 'react'; +import type { Post } from 'contentlayer/generated'; +import { siteConfig } from '@/lib/config'; +import { PostListItem } from './post-list-item'; + +interface Props { + posts: Post[]; + pageSize?: number; +} + +type SortOrder = 'new' | 'old'; + +export function PostListWithControls({ posts, pageSize }: Props) { + const [sortOrder, setSortOrder] = useState('new'); + const [page, setPage] = useState(1); + + const size = pageSize ?? siteConfig.postsPerPage ?? 5; + + const sortedPosts = useMemo(() => { + const arr = [...posts]; + arr.sort((a, b) => { + const aDate = a.published_at + ? new Date(a.published_at).getTime() + : 0; + const bDate = b.published_at + ? new Date(b.published_at).getTime() + : 0; + return sortOrder === 'new' ? bDate - aDate : aDate - bDate; + }); + return arr; + }, [posts, sortOrder]); + + const totalPages = Math.max(1, Math.ceil(sortedPosts.length / size)); + const currentPage = Math.min(page, totalPages); + const start = (currentPage - 1) * size; + const currentPosts = sortedPosts.slice(start, start + size); + + const handleChangeSort = (order: SortOrder) => { + setSortOrder(order); + setPage(1); + }; + + const goToPage = (p: number) => { + if (p < 1 || p > totalPages) return; + setPage(p); + }; + + return ( +
+
+
+ 排序: + + +
+
+ 第 {currentPage} / {totalPages} 頁 +
+
+ +
    + {currentPosts.map((post) => ( + + ))} +
+ + {totalPages > 1 && ( + + )} +
+ ); +} +