fix: button focus ring, playlist reorder mode, edit modal mobile, backdrop-filter
- button:focus{outline:none} — removes the blue tap-highlight box in WebView
- Playlist reorder: replaced always-on drag handles with explicit 'Neu anordnen'
mode. Handles only appear after tapping the button; a sticky Speichern/Abbrechen
bar appears above the list. Touch drag is now fully supported. Context-menu
(long-press) is blocked while reorder mode is active to prevent conflicts.
- styles.css: .pl-drag-handle always visible when pl-draggable (no hover needed)
- Edit modal footer: wrap buttons on small screens so Save doesn't overflow
- Edit modal overlay: remove backdrop-filter:blur — causes WebView compositor
glitch where 3/4 of the modal disappears after returning from background
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
68bec67880
commit
9d8a5a22bb
4 files changed
+112
-36
No files matched your search
+89
-32
@@ -557,11 +557,10 @@ export async function loadPlaylistDetail(pl) {
|
||||
playQueue(shuffled, 0);
|
||||
};
|
||||
sl.innerHTML = '';
|
||||
sl.classList.add('pl-draggable');
|
||||
const plSearch = document.getElementById('playlist-search');
|
||||
if (plSearch) plSearch.value = '';
|
||||
songs.forEach((s,i) => sl.appendChild(makeSongItem(s, i, songs, { noArtistLink: true, noAlbumLink: true })));
|
||||
_initPlaylistDragDrop(sl, songs, S._currentPlaylist);
|
||||
_setupReorderMode(sl, songs, S._currentPlaylist);
|
||||
} catch(e) { sl.innerHTML = `<p style="color:var(--danger)">${e.message}</p>`; }
|
||||
}
|
||||
|
||||
@@ -574,9 +573,28 @@ export function filterPlaylist(val) {
|
||||
});
|
||||
}
|
||||
|
||||
function _initPlaylistDragDrop(container, songs, pl) {
|
||||
let dragSrc = null;
|
||||
let saveTimer = null;
|
||||
// ─── PLAYLIST REORDER MODE ───────────────────────────────────────────────
|
||||
let _reorderCtx = null;
|
||||
|
||||
function _setupReorderMode(container, songs, pl) {
|
||||
// Exit any previous reorder mode silently when loading a new playlist.
|
||||
if (_reorderCtx) _exitReorderMode(false);
|
||||
_reorderCtx = { container, songs, pl };
|
||||
const btn = document.getElementById('btn-reorder-playlist');
|
||||
if (btn) btn.onclick = plReorderEnter;
|
||||
const bar = document.getElementById('pl-reorder-bar');
|
||||
if (bar) bar.style.display = 'none';
|
||||
}
|
||||
|
||||
export function plReorderEnter() {
|
||||
if (!_reorderCtx) return;
|
||||
const { container, songs } = _reorderCtx;
|
||||
container.classList.add('pl-draggable');
|
||||
// Block context-menu (long-press) on all items while reordering.
|
||||
container.addEventListener('contextmenu', _blockCtxMenu, true);
|
||||
|
||||
let dragSrc = null;
|
||||
let touchDragSrc = null;
|
||||
|
||||
Array.from(container.children).forEach((item, i) => {
|
||||
const handle = document.createElement('div');
|
||||
@@ -585,54 +603,93 @@ function _initPlaylistDragDrop(container, songs, pl) {
|
||||
item.insertBefore(handle, item.firstChild);
|
||||
item._songRef = songs[i];
|
||||
|
||||
// Track whether drag started from the handle (e.target in dragstart is always the item, not the handle)
|
||||
// ── Desktop drag ──
|
||||
let fromHandle = false;
|
||||
handle.addEventListener('mousedown', () => { fromHandle = true; item.draggable = true; });
|
||||
|
||||
item.addEventListener('dragstart', e => {
|
||||
if (!fromHandle) { e.preventDefault(); item.draggable = false; return; }
|
||||
fromHandle = false;
|
||||
dragSrc = item;
|
||||
fromHandle = false; dragSrc = item;
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
e.dataTransfer.setData('text/plain', ''); // required by Firefox
|
||||
e.dataTransfer.setData('text/plain', '');
|
||||
setTimeout(() => item.classList.add('dragging'), 0);
|
||||
});
|
||||
item.addEventListener('dragend', () => {
|
||||
fromHandle = false;
|
||||
item.draggable = false;
|
||||
dragSrc = null;
|
||||
fromHandle = false; item.draggable = false; 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';
|
||||
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');
|
||||
});
|
||||
item.addEventListener('dragleave', e => {
|
||||
if (!item.contains(e.relatedTarget)) item.classList.remove('drag-over');
|
||||
});
|
||||
item.addEventListener('dragleave', e => { if (!item.contains(e.relatedTarget)) item.classList.remove('drag-over'); });
|
||||
item.addEventListener('drop', e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
e.preventDefault(); e.stopPropagation();
|
||||
if (!dragSrc || item === dragSrc) return;
|
||||
item.classList.remove('drag-over');
|
||||
const allItems = Array.from(container.children);
|
||||
const fromIdx = allItems.indexOf(dragSrc);
|
||||
const toIdx = allItems.indexOf(item);
|
||||
container.insertBefore(dragSrc, fromIdx < toIdx ? item.nextSibling : item);
|
||||
const newOrder = Array.from(container.children).map(el => el._songRef);
|
||||
songs.splice(0, songs.length, ...newOrder);
|
||||
Array.from(container.children).forEach((el, idx) => {
|
||||
const n = el.querySelector('.song-num');
|
||||
if (n) n.textContent = idx + 1;
|
||||
});
|
||||
clearTimeout(saveTimer);
|
||||
saveTimer = setTimeout(() => _savePlaylistOrder(pl, songs), 800);
|
||||
_doReorder(container, songs, dragSrc, item);
|
||||
});
|
||||
|
||||
// ── Touch drag ──
|
||||
handle.addEventListener('touchstart', e => {
|
||||
e.preventDefault(); e.stopPropagation();
|
||||
touchDragSrc = item; item.classList.add('dragging');
|
||||
}, { passive: false });
|
||||
handle.addEventListener('touchmove', e => {
|
||||
if (!touchDragSrc) return;
|
||||
e.preventDefault();
|
||||
const t = e.touches[0];
|
||||
container.querySelectorAll('.drag-over').forEach(el => el.classList.remove('drag-over'));
|
||||
const over = _itemAtPoint(container, t.clientX, t.clientY, touchDragSrc);
|
||||
if (over) over.classList.add('drag-over');
|
||||
}, { passive: false });
|
||||
handle.addEventListener('touchend', e => {
|
||||
if (!touchDragSrc) return;
|
||||
const t = e.changedTouches[0];
|
||||
touchDragSrc.classList.remove('dragging');
|
||||
container.querySelectorAll('.drag-over').forEach(el => el.classList.remove('drag-over'));
|
||||
const over = _itemAtPoint(container, t.clientX, t.clientY, touchDragSrc);
|
||||
if (over) _doReorder(container, songs, touchDragSrc, over);
|
||||
touchDragSrc = null;
|
||||
}, { passive: false });
|
||||
});
|
||||
|
||||
const bar = document.getElementById('pl-reorder-bar');
|
||||
if (bar) bar.style.display = 'flex';
|
||||
}
|
||||
|
||||
function _blockCtxMenu(e) { e.preventDefault(); e.stopPropagation(); }
|
||||
|
||||
function _itemAtPoint(container, x, y, exclude) {
|
||||
return (document.elementsFromPoint(x, y) || [])
|
||||
.find(el => el.classList?.contains('song-item') && el !== exclude && container.contains(el)) || null;
|
||||
}
|
||||
|
||||
function _doReorder(container, songs, src, target) {
|
||||
const all = Array.from(container.children);
|
||||
const from = all.indexOf(src), to = all.indexOf(target);
|
||||
container.insertBefore(src, from < to ? target.nextSibling : target);
|
||||
const newOrder = Array.from(container.children).map(el => el._songRef);
|
||||
songs.splice(0, songs.length, ...newOrder);
|
||||
Array.from(container.children).forEach((el, i) => {
|
||||
const n = el.querySelector('.song-num'); if (n) n.textContent = i + 1;
|
||||
});
|
||||
}
|
||||
|
||||
export function plReorderSave() { _exitReorderMode(true); }
|
||||
export function plReorderCancel() { _exitReorderMode(false); }
|
||||
|
||||
function _exitReorderMode(save) {
|
||||
if (!_reorderCtx) return;
|
||||
const { container, songs, pl } = _reorderCtx;
|
||||
container.classList.remove('pl-draggable');
|
||||
container.removeEventListener('contextmenu', _blockCtxMenu, true);
|
||||
container.querySelectorAll('.pl-drag-handle').forEach(h => h.remove());
|
||||
const bar = document.getElementById('pl-reorder-bar');
|
||||
if (bar) bar.style.display = 'none';
|
||||
if (save) _savePlaylistOrder(pl, songs);
|
||||
}
|
||||
|
||||
async function _savePlaylistOrder(pl, songs) {
|
||||
|
||||
Reference in new issue
Block a user