feat: overhaul sticky toc styling

This commit is contained in:
Youwen Wu 2024-04-06 18:31:45 -07:00
parent cadc1f637f
commit 24e0a6f1ff
Signed by: youwen5
GPG key ID: 865658ED1FE61EC3
4 changed files with 59 additions and 105 deletions

View file

@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import { ChevronRight } from 'svelte-radix'; import { ChevronRight } from 'svelte-radix';
import PostMetadata from './PostMetadata.svelte'; import PostMetadata from './PostMetadata.svelte';
import Separator from '../ui/separator/separator.svelte';
export let doc: BlogDocument; export let doc: BlogDocument;
</script> </script>
@ -31,5 +32,6 @@
<div class="markdown-body mt-8 font-serif"> <div class="markdown-body mt-8 font-serif">
{@html doc.content} {@html doc.content}
<div role="note" id="end-marker" aria-label="End of article" class="invisible h-0 w-0">End</div>
</div> </div>
</article> </article>

View file

@ -1,72 +0,0 @@
<script lang="ts">
import { toclink, type TocStore } from '@svelte-put/toc';
import * as Card from '$lib/components/ui/card';
import { onMount } from 'svelte';
export let tocStore: TocStore;
export let distanceFromRight: number;
let el: HTMLElement;
let hide = false;
const readjust = () => {
el && (el.style.right = `calc(${distanceFromRight - el.clientWidth}px - 20px)`);
};
onMount(() => {
readjust();
});
$: readjust();
$: if (distanceFromRight < 220 && !hide) {
hide = true;
}
$: if (distanceFromRight >= 220 && hide) {
hide = false;
}
const calcIndent = (el: HTMLElement) => {
readjust();
if (el.tagName === 'H1' || el.tagName === 'H2') return '0px';
if (el.tagName === 'H3') return '1rem';
if (el.tagName === 'H4') return '2rem';
if (el.tagName === 'H5') return '3rem';
if (el.tagName === 'H6') return '4rem';
return '0px';
};
const calcTextClasses = (el: HTMLElement) => {
if (el.tagName === 'H1') return 'text-lg mb-2';
if (el.tagName === 'H2') return 'text-lg my-2';
return 'text-sm my-2 text-muted-foreground';
};
</script>
<aside class="fixed top-36 x-4 mx-4" bind:this={el}>
<Card.Root>
<Card.Header>
<Card.Title><h2 class="text-xl">On this page</h2></Card.Title>
</Card.Header>
<Card.Content>
{#if $tocStore.items.size}
<ol>
{#each $tocStore.items.values() as tocItem}
<li
style="margin-left: {calcIndent(tocItem.element)}"
class={calcTextClasses(tocItem.element)}
>
<!-- svelte-ignore a11y-missing-attribute a11y-missing-content -->
<a
use:toclink={{ store: tocStore, tocItem, observe: true }}
class="hover:underline"
/>
</li>
{/each}
</ol>
{/if}
</Card.Content>
</Card.Root>
</aside>

View file

@ -0,0 +1,34 @@
<script lang="ts">
import { toclink, type TocStore } from '@svelte-put/toc';
import * as Card from '$lib/components/ui/card';
export let tocStore: TocStore;
const calcTextClasses = (el: HTMLElement) => {
if (el.tagName === 'H1') return 'text-md xl:text-lg';
if (el.tagName === 'H2') return 'text-md xl:text-lg';
if (el.id === 'end-marker') return 'text-md xl:text-lg';
return 'text-sm text-muted-foreground';
};
</script>
<Card.Root>
<Card.Header>
<Card.Title><h2 class="text-lg xl:text-xl">On this page</h2></Card.Title>
</Card.Header>
<Card.Content>
{#if $tocStore.items.size}
<ol>
{#each $tocStore.items.values() as tocItem}
<li>
<!-- svelte-ignore a11y-missing-attribute a11y-missing-content -->
<a
use:toclink={{ store: tocStore, tocItem, observe: true }}
class={`hover:bg-muted px-2 py-1 rounded-r-sm transition-all border-l-secondary border-l-4 ${calcTextClasses(tocItem.element)}`}
/>
</li>
{/each}
</ol>
{/if}
</Card.Content>
</Card.Root>

View file

@ -6,24 +6,9 @@
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
import Article from '$lib/components/Blog/Article.svelte'; import Article from '$lib/components/Blog/Article.svelte';
import { toc, createTocStore } from '@svelte-put/toc'; import { toc, createTocStore } from '@svelte-put/toc';
import StickyToc from '$lib/components/StickyToc.svelte'; import StickyToc from '$lib/components/Toc/StickyToc.svelte';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
let distanceFromRight = 0;
let mainElement: HTMLElement;
const handleResize = () => {
const screenWidth = window.innerWidth;
const mainWidth = mainElement.offsetWidth;
distanceFromRight = screenWidth - mainWidth - mainElement.offsetLeft;
console.log(distanceFromRight);
};
onMount(() => {
handleResize();
});
const tocStore = createTocStore(); const tocStore = createTocStore();
export let data: PageData; export let data: PageData;
@ -48,21 +33,26 @@
<meta name="author" content="Youwen Wu" /> <meta name="author" content="Youwen Wu" />
</svelte:head> </svelte:head>
<svelte:window on:resize={handleResize} /> <div class="md:flex max-w-6xl mx-auto mt-14 px-4">
<main
class="flex-grow basis-3/4"
use:toc={{
store: tocStore,
observe: true,
anchor: {
properties: { 'aria-hidden': 'true', class: 'hidden' },
position: 'before'
},
scrollMarginTop: 120,
selector: 'h1, h2, h3, h4, h5, h6, #end-marker'
}}
>
<Article {doc} />
</main>
<main <aside class="basis-1/4 relative hidden md:block">
bind:this={mainElement} <div class="fixed mx-8">
class="max-w-4xl mx-auto lg:mx-0 xl:mx-auto px-4 mt-8 mb-14" <StickyToc {tocStore} />
use:toc={{ </div>
store: tocStore, </aside>
observe: true, </div>
anchor: {
properties: { 'aria-hidden': 'true', 'tab-index': '-1', class: 'hidden' },
position: 'before'
}
}}
>
<Article {doc} />
</main>
<StickyToc {tocStore} {distanceFromRight} />