2020-10-12 05:27:16 +00:00
|
|
|
import { nextTick, defineComponent } from 'vue';
|
2019-01-12 03:33:27 +00:00
|
|
|
import PropTypes from '../_util/vue-types';
|
|
|
|
import { alignElement, alignPoint } from 'dom-align';
|
2020-03-07 11:45:13 +00:00
|
|
|
import addEventListener from '../vc-util/Dom/addEventListener';
|
2020-10-10 05:57:37 +00:00
|
|
|
import { isWindow, buffer, isSamePoint, restoreFocus, monitorResize } from './util';
|
2020-10-02 13:59:53 +00:00
|
|
|
import { cloneElement } from '../_util/vnode';
|
2020-09-02 14:44:16 +00:00
|
|
|
import clonedeep from 'lodash-es/cloneDeep';
|
2020-06-21 14:45:30 +00:00
|
|
|
import { getSlot, findDOMNode } from '../_util/props-util';
|
2020-10-10 05:57:37 +00:00
|
|
|
import useBuffer from './hooks/useBuffer';
|
2020-10-25 12:44:26 +00:00
|
|
|
import isVisible from '../vc-util/Dom/isVisible';
|
2018-03-19 02:16:27 +00:00
|
|
|
|
2019-01-12 03:33:27 +00:00
|
|
|
function getElement(func) {
|
|
|
|
if (typeof func !== 'function' || !func) return null;
|
|
|
|
return func();
|
2018-09-05 13:28:54 +00:00
|
|
|
}
|
2017-12-12 07:05:45 +00:00
|
|
|
|
2019-01-12 03:33:27 +00:00
|
|
|
function getPoint(point) {
|
|
|
|
if (typeof point !== 'object' || !point) return null;
|
|
|
|
return point;
|
2017-12-12 07:05:45 +00:00
|
|
|
}
|
|
|
|
|
2020-10-12 05:27:16 +00:00
|
|
|
export default defineComponent({
|
2017-12-12 07:05:45 +00:00
|
|
|
props: {
|
|
|
|
childrenProps: PropTypes.object,
|
|
|
|
align: PropTypes.object.isRequired,
|
2019-01-12 03:33:27 +00:00
|
|
|
target: PropTypes.oneOfType([PropTypes.func, PropTypes.object]).def(() => window),
|
2017-12-12 07:05:45 +00:00
|
|
|
monitorBufferTime: PropTypes.number.def(50),
|
2020-10-10 10:16:28 +00:00
|
|
|
monitorWindowResize: PropTypes.looseBool.def(false),
|
|
|
|
disabled: PropTypes.looseBool.def(false),
|
2017-12-12 07:05:45 +00:00
|
|
|
},
|
2019-01-12 03:33:27 +00:00
|
|
|
data() {
|
|
|
|
this.aligned = false;
|
2020-10-10 05:57:37 +00:00
|
|
|
this.sourceResizeMonitor = { cancel: () => {} };
|
|
|
|
this.resizeMonitor = { cancel: () => {} };
|
2020-08-12 08:30:13 +00:00
|
|
|
this.prevProps = { ...this.$props };
|
2020-10-10 05:57:37 +00:00
|
|
|
const [forceAlign, cancelForceAlign] = useBuffer(this.goAlign, 0);
|
|
|
|
return {
|
|
|
|
forceAlign,
|
|
|
|
cancelForceAlign,
|
|
|
|
};
|
2017-12-13 02:06:26 +00:00
|
|
|
},
|
2019-01-12 03:33:27 +00:00
|
|
|
mounted() {
|
2020-06-20 02:08:45 +00:00
|
|
|
nextTick(() => {
|
2019-01-12 03:33:27 +00:00
|
|
|
const props = this.$props;
|
2018-02-27 04:17:53 +00:00
|
|
|
// if parent ref not attached .... use document.getElementById
|
2019-01-12 03:33:27 +00:00
|
|
|
!this.aligned && this.forceAlign();
|
2018-02-27 04:17:53 +00:00
|
|
|
if (!props.disabled && props.monitorWindowResize) {
|
2019-01-12 03:33:27 +00:00
|
|
|
this.startMonitorWindowResize();
|
2018-02-27 04:17:53 +00:00
|
|
|
}
|
2020-10-10 05:57:37 +00:00
|
|
|
this.startMonitorElementResize();
|
2019-01-12 03:33:27 +00:00
|
|
|
});
|
2017-12-12 07:05:45 +00:00
|
|
|
},
|
2019-01-12 03:33:27 +00:00
|
|
|
updated() {
|
2020-06-20 02:08:45 +00:00
|
|
|
nextTick(() => {
|
2019-01-12 03:33:27 +00:00
|
|
|
const prevProps = this.prevProps;
|
|
|
|
const props = this.$props;
|
|
|
|
let reAlign = false;
|
2018-09-13 04:19:03 +00:00
|
|
|
if (!props.disabled) {
|
2018-09-05 13:28:54 +00:00
|
|
|
if (prevProps.disabled) {
|
2019-01-12 03:33:27 +00:00
|
|
|
reAlign = true;
|
2018-02-27 04:17:53 +00:00
|
|
|
} else {
|
2019-01-12 03:33:27 +00:00
|
|
|
const lastElement = getElement(prevProps.target);
|
|
|
|
const currentElement = getElement(props.target);
|
|
|
|
const lastPoint = getPoint(prevProps.target);
|
|
|
|
const currentPoint = getPoint(props.target);
|
2018-09-05 13:28:54 +00:00
|
|
|
if (isWindow(lastElement) && isWindow(currentElement)) {
|
2019-01-12 03:33:27 +00:00
|
|
|
// Skip if is window
|
|
|
|
reAlign = false;
|
2018-09-05 13:28:54 +00:00
|
|
|
} else if (
|
2019-01-12 03:33:27 +00:00
|
|
|
(lastElement && !currentElement && currentPoint) || // Change from element to point
|
2020-10-10 05:57:37 +00:00
|
|
|
(lastPoint && currentPoint && currentElement) // Change from point to element
|
2019-01-12 03:33:27 +00:00
|
|
|
) {
|
|
|
|
reAlign = true;
|
2018-02-27 04:17:53 +00:00
|
|
|
}
|
2018-01-15 09:33:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-27 04:17:53 +00:00
|
|
|
if (reAlign) {
|
2019-01-12 03:33:27 +00:00
|
|
|
this.forceAlign();
|
2020-10-10 05:57:37 +00:00
|
|
|
} else {
|
|
|
|
this.startMonitorElementResize();
|
2018-02-27 04:17:53 +00:00
|
|
|
}
|
2018-01-15 09:33:34 +00:00
|
|
|
|
2018-02-27 04:17:53 +00:00
|
|
|
if (props.monitorWindowResize && !props.disabled) {
|
2019-01-12 03:33:27 +00:00
|
|
|
this.startMonitorWindowResize();
|
2018-02-27 04:17:53 +00:00
|
|
|
} else {
|
2019-01-12 03:33:27 +00:00
|
|
|
this.stopMonitorWindowResize();
|
2018-02-27 04:17:53 +00:00
|
|
|
}
|
2019-01-12 03:33:27 +00:00
|
|
|
this.prevProps = { ...this.$props, align: clonedeep(this.$props.align) };
|
|
|
|
});
|
2018-01-15 09:33:34 +00:00
|
|
|
},
|
2020-06-11 08:13:09 +00:00
|
|
|
beforeUnmount() {
|
2019-01-12 03:33:27 +00:00
|
|
|
this.stopMonitorWindowResize();
|
2020-10-10 05:57:37 +00:00
|
|
|
this.resizeMonitor?.cancel();
|
|
|
|
this.sourceResizeMonitor?.cancel();
|
|
|
|
this.cancelForceAlign();
|
2017-12-12 07:05:45 +00:00
|
|
|
},
|
|
|
|
methods: {
|
2020-10-09 22:58:19 +00:00
|
|
|
startMonitorElementResize() {
|
2020-10-10 05:57:37 +00:00
|
|
|
const prevProps = this.prevProps;
|
|
|
|
const props = this.$props;
|
|
|
|
const lastElement = getElement(prevProps.target);
|
|
|
|
const currentElement = getElement(props.target);
|
|
|
|
const lastPoint = getPoint(prevProps.target);
|
|
|
|
const currentPoint = getPoint(props.target);
|
|
|
|
const source = findDOMNode(this);
|
|
|
|
const { sourceResizeMonitor, resizeMonitor } = this;
|
|
|
|
if (source !== sourceResizeMonitor.element) {
|
|
|
|
sourceResizeMonitor?.cancel();
|
|
|
|
sourceResizeMonitor.element = source;
|
|
|
|
sourceResizeMonitor.cancel = monitorResize(source, this.forceAlign);
|
|
|
|
}
|
|
|
|
if (lastElement !== currentElement || !isSamePoint(lastPoint, currentPoint)) {
|
|
|
|
//this.forceAlign();
|
|
|
|
// Add resize observer
|
|
|
|
if (resizeMonitor.element !== currentElement) {
|
|
|
|
resizeMonitor?.cancel();
|
|
|
|
resizeMonitor.element = currentElement;
|
|
|
|
resizeMonitor.cancel = monitorResize(currentElement, this.forceAlign);
|
|
|
|
}
|
|
|
|
}
|
2020-10-09 22:58:19 +00:00
|
|
|
},
|
2019-01-12 03:33:27 +00:00
|
|
|
startMonitorWindowResize() {
|
2017-12-12 07:05:45 +00:00
|
|
|
if (!this.resizeHandler) {
|
2019-01-12 03:33:27 +00:00
|
|
|
this.bufferMonitor = buffer(this.forceAlign, this.$props.monitorBufferTime);
|
|
|
|
this.resizeHandler = addEventListener(window, 'resize', this.bufferMonitor);
|
2017-12-12 07:05:45 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2019-01-12 03:33:27 +00:00
|
|
|
stopMonitorWindowResize() {
|
2017-12-12 07:05:45 +00:00
|
|
|
if (this.resizeHandler) {
|
2019-01-12 03:33:27 +00:00
|
|
|
this.bufferMonitor.clear();
|
|
|
|
this.resizeHandler.remove();
|
|
|
|
this.resizeHandler = null;
|
2017-12-12 07:05:45 +00:00
|
|
|
}
|
|
|
|
},
|
2020-10-10 05:57:37 +00:00
|
|
|
goAlign() {
|
2019-01-12 03:33:27 +00:00
|
|
|
const { disabled, target, align } = this.$props;
|
2018-09-05 13:28:54 +00:00
|
|
|
if (!disabled && target) {
|
2020-06-21 14:45:30 +00:00
|
|
|
const source = findDOMNode(this);
|
2019-01-12 03:33:27 +00:00
|
|
|
let result;
|
|
|
|
const element = getElement(target);
|
|
|
|
const point = getPoint(target);
|
2018-09-05 13:28:54 +00:00
|
|
|
|
2019-09-03 14:59:41 +00:00
|
|
|
// IE lose focus after element realign
|
|
|
|
// We should record activeElement and restore later
|
|
|
|
const activeElement = document.activeElement;
|
2020-10-25 12:44:26 +00:00
|
|
|
if (element && isVisible(element)) {
|
2019-01-12 03:33:27 +00:00
|
|
|
result = alignElement(source, element, align);
|
2018-09-05 13:28:54 +00:00
|
|
|
} else if (point) {
|
2019-01-12 03:33:27 +00:00
|
|
|
result = alignPoint(source, point, align);
|
2018-09-05 13:28:54 +00:00
|
|
|
}
|
2019-09-03 14:59:41 +00:00
|
|
|
restoreFocus(activeElement, source);
|
2019-01-12 03:33:27 +00:00
|
|
|
this.aligned = true;
|
2020-10-10 05:57:37 +00:00
|
|
|
this.$attrs.onAlign && result && this.$attrs.onAlign(source, result);
|
|
|
|
return true;
|
2017-12-12 07:05:45 +00:00
|
|
|
}
|
2020-10-10 05:57:37 +00:00
|
|
|
return false;
|
2017-12-12 07:05:45 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2019-01-12 03:33:27 +00:00
|
|
|
render() {
|
|
|
|
const { childrenProps } = this.$props;
|
2020-04-15 14:56:58 +00:00
|
|
|
const child = getSlot(this);
|
2019-02-13 14:11:27 +00:00
|
|
|
if (child && childrenProps) {
|
2020-06-21 14:45:30 +00:00
|
|
|
return cloneElement(child[0], childrenProps);
|
2017-12-12 07:05:45 +00:00
|
|
|
}
|
2020-04-15 14:56:58 +00:00
|
|
|
return child && child[0];
|
2017-12-12 07:05:45 +00:00
|
|
|
},
|
2020-10-12 05:27:16 +00:00
|
|
|
});
|