@extclp/vexip-ui
Version:
A Vue 3 UI library, Highly customizability, full TypeScript, performance pretty good
1 lines • 16.2 kB
Source Map (JSON)
{"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":";;;;;;;;;;;;;;;;;AAgCM,UAAAA,IAAKC,GAAc,QAAQ,GAG3BC,IAAQC,GAAS,UADRC,GAC0B;AAAA,MACvC,QAAQ;AAAA,QACN,SAAS;AAAA,QACT,QAAQ;AAAA,MACV;AAAA,MACA,QAAQ;AAAA,QACN,SAAS;AAAA,QACT,QAAQ;AAAA,MACV;AAAA,MACA,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,gBAAgB;AAAA,MAChB,kBAAkB,MAAMJ,EAAG,GAAG,MAAM;AAAA,MACpC,SAAS;AAAA,QACP,SAAS,MAAM,CAAC;AAAA,QAChB,QAAQ;AAAA,MACV;AAAA,MACA,UAAU;AAAA,MACV,aAAa;AAAA,MACb,OAAO,OAAO,CAAC;AAAA,IAAA,CAChB,GAEKK,IAAOC,GAIPC,IAAgBC,EAAIN,EAAM,MAAM,GAChCO,IAAYD,EAAI,EAAK,GACrBE,IAAYF,EAAI,CAAC,GACjBG,wBAAiB,IAAqB,GAEtCC,IAAUJ,EAAiB;AAE7B,QAAAK,GAEAC,IAAc,IACdC,IAAyC,MACzCC,IAA8B,MAC9BC,IAAgB;AAEpB,IAAIC,KAAY,CAACX,EAAc,SAASL,EAAM,aAC9BK,EAAA,QAAQ,mBAAmB,SAAS,IAAI,IAGxDY;AAAA,MACEC;AAAA,MACAC,EAAS;AAAA,QACP,eAAAd;AAAA,QACA,cAAAe;AAAA,QACA,cAAAC;AAAA,QACA,cAAAC;AAAA,MACD,CAAA;AAAA,IACH,GAEAC;AAAA,MACE,MAAMvB,EAAM;AAAA,MACZ,CAASwB,MAAA;AACP,QAAAnB,EAAc,QAAQmB;AAAA,MAAA;AAAA,IAE1B,GACMD,EAAA,MAAMvB,EAAM,QAAQyB,CAAe,GAEzCC,EAAU,MAAM;AACE,MAAAD,EAAA,GACME,EAAA;AAAA,IAAA,CACvB,GAEDC,EAAgB,MAAM;AACL,MAAAC,EAAA,GACf,aAAalB,CAAK;AAAA,IAAA,CACnB;AAED,aAASS,EAAaU,GAAwB;AAC5C,MAAArB,EAAW,IAAIqB,CAAK,GACdA,EAAA,SAASzB,EAAc,UAAUyB,EAAM;AAAA,IAAA;AAG/C,aAAST,EAAaS,GAAwB;AAC5C,MAAArB,EAAW,OAAOqB,CAAK;AAAA,IAAA;AAGzB,UAAMC,IAAWC,EAAmB;AAEpC,aAASP,IAAkB;AACV,MAAAI,EAAA,GACfb,KACEiB,EAAS,MAAM;;AACb,cAAMC,IAAkBlC,EAAM;AAEd,QAAAe,IAAA;AAEhB,YAAIoB,IAA+D,MAC/DC,IAAU;AA4Bd,YA1BI,OAAOF,KAAW,WAChBA,EAAO,WAAW,MAAM,KAChBE,IAAAF,EAAO,UAAU,CAAC,GAC5BE,IAAUA,KAAW,YACZ,CAAC,UAAU,YAAY,MAAM,EAAE,SAASF,CAAM,IAC1CC,IAAA,SACJD,MAAW,SACpBC,IAAa,SAAS,OACbD,MAAW,SACpBC,IAAaJ,EAAS,OAETI,IAAA,SAAS,cAAcD,CAAM,IAEnC,OAAOA,KAAW,aAC3BC,IAAaD,EAAO,IACXG,EAAUH,CAAM,MACZC,IAAAD,IAGXC,MAAe,UAAUE,EAAUF,CAAU,IACjCvB,IAAA,KAEAA,IAAA,IAIXA;AA0CS,UAAAC,IAAAsB,GACFtB,EAAA,iBAAiB,UAAUyB,CAAqB;AAAA,aA3C1C;AAKhB,eAHaH,IAAAA,GACbA,IAAaI,GAAQJ,KAAA,gBAAAA,EAAY,KAAK,IAAIA,IAAaJ,EAAS,QAEzDI,KAAY;AACX,kBAAAK,KAAOC,IAAAN,EAAW,SAAX,gBAAAM,EAAiB;AAE1B,gBAAAD,MAAS,YAAYA,MAAS,gBAAgB;AAChD,cAAA1B,IAAW4B,GAAa,EAAE,WAAWP,EAAA,CAAmB;AAExD;AAAA,YAAA;AAGI,kBAAAQ,KAAUC,IAAAT,EAAW,SAAX,gBAAAS,EAAkBR;AAElC,gBAAIO,GAAS;AACP,cAAAN,EAAUM,CAAO,KACL/B,IAAA,IACFC,IAAA8B,KAED7B,IAAA6B;AAGb;AAAA,YAAA;AAGF,YAAAR,IAAaA,EAAW;AAAA,UAAA;AAG1B,UAAIrB,KACFA,EAAS,kBAAkBwB,CAAqB,GAChDzB,IAAYC,EAAS,OACXD,MACID,IAAA,IACFC,KAAAgC,KAAAC,IAAAf,EAAS,WAAT,gBAAAe,EAAiB,UAAjB,gBAAAD,EAAwB,MAGlCjC,KAAeC,KACPA,EAAA,iBAAiB,UAAUyB,CAAqB;AAAA,QAC5D;AAAA,MAIF,CACD;AAAA,IAAA;AAGL,aAASS,IAAiB;AACpB,aAAClC,IAEEA,MAAc,SAAS,SAAS,kBAAmBA,IAFnC;AAAA,IAEmC;AAG5D,aAASmC,EAAmBC,GAAmB;AAC7C,UAAI,CAACxC,EAAW,QAAQ,CAACI,EAAW;AAE9B,YAAAqC,IAAeH,IAAkB,WACjCI,IAAiD,CAAC;AAEpD,UAAAC,IAASH,IAAYjD,EAAM;AAE/B,MAAIY,MACQwC,KAAAF,IAGZzC,EAAW,QAAQ,CAASqB,MAAA;AAC1B,cAAMuB,IAAKvB,EAAM;AAEjB,YAAI,CAACuB,EAAG,WAAW,GAAG,EAAG;AAEnB,cAAAC,IAAU,SAAS,cAAcD,CAAE;AAEzC,QAAIC,KACFH,EAAW,KAAK;AAAA,UACd,MAAME;AAAA,UACN,QAAQC,EAAQ;AAAA,QAAA,CACjB;AAAA,MACH,CACD,GAEDH,EAAW,KAAK,CAACI,GAAMC,MAASD,EAAK,SAASC,EAAK,MAAM,GACzDL,EAAW,KAAK;AAAA,QACd,MAAM;AAAA,QACN,QAAQ;AAAA,MAAA,CACT;AAED,UAAIM,IAAc;AAET,eAAA,IAAI,GAAGC,IAAMP,EAAW,SAAS,GAAG,IAAIO,GAAK,EAAE,GAAG;AACnD,cAAAC,IAAUR,EAAW,CAAC,GACtBK,IAAOL,EAAW,IAAI,CAAC;AAE7B,YAAIQ,EAAQ,UAAUP,KAAUI,EAAK,SAASJ,GAAQ;AACpD,UAAAK,IAAcE,EAAQ;AAEtB;AAAA,QAAA;AAAA,MACF;AAGc,MAAA5C,IAAAkC,GAEZ5C,EAAc,UAAUoD,MAC1BpD,EAAc,QAAQoD,GACtBtD,EAAK,iBAAiBsD,CAAW,GACvBG,GAAA5D,EAAM,UAAUyD,CAAW;AAAA,IACvC;AAGF,aAASnB,EAAsBuB,GAAc;AAC3C,UAAItD,EAAU,MAAO;AAErB,YAAM0C,IAAYrC,KAEXiD,EAAM,WAAW,UAAUA,EAAM,WAAW,WACzC,SAAS,kBACTA,EAAM,QACV,YACDA,EAAqB;AAE1B,MAAAb,EAAmBC,CAAS,GACNtB,EAAA;AAAA,IAAA;AAGxB,aAASE,IAAiB;AACxB,MAAIf,MACFA,EAAS,qBAAqBwB,CAAqB,GACxCxB,IAAA,OAGTD,KACQA,EAAA,oBAAoB,UAAUyB,CAAqB;AAAA,IAC/D;AAGF,aAAShB,EAAawC,GAAc;AAClC,UACG,CAAC9D,EAAM,eAAe8D,MAASzD,EAAc,SAC9C,CAACyD,EAAK,WAAW,GAAG,KACpBA,EAAK,SAAS;AAEd;AAGI,YAAAR,IAAU,SAAS,cAAcQ,CAAI;AAE3C,UAAI,CAACR,EAAS;AAEd,mBAAa3C,CAAK,GAElBJ,EAAU,QAAQ;AAElB,YAAMwD,IAAaT,EAAQ,WACrBU,IAAW,KAAK,IAAIhE,EAAM,gBAAgB,CAAC;AAEjD,UAAIY,KAAeC,GAAW;AAC5B,cAAMoD,IAAclB,EAAe,GAE7BmB,IAAK,KAAK;AAAA,UACdH,IAAaE,EAAY,YAAYjE,EAAM;AAAA,UAC3CiE,EAAY,eAAeA,EAAY;AAAA,QACzC;AAEA,QAAAE,GAAgBF,GAAalD,GAAemD,GAAIF,GAAU,MAAM;AAC9D,UAAArD,IAAQ,WAAW,MAAM;AACvB,YAAAJ,EAAU,QAAQ;AAAA,aACjB,EAAE;AAAA,QAAA,CACN,GACDyC,EAAmBkB,CAAE,GACCvC,EAAA;AAAA,iBACbb,GAAU;AACnB,cAAM,CAACsD,GAAKC,CAAG,IAAIvD,EAAS,gBAAgB,GACtCwD,IAAU,KAAK,IAAI,KAAK,IAAIP,IAAa/D,EAAM,QAAQqE,CAAG,GAAGD,CAAG;AAEtE,QAAAtD,EAAS,SAAS,GAAGwD,GAASN,CAAQ,EAAE,KAAK,MAAM;AACjD,UAAArD,IAAQ,WAAW,MAAM;AACvB,YAAAJ,EAAU,QAAQ;AAAA,UAAA,GACjByD,IAAW,EAAE;AAAA,QAAA,CACjB,GAEDhB,EAAmBsB,CAAO,GACJ3C,EAAA;AAAA,MAAA;AAEtB,QAAApB,EAAU,QAAQ;AAGhB,MAAAS,KAAYhB,EAAM,YAAY,aAChC,SAAS,OAAO,mBAAmBK,EAAc,MAAM,QAAQ,MAAM,EAAE,CAAC;AAAA,IAC1E;AAGF,aAASsB,IAAwB;;AAC/B,YAAM8B,IAAc,MAAM,KAAKhD,CAAU,EAAE;AAAA,QACzC,CAASqB,MAAAA,EAAM,MAAMA,EAAM,OAAOzB,EAAc;AAAA,MAClD;AAEA,UAAIoD,KAAA,QAAAA,EAAa,IAAI;AACb,cAAAc,IAAWd,EAAY,GAAG,sBAAsB,GAChDe,MAAa/B,IAAA/B,EAAQ,UAAR,gBAAA+B,EAAe,wBAAwB,QAAO;AAEjE,QAAAjC,EAAU,QAAQ+D,EAAS,MAAMC,IAAaD,EAAS,SAAS,IAAI;AAAA,MAAA;AAAA,IACtE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}