export class DragAndDropManager {
    constructor(draggableSelector, options = {}) {
        this.draggables = new Set();
        this.dropZones = new Set();
        this.currentDragged = null;
        this.handleDragStart = (event) => {
            const target = event.currentTarget;
            this.currentDragged = target;
            target.classList.add(this.options.draggingClass);
            target.setAttribute("aria-grabbed", "true");
            if (event.dataTransfer) {
                event.dataTransfer.effectAllowed = this.options.effectAllowed;
                event.dataTransfer.setData(this.options.dataFormat, target.id || "dragged");
            }
            this.options.onDragStart(target, event);
        };
        this.handleDragEnd = (event) => {
            const target = event.currentTarget;
            target.classList.remove(this.options.draggingClass);
            target.setAttribute("aria-grabbed", "false");
            this.dropZones.forEach((zone) => {
                zone.classList.remove(this.options.dragOverClass);
            });
            this.options.onDragEnd(target, event);
            this.currentDragged = null;
        };
        this.handleDragOver = (event) => {
            const target = event.currentTarget;
            const result = this.options.onDragOver(target, event);
            if (result !== false) {
                event.preventDefault();
                if (event.dataTransfer) {
                    event.dataTransfer.dropEffect = "move";
                }
            }
        };
        this.handleDragEnter = (event) => {
            const target = event.currentTarget;
            target.classList.add(this.options.dragOverClass);
        };
        this.handleDragLeave = (event) => {
            const target = event.currentTarget;
            const relatedTarget = event.relatedTarget;
            if (!target.contains(relatedTarget)) {
                target.classList.remove(this.options.dragOverClass);
            }
        };
        this.handleDrop = (event) => {
            event.preventDefault();
            const dropZone = event.currentTarget;
            dropZone.classList.remove(this.options.dragOverClass);
            if (this.currentDragged) {
                if (!this.options.allowCrossContainer) {
                    const originalParent = this.currentDragged.parentElement;
                    if (originalParent !== dropZone) {
                        return;
                    }
                }
                this.options.onDrop(this.currentDragged, dropZone, event);
            }
        };
        this.options = {
            draggingClass: options.draggingClass ?? "ss-dragging",
            dragOverClass: options.dragOverClass ?? "ss-drag-over",
            dropZoneSelector: options.dropZoneSelector ?? "[data-ss='dropzone']",
            allowCrossContainer: options.allowCrossContainer ?? true,
            effectAllowed: options.effectAllowed ?? "move",
            dataFormat: options.dataFormat ?? "text/plain",
            onDragStart: options.onDragStart ?? (() => { }),
            onDragEnd: options.onDragEnd ?? (() => { }),
            onDrop: options.onDrop ?? (() => { }),
            onDragOver: options.onDragOver ?? (() => true),
            handleSelector: options.handleSelector ?? "",
        };
        this.initDraggables(draggableSelector);
        this.initDropZones();
    }
    addDraggable(element) {
        this.setupDraggable(element);
        this.draggables.add(element);
    }
    removeDraggable(element) {
        this.teardownDraggable(element);
        this.draggables.delete(element);
    }
    addDropZone(element) {
        this.setupDropZone(element);
        this.dropZones.add(element);
    }
    removeDropZone(element) {
        this.teardownDropZone(element);
        this.dropZones.delete(element);
    }
    getDragged() {
        return this.currentDragged;
    }
    setDraggable(element, enabled) {
        element.setAttribute("draggable", String(enabled));
        element.setAttribute("aria-grabbed", String(enabled && this.currentDragged === element));
    }
    destroy() {
        this.draggables.forEach((el) => this.teardownDraggable(el));
        this.dropZones.forEach((el) => this.teardownDropZone(el));
        this.draggables.clear();
        this.dropZones.clear();
        this.currentDragged = null;
    }
    static initDraggables() {
        const managers = [];
        const draggables = document.querySelectorAll('[data-ss="draggable"]');
        draggables.forEach((el) => {
            const handle = el.dataset.ssDraggableHandle;
            const manager = new DragAndDropManager(el, {
                handleSelector: handle,
            });
            managers.push(manager);
        });
        return managers;
    }
    initDraggables(selector) {
        let elements;
        if (typeof selector === "string") {
            elements = Array.from(document.querySelectorAll(selector));
        }
        else if (Array.isArray(selector)) {
            elements = selector;
        }
        else {
            elements = [selector];
        }
        elements.forEach((el) => {
            this.setupDraggable(el);
            this.draggables.add(el);
        });
    }
    initDropZones() {
        const zones = document.querySelectorAll(this.options.dropZoneSelector);
        zones.forEach((zone) => {
            this.setupDropZone(zone);
            this.dropZones.add(zone);
        });
    }
    setupDraggable(element) {
        element.setAttribute("draggable", "true");
        element.setAttribute("role", "listitem");
        element.setAttribute("aria-grabbed", "false");
        if (this.options.handleSelector) {
            const handle = element.querySelector(this.options.handleSelector);
            if (handle) {
                handle.style.cursor = "grab";
                element.setAttribute("draggable", "false");
                handle.addEventListener("mousedown", () => {
                    element.setAttribute("draggable", "true");
                });
                handle.addEventListener("mouseup", () => {
                    element.setAttribute("draggable", "false");
                });
            }
        }
        element.addEventListener("dragstart", this.handleDragStart);
        element.addEventListener("dragend", this.handleDragEnd);
    }
    teardownDraggable(element) {
        element.removeAttribute("draggable");
        element.removeAttribute("aria-grabbed");
        element.removeEventListener("dragstart", this.handleDragStart);
        element.removeEventListener("dragend", this.handleDragEnd);
    }
    setupDropZone(element) {
        element.setAttribute("role", "list");
        element.setAttribute("aria-dropeffect", "move");
        element.addEventListener("dragover", this.handleDragOver);
        element.addEventListener("dragenter", this.handleDragEnter);
        element.addEventListener("dragleave", this.handleDragLeave);
        element.addEventListener("drop", this.handleDrop);
    }
    teardownDropZone(element) {
        element.removeAttribute("aria-dropeffect");
        element.removeEventListener("dragover", this.handleDragOver);
        element.removeEventListener("dragenter", this.handleDragEnter);
        element.removeEventListener("dragleave", this.handleDragLeave);
        element.removeEventListener("drop", this.handleDrop);
    }
}
export default DragAndDropManager;
//# sourceMappingURL=DragAndDropManager.js.map