You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
127 lines
3.6 KiB
127 lines
3.6 KiB
<script lang='ts'> |
|
import { Button, Sidebar, SidebarGroup, SidebarItem, SidebarWrapper, Skeleton, TextPlaceholder, Tooltip } from 'flowbite-svelte'; |
|
import { onMount } from 'svelte'; |
|
import { BookOutline } from 'flowbite-svelte-icons'; |
|
import Preview from './Preview.svelte'; |
|
import { pharosInstance } from '$lib/parser'; |
|
import { page } from '$app/state'; |
|
|
|
let { rootId }: { rootId: string } = $props(); |
|
|
|
if (rootId !== $pharosInstance.getRootIndexId()) { |
|
console.error('Root ID does not match parser root index ID'); |
|
} |
|
|
|
let activeHash = $state(page.url.hash); |
|
|
|
function normalizeHashPath(str: string): string { |
|
return str |
|
.toLowerCase() |
|
.replace(/\s+/g, '-') |
|
.replace(/[^\w-]/g, ''); |
|
} |
|
|
|
function scrollToElementWithOffset() { |
|
const hash = window.location.hash; |
|
if (hash) { |
|
const targetElement = document.querySelector(hash); |
|
if (targetElement) { |
|
const headerOffset = 80; |
|
const elementPosition = targetElement.getBoundingClientRect().top; |
|
const offsetPosition = elementPosition + window.scrollY - headerOffset; |
|
|
|
window.scrollTo({ |
|
top: offsetPosition, |
|
behavior: 'auto', |
|
}); |
|
} |
|
} |
|
} |
|
|
|
let showToc: boolean = true; |
|
let showTocButton: boolean = false; |
|
const tocBreakpoint = 1140; |
|
|
|
/** |
|
* Hides the table of contents sidebar when the window shrinks below a certain size. This |
|
* prevents the sidebar from occluding the article content. |
|
*/ |
|
const setTocVisibilityOnResize = () => { |
|
showToc = window.innerWidth >= tocBreakpoint; |
|
showTocButton = window.innerWidth < tocBreakpoint; |
|
}; |
|
|
|
/** |
|
* Hides the table of contents sidebar when the user clicks outside of it. |
|
*/ |
|
const hideTocOnClick = (ev: MouseEvent) => { |
|
const target = ev.target as HTMLElement; |
|
|
|
if (target.closest('.sidebar-leather') || target.closest('.btn-leather')) { |
|
return; |
|
} |
|
|
|
if (showToc) { |
|
showToc = false; |
|
} |
|
}; |
|
|
|
onMount(() => { |
|
// Always check whether the TOC sidebar should be visible. |
|
setTocVisibilityOnResize(); |
|
|
|
window.addEventListener('hashchange', scrollToElementWithOffset); |
|
// Also handle the case where the user lands on the page with a hash in the URL |
|
scrollToElementWithOffset(); |
|
|
|
window.addEventListener('resize', setTocVisibilityOnResize); |
|
window.addEventListener('click', hideTocOnClick); |
|
|
|
return () => { |
|
window.removeEventListener('hashchange', scrollToElementWithOffset); |
|
window.removeEventListener('resize', setTocVisibilityOnResize); |
|
window.removeEventListener('click', hideTocOnClick); |
|
}; |
|
}); |
|
</script> |
|
|
|
{#if showTocButton && !showToc} |
|
<Button |
|
class='btn-leather fixed top-20 left-4 h-6 w-6' |
|
outline={true} |
|
on:click={ev => { |
|
showToc = true; |
|
ev.stopPropagation(); |
|
}} |
|
> |
|
<BookOutline /> |
|
</Button> |
|
<Tooltip> |
|
Show Table of Contents |
|
</Tooltip> |
|
{/if} |
|
<!-- TODO: Get TOC from parser. --> |
|
<!-- {#if showToc} |
|
<Sidebar class='sidebar-leather fixed top-20 left-0 px-4 w-60' {activeHash}> |
|
<SidebarWrapper> |
|
<SidebarGroup class='sidebar-group-leather overflow-y-scroll'> |
|
{#each events as event} |
|
<SidebarItem |
|
class='sidebar-item-leather' |
|
label={event.getMatchingTags('title')[0][1]} |
|
href={`${$page.url.pathname}#${normalizeHashPath(event.getMatchingTags('title')[0][1])}`} |
|
/> |
|
{/each} |
|
</SidebarGroup> |
|
</SidebarWrapper> |
|
</Sidebar> |
|
{/if} --> |
|
<div class='flex flex-col space-y-4 max-w-2xl'> |
|
<Preview {rootId} /> |
|
</div> |
|
|
|
<style> |
|
:global(.sidebar-group-leather) { |
|
max-height: calc(100vh - 8rem); |
|
} |
|
</style>
|
|
|