@zohodesk/hooks
Version:
Unified Component Library - Hooks
83 lines (75 loc) • 2.21 kB
JavaScript
import { useState, useRef, useEffect } from "react";
export default function useHoverDisclosure() {
let activeClass = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "";
let eventHandlers = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
const [isOpen, setIsOpen] = useState(false);
const containerRef = useRef(null);
const lastPointerType = useRef("mouse");
const isHoverDeviceRef = useRef(false);
const {
onClick,
onBlur,
onPointerDown,
onPointerLeave,
onPointerEnter,
onKeyDown
} = eventHandlers; // Detect if device supports real hover
useEffect(() => {
if (typeof window !== "undefined") {
isHoverDeviceRef.current = window.matchMedia("(hover: hover)").matches;
}
}, []);
const triggerProps = {
ref: containerRef,
// Track pointer type
onPointerDown: e => {
lastPointerType.current = e.pointerType;
onPointerDown?.(e);
},
// Hover open (only if device supports hover AND pointer is mouse)
onPointerEnter: e => {
if (isHoverDeviceRef.current && e.pointerType === "mouse") {
setIsOpen(true);
}
onPointerEnter?.(e);
},
onPointerLeave: e => {
if (isHoverDeviceRef.current && e.pointerType === "mouse") {
setIsOpen(false);
}
onPointerLeave?.(e);
},
// Toggle for non-mouse OR non-hover devices
onClick: e => {
const isTouchInteraction = lastPointerType.current !== "mouse" || !isHoverDeviceRef.current;
if (isTouchInteraction) {
e.stopPropagation();
setIsOpen(prev => !prev);
}
onClick?.(e);
},
// Keyboard support
onKeyDown: e => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
setIsOpen(prev => !prev);
}
if (e.key === "Escape") {
setIsOpen(false);
}
onKeyDown?.(e);
},
// Close when focus leaves
onBlur: e => {
if (containerRef.current && !containerRef.current.contains(e.relatedTarget)) {
setIsOpen(false);
}
onBlur?.(e);
}
};
return {
isOpen,
activeClass: isOpen ? activeClass : "",
triggerProps
};
}