import { createDndManager, type DndAnnouncement, type DndManager } from "@wrnexus/dnd"; export interface MountedEditor { destroy(): void; refresh(): void; } export interface MountEditorOptions { direction?: "ltr" | "rtl"; announce?(announcement: DndAnnouncement): string; onMove(intent: { nodeIds: string[]; to: { parentId: string; slot: string; index: number }; }): boolean | void | Promise; } export function mountBlockEditor(root: HTMLElement, options: MountEditorOptions): MountedEditor { let manager: DndManager | undefined; let cleanups: Array<() => void> = []; const live = document.createElement("div"); live.setAttribute("role", "status"); live.setAttribute("aria-live", "polite"); live.setAttribute("aria-atomic", "true"); live.style.cssText = "position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0"; root.append(live); const refresh = () => { cleanups.forEach((cleanup) => cleanup()); cleanups = []; manager?.destroy(); manager = createDndManager({ direction: options.direction ?? (getComputedStyle(root).direction === "rtl" ? "rtl" : "ltr"), reducedMotion: matchMedia("(prefers-reduced-motion: reduce)").matches, announce(announcement) { live.textContent = options.announce?.(announcement) ?? announcement.type; }, async onMoveIntent(intent) { const [parentId, slot] = String(intent.to.group).split(":", 2); if (!parentId || !slot) return false; return options.onMove({ nodeIds: intent.ids.map(String), to: { parentId, slot, index: intent.to.index }, }); }, }); for (const element of root.querySelectorAll( "[data-block-id][data-block-parent][data-block-slot]", )) { const id = element.dataset.blockId!; const parent = element.dataset.blockParent!; const slot = element.dataset.blockSlot!; const siblings = [ ...root.querySelectorAll("[data-block-id][data-block-parent][data-block-slot]"), ].filter( (candidate) => candidate.dataset.blockParent === parent && candidate.dataset.blockSlot === slot, ); const handle = element.querySelector("[data-block-handle]") ?? undefined; cleanups.push( manager.registerSortable({ id, group: `${parent}:${slot}`, index: siblings.indexOf(element), element, handle, type: element.dataset.blockType, }), ); } }; refresh(); return { refresh, destroy() { cleanups.forEach((cleanup) => cleanup()); manager?.destroy(); live.remove(); }, }; }