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 <noreply@anthropic.com>
This commit is contained in:
@@ -70,14 +70,16 @@
|
||||
li.className = 'trackRow'
|
||||
li.draggable = true
|
||||
li.dataset.index = String(idx)
|
||||
// 기본 상태에서는 contenteditable 을 켜지 않는다. 더블클릭 시에만 편집 모드 ON.
|
||||
// 이렇게 해야 어디를 눌러도 드래그가 시작될 수 있다.
|
||||
li.innerHTML =
|
||||
'<span class="rowNum">' + (idx + 1) + '</span>' +
|
||||
'<img class="rowThumb" src="' + thumbUrl(entry.url) + '" alt="" loading="lazy"/>' +
|
||||
'<img class="rowThumb" src="' + thumbUrl(entry.url) + '" alt="" loading="lazy" draggable="false"/>' +
|
||||
'<div class="rowMeta">' +
|
||||
'<div class="rowTitle" contenteditable="true" spellcheck="false" data-field="title" data-placeholder="(제목 없음)">' +
|
||||
'<div class="rowTitle" spellcheck="false" data-field="title" data-placeholder="(제목 없음)" title="더블클릭해서 수정">' +
|
||||
escapeHtml(entry.title || '') +
|
||||
'</div>' +
|
||||
'<div class="rowSub" contenteditable="true" spellcheck="false" data-field="artist" data-placeholder="(가수 미상)">' +
|
||||
'<div class="rowSub" spellcheck="false" data-field="artist" data-placeholder="(가수 미상)" title="더블클릭해서 수정">' +
|
||||
escapeHtml(entry.artist || '') +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
@@ -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() }
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user