feat: add search filter bar to playlist detail and favorites views

This commit is contained in:
Honey committed 2026-05-13 13:58:08 +02:00
1 parent d5470a6440
commit e78df7bdb9
4 files changed
+37 -2

No files matched your search

+8
View File
@@ -232,6 +232,10 @@
<button class="sort-chip" data-sort="artist" onclick="loadStarred('artist')">Künstler</button>
<button class="sort-chip" data-sort="plays" onclick="loadStarred('plays')">Meistgespielt</button>
</div>
<div class="list-search-wrap">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input id="starred-search" class="list-search" type="text" placeholder="In Favoriten suchen…" oninput="filterStarred(this.value)">
</div>
<div id="starred-songs" class="song-list"></div>
</div>
@@ -265,6 +269,10 @@
</div>
</div>
</div>
<div class="list-search-wrap">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input id="playlist-search" class="list-search" type="text" placeholder="In Playlist suchen…" oninput="filterPlaylist(this.value)">
</div>
<div class="song-list" id="playlist-songs"></div>
</div>
+2 -2
View File
@@ -15,7 +15,7 @@ import {
navigate, _navGoBack, _navLoad, onLocalSearch, onMobileSearch,
onGlobalSearch, s_goArtist, s_goAlbum, playerBarGoArtist, playerBarGoAlbum,
searchSimilarArtist, searchAndPreview, loadItunesAlbum, expandArtistBio,
loadAlbums, loadStarred, loadRandom,
loadAlbums, loadStarred, loadRandom, filterStarred, filterPlaylist,
} from './views.js';
import { loadSpotifyImporter, resolveSpotifyLink, clearSpotifyPreview, spotifyDlAll } from './spotify.js';
import {
@@ -282,7 +282,7 @@ Object.assign(window, {
openSettings, closeSettings, saveSettings, confirmDisconnect, checkBackend,
openSidebar, closeSidebar, switchNavMode, showCtxMenu,
// Views
loadAlbums, loadStarred, loadRandom, loadSpotifyImporter, resolveSpotifyLink, clearSpotifyPreview, spotifyDlAll,
loadAlbums, loadStarred, loadRandom, filterStarred, filterPlaylist, loadSpotifyImporter, resolveSpotifyLink, clearSpotifyPreview, spotifyDlAll,
executeSpotifyDownload,
openPlModal, closePlModal, addSongsToPlaylist, createAndAddPlaylist, createPlaylistPrompt,
showSongInfo, closeInfoModal,
+22
View File
@@ -557,11 +557,22 @@ export async function loadPlaylistDetail(pl) {
};
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);
} catch(e) { sl.innerHTML = `<p style="color:var(--danger)">${e.message}</p>`; }
}
export function filterPlaylist(val) {
const q = val.trim().toLowerCase();
document.querySelectorAll('#playlist-songs .song-item').forEach(item => {
const title = (item.querySelector('.song-title')?.textContent || '').toLowerCase();
const artist = (item.querySelector('.song-artist')?.textContent || '').toLowerCase();
item.style.display = (!q || title.includes(q) || artist.includes(q)) ? '' : 'none';
});
}
function _initPlaylistDragDrop(container, songs, pl) {
let dragSrc = null;
let saveTimer = null;
@@ -645,6 +656,8 @@ export async function loadStarred(sort = _starredSort) {
c.classList.toggle('active', c.dataset.sort === sort));
const el = document.getElementById('starred-songs');
el.innerHTML = '<div class="spinner"></div>';
const searchEl = document.getElementById('starred-search');
if (searchEl) searchEl.value = '';
try {
const r = await api('getStarred2');
let songs = (r.starred2?.song || []).map(s => ({...s, _starred: true}));
@@ -659,6 +672,15 @@ export async function loadStarred(sort = _starredSort) {
} catch(e) { el.innerHTML = `<p style="color:var(--danger)">${e.message}</p>`; }
}
export function filterStarred(val) {
const q = val.trim().toLowerCase();
document.querySelectorAll('#starred-songs .song-item').forEach(item => {
const title = (item.querySelector('.song-title')?.textContent || '').toLowerCase();
const artist = (item.querySelector('.song-artist')?.textContent || '').toLowerCase();
item.style.display = (!q || title.includes(q) || artist.includes(q)) ? '' : 'none';
});
}
export async function loadRandom() {
const el = document.getElementById('random-songs');
el.innerHTML = '<div class="spinner"></div>';
+5
View File
@@ -718,6 +718,11 @@ nav{flex:1;overflow-y:auto;padding:4px 10px}
.sort-chip{padding:6px 14px;background:var(--surface);border:1px solid var(--border2);border-radius:20px;font-size:12px;font-weight:600;cursor:pointer;white-space:nowrap;color:var(--text2);font-family:var(--font);transition:border-color .15s,color .15s,background .15s}
.sort-chip:hover{border-color:var(--accent);color:var(--accent)}
.sort-chip.active{background:var(--accent);border-color:var(--accent);color:#000}
.list-search-wrap{position:relative;margin-bottom:12px}
.list-search-wrap svg{position:absolute;left:10px;top:50%;transform:translateY(-50%);color:var(--text3);pointer-events:none}
.list-search{width:100%;box-sizing:border-box;padding:8px 12px 8px 32px;background:var(--surface);border:1px solid var(--border2);border-radius:8px;color:var(--text1);font-size:13px;font-family:var(--font);outline:none;transition:border-color .15s}
.list-search:focus{border-color:var(--accent)}
.list-search::placeholder{color:var(--text3)}
.topsong-item{display:grid;grid-template-columns:28px 44px 1fr auto;align-items:center;gap:10px;padding:7px 10px;border-radius:8px;cursor:pointer;transition:background .15s}
.topsong-item:hover{background:var(--surface)}
.topsong-num{font-size:11px;color:var(--text3);font-family:var(--mono);text-align:center}