@wix/design-system
Version:
@wix/design-system
120 lines (118 loc) • 4.11 kB
JavaScript
;
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