'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 ]) }; }