feat: persistent inline header search with overlay results

Replaces the hidden toggle-based mobile search bar with a permanently
visible pill-shaped search input in the header row, next to the logo.

- Search is always visible — no button click required
- Typing shows a floating overlay with library results (artists, albums,
  songs) without navigating away from the current page
- Clearing the input or navigating to any page closes the overlay silently
- The ✕ clear button appears only while text is present
- navigate() now calls closeHeaderSearch() so results disappear cleanly
  when tapping any result or bottom nav tab
- Old toggleMobileSearch / closeMobileSearch kept as no-op stubs for safety

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Sonnet 4.6 committed 2026-05-27 12:55:26 +02:00
1 parent b7afa19f70
commit 36f968881b
5 files changed
+48 -38

No files matched your search

+26 -8
View File
@@ -27,7 +27,18 @@ export function _navLoad() {
return false;
}
export function closeHeaderSearch() {
const overlay = document.getElementById('header-search-overlay');
const input = document.getElementById('header-search-input');
const clearBtn = document.getElementById('header-search-clear');
if (overlay) overlay.style.display = 'none';
if (input) input.value = '';
if (clearBtn) clearBtn.style.display = 'none';
clearTimeout(S._headerSearchTimer);
}
export function navigate(page, data=null) {
closeHeaderSearch();
document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
document.querySelectorAll('.nav-item,.bnav-item').forEach(n => n.classList.remove('active','active-blue'));
const cls = ['discover','new-releases','recommendations','spotify-importer'].includes(page) ? 'active-blue' : 'active';
@@ -940,8 +951,8 @@ export function onLocalSearch(val) {
S.searchTimer = setTimeout(() => doLocalSearch(val), 350);
}
export async function doLocalSearch(q) {
const el = document.getElementById('search-results');
export async function doLocalSearch(q, el) {
if (!el) el = document.getElementById('search-results');
el.innerHTML = '<div class="spinner"></div>';
try {
const hiragana = await _romajiToHiragana(q);
@@ -999,14 +1010,21 @@ export async function doLocalSearch(q) {
} catch(e) { el.innerHTML = `<p style="color:var(--danger)">${e.message}</p>`; }
}
// ─── MOBILE SEARCH ───
export function onMobileSearch(val) {
clearTimeout(S._mobileSearchTimer);
if(!val.trim()) { navigate('home'); return; }
navigate('search');
S._mobileSearchTimer = setTimeout(() => doLocalSearch(val), 300);
// ─── HEADER SEARCH (persistent inline bar) ───
export function onHeaderSearch(val) {
const overlay = document.getElementById('header-search-overlay');
const clearBtn = document.getElementById('header-search-clear');
if (clearBtn) clearBtn.style.display = val ? 'block' : 'none';
clearTimeout(S._headerSearchTimer);
if (!val.trim()) { if (overlay) overlay.style.display = 'none'; return; }
if (overlay) overlay.style.display = 'block';
const el = document.getElementById('header-search-results');
S._headerSearchTimer = setTimeout(() => doLocalSearch(val, el), 300);
}
// ─── MOBILE SEARCH (legacy no-op — replaced by inline header search) ───
export function onMobileSearch(val) { onHeaderSearch(val); }
// ─── GLOBAL (ITUNES) SEARCH ───
export function onGlobalSearch(val) {
clearTimeout(S.globalSearchTimer);