"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const react_1 = require("react");
function clamp(num, min, max) {
    return num <= min ? min : num >= max ? max : num;
}
function useDraggable({ onMove, onDrop, getBoundingRect = () => document.body.getBoundingClientRect(), handleSelector, }) {
    const startRef = (0, react_1.useRef)(null);
    const handleRef = (0, react_1.useRef)(null);
    const targetRef = (0, react_1.useRef)(null);
    const handleRefCallback = (0, react_1.useCallback)((newNode) => {
        const oldNode = handleRef.current;
        if (oldNode) {
            oldNode.removeEventListener('mousedown', handleMouseDown);
        }
        if (newNode) {
            newNode.addEventListener('mousedown', handleMouseDown);
            boundInitialPosition();
        }
        handleRef.current = newNode;
    }, []);
    (0, react_1.useEffect)(() => {
        if (handleSelector) {
            /**
             * Element may not be yet attached.
             * This gives time react to render the node handle.
             */
            setTimeout(() => {
                const node = targetRef?.current?.querySelector(handleSelector);
                node && handleRefCallback(node);
            }, 16);
        }
    }, [handleSelector]);
    const applyTransform = (dx, dy) => {
        if (!targetRef.current)
            return;
        targetRef.current.style.transform = `translate3d(${dx}px, ${dy}px, 0)`;
    };
    const getTranslation = (event) => {
        if (!startRef.current)
            return { dx: 0, dy: 0 };
        const { pageX, pageY, bounds } = startRef.current;
        return {
            dx: clamp(event.pageX - pageX, bounds.left, bounds.right),
            dy: clamp(event.pageY - pageY, bounds.top, bounds.bottom),
        };
    };
    const getTranslationBounds = (targetRect, boundingRect) => {
        const left = boundingRect.x - targetRect.x;
        const right = left + boundingRect.width - targetRect.width;
        const top = boundingRect.y - targetRect.y;
        const bottom = top + boundingRect.height - targetRect.height;
        return { left, right, top, bottom };
    };
    const handleMouseDown = (event) => {
        if (!targetRef.current)
            return;
        event.preventDefault();
        document.addEventListener('mouseup', handleMouseUp);
        document.addEventListener('mousemove', handleMouseMove);
        const targetRect = targetRef.current.getBoundingClientRect();
        const boundingRect = getBoundingRect();
        startRef.current = {
            pageX: event.pageX,
            pageY: event.pageY,
            bounds: getTranslationBounds(targetRect, boundingRect),
        };
    };
    const handleMouseUp = (event) => {
        event.preventDefault();
        document.removeEventListener('mouseup', handleMouseUp);
        document.removeEventListener('mousemove', handleMouseMove);
        const { dx, dy } = getTranslation(event);
        applyTransform(0, 0);
        onDrop && onDrop(dx, dy);
    };
    const handleMouseMove = (event) => {
        event.preventDefault();
        const { dx, dy } = getTranslation(event);
        applyTransform(dx, dy);
        onMove && onMove(event);
    };
    const boundInitialPosition = () => {
        if (!targetRef.current)
            return;
        const targetRect = targetRef.current.getBoundingClientRect();
        const boundingRect = getBoundingRect();
        const bounds = getTranslationBounds(targetRect, boundingRect);
        const dx = clamp(0, bounds.left, bounds.right);
        const dy = clamp(0, bounds.top, bounds.bottom);
        if (onDrop && (dx !== 0 || dy !== 0))
            onDrop(dx, dy);
    };
    return { handleRef: handleRefCallback, targetRef, applyTransform };
}
exports.default = useDraggable;
