make FilterPanel hideable and responsive across all library views

Adds a toggle button to show/hide the filter panel in Movies, Games,
Mixed, and TV views. On mobile the layout stacks vertically (filter
above content); on md+ it returns to the side-by-side layout. The
toggle button highlights when filters are active so hidden filters
remain discoverable. Also fixes a layout bug where items-start on the
flex-col container caused MixedView thumbnails to collapse on narrow
screens.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Garret Patti
2026-04-05 16:29:49 -04:00
parent bc77abbd8b
commit ca4bea084a
5 changed files with 140 additions and 55 deletions

View File

@@ -26,6 +26,7 @@ export default function TvView({ libraryId }: Props) {
const [selectedTagIds, setSelectedTagIds] = useState<Set<string>>(new Set())
const [assignments, setAssignments] = useState<Record<string, string[]>>({})
const [filterRefreshKey, setFilterRefreshKey] = useState(0)
const [showFilters, setShowFilters] = useState(true)
const [menuOpen, setMenuOpen] = useState(false)
const [confirming, setConfirming] = useState(false)
const [deleting, setDeleting] = useState(false)
@@ -123,6 +124,8 @@ export default function TvView({ libraryId }: Props) {
.catch(() => setDeleting(false))
}
const filtersActive = search !== '' || selectedTagIds.size > 0
const filteredSeries = series.filter((s) => {
if (search && !s.title.toLowerCase().includes(search.toLowerCase())) return false
if (selectedTagIds.size > 0) {
@@ -179,19 +182,36 @@ export default function TvView({ libraryId }: Props) {
</div>
{view === 'series' && (
<div className="flex gap-6 items-start">
<div className="w-52 flex-shrink-0">
<FilterPanel
libraryId={libraryId}
assignments={assignments}
search={search}
onSearchChange={setSearch}
selectedTagIds={selectedTagIds}
onTagToggle={toggleTag}
refreshKey={filterRefreshKey}
/>
<>
<div className="flex items-center gap-2 mb-4">
<button
onClick={() => setShowFilters((v) => !v)}
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium transition-colors"
style={{
backgroundColor: (showFilters || filtersActive) ? 'var(--accent)' : 'var(--surface)',
color: (showFilters || filtersActive) ? '#fff' : 'var(--text-secondary)',
border: '1px solid var(--border)',
}}
aria-label={showFilters ? 'Hide filters' : 'Show filters'}
>
Filters{filtersActive ? ' ●' : ''}
</button>
</div>
<div className="flex-1 min-w-0">
<div className="flex flex-col md:flex-row gap-6 md:items-start">
{showFilters && (
<div className="w-full md:w-52 md:flex-shrink-0">
<FilterPanel
libraryId={libraryId}
assignments={assignments}
search={search}
onSearchChange={setSearch}
selectedTagIds={selectedTagIds}
onTagToggle={toggleTag}
refreshKey={filterRefreshKey}
/>
</div>
)}
<div className="flex-1 min-w-0">
{loading ? (
<SeriesLoadingGrid />
) : error ? (
@@ -238,8 +258,9 @@ export default function TvView({ libraryId }: Props) {
))}
</div>
)}
</div>
</div>
</div>
</>
)}
{view === 'seasons' && selectedSeries && (