class Align extends Component {
...
static defaultProps = {
target: () => window, // 默认target
monitorBufferTime: 50, // 防抖时间
monitorWindowResize: false,
disabled: false, // 是否禁止对齐
};
componentDidMount() {
const props = this.props;
// if parent ref not attached .... use document.getElementById
this.forceAlign();
if (!props.disabled && props.monitorWindowResize) {
this.startMonitorWindowResize();
}
}
componentDidUpdate(prevProps) {
let reAlign = false;
const props = this.props;
// 下面三种情况会发生重新对齐
// 1.由disabled转为非disabled
// 2. target改变
// 3. source元素大小改变
if (!props.disabled) {
const source = ReactDOM.findDOMNode(this);
const sourceRect = source ? source.getBoundingClientRect() : null;
if (prevProps.disabled) {
// 之前是disabled
reAlign = true;
} else {
const lastElement = getElement(prevProps.target);
const currentElement = getElement(props.target);
const lastPoint = getPoint(prevProps.target);
const currentPoint = getPoint(props.target);
if (isWindow(lastElement) && isWindow(currentElement)) {
// Skip if is window
reAlign = false;
} else if (
lastElement !== currentElement || // Element change
(lastElement && !currentElement && currentPoint) || // Change from element to point
(lastPoint && currentPoint && currentElement) || // Change from point to element
(currentPoint && !isSamePoint(lastPoint, currentPoint))
) {
reAlign = true;
}
// If source element size changed
const preRect = this.sourceRect || {};
if (
!reAlign &&
source &&
(!isSimilarValue(preRect.width, sourceRect.width) || !isSimilarValue(preRect.height, sourceRect.height))
) {
reAlign = true;
}
}
this.sourceRect = sourceRect;
}
if (reAlign) {
this.forceAlign();
}
if (props.monitorWindowResize && !props.disabled) {
this.startMonitorWindowResize();
} else {
this.stopMonitorWindowResize();
}
}
componentWillUnmount() {
this.stopMonitorWindowResize();
}
startMonitorWindowResize() {
if (!this.resizeHandler) { // 防止重复添加监听
this.bufferMonitor = buffer(this.forceAlign, this.props.monitorBufferTime);
this.resizeHandler = addEventListener(window, 'resize', this.bufferMonitor);
}
}
stopMonitorWindowResize() {
if (this.resizeHandler) {
this.bufferMonitor.clear();
this.resizeHandler.remove();
this.resizeHandler = null;
}
}
forceAlign = () => {
const { disabled, target, align, onAlign } = this.props;
if (!disabled && target) {
// 通过 ReactDOM.findDOMNode(this) 获取source
// this其实最终获取的还是child component的DOM节点,因为render还是返回child
const source = ReactDOM.findDOMNode(this);
let result;
const element = getElement(target);
const point = getPoint(target);
// IE lose focus after element realign
// We should record activeElement and restore later
const activeElement = document.activeElement;
if (element) {
result = alignElement(source, element, align);
} else if (point) {
result = alignPoint(source, point, align);
}
restoreFocus(activeElement, source);
if (onAlign) {
// 如果有onAlign方法,即在`对齐`之后触发
onAlign(source, result);
}
}
}
render() {
const { childrenProps, children } = this.props;
const child = React.Children.only(children);
if (childrenProps) {
const newProps = {};
const propList = Object.keys(childrenProps);
propList.forEach((prop) => {
newProps[prop] = this.props[childrenProps[prop]];
});
// 如果有childrenProps,需要通过cloneElement将childrenProps复制到该组件中
return React.cloneElement(child, newProps);
}
return child;
}
}