feat(blog): add pagination

This commit is contained in:
Jewgeni Lewash 2024-03-10 18:13:44 +01:00
parent 5d8d7f552c
commit e3c5b3f31f
4 changed files with 227 additions and 39 deletions

View File

@ -0,0 +1,117 @@
import * as React from "react"
import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react"
import { cn } from "@/lib/utils"
import { ButtonProps, buttonVariants } from "@/components/ui/button"
const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
<nav
role="navigation"
aria-label="pagination"
className={cn("mx-auto flex w-full justify-center", className)}
{...props}
/>
)
Pagination.displayName = "Pagination"
const PaginationContent = React.forwardRef<
HTMLUListElement,
React.ComponentProps<"ul">
>(({ className, ...props }, ref) => (
<ul
ref={ref}
className={cn("flex flex-row items-center gap-1", className)}
{...props}
/>
))
PaginationContent.displayName = "PaginationContent"
const PaginationItem = React.forwardRef<
HTMLLIElement,
React.ComponentProps<"li">
>(({ className, ...props }, ref) => (
<li ref={ref} className={cn("", className)} {...props} />
))
PaginationItem.displayName = "PaginationItem"
type PaginationLinkProps = {
isActive?: boolean
} & Pick<ButtonProps, "size"> &
React.ComponentProps<"a">
const PaginationLink = ({
className,
isActive,
size = "icon",
...props
}: PaginationLinkProps) => (
<a
aria-current={isActive ? "page" : undefined}
className={cn(
buttonVariants({
variant: isActive ? "outline" : "ghost",
size,
}),
className
)}
{...props}
/>
)
PaginationLink.displayName = "PaginationLink"
const PaginationPrevious = ({
className,
...props
}: React.ComponentProps<typeof PaginationLink>) => (
<PaginationLink
aria-label="Go to previous page"
size="default"
className={cn("gap-1 pl-2.5", className)}
{...props}
>
<ChevronLeft className="h-4 w-4" />
<span>Previous</span>
</PaginationLink>
)
PaginationPrevious.displayName = "PaginationPrevious"
const PaginationNext = ({
className,
...props
}: React.ComponentProps<typeof PaginationLink>) => (
<PaginationLink
aria-label="Go to next page"
size="default"
className={cn("gap-1 pr-2.5", className)}
{...props}
>
<span>Next</span>
<ChevronRight className="h-4 w-4" />
</PaginationLink>
)
PaginationNext.displayName = "PaginationNext"
const PaginationEllipsis = ({
className,
...props
}: React.ComponentProps<"span">) => (
<span
aria-hidden
className={cn("flex h-9 w-9 items-center justify-center", className)}
{...props}
>
<MoreHorizontal className="h-4 w-4" />
<span className="sr-only">More pages</span>
</span>
)
PaginationEllipsis.displayName = "PaginationEllipsis"
export {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
}

View File

@ -1,39 +0,0 @@
---
import { getCollection } from 'astro:content';
import BaseLayout from '@/layouts/BaseLayout.astro';
import Heading from '@/components/ui/Heading.astro';
const allBlogposts = await getCollection('blog');
---
<BaseLayout
title="Blog | Astro Deploy"
description="Explore insightful articles on our Blog: Dive into a world of knowledge through posts crafted with expertise. Discover, learn, and grow with us.">
<div class="container mx-auto px-4 py-16 sm:py-24 lg:px-8 lg:py-32 xl:max-w-7xl">
<div class="mb-12 text-center text-zinc-900 dark:text-zinc-200">
<Heading level={1}>Blog</Heading>
<p>Here are some blogposts, they are located in the repository as mdx files.</p>
</div>
<hr class="mb-12 dark:border-zinc-700/75" />
<div class="grid grid-cols-1 gap-8 md:grid-cols-2">
{
allBlogposts.map((post) => (
<div class="flex flex-col space-y-4">
<h2
transition:name={post.data.title}
class="text-lg font-semibold text-zinc-800 hover:text-zinc-600 dark:text-zinc-200 dark:hover:text-zinc-400 sm:text-xl">
<a href={`/blog/${post.slug}/`}>{post.data.title}</a>
</h2>
<p class="text-sm leading-relaxed text-zinc-600 dark:text-zinc-400">
{post.data.description}
</p>
<a
class="text-sm font-medium text-primary-600 hover:text-primary-400 dark:text-primary-400 dark:hover:text-primary-300"
href={`/blog/${post.slug}/`}>
Read more
</a>
</div>
))
}
</div>
</div>
</BaseLayout>

107
src/pages/blog/[page].astro Normal file
View File

@ -0,0 +1,107 @@
---
import { getCollection } from 'astro:content';
import BaseLayout from '@/layouts/BaseLayout.astro';
import Heading from '@/components/ui/Heading.astro';
import {
Pagination,
PaginationContent,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from '@/components/ui/pagination';
interface BlogPost {
data: {
title: string;
description: string;
};
slug: string;
}
export async function getStaticPaths({ paginate }) {
const allBlogposts = await getCollection('blog');
const postsPerPage = 4;
return paginate(allBlogposts, {
pageSize: postsPerPage,
});
}
const {
page,
}: {
page: {
data: BlogPost[];
url: { prev: string; current: string; next: string };
currentPage: number;
lastPage: number;
};
} = Astro.props;
---
<BaseLayout
title="Blog | Astro Deploy"
description="Explore insightful articles on our Blog: Dive into a world of knowledge through posts crafted with expertise. Discover, learn, and grow with us.">
<div class="container mx-auto px-4 py-16 sm:py-24 lg:px-8 lg:py-32 xl:max-w-7xl">
<div class="mb-12 text-center text-zinc-900 dark:text-zinc-200">
<Heading level={1}>Blog</Heading>
<p>Here are some blogposts, they are located in the repository as mdx files.</p>
</div>
<hr class="mb-12 dark:border-zinc-700/75" />
<div class="grid grid-cols-1 gap-8 md:grid-cols-2">
{
page.data.map((post) => (
<div class="flex flex-col space-y-4">
<h2
transition:name={post.data.title}
class="text-lg font-semibold text-zinc-800 hover:text-zinc-600 dark:text-zinc-200 dark:hover:text-zinc-400 sm:text-xl">
<a href={`/blog/${post.slug}/`}>{post.data.title}</a>
</h2>
<p class="text-sm leading-relaxed text-zinc-600 dark:text-zinc-400">
{post.data.description}
</p>
<a
class="text-sm font-medium text-primary-600 hover:text-primary-400 dark:text-primary-400 dark:hover:text-primary-300"
href={`/blog/${post.slug}/`}>
Read more
</a>
</div>
))
}
</div>
<div class="mt-12 text-black dark:text-white">
<Pagination>
<PaginationContent>
{
page.currentPage > 1 && (
<>
<PaginationItem>
<PaginationPrevious href={page.url.prev} />
</PaginationItem>
<PaginationItem>
<PaginationLink href="/blog/">1</PaginationLink>
</PaginationItem>
</>
)
}
<PaginationItem>
<PaginationLink href={page.url.current} isActive>{page.currentPage} </PaginationLink>
</PaginationItem>
{
page.currentPage < page.lastPage && (
<>
<PaginationItem>
<PaginationLink href={`/blog/${page.lastPage}`}>{page.lastPage}</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationNext href={page.url.next} />
</PaginationItem>
</>
)
}
</PaginationContent>
</Pagination>
</div>
</div>
</BaseLayout>

View File

@ -0,0 +1,3 @@
---
return Astro.redirect('/blog/1');
---