feat(tags): tag pages get the archive filter bar and topic chips
/tags/<tag>/ passed chrome={false}, so a tag page showed a bare list — no filter
input and no chips — while /posts/ had both. Enable the chrome and add a
chipPosts prop: the tag strip is built from the whole language collection because
its chips navigate (page-scoped counts would lie), while category chips and the
row filter stay scoped to the posts on the page so their counts match the rows.
The current tag is marked is-active + aria-current instead of looking clickable.
This commit is contained in:
1 parent
84b412470b
commit
557f312bc7
3 files changed
+37
-9
No files matched your search
@@ -7,11 +7,21 @@ interface Props {
|
||||
lang?: 'en' | 'zh';
|
||||
/** Filter bar + chips. Off on tag/collection pages, where the set is already narrow. */
|
||||
chrome?: boolean;
|
||||
/**
|
||||
* Posts the tag-chip strip is built from. Defaults to `posts`, which is right on
|
||||
* the archive; a tag page passes the whole language collection so its strip is the
|
||||
* same site-wide topic menu as /posts/ (the chips navigate, so their counts must be
|
||||
* site-wide or they would lie). Category chips and the row filter stay scoped to
|
||||
* `posts`, so their counts always match the rows actually on the page.
|
||||
*/
|
||||
chipPosts?: CollectionEntry<'posts'>[];
|
||||
/** On a tag page, the tag being viewed — its chip is marked active instead of looking clickable-to-self. */
|
||||
activeTag?: string;
|
||||
}
|
||||
const { posts, lang = 'en', chrome = true } = Astro.props;
|
||||
const { posts, lang = 'en', chrome = true, chipPosts = posts, activeTag } = Astro.props;
|
||||
const isZh = lang === 'zh';
|
||||
|
||||
const counts = tagCounts(posts);
|
||||
const counts = tagCounts(chipPosts);
|
||||
const years = [...new Set(posts.map((p) => p.data.pubDate.getUTCFullYear()))].sort((a, b) => b - a);
|
||||
const categories = [...new Set(posts.map((p) => p.data.category))]
|
||||
.map((c) => ({ name: c, n: posts.filter((p) => p.data.category === c).length }))
|
||||
@@ -56,7 +66,11 @@ const labels = isZh
|
||||
|
||||
<div class="archive-chips archive-tags" role="group" aria-label={isZh ? '按标签筛选' : 'Filter by tag'}>
|
||||
{chips.map((c) => (
|
||||
<a class="chip tag-chip" href={tagHref(c.tag, lang)}>{c.tag} <span>{c.n}</span></a>
|
||||
<a
|
||||
class:list={['chip', 'tag-chip', { 'is-active': c.tag === activeTag }]}
|
||||
href={tagHref(c.tag, lang)}
|
||||
aria-current={c.tag === activeTag ? 'page' : undefined}
|
||||
>{c.tag} <span>{c.n}</span></a>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -16,11 +16,16 @@ export async function getStaticPaths() {
|
||||
const counts = tagCounts(posts);
|
||||
return tagPageNames(posts).map((tag) => ({
|
||||
params: { tag },
|
||||
props: { tag, n: counts.get(tag) ?? 0, posts: posts.filter((p) => p.data.tags.includes(tag)) },
|
||||
props: {
|
||||
tag,
|
||||
n: counts.get(tag) ?? 0,
|
||||
posts: posts.filter((p) => p.data.tags.includes(tag)),
|
||||
all: posts,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
const { tag, posts } = Astro.props as { tag: string; posts: any[] };
|
||||
const { tag, posts, all } = Astro.props as { tag: string; posts: any[]; all: any[] };
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
@@ -36,5 +41,8 @@ const { tag, posts } = Astro.props as { tag: string; posts: any[] };
|
||||
<span class="sep">·</span>
|
||||
<a href="/categories/">Categories</a>
|
||||
</p>
|
||||
<Archive posts={posts} lang="en" chrome={false} />
|
||||
{/* Same filter bar and topic chips as /posts/ — a tag page that hides them makes
|
||||
the reader go back up to the archive to filter anything. `chipPosts` is the whole
|
||||
language collection so the tag strip keeps site-wide counts. */}
|
||||
<Archive posts={posts} chipPosts={all} activeTag={tag} lang="en" />
|
||||
</BaseLayout>
|
||||
@@ -14,11 +14,16 @@ export async function getStaticPaths() {
|
||||
const counts = tagCounts(posts);
|
||||
return tagPageNames(posts).map((tag) => ({
|
||||
params: { tag },
|
||||
props: { tag, n: counts.get(tag) ?? 0, posts: posts.filter((p) => p.data.tags.includes(tag)) },
|
||||
props: {
|
||||
tag,
|
||||
n: counts.get(tag) ?? 0,
|
||||
posts: posts.filter((p) => p.data.tags.includes(tag)),
|
||||
all: posts,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
const { tag, posts } = Astro.props as { tag: string; posts: any[] };
|
||||
const { tag, posts, all } = Astro.props as { tag: string; posts: any[]; all: any[] };
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
@@ -35,5 +40,6 @@ const { tag, posts } = Astro.props as { tag: string; posts: any[] };
|
||||
<span class="sep">·</span>
|
||||
<a href="/zh/categories/">分类</a>
|
||||
</p>
|
||||
<Archive posts={posts} lang="zh" chrome={false} />
|
||||
{/* 与 /zh/posts/ 相同的筛选栏与主题标签条;chipPosts 传全站集合,标签条的计数才是全站数。 */}
|
||||
<Archive posts={posts} chipPosts={all} activeTag={tag} lang="zh" />
|
||||
</BaseLayout>
|
||||
Reference in new issue
Block a user