mirror of
https://github.com/10h30/blog-balodeplao.git
synced 2026-05-12 15:21:15 +09:00
feat: implement pagination utility and refactor static paths for blog, category, and tag pages
This commit is contained in:
@@ -4,15 +4,20 @@ import { getCollection } from "astro:content";
|
|||||||
import Headline from "@/components/ui/Headline.astro";
|
import Headline from "@/components/ui/Headline.astro";
|
||||||
import Pagination from "@/components/ui/Pagination.astro";
|
import Pagination from "@/components/ui/Pagination.astro";
|
||||||
import PostItem from "@/components/blog/PostItem.astro";
|
import PostItem from "@/components/blog/PostItem.astro";
|
||||||
import type { GetStaticPathsOptions } from "astro";
|
import { buildPaginatedPaths } from "@/utils/paginate";
|
||||||
|
|
||||||
export async function getStaticPaths({ paginate }: GetStaticPathsOptions) {
|
export async function getStaticPaths() {
|
||||||
const blogEntries = await getCollection("blog");
|
const blogEntries = await getCollection("blog");
|
||||||
const sortedPosts = blogEntries
|
const sortedPosts = blogEntries
|
||||||
.filter((post) => post.data && post.data.pubDate)
|
.filter((post) => post.data && post.data.pubDate)
|
||||||
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
|
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
|
||||||
|
|
||||||
return paginate(sortedPosts, { pageSize: 6 });
|
return buildPaginatedPaths(sortedPosts, "/blog").map(
|
||||||
|
({ pageParam, page }) => ({
|
||||||
|
params: { page: pageParam },
|
||||||
|
props: { page },
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { page } = Astro.props;
|
const { page } = Astro.props;
|
||||||
|
|||||||
@@ -4,16 +4,14 @@ import { getCollection } from "astro:content";
|
|||||||
import Headline from "@/components/ui/Headline.astro";
|
import Headline from "@/components/ui/Headline.astro";
|
||||||
import PostItem from "@/components/blog/PostItem.astro";
|
import PostItem from "@/components/blog/PostItem.astro";
|
||||||
import Pagination from "@/components/ui/Pagination.astro";
|
import Pagination from "@/components/ui/Pagination.astro";
|
||||||
import type { Page } from "astro";
|
|
||||||
import type { CollectionEntry } from "astro:content";
|
|
||||||
import {
|
import {
|
||||||
getTaxonomyMap,
|
getTaxonomyMap,
|
||||||
getTaxonomyPath,
|
getTaxonomyPath,
|
||||||
type TaxonomyItem,
|
type TaxonomyItem,
|
||||||
} from "@/utils/taxonomy";
|
} from "@/utils/taxonomy";
|
||||||
|
import { buildPaginatedPaths } from "@/utils/paginate";
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const PAGE_SIZE = 6;
|
|
||||||
const allPosts = await getCollection("blog");
|
const allPosts = await getCollection("blog");
|
||||||
const posts = allPosts.filter((post) => post.data && post.data.pubDate);
|
const posts = allPosts.filter((post) => post.data && post.data.pubDate);
|
||||||
|
|
||||||
@@ -31,32 +29,22 @@ export async function getStaticPaths() {
|
|||||||
const cat =
|
const cat =
|
||||||
getTaxonomyMap("category").get(slug) ??
|
getTaxonomyMap("category").get(slug) ??
|
||||||
({ slug, name: slug } as TaxonomyItem);
|
({ slug, name: slug } as TaxonomyItem);
|
||||||
const totalPages = Math.max(1, Math.ceil(catPosts.length / PAGE_SIZE));
|
|
||||||
|
|
||||||
return Array.from({ length: totalPages }, (_, i) => {
|
return buildPaginatedPaths(catPosts, `/category/${fullPath}`).map(
|
||||||
const pageNum = i + 1;
|
({ pageParam, page }) => ({
|
||||||
return {
|
params: { rest: pageParam ? `${fullPath}/${pageParam}` : fullPath },
|
||||||
params: { rest: pageNum === 1 ? fullPath : `${fullPath}/${pageNum}` },
|
|
||||||
props: {
|
props: {
|
||||||
cat,
|
cat,
|
||||||
fullPath,
|
fullPath,
|
||||||
posts: catPosts.slice(i * PAGE_SIZE, (i + 1) * PAGE_SIZE),
|
posts: page.data,
|
||||||
currentPage: pageNum,
|
currentPage: page.currentPage,
|
||||||
lastPage: totalPages,
|
lastPage: page.lastPage,
|
||||||
total: catPosts.length,
|
total: page.total,
|
||||||
prevUrl:
|
prevUrl: page.url.prev,
|
||||||
pageNum === 1
|
nextUrl: page.url.next,
|
||||||
? undefined
|
|
||||||
: pageNum === 2
|
|
||||||
? `/category/${fullPath}`
|
|
||||||
: `/category/${fullPath}/${pageNum - 1}`,
|
|
||||||
nextUrl:
|
|
||||||
pageNum === totalPages
|
|
||||||
? undefined
|
|
||||||
: `/category/${fullPath}/${pageNum + 1}`,
|
|
||||||
},
|
},
|
||||||
};
|
}),
|
||||||
});
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import { getCollection } from "astro:content";
|
|||||||
import Headline from "@/components/ui/Headline.astro";
|
import Headline from "@/components/ui/Headline.astro";
|
||||||
import PostItem from "@/components/blog/PostItem.astro";
|
import PostItem from "@/components/blog/PostItem.astro";
|
||||||
import Pagination from "@/components/ui/Pagination.astro";
|
import Pagination from "@/components/ui/Pagination.astro";
|
||||||
import type { GetStaticPathsOptions } from "astro";
|
|
||||||
import { getTaxonomyMap } from "@/utils/taxonomy";
|
import { getTaxonomyMap } from "@/utils/taxonomy";
|
||||||
|
import { buildPaginatedPaths } from "@/utils/paginate";
|
||||||
|
|
||||||
export async function getStaticPaths({ paginate }: GetStaticPathsOptions) {
|
export async function getStaticPaths() {
|
||||||
const allPosts = await getCollection("blog");
|
const allPosts = await getCollection("blog");
|
||||||
const posts = allPosts.filter((post) => post.data && post.data.pubDate);
|
const posts = allPosts.filter((post) => post.data && post.data.pubDate);
|
||||||
|
|
||||||
@@ -21,10 +21,12 @@ export async function getStaticPaths({ paginate }: GetStaticPathsOptions) {
|
|||||||
.filter((post) => post.data.categories?.includes(category))
|
.filter((post) => post.data.categories?.includes(category))
|
||||||
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
|
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
|
||||||
|
|
||||||
return paginate(categoryPosts, {
|
return buildPaginatedPaths(categoryPosts, `/category/${category}`).map(
|
||||||
params: { category },
|
({ pageParam, page }) => ({
|
||||||
pageSize: 6,
|
params: { category, page: pageParam },
|
||||||
});
|
props: { page },
|
||||||
|
}),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,16 +4,14 @@ import { getCollection } from "astro:content";
|
|||||||
import Headline from "@/components/ui/Headline.astro";
|
import Headline from "@/components/ui/Headline.astro";
|
||||||
import PostItem from "@/components/blog/PostItem.astro";
|
import PostItem from "@/components/blog/PostItem.astro";
|
||||||
import Pagination from "@/components/ui/Pagination.astro";
|
import Pagination from "@/components/ui/Pagination.astro";
|
||||||
import type { Page } from "astro";
|
|
||||||
import type { CollectionEntry } from "astro:content";
|
|
||||||
import {
|
import {
|
||||||
getTaxonomyMap,
|
getTaxonomyMap,
|
||||||
getTaxonomyPath,
|
getTaxonomyPath,
|
||||||
type TaxonomyItem,
|
type TaxonomyItem,
|
||||||
} from "@/utils/taxonomy";
|
} from "@/utils/taxonomy";
|
||||||
|
import { buildPaginatedPaths } from "@/utils/paginate";
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const PAGE_SIZE = 6;
|
|
||||||
const allPosts = await getCollection("blog");
|
const allPosts = await getCollection("blog");
|
||||||
const posts = allPosts.filter((post) => post.data && post.data.pubDate);
|
const posts = allPosts.filter((post) => post.data && post.data.pubDate);
|
||||||
|
|
||||||
@@ -31,32 +29,22 @@ export async function getStaticPaths() {
|
|||||||
const dest =
|
const dest =
|
||||||
getTaxonomyMap("destination").get(slug) ??
|
getTaxonomyMap("destination").get(slug) ??
|
||||||
({ slug, name: slug } as TaxonomyItem);
|
({ slug, name: slug } as TaxonomyItem);
|
||||||
const totalPages = Math.max(1, Math.ceil(destPosts.length / PAGE_SIZE));
|
|
||||||
|
|
||||||
return Array.from({ length: totalPages }, (_, i) => {
|
return buildPaginatedPaths(destPosts, `/destination/${fullPath}`).map(
|
||||||
const pageNum = i + 1;
|
({ pageParam, page }) => ({
|
||||||
return {
|
params: { rest: pageParam ? `${fullPath}/${pageParam}` : fullPath },
|
||||||
params: { rest: pageNum === 1 ? fullPath : `${fullPath}/${pageNum}` },
|
|
||||||
props: {
|
props: {
|
||||||
dest,
|
dest,
|
||||||
fullPath,
|
fullPath,
|
||||||
posts: destPosts.slice(i * PAGE_SIZE, (i + 1) * PAGE_SIZE),
|
posts: page.data,
|
||||||
currentPage: pageNum,
|
currentPage: page.currentPage,
|
||||||
lastPage: totalPages,
|
lastPage: page.lastPage,
|
||||||
total: destPosts.length,
|
total: page.total,
|
||||||
prevUrl:
|
prevUrl: page.url.prev,
|
||||||
pageNum === 1
|
nextUrl: page.url.next,
|
||||||
? undefined
|
|
||||||
: pageNum === 2
|
|
||||||
? `/destination/${fullPath}`
|
|
||||||
: `/destination/${fullPath}/${pageNum - 1}`,
|
|
||||||
nextUrl:
|
|
||||||
pageNum === totalPages
|
|
||||||
? undefined
|
|
||||||
: `/destination/${fullPath}/${pageNum + 1}`,
|
|
||||||
},
|
},
|
||||||
};
|
}),
|
||||||
});
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import { getCollection } from "astro:content";
|
|||||||
import Headline from "@/components/ui/Headline.astro";
|
import Headline from "@/components/ui/Headline.astro";
|
||||||
import PostItem from "@/components/blog/PostItem.astro";
|
import PostItem from "@/components/blog/PostItem.astro";
|
||||||
import Pagination from "@/components/ui/Pagination.astro";
|
import Pagination from "@/components/ui/Pagination.astro";
|
||||||
import type { GetStaticPathsOptions } from "astro";
|
|
||||||
import { getTaxonomyMap } from "@/utils/taxonomy";
|
import { getTaxonomyMap } from "@/utils/taxonomy";
|
||||||
|
import { buildPaginatedPaths } from "@/utils/paginate";
|
||||||
|
|
||||||
export async function getStaticPaths({ paginate }: GetStaticPathsOptions) {
|
export async function getStaticPaths() {
|
||||||
const allPosts = await getCollection("blog");
|
const allPosts = await getCollection("blog");
|
||||||
const posts = allPosts.filter((post) => post.data && post.data.pubDate);
|
const posts = allPosts.filter((post) => post.data && post.data.pubDate);
|
||||||
|
|
||||||
@@ -21,10 +21,12 @@ export async function getStaticPaths({ paginate }: GetStaticPathsOptions) {
|
|||||||
.filter((post) => post.data.tags?.includes(tag))
|
.filter((post) => post.data.tags?.includes(tag))
|
||||||
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
|
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
|
||||||
|
|
||||||
return paginate(tagPosts, {
|
return buildPaginatedPaths(tagPosts, `/tag/${tag}`).map(
|
||||||
params: { tag },
|
({ pageParam, page }) => ({
|
||||||
pageSize: 6,
|
params: { tag, page: pageParam },
|
||||||
});
|
props: { page },
|
||||||
|
}),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
export const DEFAULT_PAGE_SIZE = 6;
|
||||||
|
|
||||||
|
export interface PageMeta {
|
||||||
|
data: unknown[];
|
||||||
|
total: number;
|
||||||
|
currentPage: number;
|
||||||
|
lastPage: number;
|
||||||
|
url: { prev?: string; next?: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns paginated static paths using WordPress-style /page/{n} URLs.
|
||||||
|
*
|
||||||
|
* @param items Full sorted list of items to paginate.
|
||||||
|
* @param basePath Base URL without trailing slash, e.g. "/blog" or "/category/travel".
|
||||||
|
* @param pageSize Number of items per page (default: DEFAULT_PAGE_SIZE).
|
||||||
|
* @returns Array of { params: { page }, props: { page: PageMeta } }.
|
||||||
|
*/
|
||||||
|
export function buildPaginatedPaths<T>(
|
||||||
|
items: T[],
|
||||||
|
basePath: string,
|
||||||
|
pageSize = DEFAULT_PAGE_SIZE,
|
||||||
|
) {
|
||||||
|
const totalPages = Math.max(1, Math.ceil(items.length / pageSize));
|
||||||
|
|
||||||
|
return Array.from({ length: totalPages }, (_, i) => {
|
||||||
|
const pageNum = i + 1;
|
||||||
|
const data = items.slice(i * pageSize, (i + 1) * pageSize);
|
||||||
|
|
||||||
|
const prev =
|
||||||
|
pageNum === 1
|
||||||
|
? undefined
|
||||||
|
: pageNum === 2
|
||||||
|
? basePath
|
||||||
|
: `${basePath}/page/${pageNum - 1}`;
|
||||||
|
const next =
|
||||||
|
pageNum === totalPages ? undefined : `${basePath}/page/${pageNum + 1}`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
// page param: undefined for page 1 (matches the base URL), "page/N" for N≥2
|
||||||
|
pageParam: pageNum === 1 ? undefined : `page/${pageNum}`,
|
||||||
|
page: {
|
||||||
|
data,
|
||||||
|
total: items.length,
|
||||||
|
currentPage: pageNum,
|
||||||
|
lastPage: totalPages,
|
||||||
|
url: { prev, next },
|
||||||
|
} as PageMeta & { data: T[] },
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user