From 3988d18182446a4fdf2b8c37f73c3121df35c3b8 Mon Sep 17 00:00:00 2001 From: Honey Date: Wed, 29 Apr 2026 00:10:36 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20drag-and-drop=20=E2=80=94=20use=20fromHa?= =?UTF-8?q?ndle=20bool=20instead=20of=20e.target=20check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/views.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/views.js b/src/views.js index d439307..38f9739 100644 --- a/src/views.js +++ b/src/views.js @@ -558,16 +558,22 @@ function _initPlaylistDragDrop(container, songs, pl) { handle.innerHTML = ''; 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); });