11---
2- import type { BlogEntry , SiteLang } from " @/lib/blog" ;
3- import { formatBlogDate } from " @/lib/blog " ;
2+ import type { SiteLang } from " @/lib/blog" ;
3+ import { renderNewsBody , type NewsEntry } from " @/lib/news " ;
44import Button from " @/components/Button.astro" ;
55
66interface Props {
77 lang: SiteLang ;
8- posts : BlogEntry [];
8+ news : NewsEntry [];
99}
1010
11- const { lang, posts } = Astro .props ;
11+ const { lang, news } = Astro .props ;
1212
1313const copy = {
1414 zh: {
1515 eyebrow: " Timeline / News" ,
1616 title: " 近期动态" ,
17- description:
18- " 用一条时间线把最近的更新节点串起来。点开任意标题,就能进入对应博客页。" ,
17+ description: " " ,
1918 empty: " 当前还没有新闻,稍后再来看看。" ,
2019 cta: " 查看全部动态" ,
21- link: " 查看节点" ,
2220 },
2321 en: {
2422 eyebrow: " Timeline / News" ,
2523 title: " Latest Updates" ,
26- description:
27- " A single timeline connects the latest milestones. Open any title to jump to the matching blog post." ,
24+ description: " " ,
2825 empty: " No news posts yet. Check back soon." ,
29- cta: " Browse all posts" ,
30- link: " Open node" ,
26+ cta: " View all updates" ,
3127 },
3228} as const ;
3329
3430const text = copy [lang ];
35- const featuredPosts = posts .slice (0 , 12 );
3631---
3732
3833<section id =" news" class =" timeline-section" >
3934 <div class =" timeline-heading" >
4035 <span >{ text .eyebrow } </span >
4136 <h2 >{ text .title } </h2 >
4237 <!-- <p>{text.description}</p> -->
43- <Button href ={ lang === " zh" ? " /zh/blog " : " /en/blog " } >{ text .cta } </Button >
38+ <Button href ={ lang === " zh" ? " /zh/news " : " /en/news " } >{ text .cta } </Button >
4439 </div >
4540
4641 {
47- featuredPosts .length ? (
42+ news .length ? (
4843 <ol class = " timeline-list" >
49- { featuredPosts .map ((post , index ) => {
50- const href =
51- lang === " zh" ? ` /zh/blog/${post .slug } ` : ` /en/blog/${post .slug } ` ;
52-
53- return (
54- <li
55- class :list = { [
56- " timeline-item" ,
57- index % 2 === 1 && " timeline-item-reverse" ,
58- ]}
59- >
60- <div class = " timeline-entry" >
61- <h3 >
62- <a href = { href } >{ post .data .title } </a >
63- </h3 >
64- <time
65- class = " timeline-date"
66- datetime = { post .data .date .toISOString ()}
67- >
68- { formatBlogDate (post .data .date , lang )}
69- </time >
70- <a class = " timeline-link" href = { href } >
71- { text .link }
72- </a >
73- </div >
74- <div class = " timeline-axis" aria-hidden = " true" >
75- <span class = " timeline-node" />
76- </div >
77- </li >
78- );
79- })}
44+ { news .map ((item , index ) => (
45+ <li
46+ class :list = { [
47+ " timeline-item" ,
48+ index % 2 === 1 && " timeline-item-reverse" ,
49+ ]}
50+ >
51+ <div class = " timeline-entry" >
52+ <time class = " timeline-date" datetime = { item .date .toISOString ()} >
53+ { item .rawDate }
54+ </time >
55+ <p class = " timeline-body" set :html = { renderNewsBody (item .body )} />
56+ </div >
57+ <div class = " timeline-axis" aria-hidden = " true" >
58+ <span class = " timeline-node" />
59+ </div >
60+ </li >
61+ ))}
8062 </ol >
8163 ) : (
8264 <p class = " timeline-empty" >{ text .empty } </p >
@@ -180,13 +162,6 @@ const featuredPosts = posts.slice(0, 12);
180162 align-content: end;
181163 min-height: 104px;
182164 padding: 0.35rem 0.2rem;
183- transition:
184- transform 0.22s ease,
185- opacity 0.22s ease;
186- }
187-
188- .timeline-entry:hover {
189- transform: translateY(-2px);
190165 }
191166
192167 .timeline-item-reverse .timeline-entry {
@@ -198,40 +173,32 @@ const featuredPosts = posts.slice(0, 12);
198173 grid-row: 1;
199174 }
200175
201- .timeline-entry h3 {
202- margin: 0;
203- font-size: 0.98rem;
204- line-height: 1.35;
205- }
206-
207- .timeline-entry h3 a {
208- color: var(--text-color);
209- text-decoration: none;
210- }
211-
212- .timeline-entry h3 a:hover {
176+ .timeline-date {
213177 color: var(--accent-color);
214- }
215-
216- .timeline-date,
217- .timeline-link {
218- color: var(--sub-text-color);
219178 font-size: 0.8rem;
179+ font-weight: 700;
220180 letter-spacing: 0.03em;
181+ display: block;
221182 }
222183
223- .timeline-date {
224- display: block;
184+ .timeline-body {
185+ margin: 0;
186+ font-size: 0.92rem;
187+ line-height: 1.4;
188+ color: var(--text-color);
189+ display: -webkit-box;
190+ -webkit-box-orient: vertical;
191+ -webkit-line-clamp: 3;
192+ overflow: hidden;
225193 }
226194
227- .timeline-link {
228- width: fit-content;
229- text-decoration: none;
195+ .timeline-body :global(a) {
230196 color: var(--accent-color);
197+ text-decoration: none;
231198 font-weight: 600;
232199 }
233200
234- .timeline-link: hover {
201+ .timeline-body :global(a: hover) {
235202 text-decoration: underline;
236203 }
237204
@@ -279,18 +246,14 @@ const featuredPosts = posts.slice(0, 12);
279246 0 8px 18px color-mix(in srgb, var(--accent-color) 24%, transparent);
280247 }
281248
282- .timeline-item:nth-child(n + 7 ) {
249+ .timeline-item:nth-child(n + 6 ) {
283250 display: none;
284251 }
285252
286253 @media (max-width: 1180px) {
287254 .timeline-list {
288255 grid-template-columns: repeat(5, minmax(0, 1fr));
289256 }
290-
291- .timeline-item:nth-child(n + 6) {
292- display: none;
293- }
294257 }
295258
296259 @media (max-width: 980px) {
@@ -341,8 +304,7 @@ const featuredPosts = posts.slice(0, 12);
341304 }
342305
343306 .timeline-item:nth-child(n + 5),
344- .timeline-item:nth-child(n + 6),
345- .timeline-item:nth-child(n + 7) {
307+ .timeline-item:nth-child(n + 6) {
346308 display: grid;
347309 }
348310
@@ -383,5 +345,9 @@ const featuredPosts = posts.slice(0, 12);
383345 .timeline-item:last-child .timeline-entry {
384346 padding-bottom: 0;
385347 }
348+
349+ .timeline-body {
350+ -webkit-line-clamp: unset;
351+ }
386352 }
387353</style >
0 commit comments