use-hover-toggle.ts 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. import type { Arrayable, MaybeElementRef } from '@vueuse/core';
  2. import { computed, onUnmounted, ref, watch } from 'vue';
  3. import type { Ref } from 'vue';
  4. import { isFunction } from '@vben/utils';
  5. import { useMouseInElement } from '@vueuse/core';
  6. /**
  7. * 监测鼠标是否在元素内部,如果在元素内部则返回 true,否则返回 false
  8. * @param refElement 所有需要检测的元素。如果提供了一个数组,那么鼠标在任何一个元素内部都会返回 true
  9. * @param delay 延迟更新状态的时间
  10. * @returns 返回一个数组,第一个元素是一个 ref,表示鼠标是否在元素内部,第二个元素是一个控制器,可以通过 enable 和 disable 方法来控制监听器的启用和禁用
  11. */
  12. export function useHoverToggle(
  13. refElement: Arrayable<MaybeElementRef>,
  14. delay: (() => number) | number = 500,
  15. ) {
  16. const isOutsides: Array<Ref<boolean>> = [];
  17. const value = ref(false);
  18. const timer = ref<ReturnType<typeof setTimeout> | undefined>();
  19. const refs = Array.isArray(refElement) ? refElement : [refElement];
  20. refs.forEach((refEle) => {
  21. const listener = useMouseInElement(refEle, { handleOutside: true });
  22. isOutsides.push(listener.isOutside);
  23. });
  24. const isOutsideAll = computed(() => isOutsides.every((v) => v.value));
  25. function setValueDelay(val: boolean) {
  26. timer.value && clearTimeout(timer.value);
  27. timer.value = setTimeout(
  28. () => {
  29. value.value = val;
  30. timer.value = undefined;
  31. },
  32. isFunction(delay) ? delay() : delay,
  33. );
  34. }
  35. const watcher = watch(
  36. isOutsideAll,
  37. (val) => {
  38. setValueDelay(!val);
  39. },
  40. { immediate: true },
  41. );
  42. const controller = {
  43. enable() {
  44. watcher.resume();
  45. },
  46. disable() {
  47. watcher.pause();
  48. },
  49. };
  50. onUnmounted(() => {
  51. timer.value && clearTimeout(timer.value);
  52. });
  53. return [value, controller] as [typeof value, typeof controller];
  54. }