index.js 8.6 KB

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