From 1be42e540649fce74f1b76f52893d8f70401ddf5 Mon Sep 17 00:00:00 2001 From: xarmian Date: Sat, 30 May 2026 15:30:54 -0400 Subject: [PATCH] feat(collections): multi-select tag filter + per-collection tag counts (TASK-1666) (#668) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a TagFilter chip row to the collection page FilterBar — one toggle chip per tag present in the collection, each showing its item count, with OR selection semantics. Tag counts are derived client-side from the local index (no network round-trip) and honor the showArchived toggle. Selection round-trips through the `tags` URL param and the saved-view ViewConfig (rides along as a single `op: 'in'` filter). Implements TASK-1666. --- .../components/collections/FilterBar.svelte | 23 +++- .../components/collections/TagFilter.svelte | 109 ++++++++++++++++++ .../[workspace]/[collection]/+page.svelte | 80 +++++++++++-- 3 files changed, 204 insertions(+), 8 deletions(-) create mode 100644 web/src/lib/components/collections/TagFilter.svelte diff --git a/web/src/lib/components/collections/FilterBar.svelte b/web/src/lib/components/collections/FilterBar.svelte index 9036e3cd..5ceb6676 100644 --- a/web/src/lib/components/collections/FilterBar.svelte +++ b/web/src/lib/components/collections/FilterBar.svelte @@ -2,6 +2,7 @@ import type { Collection } from '$lib/types'; import { parseSchema } from '$lib/types'; import BottomSheet from '$lib/components/common/BottomSheet.svelte'; + import TagFilter from '$lib/components/collections/TagFilter.svelte'; interface Props { collection: Collection; @@ -10,10 +11,26 @@ onFilterChange: (filters: Record) => void; onSearchChange: (query: string) => void; relationLabels?: Record; + /** Tags present in this collection, with counts, ordered by count desc. */ + tagCounts?: { tag: string; count: number }[]; + /** Currently-selected tag filters (OR semantics). */ + selectedTags?: string[]; + onTagFilterChange?: (tags: string[]) => void; searchInputEl?: HTMLInputElement | undefined; } - let { collection, activeFilters, searchQuery, onFilterChange, onSearchChange, relationLabels = {}, searchInputEl = $bindable() }: Props = $props(); + let { + collection, + activeFilters, + searchQuery, + onFilterChange, + onSearchChange, + relationLabels = {}, + tagCounts = [], + selectedTags = [], + onTagFilterChange = () => {}, + searchInputEl = $bindable(), + }: Props = $props(); let schema = $derived(parseSchema(collection)); let statusField = $derived(schema.fields.find((f) => f.key === 'status')); @@ -162,6 +179,10 @@ {/if} {/if} + {#if tagCounts.length > 0} + + {/if} +