UNPKG

bootstrap-vue-next

Version:

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

182 lines (181 loc) 5.54 kB
import { g as useIntersectionObserver, h as useMutationObserver } from "../../../index-Bg04zFDG.mjs"; import { toRef, ref, watch, getCurrentInstance, nextTick, onMounted, computed, readonly } from "vue"; import { g as getElement } from "../../../getElement-DrDVR-jO.mjs"; import { s as syncRef } from "../../../index-wxu-PlLx.mjs"; const 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 = [0.1, 0.5, 1], watchChanges = true } = options; const current = ref(null); const list = ref([]); const nodeList = ref([]); const ctx = getCurrentInstance(); if (!ctx) { 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 iobs = useIntersectionObserver( nodeList, (entries) => { const scrollTop = (scrollRoot.value || document?.documentElement)?.scrollTop; 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: root ? getElement(root) : scrollRoot, 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 el = href ? document?.querySelector(href) : 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 = () => { iobs.stop(); mobs.stop(); }; return { current: readonly(current), list, content: resolvedContent, target: resolvedTarget, scrollIntoView, updateList, cleanup }; }; export { useScrollspy }; //# sourceMappingURL=index.mjs.map