index.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. "use strict";
  2. "use client";
  3. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
  4. Object.defineProperty(exports, "__esModule", {
  5. value: true
  6. });
  7. exports.default = void 0;
  8. var _react = _interopRequireDefault(require("react"));
  9. var _classnames = _interopRequireDefault(require("classnames"));
  10. var _rcResizeObserver = _interopRequireDefault(require("rc-resize-observer"));
  11. var _throttleByAnimationFrame = _interopRequireDefault(require("../_util/throttleByAnimationFrame"));
  12. var _configProvider = require("../config-provider");
  13. var _style = _interopRequireDefault(require("./style"));
  14. var _utils = require("./utils");
  15. var __rest = void 0 && (void 0).__rest || function (s, e) {
  16. var t = {};
  17. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
  18. if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
  19. if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
  20. }
  21. return t;
  22. };
  23. const TRIGGER_EVENTS = ['resize', 'scroll', 'touchstart', 'touchmove', 'touchend', 'pageshow', 'load'];
  24. function getDefaultTarget() {
  25. return typeof window !== 'undefined' ? window : null;
  26. }
  27. const AFFIX_STATUS_NONE = 0;
  28. const AFFIX_STATUS_PREPARE = 1;
  29. const Affix = /*#__PURE__*/_react.default.forwardRef((props, ref) => {
  30. var _a;
  31. const {
  32. style,
  33. offsetTop,
  34. offsetBottom,
  35. prefixCls,
  36. className,
  37. rootClassName,
  38. children,
  39. target,
  40. onChange,
  41. onTestUpdatePosition
  42. } = props,
  43. restProps = __rest(props, ["style", "offsetTop", "offsetBottom", "prefixCls", "className", "rootClassName", "children", "target", "onChange", "onTestUpdatePosition"]);
  44. const {
  45. getPrefixCls,
  46. getTargetContainer
  47. } = _react.default.useContext(_configProvider.ConfigContext);
  48. const affixPrefixCls = getPrefixCls('affix', prefixCls);
  49. const [lastAffix, setLastAffix] = _react.default.useState(false);
  50. const [affixStyle, setAffixStyle] = _react.default.useState();
  51. const [placeholderStyle, setPlaceholderStyle] = _react.default.useState();
  52. const status = _react.default.useRef(AFFIX_STATUS_NONE);
  53. const prevTarget = _react.default.useRef(null);
  54. const prevListener = _react.default.useRef(null);
  55. const placeholderNodeRef = _react.default.useRef(null);
  56. const fixedNodeRef = _react.default.useRef(null);
  57. const timer = _react.default.useRef(null);
  58. const targetFunc = (_a = target !== null && target !== void 0 ? target : getTargetContainer) !== null && _a !== void 0 ? _a : getDefaultTarget;
  59. const internalOffsetTop = offsetBottom === undefined && offsetTop === undefined ? 0 : offsetTop;
  60. // =================== Measure ===================
  61. const measure = () => {
  62. if (status.current !== AFFIX_STATUS_PREPARE || !fixedNodeRef.current || !placeholderNodeRef.current || !targetFunc) {
  63. return;
  64. }
  65. const targetNode = targetFunc();
  66. if (targetNode) {
  67. const newState = {
  68. status: AFFIX_STATUS_NONE
  69. };
  70. const placeholderRect = (0, _utils.getTargetRect)(placeholderNodeRef.current);
  71. if (placeholderRect.top === 0 && placeholderRect.left === 0 && placeholderRect.width === 0 && placeholderRect.height === 0) {
  72. return;
  73. }
  74. const targetRect = (0, _utils.getTargetRect)(targetNode);
  75. const fixedTop = (0, _utils.getFixedTop)(placeholderRect, targetRect, internalOffsetTop);
  76. const fixedBottom = (0, _utils.getFixedBottom)(placeholderRect, targetRect, offsetBottom);
  77. if (fixedTop !== undefined) {
  78. newState.affixStyle = {
  79. position: 'fixed',
  80. top: fixedTop,
  81. width: placeholderRect.width,
  82. height: placeholderRect.height
  83. };
  84. newState.placeholderStyle = {
  85. width: placeholderRect.width,
  86. height: placeholderRect.height
  87. };
  88. } else if (fixedBottom !== undefined) {
  89. newState.affixStyle = {
  90. position: 'fixed',
  91. bottom: fixedBottom,
  92. width: placeholderRect.width,
  93. height: placeholderRect.height
  94. };
  95. newState.placeholderStyle = {
  96. width: placeholderRect.width,
  97. height: placeholderRect.height
  98. };
  99. }
  100. newState.lastAffix = !!newState.affixStyle;
  101. if (lastAffix !== newState.lastAffix) {
  102. onChange === null || onChange === void 0 ? void 0 : onChange(newState.lastAffix);
  103. }
  104. status.current = newState.status;
  105. setAffixStyle(newState.affixStyle);
  106. setPlaceholderStyle(newState.placeholderStyle);
  107. setLastAffix(newState.lastAffix);
  108. }
  109. };
  110. const prepareMeasure = () => {
  111. status.current = AFFIX_STATUS_PREPARE;
  112. measure();
  113. if (process.env.NODE_ENV === 'test') {
  114. onTestUpdatePosition === null || onTestUpdatePosition === void 0 ? void 0 : onTestUpdatePosition();
  115. }
  116. };
  117. const updatePosition = (0, _throttleByAnimationFrame.default)(() => {
  118. prepareMeasure();
  119. });
  120. const lazyUpdatePosition = (0, _throttleByAnimationFrame.default)(() => {
  121. // Check position change before measure to make Safari smooth
  122. if (targetFunc && affixStyle) {
  123. const targetNode = targetFunc();
  124. if (targetNode && placeholderNodeRef.current) {
  125. const targetRect = (0, _utils.getTargetRect)(targetNode);
  126. const placeholderRect = (0, _utils.getTargetRect)(placeholderNodeRef.current);
  127. const fixedTop = (0, _utils.getFixedTop)(placeholderRect, targetRect, internalOffsetTop);
  128. const fixedBottom = (0, _utils.getFixedBottom)(placeholderRect, targetRect, offsetBottom);
  129. if (fixedTop !== undefined && affixStyle.top === fixedTop || fixedBottom !== undefined && affixStyle.bottom === fixedBottom) {
  130. return;
  131. }
  132. }
  133. }
  134. // Directly call prepare measure since it's already throttled.
  135. prepareMeasure();
  136. });
  137. const addListeners = () => {
  138. const listenerTarget = targetFunc === null || targetFunc === void 0 ? void 0 : targetFunc();
  139. if (!listenerTarget) {
  140. return;
  141. }
  142. TRIGGER_EVENTS.forEach(eventName => {
  143. var _a;
  144. if (prevListener.current) {
  145. (_a = prevTarget.current) === null || _a === void 0 ? void 0 : _a.removeEventListener(eventName, prevListener.current);
  146. }
  147. listenerTarget === null || listenerTarget === void 0 ? void 0 : listenerTarget.addEventListener(eventName, lazyUpdatePosition);
  148. });
  149. prevTarget.current = listenerTarget;
  150. prevListener.current = lazyUpdatePosition;
  151. };
  152. const removeListeners = () => {
  153. if (timer.current) {
  154. clearTimeout(timer.current);
  155. timer.current = null;
  156. }
  157. const newTarget = targetFunc === null || targetFunc === void 0 ? void 0 : targetFunc();
  158. TRIGGER_EVENTS.forEach(eventName => {
  159. var _a;
  160. newTarget === null || newTarget === void 0 ? void 0 : newTarget.removeEventListener(eventName, lazyUpdatePosition);
  161. if (prevListener.current) {
  162. (_a = prevTarget.current) === null || _a === void 0 ? void 0 : _a.removeEventListener(eventName, prevListener.current);
  163. }
  164. });
  165. updatePosition.cancel();
  166. lazyUpdatePosition.cancel();
  167. };
  168. _react.default.useImperativeHandle(ref, () => ({
  169. updatePosition
  170. }));
  171. // mount & unmount
  172. _react.default.useEffect(() => {
  173. // [Legacy] Wait for parent component ref has its value.
  174. // We should use target as directly element instead of function which makes element check hard.
  175. timer.current = setTimeout(addListeners);
  176. return () => removeListeners();
  177. }, []);
  178. _react.default.useEffect(() => {
  179. addListeners();
  180. return () => removeListeners();
  181. }, [target, affixStyle, lastAffix, offsetTop, offsetBottom]);
  182. _react.default.useEffect(() => {
  183. updatePosition();
  184. }, [target, offsetTop, offsetBottom]);
  185. const [wrapCSSVar, hashId, cssVarCls] = (0, _style.default)(affixPrefixCls);
  186. const rootCls = (0, _classnames.default)(rootClassName, hashId, affixPrefixCls, cssVarCls);
  187. const mergedCls = (0, _classnames.default)({
  188. [rootCls]: affixStyle
  189. });
  190. return wrapCSSVar(/*#__PURE__*/_react.default.createElement(_rcResizeObserver.default, {
  191. onResize: updatePosition
  192. }, /*#__PURE__*/_react.default.createElement("div", Object.assign({
  193. style: style,
  194. className: className,
  195. ref: placeholderNodeRef
  196. }, restProps), affixStyle && /*#__PURE__*/_react.default.createElement("div", {
  197. style: placeholderStyle,
  198. "aria-hidden": "true"
  199. }), /*#__PURE__*/_react.default.createElement("div", {
  200. className: mergedCls,
  201. ref: fixedNodeRef,
  202. style: affixStyle
  203. }, /*#__PURE__*/_react.default.createElement(_rcResizeObserver.default, {
  204. onResize: updatePosition
  205. }, children)))));
  206. });
  207. if (process.env.NODE_ENV !== 'production') {
  208. Affix.displayName = 'Affix';
  209. }
  210. var _default = exports.default = Affix;