{"version":3,"file":"anchor.vue2.mjs","sources":["../../../components/anchor/anchor.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { AnchorLink } from '@/components/anchor-link'\nimport { Renderer } from '@/components/renderer'\n\nimport {\n  getCurrentInstance,\n  isVNode,\n  nextTick,\n  onBeforeUnmount,\n  onMounted,\n  provide,\n  reactive,\n  ref,\n  watch\n} from 'vue'\n\nimport { emitEvent, useNameHelper, useProps } from '@vexip-ui/config'\nimport { proxyExposed } from '@vexip-ui/hooks'\nimport { isClient, isElement } from '@vexip-ui/utils'\nimport { anchorProps } from './props'\nimport { animateScrollTo } from './helper'\nimport { ANCHOR_STATE } from './symbol'\n\nimport type { ComponentInternalInstance } from 'vue'\nimport type { NativeScrollExposed } from '@/components/native-scroll'\nimport type { Scroll } from '@/components/scroll'\nimport type { AnchorLinkState, AnchorSlots, AnchorState } from './symbol'\n\ntype ScrollType = NativeScrollExposed & InstanceType<typeof Scroll>\n\ndefineOptions({ name: 'Anchor' })\n\nconst nh = useNameHelper('anchor')\n\nconst _props = defineProps(anchorProps)\nconst props = useProps('anchor', _props, {\n  active: {\n    default: '',\n    static: true\n  },\n  viewer: {\n    default: null,\n    static: true\n  },\n  offset: 8,\n  marker: false,\n  scrollDuration: 500,\n  markerTransition: () => nh.ns('fade'),\n  options: {\n    default: () => [],\n    static: true\n  },\n  bindHash: false,\n  forceActive: false,\n  slots: () => ({})\n})\n\nconst emit = defineEmits(['update:active'])\n\ndefineSlots<AnchorSlots>()\n\nconst currentActive = ref(props.active)\nconst animating = ref(false)\nconst markerTop = ref(0)\nconst linkStates = new Set<AnchorLinkState>()\n\nconst wrapper = ref<HTMLElement>()\n\nlet timer: ReturnType<typeof setTimeout>\n\nlet isRawViewer = false\nlet container: Window | HTMLElement | null = null\nlet scroller: ScrollType | null = null\nlet prevScrollTop = 0\n\nif (isClient && !currentActive.value && props.bindHash) {\n  currentActive.value = decodeURIComponent(location.hash)\n}\n\nprovide<AnchorState>(\n  ANCHOR_STATE,\n  reactive({\n    currentActive,\n    increaseLink,\n    decreaseLink,\n    handleActive\n  })\n)\n\nwatch(\n  () => props.active,\n  value => {\n    currentActive.value = value\n  }\n)\nwatch(() => props.viewer, updateContainer)\n\nonMounted(() => {\n  updateContainer()\n  computeMarkerPosition()\n})\n\nonBeforeUnmount(() => {\n  removeListener()\n  clearTimeout(timer)\n})\n\nfunction increaseLink(state: AnchorLinkState) {\n  linkStates.add(state)\n  state.active = currentActive.value === state.to\n}\n\nfunction decreaseLink(state: AnchorLinkState) {\n  linkStates.delete(state)\n}\n\nconst instance = getCurrentInstance()!\n\nfunction updateContainer() {\n  removeListener()\n  isClient &&\n    nextTick(() => {\n      const viewer: unknown = props.viewer\n\n      prevScrollTop = 0\n\n      let _container: Window | Node | ComponentInternalInstance | null = null\n      let refName = 'scroll'\n\n      if (typeof viewer === 'string') {\n        if (viewer.startsWith('ref:')) {\n          refName = viewer.substring(4)\n          refName = refName || 'scroll'\n        } else if (['window', 'document', 'html'].includes(viewer)) {\n          _container = window\n        } else if (viewer === 'body') {\n          _container = document.body\n        } else if (viewer === 'root') {\n          _container = instance.root\n        } else {\n          _container = document.querySelector(viewer)\n        }\n      } else if (typeof viewer === 'function') {\n        _container = viewer()\n      } else if (isElement(viewer)) {\n        _container = viewer\n      }\n\n      if (_container === window || isElement(_container)) {\n        isRawViewer = true\n      } else {\n        isRawViewer = false\n        // container = this.$parent\n      }\n\n      if (!isRawViewer) {\n        // ComponentInternalInstance\n        _container = _container as ComponentInternalInstance\n        _container = isVNode(_container?.vnode) ? _container : instance.parent\n\n        while (_container) {\n          const name = _container.type?.name\n\n          if (name === 'Scroll' || name === 'NativeScroll') {\n            scroller = proxyExposed({ component: _container } as any)\n\n            break\n          }\n\n          const refTemp = _container.refs?.[refName]\n\n          if (refTemp) {\n            if (isElement(refTemp)) {\n              isRawViewer = true\n              container = refTemp as HTMLElement\n            } else {\n              scroller = refTemp as ScrollType\n            }\n\n            break\n          }\n\n          _container = _container.parent\n        }\n\n        if (scroller) {\n          scroller.addScrollListener(handleContainerScroll)\n          container = scroller.$el\n        } else if (!container) {\n          isRawViewer = true\n          container = instance.parent?.proxy?.$el as HTMLElement\n        }\n\n        if (isRawViewer && container) {\n          container.addEventListener('scroll', handleContainerScroll)\n        }\n      } else {\n        container = _container as HTMLElement\n        container.addEventListener('scroll', handleContainerScroll)\n      }\n    })\n}\n\nfunction getContainerEl() {\n  if (!container) return null\n\n  return container === window ? document.documentElement : (container as HTMLElement)\n}\n\nfunction computeCurrentLink(scrollTop: number) {\n  if (!linkStates.size || !container) return\n\n  const containerTop = getContainerEl()!.offsetTop\n  const offsetList: { link: string, offset: number }[] = []\n\n  let offset = scrollTop + props.offset\n\n  if (isRawViewer) {\n    offset += containerTop\n  }\n\n  linkStates.forEach(state => {\n    const id = state.to\n\n    if (!id.startsWith('#')) return\n\n    const element = document.querySelector(id) as HTMLElement | null\n\n    if (element) {\n      offsetList.push({\n        link: id,\n        offset: element.offsetTop\n      })\n    }\n  })\n\n  offsetList.sort((prev, next) => prev.offset - next.offset)\n  offsetList.push({\n    link: '',\n    offset: Infinity\n  })\n\n  let currentLink = ''\n\n  for (let i = 0, len = offsetList.length - 1; i < len; ++i) {\n    const current = offsetList[i]\n    const next = offsetList[i + 1]\n\n    if (current.offset <= offset && next.offset > offset) {\n      currentLink = current.link\n\n      break\n    }\n  }\n\n  prevScrollTop = scrollTop\n\n  if (currentActive.value !== currentLink) {\n    currentActive.value = currentLink\n    emit('update:active', currentLink)\n    emitEvent(props.onChange, currentLink)\n  }\n}\n\nfunction handleContainerScroll(event: Event) {\n  if (animating.value) return\n\n  const scrollTop = isRawViewer\n    ? (\n        (event.target === window || event.target === document\n          ? document.documentElement\n          : event.target) as HTMLElement\n      ).scrollTop\n    : (event as MouseEvent).clientY\n\n  computeCurrentLink(scrollTop)\n  computeMarkerPosition()\n}\n\nfunction removeListener() {\n  if (scroller) {\n    scroller.removeScrollListener(handleContainerScroll)\n    scroller = null\n  }\n\n  if (container) {\n    container.removeEventListener('scroll', handleContainerScroll)\n  }\n}\n\nfunction handleActive(link: string) {\n  if (\n    (!props.forceActive && link === currentActive.value) ||\n    !link.startsWith('#') ||\n    link.length < 2\n  ) {\n    return\n  }\n\n  const element = document.querySelector(link) as HTMLElement | null\n\n  if (!element) return\n\n  clearTimeout(timer)\n\n  animating.value = true\n\n  const elementTop = element.offsetTop\n  const duration = Math.max(props.scrollDuration, 0)\n\n  if (isRawViewer && container) {\n    const containerEl = getContainerEl()!\n    // const from = containerEl.scrollTop\n    const to = Math.min(\n      elementTop - containerEl.offsetTop - props.offset,\n      containerEl.scrollHeight - containerEl.clientHeight\n    )\n\n    animateScrollTo(containerEl, prevScrollTop, to, duration, () => {\n      timer = setTimeout(() => {\n        animating.value = false\n      }, 10)\n    })\n    computeCurrentLink(to)\n    computeMarkerPosition()\n  } else if (scroller) {\n    const [min, max] = scroller.getYScrollLimit()\n    const clientY = Math.max(Math.min(elementTop - props.offset, max), min)\n\n    scroller.scrollTo(0, clientY, duration).then(() => {\n      timer = setTimeout(() => {\n        animating.value = false\n      }, duration + 10)\n    })\n\n    computeCurrentLink(clientY)\n    computeMarkerPosition()\n  } else {\n    animating.value = false\n  }\n\n  if (isClient && props.bindHash && location) {\n    location.hash = encodeURIComponent(currentActive.value.replace(/^#/, ''))\n  }\n}\n\nfunction computeMarkerPosition() {\n  const currentLink = Array.from(linkStates).find(\n    state => state.to && state.to === currentActive.value\n  )\n\n  if (currentLink?.el) {\n    const linkRect = currentLink.el.getBoundingClientRect()\n    const wrapperTop = wrapper.value?.getBoundingClientRect().top ?? 0\n\n    markerTop.value = linkRect.top - wrapperTop + linkRect.height / 2 + 0.5\n  }\n}\n</script>\n\n<template>\n  <div\n    ref=\"wrapper\"\n    :class=\"{\n      [nh.b()]: true,\n      [nh.bs('vars')]: true,\n      [nh.bm('inherit')]: props.inherit,\n      [nh.bm('no-marker')]: !props.marker\n    }\"\n  >\n    <ul :class=\"nh.be('list')\">\n      <slot>\n        <Renderer :renderer=\"props.slots.default\">\n          <AnchorLink\n            v-for=\"link in props.options\"\n            :key=\"link.to\"\n            :to=\"link.to\"\n            :title=\"link.title\"\n            :children=\"link.children\"\n          >\n            {{ link.label }}\n          </AnchorLink>\n        </Renderer>\n      </slot>\n    </ul>\n    <Transition appear :name=\"props.markerTransition\">\n      <div\n        v-if=\"props.marker && currentActive\"\n        :class=\"nh.be('marker')\"\n        :style=\"{ top: `${markerTop}px` }\"\n      >\n        <slot name=\"marker\">\n          <Renderer :renderer=\"props.slots.marker\">\n            <div :class=\"nh.be('pointer')\"></div>\n          </Renderer>\n        </slot>\n      </div>\n    </Transition>\n  </div>\n</template>\n"],"names":["nh","useNameHelper","props","useProps","__props","emit","__emit","currentActive","ref","animating","markerTop","linkStates","wrapper","timer","isRawViewer","container","scroller","prevScrollTop","isClient","provide","ANCHOR_STATE","reactive","increaseLink","decreaseLink","handleActive","watch","value","updateContainer","onMounted","computeMarkerPosition","onBeforeUnmount","removeListener","state","instance","getCurrentInstance","nextTick","viewer","_container","refName","isElement","handleContainerScroll","isVNode","name","_a","proxyExposed","refTemp","_b","_d","_c","getContainerEl","computeCurrentLink","scrollTop","containerTop","offsetList","offset","id","element","prev","next","currentLink","len","current","emitEvent","event","link","elementTop","duration","containerEl","to","animateScrollTo","min","max","clientY","linkRect","wrapperTop"],"mappings":"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"}