Merge pull request #154 from blacksigkill/feature/playlist-search
add a search bar in playlists to quickly find titles
This commit is contained in:
commit
31c9890db8
3 changed files with 79 additions and 0 deletions
|
|
@ -1642,6 +1642,14 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
<div class="track-list-search-container">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="track-list-search-input"
|
||||||
|
placeholder="Search tracks..."
|
||||||
|
class="track-list-search-input"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div id="playlist-detail-tracklist" class="track-list"></div>
|
<div id="playlist-detail-tracklist" class="track-list"></div>
|
||||||
|
|
||||||
<section
|
<section
|
||||||
|
|
|
||||||
43
js/ui.js
43
js/ui.js
|
|
@ -539,6 +539,43 @@ export class UIRenderer {
|
||||||
.join('')}</div>`;
|
.join('')}</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupTracklistSearch(searchInputId = 'track-list-search-input', tracklistContainerId = 'playlist-detail-tracklist') {
|
||||||
|
const searchInput = document.getElementById(searchInputId);
|
||||||
|
const tracklistContainer = document.getElementById(tracklistContainerId);
|
||||||
|
|
||||||
|
if (!searchInput || !tracklistContainer) return;
|
||||||
|
|
||||||
|
// Remove previous listener if exists
|
||||||
|
const oldListener = searchInput._searchListener;
|
||||||
|
if (oldListener) {
|
||||||
|
searchInput.removeEventListener('input', oldListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new listener
|
||||||
|
const listener = () => {
|
||||||
|
const query = searchInput.value.toLowerCase().trim();
|
||||||
|
const trackItems = tracklistContainer.querySelectorAll('.track-item');
|
||||||
|
|
||||||
|
trackItems.forEach((item) => {
|
||||||
|
const trackData = trackDataStore.get(item);
|
||||||
|
if (!trackData) {
|
||||||
|
item.style.display = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const title = (trackData.title || '').toLowerCase();
|
||||||
|
const artist = (trackData.artist?.name || trackData.artists?.[0]?.name || '').toLowerCase();
|
||||||
|
const album = (trackData.album?.title || '').toLowerCase();
|
||||||
|
|
||||||
|
const matches = title.includes(query) || artist.includes(query) || album.includes(query);
|
||||||
|
item.style.display = matches ? '' : 'none';
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
searchInput._searchListener = listener;
|
||||||
|
searchInput.addEventListener('input', listener);
|
||||||
|
}
|
||||||
|
|
||||||
renderListWithTracks(container, tracks, showCover, append = false, useTrackNumber = false) {
|
renderListWithTracks(container, tracks, showCover, append = false, useTrackNumber = false) {
|
||||||
const fragment = document.createDocumentFragment();
|
const fragment = document.createDocumentFragment();
|
||||||
const tempDiv = document.createElement('div');
|
const tempDiv = document.createElement('div');
|
||||||
|
|
@ -2085,6 +2122,9 @@ export class UIRenderer {
|
||||||
isUserPlaylist: true,
|
isUserPlaylist: true,
|
||||||
});
|
});
|
||||||
document.title = `${playlistData.name || playlistData.title} - Monochrome`;
|
document.title = `${playlistData.name || playlistData.title} - Monochrome`;
|
||||||
|
|
||||||
|
// Setup playlist search
|
||||||
|
this.setupTracklistSearch();
|
||||||
} else {
|
} else {
|
||||||
// If source was explicitly 'user' and we didn't find it, fail.
|
// If source was explicitly 'user' and we didn't find it, fail.
|
||||||
if (source === 'user') {
|
if (source === 'user') {
|
||||||
|
|
@ -2154,6 +2194,9 @@ export class UIRenderer {
|
||||||
recentActivityManager.addPlaylist(playlist);
|
recentActivityManager.addPlaylist(playlist);
|
||||||
document.title = playlist.title || 'Artist Mix';
|
document.title = playlist.title || 'Artist Mix';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup playlist search
|
||||||
|
this.setupTracklistSearch();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load playlist:', error);
|
console.error('Failed to load playlist:', error);
|
||||||
tracklistContainer.innerHTML = createPlaceholder(`Could not load playlist details. ${error.message}`);
|
tracklistContainer.innerHTML = createPlaceholder(`Could not load playlist details. ${error.message}`);
|
||||||
|
|
|
||||||
28
styles.css
28
styles.css
|
|
@ -5515,3 +5515,31 @@ textarea:focus {
|
||||||
min-width: 30px;
|
min-width: 30px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Track List Search */
|
||||||
|
.track-list-search-container {
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.track-list-search-input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
background-color: var(--input);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
color: var(--foreground);
|
||||||
|
font-size: 0.95rem;
|
||||||
|
transition:
|
||||||
|
box-shadow var(--transition-fast),
|
||||||
|
border-color var(--transition-fast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.track-list-search-input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--ring);
|
||||||
|
box-shadow: 0 0 0 3px rgb(var(--highlight-rgb) / 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.track-list-search-input::placeholder {
|
||||||
|
color: var(--muted-foreground);
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue