feat: astro built-ins — news pagination, prefetch, RSS, OG/SEO meta, TOC scrollspy (Starlight-pattern)
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
---
|
||||
import { getCollection } from 'astro:content';
|
||||
import Base from '../../layouts/Base.astro';
|
||||
|
||||
// Paginated archive via Astro's built-in paginate(): the [...page] rest route
|
||||
// makes page 1 the bare /news URL, then /news/2 … /news/N.
|
||||
export async function getStaticPaths({ paginate }) {
|
||||
const posts = (await getCollection('news', ({ data }) => !data.draft)).sort(
|
||||
(a, b) => b.data.date.valueOf() - a.data.date.valueOf(),
|
||||
);
|
||||
return paginate(posts, { pageSize: 24 });
|
||||
}
|
||||
|
||||
const { page } = Astro.props;
|
||||
const first = page.currentPage === 1;
|
||||
const fmt = (d) => new Intl.DateTimeFormat('en-GB', { dateStyle: 'long' }).format(d);
|
||||
const iso = (d) => d.toISOString().slice(0, 10);
|
||||
// Windowed page list — 1 … n-1 n n+1 … last (0 marks an ellipsis)
|
||||
const nums = [];
|
||||
for (let n = 1; n <= page.lastPage; n++) {
|
||||
if (n === 1 || n === page.lastPage || Math.abs(n - page.currentPage) <= 1) nums.push(n);
|
||||
else if (nums.at(-1) !== 0) nums.push(0);
|
||||
}
|
||||
const hrefFor = (n) => (n === 1 ? '/news' : `/news/${n}`);
|
||||
---
|
||||
<Base
|
||||
title={first ? 'CTAO Science Portal — News' : `CTAO Science Portal — News, page ${page.currentPage}`}
|
||||
description={first ? 'News and announcements of the CTAO' : `News and announcements of the CTAO — page ${page.currentPage} of ${page.lastPage}`}
|
||||
>
|
||||
<section class="hero-band">
|
||||
<div class="band-bg" aria-hidden="true"></div>
|
||||
<div class="container hero">
|
||||
<div class="eyebrow">Cherenkov Telescope Array Observatory</div>
|
||||
<h1>News & Announcements</h1>
|
||||
<p>Exploring the Universe at the Highest Energies</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container page">
|
||||
<h2 class="sr-only">{first ? 'All news' : `All news — page ${page.currentPage}`}</h2>
|
||||
<div class="grid">
|
||||
{page.data.map((post, i) => (
|
||||
<article class={first && i === 0 ? 'card card--featured' : 'card'}>
|
||||
{post.data.cover && (
|
||||
<img class="cover" src={encodeURI(post.data.cover)} alt="" loading={first && i === 0 ? 'eager' : 'lazy'} />
|
||||
)}
|
||||
<div class="body">
|
||||
<span class="cat">{post.data.category}</span>
|
||||
<h3><a href={`/news/${post.id}`}>{post.data.title}</a></h3>
|
||||
<p class="desc">{post.data.description}</p>
|
||||
<div class="meta">{post.data.author} · <time datetime={iso(post.data.date)}>{fmt(post.data.date)}</time></div>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
{page.lastPage > 1 && (
|
||||
<nav class="pagination" aria-label="News pages">
|
||||
{page.url.prev
|
||||
? <a class="page-link page-step" href={page.url.prev} rel="prev">← Newer</a>
|
||||
: <span class="page-link page-step" aria-hidden="true">← Newer</span>}
|
||||
<ol>
|
||||
{nums.map((n) => (
|
||||
<li>
|
||||
{n === 0
|
||||
? <span class="page-gap" aria-hidden="true">…</span>
|
||||
: <a class="page-link" href={hrefFor(n)} aria-current={n === page.currentPage ? 'page' : undefined}><span class="sr-only">Page </span>{n}</a>}
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
{page.url.next
|
||||
? <a class="page-link page-step" href={page.url.next} rel="next">Older →</a>
|
||||
: <span class="page-link page-step" aria-hidden="true">Older →</span>}
|
||||
</nav>
|
||||
)}
|
||||
</section>
|
||||
</Base>
|
||||
Reference in New Issue
Block a user