"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                t[p[i]] = s[p[i]];
        }
    return t;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DragAndDropItem = exports.DragAndDrop = void 0;
/** biome-ignore-all lint/correctness/useHookAtTopLevel: False positive because of the way forwardRef() is added */
const react_1 = __importStar(require("react"));
const utils_external_1 = require("../../../utils-external");
const Floating_1 = require("../../../utils/components/floating/Floating");
const helpers_1 = require("../../../utils/helpers");
const DragAndDropItem_1 = __importDefault(require("../item/DragAndDropItem"));
exports.DragAndDropItem = DragAndDropItem_1.default;
const DragAndDrop_context_1 = require("./DragAndDrop.context");
/**
 * TODO:
 *
 * Backlog:
 * [ ] Quick nav (< > samtidig) - få piltastene til å fungere - ignore?
 * [ ] Look at instructions text
 *
 */
const DRAG_THRESHOLD = 4; // Minimum movement in pixels to start dragging
function DragAndDropInner(_a, forwardedRef) {
    var _b;
    var { items, setItems, renderItem, className } = _a, rest = __rest(_a, ["items", "setItems", "renderItem", "className"]);
    const instructionsId = (0, utils_external_1.useId)();
    const [activeItem, setActiveItem] = (0, react_1.useState)(null);
    const [dropTarget, setDropTarget] = (0, react_1.useState)(null);
    const [dragHandlerActive, setDragHandlerActive] = (0, react_1.useState)(null);
    const [overlayWidth, setOverlayWidth] = (0, react_1.useState)(null);
    const [announcer, setAnnouncer] = (0, react_1.useState)("");
    const initialItemsRef = (0, react_1.useRef)(null);
    const virtualPositionRef = (0, react_1.useRef)({ x: 0, y: 0 });
    const itemsById = (0, react_1.useMemo)(() => new Map(items.map((item) => [item.id, item])), [items]);
    const activeData = activeItem ? itemsById.get(activeItem.id) : undefined;
    const activeItemRef = (0, react_1.useRef)(null);
    const dropTargetRef = (0, react_1.useRef)(null);
    const virtualRef = (0, react_1.useMemo)(() => ({
        getBoundingClientRect: () => DOMRect.fromRect({
            width: 0,
            height: 0,
            x: virtualPositionRef.current.x,
            y: virtualPositionRef.current.y,
        }),
    }), []);
    const pendingDragStartRef = (0, react_1.useRef)(null);
    const saveInitialItems = (0, react_1.useCallback)(() => {
        initialItemsRef.current = items;
    }, [items]);
    const keyboardDragStart = (item) => {
        if (item) {
            saveInitialItems();
        }
        else {
            initialItemsRef.current = null;
        }
        setDragHandlerActive(item);
    };
    const startPendingDrag = (event, item, element) => {
        pendingDragStartRef.current = {
            item,
            element: element || null,
            pointerId: event.pointerId,
            startX: event.clientX,
            startY: event.clientY,
        };
    };
    const setCombinedActiveItem = (0, react_1.useCallback)((item) => {
        activeItemRef.current = item;
        setActiveItem(item);
    }, []);
    const setCombinedDropTarget = (0, react_1.useCallback)((item) => {
        const previous = dropTargetRef.current;
        if ((previous === null || previous === void 0 ? void 0 : previous.id) === (item === null || item === void 0 ? void 0 : item.id) && (previous === null || previous === void 0 ? void 0 : previous.index) === (item === null || item === void 0 ? void 0 : item.index)) {
            return;
        }
        dropTargetRef.current = item;
        setDropTarget(item);
    }, []);
    const reorderItems = (0, react_1.useCallback)((fromIndex, toIndex) => {
        const currentItems = items;
        if (fromIndex === toIndex ||
            fromIndex < 0 ||
            toIndex < 0 ||
            fromIndex >= currentItems.length ||
            toIndex >= currentItems.length) {
            return;
        }
        const newItems = [...currentItems];
        const [movedItem] = newItems.splice(fromIndex, 1);
        if (!movedItem) {
            return;
        }
        newItems.splice(toIndex, 0, movedItem);
        setItems(newItems);
    }, [items, setItems]);
    const cancelDrag = (0, react_1.useCallback)((resetOrder = false) => {
        if (resetOrder && initialItemsRef.current) {
            setItems(initialItemsRef.current);
        }
        setOverlayWidth(null);
        setDragHandlerActive(null);
        setCombinedActiveItem(null);
        setCombinedDropTarget(null);
        pendingDragStartRef.current = null;
        initialItemsRef.current = null;
    }, [setItems, setCombinedActiveItem, setCombinedDropTarget]);
    (0, react_1.useEffect)(() => {
        /* This useEffect is used to toggle a class on the html element when dragging,
          to prevent cursor issues when dragging over interactive elements,
          and to prevent text selection during dragging. */
        if (activeItem) {
            document.documentElement.setAttribute("data-dragging-cursor", "true");
            document.body.style.userSelect = "none";
        }
        else {
            document.documentElement.removeAttribute("data-dragging-cursor");
            document.body.style.userSelect = "";
        }
        return () => {
            document.documentElement.removeAttribute("data-dragging-cursor");
            document.body.style.userSelect = "";
        };
    }, [activeItem]);
    (0, react_1.useEffect)(() => {
        const handlePointerMove = (event) => {
            var _a, _b, _c;
            const pendingStart = pendingDragStartRef.current;
            const activeRef = activeItemRef.current;
            const element = pendingStart === null || pendingStart === void 0 ? void 0 : pendingStart.element;
            if (!activeRef && pendingStart) {
                const deltaX = Math.abs(event.clientX - pendingStart.startX);
                const deltaY = Math.abs(event.clientY - pendingStart.startY);
                if (deltaX >= DRAG_THRESHOLD || deltaY >= DRAG_THRESHOLD) {
                    if (element) {
                        element.setPointerCapture(pendingStart.pointerId);
                    }
                    setOverlayWidth((_a = element === null || element === void 0 ? void 0 : element.getBoundingClientRect().width) !== null && _a !== void 0 ? _a : null);
                    saveInitialItems();
                    setCombinedActiveItem(pendingStart.item);
                    setCombinedDropTarget(pendingStart.item);
                    pendingDragStartRef.current = null;
                }
                return;
            }
            const active = activeItemRef.current;
            if (!active)
                return;
            virtualPositionRef.current = { x: event.clientX, y: event.clientY };
            const elements = document.elementsFromPoint(event.clientX, event.clientY);
            const matchingElements = elements.filter((el) => el instanceof HTMLElement && Boolean(el.closest("[data-dnd-id]")));
            const itemElements = matchingElements
                .map((el) => el.closest("[data-dnd-id]"))
                .filter((el) => el instanceof HTMLElement);
            const uniqueItemElements = Array.from(new Set(itemElements));
            const targetElement = (_c = (_b = uniqueItemElements.find((el) => el.dataset.dndId !== active.id)) !== null && _b !== void 0 ? _b : uniqueItemElements.find((el) => el.dataset.dndId === active.id)) !== null && _c !== void 0 ? _c : null;
            if (!targetElement) {
                setCombinedDropTarget(null);
                return;
            }
            const hoveredId = targetElement.dataset.dndId;
            const hoveredIndex = Number(targetElement.dataset.dndIndex);
            if (!hoveredId || Number.isNaN(hoveredIndex)) {
                setCombinedDropTarget(null);
                return;
            }
            setCombinedDropTarget({ id: hoveredId, index: hoveredIndex });
        };
        const handlePointerUp = () => {
            if (!activeItemRef.current) {
                pendingDragStartRef.current = null;
                return;
            }
            const active = activeItemRef.current;
            const target = dropTargetRef.current;
            if (active && target && active.id !== target.id) {
                reorderItems(active.index, target.index);
            }
            cancelDrag();
        };
        const handlePointerCancel = () => cancelDrag(true);
        window.addEventListener("pointermove", handlePointerMove);
        window.addEventListener("pointerup", handlePointerUp);
        window.addEventListener("pointercancel", handlePointerCancel);
        return () => {
            window.removeEventListener("pointermove", handlePointerMove);
            window.removeEventListener("pointerup", handlePointerUp);
            window.removeEventListener("pointercancel", handlePointerCancel);
        };
    }, [
        setCombinedDropTarget,
        setCombinedActiveItem,
        reorderItems,
        saveInitialItems,
        cancelDrag,
    ]);
    const describedBy = (0, helpers_1.cl)(rest["aria-describedby"], instructionsId);
    const onKeyboardDragEnd = (diff, label) => {
        if (!dragHandlerActive)
            return;
        const targetIndex = dragHandlerActive.index + diff;
        if (targetIndex < 0 || targetIndex >= items.length) {
            return;
        }
        setAnnouncer(`${label}. Plass ${targetIndex + 1} av ${items.length}.`);
        reorderItems(dragHandlerActive.index, targetIndex);
        setDragHandlerActive(Object.assign(Object.assign({}, dragHandlerActive), { index: targetIndex }));
    };
    return (react_1.default.createElement(DragAndDrop_context_1.DragAndDropProvider, { activeItem: activeItem, setActiveItem: setCombinedActiveItem, dropTarget: dropTarget, setDropTarget: setCombinedDropTarget, dragHandlerActive: dragHandlerActive, onKeyboardDragStart: keyboardDragStart, onKeyboardDragEnd: onKeyboardDragEnd, startPendingDrag: startPendingDrag, cancelDrag: cancelDrag, setAnnouncer: setAnnouncer, itemAmount: items.length },
        react_1.default.createElement("span", { id: instructionsId, className: "aksel-sr-only" }, "Bruk Tab for \u00E5 fokusere p\u00E5 en kolonne. Trykk mellomrom eller enter for \u00E5 starte flytting, bruk piltastene for \u00E5 flytte kolonnen, trykk mellomrom eller enter for \u00E5 slippe, eller Escape for \u00E5 avbryte."),
        react_1.default.createElement("div", { "aria-live": "assertive", className: "aksel-sr-only", "aria-atomic": true }, announcer),
        react_1.default.createElement("ul", Object.assign({}, rest, { ref: forwardedRef, "aria-label": (_b = rest["aria-label"]) !== null && _b !== void 0 ? _b : "Kolonneinnstillinger", "aria-describedby": describedBy, className: (0, helpers_1.cl)("aksel-data-table__drag-and-drop-root", className) }), items.map((item, index) => {
            return (react_1.default.createElement(DragAndDropItem_1.default, { key: item.id, id: item.id, index: index, itemLabel: item.label }, renderItem(item, index)));
        })),
        activeItem && activeData && (react_1.default.createElement(Floating_1.Floating, null,
            react_1.default.createElement(Floating_1.Floating.Anchor, { virtualRef: virtualRef },
                react_1.default.createElement("span", null)),
            react_1.default.createElement(Floating_1.Floating.Content, { align: "start", updatePositionStrategy: "always", "aria-hidden": true, style: {
                    pointerEvents: "none",
                    boxSizing: "border-box",
                    width: overlayWidth ? `${overlayWidth}px` : "fit-content",
                } },
                react_1.default.createElement(DragAndDropItem_1.default, { id: activeItem.id, index: activeItem.index, isOverlay: true, itemLabel: activeData.label }, renderItem(activeData, activeItem.index)))))));
}
const DragAndDrop = (0, react_1.forwardRef)(DragAndDropInner);
exports.DragAndDrop = DragAndDrop;
exports.default = DragAndDrop;
//# sourceMappingURL=DragAndDropRoot.js.map