mirror of
https://github.com/youwen5/site.git
synced 2024-11-24 09:23:50 -08:00
feat: add sticky toc
This commit is contained in:
parent
8233c4c49b
commit
cadc1f637f
4 changed files with 120 additions and 1 deletions
|
@ -47,6 +47,7 @@
|
|||
"@fontsource/geist-sans": "^5.0.2",
|
||||
"@fontsource/merriweather": "^5.0.12",
|
||||
"@fontsource/zilla-slab": "^5.0.12",
|
||||
"@svelte-put/toc": "^5.0.1",
|
||||
"@sveltejs/adapter-vercel": "^5.2.0",
|
||||
"bits-ui": "^0.21.2",
|
||||
"clsx": "^2.1.0",
|
||||
|
|
|
@ -17,6 +17,9 @@ dependencies:
|
|||
'@fontsource/zilla-slab':
|
||||
specifier: ^5.0.12
|
||||
version: 5.0.12
|
||||
'@svelte-put/toc':
|
||||
specifier: ^5.0.1
|
||||
version: 5.0.1(svelte@4.2.12)
|
||||
'@sveltejs/adapter-vercel':
|
||||
specifier: ^5.2.0
|
||||
version: 5.2.0(@sveltejs/kit@2.5.5)
|
||||
|
@ -890,6 +893,14 @@ packages:
|
|||
requiresBuild: true
|
||||
optional: true
|
||||
|
||||
/@svelte-put/toc@5.0.1(svelte@4.2.12):
|
||||
resolution: {integrity: sha512-oxDFPM2Y0XmhhXoNDhcPltf8RxhIvtiotFL0PX9NLossTumuAPiLVaDeEsGfBOn+nZK2J1oo9XqJU3NWHDROcw==}
|
||||
peerDependencies:
|
||||
svelte: ^3.55.0 || ^4.0.0
|
||||
dependencies:
|
||||
svelte: 4.2.12
|
||||
dev: false
|
||||
|
||||
/@sveltejs/adapter-vercel@5.2.0(@sveltejs/kit@2.5.5):
|
||||
resolution: {integrity: sha512-872y13DxKcOBxgnXc4C2YHRw1ow9N1CpUxMH34NYFqCn6PUO6f34qle8v/Byr8sHEC/d+PZIAI3MJs3c8f7TfA==}
|
||||
peerDependencies:
|
||||
|
|
72
src/lib/components/StickyToc.svelte
Normal file
72
src/lib/components/StickyToc.svelte
Normal file
|
@ -0,0 +1,72 @@
|
|||
<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>
|
|
@ -5,6 +5,26 @@
|
|||
// import { config } from '$lib/stores/index.js';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import Article from '$lib/components/Blog/Article.svelte';
|
||||
import { toc, createTocStore } from '@svelte-put/toc';
|
||||
import StickyToc from '$lib/components/StickyToc.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();
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
|
@ -28,6 +48,21 @@
|
|||
<meta name="author" content="Youwen Wu" />
|
||||
</svelte:head>
|
||||
|
||||
<main class="max-w-4xl md:mx-auto mx-4 mt-8 mb-14">
|
||||
<svelte:window on:resize={handleResize} />
|
||||
|
||||
<main
|
||||
bind:this={mainElement}
|
||||
class="max-w-4xl mx-auto lg:mx-0 xl:mx-auto px-4 mt-8 mb-14"
|
||||
use:toc={{
|
||||
store: tocStore,
|
||||
observe: true,
|
||||
anchor: {
|
||||
properties: { 'aria-hidden': 'true', 'tab-index': '-1', class: 'hidden' },
|
||||
position: 'before'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Article {doc} />
|
||||
</main>
|
||||
|
||||
<StickyToc {tocStore} {distanceFromRight} />
|
||||
|
|
Loading…
Reference in a new issue