FieldPicker
Picks one field of an entity type and emits its dotted path, descending through linked types on the way.
It sits on the Popover over a Command list rather than the shared picker control: its list is a stack of levels a row descends into, not one flat list of values, and its press, its keyboard and its dismissal already match the pickers.
Single and multi: this picker takes one path, and ColumnPicker is its ordered multi, an array of paths ordered by drag.
Install
Section titled “Install”pnpm dlx shadcn@latest add https://sg-widgets.vercel.app/r/react/field-picker.jsonpnpm dlx shadcn-svelte@latest add https://sg-widgets.vercel.app/r/svelte/field-picker.jsonReact
Loading the React demo…
Svelte
Loading the Svelte demo…
| prop | type | default | meaning |
|---|---|---|---|
context | SgContext | required | The widget context. The schema is read through it, once per page. |
entityType | string | required | The type the path starts on. |
value | string | '' | The dotted path. Empty when nothing is chosen. |
options | string[] | — | A fixed list of paths, offered flat. Each row is labelled by its resolved path; the list restrictions below do not apply. |
deepLinks | boolean | false | Allow descending through entity fields. |
showCode | boolean | false | Show the programmatic name beside the display name in each row. |
maxDepth | number | 2 | How many hops a path may take. |
dataTypes | string | string[] | — | Data types a field must have to be selected. |
validTypes | string[] | — | A field is selectable only if it links one of these. |
exclude | string[] | — | Full dotted paths to drop. |
hidePaths | string[] | — | Dotted prefixes to drop, along with everything beneath them. |
filterableOnly | boolean | false | Drop the data types the API refuses in a filter. |
extraFields | { name, displayName? }[] | — | Synthetic entries offered at the root only, labelled computed. |
filter | (field, path) => boolean | — | Your own visibility test over the schema and the candidate's full path. |
closeOnSelect | boolean | true | Off keeps the popover open for the next pick. |
placeholder | string | 'Select a field' | Shown while nothing is chosen. |
searchPlaceholder | string | 'Search fields…' | Shown in the search box. |
emptyLabel | string | 'No match' | Shown when the search matches nothing. |
loadingLabel | string | 'Loading…' | Names the skeletons a read stands behind, for a screen reader. |
errorLabel | string | — | Shown in place of what the failed read said. |
clearable | boolean | true | Show the clear control once a field is chosen. |
readonly | boolean | false | Keeps full contrast and removes the chevron and the clear control. |
disabled | boolean | false | |
invalid | boolean | false | Applies the invalid ring and aria-invalid. |
size | 'sm' | 'md' | 'lg' | 'md' | |
open | boolean | false | Whether the popover is showing. Two-way in Svelte. |
className / class | 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.
The value is a dotted path. A root field is its own code; every hop names the field followed and the type it landed on, which is the syntax a projection and a filter both take.
sg_status_listentity.Shot.codeproject.Project.sg_start_datedataTypes and validTypes bind what may be chosen, not what may be walked through. A picker
restricted to dates still lists the link fields, so a date behind a link stays reachable. Everything
else — exclude, hidePaths, filterableOnly and filter — hides the row outright and is measured
against the full dotted path, so excluding a root field leaves a field of the same name behind a hop
alone.
A link that declares one target type descends at once. One declaring several replaces the list with
one row per type and asks which. A type already on the path is not offered again, and the path stops
at maxDepth.
options fills the list a second way: the paths you name, flat, in the order you give them, each
labelled by its resolved path with its data-type glyph and, under showCode, its code. A path the
schema cannot resolve keeps its place, shown as it was written and marked in its sub-label. With
options set the list does not nest, so deepLinks, maxDepth, hidePaths, exclude,
dataTypes, validTypes, filterableOnly, extraFields and filter do not apply and the
breadcrumb never shows. The search box narrows the flat list on the label and on the path.
The closed control shows the friendly path, the display names joined with a chevron, never the raw one.
Events
Section titled “Events”| event | payload | when |
|---|---|---|
onValueChange | string | A field is chosen, or the clear control is pressed, which sends the empty string. |
onOpenChange | boolean | The popover opened or closed. Svelte also binds it with bind:open. |
None.
Keyboard
Section titled “Keyboard”| key | does |
|---|---|
Enter, Space | On the closed control, opens it. |
Down, Up | Move the cursor through the rows. |
Right | Descend into the highlighted field, or pick the highlighted target type. |
Left | Go back one level, or cancel the choice of target type. |
Enter | Choose the highlighted field. On a field that can only be descended into, descends. |
Backspace | In an empty search box, clears the value. |
Escape | Close, keeping the value. |
The search box clears on every hop, on every selection and on every close, and the control is never remounted, so focus stays where you are typing.
The control is the Popover and the Command list rather than the picker base: a field list walks a path through links, so its popup carries a breadcrumb and its rows descend, which the base’s flat list does not. It keeps the picker contract of design rule 7 all the same, and A drive over both frameworks checks it.
API behaviour
Section titled “API behaviour”A dotted path through a multi_entity field reads back nothing: HTTP 200 with the key absent from
attributes, indistinguishable from no data. Only single entity fields are descended into
(016_dotted_multi_entity).
A dotted path names the type it travels through, and a projection checks that middle segment against
the field’s valid_types (field_types/entity).
GET /schema/<Type>/fields is 48KB and about 330ms per type, so the schema service caches it and a
hop back to a type already visited costs nothing (002_schema).
Five data types take no filter operator at all — url, calculated, password, serializable and
summary — and the API answers data type cannot be used in a filter for each. filterableOnly
drops them (017_filter_operators).
Reference
Section titled “Reference”The list is one engine in both frameworks: it is always open, always in place and always holds a highlight. It fades at whichever edge has more content past it, holds a gutter for its scrollbar, and carries a live region saying what it is doing. The pattern is coss.com/ui at e937bec, read as a reference and not installed.