65 lines
1.3 KiB
Svelte
65 lines
1.3 KiB
Svelte
<script lang="ts">
|
|
import { type Snippet } from 'svelte';
|
|
import Sec from './Sec.svelte';
|
|
import { sections, type Section } from './article.svelte';
|
|
|
|
interface Props {
|
|
head: Snippet;
|
|
children: Snippet;
|
|
}
|
|
|
|
let { head, children }: Props = $props();
|
|
|
|
let tocViewed = $state(false);
|
|
let tocElem: HTMLElement | undefined = $state();
|
|
const observeTOC: IntersectionObserverCallback = (entries, obs) => {
|
|
for (const e of entries) {
|
|
if (!e.isIntersecting) {
|
|
continue;
|
|
}
|
|
tocViewed = true;
|
|
obs.disconnect();
|
|
}
|
|
};
|
|
|
|
$effect(() => {
|
|
if (tocElem == null) {
|
|
return;
|
|
}
|
|
const obs = new IntersectionObserver(observeTOC, { rootMargin: '20% 0px' });
|
|
obs.observe(tocElem);
|
|
});
|
|
|
|
function tocClass(s: Section) {
|
|
switch (s.h) {
|
|
case 1:
|
|
return null;
|
|
case 2:
|
|
return null;
|
|
case 3:
|
|
return 'ml-4';
|
|
case 4:
|
|
return 'ml-8';
|
|
case 5:
|
|
return 'ml-12';
|
|
case 6:
|
|
return 'ml-16';
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<article class="mx-auto max-w-4xl text-justify hyphens-auto">
|
|
{@render head()}
|
|
{#if sections.length > 0}
|
|
<Sec h={2} id="toc" toc={false}>Table of Contents</Sec>
|
|
<ul class="mb-4 list-disc pl-4" bind:this={tocElem}>
|
|
{#if tocViewed}
|
|
{#each sections as s (s.id)}
|
|
<li class={tocClass(s)}><a href={`#${s.id}`}>{@render s.children()}</a></li>
|
|
{/each}
|
|
{/if}
|
|
</ul>
|
|
{/if}
|
|
{@render children()}
|
|
</article>
|