UNPKG

bootstrap-vue-next

Version:

Seamless integration of Vue 3, Bootstrap 5, and TypeScript for modern, type-safe UI development

142 lines (141 loc) 5.02 kB
import { N as syncRef, g as useIntersectionObserver, y as useMutationObserver } from "../../../dist-B10a-gZ8.mjs"; import { i as getSafeDocument } from "../../../dom-AhkaSoh8.mjs"; import { t as getElement } from "../../../getElement-0_htvrFw.mjs"; import { computed, getCurrentInstance, nextTick, onMounted, readonly, ref, toRef, unref, watch } from "vue"; //#region src/composables/useScrollspy/index.ts var useScrollspy = (content, target, options = {}) => { const cont = toRef(content); const tar = toRef(target); const resolvedContent = ref(getElement(cont.value)); const resolvedTarget = ref(getElement(tar.value)); watch([cont, tar], () => { updateList(); }); const { contentQuery = ":scope > [id]", targetQuery = "[href]", manual = false, root, rootMargin = "0px 0px -25%", threshold = [ .1, .5, 1 ], watchChanges = true } = options; const current = ref(null); const list = ref([]); const nodeList = ref([]); if (!getCurrentInstance()) nextTick(() => { updateList(); }); else onMounted(() => { syncRef(cont, resolvedContent, { transform: { ltr: (v) => getElement(v) }, direction: "ltr", immediate: true }); syncRef(tar, resolvedTarget, { transform: { ltr: (v) => getElement(v) }, direction: "ltr", immediate: true }); updateList(); }); const updateList = () => { nodeList.value = resolvedContent.value ? Array.from(resolvedContent.value.querySelectorAll(contentQuery)) : []; list.value = nodeList.value.map((el) => ({ id: el.id, el, visible: false, text: el.textContent })); }; let isScrollingDown = true; let previousScrollTop = 0; const scrollRoot = computed(() => resolvedContent.value && getComputedStyle(resolvedContent.value).overflowY === "visible" ? null : resolvedContent.value); const jobs = useIntersectionObserver(nodeList, (entries) => { const doc = getSafeDocument(); const scrollTop = (scrollRoot.value ? scrollRoot.value : doc !== null ? doc.documentElement : void 0)?.scrollTop || 0; isScrollingDown = scrollTop > previousScrollTop; previousScrollTop = scrollTop; entries.forEach((entry) => { if (entry.isIntersecting) { list.value.forEach((node) => { if (node.el === entry.target) node.visible = true; }); return; } list.value.forEach((node) => { if (node.el === entry.target) node.visible = false; }); }); let newId = null; if (isScrollingDown) newId = [...list.value].reverse().find((node) => node.visible)?.id || null; else newId = list.value.find((node) => node.visible)?.id || null; if (newId !== null) current.value = newId; if (!current.value) current.value = list.value[0]?.id || null; }, { root: computed(() => root ? getElement(unref(root)) : scrollRoot.value), rootMargin, threshold }); watch(current, (newId) => { if (manual) return; const nodes = resolvedTarget.value?.querySelectorAll(targetQuery); if (nodes === void 0) return; let foundParent = false; let activeElement = null; nodes.forEach((node) => { const parentDropdown = node.closest(".dropdown"); if (node.getAttribute("href")?.includes(`#${newId}`)) { activeElement = node; node.classList.add("active"); if (parentDropdown) { parentDropdown?.querySelector(".dropdown-toggle")?.classList.add("active"); foundParent = true; } let parentNav = node.closest(".nav")?.previousSibling; while (parentNav?.classList?.contains("nav-item")) { foundParent = true; parentNav.querySelector(".nav-link")?.classList.add("active"); parentNav = parentNav.closest(".nav")?.previousSibling; } } else { node.classList.remove("active"); if (parentDropdown && !foundParent) parentDropdown?.querySelector(".dropdown-toggle")?.classList.remove("active"); if (!foundParent) { let parentNav = node.closest(".nav")?.previousSibling; while (parentNav?.classList?.contains("nav-item")) { foundParent = true; if (parentNav.querySelector(".nav-link") !== activeElement) parentNav.querySelector(".nav-link")?.classList.remove("active"); parentNav = parentNav.closest(".nav")?.previousSibling; } } } }); }); const mobs = !watchChanges ? { stop: () => {} } : useMutationObserver(resolvedContent, () => { updateList(); }, { childList: true }); const scrollIntoView = (event, smooth = false) => { event.preventDefault(); const href = event.target?.getAttribute?.("href"); const doc = getSafeDocument(); const el = href ? doc?.querySelector(href) ?? null : null; if (el && resolvedContent.value) if (resolvedContent.value.scrollTo) resolvedContent.value.scrollTo({ top: el.offsetTop, behavior: smooth ? "smooth" : "auto" }); else resolvedContent.value.scrollTop = el.offsetTop; }; const cleanup = () => { jobs.stop(); mobs.stop(); }; return { current: readonly(current), list, content: resolvedContent, target: resolvedTarget, scrollIntoView, updateList, cleanup }; }; //#endregion export { useScrollspy }; //# sourceMappingURL=index.mjs.map