fix: hide section-locked filter pills in their own sections

Each section's preset locks one filter dimension that defines the
section: Rated → ratingMin, Discarded → flag, Tags → groupBy=tag.
Showing the matching pill in the toolbar while you're inside that
section is either redundant (it's already on) or actively breaks the
view (toggling it would either become a no-op or filter the section
into one bucket).

Hide the corresponding pill in each section: Rating in Rated, Flag in
Discarded, Tags in Tags. The user navigates away to a different
section to change the locked dimension.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-08 21:04:45 +02:00
parent 30e0900e49
commit d7f953d0a9

View File

@@ -45,6 +45,16 @@ export function FilterBar() {
const sortBy = useFilterStore((s) => s.sortBy) const sortBy = useFilterStore((s) => s.sortBy)
const sortOrder = useFilterStore((s) => s.sortOrder) const sortOrder = useFilterStore((s) => s.sortOrder)
const tagIds = useFilterStore((s) => s.tagIds) const tagIds = useFilterStore((s) => s.tagIds)
const currentSection = useFilterStore((s) => s.currentSection)
// Section-locked pills: each section has a preset filter that defines
// it (Rated → ratingMin, Discarded → flag, Tags → groupBy=tag). The
// matching pill is hidden while you're inside that section because
// toggling it would either be a no-op or break the section. The user
// can still navigate away to change the locked dimension.
const hideRatingPill = currentSection === 'rated'
const hideFlagPill = currentSection === 'discarded'
const hideTagsPill = currentSection === 'tags'
const setDateFrom = useFilterStore((s) => s.setDateFrom) const setDateFrom = useFilterStore((s) => s.setDateFrom)
const setDateTo = useFilterStore((s) => s.setDateTo) const setDateTo = useFilterStore((s) => s.setDateTo)
@@ -216,36 +226,40 @@ export function FilterBar() {
</div> </div>
</FilterPill> </FilterPill>
{/* Rating */} {/* Rating — hidden in the Rated section since the section already
<FilterPill * pins ratingMin and the only useful tweak (ratingMin >= N) lives
label="Rating" * in the section preset itself. */}
value={ratingValue} {!hideRatingPill && (
isActive={ratingActive} <FilterPill
onClear={() => setRatingMin(0)} label="Rating"
> value={ratingValue}
<div> isActive={ratingActive}
<p className="mb-1 text-[11px] text-text-muted">Minimum</p> onClear={() => setRatingMin(0)}
<div className="flex gap-1"> >
{[1, 2, 3, 4, 5].map((n) => ( <div>
<button <p className="mb-1 text-[11px] text-text-muted">Minimum</p>
key={n} <div className="flex gap-1">
onClick={() => setRatingMin(ratingMin === n ? 0 : n)} {[1, 2, 3, 4, 5].map((n) => (
className="p-0.5" <button
title={`At least ${n} star${n > 1 ? 's' : ''}`} key={n}
> onClick={() => setRatingMin(ratingMin === n ? 0 : n)}
<Star className="p-0.5"
className={clsx( title={`At least ${n} star${n > 1 ? 's' : ''}`}
'h-5 w-5 transition-colors', >
n <= ratingMin <Star
? 'fill-star text-star' className={clsx(
: 'text-text-muted hover:text-star' 'h-5 w-5 transition-colors',
)} n <= ratingMin
/> ? 'fill-star text-star'
</button> : 'text-text-muted hover:text-star'
))} )}
/>
</button>
))}
</div>
</div> </div>
</div> </FilterPill>
</FilterPill> )}
{/* Color */} {/* Color */}
<FilterPill <FilterPill
@@ -282,41 +296,46 @@ export function FilterBar() {
</div> </div>
</FilterPill> </FilterPill>
{/* Flag — discarded toggle */} {/* Flag — hidden in the Discarded section, where the flag is
<FilterPill * pinned to "discarded" by the section preset. */}
label="Flag" {!hideFlagPill && (
value={flagValue} <FilterPill
isActive={flagActive} label="Flag"
onClear={() => setFlag('any')} value={flagValue}
> isActive={flagActive}
<div className="flex flex-col gap-1"> onClear={() => setFlag('any')}
<button >
onClick={() => setFlag('any')} <div className="flex flex-col gap-1">
className={clsx( <button
'rounded px-2 py-1 text-left text-xs transition-colors', onClick={() => setFlag('any')}
flag === 'any' className={clsx(
? 'bg-primary text-white' 'rounded px-2 py-1 text-left text-xs transition-colors',
: 'bg-surface-2 text-text-muted hover:bg-surface-offset hover:text-text' flag === 'any'
)} ? 'bg-primary text-white'
> : 'bg-surface-2 text-text-muted hover:bg-surface-offset hover:text-text'
Any )}
</button> >
<button Any
onClick={() => setFlag('discarded')} </button>
className={clsx( <button
'rounded px-2 py-1 text-left text-xs transition-colors', onClick={() => setFlag('discarded')}
flag === 'discarded' className={clsx(
? 'bg-primary text-white' 'rounded px-2 py-1 text-left text-xs transition-colors',
: 'bg-surface-2 text-text-muted hover:bg-surface-offset hover:text-text' flag === 'discarded'
)} ? 'bg-primary text-white'
> : 'bg-surface-2 text-text-muted hover:bg-surface-offset hover:text-text'
Discarded )}
</button> >
</div> Discarded
</FilterPill> </button>
</div>
</FilterPill>
)}
{/* Tags */} {/* Tags — hidden in the Tags section since the section already
{allTags.length > 0 && ( * groups everything by tag and re-applying a tag filter on top
* collapses the view to a single bucket. */}
{!hideTagsPill && allTags.length > 0 && (
<FilterPill <FilterPill
label="Tags" label="Tags"
value={tagValue} value={tagValue}