Skip to content

Commit e64708c

Browse files
committed
feat: 初始化list页面布局
1 parent a12b3d6 commit e64708c

File tree

6 files changed

+173
-47
lines changed

6 files changed

+173
-47
lines changed

src/app/(app)/list/page.tsx

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
'use client';
2+
3+
import React from 'react';
4+
import { m } from 'framer-motion';
5+
6+
import { NormalContainer } from '@/components/layout/container/Normal';
7+
import { PostItem } from '@/components/modules/list/PostItem';
8+
9+
// 假数据
10+
const mockData = [
11+
{ id: 1, title: '文章标题 1', content: '这是文章内容 1' },
12+
{ id: 2, title: '文章标题 2', content: '这是文章内容 2' },
13+
{ id: 3, title: '文章标题 3', content: '这是文章内容 3' },
14+
{ id: 12, title: '文章标题 1', content: '这是文章内容 1' },
15+
{ id: 22, title: '文章标题 2', content: '这是文章内容 2' },
16+
{ id: 32, title: '文章标题 3', content: '这是文章内容 3' },
17+
18+
// 更多假数据
19+
];
20+
21+
const ArticleList: React.FC = () => {
22+
return (
23+
<NormalContainer>
24+
<ul>
25+
{mockData.map((item, index) => (
26+
<m.li
27+
initial={{ y: 50, opacity: 0.01 }}
28+
animate={{
29+
y: 0,
30+
opacity: 1,
31+
transition: {
32+
delay: index * 0.1,
33+
type: 'spring',
34+
damping: 10,
35+
stiffness: 100,
36+
},
37+
}}
38+
key={item.id}
39+
>
40+
<PostItem data={item} />
41+
</m.li>
42+
))}
43+
</ul>
44+
</NormalContainer>
45+
);
46+
};
47+
48+
export default ArticleList;

src/components/icons/fa-hash.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import type { SVGProps } from 'react';
2+
3+
export function FeHash(props: SVGProps<SVGSVGElement>) {
4+
return (
5+
<svg width="1em" height="1em" viewBox="0 0 24 24" {...props}>
6+
<path
7+
fill="currentColor"
8+
fillRule="evenodd"
9+
d="M10 15h4V9h-4v6Zm0 2v3a1 1 0 0 1-2 0v-3H5a1 1 0 0 1 0-2h3V9H5a1 1 0 1 1 0-2h3V4a1 1 0 1 1 2 0v3h4V4a1 1 0 0 1 2 0v3h3a1 1 0 0 1 0 2h-3v6h3a1 1 0 0 1 0 2h-3v3a1 1 0 0 1-2 0v-3h-4Z"
10+
/>
11+
</svg>
12+
);
13+
}

src/components/layout/Footer/Footer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const Footer = () => {
66
return (
77
<footer
88
data-hide-print
9-
className="relative z-[1] h-28 mt-32 pb-6 border-t border-x-uk-separator-opaque-light py-6 text-base-content/80 dark:border-white/10"
9+
className="relative z-[1] h-28 mt-44 pb-6 border-t border-x-uk-separator-opaque-light py-6 text-base-content/80 dark:border-white/10"
1010
>
1111
<div className="px-4 sm:px-8 h-full">
1212
<div className="relative mx-auto max-w-7xl lg:px-8 h-full flex flex-row items-center justify-around">

src/components/layout/Header/HeaderCenterContent.tsx

Lines changed: 9 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,10 @@ import Link from 'next/link';
88
import { MenuPopover } from './MenuPopover';
99

1010
import {
11-
FaSolidCircleNotch,
1211
FaSolidComments,
1312
FaSolidDotCircle,
1413
FaSolidFeatherAlt,
15-
FaSolidHistory,
1614
FaSolidUserFriends,
17-
IcTwotoneSignpost,
18-
IonBook,
1915
MdiFlask,
2016
} from '@/components/icons/menu-collection';
2117
import { cn } from '@/lib/helper';
@@ -186,56 +182,23 @@ const headerMenuConfig: IHeaderMenu[] = [
186182
},
187183
{
188184
title: '文稿',
189-
path: '/posts',
190-
type: 'Post',
191-
subMenu: [],
192-
icon: React.createElement(IcTwotoneSignpost),
193-
},
194-
{
195-
title: '手记',
196185
type: 'Note',
197-
path: '/notes',
186+
path: '/list',
198187
icon: React.createElement(FaSolidFeatherAlt),
199188
},
200-
201-
{
202-
title: '时光',
203-
icon: React.createElement(FaSolidHistory),
204-
path: '/timeline',
205-
subMenu: [
206-
{
207-
title: '手记',
208-
icon: React.createElement(FaSolidFeatherAlt),
209-
path: '/timeline?type=note',
210-
},
211-
{
212-
title: '文稿',
213-
icon: React.createElement(IonBook),
214-
path: '/timeline?type=post',
215-
},
216-
],
217-
},
218189
{
219190
title: '友链',
220191
icon: React.createElement(FaSolidUserFriends),
221192
path: '/friends',
222193
},
223-
224194
{
225-
title: '更多',
226-
icon: React.createElement(FaSolidCircleNotch),
227-
path: '#',
228-
subMenu: [
229-
{
230-
title: '项目',
231-
icon: React.createElement(MdiFlask),
232-
path: '/projects',
233-
},
234-
{
235-
title: '自述',
236-
path: '/about',
237-
icon: React.createElement(FaSolidComments),
238-
},
239-
],
195+
title: '项目',
196+
icon: React.createElement(MdiFlask),
197+
path: '/projects',
198+
},
199+
{
200+
title: '自述',
201+
path: '/about',
202+
icon: React.createElement(FaSolidComments),
240203
},
241204
];
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import Link from 'next/link';
2+
import { memo } from 'react';
3+
4+
import { PostItemHoverOverlay } from './PostItemHoverOverlay';
5+
6+
import { MdiClockOutline } from '@/components/icons/clock';
7+
import { FeHash } from '@/components/icons/fa-hash';
8+
9+
export const PostItem = memo<{ data: any }>(function PostItem({ data }) {
10+
const categorySlug = data.category?.slug;
11+
const postLink = `/posts/${categorySlug}/${data.slug}`;
12+
13+
return (
14+
<Link href={postLink} className="relative flex flex-col py-8 focus-visible:!shadow-none">
15+
<PostItemHoverOverlay />
16+
<h2 className="relative text-balance break-words text-2xl font-medium">{data.title}</h2>
17+
18+
<div className="post-meta-bar mt-2 flex select-none flex-wrap items-center justify-start gap-8 text-base-content/60">
19+
<span className=" flex min-w-0 items-center space-x-1 text-sm">
20+
<MdiClockOutline />
21+
<p>2024 年 6 月 4 日 星期二</p>
22+
</span>
23+
<span className="flex min-w-0 items-center space-x-1 text-sm">
24+
<FeHash className="translate-y-[0.5px]" />
25+
<p>技术/react</p>
26+
</span>
27+
</div>
28+
</Link>
29+
);
30+
});
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
'use client';
2+
3+
import clsx from 'clsx';
4+
import { AnimatePresence, m } from 'framer-motion';
5+
import { useEffect, useRef, useState } from 'react';
6+
7+
export const PostItemHoverOverlay = () => {
8+
const [mouseEnter, setMouseEnter] = useState(false);
9+
const ref = useRef<HTMLDivElement>(null);
10+
useEffect(() => {
11+
const $ref = ref.current;
12+
if (!$ref) return;
13+
14+
const $parent = $ref.parentElement;
15+
16+
if (!$parent) return;
17+
18+
$parent.onfocus = () => {
19+
setMouseEnter(true);
20+
};
21+
22+
$parent.onblur = () => {
23+
setMouseEnter(false);
24+
};
25+
26+
return () => {
27+
$parent.onfocus = null;
28+
$parent.onblur = null;
29+
};
30+
}, []);
31+
32+
return (
33+
<>
34+
<div
35+
ref={ref}
36+
className="absolute inset-0 z-10"
37+
onMouseEnter={() => {
38+
setMouseEnter(true);
39+
}}
40+
onMouseLeave={() => {
41+
setMouseEnter(false);
42+
}}
43+
/>
44+
45+
<AnimatePresence>
46+
{mouseEnter && (
47+
<m.div
48+
layout
49+
initial={{
50+
opacity: 0.2,
51+
scale: 0.95,
52+
}}
53+
animate={{
54+
opacity: 1,
55+
scale: 1,
56+
}}
57+
exit={{
58+
opacity: 0,
59+
scale: 0.95,
60+
}}
61+
layoutId="post-item-hover-overlay"
62+
className={clsx(
63+
'absolute z-[-1] rounded-xl',
64+
'bg-accent/10 dark:bg-neutral-800',
65+
'inset-y-4 -left-4 -right-6',
66+
)}
67+
/>
68+
)}
69+
</AnimatePresence>
70+
</>
71+
);
72+
};

0 commit comments

Comments
 (0)