feat(fase-11): TagChip + TagPicker components + filter unit tests (11.3.1-2)

TagChip — small pill rendering a tag's name with the colour from the 8-preset
palette. Three usage shapes (mutually exclusive):
  * display-only (default)
  * with onRemove → renders an inline X (used by picker-selected chips)
  * with onSelect → renders the whole chip as a button (used by the lateral
    filter bar and by item-row chips that filter the list on click)
Nested <button> was the SSR warning from the first cut — fixed by making
onSelect / onRemove mutually exclusive.

TagPicker — combobox-style picker driven by filterTagOptions (pure helper
in tag-picker-filter.ts so it can be unit-tested without rendering). Lists
matches by substring, surfaces a "Create `{query}`" affordance only when no
existing tag matches the trimmed query exactly. Enter key prefers toggling
the first match, falls back to creating. Re-usable from the item modal,
the list-filter bar, and the settings tag-management section.

tag-picker-filter.test.ts — 7 unit tests (TP-01..07) covering case-
insensitive substring match, empty query → full list, create affordance
gating on exact-match absence, exclusion of already-selected ids, and
createName preserving verbatim user input (no lowercasing).

tailwind.config.ts safelists the runtime-built bg-/text-/ring-/dot
combinations for all 8 preset colours so JIT doesn't strip them.

Messages: 9 new keys in en + es (tag picker, filter bar, settings section).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-18 03:08:54 +02:00
parent c1152fac90
commit 8fa629ad74
7 changed files with 338 additions and 2 deletions

View File

@@ -1,7 +1,23 @@
import type { Config } from 'tailwindcss';
// Fase 11: tag colours are picked at runtime from an 8-preset palette
// (see public.item_tags.color CHECK constraint). Tailwind cannot statically
// see the class names (they're built from the colour string), so safelist
// every (variant × intensity) combination the chip + dot use.
const TAG_COLORS = ['slate', 'red', 'amber', 'green', 'sky', 'indigo', 'pink', 'stone'];
const tagSafelist = TAG_COLORS.flatMap((c) => [
`bg-${c}-100`,
`text-${c}-700`,
`ring-${c}-200`,
`dark:bg-${c}-900/40`,
`dark:text-${c}-200`,
`dark:ring-${c}-700/50`,
`bg-${c}-500`
]);
export default {
content: ['./src/**/*.{html,js,svelte,ts}'],
safelist: tagSafelist,
// Fase 9.1: drive dark mode from <html data-theme="dark"> so the inline
// anti-FOUC script in app.html can set the attribute synchronously before
// the first paint. Existing `dark:` utilities keep working as-is.