fix: drag-and-drop — set draggable upfront, add Firefox setData, fix duplicate dragend

draggable=true must be set before any mouse event (not on mousedown).
Firefox requires setData() for drag to work. Removed duplicate dragend
listener that was resetting draggable=false and conflicting with the first.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Sonnet 4.6 committed 2026-04-28 21:21:13 +02:00
1 parent 33c0751db6
commit ca6dd87531
1 file changed
+6 -4
+6 -4
View File
@@ -558,19 +558,23 @@ function _initPlaylistDragDrop(container, songs, pl) {
handle.innerHTML = '<svg width="10" height="14" viewBox="0 0 10 14" fill="currentColor"><circle cx="3" cy="2" r="1.3"/><circle cx="7" cy="2" r="1.3"/><circle cx="3" cy="7" r="1.3"/><circle cx="7" cy="7" r="1.3"/><circle cx="3" cy="12" r="1.3"/><circle cx="7" cy="12" r="1.3"/></svg>';
item.insertBefore(handle, item.firstChild);
item._songRef = songs[i];
item.draggable = true; // set upfront so browser registers it before any event
item.addEventListener('dragstart', e => {
if (!e.target.closest('.pl-drag-handle')) { e.preventDefault(); return; }
dragSrc = item;
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/plain', ''); // required by Firefox
setTimeout(() => item.classList.add('dragging'), 0);
});
item.addEventListener('dragend', () => {
dragSrc = null;
item.classList.remove('dragging');
container.querySelectorAll('.drag-over').forEach(el => el.classList.remove('drag-over'));
});
item.addEventListener('dragover', e => {
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
if (item === dragSrc) return;
container.querySelectorAll('.drag-over').forEach(el => el.classList.remove('drag-over'));
item.classList.add('drag-over');
@@ -580,6 +584,7 @@ function _initPlaylistDragDrop(container, songs, pl) {
});
item.addEventListener('drop', e => {
e.preventDefault();
e.stopPropagation();
if (!dragSrc || item === dragSrc) return;
item.classList.remove('drag-over');
const allItems = Array.from(container.children);
@@ -594,13 +599,10 @@ function _initPlaylistDragDrop(container, songs, pl) {
const n = el.querySelector('.song-num');
if (n) n.textContent = idx + 1;
});
// Debounced save
// Debounced save to Navidrome
clearTimeout(saveTimer);
saveTimer = setTimeout(() => _savePlaylistOrder(pl, songs), 800);
});
handle.addEventListener('mousedown', () => { item.draggable = true; });
item.addEventListener('dragend', () => { item.draggable = false; });
});
}