| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- import { defineComponent, useSlots, ref, computed, watch, onMounted, provide, openBlock, createElementBlock, normalizeClass, unref, normalizeStyle, createCommentVNode, createElementVNode, renderSlot, nextTick } from 'vue';
- import { useEventListener } from '@vueuse/core';
- import { anchorProps, anchorEmits } from './anchor.mjs';
- import { anchorKey } from './constants.mjs';
- import _export_sfc from '../../../_virtual/plugin-vue_export-helper.mjs';
- import { getElement } from '../../../utils/dom/element.mjs';
- import { throttleByRaf } from '../../../utils/throttleByRaf.mjs';
- import { getScrollElement, animateScrollTo, getScrollTop } from '../../../utils/dom/scroll.mjs';
- import { getOffsetTopDistance } from '../../../utils/dom/position.mjs';
- import { useNamespace } from '../../../hooks/use-namespace/index.mjs';
- import { isWindow, isUndefined } from '../../../utils/types.mjs';
- import { CHANGE_EVENT } from '../../../constants/event.mjs';
- const __default__ = defineComponent({
- name: "ElAnchor"
- });
- const _sfc_main = /* @__PURE__ */ defineComponent({
- ...__default__,
- props: anchorProps,
- emits: anchorEmits,
- setup(__props, { expose, emit }) {
- const props = __props;
- const slots = useSlots();
- const currentAnchor = ref("");
- const markerStyle = ref({});
- const anchorRef = ref(null);
- const markerRef = ref(null);
- const containerEl = ref();
- const links = {};
- let isScrolling = false;
- let currentScrollTop = 0;
- const ns = useNamespace("anchor");
- const cls = computed(() => [
- ns.b(),
- props.type === "underline" ? ns.m("underline") : "",
- ns.m(props.direction)
- ]);
- const addLink = (state) => {
- links[state.href] = state.el;
- };
- const removeLink = (href) => {
- delete links[href];
- };
- const setCurrentAnchor = (href) => {
- const activeHref = currentAnchor.value;
- if (activeHref !== href) {
- currentAnchor.value = href;
- emit(CHANGE_EVENT, href);
- }
- };
- let clearAnimate = null;
- const scrollToAnchor = (href) => {
- if (!containerEl.value)
- return;
- const target = getElement(href);
- if (!target)
- return;
- if (clearAnimate)
- clearAnimate();
- isScrolling = true;
- const scrollEle = getScrollElement(target, containerEl.value);
- const distance = getOffsetTopDistance(target, scrollEle);
- const max = scrollEle.scrollHeight - scrollEle.clientHeight;
- const to = Math.min(distance - props.offset, max);
- clearAnimate = animateScrollTo(containerEl.value, currentScrollTop, to, props.duration, () => {
- setTimeout(() => {
- isScrolling = false;
- }, 20);
- });
- };
- const scrollTo = (href) => {
- if (href) {
- setCurrentAnchor(href);
- scrollToAnchor(href);
- }
- };
- const handleClick = (e, href) => {
- emit("click", e, href);
- scrollTo(href);
- };
- const handleScroll = throttleByRaf(() => {
- if (containerEl.value) {
- currentScrollTop = getScrollTop(containerEl.value);
- }
- const currentHref = getCurrentHref();
- if (isScrolling || isUndefined(currentHref))
- return;
- setCurrentAnchor(currentHref);
- });
- const getCurrentHref = () => {
- if (!containerEl.value)
- return;
- const scrollTop = getScrollTop(containerEl.value);
- const anchorTopList = [];
- for (const href of Object.keys(links)) {
- const target = getElement(href);
- if (!target)
- continue;
- const scrollEle = getScrollElement(target, containerEl.value);
- const distance = getOffsetTopDistance(target, scrollEle);
- anchorTopList.push({
- top: distance - props.offset - props.bound,
- href
- });
- }
- anchorTopList.sort((prev, next) => prev.top - next.top);
- for (let i = 0; i < anchorTopList.length; i++) {
- const item = anchorTopList[i];
- const next = anchorTopList[i + 1];
- if (i === 0 && scrollTop === 0) {
- return props.selectScrollTop ? item.href : "";
- }
- if (item.top <= scrollTop && (!next || next.top > scrollTop)) {
- return item.href;
- }
- }
- };
- const getContainer = () => {
- const el = getElement(props.container);
- if (!el || isWindow(el)) {
- containerEl.value = window;
- } else {
- containerEl.value = el;
- }
- };
- useEventListener(containerEl, "scroll", handleScroll);
- const updateMarkerStyle = () => {
- nextTick(() => {
- if (!anchorRef.value || !markerRef.value || !currentAnchor.value) {
- markerStyle.value = {};
- return;
- }
- const currentLinkEl = links[currentAnchor.value];
- if (!currentLinkEl) {
- markerStyle.value = {};
- return;
- }
- const anchorRect = anchorRef.value.getBoundingClientRect();
- const markerRect = markerRef.value.getBoundingClientRect();
- const linkRect = currentLinkEl.getBoundingClientRect();
- if (props.direction === "horizontal") {
- const left = linkRect.left - anchorRect.left;
- markerStyle.value = {
- left: `${left}px`,
- width: `${linkRect.width}px`,
- opacity: 1
- };
- } else {
- const top = linkRect.top - anchorRect.top + (linkRect.height - markerRect.height) / 2;
- markerStyle.value = {
- top: `${top}px`,
- opacity: 1
- };
- }
- });
- };
- watch(currentAnchor, updateMarkerStyle);
- watch(() => {
- var _a;
- return (_a = slots.default) == null ? void 0 : _a.call(slots);
- }, updateMarkerStyle);
- onMounted(() => {
- getContainer();
- const hash = decodeURIComponent(window.location.hash);
- const target = getElement(hash);
- if (target) {
- scrollTo(hash);
- } else {
- handleScroll();
- }
- });
- watch(() => props.container, () => {
- getContainer();
- });
- provide(anchorKey, {
- ns,
- direction: props.direction,
- currentAnchor,
- addLink,
- removeLink,
- handleClick
- });
- expose({
- scrollTo
- });
- return (_ctx, _cache) => {
- return openBlock(), createElementBlock("div", {
- ref_key: "anchorRef",
- ref: anchorRef,
- class: normalizeClass(unref(cls))
- }, [
- _ctx.marker ? (openBlock(), createElementBlock("div", {
- key: 0,
- ref_key: "markerRef",
- ref: markerRef,
- class: normalizeClass(unref(ns).e("marker")),
- style: normalizeStyle(markerStyle.value)
- }, null, 6)) : createCommentVNode("v-if", true),
- createElementVNode("div", {
- class: normalizeClass(unref(ns).e("list"))
- }, [
- renderSlot(_ctx.$slots, "default")
- ], 2)
- ], 2);
- };
- }
- });
- var Anchor = /* @__PURE__ */ _export_sfc(_sfc_main, [["__file", "anchor.vue"]]);
- export { Anchor as default };
- //# sourceMappingURL=anchor2.mjs.map
|