♻️(component): [dropdown]优化定位算法

This commit is contained in:
sight 2022-06-26 21:10:14 +08:00
parent 802e6696d0
commit bfde04c2f1

View File

@ -41,6 +41,9 @@ export interface LayDropdownProps {
blurToClose?: boolean; blurToClose?: boolean;
clickOutsideToClose?: boolean; clickOutsideToClose?: boolean;
contentOffset?: number; contentOffset?: number;
mouseEnterDelay?: number;
mouseLeaveDelay?: number;
focusDelay?: number;
} }
const props = withDefaults(defineProps<LayDropdownProps>(), { const props = withDefaults(defineProps<LayDropdownProps>(), {
@ -57,6 +60,9 @@ const props = withDefaults(defineProps<LayDropdownProps>(), {
blurToClose: true, blurToClose: true,
clickOutsideToClose: true, clickOutsideToClose: true,
contentOffset: 2, contentOffset: 2,
mouseEnterDelay: 150,
mouseLeaveDelay: 150,
focusDelay: 150,
}); });
const dropdownRef = shallowRef<HTMLElement | undefined>(); const dropdownRef = shallowRef<HTMLElement | undefined>();
@ -128,9 +134,7 @@ const updateContentStyle = () => {
} }
const triggerRect = dropdownRef.value.getBoundingClientRect(); const triggerRect = dropdownRef.value.getBoundingClientRect();
const contentRect = contentRef.value.getBoundingClientRect(); const contentRect = contentRef.value.getBoundingClientRect();
const { style } = getContentStyle(props.placement, triggerRect, contentRect, { const { style } = getContentStyle(props.placement, triggerRect, contentRect);
autoFitPosition: props.autoFitPosition,
});
if (props.autoFitMinWidth) { if (props.autoFitMinWidth) {
style.minWidth = `${triggerRect.width}px`; style.minWidth = `${triggerRect.width}px`;
@ -138,8 +142,29 @@ const updateContentStyle = () => {
if (props.autoFitWidth) { if (props.autoFitWidth) {
style.width = `${triggerRect.width}px`; style.width = `${triggerRect.width}px`;
} }
contentStyle.value = style; contentStyle.value = style;
if (props.autoFitPosition) {
nextTick(() => {
const triggerRect = dropdownRef.value!.getBoundingClientRect();
const contentRect = contentRef.value!.getBoundingClientRect();
let { top, left } = style;
top = Number(top.toString().replace("px", ""))
left = Number(left.toString().replace("px", ""))
const { top: fitTop, left: fitLeft } = getFitPlacement(
top,
left,
props.placement,
triggerRect,
contentRect
);
style.top = `${fitTop}px`;
style.left = `${fitLeft}px`;
contentStyle.value = {
...style
}
})
}
}; };
const getContentStyle = ( const getContentStyle = (
@ -147,25 +172,12 @@ const getContentStyle = (
triggerRect: DOMRect, triggerRect: DOMRect,
contentRect: DOMRect, contentRect: DOMRect,
{ {
autoFitPosition = false,
customStyle = {}, customStyle = {},
}: { }: {
autoFitPosition?: boolean;
customStyle?: CSSProperties; customStyle?: CSSProperties;
} = {} } = {}
) => { ) => {
let { top, left } = getContentOffset(placement, triggerRect, contentRect); let { top, left } = getContentOffset(placement, triggerRect, contentRect);
if (autoFitPosition) {
const { top: fitTop, left: fitLeft } = getFitPlacement(
top,
left,
placement,
triggerRect,
contentRect
);
top = fitTop;
left = fitLeft;
}
const style = { const style = {
top: `${top}px`, top: `${top}px`,
left: `${left}px`, left: `${left}px`,
@ -184,50 +196,29 @@ const getFitPlacement = (
contentRect: DOMRect contentRect: DOMRect
) => { ) => {
// //
if (triggerRect.bottom + contentRect.height > windowHeight.value) { if (contentRect.bottom > windowHeight.value) {
top = -contentRect.height - props.contentOffset; top = -contentRect.height - props.contentOffset;
} }
// //
if (triggerRect.top - contentRect.height < 0) { if (contentRect.top < 0) {
top = triggerRect.height + props.contentOffset; top = triggerRect.height + props.contentOffset;
} }
if (["bottom-right", "top-right"].includes(placement)) {
// //
const contentRectLeft = if(contentRect.left < 0){
triggerRect.left - (contentRect.width - triggerRect.width); left = left + (0 - contentRect.left)
if (contentRectLeft < 0) {
left = left + (0 - contentRectLeft);
}
} }
if (["bottom-left", "top-left"].includes(placement)) {
// //
const contentRectRight = if(contentRect.right > windowWidth.value){
triggerRect.right + (contentRect.width - triggerRect.width); left = left - (contentRect.right - windowWidth.value)
if (contentRectRight > windowWidth.value) {
left = left - (contentRectRight - windowWidth.value);
}
} }
if (["bottom", "top"].includes(placement)) {
const contentRectLeft =
triggerRect.left - (contentRect.width - triggerRect.width) / 2;
const contentRectRight =
triggerRect.right + (contentRect.width - triggerRect.width) / 2;
//
if (contentRectLeft < 0) {
left = left + (0 - contentRectLeft);
}
//
if (contentRectRight > windowWidth.value) {
left = left - (contentRectRight - windowWidth.value);
}
}
return { return {
top, top,
left, left,
}; };
}; }
const getContentOffset = ( const getContentOffset = (
placement: DropdownPlacement, placement: DropdownPlacement,
@ -320,21 +311,21 @@ const handleMouseEnter = () => {
if (props.disabled || !triggerMethods.value.includes("hover")) { if (props.disabled || !triggerMethods.value.includes("hover")) {
return; return;
} }
open(250); open(props.mouseEnterDelay);
}; };
const handleMouseLeave = () => { const handleMouseLeave = () => {
if (props.disabled || !triggerMethods.value.includes("hover")) { if (props.disabled || !triggerMethods.value.includes("hover")) {
return; return;
} }
hide(150); hide(props.mouseLeaveDelay);
}; };
const handleFocusin = () => { const handleFocusin = () => {
if (props.disabled || !triggerMethods.value.includes("focus")) { if (props.disabled || !triggerMethods.value.includes("focus")) {
return; return;
} }
open(); open(props.focusDelay);
}; };
const handleFocusout = () => { const handleFocusout = () => {