Selaa lähdekoodia

feat(hooks): support separate enter/leave delays in useHoverToggle (#6325)

Co-authored-by: xiaobin <xiaobin_chen@fzzixun.com>
broBinChen 2 viikkoa sitten
vanhempi
commit
b69320c070
1 muutettua tiedostoa jossa 65 lisäystä ja 12 poistoa
  1. 65 12
      packages/effects/hooks/src/use-hover-toggle.ts

+ 65 - 12
packages/effects/hooks/src/use-hover-toggle.ts

@@ -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];