refactor(index): merge changes from erudite
All checks were successful
build dist / build-dist (push) Successful in 29s
All checks were successful
build dist / build-dist (push) Successful in 29s
This commit is contained in:
parent
b7f170b73f
commit
e783f822b6
5 changed files with 66 additions and 72 deletions
|
@ -1,86 +1,71 @@
|
|||
---
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { cn } from '@/lib/utils'
|
||||
import type { MarkdownHeading } from 'astro'
|
||||
import { Icon } from 'astro-icon/components'
|
||||
import TableOfContentsHeading from './TableOfContentsHeading.astro'
|
||||
|
||||
export interface Heading {
|
||||
depth: number
|
||||
slug: string
|
||||
text: string
|
||||
subheadings: Heading[]
|
||||
type Props = {
|
||||
headings: MarkdownHeading[]
|
||||
}
|
||||
|
||||
const { headings } = Astro.props
|
||||
const toc = buildToc(headings)
|
||||
|
||||
function buildToc(headings: Heading[]): Heading[] {
|
||||
const toc: Heading[] = []
|
||||
const stack: Heading[] = []
|
||||
|
||||
// biome-ignore lint/complexity/noForEach: <explanation>
|
||||
headings.forEach((h) => {
|
||||
const heading = { ...h, subheadings: [] }
|
||||
|
||||
while (stack.length > 0 && stack[stack.length - 1].depth >= heading.depth) {
|
||||
stack.pop()
|
||||
}
|
||||
|
||||
if (stack.length === 0) {
|
||||
toc.push(heading)
|
||||
} else {
|
||||
stack[stack.length - 1].subheadings.push(heading)
|
||||
}
|
||||
|
||||
stack.push(heading)
|
||||
})
|
||||
|
||||
return toc
|
||||
function getHeadingMargin(depth: number): string {
|
||||
const margins: Record<number, string> = {
|
||||
3: 'ml-4',
|
||||
4: 'ml-8',
|
||||
5: 'ml-12',
|
||||
6: 'ml-16',
|
||||
}
|
||||
return margins[depth] || ''
|
||||
}
|
||||
---
|
||||
|
||||
<details
|
||||
open
|
||||
class="group col-start-2 mx-4 block rounded-xl border p-4 xl:hidden"
|
||||
class="group col-start-2 rounded-xl border p-4 xl:sticky xl:top-20 xl:col-start-1 xl:mr-8 xl:ml-auto xl:h-[calc(100vh-5rem)] xl:max-w-fit xl:rounded-none xl:border-none xl:p-0"
|
||||
>
|
||||
<summary
|
||||
class="flex cursor-pointer items-center justify-between text-xl font-medium group-open:pb-4"
|
||||
class="flex cursor-pointer items-center justify-between text-xl font-medium group-open:pb-4 xl:hidden"
|
||||
>
|
||||
Table of Contents
|
||||
<span>Table of Contents</span>
|
||||
<Icon
|
||||
name="lucide:chevron-down"
|
||||
class="size-5 transition-transform group-open:rotate-180"
|
||||
class="size-5 shrink-0 transition-transform group-open:rotate-180"
|
||||
/>
|
||||
</summary>
|
||||
|
||||
<ScrollArea
|
||||
client:load
|
||||
className="flex max-h-64 flex-col overflow-y-auto"
|
||||
className="flex max-h-64 flex-col overflow-y-auto xl:max-h-[calc(100vh-8rem)]"
|
||||
type="always"
|
||||
>
|
||||
<nav>
|
||||
<ul></ul>
|
||||
{toc.map((heading) => <TableOfContentsHeading heading={heading} />)}
|
||||
</nav>
|
||||
<ul
|
||||
class="flex list-none flex-col gap-y-2 px-4 xl:mr-8"
|
||||
id="table-of-contents"
|
||||
>
|
||||
<li class="hidden text-lg font-medium xl:block">Table of Contents</li>
|
||||
{
|
||||
headings.map((heading) => (
|
||||
<li
|
||||
class={cn(
|
||||
'text-foreground/60 px-4 text-sm xl:p-0',
|
||||
getHeadingMargin(heading.depth),
|
||||
)}
|
||||
>
|
||||
<a
|
||||
href={`#${heading.slug}`}
|
||||
class="marker:text-foreground/30 list-item list-disc underline decoration-transparent underline-offset-[3px] transition-colors duration-200 hover:decoration-inherit xl:list-none"
|
||||
>
|
||||
{heading.text}
|
||||
</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</ScrollArea>
|
||||
</details>
|
||||
|
||||
<nav
|
||||
class="sticky top-20 col-start-1 hidden h-[calc(100vh-5rem)] text-xs leading-4 xl:block"
|
||||
>
|
||||
<div class="flex justify-end">
|
||||
<ScrollArea client:load className="max-h-[calc(100vh-8rem)]" type="always">
|
||||
<ul
|
||||
class="flex flex-col justify-end gap-y-2 overflow-y-auto px-8"
|
||||
id="toc-container"
|
||||
>
|
||||
<li>
|
||||
<h2 class="mb-2 text-lg font-medium">Table of Contents</h2>
|
||||
</li>
|
||||
{toc.map((heading) => <TableOfContentsHeading heading={heading} />)}
|
||||
</ul>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<script>
|
||||
function setupToc() {
|
||||
const header = document.querySelector('header')
|
||||
|
@ -94,7 +79,7 @@ function buildToc(headings: Heading[]): Heading[] {
|
|||
|
||||
const id = heading.getAttribute('id')
|
||||
const link = document.querySelector(
|
||||
`#toc-container li a[href="#${id}"]`,
|
||||
`#table-of-contents li a[href="#${id}"]`,
|
||||
)
|
||||
if (!link) return
|
||||
|
||||
|
@ -115,4 +100,4 @@ function buildToc(headings: Heading[]): Heading[] {
|
|||
|
||||
document.addEventListener('astro:page-load', setupToc)
|
||||
document.addEventListener('astro:after-swap', setupToc)
|
||||
</script>
|
||||
</script>
|
Loading…
Add table
Add a link
Reference in a new issue