Anchor.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  2. import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
  3. import { createVNode as _createVNode } from "vue";
  4. import { defineComponent, nextTick, onBeforeUnmount, onMounted, onUpdated, reactive, ref, computed } from 'vue';
  5. import classNames from '../_util/classNames';
  6. import addEventListener from '../vc-util/Dom/addEventListener';
  7. import Affix from '../affix';
  8. import scrollTo from '../_util/scrollTo';
  9. import getScroll from '../_util/getScroll';
  10. import useConfigInject from '../_util/hooks/useConfigInject';
  11. import useProvideAnchor from './context';
  12. function getDefaultContainer() {
  13. return window;
  14. }
  15. function getOffsetTop(element, container) {
  16. if (!element.getClientRects().length) {
  17. return 0;
  18. }
  19. var rect = element.getBoundingClientRect();
  20. if (rect.width || rect.height) {
  21. if (container === window) {
  22. container = element.ownerDocument.documentElement;
  23. return rect.top - container.clientTop;
  24. }
  25. return rect.top - container.getBoundingClientRect().top;
  26. }
  27. return rect.top;
  28. }
  29. var sharpMatcherRegx = /#([\S ]+)$/;
  30. export var anchorProps = function anchorProps() {
  31. return {
  32. prefixCls: String,
  33. offsetTop: Number,
  34. bounds: Number,
  35. affix: {
  36. type: Boolean,
  37. default: true
  38. },
  39. showInkInFixed: {
  40. type: Boolean,
  41. default: false
  42. },
  43. getContainer: Function,
  44. wrapperClass: String,
  45. wrapperStyle: {
  46. type: Object,
  47. default: undefined
  48. },
  49. getCurrentAnchor: Function,
  50. targetOffset: Number,
  51. onChange: Function,
  52. onClick: Function
  53. };
  54. };
  55. export default defineComponent({
  56. compatConfig: {
  57. MODE: 3
  58. },
  59. name: 'AAnchor',
  60. inheritAttrs: false,
  61. props: anchorProps(),
  62. setup: function setup(props, _ref) {
  63. var emit = _ref.emit,
  64. attrs = _ref.attrs,
  65. slots = _ref.slots,
  66. expose = _ref.expose;
  67. var _useConfigInject = useConfigInject('anchor', props),
  68. prefixCls = _useConfigInject.prefixCls,
  69. getTargetContainer = _useConfigInject.getTargetContainer,
  70. direction = _useConfigInject.direction;
  71. var inkNodeRef = ref();
  72. var anchorRef = ref();
  73. var state = reactive({
  74. links: [],
  75. scrollContainer: null,
  76. scrollEvent: null,
  77. animating: false
  78. });
  79. var activeLink = ref(null);
  80. var getContainer = computed(function () {
  81. var getContainer = props.getContainer;
  82. return getContainer || getTargetContainer.value || getDefaultContainer;
  83. });
  84. // func...
  85. var getCurrentAnchor = function getCurrentAnchor() {
  86. var offsetTop = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
  87. var bounds = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 5;
  88. var linkSections = [];
  89. var container = getContainer.value();
  90. state.links.forEach(function (link) {
  91. var sharpLinkMatch = sharpMatcherRegx.exec(link.toString());
  92. if (!sharpLinkMatch) {
  93. return;
  94. }
  95. var target = document.getElementById(sharpLinkMatch[1]);
  96. if (target) {
  97. var top = getOffsetTop(target, container);
  98. if (top < offsetTop + bounds) {
  99. linkSections.push({
  100. link: link,
  101. top: top
  102. });
  103. }
  104. }
  105. });
  106. if (linkSections.length) {
  107. var maxSection = linkSections.reduce(function (prev, curr) {
  108. return curr.top > prev.top ? curr : prev;
  109. });
  110. return maxSection.link;
  111. }
  112. return '';
  113. };
  114. var setCurrentActiveLink = function setCurrentActiveLink(link) {
  115. var getCurrentAnchor = props.getCurrentAnchor;
  116. if (activeLink.value === link) {
  117. return;
  118. }
  119. activeLink.value = typeof getCurrentAnchor === 'function' ? getCurrentAnchor() : link;
  120. emit('change', link);
  121. };
  122. var handleScrollTo = function handleScrollTo(link) {
  123. var offsetTop = props.offsetTop,
  124. targetOffset = props.targetOffset;
  125. setCurrentActiveLink(link);
  126. var container = getContainer.value();
  127. var scrollTop = getScroll(container, true);
  128. var sharpLinkMatch = sharpMatcherRegx.exec(link);
  129. if (!sharpLinkMatch) {
  130. return;
  131. }
  132. var targetElement = document.getElementById(sharpLinkMatch[1]);
  133. if (!targetElement) {
  134. return;
  135. }
  136. var eleOffsetTop = getOffsetTop(targetElement, container);
  137. var y = scrollTop + eleOffsetTop;
  138. y -= targetOffset !== undefined ? targetOffset : offsetTop || 0;
  139. state.animating = true;
  140. scrollTo(y, {
  141. callback: function callback() {
  142. state.animating = false;
  143. },
  144. getContainer: getContainer.value
  145. });
  146. };
  147. expose({
  148. scrollTo: handleScrollTo
  149. });
  150. var handleScroll = function handleScroll() {
  151. if (state.animating) {
  152. return;
  153. }
  154. var offsetTop = props.offsetTop,
  155. bounds = props.bounds,
  156. targetOffset = props.targetOffset;
  157. var currentActiveLink = getCurrentAnchor(targetOffset !== undefined ? targetOffset : offsetTop || 0, bounds);
  158. setCurrentActiveLink(currentActiveLink);
  159. };
  160. var updateInk = function updateInk() {
  161. var linkNode = anchorRef.value.getElementsByClassName("".concat(prefixCls.value, "-link-title-active"))[0];
  162. if (linkNode) {
  163. inkNodeRef.value.style.top = "".concat(linkNode.offsetTop + linkNode.clientHeight / 2 - 4.5, "px");
  164. }
  165. };
  166. useProvideAnchor({
  167. registerLink: function registerLink(link) {
  168. if (!state.links.includes(link)) {
  169. state.links.push(link);
  170. }
  171. },
  172. unregisterLink: function unregisterLink(link) {
  173. var index = state.links.indexOf(link);
  174. if (index !== -1) {
  175. state.links.splice(index, 1);
  176. }
  177. },
  178. activeLink: activeLink,
  179. scrollTo: handleScrollTo,
  180. handleClick: function handleClick(e, info) {
  181. emit('click', e, info);
  182. }
  183. });
  184. onMounted(function () {
  185. nextTick(function () {
  186. var container = getContainer.value();
  187. state.scrollContainer = container;
  188. state.scrollEvent = addEventListener(state.scrollContainer, 'scroll', handleScroll);
  189. handleScroll();
  190. });
  191. });
  192. onBeforeUnmount(function () {
  193. if (state.scrollEvent) {
  194. state.scrollEvent.remove();
  195. }
  196. });
  197. onUpdated(function () {
  198. if (state.scrollEvent) {
  199. var currentContainer = getContainer.value();
  200. if (state.scrollContainer !== currentContainer) {
  201. state.scrollContainer = currentContainer;
  202. state.scrollEvent.remove();
  203. state.scrollEvent = addEventListener(state.scrollContainer, 'scroll', handleScroll);
  204. handleScroll();
  205. }
  206. }
  207. updateInk();
  208. });
  209. return function () {
  210. var _slots$default;
  211. var offsetTop = props.offsetTop,
  212. affix = props.affix,
  213. showInkInFixed = props.showInkInFixed;
  214. var pre = prefixCls.value;
  215. var inkClass = classNames("".concat(pre, "-ink-ball"), {
  216. visible: activeLink.value
  217. });
  218. var wrapperClass = classNames(props.wrapperClass, "".concat(pre, "-wrapper"), _defineProperty({}, "".concat(pre, "-rtl"), direction.value === 'rtl'));
  219. var anchorClass = classNames(pre, _defineProperty({}, "".concat(pre, "-fixed"), !affix && !showInkInFixed));
  220. var wrapperStyle = _objectSpread({
  221. maxHeight: offsetTop ? "calc(100vh - ".concat(offsetTop, "px)") : '100vh'
  222. }, props.wrapperStyle);
  223. var anchorContent = _createVNode("div", {
  224. "class": wrapperClass,
  225. "style": wrapperStyle,
  226. "ref": anchorRef
  227. }, [_createVNode("div", {
  228. "class": anchorClass
  229. }, [_createVNode("div", {
  230. "class": "".concat(pre, "-ink")
  231. }, [_createVNode("span", {
  232. "class": inkClass,
  233. "ref": inkNodeRef
  234. }, null)]), (_slots$default = slots.default) === null || _slots$default === void 0 ? void 0 : _slots$default.call(slots)])]);
  235. return !affix ? anchorContent : _createVNode(Affix, _objectSpread(_objectSpread({}, attrs), {}, {
  236. "offsetTop": offsetTop,
  237. "target": getContainer.value
  238. }), {
  239. default: function _default() {
  240. return [anchorContent];
  241. }
  242. });
  243. };
  244. }
  245. });