116 lines
3.6 KiB
Plaintext
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>
|