UNPKG

@zohodesk/hooks

Version:

Unified Component Library - Hooks

83 lines (75 loc) 2.21 kB
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 }; }