<script>
    import { onMount } from 'svelte';
    import { makeSlug } from '$lib/utils.js';

    export let items;

    items.forEach(i => i.slug = makeSlug(i.text));

    let headings = [];
    let currentHeadingSlug = null;
    let currentSubheadingSlug = null;

    function setCurrentHeading() {
        for (const h of headings) {
            const yPos = h.getBoundingClientRect().y;
            if (yPos > (window.innerHeight / 3)) {
                break;
            }
            if (h.tagName === 'H2') {
                currentHeadingSlug = h.id;
                currentSubheadingSlug = null;
            }
            if (h.tagName === 'H3') {
                currentSubheadingSlug = h.id
            }
        }
    }

    function ellipsize(text) {
        return text;

        // not sure about this, decide on it later
        // if (text.length > 40) {
        //     text = text.slice(0, 40);
        //     // looks weird when we have an ellipsis following a space
        //     if (text.slice(-1) === ' ') {
        //         text = text.slice(0, -1);
        //     }
        //     return text + '…';
        // }
        // return text;
    }

    onMount (() => {
        // These shouldn't change over the life of the page, so we can cache them
        headings = Array.from(document.querySelectorAll('h2[id], h3[id]'));
        setCurrentHeading();
    });
</script>

<svelte:window on:scroll={setCurrentHeading} />

<style lang="scss">
    #toc {
        position: sticky;
        top: 1.5rem;
        margin-left: 1rem;
        margin-right: 4rem;

        max-width: 18rem;
        color: var(--content-color-faded);

        // minimum desirable TOC width is 8rem
        // add 4rem for margins, giving total gutter width of 12.5rem
        // multiply by 2 since there are two equally-sized gutters, then add content-width (52.5rem)
        @media(max-width: 77.5rem) {
            display: none;
        }
    }

    // margin-left is to match the padding on the top-level list items,
    // but here it needs to be margin so that the border is also shifted 
    h5 {
        font-variant: petite-caps;
        font-weight: 500;
        max-width: fit-content;

        margin-top: 0;
        margin-bottom: 0.25em;

        padding-bottom: 0.25em;
        border-bottom: 1px solid currentcolor;
        // make the border stretch beyond the text just a bit, because I like the effect 
        padding-right: 1.5rem;
    }

    ul {
        margin: 0;
        padding: 0;
        list-style: none;
    }

    li {
        position: relative;
        margin-top: 0.45em;
        font-size: var(--content-size-sm);
        // make sure that one item wrapped across multiple lines doesn't just looke like multiple items
        line-height: 1.1;

        &.depth-2 {
            align-items: stretch;
            margin-bottom: 0.2rem;
        }

        &.depth-3 {
            align-items: center;
            margin-bottom: 0.05rem;
        }

        &.current, &:hover {
            color: var(--content-color);
        }   
    }

    .marker {
        position: absolute;
        left: -0.6rem;

        .current &, li:hover & {
            background-color: var(--accent-color);
        }

        &.bar {
            width: 0.125rem;
            height: 100%;
        }

        &.dot {
            width: 0.2rem;
            height: 0.2rem;
            border-radius: 50%;
            // vertically center within its containing block 
            top: 0;
            bottom: 0;
            margin: auto 0;
        }
    }

    // default link styling messes everything up again
    a {
        color: inherit;
        text-decoration: none;
    }
</style>


<div id="toc">
    <h5>
        <span class="heading">Contents</span>
    </h5>
    <ul>
        {#each items as item}
            {#if item.depth === 2}
                <li class="depth-2" class:current={item.slug === currentHeadingSlug} style:align-items="stretch">
                    <span class="marker bar"></span>
                    <a href="#{item.slug}">{ellipsize(item.text)}</a>
                </li>
            {:else if item.depth === 3}
                <li class="depth-3" class:current={item.slug === currentSubheadingSlug} style:align-items="center" style:margin-left="0.75em">
                    <span class="marker dot"></span>
                    <a href="#{item.slug}">{ellipsize(item.text)}</a>
                </li>
            {/if}
        {/each}
    </ul>
</div>