Skip to content

Commit

Permalink
feat: complete blog page
Browse files Browse the repository at this point in the history
  • Loading branch information
liony823 committed May 12, 2024
1 parent 2211d6e commit ccd61f9
Show file tree
Hide file tree
Showing 28 changed files with 437 additions and 196 deletions.
Binary file modified bun.lockb
Binary file not shown.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"react-hook-form": "^7.51.3",
"react-resizable-panels": "^2.0.18",
"react-type-animation": "^3.2.0",
"react-wrap-balancer": "^1.1.0",
"tailwind-merge": "^2.2.2",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.22.5"
Expand Down
2 changes: 1 addition & 1 deletion src/app/(site)/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export function Header() {
<>
<motion.header
className={cn(
'relative w-full z-50 flex flex-col mb-[var(--header-mb,0px)]',
'relative w-full z-10 flex flex-col mb-[var(--header-mb,0px)]',
isHomePage
? 'h-[var(--header-height,180px)]'
: 'h-[var(--header-height,64px)]'
Expand Down
2 changes: 1 addition & 1 deletion src/app/(site)/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ function MobileNav({ className }: { className?: string }) {
<nav className="mt-4">
<ul className="divide-y text-base text-zinc-800 dark:text-zinc-300 divide-zinc-500/20 flex flex-col">
{Navs.map((nav) => (
<li key={nav.link}>
<li key={nav.link} onClick={() => setDialogOpen(false)}>
<Link href={nav.link} className="py-3 block">
{nav.label}
</Link>
Expand Down
18 changes: 6 additions & 12 deletions src/app/(site)/Post.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { EResponseCode } from '~/config/enum';
import http from '~/http';
import { RecentBlogArticles } from './blog/BlogArticles';
import { PencilSwooshIcon } from '~/assets';

async function fetchArticles() {
const { data, code } = await http<ArticleListRes, ArticleListReq>(
Expand All @@ -17,17 +16,12 @@ async function fetchArticles() {

export default async function Post() {
const articles = await fetchArticles();
return (
<div>
<p className="text-sm font-semibold mb-10 text-zinc-800 dark:text-zinc-200 flex items-center">
<PencilSwooshIcon className="mr-2 text-xl" />
近期文章
</p>
{articles && articles.length > 0 ? (
<RecentBlogArticles articles={articles || []} />
) : (
<p className="mb-2 text-muted-foreground">暂无博客...</p>
)}
return articles && articles.length > 0 ? (
<div className="relative sm:pb-12 sm:ml-[calc(2rem+1px)] md:ml-[calc(3.5rem+1px)] lg:ml-[max(calc(14.5rem+1px),calc(100%-50rem))]">
<div className="hidden absolute top-3 bottom-0 right-full mr-7 md:mr-[3.25rem] w-px bg-slate-200 dark:bg-slate-800 sm:block"></div>
<RecentBlogArticles articles={articles || []} />
</div>
) : (
<p className="mb-2 text-muted-foreground">暂无博客...</p>
);
}
6 changes: 3 additions & 3 deletions src/app/(site)/Resume.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default function Resume() {
variants={variants}
initial="initial"
animate="animate"
className="relative w-full flex flex-col gap-4 pt-10 text-2xl h-[calc(100vh-var(--header-height))]"
className="relative w-full flex flex-col gap-4 pt-10 text-2xl h-[calc(80vh-var(--header-height))] lg:h-[calc(100vh-var(--header-height))]"
>
<motion.p variants={variantItm}>
👋你好,我是
Expand All @@ -43,9 +43,9 @@ export default function Resume() {
className="text-2xl font-medium"
sequence={[
300,
'一名全栈开发工程师 。',
'一名前端开发工程师。',
2000,
'A Full Stack <Developer /> .',
'A Front-end <Developer /> .',
5000
]}
speed={10}
Expand Down
103 changes: 55 additions & 48 deletions src/app/(site)/blog/BlogArticleCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,7 @@
import dayjs from 'dayjs';
import { motion } from 'framer-motion';
import Link from 'next/link';
import {
ArrowRightIcon,
CalendarIcon,
CursorClickIcon,
TagIcon
} from '~/assets';
import { prettifyNumber } from '~/lib/math';
import { ArrowRightIcon, CalendarIcon, CircleIcon, TagIcon } from '~/assets';
import Image from 'next/image';

export function RecentBlogArticleCard({ article }: { article: Article }) {
Expand All @@ -25,41 +19,49 @@ export function RecentBlogArticleCard({ article }: { article: Article }) {
}
}
}}
className="group relative flex flex-col items-start"
className="relative group"
>
<h2 className="text-base font-semibold tracking-tight text-zinc-800 dark:text-zinc-100">
<Link href={{ pathname: `/blog/${article.id}` }}>
<span className="absolute -inset-x-4 -inset-y-6 z-20 sm:-inset-x-6 sm:rounded-2xl"></span>
<span className="relative z-10">{article.title}</span>
</Link>
</h2>
<div className="relative z-10 flex justify-between mb-3 order-first text-sm text-zinc-400 dark:text-zinc-500 space-x-3">
<span className="absolute inset-y-0 left-0 flex items-center">
<span className="bg-zinc-200 dark:bg-zinc-500 w-0.5 h-4 rounded-full"></span>
</span>
<time className="flex items-center space-x-2">
<CalendarIcon />
<span>{dayjs(article.createAt).format('DD/MM/YYYY')}</span>
</time>
<span className="flex items-center space-x-2">
<CursorClickIcon />
<span>{prettifyNumber(2000)}</span>
</span>
<div className="absolute -inset-y-2.5 -inset-x-4 md:-inset-y-4 md:-inset-x-6 sm:rounded-2xl group-hover:bg-amber-50/70 dark:group-hover:bg-amber-800/50 opacity-0 scale-95 transition group-hover:scale-100 group-hover:opacity-100"></div>
<CircleIcon className="hidden absolute right-full mr-6 top-2 text-slate-200 dark:text-slate-600 md:mr-12 w-[calc(0.5rem+1px)] h-[calc(0.5rem+1px)] overflow-visible sm:block" />
<div className="relative grid grid-cols-1 sm:grid-cols-3">
<div className="sm:col-span-2">
<h3 className="text-base font-semibold tracking-tight text-zinc-800 dark:text-zinc-100 pt-8 lg:pt-0">
{article.title}
</h3>
<dl className="absolute left-0 top-0 lg:left-auto lg:right-full lg:mr-[calc(6.5rem+1px)] text-slate-500 dark:text-slate-400">
<dt className="sr-only">Date</dt>
<dd className="whitespace-nowrap text-sm leading-6 dark:text-slate-400">
<time dateTime={article.createAt}>
{dayjs(article.createAt).format('DD/MM/YYYY')}
</time>
</dd>
</dl>
<div className="mt-2 mb-4 text-sm text-zinc-600 dark:text-zinc-400">
<p>{article.summary}</p>
<p className="relative mt-2 flex items-center opacity-85">
<TagIcon />
<span>{article.tags.join('、')}</span>
</p>
</div>
</div>
<Image
src={article.coverUrl}
alt={article.title}
width={2880}
height={1510}
className="hidden sm:block sm:col-span-1 ml-3 object-contain"
/>
</div>

<p className="relative z-10 mt-2 text-sm text-zinc-600 dark:text-zinc-400">
{article.summary}
</p>
<p className="relative z-10 mt-2 text-sm flex items-center text-zinc-500 dark:text-zinc-300 space-x-2">
<TagIcon />
<span>{article.tags.join('、')}</span>
</p>
<p className="relative z-10 mt-4 text-sm flex items-center text-amber-700 group-hover:text-amber-800 transition-colors">
阅读更多{' '}
<Link
href={{ pathname: `/blog/${article.title}` }}
className="text-sm flex items-center text-amber-700 group-hover:text-amber-800 transition-colors"
>
<span className="relative">
阅读博客<span className="sr-only">, {article.title}</span>
</span>
<span className="absolute -inset-y-2.5 -inset-x-4 md:-inset-y-4 md:-inset-x-6 sm:rounded-2xl"></span>
<ArrowRightIcon className="w-5 h-4 ml-2 opacity-0 -translate-x-2 group-hover:translate-x-0 group-hover:opacity-85 transition-all ease-in-out duration-150" />
</p>

<div className="absolute -inset-x-4 -inset-y-6 z-0 bg-zinc-100 sm:-inset-x-6 sm:rounded-2xl dark:bg-zinc-800/50 opacity-0 scale-95 transition group-hover:scale-100 group-hover:opacity-100"></div>
</Link>
</motion.article>
);
}
Expand All @@ -77,7 +79,7 @@ export function BlogArticleCard({ article }: { article: Article }) {
// }
}
}}
className="group relative flex flex-col rounded-2xl border cursor-pointer"
className="group relative flex flex-col rounded-2xl border"
>
<Image
src={article.coverUrl}
Expand All @@ -87,21 +89,26 @@ export function BlogArticleCard({ article }: { article: Article }) {
className="w-full rounded-t-2xl"
/>
<div className="px-5 py-4">
<h2 className="text-base font-semibold tracking-tight text-zinc-800 dark:text-zinc-100">
{article.title}
</h2>
<Link
href={`/blog/${article.title}`}
className="hover:underline underline-offset-1"
>
<h2 className="text-base font-semibold tracking-tight text-zinc-800 dark:text-zinc-100">
{article.title}
</h2>
</Link>
<p className="mt-2 text-sm text-zinc-600 dark:text-zinc-400">
{article.summary}
</p>
<div className="flex my-3 text-sm text-zinc-400 dark:text-zinc-500 space-x-3">
<div className="flex items-center space-x-3 justify-between my-3 text-sm text-zinc-400 dark:text-zinc-500">
<time className="flex items-center space-x-2">
<CalendarIcon />
<span>{dayjs(article.createAt).format('DD/MM/YYYY')}</span>
</time>
<span className="flex items-center space-x-2">
<CursorClickIcon />
<span>{prettifyNumber(2000)}</span>
</span>
<p className="relative flex items-center">
<TagIcon />
<span>{article.tags.join('、')}</span>
</p>
</div>
</div>
</motion.article>
Expand Down
4 changes: 2 additions & 2 deletions src/app/(site)/blog/BlogArticles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,18 @@ export function BlogArticles({ articles }: { articles: Article[] }) {
export function RecentBlogArticles({ articles }: { articles: Article[] }) {
return (
<motion.div
className="space-y-16"
variants={{
initial: { opacity: 0 },
animate: {
opacity: 1,
transition: {
staggerChildren: 0.3
staggerChildren: 0.2
}
}
}}
initial="initial"
whileInView="animate"
className="flex flex-col gap-16"
>
{articles.map((article) => (
<RecentBlogArticleCard key={article.id} article={article} />
Expand Down
123 changes: 123 additions & 0 deletions src/app/(site)/blog/BlogPostPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
'use client';
import { Button, Container, MarkdownEditor, MarkdownText } from '~/components';
import Link from 'next/link';
import { CalendarIcon, TagIcon, UTurnLeftIcon } from '~/assets';
import { motion } from 'framer-motion';
import Image from 'next/image';
import dayjs from 'dayjs';
import Balancer from 'react-wrap-balancer';
import { MilkdownProvider } from '@milkdown/react';
import { ProsemirrorAdapterProvider } from '@prosemirror-adapter/react';

export function BlogPostPage({ post }: { post: Article }) {
return (
<Container className="mt-16 sm:mt-32">
<div className="w-full relative">
<Button
asChild
className="group z-20 mb-8 flex h-10 w-10 p-0 items-center justify-center rounded-full bg-white shadow-md shadow-zinc-800/5 ring-1 ring-zinc-900/5 transition dark:border dark:border-zinc-700/50 dark:bg-zinc-800 dark:ring-0 dark:ring-white/10 dark:hover:border-zinc-700 dark:hover:ring-white/20 lg:absolute lg:-left-5 lg:-mt-2 lg:mb-0 lg:z-10 xl:-top-1.5 xl:left-0 xl:mt-0"
variant="secondary"
aria-label="返回博客页面"
>
<Link href="/blog">
<UTurnLeftIcon className="stroke-zinc-500 transition group-hover:stroke-zinc-700 dark:stroke-zinc-500 dark:group-hover:stroke-zinc-400" />
</Link>
</Button>
<article>
<header className="relative flex flex-col items-center pb-5 after:absolute after:-bottom-1 after:block after:h-px after:w-full after:rounded after:bg-gradient-to-r after:from-zinc-400/20 after:via-zinc-200/10 after:to-transparent dark:after:from-zinc-600/20 dark:after:via-zinc-700/10">
<motion.div
className="relative mb-7 aspect-[240/135] w-full md:mb-12 md:w-[85%]"
initial={{ opacity: 0, scale: 0.96, y: 10 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
transition={{
duration: 0.35,
type: 'spring',
stiffness: 120,
damping: 20
}}
>
<div className="absolute z-0 hidden aspect-[240/135] w-full blur-xl saturate-150 after:absolute after:inset-0 after:hidden after:bg-white/50 dark:after:bg-black/50 md:block md:after:block">
<Image
src={post.coverUrl}
alt=""
className="select-none"
unoptimized
fill
aria-hidden={true}
/>
</div>
<Image
src={post.coverUrl}
alt={post.title}
className="select-none rounded-2xl ring-1 ring-zinc-900/5 transition dark:ring-0 dark:ring-white/10 dark:hover:border-zinc-700 dark:hover:ring-white/20 md:rounded-3xl"
placeholder="blur"
blurDataURL={post.coverUrl}
unoptimized
fill
/>
</motion.div>
<motion.div
className="flex w-full items-center space-x-4 text-sm font-medium text-zinc-600/80 dark:text-zinc-400/80"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.15,
type: 'spring',
stiffness: 150,
damping: 20,
delay: 0.1
}}
>
<time
dateTime={post.updateAt}
className="flex items-center space-x-1.5"
>
<CalendarIcon />
<span>{dayjs(post.updateAt).format('YYYY/MM/DD')}</span>
</time>
<span className="inline-flex items-center space-x-1.5">
<TagIcon />
<span>{post.tags?.join(', ')}</span>
</span>
</motion.div>
<motion.h1
className="mt-6 w-full text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl"
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.2,
type: 'spring',
stiffness: 150,
damping: 30,
delay: 0.2
}}
>
<Balancer>{post.title}</Balancer>
</motion.h1>
<motion.p
className="my-5 w-full text-sm font-medium text-zinc-500"
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.2,
type: 'spring',
stiffness: 150,
damping: 20,
delay: 0.23
}}
>
{post.summary}
</motion.p>
</header>
<div className="mt-8">
<MilkdownProvider>
<ProsemirrorAdapterProvider>
<MarkdownText value={post.content} />
</ProsemirrorAdapterProvider>
</MilkdownProvider>
</div>
</article>
</div>
</Container>
);
}
Loading

0 comments on commit ccd61f9

Please sign in to comment.