import type { TranscriptionBlockData } from '$lib/types'; type Options = { getSortedBlocks: () => TranscriptionBlockData[]; onReorder: (blockIds: string[]) => void; }; export function createBlockDragDrop({ getSortedBlocks, onReorder }: Options) { let draggedBlockId = $state(null); let dropTargetIdx = $state(null); let dragOffsetY = $state(0); // Internal mutable refs — not reactive let dragStartY = 0; let capturedEl: HTMLElement | null = null; let listEl: HTMLElement | null = null; function setListElement(el: HTMLElement | null): void { listEl = el; } function handleGripDown(e: PointerEvent, blockId: string): void { if (!(e.target as HTMLElement).closest('[data-drag-handle]')) return; e.preventDefault(); draggedBlockId = blockId; dragStartY = e.clientY; dragOffsetY = 0; capturedEl = (e.target as HTMLElement).closest('[data-block-wrapper]') as HTMLElement; capturedEl?.setPointerCapture(e.pointerId); } function handlePointerMove(e: PointerEvent): void { if (!draggedBlockId || !listEl) return; dragOffsetY = e.clientY - dragStartY; const sortedBlocks = getSortedBlocks(); const wrappers = Array.from(listEl.querySelectorAll('[data-block-wrapper]')); const dragIdx = sortedBlocks.findIndex((b) => b.id === draggedBlockId); let target: number | null = null; for (let i = 0; i < wrappers.length; i++) { const rect = wrappers[i].getBoundingClientRect(); if (e.clientY < rect.top + rect.height / 2) { target = i; break; } } if (target === null) target = wrappers.length; if (target === dragIdx || target === dragIdx + 1) target = null; dropTargetIdx = target; } function handlePointerUp(): void { if (!draggedBlockId) return; if (dropTargetIdx !== null) { const sorted = [...getSortedBlocks()]; const fromIdx = sorted.findIndex((b) => b.id === draggedBlockId); if (fromIdx >= 0) { const [moved] = sorted.splice(fromIdx, 1); const insertAt = dropTargetIdx > fromIdx ? dropTargetIdx - 1 : dropTargetIdx; sorted.splice(insertAt, 0, moved); onReorder(sorted.map((b) => b.id)); } } draggedBlockId = null; dropTargetIdx = null; dragOffsetY = 0; capturedEl = null; } return { get draggedBlockId() { return draggedBlockId; }, get dropTargetIdx() { return dropTargetIdx; }, get dragOffsetY() { return dragOffsetY; }, setListElement, handleGripDown, handlePointerMove, handlePointerUp }; }