linkmore-design
Version:
π πlmη»δ»ΆεΊγπ
277 lines (266 loc) β’ 9.34 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _applyDecoratedDescriptor2 = _interopRequireDefault(require("@babel/runtime/helpers/applyDecoratedDescriptor"));
var _classnames = _interopRequireDefault(require("classnames"));
var _rcResizeObserver = _interopRequireDefault(require("rc-resize-observer"));
var _omit = _interopRequireDefault(require("rc-util/lib/omit"));
var React = _interopRequireWildcard(require("react"));
var _configProvider = require("../config-provider");
var _throttleByAnimationFrame = require("../_util/throttleByAnimationFrame");
var _utils = require("./utils");
var _dec, _dec2, _class;
function getDefaultTarget() {
return typeof window !== 'undefined' ? window : null;
}
// Affix
var AffixStatus;
(function (AffixStatus) {
AffixStatus[AffixStatus["None"] = 0] = "None";
AffixStatus[AffixStatus["Prepare"] = 1] = "Prepare";
})(AffixStatus || (AffixStatus = {}));
let Affix = (_dec = (0, _throttleByAnimationFrame.throttleByAnimationFrameDecorator)(), _dec2 = (0, _throttleByAnimationFrame.throttleByAnimationFrameDecorator)(), (_class = class Affix extends React.Component {
static contextType = _configProvider.ConfigContext;
state = {
status: AffixStatus.None,
lastAffix: false,
prevTarget: null
};
placeholderNode;
fixedNode;
timeout;
context;
getTargetFunc() {
const {
getTargetContainer
} = this.context;
const {
target
} = this.props;
if (target !== undefined) {
return target;
}
return getTargetContainer ?? getDefaultTarget;
}
// Event handler
componentDidMount() {
const targetFunc = this.getTargetFunc();
if (targetFunc) {
// [Legacy] Wait for parent component ref has its value.
// We should use target as directly element instead of function which makes element check hard.
this.timeout = setTimeout(() => {
(0, _utils.addObserveTarget)(targetFunc(), this);
// Mock Event object.
this.updatePosition();
});
}
}
componentDidUpdate(prevProps) {
const {
prevTarget
} = this.state;
const targetFunc = this.getTargetFunc();
const newTarget = targetFunc?.() || null;
if (prevTarget !== newTarget) {
(0, _utils.removeObserveTarget)(this);
if (newTarget) {
(0, _utils.addObserveTarget)(newTarget, this);
// Mock Event object.
this.updatePosition();
}
// eslint-disable-next-line react/no-did-update-set-state
this.setState({
prevTarget: newTarget
});
}
if (prevProps.offsetTop !== this.props.offsetTop || prevProps.offsetBottom !== this.props.offsetBottom) {
this.updatePosition();
}
this.measure();
}
componentWillUnmount() {
clearTimeout(this.timeout);
(0, _utils.removeObserveTarget)(this);
this.updatePosition.cancel();
// https://github.com/ant-design/ant-design/issues/22683
this.lazyUpdatePosition.cancel();
}
getOffsetTop = () => {
const {
offsetBottom,
offsetTop
} = this.props;
return offsetBottom === undefined && offsetTop === undefined ? 0 : offsetTop;
};
getOffsetBottom = () => this.props.offsetBottom;
savePlaceholderNode = node => {
this.placeholderNode = node;
};
saveFixedNode = node => {
this.fixedNode = node;
};
// =================== Measure ===================
measure = () => {
const {
status,
lastAffix
} = this.state;
const {
onChange
} = this.props;
const targetFunc = this.getTargetFunc();
if (status !== AffixStatus.Prepare || !this.fixedNode || !this.placeholderNode || !targetFunc) {
return;
}
const offsetTop = this.getOffsetTop();
const offsetBottom = this.getOffsetBottom();
const targetNode = targetFunc();
if (!targetNode) {
return;
}
const newState = {
status: AffixStatus.None
};
const targetRect = (0, _utils.getTargetRect)(targetNode);
const placeholderReact = (0, _utils.getTargetRect)(this.placeholderNode);
const fixedTop = (0, _utils.getFixedTop)(placeholderReact, targetRect, offsetTop);
const fixedBottom = (0, _utils.getFixedBottom)(placeholderReact, targetRect, offsetBottom);
if (placeholderReact.top === 0 && placeholderReact.left === 0 && placeholderReact.width === 0 && placeholderReact.height === 0) {
return;
}
if (fixedTop !== undefined) {
newState.affixStyle = {
position: 'fixed',
top: fixedTop,
width: placeholderReact.width,
height: placeholderReact.height
};
newState.placeholderStyle = {
width: placeholderReact.width,
height: placeholderReact.height
};
} else if (fixedBottom !== undefined) {
newState.affixStyle = {
position: 'fixed',
bottom: fixedBottom,
width: placeholderReact.width,
height: placeholderReact.height
};
newState.placeholderStyle = {
width: placeholderReact.width,
height: placeholderReact.height
};
}
newState.lastAffix = !!newState.affixStyle;
if (onChange && lastAffix !== newState.lastAffix) {
onChange(newState.lastAffix);
}
this.setState(newState);
};
// @ts-ignore TS6133
prepareMeasure = () => {
// event param is used before. Keep compatible ts define here.
this.setState({
status: AffixStatus.Prepare,
affixStyle: undefined,
placeholderStyle: undefined
});
// Test if `updatePosition` called
if (process.env.NODE_ENV === 'test') {
const {
onTestUpdatePosition
} = this.props;
onTestUpdatePosition?.();
}
};
// Handle realign logic
updatePosition() {
this.prepareMeasure();
}
lazyUpdatePosition() {
const targetFunc = this.getTargetFunc();
const {
affixStyle
} = this.state;
// Check position change before measure to make Safari smooth
if (targetFunc && affixStyle) {
const offsetTop = this.getOffsetTop();
const offsetBottom = this.getOffsetBottom();
const targetNode = targetFunc();
if (targetNode && this.placeholderNode) {
const targetRect = (0, _utils.getTargetRect)(targetNode);
const placeholderReact = (0, _utils.getTargetRect)(this.placeholderNode);
const fixedTop = (0, _utils.getFixedTop)(placeholderReact, targetRect, offsetTop);
const fixedBottom = (0, _utils.getFixedBottom)(placeholderReact, targetRect, offsetBottom);
if (fixedTop !== undefined && affixStyle.top === fixedTop || fixedBottom !== undefined && affixStyle.bottom === fixedBottom) {
return;
}
}
}
// Directly call prepare measure since it's already throttled.
this.prepareMeasure();
}
// =================== Render ===================
render() {
const {
affixStyle,
placeholderStyle
} = this.state;
const {
affixPrefixCls,
children
} = this.props;
const className = (0, _classnames.default)({
[affixPrefixCls]: !!affixStyle
});
let props = (0, _omit.default)(this.props, ['prefixCls', 'offsetTop', 'offsetBottom', 'target', 'onChange', 'affixPrefixCls']);
// Omit this since `onTestUpdatePosition` only works on test.
if (process.env.NODE_ENV === 'test') {
props = (0, _omit.default)(props, ['onTestUpdatePosition']);
}
return /*#__PURE__*/React.createElement(_rcResizeObserver.default, {
onResize: () => {
this.updatePosition();
}
}, /*#__PURE__*/React.createElement("div", (0, _extends2.default)({}, props, {
ref: this.savePlaceholderNode
}), affixStyle && /*#__PURE__*/React.createElement("div", {
style: placeholderStyle,
"aria-hidden": "true"
}), /*#__PURE__*/React.createElement("div", {
className: className,
ref: this.saveFixedNode,
style: affixStyle
}, /*#__PURE__*/React.createElement(_rcResizeObserver.default, {
onResize: () => {
this.updatePosition();
}
}, children))));
}
}, ((0, _applyDecoratedDescriptor2.default)(_class.prototype, "updatePosition", [_dec], Object.getOwnPropertyDescriptor(_class.prototype, "updatePosition"), _class.prototype), (0, _applyDecoratedDescriptor2.default)(_class.prototype, "lazyUpdatePosition", [_dec2], Object.getOwnPropertyDescriptor(_class.prototype, "lazyUpdatePosition"), _class.prototype)), _class)); // just use in test
const AffixFC = /*#__PURE__*/React.forwardRef((props, ref) => {
const {
prefixCls: customizePrefixCls
} = props;
const {
getPrefixCls
} = React.useContext(_configProvider.ConfigContext);
const affixPrefixCls = getPrefixCls('affix', customizePrefixCls);
const affixProps = {
...props,
affixPrefixCls
};
return /*#__PURE__*/React.createElement(Affix, (0, _extends2.default)({}, affixProps, {
ref: ref
}));
});
if (process.env.NODE_ENV !== 'production') {
AffixFC.displayName = 'Affix';
}
var _default = AffixFC;
exports.default = _default;