refactor: replace pagination logic with Astro built-in pagination (#376)

This commit is contained in:
Timon Jurschitsch
2024-09-16 08:05:50 +02:00
committed by GitHub
parent 70b2486293
commit 08b1676cdd
12 changed files with 102 additions and 195 deletions
+14 -20
View File
@@ -1,49 +1,43 @@
--- ---
import type { Page } from "astro";
import LinkButton from "./LinkButton.astro"; import LinkButton from "./LinkButton.astro";
import type { CollectionEntry } from "astro:content";
export interface Props { export interface Props {
currentPage: number; page: Page<CollectionEntry<"blog">>;
totalPages: number;
prevUrl: string;
nextUrl: string;
} }
const { currentPage, totalPages, prevUrl, nextUrl } = Astro.props; const { page } = Astro.props;
const prev = currentPage > 1 ? "" : "disabled";
const next = currentPage < totalPages ? "" : "disabled";
const isPrevDisabled = prev === "disabled";
const isNextDisabled = next === "disabled";
--- ---
{ {
totalPages > 1 && ( page.lastPage > 1 && (
<nav class="pagination-wrapper" aria-label="Pagination"> <nav class="pagination-wrapper" aria-label="Pagination">
<LinkButton <LinkButton
disabled={isPrevDisabled} disabled={!page.url.prev}
href={prevUrl} href={page.url.prev as string}
className={`mr-4 select-none ${prev}`} className={`mr-4 select-none ${page.url.prev ? "" : "disabled"}`}
ariaLabel="Previous" ariaLabel="Previous"
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class:list={[{ "disabled-svg": isPrevDisabled }]} class:list={[{ "disabled-svg": !page.url.prev }]}
> >
<path d="M12.707 17.293 8.414 13H18v-2H8.414l4.293-4.293-1.414-1.414L4.586 12l6.707 6.707z" /> <path d="M12.707 17.293 8.414 13H18v-2H8.414l4.293-4.293-1.414-1.414L4.586 12l6.707 6.707z" />
</svg> </svg>
Prev Prev
</LinkButton> </LinkButton>
{currentPage} / {totalPages} {page.currentPage} / {page.lastPage}
<LinkButton <LinkButton
disabled={isNextDisabled} disabled={!page.url.next}
href={nextUrl} href={page.url.next as string}
className={`ml-4 select-none ${next}`} className={`mx-4 select-none ${page.url.next ? "" : "disabled"}`}
ariaLabel="Next" ariaLabel="Next"
> >
Next Next
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class:list={[{ "disabled-svg": isNextDisabled }]} class:list={[{ "disabled-svg": !page.url.next }]}
> >
<path d="m11.293 17.293 1.414 1.414L19.414 12l-6.707-6.707-1.414 1.414L15.586 11H6v2h9.586z" /> <path d="m11.293 17.293 1.414 1.414L19.414 12l-6.707-6.707-1.414 1.414L15.586 11H6v2h9.586z" />
</svg> </svg>
+7 -13
View File
@@ -1,5 +1,4 @@
--- ---
import type { CollectionEntry } from "astro:content";
import Layout from "@layouts/Layout.astro"; import Layout from "@layouts/Layout.astro";
import Main from "@layouts/Main.astro"; import Main from "@layouts/Main.astro";
import Header from "@components/Header.astro"; import Header from "@components/Header.astro";
@@ -7,14 +6,14 @@ import Footer from "@components/Footer.astro";
import Pagination from "@components/Pagination.astro"; import Pagination from "@components/Pagination.astro";
import Card from "@components/Card"; import Card from "@components/Card";
import { SITE } from "@config"; import { SITE } from "@config";
import type { Page } from "astro";
import type { CollectionEntry } from "astro:content";
export interface Props { export interface Props {
currentPage: number; page: Page<CollectionEntry<"blog">>;
totalPages: number;
paginatedPosts: CollectionEntry<"blog">[];
} }
const { currentPage, totalPages, paginatedPosts } = Astro.props; const { page } = Astro.props;
--- ---
<Layout title={`Posts | ${SITE.title}`}> <Layout title={`Posts | ${SITE.title}`}>
@@ -22,19 +21,14 @@ const { currentPage, totalPages, paginatedPosts } = Astro.props;
<Main pageTitle="Posts" pageDesc="All the articles I've posted."> <Main pageTitle="Posts" pageDesc="All the articles I've posted.">
<ul> <ul>
{ {
paginatedPosts.map(({ data, slug }) => ( page.data.map(({ data, slug }) => (
<Card href={`/posts/${slug}/`} frontmatter={data} /> <Card href={`/posts/${slug}/`} frontmatter={data} />
)) ))
} }
</ul> </ul>
</Main> </Main>
<Pagination <Pagination {page} />
{currentPage}
{totalPages}
prevUrl={`/posts${currentPage - 1 !== 1 ? "/" + (currentPage - 1) : ""}/`}
nextUrl={`/posts/${currentPage + 1}/`}
/>
<Footer noMarginTop={totalPages > 1} /> <Footer noMarginTop={page.lastPage > 1} />
</Layout> </Layout>
+7 -15
View File
@@ -1,5 +1,4 @@
--- ---
import { type CollectionEntry } from "astro:content";
import Layout from "@layouts/Layout.astro"; import Layout from "@layouts/Layout.astro";
import Main from "@layouts/Main.astro"; import Main from "@layouts/Main.astro";
import Header from "@components/Header.astro"; import Header from "@components/Header.astro";
@@ -7,16 +6,16 @@ import Footer from "@components/Footer.astro";
import Card from "@components/Card"; import Card from "@components/Card";
import Pagination from "@components/Pagination.astro"; import Pagination from "@components/Pagination.astro";
import { SITE } from "@config"; import { SITE } from "@config";
import type { Page } from "astro";
import type { CollectionEntry } from "astro:content";
export interface Props { export interface Props {
currentPage: number; page: Page<CollectionEntry<"blog">>;
totalPages: number;
paginatedPosts: CollectionEntry<"blog">[];
tag: string; tag: string;
tagName: string; tagName: string;
} }
const { currentPage, totalPages, paginatedPosts, tag, tagName } = Astro.props; const { page, tag, tagName } = Astro.props;
--- ---
<Layout title={`Tag: ${tagName} | ${SITE.title}`}> <Layout title={`Tag: ${tagName} | ${SITE.title}`}>
@@ -29,21 +28,14 @@ const { currentPage, totalPages, paginatedPosts, tag, tagName } = Astro.props;
<h1 slot="title" transition:name={tag}>{`Tag:${tag}`}</h1> <h1 slot="title" transition:name={tag}>{`Tag:${tag}`}</h1>
<ul> <ul>
{ {
paginatedPosts.map(({ data, slug }) => ( page.data.map(({ data, slug }) => (
<Card href={`/posts/${slug}/`} frontmatter={data} /> <Card href={`/posts/${slug}/`} frontmatter={data} />
)) ))
} }
</ul> </ul>
</Main> </Main>
<Pagination <Pagination {page} />
{currentPage}
{totalPages}
prevUrl={`/tags/${tag}${
currentPage - 1 !== 1 ? "/" + (currentPage - 1) : ""
}/`}
nextUrl={`/tags/${tag}/${currentPage + 1}/`}
/>
<Footer noMarginTop={totalPages > 1} /> <Footer noMarginTop={page.lastPage > 1} />
</Layout> </Layout>
+15
View File
@@ -0,0 +1,15 @@
---
import { SITE } from "@config";
import Posts from "@layouts/Posts.astro";
import type { GetStaticPaths } from "astro";
import { getCollection } from "astro:content";
export const getStaticPaths = (async ({ paginate }) => {
const posts = await getCollection("blog", ({ data }) => !data.draft);
return paginate(posts, { pageSize: SITE.postPerPage });
}) satisfies GetStaticPaths;
const { page } = Astro.props;
---
<Posts {page} />
+15
View File
@@ -0,0 +1,15 @@
---
import { SITE } from "@config";
import Posts from "@layouts/Posts.astro";
import type { GetStaticPaths } from "astro";
import { getCollection } from "astro:content";
export const getStaticPaths = (async ({ paginate }) => {
const posts = await getCollection("blog", ({ data }) => !data.draft);
return paginate(posts, { pageSize: SITE.postPerPage });
}) satisfies GetStaticPaths;
const { page } = Astro.props;
---
<Posts {page} />
+2 -20
View File
@@ -1,10 +1,6 @@
--- ---
import { type CollectionEntry, getCollection } from "astro:content"; import { type CollectionEntry, getCollection } from "astro:content";
import Posts from "@layouts/Posts.astro";
import PostDetails from "@layouts/PostDetails.astro"; import PostDetails from "@layouts/PostDetails.astro";
import getSortedPosts from "@utils/getSortedPosts";
import getPageNumbers from "@utils/getPageNumbers";
import getPagination from "@utils/getPagination";
export interface Props { export interface Props {
post: CollectionEntry<"blog">; post: CollectionEntry<"blog">;
@@ -18,24 +14,10 @@ export async function getStaticPaths() {
props: { post }, props: { post },
})); }));
const pagePaths = getPageNumbers(posts.length).map(pageNum => ({ return postResult;
params: { slug: String(pageNum) },
}));
return [...postResult, ...pagePaths];
} }
const { slug } = Astro.params;
const { post } = Astro.props; const { post } = Astro.props;
const posts = await getCollection("blog");
const sortedPosts = getSortedPosts(posts);
const pagination = getPagination({
posts: sortedPosts,
page: slug,
});
--- ---
{post ? <PostDetails post={post} /> : <Posts {...pagination} />} <PostDetails post={post} />
-18
View File
@@ -1,18 +0,0 @@
---
import { getCollection } from "astro:content";
import Posts from "@layouts/Posts.astro";
import getSortedPosts from "@utils/getSortedPosts";
import getPagination from "@utils/getPagination";
const posts = await getCollection("blog");
const sortedPosts = getSortedPosts(posts);
const pagination = getPagination({
posts: sortedPosts,
page: 1,
isIndex: true,
});
---
<Posts {...pagination} />
+29
View File
@@ -0,0 +1,29 @@
---
import { getCollection } from "astro:content";
import TagPosts from "@layouts/TagPosts.astro";
import getUniqueTags from "@utils/getUniqueTags";
import getPostsByTag from "@utils/getPostsByTag";
import type { GetStaticPathsOptions } from "astro";
import { SITE } from "@config";
export async function getStaticPaths({ paginate }: GetStaticPathsOptions) {
const posts = await getCollection("blog");
const tags = getUniqueTags(posts);
return tags.flatMap(({ tag, tagName }) => {
const tagPosts = getPostsByTag(posts, tag);
return paginate(tagPosts, {
params: { tag },
props: { tagName },
pageSize: SITE.postPerPage,
});
});
}
const params = Astro.params;
const { tag } = params;
const { page, tagName } = Astro.props;
---
<TagPosts {page} {tag} {tagName} />
+13 -28
View File
@@ -1,44 +1,29 @@
--- ---
import { type CollectionEntry, getCollection } from "astro:content"; import { getCollection } from "astro:content";
import TagPosts from "@layouts/TagPosts.astro"; import TagPosts from "@layouts/TagPosts.astro";
import getUniqueTags from "@utils/getUniqueTags"; import getUniqueTags from "@utils/getUniqueTags";
import getPostsByTag from "@utils/getPostsByTag"; import getPostsByTag from "@utils/getPostsByTag";
import getPageNumbers from "@utils/getPageNumbers"; import type { GetStaticPathsOptions } from "astro";
import getPagination from "@utils/getPagination"; import { SITE } from "@config";
export interface Props { export async function getStaticPaths({ paginate }: GetStaticPathsOptions) {
post: CollectionEntry<"blog">;
tag: string;
tagName: string;
}
export async function getStaticPaths() {
const posts = await getCollection("blog"); const posts = await getCollection("blog");
const tags = getUniqueTags(posts); const tags = getUniqueTags(posts);
return tags.flatMap(({ tag, tagName }) => { return tags.flatMap(({ tag, tagName }) => {
const tagPosts = getPostsByTag(posts, tag); const tagPosts = getPostsByTag(posts, tag);
const totalPages = getPageNumbers(tagPosts.length);
return totalPages.map(page => ({ return paginate(tagPosts, {
params: { tag, page }, params: { tag },
props: { tag, tagName }, props: { tagName },
})); pageSize: SITE.postPerPage,
});
}); });
} }
const { page } = Astro.params; const params = Astro.params;
const { tag, tagName } = Astro.props; const { tag } = params;
const { page, tagName } = Astro.props;
const posts = await getCollection("blog", ({ data }) => !data.draft);
const postsByTag = getPostsByTag(posts, tag);
const pagination = getPagination({
posts: postsByTag,
page,
});
--- ---
<TagPosts {...pagination} {tag} {tagName} /> <TagPosts {page} {tag} {tagName} />
-32
View File
@@ -1,32 +0,0 @@
---
import { getCollection } from "astro:content";
import TagPosts from "@layouts/TagPosts.astro";
import getPostsByTag from "@utils/getPostsByTag";
import getPagination from "@utils/getPagination";
import getUniqueTags from "@utils/getUniqueTags";
export async function getStaticPaths() {
const posts = await getCollection("blog");
const tags = getUniqueTags(posts);
return tags.map(({ tag, tagName }) => {
return {
params: { tag },
props: { tag, tagName, posts },
};
});
}
const { tag, tagName, posts } = Astro.props;
const postsByTag = getPostsByTag(posts, tag);
const pagination = getPagination({
posts: postsByTag,
page: 1,
isIndex: true,
});
---
<TagPosts {...pagination} {tag} {tagName} />
-14
View File
@@ -1,14 +0,0 @@
import { SITE } from "@config";
const getPageNumbers = (numberOfPosts: number) => {
const numberOfPages = numberOfPosts / Number(SITE.postPerPage);
let pageNumbers: number[] = [];
for (let i = 1; i <= Math.ceil(numberOfPages); i++) {
pageNumbers = [...pageNumbers, i];
}
return pageNumbers;
};
export default getPageNumbers;
-35
View File
@@ -1,35 +0,0 @@
import { SITE } from "@config";
import getPageNumbers from "./getPageNumbers";
interface GetPaginationProps<T> {
posts: T;
page: string | number;
isIndex?: boolean;
}
const getPagination = <T>({
posts,
page,
isIndex = false,
}: GetPaginationProps<T[]>) => {
const totalPagesArray = getPageNumbers(posts.length);
const totalPages = totalPagesArray.length;
const currentPage = isIndex
? 1
: page && !isNaN(Number(page)) && totalPagesArray.includes(Number(page))
? Number(page)
: 0;
const lastPost = isIndex ? SITE.postPerPage : currentPage * SITE.postPerPage;
const startPost = isIndex ? 0 : lastPost - SITE.postPerPage;
const paginatedPosts = posts.slice(startPost, lastPost);
return {
totalPages,
currentPage,
paginatedPosts,
};
};
export default getPagination;