Merge pull request #155 from blacksigkill/feature/discard-search
Discard search with "x" button
This commit is contained in:
commit
9ac90d8fab
4 changed files with 120 additions and 39 deletions
41
index.html
41
index.html
|
|
@ -1073,6 +1073,14 @@
|
||||||
autocapitalize="off"
|
autocapitalize="off"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="search-clear-btn btn-icon"
|
||||||
|
title="Clear search"
|
||||||
|
style="display: none"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
<div id="search-history" class="search-history" style="display: none"></div>
|
<div id="search-history" class="search-history" style="display: none"></div>
|
||||||
</form>
|
</form>
|
||||||
</header>
|
</header>
|
||||||
|
|
@ -1642,14 +1650,41 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div class="track-list-search-container">
|
<form class="track-list-search-container" onsubmit="return false;">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="search-icon"
|
||||||
|
>
|
||||||
|
<circle cx="11" cy="11" r="8"></circle>
|
||||||
|
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||||
|
</svg>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="search"
|
||||||
id="track-list-search-input"
|
id="track-list-search-input"
|
||||||
placeholder="Search tracks..."
|
placeholder="Search tracks..."
|
||||||
class="track-list-search-input"
|
class="track-list-search-input"
|
||||||
|
autocomplete="off"
|
||||||
|
autocorrect="off"
|
||||||
|
autocapitalize="off"
|
||||||
|
spellcheck="false"
|
||||||
/>
|
/>
|
||||||
</div>
|
<button
|
||||||
|
type="button"
|
||||||
|
class="search-clear-btn btn-icon"
|
||||||
|
title="Clear search"
|
||||||
|
style="display: none"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
<div id="playlist-detail-tracklist" class="track-list"></div>
|
<div id="playlist-detail-tracklist" class="track-list"></div>
|
||||||
|
|
||||||
<section
|
<section
|
||||||
|
|
|
||||||
|
|
@ -1274,6 +1274,9 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||||
const searchForm = document.getElementById('search-form');
|
const searchForm = document.getElementById('search-form');
|
||||||
const searchInput = document.getElementById('search-input');
|
const searchInput = document.getElementById('search-input');
|
||||||
|
|
||||||
|
// Setup clear button for search bar
|
||||||
|
ui.setupSearchClearButton(searchInput);
|
||||||
|
|
||||||
const performSearch = debounce((query) => {
|
const performSearch = debounce((query) => {
|
||||||
if (query) {
|
if (query) {
|
||||||
navigate(`/search/${encodeURIComponent(query)}`);
|
navigate(`/search/${encodeURIComponent(query)}`);
|
||||||
|
|
|
||||||
35
js/ui.js
35
js/ui.js
|
|
@ -539,6 +539,33 @@ export class UIRenderer {
|
||||||
.join('')}</div>`;
|
.join('')}</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupSearchClearButton(inputElement, clearBtnSelector = '.search-clear-btn') {
|
||||||
|
if (!inputElement) return;
|
||||||
|
|
||||||
|
const clearBtn = inputElement.parentElement?.querySelector(clearBtnSelector);
|
||||||
|
if (!clearBtn) return;
|
||||||
|
|
||||||
|
// Remove old listener if exists
|
||||||
|
const oldListener = clearBtn._clearListener;
|
||||||
|
if (oldListener) clearBtn.removeEventListener('click', oldListener);
|
||||||
|
|
||||||
|
// Toggle visibility based on input value
|
||||||
|
const toggleVisibility = () => {
|
||||||
|
clearBtn.style.display = inputElement.value.trim() ? 'flex' : 'none';
|
||||||
|
};
|
||||||
|
|
||||||
|
// Clear input on click
|
||||||
|
const clearListener = () => {
|
||||||
|
inputElement.value = '';
|
||||||
|
inputElement.dispatchEvent(new Event('input'));
|
||||||
|
inputElement.focus();
|
||||||
|
};
|
||||||
|
|
||||||
|
inputElement.addEventListener('input', toggleVisibility);
|
||||||
|
clearBtn._clearListener = clearListener;
|
||||||
|
clearBtn.addEventListener('click', clearListener);
|
||||||
|
}
|
||||||
|
|
||||||
setupTracklistSearch(
|
setupTracklistSearch(
|
||||||
searchInputId = 'track-list-search-input',
|
searchInputId = 'track-list-search-input',
|
||||||
tracklistContainerId = 'playlist-detail-tracklist'
|
tracklistContainerId = 'playlist-detail-tracklist'
|
||||||
|
|
@ -548,6 +575,9 @@ export class UIRenderer {
|
||||||
|
|
||||||
if (!searchInput || !tracklistContainer) return;
|
if (!searchInput || !tracklistContainer) return;
|
||||||
|
|
||||||
|
// Setup clear button
|
||||||
|
this.setupSearchClearButton(searchInput);
|
||||||
|
|
||||||
// Remove previous listener if exists
|
// Remove previous listener if exists
|
||||||
const oldListener = searchInput._searchListener;
|
const oldListener = searchInput._searchListener;
|
||||||
if (oldListener) {
|
if (oldListener) {
|
||||||
|
|
@ -1944,6 +1974,11 @@ export class UIRenderer {
|
||||||
|
|
||||||
async renderPlaylistPage(playlistId, source = null) {
|
async renderPlaylistPage(playlistId, source = null) {
|
||||||
this.showPage('playlist');
|
this.showPage('playlist');
|
||||||
|
|
||||||
|
// Reset search input for new playlist
|
||||||
|
const searchInput = document.getElementById('track-list-search-input');
|
||||||
|
if (searchInput) searchInput.value = '';
|
||||||
|
|
||||||
const imageEl = document.getElementById('playlist-detail-image');
|
const imageEl = document.getElementById('playlist-detail-image');
|
||||||
const titleEl = document.getElementById('playlist-detail-title');
|
const titleEl = document.getElementById('playlist-detail-title');
|
||||||
const metaEl = document.getElementById('playlist-detail-meta');
|
const metaEl = document.getElementById('playlist-detail-meta');
|
||||||
|
|
|
||||||
80
styles.css
80
styles.css
|
|
@ -474,14 +474,8 @@ kbd {
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-bar svg.search-icon {
|
.search-bar svg.search-icon {
|
||||||
position: absolute;
|
|
||||||
left: 0.75rem;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
color: var(--muted-foreground);
|
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
pointer-events: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-nav .nav-item a:hover {
|
.sidebar-nav .nav-item a:hover {
|
||||||
|
|
@ -576,20 +570,11 @@ kbd {
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-bar {
|
.search-bar {
|
||||||
position: relative;
|
|
||||||
width: 80%;
|
width: 80%;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-bar input {
|
.search-bar input {
|
||||||
width: 100%;
|
|
||||||
padding: 0.75rem 0.75rem 0.75rem 2.5rem;
|
|
||||||
background-color: var(--input);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: var(--radius);
|
|
||||||
color: var(--foreground);
|
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
transition:
|
transition:
|
||||||
box-shadow var(--transition-fast),
|
box-shadow var(--transition-fast),
|
||||||
|
|
@ -597,10 +582,52 @@ kbd {
|
||||||
background-color var(--transition-fast);
|
background-color var(--transition-fast);
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-bar input:focus {
|
/* Shared search bar styles */
|
||||||
|
.search-bar,
|
||||||
|
.track-list-search-container {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar svg.search-icon,
|
||||||
|
.track-list-search-container svg.search-icon {
|
||||||
|
position: absolute;
|
||||||
|
left: 0.75rem;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
color: var(--muted-foreground);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar input,
|
||||||
|
.track-list-search-input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.75rem 2.5rem;
|
||||||
|
background-color: var(--input);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
color: var(--foreground);
|
||||||
|
transition:
|
||||||
|
box-shadow var(--transition-fast),
|
||||||
|
border-color var(--transition-fast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar input:focus,
|
||||||
|
.track-list-search-input:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: var(--ring);
|
border-color: var(--ring);
|
||||||
box-shadow: 0 0 0 3px rgb(var(--highlight-rgb), 0.2);
|
box-shadow: 0 0 0 3px rgb(var(--highlight-rgb) / 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-clear-btn {
|
||||||
|
position: absolute;
|
||||||
|
right: 0.25rem;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
font-size: 1.5rem;
|
||||||
|
line-height: 1;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.has-page-background .search-bar input {
|
body.has-page-background .search-bar input {
|
||||||
|
|
@ -5522,24 +5549,5 @@ textarea:focus {
|
||||||
}
|
}
|
||||||
|
|
||||||
.track-list-search-input {
|
.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;
|
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