From 8aa6086976148365e6834fcc51b7f6b907f4a48e Mon Sep 17 00:00:00 2001 From: dev Date: Fri, 22 May 2026 03:56:57 +0200 Subject: [PATCH] docs(search-field demo): add live filterable results panel The demo only had the SearchField input by itself with a trace strip; nothing actually demonstrated the search outcome. Added a 34-item dataset of UIX components (name + group) and a live results grid that filters by name or group substring as the user types. - Empty input renders 34/34 results. - Typing "form" filters to 17 components from the Forms group. - "drawer" filters to 1 (Drawer). - Empty results state renders a graceful "No matches for ..." message. - Counter shows `results N / total` so the demo doubles as a quick feedback loop for the debounceMs control (count + meta animate together when the debounced onValueChange finally fires). CSS lives inline in the demo (`.search-field-results*` selectors) since this is demo content rather than reusable component visuals. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../uix/components/search-field/+page.svelte | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/web/routes/uix/components/search-field/+page.svelte b/web/routes/uix/components/search-field/+page.svelte index 7cae0d23f..41b223a96 100644 --- a/web/routes/uix/components/search-field/+page.svelte +++ b/web/routes/uix/components/search-field/+page.svelte @@ -38,6 +38,53 @@ const partsList = $derived([...compiled.parts.byKebab.values()]); const events = $derived([...compiled.actions.byName.values()]); const isEmpty = $derived(value.length === 0); + + // Demo dataset for the live search panel. + const components: { name: string; group: string }[] = [ + { name: 'Accordion', group: 'Disclosure' }, + { name: 'Avatar', group: 'Media' }, + { name: 'Breadcrumb', group: 'Navigation' }, + { name: 'Button', group: 'Action' }, + { name: 'Calendar', group: 'Forms' }, + { name: 'Checkbox', group: 'Forms' }, + { name: 'Collapsible', group: 'Disclosure' }, + { name: 'Color picker', group: 'Forms' }, + { name: 'Combobox', group: 'Forms' }, + { name: 'Date picker', group: 'Forms' }, + { name: 'Dialog', group: 'Overlay' }, + { name: 'Drawer', group: 'Overlay' }, + { name: 'Editable', group: 'Forms' }, + { name: 'Field', group: 'Forms' }, + { name: 'File upload', group: 'Forms' }, + { name: 'Icon', group: 'Media' }, + { name: 'Meter', group: 'Status' }, + { name: 'Number field', group: 'Forms' }, + { name: 'Pagination', group: 'Navigation' }, + { name: 'Popover', group: 'Overlay' }, + { name: 'Progress', group: 'Status' }, + { name: 'Radio group', group: 'Forms' }, + { name: 'Rating group', group: 'Forms' }, + { name: 'Search field', group: 'Forms' }, + { name: 'Select', group: 'Forms' }, + { name: 'Slider', group: 'Forms' }, + { name: 'Switch', group: 'Forms' }, + { name: 'Tabs', group: 'Disclosure' }, + { name: 'Tag group', group: 'Forms' }, + { name: 'Time picker', group: 'Forms' }, + { name: 'Toast', group: 'Overlay' }, + { name: 'Toggle', group: 'Action' }, + { name: 'Toolbar', group: 'Action' }, + { name: 'Tooltip', group: 'Overlay' } + ]; + + // Filter matches name OR group, case-insensitive. + const results = $derived.by(() => { + const q = value.trim().toLowerCase(); + if (!q) return components; + return components.filter( + (c) => c.name.toLowerCase().includes(q) || c.group.toLowerCase().includes(q) + ); + }); const variants: SearchFieldVariant[] = ['surface', 'outline', 'ghost']; const colors: SearchFieldColor[] = [ 'primary', @@ -170,6 +217,31 @@ + + +
    + {#if results.length === 0} +
  • + No matches for "{value}" +
  • + {:else} + {#each results as item (item.name)} +
  • + {item.name} + {item.group} +
  • + {/each} + {/if} +
+
+ results + {results.length} / {components.length} +
@@ -561,3 +633,51 @@ {/if}
+ +