UNPKG

nimiq-vitepress-theme

Version:

Nimiq UI theme for VitePress

84 lines (83 loc) 2.55 kB
import { createSharedComposable, useScroll, useThrottleFn } from "@vueuse/core"; import { useData, useRoute } from "vitepress"; import { computed, nextTick, onMounted, ref, watch } from "vue"; export const useSecondarySidebar = createSharedComposable(() => { const { frontmatter } = useData(); const headingTree = ref([]); const activeHeadings = ref([]); function updateHeadingTree() { if (import.meta.env.SSR) return; const nodes = document.querySelectorAll("article :where(h2,h3):not([data-card] *)"); const tree = []; let lastH2 = null; nodes.forEach((node) => { const el = node; if (!el.id) return; const level = Number(el.tagName[1]); const heading = { hashPath: el.id, text: el.textContent?.trim() || "", level, items: [] }; if (level === 2) { tree.push(heading); lastH2 = heading; } else if (level === 3) { if (lastH2) { lastH2.items.push(heading); } else { tree.push(heading); } } }); headingTree.value = tree; } const updateActiveHeadings = useThrottleFn(async () => { await nextTick(); const active = []; function checkHeading(heading) { const el = document.getElementById(heading.hashPath); if (el) { const rect = el.getBoundingClientRect(); if (rect.top < window.innerHeight && rect.bottom > 0) { active.push(heading.hashPath); } } heading.items.forEach(checkHeading); } headingTree.value.forEach(checkHeading); activeHeadings.value = active; }, 100); const { y: scrollY } = useScroll(void 0); const route = useRoute(); watch(route, async () => { await nextTick(); updateHeadingTree(); }); onMounted(() => updateHeadingTree()); watch(scrollY, updateActiveHeadings); const showOutline = computed(() => { if (frontmatter.value.outline !== void 0) return !!frontmatter.value.outline; return headingTree.value.length > 0; }); const showWidget = computed(() => frontmatter.value.widget !== false); const showSecondarySidebar = computed(() => { if (frontmatter.value.showSecondarySidebar !== void 0) return frontmatter.value.showSecondarySidebar !== false; return showWidget.value && showOutline.value; }); function isHeadingActive(hash) { return activeHeadings.value.includes(hash); } return { headingTree, isHeadingActive, showOutline, showWidget, showSecondarySidebar }; });