StatusMultiPicker
Picks any number of status codes for an entity type, offering only the codes the project it is scoped to allows.
Single and multi: StatusPicker takes one code.
Install
Section titled “Install”pnpm dlx shadcn@latest add https://sg-widgets.vercel.app/r/react/status-multi-picker.jsonpnpm dlx shadcn-svelte@latest add https://sg-widgets.vercel.app/r/svelte/status-multi-picker.jsonReact
Loading the React demo…
Svelte
Loading the Svelte demo…
| prop | type | default | meaning |
|---|---|---|---|
searchPlaceholder | string | 'Search statuses…' | Placeholder of the search box. |
summary | 'chips' | 'ellipsis' | 'count' | 'ellipsis' | How many of the selection the control shows. |
badge | 'both' | 'icon' | 'text' | 'glyph' | 'both' | What one selected status is drawn as. |
max | number | 0 | Badges drawn before the rest becomes +n. 0 lets the row fit what it can, and badge="icon" draws twice as many. |
contextfrom StatusPicker | SgContext | required | The widget context. The options and the status table are read through it, once per page. |
entityTypefrom StatusPicker | string | required | The type whose status field is offered. |
projectIdfrom StatusPicker | number | — | Offer the codes this project allows. |
projectIdsfrom StatusPicker | number[] | — | Offer the codes every one of these projects allows. |
fieldfrom StatusPicker | string | status field | A list or status field other than the type's own. |
value | string[] | [] | The selected codes. bind:value in Svelte, value with onValueChange in React. |
placeholder | string | 'Select statuses' | Shown with nothing selected. |
emptyLabel | string | 'No match' | Shown when the search matches nothing. |
loadingLabelfrom StatusPicker | string | 'Loading…' | Names the skeletons a read stands behind, for a screen reader. |
errorLabelfrom StatusPicker | string | — | Shown in place of what the failed read said. |
clearablefrom StatusPicker | boolean | the field | Offers a control that clears the value. Unset, it follows the field: a mandatory one is never clearable. |
readonlyfrom StatusPicker | boolean | false | Keeps full contrast and drops the chevron, the popup and the clear control. |
disabledfrom StatusPicker | boolean | false | Greys the control and takes it out of the tab order. |
invalidfrom StatusPicker | boolean | false | Sets aria-invalid and the destructive ring. |
showCodefrom StatusPicker | boolean | true | Draws the code as a row's right-aligned secondary, when it says more than the label. |
secondaryfrom StatusPicker | (option) => string | the code | A row's right-aligned value, of the caller's own making. |
subLabelfrom StatusPicker | (option) => string | — | The muted line under a row's label. |
siteUrlfrom StatusPicker | string | the context's | Passed to every badge, for a sprite cell outside the status icon set. |
sizefrom StatusPicker | 'sm' | 'md' | 'lg' | 'md' | Control height: 7, 8 and 9. |
openfrom StatusPicker | boolean | false | Whether the popup is showing. Two-way in Svelte. |
class / classNamefrom StatusPicker | string | — | Merged after the widget's own classes. |
Every other attribute is spread onto the root: id, aria-*, data-*, key handlers and a
ref to the root element.
summary and badge are two axes. summary says how many of the selection the control shows:
chips makes it a token field with every badge, wrapped, each with its own remove control inside the
badge, the caret beside them, and +n after the last badge max allows; ellipsis makes it a one-line trigger with
as many whole badges as the row fits, never a cut one, then +n inline after the last, and the
search box at the top of the popup; count is the same trigger reading 3 selected.
badge says what one status is drawn as: both is the icon and the label, icon drops the label
and the per-badge remove, so removal happens in the list and twice as many fit, and text is the
label alone.
A row is the row anatomy of the design rules, after its checkbox: the status icon as the leading mark,
the display name with the matched runs bold, and the code right-aligned. It is what the
filter bar draws for a status facet, so a page listing statuses twice lists
them the same way. A status has one display name and one icon, so the row’s labelField and
thumbnail are fixed, and it has no fields of its own, so its secondary is a function rather than a
field name. The control keeps the badges, where a status is a value rather than an option.
A press on +n opens the list, where the hidden codes are. Without a project the options are the site
vocabulary, which hides nothing. projectIds offers the intersection of what each project allows,
in the first project’s order.
The root carries the size and, while the options load, data-loading.
Events
Section titled “Events”| event | payload | when |
|---|---|---|
onValueChange | string[] | A status was ticked or unticked. |
onOpenChangefrom StatusPicker | boolean | The popup opened or closed. |
onValueChange fires with the whole array on every change, including the empty array from the clear
control. A selected code the option set does not carry is kept, never dropped.
onOpenChange fires when the popup opens or closes. Svelte also binds it with bind:open.
None. The rows are the shared picker row; the closed state is status badges.
Keyboard
Section titled “Keyboard”| key | does |
|---|---|
| Space, Enter | On the chevron, opens the list |
| any text | Narrows the rows, from the caret under chips and from the popup's search box otherwise |
| Home End | First and last row |
| Space, Enter, ↓from StatusPicker | Opens the list |
| ↑ ↓ | Moves the highlight and keeps it in view; the last row holds it rather than wrapping |
| Enter | Toggles the highlighted status; the list stays open |
| Backspace | In an empty search box, highlights the last chip; a second one removes it |
| Esc | Closes, keeps the selection and clears the query; on a closed control it does nothing |
| Tabfrom StatusPicker | Leaves the control, or reaches the clear control when there is one |
API behaviour
Section titled “API behaviour”A project’s usable statuses are valid_values minus hidden_values, read with project_id.
valid_values is the site’s whole vocabulary and is byte-identical at every scope, so reading it
alone tells you nothing about a project (probe 009).
REST does not enforce hidden_values on write: a hidden status writes and reads back. Subtracting is
right for offering a choice and wrong for testing a row, so a selected code outside the option set
keeps a row of its own here, labelled with the code (field_types/status_list).
A status list has no substring operator, so there is no server-side type-ahead over it. The vocabulary is read once and the query input narrows it in the browser (field_types/status_list).
Labels come from display_values, and a missing key falls back to the raw code rather than dropping
the option (probe 009).
Project’s status field is sg_status, a plain list rather than a status_list. There is no Status
row behind a list, so those options carry no icon and no colour (entity_types/Project).
Reference
Section titled “Reference”The row, chip and checkbox anatomy follows shadcn’s Base UI Combobox, read through shadcn 4.21.0. The primitive underneath is Base UI Combobox 1.8.0 in React and Bits UI Combobox 2.19.1 in Svelte. shadcn-svelte ships no combobox item, so each widget composes the headless primitive of its framework and both draw the same rows, the same classes and the same states.