6c0a9613f20c55bccea363c3159fb12722524b5bf8b0ff9da9f385ef4e37bb196af01daa3ec1b0a8106248d1cd4e8c9dc63b72aac73650ddbe84f4b52eedbc 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', { value: true });
  3. var vue = require('vue');
  4. var core = require('@vueuse/core');
  5. var anchor = require('./anchor.js');
  6. var constants = require('./constants.js');
  7. var pluginVue_exportHelper = require('../../../_virtual/plugin-vue_export-helper.js');
  8. var element = require('../../../utils/dom/element.js');
  9. var throttleByRaf = require('../../../utils/throttleByRaf.js');
  10. var scroll = require('../../../utils/dom/scroll.js');
  11. var position = require('../../../utils/dom/position.js');
  12. var index = require('../../../hooks/use-namespace/index.js');
  13. var types = require('../../../utils/types.js');
  14. var event = require('../../../constants/event.js');
  15. const __default__ = vue.defineComponent({
  16. name: "ElAnchor"
  17. });
  18. const _sfc_main = /* @__PURE__ */ vue.defineComponent({
  19. ...__default__,
  20. props: anchor.anchorProps,
  21. emits: anchor.anchorEmits,
  22. setup(__props, { expose, emit }) {
  23. const props = __props;
  24. const slots = vue.useSlots();
  25. const currentAnchor = vue.ref("");
  26. const markerStyle = vue.ref({});
  27. const anchorRef = vue.ref(null);
  28. const markerRef = vue.ref(null);
  29. const containerEl = vue.ref();
  30. const links = {};
  31. let isScrolling = false;
  32. let currentScrollTop = 0;
  33. const ns = index.useNamespace("anchor");
  34. const cls = vue.computed(() => [
  35. ns.b(),
  36. props.type === "underline" ? ns.m("underline") : "",
  37. ns.m(props.direction)
  38. ]);
  39. const addLink = (state) => {
  40. links[state.href] = state.el;
  41. };
  42. const removeLink = (href) => {
  43. delete links[href];
  44. };
  45. const setCurrentAnchor = (href) => {
  46. const activeHref = currentAnchor.value;
  47. if (activeHref !== href) {
  48. currentAnchor.value = href;
  49. emit(event.CHANGE_EVENT, href);
  50. }
  51. };
  52. let clearAnimate = null;
  53. const scrollToAnchor = (href) => {
  54. if (!containerEl.value)
  55. return;
  56. const target = element.getElement(href);
  57. if (!target)
  58. return;
  59. if (clearAnimate)
  60. clearAnimate();
  61. isScrolling = true;
  62. const scrollEle = scroll.getScrollElement(target, containerEl.value);
  63. const distance = position.getOffsetTopDistance(target, scrollEle);
  64. const max = scrollEle.scrollHeight - scrollEle.clientHeight;
  65. const to = Math.min(distance - props.offset, max);
  66. clearAnimate = scroll.animateScrollTo(containerEl.value, currentScrollTop, to, props.duration, () => {
  67. setTimeout(() => {
  68. isScrolling = false;
  69. }, 20);
  70. });
  71. };
  72. const scrollTo = (href) => {
  73. if (href) {
  74. setCurrentAnchor(href);
  75. scrollToAnchor(href);
  76. }
  77. };
  78. const handleClick = (e, href) => {
  79. emit("click", e, href);
  80. scrollTo(href);
  81. };
  82. const handleScroll = throttleByRaf.throttleByRaf(() => {
  83. if (containerEl.value) {
  84. currentScrollTop = scroll.getScrollTop(containerEl.value);
  85. }
  86. const currentHref = getCurrentHref();
  87. if (isScrolling || types.isUndefined(currentHref))
  88. return;
  89. setCurrentAnchor(currentHref);
  90. });
  91. const getCurrentHref = () => {
  92. if (!containerEl.value)
  93. return;
  94. const scrollTop = scroll.getScrollTop(containerEl.value);
  95. const anchorTopList = [];
  96. for (const href of Object.keys(links)) {
  97. const target = element.getElement(href);
  98. if (!target)
  99. continue;
  100. const scrollEle = scroll.getScrollElement(target, containerEl.value);
  101. const distance = position.getOffsetTopDistance(target, scrollEle);
  102. anchorTopList.push({
  103. top: distance - props.offset - props.bound,
  104. href
  105. });
  106. }
  107. anchorTopList.sort((prev, next) => prev.top - next.top);
  108. for (let i = 0; i < anchorTopList.length; i++) {
  109. const item = anchorTopList[i];
  110. const next = anchorTopList[i + 1];
  111. if (i === 0 && scrollTop === 0) {
  112. return props.selectScrollTop ? item.href : "";
  113. }
  114. if (item.top <= scrollTop && (!next || next.top > scrollTop)) {
  115. return item.href;
  116. }
  117. }
  118. };
  119. const getContainer = () => {
  120. const el = element.getElement(props.container);
  121. if (!el || types.isWindow(el)) {
  122. containerEl.value = window;
  123. } else {
  124. containerEl.value = el;
  125. }
  126. };
  127. core.useEventListener(containerEl, "scroll", handleScroll);
  128. const updateMarkerStyle = () => {
  129. vue.nextTick(() => {
  130. if (!anchorRef.value || !markerRef.value || !currentAnchor.value) {
  131. markerStyle.value = {};
  132. return;
  133. }
  134. const currentLinkEl = links[currentAnchor.value];
  135. if (!currentLinkEl) {
  136. markerStyle.value = {};
  137. return;
  138. }
  139. const anchorRect = anchorRef.value.getBoundingClientRect();
  140. const markerRect = markerRef.value.getBoundingClientRect();
  141. const linkRect = currentLinkEl.getBoundingClientRect();
  142. if (props.direction === "horizontal") {
  143. const left = linkRect.left - anchorRect.left;
  144. markerStyle.value = {
  145. left: `${left}px`,
  146. width: `${linkRect.width}px`,
  147. opacity: 1
  148. };
  149. } else {
  150. const top = linkRect.top - anchorRect.top + (linkRect.height - markerRect.height) / 2;
  151. markerStyle.value = {
  152. top: `${top}px`,
  153. opacity: 1
  154. };
  155. }
  156. });
  157. };
  158. vue.watch(currentAnchor, updateMarkerStyle);
  159. vue.watch(() => {
  160. var _a;
  161. return (_a = slots.default) == null ? void 0 : _a.call(slots);
  162. }, updateMarkerStyle);
  163. vue.onMounted(() => {
  164. getContainer();
  165. const hash = decodeURIComponent(window.location.hash);
  166. const target = element.getElement(hash);
  167. if (target) {
  168. scrollTo(hash);
  169. } else {
  170. handleScroll();
  171. }
  172. });
  173. vue.watch(() => props.container, () => {
  174. getContainer();
  175. });
  176. vue.provide(constants.anchorKey, {
  177. ns,
  178. direction: props.direction,
  179. currentAnchor,
  180. addLink,
  181. removeLink,
  182. handleClick
  183. });
  184. expose({
  185. scrollTo
  186. });
  187. return (_ctx, _cache) => {
  188. return vue.openBlock(), vue.createElementBlock("div", {
  189. ref_key: "anchorRef",
  190. ref: anchorRef,
  191. class: vue.normalizeClass(vue.unref(cls))
  192. }, [
  193. _ctx.marker ? (vue.openBlock(), vue.createElementBlock("div", {
  194. key: 0,
  195. ref_key: "markerRef",
  196. ref: markerRef,
  197. class: vue.normalizeClass(vue.unref(ns).e("marker")),
  198. style: vue.normalizeStyle(markerStyle.value)
  199. }, null, 6)) : vue.createCommentVNode("v-if", true),
  200. vue.createElementVNode("div", {
  201. class: vue.normalizeClass(vue.unref(ns).e("list"))
  202. }, [
  203. vue.renderSlot(_ctx.$slots, "default")
  204. ], 2)
  205. ], 2);
  206. };
  207. }
  208. });
  209. var Anchor = /* @__PURE__ */ pluginVue_exportHelper["default"](_sfc_main, [["__file", "anchor.vue"]]);
  210. exports["default"] = Anchor;
  211. //# sourceMappingURL=anchor2.js.map