index.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  2. import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
  3. import _extends from "@babel/runtime/helpers/esm/extends";
  4. import { createVNode as _createVNode } from "vue";
  5. import { defineComponent, ref, reactive, watch, onMounted, getCurrentInstance, computed, onUnmounted, onUpdated } from 'vue';
  6. import classNames from '../_util/classNames';
  7. import ResizeObserver from '../vc-resize-observer';
  8. import throttleByAnimationFrame from '../_util/throttleByAnimationFrame';
  9. import { withInstall } from '../_util/type';
  10. import { addObserveTarget, removeObserveTarget, getTargetRect, getFixedTop, getFixedBottom } from './utils';
  11. import useConfigInject from '../_util/hooks/useConfigInject';
  12. import omit from '../_util/omit';
  13. function getDefaultTarget() {
  14. return typeof window !== 'undefined' ? window : null;
  15. }
  16. var AffixStatus;
  17. (function (AffixStatus) {
  18. AffixStatus[AffixStatus["None"] = 0] = "None";
  19. AffixStatus[AffixStatus["Prepare"] = 1] = "Prepare";
  20. })(AffixStatus || (AffixStatus = {}));
  21. // Affix
  22. export var affixProps = function affixProps() {
  23. return {
  24. /**
  25. * 距离窗口顶部达到指定偏移量后触发
  26. */
  27. offsetTop: Number,
  28. /** 距离窗口底部达到指定偏移量后触发 */
  29. offsetBottom: Number,
  30. /** 设置 Affix 需要监听其滚动事件的元素,值为一个返回对应 DOM 元素的函数 */
  31. target: {
  32. type: Function,
  33. default: getDefaultTarget
  34. },
  35. prefixCls: String,
  36. /** 固定状态改变时触发的回调函数 */
  37. onChange: Function,
  38. onTestUpdatePosition: Function
  39. };
  40. };
  41. var Affix = defineComponent({
  42. compatConfig: {
  43. MODE: 3
  44. },
  45. name: 'AAffix',
  46. props: affixProps(),
  47. setup: function setup(props, _ref) {
  48. var slots = _ref.slots,
  49. emit = _ref.emit,
  50. expose = _ref.expose;
  51. var placeholderNode = ref();
  52. var fixedNode = ref();
  53. var state = reactive({
  54. affixStyle: undefined,
  55. placeholderStyle: undefined,
  56. status: AffixStatus.None,
  57. lastAffix: false,
  58. prevTarget: null,
  59. timeout: null
  60. });
  61. var currentInstance = getCurrentInstance();
  62. var offsetTop = computed(function () {
  63. return props.offsetBottom === undefined && props.offsetTop === undefined ? 0 : props.offsetTop;
  64. });
  65. var offsetBottom = computed(function () {
  66. return props.offsetBottom;
  67. });
  68. var measure = function measure() {
  69. var status = state.status,
  70. lastAffix = state.lastAffix;
  71. var target = props.target;
  72. if (status !== AffixStatus.Prepare || !fixedNode.value || !placeholderNode.value || !target) {
  73. return;
  74. }
  75. var targetNode = target();
  76. if (!targetNode) {
  77. return;
  78. }
  79. var newState = {
  80. status: AffixStatus.None
  81. };
  82. var targetRect = getTargetRect(targetNode);
  83. var placeholderRect = getTargetRect(placeholderNode.value);
  84. var fixedTop = getFixedTop(placeholderRect, targetRect, offsetTop.value);
  85. var fixedBottom = getFixedBottom(placeholderRect, targetRect, offsetBottom.value);
  86. if (fixedTop !== undefined) {
  87. newState.affixStyle = {
  88. position: 'fixed',
  89. top: fixedTop,
  90. width: placeholderRect.width + 'px',
  91. height: placeholderRect.height + 'px'
  92. };
  93. newState.placeholderStyle = {
  94. width: placeholderRect.width + 'px',
  95. height: placeholderRect.height + 'px'
  96. };
  97. } else if (fixedBottom !== undefined) {
  98. newState.affixStyle = {
  99. position: 'fixed',
  100. bottom: fixedBottom,
  101. width: placeholderRect.width + 'px',
  102. height: placeholderRect.height + 'px'
  103. };
  104. newState.placeholderStyle = {
  105. width: placeholderRect.width + 'px',
  106. height: placeholderRect.height + 'px'
  107. };
  108. }
  109. newState.lastAffix = !!newState.affixStyle;
  110. if (lastAffix !== newState.lastAffix) {
  111. emit('change', newState.lastAffix);
  112. }
  113. // update state
  114. _extends(state, newState);
  115. };
  116. var prepareMeasure = function prepareMeasure() {
  117. _extends(state, {
  118. status: AffixStatus.Prepare,
  119. affixStyle: undefined,
  120. placeholderStyle: undefined
  121. });
  122. currentInstance.update();
  123. // Test if `updatePosition` called
  124. if (process.env.NODE_ENV === 'test') {
  125. emit('testUpdatePosition');
  126. }
  127. };
  128. var updatePosition = throttleByAnimationFrame(function () {
  129. prepareMeasure();
  130. });
  131. var lazyUpdatePosition = throttleByAnimationFrame(function () {
  132. var target = props.target;
  133. var affixStyle = state.affixStyle;
  134. // Check position change before measure to make Safari smooth
  135. if (target && affixStyle) {
  136. var targetNode = target();
  137. if (targetNode && placeholderNode.value) {
  138. var targetRect = getTargetRect(targetNode);
  139. var placeholderRect = getTargetRect(placeholderNode.value);
  140. var fixedTop = getFixedTop(placeholderRect, targetRect, offsetTop.value);
  141. var fixedBottom = getFixedBottom(placeholderRect, targetRect, offsetBottom.value);
  142. if (fixedTop !== undefined && affixStyle.top === fixedTop || fixedBottom !== undefined && affixStyle.bottom === fixedBottom) {
  143. return;
  144. }
  145. }
  146. }
  147. // Directly call prepare measure since it's already throttled.
  148. prepareMeasure();
  149. });
  150. expose({
  151. updatePosition: updatePosition,
  152. lazyUpdatePosition: lazyUpdatePosition
  153. });
  154. watch(function () {
  155. return props.target;
  156. }, function (val) {
  157. var newTarget = (val === null || val === void 0 ? void 0 : val()) || null;
  158. if (state.prevTarget !== newTarget) {
  159. removeObserveTarget(currentInstance);
  160. if (newTarget) {
  161. addObserveTarget(newTarget, currentInstance);
  162. // Mock Event object.
  163. updatePosition();
  164. }
  165. state.prevTarget = newTarget;
  166. }
  167. });
  168. watch(function () {
  169. return [props.offsetTop, props.offsetBottom];
  170. }, updatePosition);
  171. onMounted(function () {
  172. var target = props.target;
  173. if (target) {
  174. // [Legacy] Wait for parent component ref has its value.
  175. // We should use target as directly element instead of function which makes element check hard.
  176. state.timeout = setTimeout(function () {
  177. addObserveTarget(target(), currentInstance);
  178. // Mock Event object.
  179. updatePosition();
  180. });
  181. }
  182. });
  183. onUpdated(function () {
  184. measure();
  185. });
  186. onUnmounted(function () {
  187. clearTimeout(state.timeout);
  188. removeObserveTarget(currentInstance);
  189. updatePosition.cancel();
  190. // https://github.com/ant-design/ant-design/issues/22683
  191. lazyUpdatePosition.cancel();
  192. });
  193. var _useConfigInject = useConfigInject('affix', props),
  194. prefixCls = _useConfigInject.prefixCls;
  195. return function () {
  196. var _slots$default;
  197. var affixStyle = state.affixStyle,
  198. placeholderStyle = state.placeholderStyle;
  199. var className = classNames(_defineProperty({}, prefixCls.value, affixStyle));
  200. var restProps = omit(props, ['prefixCls', 'offsetTop', 'offsetBottom', 'target', 'onChange', 'onTestUpdatePosition']);
  201. return _createVNode(ResizeObserver, {
  202. "onResize": updatePosition
  203. }, {
  204. default: function _default() {
  205. return [_createVNode("div", _objectSpread(_objectSpread({}, restProps), {}, {
  206. "style": placeholderStyle,
  207. "ref": placeholderNode
  208. }), [_createVNode("div", {
  209. "class": className,
  210. "ref": fixedNode,
  211. "style": affixStyle
  212. }, [(_slots$default = slots.default) === null || _slots$default === void 0 ? void 0 : _slots$default.call(slots)])])];
  213. }
  214. });
  215. };
  216. }
  217. });
  218. export default withInstall(Affix);