Private
Public Access
1
0
Files

147 lines
5.7 KiB
JavaScript

'use client';
import { useAnimationFrame, useEventCallback, useMergedRefs, useTimeout } from '@fluentui/react-utilities';
import * as React from 'react';
import { createSafeZoneAreaStateStore } from './createSafeZoneAreaStateStore';
import { SafeZoneArea } from './SafeZoneArea';
/**
* Time in milliseconds after which the safe zone area will be cleared if no mouse movement is detected.
*
* Only affects the target element, not the safe zone area itself.
*/ const MOUSE_MOVE_TARGET_POLLING_TIMEOUT = 2000;
// ---
export function useSafeZoneArea({ debug = false, disabled = false, onSafeZoneEnter, onSafeZoneMove, onSafeZoneLeave, onSafeZoneTimeout, timeout = 1500 } = {}) {
const [stateStore] = React.useState(createSafeZoneAreaStateStore);
const safeZoneAreaRef = React.useRef(null);
const containerRef = React.useRef(null);
const targetRef = React.useRef(null);
const [setSafeZoneCloseTimeout, clearSafeZoneCloseTimeout] = useTimeout();
const [requestUpdateFrame, clearUpdateFrame] = useAnimationFrame();
const mouseCoordinatesRef = React.useRef({
x: 0,
y: 0
});
const containerListenerRef = React.useMemo(()=>{
if (disabled) {
return ()=>{
// do nothing
};
}
let containerEl = null;
function onContainerMouseEnter() {
clearSafeZoneCloseTimeout();
stateStore.toggleActive(false);
}
return (el)=>{
if (el === null) {
containerEl === null || containerEl === void 0 ? void 0 : containerEl.removeEventListener('mouseenter', onContainerMouseEnter);
}
containerEl = el;
el === null || el === void 0 ? void 0 : el.addEventListener('mouseenter', onContainerMouseEnter);
};
}, [
clearSafeZoneCloseTimeout,
disabled,
stateStore
]);
const targetListenerRef = React.useMemo(()=>{
if (disabled) {
return ()=>{
// do nothing
};
}
let targetEl = null;
function onTargetMouseMove(e) {
mouseCoordinatesRef.current = {
x: e.clientX,
y: e.clientY
};
if (!stateStore.isActive()) {
stateStore.toggleActive(true);
}
setSafeZoneCloseTimeout(()=>{
stateStore.toggleActive(false);
}, MOUSE_MOVE_TARGET_POLLING_TIMEOUT);
}
return (el)=>{
if (el === null) {
clearUpdateFrame();
clearSafeZoneCloseTimeout();
targetEl === null || targetEl === void 0 ? void 0 : targetEl.removeEventListener('mousemove', onTargetMouseMove);
}
targetEl = el;
el === null || el === void 0 ? void 0 : el.addEventListener('mousemove', onTargetMouseMove);
};
}, [
clearUpdateFrame,
clearSafeZoneCloseTimeout,
disabled,
stateStore,
setSafeZoneCloseTimeout
]);
const onSvgMouseEnter = useEventCallback((e)=>{
onSafeZoneEnter === null || onSafeZoneEnter === void 0 ? void 0 : onSafeZoneEnter(e);
setSafeZoneCloseTimeout(()=>{
stateStore.toggleActive(false);
onSafeZoneTimeout === null || onSafeZoneTimeout === void 0 ? void 0 : onSafeZoneTimeout();
}, timeout);
});
const onSvgMouseMove = useEventCallback((e)=>{
setSafeZoneCloseTimeout(()=>{
stateStore.toggleActive(false);
onSafeZoneTimeout === null || onSafeZoneTimeout === void 0 ? void 0 : onSafeZoneTimeout();
}, timeout);
onSafeZoneMove === null || onSafeZoneMove === void 0 ? void 0 : onSafeZoneMove(e);
});
const onSvgMouseLeave = useEventCallback((e)=>{
onSafeZoneLeave === null || onSafeZoneLeave === void 0 ? void 0 : onSafeZoneLeave(e);
});
React.useEffect(()=>{
return stateStore.subscribe((isActive)=>{
if (isActive) {
function updateSVGs() {
const containerEl = containerRef.current;
const targetEl = targetRef.current;
if (containerEl && targetEl) {
var _safeZoneAreaRef_current;
(_safeZoneAreaRef_current = safeZoneAreaRef.current) === null || _safeZoneAreaRef_current === void 0 ? void 0 : _safeZoneAreaRef_current.updateSVG({
containerRect: containerEl.getBoundingClientRect(),
mouseCoordinates: [
mouseCoordinatesRef.current.x,
mouseCoordinatesRef.current.y
],
targetRect: targetEl.getBoundingClientRect()
});
}
requestUpdateFrame(updateSVGs);
}
updateSVGs();
return;
}
clearUpdateFrame();
});
}, [
clearUpdateFrame,
requestUpdateFrame,
stateStore
]);
return {
containerRef: useMergedRefs(containerRef, containerListenerRef),
targetRef: useMergedRefs(targetRef, targetListenerRef),
elementToRender: React.useMemo(()=>disabled ? null : /*#__PURE__*/ React.createElement(SafeZoneArea, {
debug: debug,
onMouseEnter: onSvgMouseEnter,
onMouseMove: onSvgMouseMove,
onMouseLeave: onSvgMouseLeave,
imperativeRef: safeZoneAreaRef,
stateStore: stateStore
}), [
disabled,
debug,
onSvgMouseEnter,
onSvgMouseMove,
onSvgMouseLeave,
stateStore
])
};
}