UNPKG

@wix/design-system

Version:

@wix/design-system

120 lines (118 loc) 4.11 kB
"use strict"; exports.__esModule = true; exports.useHover = exports.createHoverHandlers = void 0; var _react = require("react"); // Pointer-aware hover that ignores touch — without this, Stylable's JS-toggled // `:hovered` sticks on tap (mobile fires `mouseenter` but no `mouseleave`). // Mouse listeners are kept for unidriver test fakes; duplicate pointer/mouse // pairs from real browsers are deduped via WeakMap. var POINTER_TO_MOUSE_DEDUPE_MS = 50; var recentPointerEvents = new WeakMap(); var isHoverCapablePointer = pointerType => pointerType === 'mouse' || pointerType === 'pen'; var markPointerHandled = target => { if (target) { recentPointerEvents.set(target, Date.now()); } }; var recentlyHandledByPointer = target => { if (!target) return false; var at = recentPointerEvents.get(target); return at !== undefined && Date.now() - at < POINTER_TO_MOUSE_DEDUPE_MS; }; var useHover = exports.useHover = function useHover() { var { isDisabled, onHoverStart, onHoverEnd } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var [hovered, setHovered] = (0, _react.useState)(false); var isHoveredRef = (0, _react.useRef)(false); var onHoverStartRef = (0, _react.useRef)(onHoverStart); var onHoverEndRef = (0, _react.useRef)(onHoverEnd); (0, _react.useEffect)(() => { onHoverStartRef.current = onHoverStart; onHoverEndRef.current = onHoverEnd; }); var triggerHoverStart = (0, _react.useCallback)(event => { if (isHoveredRef.current) return; isHoveredRef.current = true; setHovered(true); onHoverStartRef.current == null || onHoverStartRef.current(event); }, []); var triggerHoverEnd = (0, _react.useCallback)(event => { if (!isHoveredRef.current) return; isHoveredRef.current = false; setHovered(false); onHoverEndRef.current == null || onHoverEndRef.current(event); }, []); (0, _react.useEffect)(() => { if (isDisabled && isHoveredRef.current) { isHoveredRef.current = false; setHovered(false); } }, [isDisabled]); var hoverProps = (0, _react.useMemo)(() => ({ onPointerEnter: event => { markPointerHandled(event.currentTarget); if (isDisabled || !isHoverCapablePointer(event.pointerType)) { return; } triggerHoverStart(event); }, onPointerLeave: event => { markPointerHandled(event.currentTarget); if (!isHoverCapablePointer(event.pointerType)) { return; } triggerHoverEnd(event); }, onMouseEnter: event => { if (recentlyHandledByPointer(event.currentTarget)) return; if (isDisabled) return; triggerHoverStart(event); }, onMouseLeave: event => { if (recentlyHandledByPointer(event.currentTarget)) return; triggerHoverEnd(event); } }), [isDisabled, triggerHoverStart, triggerHoverEnd]); return { hovered, hoverProps }; }; // Stateless variant for components that already manage hover state externally // (e.g. per-item indices in DropdownLayout, RadarChart, FacesRatingBar). var createHoverHandlers = exports.createHoverHandlers = function createHoverHandlers() { var { isDisabled, onHoverStart, onHoverEnd } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; return { onPointerEnter: event => { markPointerHandled(event.currentTarget); if (isDisabled || !isHoverCapablePointer(event.pointerType)) { return; } onHoverStart == null || onHoverStart(event); }, onPointerLeave: event => { markPointerHandled(event.currentTarget); if (!isHoverCapablePointer(event.pointerType)) { return; } onHoverEnd == null || onHoverEnd(event); }, onMouseEnter: event => { if (recentlyHandledByPointer(event.currentTarget)) return; if (isDisabled) return; onHoverStart == null || onHoverStart(event); }, onMouseLeave: event => { if (recentlyHandledByPointer(event.currentTarget)) return; onHoverEnd == null || onHoverEnd(event); } }; }; //# sourceMappingURL=useHover.js.map