From e617c71b0afb3064795cd315e9612d60c313db80 Mon Sep 17 00:00:00 2001 From: claude-bot Date: Tue, 12 May 2026 14:18:01 +0900 Subject: [PATCH] Fix drag regression by gating inline edit behind double-click The previous version had contenteditable always on for title/artist spans, which intercepted mousedown and prevented dragstart from firing on the row. Now the spans render as plain text, and double-click activates contenteditable + focus + select-all. blur or Enter/Escape exits edit mode, saves to state, and re-enables row dragging. Co-Authored-By: Claude Opus 4.7 --- public/listEditor.js | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/public/listEditor.js b/public/listEditor.js index cd77010..461b78d 100644 --- a/public/listEditor.js +++ b/public/listEditor.js @@ -70,14 +70,16 @@ li.className = 'trackRow' li.draggable = true li.dataset.index = String(idx) + // 기본 상태에서는 contenteditable 을 켜지 않는다. 더블클릭 시에만 편집 모드 ON. + // 이렇게 해야 어디를 눌러도 드래그가 시작될 수 있다. li.innerHTML = '' + (idx + 1) + '' + - '' + + '' + '
' + - '
' + + '
' + escapeHtml(entry.title || '') + '
' + - '
' + + '
' + escapeHtml(entry.artist || '') + '
' + '
' + @@ -112,18 +114,43 @@ } // ── 인라인 편집 (제목/가수) ───────────────────────── + // 기본 상태에서는 contenteditable 이 꺼져 있어서 row 어디를 클릭해도 드래그가 시작된다. + // 더블클릭 시점에 해당 칸만 contenteditable 로 켜고, 드래그는 일시 비활성화. + // blur 또는 Enter 키로 편집 종료 + 상태 저장 + 드래그 복원. function attachInlineEdit(li, idx) { - li.querySelectorAll('[contenteditable="true"]').forEach(function (el) { - el.addEventListener('focus', function () { li.draggable = false }) - el.addEventListener('blur', function () { li.draggable = true }) - el.addEventListener('input', function () { + li.querySelectorAll('[data-field]').forEach(function (el) { + el.addEventListener('mousedown', function (e) { + // 편집 모드일 때만 드래그를 막아서 텍스트 선택을 허용. + if (el.getAttribute('contenteditable') === 'true') e.stopPropagation() + }) + el.addEventListener('dblclick', function (e) { + e.stopPropagation() + if (el.getAttribute('contenteditable') === 'true') return + el.setAttribute('contenteditable', 'true') + li.draggable = false + el.focus() + try { + var range = document.createRange() + range.selectNodeContents(el) + var sel = window.getSelection() + sel.removeAllRanges() + sel.addRange(range) + } catch (_) {} + }) + el.addEventListener('blur', function () { + if (el.getAttribute('contenteditable') !== 'true') return + el.removeAttribute('contenteditable') + li.draggable = true var field = el.getAttribute('data-field') var value = (el.textContent || '').replace(/\r?\n/g, ' ').trim() + if (!state.music[idx]) return if (field === 'title') state.music[idx].title = value else if (field === 'artist') state.music[idx].artist = value }) el.addEventListener('keydown', function (e) { + if (el.getAttribute('contenteditable') !== 'true') return if (e.key === 'Enter') { e.preventDefault(); el.blur() } + else if (e.key === 'Escape') { e.preventDefault(); el.blur() } }) }) }