import { useEffect, useRef } from 'react';
/**
 * A custom hook that enables dragging and keyboard-based movement for docked modals.
 *
 * @param props - The hook configuration options
 * @param props.enabled - Whether the move functionality is enabled
 * @param props.moveRef - Reference to the element that will be moved
 * @param [props.grabRef] - Optional reference to the element used as the drag handle (defaults to moveRef)
 *
 * @returns A ref object containing information about move interactions
 *
 * @remarks
 * - Supports mouse drag (left mouse button) to horizontally reposition the modal
 * - Supports keyboard arrow keys for incremental movement when focused
 * - Holding Alt key prevents dragging and changes cursor to 'auto'
 * - Movement is constrained within viewport boundaries
 * - Uses requestAnimationFrame for smooth drag performance
 * - Keyboard movements are 5% of viewport width per key press
 */
const useModalMove = ({ enabled, moveRef, grabRef = moveRef }) => {
    const initialMouseDownX = useRef();
    const initialRect = useRef();
    const viewportWidth = useRef();
    const rafRef = useRef(-1);
    const left = useRef();
    const returnRef = useRef({
        mouseMoved: false
    });
    useEffect(() => {
        if (!enabled || !moveRef.current || !grabRef.current)
            return;
        // Handles applying the inline style for movement
        const applyMove = (amount) => {
            if (!moveRef.current || !viewportWidth.current || !initialRect.current) {
                return;
            }
            // Keep a ref to left position changes. Re-applied to docked modals after re-open.
            left.current = amount;
            // Clamp within viewport boundaries
            moveRef.current.style.left = `clamp(0px, ${amount}px, ${viewportWidth.current - initialRect.current.width}px)`;
        };
        // Apply any prior movement
        if (left.current !== undefined) {
            // Capture so applyMove has fresh measurements on each key move
            initialRect.current = moveRef.current.getBoundingClientRect();
            viewportWidth.current = document.documentElement.clientWidth;
            applyMove(left.current);
        }
        const onMouseMove = (e) => {
            if (!initialMouseDownX.current || !moveRef.current || !initialRect.current) {
                return;
            }
            // The delta of change on the X axis since the user grabbed the modal
            const movedSinceMouseDown = e.clientX - initialMouseDownX.current;
            // The new left position of the modal
            const newLeft = movedSinceMouseDown + initialRect.current.left;
            // Set flag to indicate upcoming mouseup is from user drag, not click
            returnRef.current.mouseMoved = true;
            // Avoids excessive and unnecessary update calls
            cancelAnimationFrame(rafRef.current);
            rafRef.current = requestAnimationFrame(() => {
                applyMove(newLeft);
            });
        };
        const onMouseUp = () => {
            if (!grabRef.current)
                return;
            // Remove to allow component CSS to take effect
            grabRef.current.style.userSelect = '';
            document.removeEventListener('mousemove', onMouseMove);
        };
        const onMouseDown = (e) => {
            // When Alt key is press disable grab move functionality to allow for user select
            const altPressed = e.getModifierState('Alt');
            // Only handle primary/left mouse button
            const mousePrimaryPressed = e.button === 0;
            if (!moveRef.current || !grabRef.current || !mousePrimaryPressed || altPressed) {
                return;
            }
            // Need to listen for mouseup on Document since pointer may move out of modal header at time of release.
            document.addEventListener('mouseup', onMouseUp, { once: true });
            // Where the user starts grabbing
            initialMouseDownX.current = e.clientX;
            // The rect of the modal el
            initialRect.current = moveRef.current.getBoundingClientRect();
            // Viewport width not including scrollbars
            viewportWidth.current = document.documentElement.clientWidth;
            // Disable user select while dragging to avoid awkward text selection
            grabRef.current.style.userSelect = 'none';
            // Listen to mouse movement
            document.addEventListener('mousemove', onMouseMove);
            // Reset the return ref flag
            returnRef.current.mouseMoved = false;
        };
        const onKeyUp = (e) => {
            if (grabRef.current && e.key === 'Alt') {
                // Remove to allow component CSS to take effect
                grabRef.current.style.cursor = '';
            }
        };
        const onKeyDown = (e) => {
            if (!moveRef.current)
                return;
            // If the user is presssing Alt allow for standard cursor behavior
            if (grabRef.current && e.key === 'Alt') {
                grabRef.current.style.cursor = 'auto';
            }
            if (
            // Only move on arrow keys if the modal is focus visible i.e. user keyboard tabbed to it
            !grabRef.current?.matches(':focus-visible') ||
                (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight')) {
                return;
            }
            // Capture so applyMove has fresh measurements on each key move
            initialRect.current = moveRef.current.getBoundingClientRect();
            viewportWidth.current = document.documentElement.clientWidth;
            // Move in steps proportionally to viewport width
            const keyMoveAmount = viewportWidth.current * 0.05;
            const direction = e.key === 'ArrowLeft' ? -1 : 1;
            const currentLeft = initialRect.current.left;
            const newLeft = currentLeft + keyMoveAmount * direction;
            applyMove(newLeft);
        };
        const onResize = () => {
            if (!moveRef.current || left.current === undefined)
                return;
            // Update measurements for new viewport size
            initialRect.current = moveRef.current.getBoundingClientRect();
            viewportWidth.current = document.documentElement.clientWidth;
            // Re-apply move to clamp within new viewport bounds
            applyMove(left.current);
        };
        grabRef.current.addEventListener('mousedown', onMouseDown);
        document.addEventListener('keydown', onKeyDown);
        document.addEventListener('keyup', onKeyUp);
        window.addEventListener('resize', onResize);
        return () => {
            grabRef.current?.removeEventListener('mousedown', onMouseDown);
            document.removeEventListener('mousemove', onMouseMove);
            document.removeEventListener('keydown', onKeyDown);
            document.removeEventListener('keyup', onKeyUp);
            window.removeEventListener('resize', onResize);
        };
    }, [enabled]);
    return returnRef;
};
export default useModalMove;
//# sourceMappingURL=useModalMove.js.map