-
Notifications
You must be signed in to change notification settings - Fork 35
/
ToC.svelte
83 lines (68 loc) · 1.94 KB
/
ToC.svelte
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
<script>
import { browser } from '$app/environment'
import { onMount } from 'svelte'
import Card from './Card.svelte'
export let post
let elements = []
let headings = post.headings
onMount(() => {
updateHeadings()
setActiveHeading()
})
let activeHeading = headings[0]
let scrollY
function updateHeadings() {
headings = post.headings
if (browser) {
elements = headings.map((heading) => {
return document.getElementById(heading.id)
})
}
}
function setActiveHeading() {
scrollY = window.scrollY
const visibleIndex =
elements.findIndex((element) => element.offsetTop + element.clientHeight > scrollY) - 1
activeHeading = headings[visibleIndex]
const pageHeight = document.body.scrollHeight
const scrollProgress = (scrollY + window.innerHeight) / pageHeight
if (!activeHeading) {
if (scrollProgress > 0.5) {
activeHeading = headings[headings.length - 1]
} else {
activeHeading = headings[0]
}
}
}
</script>
<svelte:window on:scroll={setActiveHeading} />
<Card>
<slot slot="description">
<ul class="flex flex-col gap-2">
{#each headings as heading}
<li
class="pl-2 transition-colors border-teal-500 heading text-zinc-500 dark:text-zinc-600 hover:text-zinc-900 dark:hover:text-zinc-100"
class:active={activeHeading === heading}
style={`--depth: ${
// consider h1 and h2 at the same depth, as h1 will only be used for page title
Math.max(0, heading.depth - 1)
}`}
>
<a href={`#${heading.id}`}>{heading.value}</a>
</li>
{/each}
</ul>
</slot>
</Card>
<style lang="postcss">
.heading {
padding-left: calc(var(--depth, 0) * 0.35rem);
}
.active {
@apply font-medium text-slate-900 border-l-2 -ml-[2px];
}
/* can't use dark: modifier in @apply */
:global(.dark) .active {
@apply text-slate-100;
}
</style>