feat: working table of contents
This commit is contained in:
parent
29b1fa4afb
commit
856a8f2795
5 changed files with 98 additions and 34 deletions
92
src/components/TableOfContents.astro
Normal file
92
src/components/TableOfContents.astro
Normal file
|
@ -0,0 +1,92 @@
|
|||
---
|
||||
import type { MarkdownHeading } from 'astro';
|
||||
|
||||
interface Props {
|
||||
headings: MarkdownHeading[];
|
||||
}
|
||||
|
||||
const { headings } = Astro.props;
|
||||
|
||||
|
||||
const filteredHeadings = headings.filter((heading) => heading.depth <= 3);
|
||||
|
||||
// github.com/rezahedi/rezahedi.dev/blob/main/src/components/TOC.astro
|
||||
function buildHierarchy(headings: MarkdownHeading[]) {
|
||||
const toc: any[] = [];
|
||||
const parentHeadings = new Map();
|
||||
|
||||
if (!headings)
|
||||
return toc;
|
||||
|
||||
headings.forEach((h: any) => {
|
||||
const heading = { ...h, subheadings: [] };
|
||||
parentHeadings.set(heading.depth, heading);
|
||||
// Change 2 to 1 if your markdown includes your <h1>
|
||||
if (heading.depth === 2) {
|
||||
toc.push(heading);
|
||||
} else {
|
||||
parentHeadings.get(heading.depth - 1)?.subheadings.push(heading);
|
||||
}
|
||||
});
|
||||
return toc;
|
||||
}
|
||||
|
||||
const toc = buildHierarchy(filteredHeadings);
|
||||
---
|
||||
<details>
|
||||
<summary>Table of Contents</summary>
|
||||
<ul class="toc-list">
|
||||
{
|
||||
toc.map((heading) => (
|
||||
<li class={`depth-${heading.depth}`} style={`margin-left: ${(heading.depth - 1)}rem;`}>
|
||||
<a href={`#${heading.slug}`}>{heading.text}</a>
|
||||
{
|
||||
heading.subheadings.length > 0 && (
|
||||
<ul>
|
||||
{
|
||||
heading.subheadings.map((subheading) => (
|
||||
<li class={`depth-${subheading.depth}`} style={`margin-left: ${(subheading.depth - 1)}rem;`}>
|
||||
<a href={`#${subheading.slug}`}>{subheading.text}</a>
|
||||
{subheading.subheadings.length > 0 && (
|
||||
<ul>
|
||||
{
|
||||
subheading.subheadings.map((subSubheading) => (
|
||||
<li class={`depth-${subSubheading.depth}`} style={`margin-left: ${(subSubheading.depth - 1)}rem;`}>
|
||||
<a href={`#${subSubheading.slug}`}>{subSubheading.text}</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
)}
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
<style>
|
||||
details {
|
||||
margin: 0.5rem auto;
|
||||
}
|
||||
.toc-list {
|
||||
list-style-type: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
.depth-1 {
|
||||
font-weight: bold;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
.depth-2 {
|
||||
font-weight: 500;
|
||||
}
|
||||
.depth-3 {
|
||||
font-size: 0.95em;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
import Layout from '../../layouts/Layout.astro';
|
||||
|
||||
import { getCollection, getEntry } from 'astro:content';
|
||||
import Comments from "../../components/Comments.astro";
|
||||
import {getImage} from "astro:assets";
|
||||
|
@ -7,6 +8,7 @@ import {siteConfig} from "../../config";
|
|||
import ReplyViaEmail from "../../components/ReplyViaEmail.astro";
|
||||
import { ExtractFirstImage } from '../../plugins/extract-images';
|
||||
import AuthorInfo from "../../components/helper/authors/Info.astro";
|
||||
import TableOfContents from "../../components/TableOfContents.astro";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const blogEntries = await getCollection('posts');
|
||||
|
@ -17,6 +19,8 @@ export async function getStaticPaths() {
|
|||
|
||||
const { entry } = Astro.props;
|
||||
const { Content } = await entry.render();
|
||||
const headings = await entry.render().then(rendered => rendered.headings);
|
||||
|
||||
const noscript = siteConfig.noClientJavaScript
|
||||
const slug = Astro.params.slug;
|
||||
const author = entry.data.author || {collection: 'authors', id: siteConfig.defaultAuthor.id};
|
||||
|
@ -48,6 +52,7 @@ const cover = customFeaturedImage || matchedImage_src?.src || firstImageURL || `
|
|||
<h1 class="title">{entry.data.title}</h1>
|
||||
<AuthorInfo data={authorData} />
|
||||
<span class="date">{new Date(entry.data.pubDate).toISOString().split('T')[0]}</span>
|
||||
{headings.length !== 0 && <TableOfContents headings={headings} />}
|
||||
<div class="content">
|
||||
<Content />
|
||||
</div>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue