Files
blog/src/components/ArticleSidebar.astro
T
2026-06-19 08:55:14 +08:00

116 lines
3.6 KiB
Plaintext

---
import { getCategoryHref, type BlogPost } from '@/lib/posts';
import SiteInfoCard from '@/components/SiteInfoCard.astro';
import { siteConfig } from '../../site.config.mjs';
type TocItem = {
depth: number;
text: string;
id: string;
};
type TocSection = {
heading: TocItem;
children: TocItem[];
};
interface Props {
post: BlogPost;
toc: TocItem[];
relatedPosts: BlogPost[];
posts: BlogPost[];
}
const { post, toc, relatedPosts, posts } = Astro.props;
const avatar = siteConfig.author.avatar;
const topDepth = toc.length > 0 ? Math.min(...toc.map((item) => item.depth)) : 1;
const tocSections = toc.reduce<TocSection[]>((sections, item) => {
if (item.depth <= topDepth || sections.length === 0) {
sections.push({ heading: item, children: [] });
} else {
sections[sections.length - 1].children.push(item);
}
return sections;
}, []);
---
<aside class="article-sidebar" aria-label="文章侧边栏">
<section class="sidebar-card article-author-card">
<img class="profile-avatar" src={avatar} alt={siteConfig.author.name} loading="lazy" />
<div class="profile-name">{siteConfig.author.name}</div>
<div class="article-author-meta">
<span>{post.dateText}</span>
{post.categories[0] && <a href={getCategoryHref(post.categories[0])}>{post.categories[0]}</a>}
</div>
</section>
{post.tags.length > 0 && (
<section class="sidebar-card article-tags-card">
<h2>
<span aria-hidden="true">#</span>
文章标签
<em>{post.tags.length}</em>
</h2>
<div class="article-tag-list" aria-label="文章标签">
{post.tags.map((tag) => (
<a href={`/tags/${encodeURIComponent(tag)}/`}>#{tag}</a>
))}
</div>
</section>
)}
<div class="article-sticky-sidebar">
{toc.length > 0 && (
<section class="sidebar-card toc-card">
<h2>
<span aria-hidden="true">☰</span>
文章目录
<em>{toc.length}</em>
</h2>
<nav class="toc-list toc-tree" aria-label="文章目录" data-toc-tree>
{tocSections.map((section, index) => (
<div class:list={['toc-group', index === 0 && 'is-open']} data-toc-group data-heading-id={section.heading.id}>
<a class="toc-link toc-link-main" href={`#${section.heading.id}`} data-toc-link data-toc-top>
<span class="toc-marker" aria-hidden="true"></span>
<span>{section.heading.text}</span>
</a>
{section.children.length > 0 && (
<div class="toc-children" data-toc-children>
{section.children.map((item) => (
<a class={`toc-link toc-link-child toc-depth-${item.depth}`} href={`#${item.id}`} data-toc-link>
<span>{item.text}</span>
</a>
))}
</div>
)}
</div>
))}
</nav>
</section>
)}
<section class="sidebar-card same-category-card">
<h2>
<span aria-hidden="true">▣</span>
同类文章
<em>{relatedPosts.length}</em>
</h2>
{relatedPosts.length > 0 ? (
<div class="same-category-list">
{relatedPosts.map((item) => (
<a href={item.href}>
<strong>{item.title}</strong>
<time datetime={item.date.toISOString()}>{item.dateText}</time>
</a>
))}
</div>
) : (
<p class="sidebar-empty">这个分类下暂时没有其他文章。</p>
)}
</section>
<SiteInfoCard posts={posts} />
</div>
</aside>