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
JavaScript
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