fix: drag-and-drop — use fromHandle bool instead of e.target check

e.target in dragstart is always the draggable item element, never the
handle child — so closest('.pl-drag-handle') always returned null and
every drag was cancelled. Now mousedown on handle sets fromHandle=true
and item.draggable=true; dragstart checks the bool instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Sonnet 4.6 committed 2026-04-29 00:10:36 +02:00
1 parent ca6dd87531
commit 3988d18182
1 file changed
+8 -5
+8 -5
View File
@@ -558,16 +558,22 @@ 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
// Track whether drag started from the handle (e.target in dragstart is always the item, not the handle)
let fromHandle = false;
handle.addEventListener('mousedown', () => { fromHandle = true; item.draggable = true; });
item.addEventListener('dragstart', e => {
if (!e.target.closest('.pl-drag-handle')) { e.preventDefault(); return; }
if (!fromHandle) { e.preventDefault(); item.draggable = false; return; }
fromHandle = false;
dragSrc = item;
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/plain', ''); // required by Firefox
setTimeout(() => item.classList.add('dragging'), 0);
});
item.addEventListener('dragend', () => {
fromHandle = false;
item.draggable = false;
dragSrc = null;
item.classList.remove('dragging');
container.querySelectorAll('.drag-over').forEach(el => el.classList.remove('drag-over'));
@@ -591,15 +597,12 @@ function _initPlaylistDragDrop(container, songs, pl) {
const fromIdx = allItems.indexOf(dragSrc);
const toIdx = allItems.indexOf(item);
container.insertBefore(dragSrc, fromIdx < toIdx ? item.nextSibling : item);
// Sync songs array to new DOM order
const newOrder = Array.from(container.children).map(el => el._songRef);
songs.splice(0, songs.length, ...newOrder);
// Update track numbers
Array.from(container.children).forEach((el, idx) => {
const n = el.querySelector('.song-num');
if (n) n.textContent = idx + 1;
});
// Debounced save to Navidrome
clearTimeout(saveTimer);
saveTimer = setTimeout(() => _savePlaylistOrder(pl, songs), 800);
});