// drag and drop support for reordering queue items // setup drag and drop for queue reordering function setupQueueDragAndDrop() { const isDropBelowCenter = (e, row) => e.clientY - row.getBoundingClientRect().top > row.offsetHeight / 2; let lastDragOverRow = null; ui.queueList.addEventListener("dragstart", (e) => { const row = e.target.closest("tr"); if ( !row || row.classList.contains(CLASSES.DRAGGING) || queueSelection.isSelected(parseInt(row.getAttribute("data-index"))) ) return; queueSelection.clear(); queueSelection.select(parseInt(row.getAttribute("data-index"))); updateRowClass( ui.queueList, queueSelection.getSelected(), CLASSES.DRAGGING, true, ); e.dataTransfer.effectAllowed = "move"; e.dataTransfer.setDragImage(row, 0, 0); }); // handle dragover and show indicator ui.queueList.addEventListener("dragover", (e) => { e.preventDefault(); e.dataTransfer.dropEffect = "move"; const row = e.target.closest("tr"); if (!row || row.classList.contains(CLASSES.DRAGGING)) return; const isBelow = isDropBelowCenter(e, row); if (lastDragOverRow !== row) { if (lastDragOverRow) { lastDragOverRow.classList.remove( CLASSES.DRAG_OVER_ABOVE, CLASSES.DRAG_OVER_BELOW, ); } row.classList.add( isBelow ? CLASSES.DRAG_OVER_BELOW : CLASSES.DRAG_OVER_ABOVE, ); lastDragOverRow = row; } else { const hasAbove = row.classList.contains(CLASSES.DRAG_OVER_ABOVE); const hasBelow = row.classList.contains(CLASSES.DRAG_OVER_BELOW); if ((isBelow && hasAbove) || (!isBelow && hasBelow)) { row.classList.remove(CLASSES.DRAG_OVER_ABOVE, CLASSES.DRAG_OVER_BELOW); row.classList.add( isBelow ? CLASSES.DRAG_OVER_BELOW : CLASSES.DRAG_OVER_ABOVE, ); } } }); // handle drop and complete move ui.queueList.addEventListener("drop", (e) => { e.preventDefault(); const row = e.target.closest("tr"); if (!row || row.classList.contains(CLASSES.DRAGGING)) return; if (lastDragOverRow) { lastDragOverRow.classList.remove( CLASSES.DRAG_OVER_ABOVE, CLASSES.DRAG_OVER_BELOW, CLASSES.DRAGGING, ); lastDragOverRow = null; } clearRowClasses(ui.queueList, CLASSES.DRAGGING); const draggedIdx = parseInt(row.getAttribute("data-index")); moveQueueItems( state.queue, queueSelection.getSelected(), isDropBelowCenter(e, row) ? draggedIdx + 1 : draggedIdx, getQueueCallbacks(), ); // refocus after DOM render cycle completes requestAnimationFrame(() => { const mainEl = document.getElementById("queue"); if (mainEl) mainEl.focus(); }); }); // cleanup on drag end ui.queueList.addEventListener("dragend", () => { if (lastDragOverRow) { lastDragOverRow.classList.remove( CLASSES.DRAG_OVER_ABOVE, CLASSES.DRAG_OVER_BELOW, CLASSES.DRAGGING, ); lastDragOverRow = null; } clearRowClasses(ui.queueList, CLASSES.DRAGGING); }); }