mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-11 19:29:33 +00:00
fix(v3): scope song search to the library + keep it above the toolbar on scroll (#560)
The topbar search (#v3-search) rendered on every screen and, on the library screen, was hidden behind the filter toolbar while scrolling (both were sticky top-0 z-20 in the #v3-main scroller). - shell.js: wrap the search in #v3-search-wrap (hidden by default) and toggle it in syncActive() so it only shows on #v3-songs; bump the topbar to z-30 so it always sits above the toolbar. - songs.js: drop the toolbar's top-0 and pin it beneath the topbar by measuring the topbar height (positionToolbar). A ResizeObserver on #v3-topbar keeps the offset correct as the topbar height changes (viewport width, search show/hide) and fixes the initial position regardless of render()/syncActive() ordering. Fixes #559 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
4dc5936712
commit
530995dd02
+9
-2
@@ -112,6 +112,10 @@
|
||||
el.classList.toggle('text-fb-textDim', !on);
|
||||
});
|
||||
setTopbarTitle(titleFor(screenId));
|
||||
// Show the song search only on the library screen. Everywhere else the
|
||||
// box is irrelevant (and would silently no-op against v3Songs.search).
|
||||
const searchWrap = document.getElementById('v3-search-wrap');
|
||||
if (searchWrap) searchWrap.classList.toggle('hidden', screenId !== 'v3-songs');
|
||||
// NOTE: we deliberately do NOT reflect the screen into location.hash on
|
||||
// every navigation. app.js's audio 'error' handler suppresses empty-src
|
||||
// errors only when `audio.src === window.location.href`; a `#/...`
|
||||
@@ -173,13 +177,16 @@
|
||||
function renderTopbar() {
|
||||
const bar = document.getElementById('v3-topbar');
|
||||
if (!bar) return;
|
||||
bar.className = 'sticky top-0 z-20 bg-fb-sidebar/80 backdrop-blur';
|
||||
bar.className = 'sticky top-0 z-30 bg-fb-sidebar/80 backdrop-blur';
|
||||
bar.innerHTML =
|
||||
// Row 1 — top utility bar: search.
|
||||
'<div class="flex items-center gap-4 px-4 md:px-8 pt-4">' +
|
||||
'<button id="v3-hamburger" class="md:hidden text-fb-textDim hover:text-fb-text shrink-0" aria-label="Menu">' +
|
||||
'<svg class="w-6 h-6" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h16"/></svg></button>' +
|
||||
'<div class="flex-1 max-w-md relative">' +
|
||||
// Search is scoped to the library — syncActive() toggles `hidden` on
|
||||
// this wrapper so it only shows on the v3-songs screen. It lives in
|
||||
// the sticky topbar, so it stays put while the song grid scrolls.
|
||||
'<div id="v3-search-wrap" class="flex-1 max-w-md relative hidden">' +
|
||||
'<svg class="w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 text-fb-textDim" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path stroke-linecap="round" d="M21 21l-4-4"/></svg>' +
|
||||
'<input id="v3-search" type="search" placeholder="Search songs…" aria-label="Search songs" ' +
|
||||
'class="w-full bg-gray-800/50 border border-gray-700 rounded-md pl-10 pr-4 py-2 text-sm ' +
|
||||
|
||||
+30
-1
@@ -619,6 +619,33 @@
|
||||
}, { passive: true });
|
||||
}
|
||||
|
||||
// Pin the sticky toolbar directly beneath the sticky topbar. Both live in
|
||||
// the #v3-main scroller, so without an explicit offset they share top:0 and
|
||||
// the toolbar covers the topbar's song search. The topbar has two responsive
|
||||
// rows, so its height is measured (and re-measured on resize) instead of
|
||||
// hard-coded.
|
||||
function positionToolbar() {
|
||||
const topbar = document.getElementById('v3-topbar');
|
||||
const bar = document.getElementById('v3-songs-toolbar');
|
||||
if (!topbar || !bar) return;
|
||||
bar.style.top = topbar.offsetHeight + 'px';
|
||||
}
|
||||
function bindToolbarReflow() {
|
||||
if (state.resizeBound) return;
|
||||
const topbar = document.getElementById('v3-topbar');
|
||||
if (!topbar) return;
|
||||
state.resizeBound = true;
|
||||
// Observe the topbar itself: its height changes with viewport width AND
|
||||
// when the song search is toggled in/out on screen changes. ResizeObserver
|
||||
// fires once on observe(), so this also fixes up the initial position
|
||||
// regardless of render() vs syncActive() ordering.
|
||||
if (typeof ResizeObserver === 'function') {
|
||||
new ResizeObserver(positionToolbar).observe(topbar);
|
||||
} else {
|
||||
window.addEventListener('resize', positionToolbar, { passive: true });
|
||||
}
|
||||
}
|
||||
|
||||
// ── Tree ────────────────────────────────────────────────────────────────
|
||||
async function loadTree() {
|
||||
const host = document.getElementById('v3-songs-tree');
|
||||
@@ -790,7 +817,7 @@
|
||||
|
||||
root.innerHTML =
|
||||
'<div class="max-w-7xl mx-auto px-6 md:px-8 pb-8">' +
|
||||
'<div class="sticky top-0 z-20 -mx-6 md:-mx-8 px-6 md:px-8 py-3 mb-4 bg-fb-sidebar/95 backdrop-blur border-b border-fb-border/40">' +
|
||||
'<div id="v3-songs-toolbar" class="sticky z-20 -mx-6 md:-mx-8 px-6 md:px-8 py-3 mb-4 bg-fb-sidebar/95 backdrop-blur border-b border-fb-border/40">' +
|
||||
'<div class="flex flex-col md:flex-row md:items-end justify-between gap-4">' +
|
||||
'<div><p class="text-fb-textDim text-sm" id="v3-songs-count"></p></div>' +
|
||||
'<div class="flex flex-wrap gap-2">' +
|
||||
@@ -870,6 +897,8 @@
|
||||
// before it tries to page deeper.
|
||||
await setView(state.view);
|
||||
bindScroll();
|
||||
positionToolbar();
|
||||
bindToolbarReflow();
|
||||
updateFilterBadge();
|
||||
state.built = true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user