SearchPicker
Search input + scrollable bordered list/grid + counter row. The body of any search-driven modal.
Composed CONTENT for a search-driven selection — not a Modal itself. Selection state and the Modal chrome (title, description, footer) stay with the consumer because both differ across usages (add multi-select vs replace single-select; cancel-only vs cancel+"Add N selected"). Wrap with `<Modal title=… footer=…><SearchPicker …/></Modal>`. The scroll container has a FIXED height (default 60vh) so the Modal does not jump in size as the user types and the filtered list shrinks — only the inner list scrolls; the empty state is centred inside the same locked container. Replaces the duplicated SearchInput + Box scaffolding in DealItems and DocumentHub demos.
Install
Pull from the workspace packages (already available if you ran pnpm add):
Examples
List layout — catalog product picker
How DealItems picks catalog items. Click the button to open the modal; the picker is multi-select via consumer state (a Set of ids), checkmark in ListRow.leading.
Grid layout — document-template picker
How DocumentHub picks templates. Single-select — clicking a tile closes the modal and "creates" the document.
Variants
listdefaultRenders children directly into the scroll container. Use with ListRow appearance="flush".gridAuto-fill CSS grid (repeat(auto-fill, minmax(gridMinWidth, 1fr))). Use with tile-shaped children.Anatomy
searchTop SearchInput, controlled by the consumer (query / onQueryChange).containerBordered scroll container with FIXED height (default "60vh"). Only the inner list scrolls — the container itself never resizes mid-filter.itemsConsumer children — ListRow rows (layout="list") or tile components (layout="grid").emptyStateCentred inside the same locked container when children is empty/null — preserves the modal size.metaOptional counter strip at the bottom — "12 of 30 templates" etc.