perf: perf animation
parent
22b72855e5
commit
103a05a471
|
@ -2,10 +2,11 @@ import { nextTick } from 'vue';
|
||||||
import PropTypes from '../_util/vue-types';
|
import PropTypes from '../_util/vue-types';
|
||||||
import { alignElement, alignPoint } from 'dom-align';
|
import { alignElement, alignPoint } from 'dom-align';
|
||||||
import addEventListener from '../vc-util/Dom/addEventListener';
|
import addEventListener from '../vc-util/Dom/addEventListener';
|
||||||
import { isWindow, buffer, isSamePoint, isSimilarValue, restoreFocus } from './util';
|
import { isWindow, buffer, isSamePoint, restoreFocus, monitorResize } from './util';
|
||||||
import { cloneElement } from '../_util/vnode';
|
import { cloneElement } from '../_util/vnode';
|
||||||
import clonedeep from 'lodash-es/cloneDeep';
|
import clonedeep from 'lodash-es/cloneDeep';
|
||||||
import { getSlot, findDOMNode } from '../_util/props-util';
|
import { getSlot, findDOMNode } from '../_util/props-util';
|
||||||
|
import useBuffer from './hooks/useBuffer';
|
||||||
|
|
||||||
function getElement(func) {
|
function getElement(func) {
|
||||||
if (typeof func !== 'function' || !func) return null;
|
if (typeof func !== 'function' || !func) return null;
|
||||||
|
@ -28,8 +29,14 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
this.aligned = false;
|
this.aligned = false;
|
||||||
|
this.sourceResizeMonitor = { cancel: () => {} };
|
||||||
|
this.resizeMonitor = { cancel: () => {} };
|
||||||
this.prevProps = { ...this.$props };
|
this.prevProps = { ...this.$props };
|
||||||
return {};
|
const [forceAlign, cancelForceAlign] = useBuffer(this.goAlign, 0);
|
||||||
|
return {
|
||||||
|
forceAlign,
|
||||||
|
cancelForceAlign,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
|
@ -39,6 +46,7 @@ export default {
|
||||||
if (!props.disabled && props.monitorWindowResize) {
|
if (!props.disabled && props.monitorWindowResize) {
|
||||||
this.startMonitorWindowResize();
|
this.startMonitorWindowResize();
|
||||||
}
|
}
|
||||||
|
this.startMonitorElementResize();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
updated() {
|
updated() {
|
||||||
|
@ -47,9 +55,6 @@ export default {
|
||||||
const props = this.$props;
|
const props = this.$props;
|
||||||
let reAlign = false;
|
let reAlign = false;
|
||||||
if (!props.disabled) {
|
if (!props.disabled) {
|
||||||
const source = findDOMNode(this);
|
|
||||||
const sourceRect = source ? source.getBoundingClientRect() : null;
|
|
||||||
|
|
||||||
if (prevProps.disabled) {
|
if (prevProps.disabled) {
|
||||||
reAlign = true;
|
reAlign = true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -61,30 +66,18 @@ export default {
|
||||||
// Skip if is window
|
// Skip if is window
|
||||||
reAlign = false;
|
reAlign = false;
|
||||||
} else if (
|
} else if (
|
||||||
lastElement !== currentElement || // Element change
|
|
||||||
(lastElement && !currentElement && currentPoint) || // Change from element to point
|
(lastElement && !currentElement && currentPoint) || // Change from element to point
|
||||||
(lastPoint && currentPoint && currentElement) || // Change from point to element
|
(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;
|
reAlign = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.sourceRect = { width: sourceRect.width, height: sourceRect.height };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reAlign) {
|
if (reAlign) {
|
||||||
this.forceAlign();
|
this.forceAlign();
|
||||||
|
} else {
|
||||||
|
this.startMonitorElementResize();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.monitorWindowResize && !props.disabled) {
|
if (props.monitorWindowResize && !props.disabled) {
|
||||||
|
@ -97,12 +90,34 @@ export default {
|
||||||
},
|
},
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
this.stopMonitorWindowResize();
|
this.stopMonitorWindowResize();
|
||||||
|
this.resizeMonitor?.cancel();
|
||||||
|
this.sourceResizeMonitor?.cancel();
|
||||||
|
this.cancelForceAlign();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// TODO
|
|
||||||
startMonitorElementResize() {
|
startMonitorElementResize() {
|
||||||
const currentElement = getElement(this.$props.target);
|
const prevProps = this.prevProps;
|
||||||
const element = findDOMNode(this);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
startMonitorWindowResize() {
|
startMonitorWindowResize() {
|
||||||
if (!this.resizeHandler) {
|
if (!this.resizeHandler) {
|
||||||
|
@ -118,8 +133,7 @@ export default {
|
||||||
this.resizeHandler = null;
|
this.resizeHandler = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
goAlign() {
|
||||||
forceAlign() {
|
|
||||||
const { disabled, target, align } = this.$props;
|
const { disabled, target, align } = this.$props;
|
||||||
if (!disabled && target) {
|
if (!disabled && target) {
|
||||||
const source = findDOMNode(this);
|
const source = findDOMNode(this);
|
||||||
|
@ -138,8 +152,10 @@ export default {
|
||||||
}
|
}
|
||||||
restoreFocus(activeElement, source);
|
restoreFocus(activeElement, source);
|
||||||
this.aligned = true;
|
this.aligned = true;
|
||||||
this.$attrs.onAlign && this.$attrs.onAlign(source, result);
|
this.$attrs.onAlign && result && this.$attrs.onAlign(source, result);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
export default (callback: () => boolean, buffer: number) => {
|
||||||
|
let called = false;
|
||||||
|
let timeout = null;
|
||||||
|
|
||||||
|
function cancelTrigger() {
|
||||||
|
window.clearTimeout(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
function trigger(force?: boolean) {
|
||||||
|
if (!called || force === true) {
|
||||||
|
if (callback() === false) {
|
||||||
|
// Not delay since callback cancelled self
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
called = true;
|
||||||
|
cancelTrigger();
|
||||||
|
timeout = window.setTimeout(() => {
|
||||||
|
called = false;
|
||||||
|
}, buffer);
|
||||||
|
} else {
|
||||||
|
cancelTrigger();
|
||||||
|
timeout = window.setTimeout(() => {
|
||||||
|
called = false;
|
||||||
|
trigger();
|
||||||
|
}, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
trigger,
|
||||||
|
() => {
|
||||||
|
called = false;
|
||||||
|
cancelTrigger();
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
|
@ -90,7 +90,7 @@ const SelectTrigger = defineComponent<SelectTriggerProps>({
|
||||||
dropdownRender,
|
dropdownRender,
|
||||||
animation,
|
animation,
|
||||||
transitionName,
|
transitionName,
|
||||||
} = props;
|
} = props as SelectTriggerProps;
|
||||||
const dropdownPrefixCls = `${prefixCls}-dropdown`;
|
const dropdownPrefixCls = `${prefixCls}-dropdown`;
|
||||||
|
|
||||||
let popupNode = popupElement;
|
let popupNode = popupElement;
|
||||||
|
|
|
@ -65,7 +65,7 @@ export interface SelectorProps {
|
||||||
maxTagCount?: number;
|
maxTagCount?: number;
|
||||||
maxTagTextLength?: number;
|
maxTagTextLength?: number;
|
||||||
maxTagPlaceholder?: VNodeChild;
|
maxTagPlaceholder?: VNodeChild;
|
||||||
tagRender?: (props: CustomTagProps) => VNode;
|
tagRender?: (props: CustomTagProps) => VNodeChild;
|
||||||
|
|
||||||
/** Check if `tokenSeparators` contains `\n` or `\r\n` */
|
/** Check if `tokenSeparators` contains `\n` or `\r\n` */
|
||||||
tokenWithEnter?: boolean;
|
tokenWithEnter?: boolean;
|
||||||
|
@ -225,7 +225,7 @@ const Selector = defineComponent<SelectorProps>({
|
||||||
onInputChange,
|
onInputChange,
|
||||||
onInputCompositionStart,
|
onInputCompositionStart,
|
||||||
onInputCompositionEnd,
|
onInputCompositionEnd,
|
||||||
} = this;
|
} = this as any;
|
||||||
const sharedProps = {
|
const sharedProps = {
|
||||||
inputRef,
|
inputRef,
|
||||||
onInputKeyDown: onInternalInputKeyDown,
|
onInputKeyDown: onInternalInputKeyDown,
|
||||||
|
|
|
@ -219,7 +219,7 @@ export default function generateSelector<
|
||||||
key?: Key;
|
key?: Key;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
}[]
|
}[]
|
||||||
>(config: GenerateConfig<OptionsType>): DefineComponent {
|
>(config: GenerateConfig<OptionsType>): DefineComponent<SelectProps<OptionsType, ValueType>> {
|
||||||
const {
|
const {
|
||||||
prefixCls: defaultPrefixCls,
|
prefixCls: defaultPrefixCls,
|
||||||
components: { optionList: OptionList },
|
components: { optionList: OptionList },
|
||||||
|
@ -600,11 +600,7 @@ export default function generateSelector<
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useSelectTriggerControl(
|
useSelectTriggerControl([containerRef, triggerRef], triggerOpen, onToggleOpen);
|
||||||
[containerRef.value, triggerRef.value && triggerRef.value.getPopupElement()],
|
|
||||||
triggerOpen,
|
|
||||||
onToggleOpen,
|
|
||||||
);
|
|
||||||
|
|
||||||
// ============================= Search =============================
|
// ============================= Search =============================
|
||||||
const triggerSearch = (searchText: string, fromTyping: boolean, isCompositing: boolean) => {
|
const triggerSearch = (searchText: string, fromTyping: boolean, isCompositing: boolean) => {
|
||||||
|
@ -923,6 +919,7 @@ export default function generateSelector<
|
||||||
onSearchSubmit,
|
onSearchSubmit,
|
||||||
containerRef,
|
containerRef,
|
||||||
listRef,
|
listRef,
|
||||||
|
triggerRef,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -964,7 +961,7 @@ export default function generateSelector<
|
||||||
displayValues,
|
displayValues,
|
||||||
activeValue,
|
activeValue,
|
||||||
onSearchSubmit,
|
onSearchSubmit,
|
||||||
} = this;
|
} = this as any;
|
||||||
const {
|
const {
|
||||||
prefixCls = defaultPrefixCls,
|
prefixCls = defaultPrefixCls,
|
||||||
class: className,
|
class: className,
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import { onBeforeUnmount, onMounted, Ref } from 'vue';
|
import { onBeforeUnmount, onMounted, Ref } from 'vue';
|
||||||
|
|
||||||
export default function useSelectTriggerControl(
|
export default function useSelectTriggerControl(
|
||||||
elements: (HTMLElement | undefined)[],
|
refs: Ref[],
|
||||||
open: Ref<boolean>,
|
open: Ref<boolean>,
|
||||||
triggerOpen: (open: boolean) => void,
|
triggerOpen: (open: boolean) => void,
|
||||||
) {
|
) {
|
||||||
function onGlobalMouseDown(event: MouseEvent) {
|
function onGlobalMouseDown(event: MouseEvent) {
|
||||||
const target = event.target as HTMLElement;
|
const target = event.target as HTMLElement;
|
||||||
|
const elements = [refs[0]?.value, refs[1]?.value?.getPopupElement()];
|
||||||
if (
|
if (
|
||||||
open.value &&
|
open.value &&
|
||||||
elements.every(element => element && !element.contains(target) && element !== target)
|
elements.every(element => element && !element.contains(target) && element !== target)
|
||||||
|
|
Loading…
Reference in New Issue