a simple web player for subsonic tinysub.devins.page
subsonic navidrome javascript
at main 108 lines 3.0 kB view raw
1// drag and drop support for reordering queue items 2 3// setup drag and drop for queue reordering 4function setupDragAndDrop() { 5 const isDropBelowCenter = (e, row) => 6 e.clientY - row.getBoundingClientRect().top > row.offsetHeight / 2; 7 8 let lastDragOverRow = null; 9 10 ui.queueList.addEventListener("dragstart", (e) => { 11 const row = getClosestRow(e.target); 12 if ( 13 !row || 14 row.classList.contains(CLASSES.DRAGGING) || 15 selectionManager.isSelected(getRowIndex(row, DATA_ATTRS.INDEX)) 16 ) 17 return; 18 19 selectionManager.clear(); 20 selectionManager.select(getRowIndex(row, DATA_ATTRS.INDEX)); 21 updateRowClass( 22 ui.queueList, 23 selectionManager.getSelected(), 24 CLASSES.DRAGGING, 25 true, 26 ); 27 e.dataTransfer.effectAllowed = "move"; 28 e.dataTransfer.setDragImage(row, 0, 0); 29 }); 30 31 // handle drag over - show drop indicator 32 ui.queueList.addEventListener("dragover", (e) => { 33 e.preventDefault(); 34 e.dataTransfer.dropEffect = "move"; 35 const row = getClosestRow(e.target); 36 if (!row || row.classList.contains(CLASSES.DRAGGING)) return; 37 38 const isBelow = isDropBelowCenter(e, row); 39 40 // only update if row changed or position within row changed 41 if (lastDragOverRow !== row) { 42 if (lastDragOverRow) { 43 lastDragOverRow.classList.remove( 44 CLASSES.DRAG_OVER_ABOVE, 45 CLASSES.DRAG_OVER_BELOW, 46 ); 47 } 48 row.classList.add( 49 isBelow ? CLASSES.DRAG_OVER_BELOW : CLASSES.DRAG_OVER_ABOVE, 50 ); 51 lastDragOverRow = row; 52 } else { 53 const hasAbove = row.classList.contains(CLASSES.DRAG_OVER_ABOVE); 54 const hasBelow = row.classList.contains(CLASSES.DRAG_OVER_BELOW); 55 56 if ((isBelow && hasAbove) || (!isBelow && hasBelow)) { 57 row.classList.remove(CLASSES.DRAG_OVER_ABOVE, CLASSES.DRAG_OVER_BELOW); 58 row.classList.add( 59 isBelow ? CLASSES.DRAG_OVER_BELOW : CLASSES.DRAG_OVER_ABOVE, 60 ); 61 } 62 } 63 }); 64 65 // handle drop and complete the move 66 ui.queueList.addEventListener("drop", (e) => { 67 e.preventDefault(); 68 const row = getClosestRow(e.target); 69 if (!row || row.classList.contains(CLASSES.DRAGGING)) return; 70 71 if (lastDragOverRow) { 72 lastDragOverRow.classList.remove( 73 CLASSES.DRAG_OVER_ABOVE, 74 CLASSES.DRAG_OVER_BELOW, 75 CLASSES.DRAGGING, 76 ); 77 lastDragOverRow = null; 78 } 79 clearRowClasses(ui.queueList, "tr", CLASSES.DRAGGING); 80 81 const draggedIdx = getRowIndex(row, DATA_ATTRS.INDEX); 82 moveQueueItems( 83 state.queue, 84 selectionManager.getSelected(), 85 isDropBelowCenter(e, row) ? draggedIdx + 1 : draggedIdx, 86 queueCallbacks, 87 ); 88 89 // refocus after DOM render cycle completes 90 requestAnimationFrame(() => { 91 const mainEl = document.getElementById("queue"); 92 if (mainEl) mainEl.focus(); 93 }); 94 }); 95 96 // cleanup on drag end 97 ui.queueList.addEventListener("dragend", () => { 98 if (lastDragOverRow) { 99 lastDragOverRow.classList.remove( 100 CLASSES.DRAG_OVER_ABOVE, 101 CLASSES.DRAG_OVER_BELOW, 102 CLASSES.DRAGGING, 103 ); 104 lastDragOverRow = null; 105 } 106 clearRowClasses(ui.queueList, "tr", CLASSES.DRAGGING); 107 }); 108}