moved and rewrote table-of-contents.svelte to use stateful design

This commit is contained in:
2026-02-15 13:36:29 +01:00
parent 151e1206ab
commit 6042157caf
6 changed files with 37 additions and 47 deletions

View File

@@ -1,32 +1,27 @@
<script lang="ts">
import {onMount} from 'svelte';
let {
stickyScrolling,
}: {
stickyScrolling?: boolean;
} = $props();
interface TocEntry {
text: string;
link: string;
let idCounter: number = 0;
/**
* possible values: 0, 1, 2, 3
*/
indentLevel: number;
}
let root: HTMLElement;
let container: HTMLElement;
let tocEntries: TocEntry[] = $state([]);
onMount(() => {
let headers = getHeaders();
headers.forEach(header => {
let headerId = getHeaderId(header);
var element = document.createElement("a");
element.classList += "toc-level-" + getHeaderLevel(header);
element.href = `#${headerId}`;
element.innerHTML = `${(header as HTMLElement).innerHTML}`;
container.appendChild(element);
tocEntries.push({
text: `${(header as HTMLElement).innerHTML}`,
link: `#${getHeaderId(header)}`,
indentLevel: getHeaderLevel(header),
});
});
// Hide table of contents if no valid entries have been found
if (headers.length == 0) {
root.style.display = "none";
}
});
let getHeaders = function(): NodeList {
@@ -43,41 +38,40 @@
text = text.replaceAll(/[^a-zA-Z0-9]/gi, "-");
id = text;
(header as HTMLElement).id = id;
idCounter += 1;
}
return id;
}
let getHeaderLevel = function(header: Node): string {
function getHeaderLevel(header: Node): number {
switch ((header as HTMLElement).tagName) {
case "H2":
return "0";
return 0;
case "H3":
return "1";
return 1;
case "H4":
return "2";
return 2;
case "H5":
return "3";
return 3;
default:
return "0";
return 0;
}
}
</script>
{#if stickyScrolling}
<div class="toc-container sticky-toc" bind:this={root}>
{@render tocList()}
</div>
{:else}
<div class="toc-container" bind:this={root}>
{@render tocList()}
{#snippet tocEntryLine({ entry }: { entry: TocEntry })}
<a class="toc-level-{entry.indentLevel}" href="{entry.link}">{@html entry.text}</a>
{/snippet}
{#if tocEntries.length > 0}
<div class="toc-container">
<ul class="toc-list">
{#each tocEntries as entry}
{@render tocEntryLine({ entry })}
{/each}
</ul>
</div>
{/if}
{#snippet tocList()}
<ul class="toc-list" bind:this={container}></ul>
{/snippet}
<style>
:global {
body {
@@ -95,14 +89,10 @@
backdrop-filter: blur(var(--blur-radius-background));
}
.sticky-toc {
position: sticky;
top: 20px;
}
.toc-list {
padding: 0;
margin: 0;
width: 100%;
}
.toc-list a {

View File

@@ -1,7 +1,7 @@
<script>
import Banner2 from "$lib/banner2.svelte";
import Content from "$lib/viewport/content.svelte";
import TableOfContents from "$lib/table-of-contents.svelte";
import TableOfContents from "$lib/components/table-of-contents.svelte";
export let data;
</script>

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import Banner2 from "$lib/banner2.svelte";
import Content from "$lib/viewport/content.svelte";
import TableOfContents from "$lib/table-of-contents.svelte";
import TableOfContents from "$lib/components/table-of-contents.svelte";
import LinkList, { type LinkEntry } from "$lib/lists/link-list.svelte";
let favouriteAlbums: LinkEntry[] = [

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import Banner2 from "$lib/banner2.svelte";
import TableOfContents from "$lib/table-of-contents.svelte";
import TableOfContents from "$lib/components/table-of-contents.svelte";
import { type Project, games, hardware, apps, music } from './projects';
import LinkList from "$lib/lists/link-list.svelte";
import Content from "$lib/viewport/content.svelte";

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import Banner2 from "$lib/banner2.svelte";
import Content from "$lib/viewport/content.svelte";
import TableOfContents from "$lib/table-of-contents.svelte";
import TableOfContents from "$lib/components/table-of-contents.svelte";
</script>
<svelte:head>

View File

@@ -1,7 +1,7 @@
<script>
import Banner2 from "$lib/banner2.svelte";
import Content from "$lib/viewport/content.svelte";
import TableOfContents from "$lib/table-of-contents.svelte";
import TableOfContents from "$lib/components/table-of-contents.svelte";
export let data;
</script>