UNPKG

hongluan-ui

Version:
233 lines (226 loc) 7 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var vue = require('vue'); var shared = require('@vue/shared'); var computeScrollIntoView = require('compute-scroll-into-view'); require('../../../utils/index.js'); require('../../../hooks/index.js'); var utils = require('./utils.js'); var types$1 = require('./types.js'); var pluginVue_exportHelper = require('../../../_virtual/plugin-vue_export-helper.js'); var types = require('../../../utils/types.js'); var index = require('../../../hooks/use-namespace/index.js'); var raf = require('../../../utils/raf.js'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var computeScrollIntoView__default = /*#__PURE__*/_interopDefaultLegacy(computeScrollIntoView); const BOUNDARY_POSITIONS = ["start", "end", "center", "nearest"]; const _sfc_main = vue.defineComponent({ name: "Anchor", props: { boundary: { type: [Number, String], default: "start", validator: (value) => { return types.isNumber(value) || BOUNDARY_POSITIONS.includes(value); } }, sliderLess: { type: Boolean, default: false }, scrollContainer: { type: [String, Object] }, changeHash: { type: Boolean, default: true }, smooth: { type: Boolean, default: true }, right: Boolean }, emits: [ "select", "change" ], setup(props, { emit }) { const { namespace } = index.useNamespace("anchor"); const anchorRef = vue.ref(null); const lineSliderRef = vue.ref(null); const links = vue.reactive({}); const currentLink = vue.ref(""); const isScrolling = vue.ref(false); const addLink = (hash, node) => { if (!hash) return; links[hash] = node; }; const removeLink = (hash) => { delete links[hash]; }; const getContainer = (targetContainer) => { let scrollContainer = targetContainer; if (!scrollContainer) { scrollContainer = props.scrollContainer; } if (shared.isString(scrollContainer)) { return utils.findNode(document, scrollContainer); } return scrollContainer || window; }; const getContainerElement = () => { var _a; if (shared.isString(props.scrollContainer)) { return utils.findNode(document, props.scrollContainer); } if (props.scrollContainer && props.scrollContainer !== window) { return props.scrollContainer; } return (_a = document.documentElement) != null ? _a : document.body; }; const handleClick = (e, hash) => { if (!props.changeHash) { e.preventDefault(); } handleAnchorChange(hash); scrollIntoView(hash); emit("select", hash, currentLink.value); }; const scrollIntoView = (hash) => { if (!hash) return; try { const node = utils.findNode(document, hash); if (!node) return; let block = props.boundary; let diff = 0; if (types.isNumber(props.boundary)) { block = "start"; diff = props.boundary; } const actions = computeScrollIntoView__default["default"](node, { block }); if (!actions.length) return; const { el, top } = actions[0]; const targetTop = top - diff; utils.slide(el, targetTop, () => { isScrolling.value = false; }); isScrolling.value = true; } catch (e) { console.error(e); } }; const handleScroll = raf.throttleByRaf(() => { if (isScrolling.value) return; const element = getFirstInViewportEle(); if (element && element.id) { const hash = `#${element.id}`; handleAnchorChange(hash); } }); const handleAnchorChange = (hash) => { if (!hash) return; if (!links[hash] && anchorRef.value) { const node = utils.findNode(anchorRef.value, `a[data-href='${hash}']`); if (!node) return; links[hash] = node; } if (hash !== currentLink.value) { currentLink.value = hash; vue.nextTick(() => { emit("change", hash); }); } }; const getFirstInViewportEle = () => { const boundary = types.isNumber(props.boundary) ? props.boundary : 0; const container = getContainerElement(); const containerRect = container.getBoundingClientRect(); const { clientHeight } = document.documentElement; const ele = getContainer(); for (const hash of Object.keys(links)) { const node = utils.findNode(document, hash); if (node) { const { top } = node.getBoundingClientRect(); if (ele === window) { const offsetTop = top - boundary; if (offsetTop >= 0 && offsetTop <= clientHeight / 2) { return node; } } else { const offsetTop = top - containerRect.top - boundary; if (offsetTop >= 0 && offsetTop <= containerRect.height / 2) { return node; } } } } return void 0; }; vue.onMounted(() => { handleScroll(); bindScrollEvent(); }); vue.onBeforeUnmount(() => { unbindScrollEvent(); }); vue.watch(currentLink, () => { const link = links[currentLink.value]; if (!props.sliderLess && link && lineSliderRef.value) { lineSliderRef.value.style.top = `${link.offsetTop}px`; } }); const bindScrollEvent = (scrollContainer) => { getContainer(scrollContainer).addEventListener("scroll", handleScroll); }; const unbindScrollEvent = (scrollContainer) => { getContainer(scrollContainer).removeEventListener("scroll", handleScroll); }; vue.provide(types$1.anchorInjectionKey, vue.reactive({ name: "Anchor", currentLink, addLink, removeLink, handleClick })); const cls = vue.computed(() => [ namespace.value, { "slider-less": props.sliderLess, "right": props.right } ]); return { cls, anchorRef, lineSliderRef }; } }); function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("div", { ref: "anchorRef", class: vue.normalizeClass(_ctx.cls) }, [ !_ctx.sliderLess ? (vue.openBlock(), vue.createElementBlock("div", { key: 0, ref: "lineSliderRef", class: vue.normalizeClass(`slider-bar`) }, null, 512)) : vue.createCommentVNode("v-if", true), vue.createElementVNode("div", { class: vue.normalizeClass(`anchor-list`) }, [ vue.renderSlot(_ctx.$slots, "default") ]) ], 2); } var Anchor = /* @__PURE__ */ pluginVue_exportHelper["default"](_sfc_main, [["render", _sfc_render]]); exports["default"] = Anchor; //# sourceMappingURL=anchor.js.map