ginb
Version:
Write & Share all in GitHub
169 lines (163 loc) • 6.23 kB
JSX
import Link from "next/link";
import { slug } from "github-slugger";
import { memo } from "react";
import { post_per_page } from "/_config";
import { notFound } from "next/navigation";
const formatDate = (date) => {
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "2-digit",
day: "2-digit",
});
return formatter.format(new Date(date));
};
export const PostCard = memo(
async ({ title, created_at, summary, path, tags, html_url, comments }) => {
return (
<div className="flex h-full w-full flex-col gap-4 rounded-lg border p-4">
<h2 className="text-2xl">
<Link href={"/" + path}>{title}</Link>
</h2>
<p className="flex-1 text-black/60 dark:text-white/60">{summary}</p>
<div className="flex justify-between">
<div className="text-sm text-black/60 dark:text-white/60">
<span>{formatDate(created_at)}</span>
{tags?.length > 0 &&
tags.map((tag) => (
<>
<span className="px-1">·</span>
<Link
className="hover:text-black dark:hover:text-white"
href={"/tags/" + slug(tag)}
>
{tag}
</Link>
</>
))}
</div>
{comments > 0 && (
<div className="text-sm text-black/60 hover:text-black dark:text-white/60 dark:hover:text-white">
<Link className="flex flex-row" href={html_url} target="_blank">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
className="my-auto h-4 w-4"
>
<path
fillRule="evenodd"
d="M1 8.74c0 .983.713 1.825 1.69 1.943.904.108 1.817.19 2.737.243.363.02.688.231.85.556l1.052 2.103a.75.75 0 0 0 1.342 0l1.052-2.103c.162-.325.487-.535.85-.556.92-.053 1.833-.134 2.738-.243.976-.118 1.689-.96 1.689-1.942V4.259c0-.982-.713-1.824-1.69-1.942a44.45 44.45 0 0 0-10.62 0C1.712 2.435 1 3.277 1 4.26v4.482Zm3-3.49a.75.75 0 0 1 .75-.75h6.5a.75.75 0 0 1 0 1.5h-6.5A.75.75 0 0 1 4 5.25ZM4.75 7a.75.75 0 0 0 0 1.5h2.5a.75.75 0 0 0 0-1.5h-2.5Z"
clipRule="evenodd"
/>
</svg>
<span>{comments} Comments</span>
</Link>
</div>
)}
</div>
</div>
);
},
);
export const PageNav = memo(async ({ current, prev, prev_link, next, next_link, total }) => {
let prevProps = {};
let nextProps = {};
if (prev && prev != 0) {
prevProps.name = Link;
prevProps.className =
"flex flex-row gap-2 rounded-lg border border-transparent hover:border-current p-2 text-black/60 hover:text-black dark:text-white/60 dark:hover:text-white";
prevProps.href = prev_link;
} else {
prevProps.name = "div";
prevProps.className = "flex flex-row gap-2 rounded-lg p-2 invisible";
}
if (next && next != 0 && next <= total) {
nextProps.name = Link;
nextProps.className =
"flex flex-row gap-2 rounded-lg border border-transparent hover:border-current p-2 text-black/60 hover:text-black dark:text-white/60 dark:hover:text-white";
nextProps.href = next_link;
} else {
nextProps.name = "div";
nextProps.className = "flex flex-row gap-2 rounded-lg p-2 invisible";
}
return (
<>
<div className="mt-4 flex flex-row justify-between">
<prevProps.name {...prevProps}>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
className="h-5 w-5"
>
<path
fillRule="evenodd"
d="M7.793 2.232a.75.75 0 0 1-.025 1.06L3.622 7.25h10.003a5.375 5.375 0 0 1 0 10.75H10.75a.75.75 0 0 1 0-1.5h2.875a3.875 3.875 0 0 0 0-7.75H3.622l4.146 3.957a.75.75 0 0 1-1.036 1.085l-5.5-5.25a.75.75 0 0 1 0-1.085l5.5-5.25a.75.75 0 0 1 1.06.025Z"
clipRule="evenodd"
/>
</svg>
<span>Prev</span>
</prevProps.name>
<span className="p-2">
{current} / {total}
</span>
<nextProps.name {...nextProps}>
<span>Next</span>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
className="h-5 w-5"
>
<path
fillRule="evenodd"
d="M12.207 2.232a.75.75 0 0 0 .025 1.06l4.146 3.958H6.375a5.375 5.375 0 0 0 0 10.75H9.25a.75.75 0 0 0 0-1.5H6.375a3.875 3.875 0 0 1 0-7.75h10.003l-4.146 3.957a.75.75 0 0 0 1.036 1.085l5.5-5.25a.75.75 0 0 0 0-1.085l-5.5-5.25a.75.75 0 0 0-1.06.025Z"
clipRule="evenodd"
/>
</svg>
</nextProps.name>
</div>
</>
);
});
const PostList = async ({ base, current = 1, posts }) => {
const pageNumber = current;
const currentPosts = posts.slice(post_per_page * (pageNumber - 1), post_per_page * pageNumber);
const pagination = {
currentPage: pageNumber,
totalPages: Math.max(Math.ceil(posts.length / post_per_page), 1),
};
if (currentPosts.length === 0) {
return notFound();
}
return (
<>
<ul className="grid auto-rows-min grid-cols-1 gap-4 lg:grid-cols-2">
{currentPosts.map((post) => (
<li key={post.slug}>
<PostCard
title={post.title}
created_at={post.created_at}
summary={post.summary}
path={post.path}
html_url={post.html_url}
tags={post.tags}
comments={post.comments}
/>
</li>
))}
</ul>
<PageNav
current={pagination.currentPage}
prev={pagination.currentPage - 1}
prev_link={
pagination.currentPage - 1 > 1 ? `${base}page/${pagination.currentPage - 1}` : base
}
next={pagination.currentPage + 1}
next_link={`${base}page/${pagination.currentPage + 1}`}
total={pagination.totalPages}
/>
</>
);
};
export default memo(PostList);