Build NapCat Artifacts / Windows Loader Test (push) Canceled after 0s
Build NapCat Artifacts / Type Check & Test (push) Canceled after 0s
Build NapCat Artifacts / Build NapCat.Framework (push) Canceled after 0s
Build NapCat Artifacts / Build NapCat.Shell (push) Canceled after 0s
130 lines
6.5 KiB
Plaintext
130 lines
6.5 KiB
Plaintext
import {useRef as $fUfeP$useRef, useState as $fUfeP$useState, useEffect as $fUfeP$useEffect} from "react";
|
|
import {useInteractionModality as $fUfeP$useInteractionModality} from "@react-aria/interactions";
|
|
import {useResizeObserver as $fUfeP$useResizeObserver} from "@react-aria/utils";
|
|
|
|
|
|
|
|
|
|
const $d275435c250248f8$var$ALLOWED_INVALID_MOVEMENTS = 2;
|
|
const $d275435c250248f8$var$THROTTLE_TIME = 50;
|
|
const $d275435c250248f8$var$TIMEOUT_TIME = 1000;
|
|
const $d275435c250248f8$var$ANGLE_PADDING = Math.PI / 12; // 15°
|
|
function $d275435c250248f8$export$85ec83e04c95f50a(options) {
|
|
let { menuRef: menuRef, submenuRef: submenuRef, isOpen: isOpen, isDisabled: isDisabled } = options;
|
|
let prevPointerPos = (0, $fUfeP$useRef)(undefined);
|
|
let submenuRect = (0, $fUfeP$useRef)(undefined);
|
|
let lastProcessedTime = (0, $fUfeP$useRef)(0);
|
|
let timeout = (0, $fUfeP$useRef)(undefined);
|
|
let autoCloseTimeout = (0, $fUfeP$useRef)(undefined);
|
|
let submenuSide = (0, $fUfeP$useRef)(undefined);
|
|
let movementsTowardsSubmenuCount = (0, $fUfeP$useRef)(2);
|
|
let [preventPointerEvents, setPreventPointerEvents] = (0, $fUfeP$useState)(false);
|
|
let updateSubmenuRect = ()=>{
|
|
if (submenuRef.current) {
|
|
submenuRect.current = submenuRef.current.getBoundingClientRect();
|
|
submenuSide.current = undefined;
|
|
}
|
|
};
|
|
(0, $fUfeP$useResizeObserver)({
|
|
ref: submenuRef,
|
|
onResize: updateSubmenuRect
|
|
});
|
|
let reset = ()=>{
|
|
setPreventPointerEvents(false);
|
|
movementsTowardsSubmenuCount.current = $d275435c250248f8$var$ALLOWED_INVALID_MOVEMENTS;
|
|
prevPointerPos.current = undefined;
|
|
};
|
|
let modality = (0, $fUfeP$useInteractionModality)();
|
|
(0, $fUfeP$useEffect)(()=>{
|
|
if (preventPointerEvents && menuRef.current) menuRef.current.style.pointerEvents = 'none';
|
|
else menuRef.current.style.pointerEvents = '';
|
|
}, [
|
|
menuRef,
|
|
preventPointerEvents
|
|
]);
|
|
(0, $fUfeP$useEffect)(()=>{
|
|
let submenu = submenuRef.current;
|
|
let menu = menuRef.current;
|
|
if (isDisabled || !submenu || !isOpen || modality !== 'pointer' || !menu) {
|
|
reset();
|
|
return;
|
|
}
|
|
submenuRect.current = submenu.getBoundingClientRect();
|
|
let onPointerMove = (e)=>{
|
|
if (e.pointerType === 'touch' || e.pointerType === 'pen') return;
|
|
let currentTime = Date.now();
|
|
// Throttle
|
|
if (currentTime - lastProcessedTime.current < $d275435c250248f8$var$THROTTLE_TIME) return;
|
|
clearTimeout(timeout.current);
|
|
clearTimeout(autoCloseTimeout.current);
|
|
let { clientX: mouseX, clientY: mouseY } = e;
|
|
if (!prevPointerPos.current) {
|
|
prevPointerPos.current = {
|
|
x: mouseX,
|
|
y: mouseY
|
|
};
|
|
return;
|
|
}
|
|
if (!submenuRect.current) return;
|
|
if (!submenuSide.current) submenuSide.current = mouseX > submenuRect.current.right ? 'left' : 'right';
|
|
// Pointer is outside of parent menu
|
|
if (mouseX < menu.getBoundingClientRect().left || mouseX > menu.getBoundingClientRect().right || mouseY < menu.getBoundingClientRect().top || mouseY > menu.getBoundingClientRect().bottom) {
|
|
reset();
|
|
return;
|
|
}
|
|
/* Check if pointer is moving towards submenu.
|
|
Uses the 2-argument arctangent (https://en.wikipedia.org/wiki/Atan2) to calculate:
|
|
- angle between previous pointer and top of submenu
|
|
- angle between previous pointer and bottom of submenu
|
|
- angle between previous pointer and current pointer (delta)
|
|
If the pointer delta angle value is between the top and bottom angle values, we know the pointer is moving towards the submenu.
|
|
*/ let prevMouseX = prevPointerPos.current.x;
|
|
let prevMouseY = prevPointerPos.current.y;
|
|
let toSubmenuX = submenuSide.current === 'right' ? submenuRect.current.left - prevMouseX : prevMouseX - submenuRect.current.right;
|
|
let angleTop = Math.atan2(prevMouseY - submenuRect.current.top, toSubmenuX) + $d275435c250248f8$var$ANGLE_PADDING;
|
|
let angleBottom = Math.atan2(prevMouseY - submenuRect.current.bottom, toSubmenuX) - $d275435c250248f8$var$ANGLE_PADDING;
|
|
let anglePointer = Math.atan2(prevMouseY - mouseY, submenuSide.current === 'left' ? -(mouseX - prevMouseX) : mouseX - prevMouseX);
|
|
let isMovingTowardsSubmenu = anglePointer < angleTop && anglePointer > angleBottom;
|
|
movementsTowardsSubmenuCount.current = isMovingTowardsSubmenu ? Math.min(movementsTowardsSubmenuCount.current + 1, $d275435c250248f8$var$ALLOWED_INVALID_MOVEMENTS) : Math.max(movementsTowardsSubmenuCount.current - 1, 0);
|
|
if (movementsTowardsSubmenuCount.current >= $d275435c250248f8$var$ALLOWED_INVALID_MOVEMENTS) setPreventPointerEvents(true);
|
|
else setPreventPointerEvents(false);
|
|
lastProcessedTime.current = currentTime;
|
|
prevPointerPos.current = {
|
|
x: mouseX,
|
|
y: mouseY
|
|
};
|
|
// If the pointer is moving towards the submenu, start a timeout to close if no other movements are made after 500ms.
|
|
if (isMovingTowardsSubmenu) timeout.current = setTimeout(()=>{
|
|
reset();
|
|
autoCloseTimeout.current = setTimeout(()=>{
|
|
// Fire a pointerover event to trigger the menu to close.
|
|
// Wait until pointer-events:none is no longer applied
|
|
let target = document.elementFromPoint(mouseX, mouseY);
|
|
if (target && menu.contains(target)) target.dispatchEvent(new PointerEvent('pointerover', {
|
|
bubbles: true,
|
|
cancelable: true
|
|
}));
|
|
}, 100);
|
|
}, $d275435c250248f8$var$TIMEOUT_TIME);
|
|
};
|
|
window.addEventListener('pointermove', onPointerMove);
|
|
return ()=>{
|
|
window.removeEventListener('pointermove', onPointerMove);
|
|
clearTimeout(timeout.current);
|
|
clearTimeout(autoCloseTimeout.current);
|
|
movementsTowardsSubmenuCount.current = $d275435c250248f8$var$ALLOWED_INVALID_MOVEMENTS;
|
|
};
|
|
}, [
|
|
isDisabled,
|
|
isOpen,
|
|
menuRef,
|
|
modality,
|
|
setPreventPointerEvents,
|
|
submenuRef
|
|
]);
|
|
}
|
|
|
|
|
|
export {$d275435c250248f8$export$85ec83e04c95f50a as useSafelyMouseToSubmenu};
|
|
//# sourceMappingURL=useSafelyMouseToSubmenu.module.js.map
|