feat: mobile search bar in header — tap icon to search Navidrome library
Search icon added to mobile header. Tapping opens an inline search bar below the header. Typing searches artists/albums/songs via Navidrome search3 API with 300ms debounce. X button closes and returns to Home. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
4b34bbc043
commit
09ce590abd
3 files changed
+48
-8
No files matched your search
@@ -508,6 +508,32 @@ async function doLocalSearch(q){
|
|||||||
}
|
}
|
||||||
function mkSection(title){const d=document.createElement('div');d.className='search-section';d.innerHTML=`<div class="search-section-title">${title}</div>`;return d}
|
function mkSection(title){const d=document.createElement('div');d.className='search-section';d.innerHTML=`<div class="search-section-title">${title}</div>`;return d}
|
||||||
|
|
||||||
|
function toggleMobileSearch(){
|
||||||
|
const bar=document.getElementById('mobile-search-bar');
|
||||||
|
const btn=document.getElementById('mobile-search-btn');
|
||||||
|
const open=bar.style.display==='none'||bar.style.display==='';
|
||||||
|
bar.style.display=open?'block':'none';
|
||||||
|
btn.style.color=open?'var(--accent)':'var(--text2)';
|
||||||
|
if(open){setTimeout(()=>document.getElementById('mobile-search-input').focus(),50);}
|
||||||
|
else{closeMobileSearch();}
|
||||||
|
}
|
||||||
|
function closeMobileSearch(){
|
||||||
|
const bar=document.getElementById('mobile-search-bar');
|
||||||
|
const inp=document.getElementById('mobile-search-input');
|
||||||
|
const btn=document.getElementById('mobile-search-btn');
|
||||||
|
bar.style.display='none';
|
||||||
|
btn.style.color='var(--text2)';
|
||||||
|
inp.value='';
|
||||||
|
navigate('home');
|
||||||
|
}
|
||||||
|
let _mobileSearchTimer=null;
|
||||||
|
function onMobileSearch(val){
|
||||||
|
clearTimeout(_mobileSearchTimer);
|
||||||
|
if(!val.trim()){navigate('home');return;}
|
||||||
|
navigate('search');
|
||||||
|
_mobileSearchTimer=setTimeout(()=>doLocalSearch(val),300);
|
||||||
|
}
|
||||||
|
|
||||||
// ─── ITUNES API ───
|
// ─── ITUNES API ───
|
||||||
const ITUNES='https://itunes.apple.com';
|
const ITUNES='https://itunes.apple.com';
|
||||||
async function itunesSearch(query,entity='musicTrack',limit=25){
|
async function itunesSearch(query,entity='musicTrack',limit=25){
|
||||||
|
|||||||
+21
-7
@@ -127,14 +127,28 @@
|
|||||||
<main class="main" id="main-content">
|
<main class="main" id="main-content">
|
||||||
|
|
||||||
<!-- Mobile header -->
|
<!-- Mobile header -->
|
||||||
<div style="display:flex;align-items:center;justify-content:space-between;padding:12px 14px;position:sticky;top:0;background:var(--bg);z-index:10;border-bottom:1px solid var(--border)" id="mobile-header">
|
<div id="mobile-header" style="position:sticky;top:0;background:var(--bg);z-index:10;border-bottom:1px solid var(--border)">
|
||||||
<div class="sidebar-logo">
|
<div style="display:flex;align-items:center;justify-content:space-between;padding:12px 14px">
|
||||||
<div class="icon" style="width:26px;height:26px;font-size:12px;border-radius:7px">♫</div>
|
<div class="sidebar-logo">
|
||||||
<span style="font-size:15px;font-weight:700">Nova<em style="color:var(--accent);font-style:normal">Sonic</em></span>
|
<div class="icon" style="width:26px;height:26px;font-size:12px;border-radius:7px">♫</div>
|
||||||
|
<span style="font-size:15px;font-weight:700">Nova<em style="color:var(--accent);font-style:normal">Sonic</em></span>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;align-items:center;gap:4px">
|
||||||
|
<button class="hbtn" id="mobile-search-btn" onclick="toggleMobileSearch()" title="Suche" style="padding:8px;color:var(--text2)">
|
||||||
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
|
||||||
|
</button>
|
||||||
|
<button class="hbtn" onclick="openSettings()" title="Einstellungen" style="padding:8px;color:var(--text2)">
|
||||||
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="mobile-search-bar" style="display:none;padding:0 14px 10px">
|
||||||
|
<div class="search-box" style="background:var(--surface);border-color:var(--border2)">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
|
||||||
|
<input id="mobile-search-input" type="text" placeholder="Künstler, Album, Titel…" oninput="onMobileSearch(this.value)" style="font-size:15px">
|
||||||
|
<button onclick="closeMobileSearch()" style="background:none;border:none;color:var(--text3);cursor:pointer;font-size:16px;padding:0 2px;line-height:1">✕</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="hbtn" onclick="openSettings()" title="Einstellungen" style="padding:8px;color:var(--text2)">
|
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- HOME -->
|
<!-- HOME -->
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const CACHE = 'ns-v25';
|
const CACHE = 'ns-v26';
|
||||||
const PRECACHE = ['/', 'index.html', 'styles.css', 'app.js', 'manifest.json', 'icon.svg'];
|
const PRECACHE = ['/', 'index.html', 'styles.css', 'app.js', 'manifest.json', 'icon.svg'];
|
||||||
const SKIP_PATHS = ['/rest/stream', '/rest/download', '/config.js'];
|
const SKIP_PATHS = ['/rest/stream', '/rest/download', '/config.js'];
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user