|
|
|
@ -8,19 +8,40 @@ import { isFunction } from '@vben/utils'; |
|
|
|
|
|
|
|
import { useElementHover } from '@vueuse/core'; |
|
|
|
|
|
|
|
interface HoverDelayOptions { |
|
|
|
/** 鼠标进入延迟时间 */ |
|
|
|
enterDelay?: (() => number) | number; |
|
|
|
/** 鼠标离开延迟时间 */ |
|
|
|
leaveDelay?: (() => number) | number; |
|
|
|
} |
|
|
|
|
|
|
|
const DEFAULT_LEAVE_DELAY = 500; // 鼠标离开延迟时间,默认为 500ms
|
|
|
|
const DEFAULT_ENTER_DELAY = 0; // 鼠标进入延迟时间,默认为 0(立即响应)
|
|
|
|
|
|
|
|
/** |
|
|
|
* 监测鼠标是否在元素内部,如果在元素内部则返回 true,否则返回 false |
|
|
|
* @param refElement 所有需要检测的元素。如果提供了一个数组,那么鼠标在任何一个元素内部都会返回 true |
|
|
|
* @param delay 延迟更新状态的时间 |
|
|
|
* @param delay 延迟更新状态的时间,可以是数字或包含进入/离开延迟的配置对象 |
|
|
|
* @returns 返回一个数组,第一个元素是一个 ref,表示鼠标是否在元素内部,第二个元素是一个控制器,可以通过 enable 和 disable 方法来控制监听器的启用和禁用 |
|
|
|
*/ |
|
|
|
export function useHoverToggle( |
|
|
|
refElement: Arrayable<MaybeElementRef>, |
|
|
|
delay: (() => number) | number = 500, |
|
|
|
delay: (() => number) | HoverDelayOptions | number = DEFAULT_LEAVE_DELAY, |
|
|
|
) { |
|
|
|
// 兼容旧版本API
|
|
|
|
const normalizedOptions: HoverDelayOptions = |
|
|
|
typeof delay === 'number' || isFunction(delay) |
|
|
|
? { enterDelay: DEFAULT_ENTER_DELAY, leaveDelay: delay } |
|
|
|
: { |
|
|
|
enterDelay: DEFAULT_ENTER_DELAY, |
|
|
|
leaveDelay: DEFAULT_LEAVE_DELAY, |
|
|
|
...delay, |
|
|
|
}; |
|
|
|
|
|
|
|
const isHovers: Array<Ref<boolean>> = []; |
|
|
|
const value = ref(false); |
|
|
|
const timer = ref<ReturnType<typeof setTimeout> | undefined>(); |
|
|
|
const enterTimer = ref<ReturnType<typeof setTimeout> | undefined>(); |
|
|
|
const leaveTimer = ref<ReturnType<typeof setTimeout> | undefined>(); |
|
|
|
const refs = Array.isArray(refElement) ? refElement : [refElement]; |
|
|
|
refs.forEach((refEle) => { |
|
|
|
const eleRef = computed(() => { |
|
|
|
@ -32,15 +53,47 @@ export function useHoverToggle( |
|
|
|
}); |
|
|
|
const isOutsideAll = computed(() => isHovers.every((v) => !v.value)); |
|
|
|
|
|
|
|
function clearTimers() { |
|
|
|
if (enterTimer.value) { |
|
|
|
clearTimeout(enterTimer.value); |
|
|
|
enterTimer.value = undefined; |
|
|
|
} |
|
|
|
if (leaveTimer.value) { |
|
|
|
clearTimeout(leaveTimer.value); |
|
|
|
leaveTimer.value = undefined; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
function setValueDelay(val: boolean) { |
|
|
|
timer.value && clearTimeout(timer.value); |
|
|
|
timer.value = setTimeout( |
|
|
|
() => { |
|
|
|
value.value = val; |
|
|
|
timer.value = undefined; |
|
|
|
}, |
|
|
|
isFunction(delay) ? delay() : delay, |
|
|
|
); |
|
|
|
clearTimers(); |
|
|
|
|
|
|
|
if (val) { |
|
|
|
// 鼠标进入
|
|
|
|
const enterDelay = normalizedOptions.enterDelay ?? DEFAULT_ENTER_DELAY; |
|
|
|
const delayTime = isFunction(enterDelay) ? enterDelay() : enterDelay; |
|
|
|
|
|
|
|
if (delayTime <= 0) { |
|
|
|
value.value = true; |
|
|
|
} else { |
|
|
|
enterTimer.value = setTimeout(() => { |
|
|
|
value.value = true; |
|
|
|
enterTimer.value = undefined; |
|
|
|
}, delayTime); |
|
|
|
} |
|
|
|
} else { |
|
|
|
// 鼠标离开
|
|
|
|
const leaveDelay = normalizedOptions.leaveDelay ?? DEFAULT_LEAVE_DELAY; |
|
|
|
const delayTime = isFunction(leaveDelay) ? leaveDelay() : leaveDelay; |
|
|
|
|
|
|
|
if (delayTime <= 0) { |
|
|
|
value.value = false; |
|
|
|
} else { |
|
|
|
leaveTimer.value = setTimeout(() => { |
|
|
|
value.value = false; |
|
|
|
leaveTimer.value = undefined; |
|
|
|
}, delayTime); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const watcher = watch( |
|
|
|
@ -61,7 +114,7 @@ export function useHoverToggle( |
|
|
|
}; |
|
|
|
|
|
|
|
onUnmounted(() => { |
|
|
|
timer.value && clearTimeout(timer.value); |
|
|
|
clearTimers(); |
|
|
|
}); |
|
|
|
|
|
|
|
return [value, controller] as [typeof value, typeof controller]; |
|
|
|
|