nimiq-vitepress-theme
Version:
Nimiq UI theme for VitePress
94 lines (93 loc) • 2.88 kB
JavaScript
import { createSharedComposable, useThrottleFn, useWindowSize } 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([]);
const { height: windowHeight } = useWindowSize();
function updateHeadingTree() {
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 || el.id === "changelog")
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 < windowHeight.value && rect.bottom > 0) {
active.push(heading.hashPath);
}
}
heading.items.forEach(checkHeading);
}
headingTree.value.forEach(checkHeading);
activeHeadings.value = active;
}, 100);
const { y: scrollY } = typeof window ? { y: ref(0) } : useScroll(window);
const route = useRoute();
watch(route, async () => {
await nextTick();
updateHeadingTree();
});
onMounted(() => updateHeadingTree());
watch(scrollY, updateActiveHeadings);
const layout = computed(() => frontmatter.value.layout || "docs");
const showOutline = computed(() => {
if (frontmatter.value.outline !== void 0)
return !!frontmatter.value.outline;
if (layout.value === "home")
return false;
return headingTree.value.length > 0;
});
const showWidget = computed(() => {
if (frontmatter.value.widget !== void 0)
return !!frontmatter.value.widget;
if (layout.value === "home")
return false;
return true;
});
const showSecondarySidebar = computed(() => {
if (frontmatter.value.secondarySidebar !== void 0)
return !!frontmatter.value.secondarySidebar;
if (layout.value === "home")
return false;
return true;
});
function isHeadingActive(hash) {
return activeHeadings.value.includes(hash);
}
return {
headingTree,
isHeadingActive,
showOutline,
showWidget,
showSecondarySidebar
};
});