{"version":3,"file":"anchor2.mjs","names":[],"sources":["../../../../../../packages/components/anchor/src/anchor.vue"],"sourcesContent":["<template>\n  <div ref=\"anchorRef\" :class=\"cls\">\n    <div\n      v-if=\"marker\"\n      ref=\"markerRef\"\n      :class=\"ns.e('marker')\"\n      :style=\"markerStyle\"\n    />\n    <div :class=\"ns.e('list')\">\n      <slot />\n    </div>\n  </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport {\n  computed,\n  nextTick,\n  onMounted,\n  provide,\n  ref,\n  useSlots,\n  watch,\n} from 'vue'\nimport { useEventListener } from '@vueuse/core'\nimport { useNamespace } from '@element-plus/hooks'\nimport {\n  animateScrollTo,\n  getElement,\n  getOffsetTopDistance,\n  getScrollElement,\n  getScrollTop,\n  isUndefined,\n  isWindow,\n  throttleByRaf,\n} from '@element-plus/utils'\nimport { CHANGE_EVENT } from '@element-plus/constants'\nimport { anchorEmits } from './anchor'\nimport { anchorKey } from './constants'\n\nimport type { CSSProperties } from 'vue'\nimport type { AnchorProps } from './anchor'\nimport type { AnchorLinkState } from './constants'\n\ndefineOptions({\n  name: 'ElAnchor',\n})\n\nconst props = withDefaults(defineProps<AnchorProps>(), {\n  offset: 0,\n  bound: 15,\n  duration: 300,\n  marker: true,\n  type: 'default',\n  direction: 'vertical',\n})\nconst emit = defineEmits(anchorEmits)\nconst slots = useSlots()\n\nconst currentAnchor = ref('')\nconst markerStyle = ref<CSSProperties>({})\nconst anchorRef = ref<HTMLElement | null>(null)\nconst markerRef = ref<HTMLElement | null>(null)\nconst containerEl = ref<HTMLElement | Window>()\n\nconst links: Record<string, HTMLElement> = {}\nlet isScrolling = false\nlet currentScrollTop = 0\n\nconst ns = useNamespace('anchor')\n\nconst cls = computed(() => [\n  ns.b(),\n  props.type === 'underline' ? ns.m('underline') : '',\n  ns.m(props.direction),\n])\n\nconst addLink = (state: AnchorLinkState) => {\n  links[state.href] = state.el\n}\n\nconst removeLink = (href: string) => {\n  delete links[href]\n}\n\nconst setCurrentAnchor = (href: string) => {\n  const activeHref = currentAnchor.value\n  if (activeHref !== href) {\n    currentAnchor.value = href\n    emit(CHANGE_EVENT, href)\n  }\n}\n\nlet clearAnimate: (() => void) | null = null\nlet currentTargetHref = ''\n\nconst scrollToAnchor = (href: string) => {\n  if (!containerEl.value) return\n  const target = getElement(href)\n  if (!target) return\n\n  if (clearAnimate) {\n    if (currentTargetHref === href) return\n    clearAnimate()\n  }\n\n  currentTargetHref = href\n  isScrolling = true\n  const scrollEle = getScrollElement(target, containerEl.value)\n  const distance = getOffsetTopDistance(target, scrollEle)\n  const max = scrollEle.scrollHeight - scrollEle.clientHeight\n  const to = Math.min(distance - props.offset, max)\n  clearAnimate = animateScrollTo(\n    containerEl.value,\n    currentScrollTop,\n    to,\n    props.duration,\n    () => {\n      // make sure it is executed after throttleByRaf's handleScroll\n      setTimeout(() => {\n        isScrolling = false\n        currentTargetHref = ''\n      }, 20)\n    }\n  )\n}\n\nconst scrollTo = (href?: string) => {\n  if (href) {\n    setCurrentAnchor(href)\n    scrollToAnchor(href)\n  }\n}\n\nconst handleClick = (e: MouseEvent, href?: string) => {\n  emit('click', e, href)\n  scrollTo(href)\n}\n\nconst handleScroll = throttleByRaf(() => {\n  if (containerEl.value) {\n    currentScrollTop = getScrollTop(containerEl.value)\n  }\n  const currentHref = getCurrentHref()\n  if (isScrolling || isUndefined(currentHref)) return\n  setCurrentAnchor(currentHref)\n})\n\nconst getCurrentHref = () => {\n  if (!containerEl.value) return\n  const scrollTop = getScrollTop(containerEl.value)\n  const anchorTopList: { top: number; href: string }[] = []\n\n  for (const href of Object.keys(links)) {\n    const target = getElement(href)\n    if (!target) continue\n    const scrollEle = getScrollElement(target, containerEl.value)\n    const distance = getOffsetTopDistance(target, scrollEle)\n    anchorTopList.push({\n      top: distance - props.offset - props.bound,\n      href,\n    })\n  }\n  anchorTopList.sort((prev, next) => prev.top - next.top)\n  for (let i = 0; i < anchorTopList.length; i++) {\n    const item = anchorTopList[i]\n    const next = anchorTopList[i + 1]\n\n    if (i === 0 && scrollTop === 0) {\n      return props.selectScrollTop ? item.href : ''\n    }\n    if (item.top <= scrollTop && (!next || next.top > scrollTop)) {\n      return item.href\n    }\n  }\n}\n\nconst getContainer = () => {\n  const el = getElement(props.container)\n  if (!el || isWindow(el)) {\n    containerEl.value = window\n  } else {\n    containerEl.value = el\n  }\n}\n\nuseEventListener(containerEl, 'scroll', handleScroll)\n\nconst updateMarkerStyle = () => {\n  nextTick(() => {\n    if (!anchorRef.value || !markerRef.value || !currentAnchor.value) {\n      markerStyle.value = {}\n      return\n    }\n    const currentLinkEl = links[currentAnchor.value]\n    if (!currentLinkEl) {\n      markerStyle.value = {}\n      return\n    }\n    const anchorRect = anchorRef.value.getBoundingClientRect()\n    const markerRect = markerRef.value.getBoundingClientRect()\n    const linkRect = currentLinkEl.getBoundingClientRect()\n\n    if (props.direction === 'horizontal') {\n      const left = linkRect.left - anchorRect.left\n      markerStyle.value = {\n        left: `${left}px`,\n        width: `${linkRect.width}px`,\n        opacity: 1,\n      }\n    } else {\n      const top =\n        linkRect.top -\n        anchorRect.top +\n        (linkRect.height - markerRect.height) / 2\n      markerStyle.value = {\n        top: `${top}px`,\n        opacity: 1,\n      }\n    }\n  })\n}\n\nwatch(currentAnchor, updateMarkerStyle)\nwatch(() => slots.default?.(), updateMarkerStyle)\n\nonMounted(() => {\n  getContainer()\n  const hash = decodeURIComponent(window.location.hash)\n  const target = getElement(hash)\n  if (target) {\n    scrollTo(hash)\n  } else {\n    handleScroll()\n  }\n})\n\nwatch(\n  () => props.container,\n  () => {\n    getContainer()\n  }\n)\n\nprovide(anchorKey, {\n  ns,\n  direction: props.direction,\n  currentAnchor,\n  addLink,\n  removeLink,\n  handleClick,\n})\n\ndefineExpose({\n  scrollTo,\n})\n</script>\n"],"mappings":""}