You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
175 lines
4.8 KiB
175 lines
4.8 KiB
/**
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
*
|
|
*/
|
|
import { getModality } from '../modality';
|
|
import useEvent from '../useEvent';
|
|
import useLayoutEffect from '../useLayoutEffect';
|
|
/**
|
|
* Types
|
|
*/
|
|
|
|
/**
|
|
* Implementation
|
|
*/
|
|
var emptyObject = {};
|
|
var opts = {
|
|
passive: true
|
|
};
|
|
var lockEventType = 'react-gui:hover:lock';
|
|
var unlockEventType = 'react-gui:hover:unlock';
|
|
|
|
var supportsPointerEvent = () => !!(typeof window !== 'undefined' && window.PointerEvent != null);
|
|
|
|
function dispatchCustomEvent(target, type, payload) {
|
|
var event = document.createEvent('CustomEvent');
|
|
|
|
var _ref = payload || emptyObject,
|
|
_ref$bubbles = _ref.bubbles,
|
|
bubbles = _ref$bubbles === void 0 ? true : _ref$bubbles,
|
|
_ref$cancelable = _ref.cancelable,
|
|
cancelable = _ref$cancelable === void 0 ? true : _ref$cancelable,
|
|
detail = _ref.detail;
|
|
|
|
event.initCustomEvent(type, bubbles, cancelable, detail);
|
|
target.dispatchEvent(event);
|
|
} // This accounts for the non-PointerEvent fallback events.
|
|
|
|
|
|
function getPointerType(event) {
|
|
var pointerType = event.pointerType;
|
|
return pointerType != null ? pointerType : getModality();
|
|
}
|
|
|
|
export default function useHover(targetRef, config) {
|
|
var contain = config.contain,
|
|
disabled = config.disabled,
|
|
onHoverStart = config.onHoverStart,
|
|
onHoverChange = config.onHoverChange,
|
|
onHoverUpdate = config.onHoverUpdate,
|
|
onHoverEnd = config.onHoverEnd;
|
|
var canUsePE = supportsPointerEvent();
|
|
var addMoveListener = useEvent(canUsePE ? 'pointermove' : 'mousemove', opts);
|
|
var addEnterListener = useEvent(canUsePE ? 'pointerenter' : 'mouseenter', opts);
|
|
var addLeaveListener = useEvent(canUsePE ? 'pointerleave' : 'mouseleave', opts); // These custom events are used to implement the "contain" prop.
|
|
|
|
var addLockListener = useEvent(lockEventType, opts);
|
|
var addUnlockListener = useEvent(unlockEventType, opts);
|
|
useLayoutEffect(() => {
|
|
var target = targetRef.current;
|
|
|
|
if (target !== null) {
|
|
/**
|
|
* End the hover gesture
|
|
*/
|
|
var hoverEnd = function hoverEnd(e) {
|
|
if (onHoverEnd != null) {
|
|
onHoverEnd(e);
|
|
}
|
|
|
|
if (onHoverChange != null) {
|
|
onHoverChange(false);
|
|
} // Remove the listeners once finished.
|
|
|
|
|
|
addMoveListener(target, null);
|
|
addLeaveListener(target, null);
|
|
};
|
|
/**
|
|
* Leave element
|
|
*/
|
|
|
|
|
|
var leaveListener = function leaveListener(e) {
|
|
var target = targetRef.current;
|
|
|
|
if (target != null && getPointerType(e) !== 'touch') {
|
|
if (contain) {
|
|
dispatchCustomEvent(target, unlockEventType);
|
|
}
|
|
|
|
hoverEnd(e);
|
|
}
|
|
};
|
|
/**
|
|
* Move within element
|
|
*/
|
|
|
|
|
|
var moveListener = function moveListener(e) {
|
|
if (getPointerType(e) !== 'touch') {
|
|
if (onHoverUpdate != null) {
|
|
// Not all browsers have these properties
|
|
if (e.x == null) {
|
|
e.x = e.clientX;
|
|
}
|
|
|
|
if (e.y == null) {
|
|
e.y = e.clientY;
|
|
}
|
|
|
|
onHoverUpdate(e);
|
|
}
|
|
}
|
|
};
|
|
/**
|
|
* Start the hover gesture
|
|
*/
|
|
|
|
|
|
var hoverStart = function hoverStart(e) {
|
|
if (onHoverStart != null) {
|
|
onHoverStart(e);
|
|
}
|
|
|
|
if (onHoverChange != null) {
|
|
onHoverChange(true);
|
|
} // Set the listeners needed for the rest of the hover gesture.
|
|
|
|
|
|
if (onHoverUpdate != null) {
|
|
addMoveListener(target, !disabled ? moveListener : null);
|
|
}
|
|
|
|
addLeaveListener(target, !disabled ? leaveListener : null);
|
|
};
|
|
/**
|
|
* Enter element
|
|
*/
|
|
|
|
|
|
var enterListener = function enterListener(e) {
|
|
var target = targetRef.current;
|
|
|
|
if (target != null && getPointerType(e) !== 'touch') {
|
|
if (contain) {
|
|
dispatchCustomEvent(target, lockEventType);
|
|
}
|
|
|
|
hoverStart(e);
|
|
|
|
var lockListener = function lockListener(lockEvent) {
|
|
if (lockEvent.target !== target) {
|
|
hoverEnd(e);
|
|
}
|
|
};
|
|
|
|
var unlockListener = function unlockListener(lockEvent) {
|
|
if (lockEvent.target !== target) {
|
|
hoverStart(e);
|
|
}
|
|
};
|
|
|
|
addLockListener(target, !disabled ? lockListener : null);
|
|
addUnlockListener(target, !disabled ? unlockListener : null);
|
|
}
|
|
};
|
|
|
|
addEnterListener(target, !disabled ? enterListener : null);
|
|
}
|
|
}, [addEnterListener, addMoveListener, addLeaveListener, addLockListener, addUnlockListener, contain, disabled, onHoverStart, onHoverChange, onHoverUpdate, onHoverEnd, targetRef]);
|
|
} |