{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/composables/useScrollspy/index.ts"],"sourcesContent":["import {syncRef, useIntersectionObserver, useMutationObserver} from '@vueuse/core'\nimport {\n  type ComponentPublicInstance,\n  computed,\n  getCurrentInstance,\n  type MaybeRef,\n  type MaybeRefOrGetter,\n  nextTick,\n  onMounted,\n  readonly,\n  type Ref,\n  ref,\n  shallowRef,\n  toRef,\n  unref,\n  watch,\n} from 'vue'\nimport {getElement} from '../../utils/getElement'\nimport {getSafeDocument} from '../../utils/dom'\n\ntype ScrollspyList = {\n  id: string | null\n  el: HTMLElement | null\n  visible: boolean\n  text: string | null\n}[]\n\ninterface ScrollspyReturn {\n  current: Readonly<Ref<string | null>>\n  list: Readonly<Ref<ScrollspyList>>\n  content: Ref<HTMLElement | undefined>\n  target: Ref<HTMLElement | undefined>\n  scrollIntoView: (event: MouseEvent) => void\n  updateList: () => void\n  cleanup: () => void\n}\n\ninterface ScrollspyOptions {\n  contentQuery: string\n  targetQuery: string\n  manual: boolean\n  root: MaybeRef<string | ComponentPublicInstance | HTMLElement | null>\n  rootMargin: string\n  threshold: number | number[]\n  watchChanges: boolean\n}\n\nexport const useScrollspy = (\n  content: MaybeRefOrGetter<string | ComponentPublicInstance | HTMLElement | null>,\n  target: MaybeRefOrGetter<string | ComponentPublicInstance | HTMLElement | null>,\n  options: Readonly<Partial<ScrollspyOptions>> = {}\n): ScrollspyReturn => {\n  const cont = toRef(content)\n  const tar = toRef(target)\n\n  const resolvedContent = shallowRef(getElement(cont.value))\n  const resolvedTarget = shallowRef(getElement(tar.value))\n\n  watch([cont, tar], () => {\n    updateList()\n  })\n  const {\n    contentQuery = ':scope > [id]',\n    targetQuery = '[href]',\n    manual = false,\n    root,\n    rootMargin = '0px 0px -25%',\n    threshold = [0.1, 0.5, 1],\n    watchChanges = true,\n  } = options\n  const current = ref<string | null>(null)\n  const list = shallowRef<ScrollspyList>([])\n  const nodeList = shallowRef<HTMLElement[]>([])\n\n  // are we called in directive?\n  const ctx = getCurrentInstance()\n  if (!ctx) {\n    nextTick(() => {\n      updateList()\n    })\n  } else {\n    onMounted(() => {\n      syncRef(cont, resolvedContent, {\n        transform: {\n          ltr: (v) => getElement(v),\n        },\n        direction: 'ltr',\n        immediate: true,\n      })\n      syncRef(tar, resolvedTarget, {\n        transform: {\n          ltr: (v) => getElement(v),\n        },\n        direction: 'ltr',\n        immediate: true,\n      })\n      updateList()\n    })\n  }\n\n  const updateList = () => {\n    nodeList.value = resolvedContent.value\n      ? (Array.from(resolvedContent.value.querySelectorAll(contentQuery)) as HTMLElement[])\n      : []\n    list.value = nodeList.value.map((el) => ({\n      id: el.id,\n      el,\n      visible: false,\n      text: el.textContent,\n    }))\n  }\n\n  let isScrollingDown = true\n  let previousScrollTop = 0\n  const scrollRoot = computed(() =>\n    resolvedContent.value && getComputedStyle(resolvedContent.value).overflowY === 'visible'\n      ? null\n      : resolvedContent.value\n  )\n\n  const jobs = useIntersectionObserver(\n    nodeList,\n    (entries) => {\n      const doc = getSafeDocument()\n      const scrollTop =\n        (scrollRoot.value ? scrollRoot.value : doc !== null ? doc.documentElement : undefined)\n          ?.scrollTop || 0\n      isScrollingDown = scrollTop > previousScrollTop\n      previousScrollTop = scrollTop\n      entries.forEach((entry) => {\n        if (entry.isIntersecting) {\n          list.value.forEach((node) => {\n            if (node.el === entry.target) {\n              node.visible = true\n            }\n          })\n          return\n        }\n        list.value.forEach((node) => {\n          if (node.el === entry.target) {\n            node.visible = false\n          }\n        })\n      })\n      let newId: string | null = null\n      if (isScrollingDown) {\n        newId = [...list.value].reverse().find((node) => node.visible)?.id || null\n      } else {\n        newId = list.value.find((node) => node.visible)?.id || null\n      }\n      if (newId !== null) {\n        current.value = newId\n      }\n      if (!current.value) {\n        current.value = list.value[0]?.id || null\n      }\n    },\n    {\n      root: computed(() => (root ? getElement(unref(root)) : scrollRoot.value)),\n      rootMargin,\n      threshold,\n    }\n  )\n  watch(current, (newId) => {\n    if (manual) return\n    const nodes = resolvedTarget.value?.querySelectorAll(targetQuery)\n    if (nodes === undefined) return\n    let foundParent = false\n    let activeElement: HTMLElement | null = null\n    nodes.forEach((node) => {\n      const parentDropdown = node.closest('.dropdown')\n\n      if (node.getAttribute('href')?.includes(`#${newId}`)) {\n        activeElement = node as HTMLElement\n        node.classList.add('active')\n        if (parentDropdown) {\n          parentDropdown?.querySelector('.dropdown-toggle')?.classList.add('active')\n          foundParent = true\n        }\n        let parentNav = node.closest('.nav')?.previousSibling as HTMLElement\n        while (parentNav?.classList?.contains('nav-item')) {\n          foundParent = true\n          parentNav.querySelector('.nav-link')?.classList.add('active')\n          parentNav = parentNav.closest('.nav')?.previousSibling as HTMLElement\n        }\n      } else {\n        node.classList.remove('active')\n        if (parentDropdown && !foundParent) {\n          parentDropdown?.querySelector('.dropdown-toggle')?.classList.remove('active')\n        }\n\n        if (!foundParent) {\n          let parentNav = node.closest('.nav')?.previousSibling as HTMLElement\n          while (parentNav?.classList?.contains('nav-item')) {\n            foundParent = true\n            if (parentNav.querySelector('.nav-link') !== activeElement) {\n              parentNav.querySelector('.nav-link')?.classList.remove('active')\n            }\n            parentNav = parentNav.closest('.nav')?.previousSibling as HTMLElement\n          }\n        }\n      }\n    })\n  })\n\n  const mobs = !watchChanges\n    ? {stop: () => {}}\n    : useMutationObserver(\n        resolvedContent,\n        () => {\n          updateList()\n        },\n        {\n          childList: true,\n        }\n      )\n  const scrollIntoView = (event: Readonly<MouseEvent>, smooth: boolean = false) => {\n    event.preventDefault()\n    const href = (event.target as HTMLElement)?.getAttribute?.('href')\n    const doc = getSafeDocument()\n    const el: HTMLElement | null = href ? (doc?.querySelector(href) ?? null) : null\n    // console.log('scrollIntoView', event, el, content.value.$el)\n    if (el && resolvedContent.value) {\n      if (resolvedContent.value.scrollTo) {\n        resolvedContent.value.scrollTo({top: el.offsetTop, behavior: smooth ? 'smooth' : 'auto'})\n      } else {\n        resolvedContent.value.scrollTop = el.offsetTop\n      }\n    }\n  }\n  const cleanup = () => {\n    jobs.stop()\n    mobs.stop()\n  }\n  return {\n    current: readonly(current),\n    list,\n    content: resolvedContent,\n    target: resolvedTarget,\n    scrollIntoView,\n    updateList,\n    cleanup,\n  }\n}\n"],"mappings":"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"}