HierarchicalSearch
Walks the navigation tree level by level, or searches it, and emits the row you pick with the path
that reaches it. It is a wrapper over search-control, which owns the
debounce and the list.
Install
Section titled “Install”pnpm dlx shadcn@latest add https://sg-widgets.vercel.app/r/react/hierarchical-search.jsonpnpm dlx shadcn-svelte@latest add https://sg-widgets.vercel.app/r/svelte/hierarchical-search.jsonReact
Loading the React demo…
Svelte
Loading the Svelte demo…
| prop | type | default | meaning |
|---|---|---|---|
context | SgContext | required | The widget context. Every read goes through it, so widgets on a page share one cache. |
rootPath | string | '/' | Where the tree starts. /Project/70 scopes it to that project. |
entityTypes | string[] | Record<string, WireCondition[] | null> | Shot, Asset, Sequence, Task | Types a search may end on. |
thumbnail | string | false | 'image' | Field holding the thumbnail URL. A row with no picture falls back to its type glyph. |
labelField | string | — | Field holding the row label. |
subLabelField | string | CollectionColumn | null | null | The muted line under the label. A resolved column renders it by type. |
subLabel | (row) => string | — | The muted line of your own. Wins over subLabelField. |
secondaryField | string | CollectionColumn | null | null | The right-aligned value, drawn by its data type. |
secondary | (row) => string | — | Right-aligned text of your own. Wins over secondaryField. |
showCode | boolean | false | Shows the row's code beside the label when the two differ. |
fields | string[] | [] | Extra fields to request, so your own sub-label or secondary can read them. |
noMatchLabel | string | 'No match' | Shown when a query matches nothing. |
size | 'sm' | 'md' | 'lg' | 'md' | Row text, leading slot and glyphs. |
class / className | string | — | Merged after the widget's own classes. |
placeholder | string | 'Search the hierarchy…' | Text in the search input. |
emptyLabel | string | 'No rows' | Shown when a level holds 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. |
Every other attribute is spread onto the root: id, aria-*, data-*, key handlers and a
ref to the root element.
With no query the list is one level of the tree. With a query it is the rows that match, each shown as the breadcrumb that reaches it, the row itself last and in bold.
A row is the shared picker row: a picture, the label, a muted sub-label and a right-aligned secondary
drawn by its data type. A level of the tree carries no picture, so those rows show their type glyph;
a searched row shows the thumbnail the second read answered. With no subLabelField and no
subLabel, the sub-label is the row’s type, or Group on a level.
entityTypes limits what a search returns. Browsing reaches every level whatever it says; a row that
is not one of these types opens instead of being picked.
Events
Section titled “Events”| event | payload | when |
|---|---|---|
onSelect | EntityRef, EntityRef[] | A row was picked. The second argument is every row its path runs through, root first. |
None.
Keyboard
Section titled “Keyboard”| key | does |
|---|---|
Down / Up | Moves through the level. |
Right | Opens the level below the row. |
Left / Backspace | Goes back up one level. |
ArrowDown / ArrowUpfrom SearchControl | Moves the highlight, through a load-more page. |
Enter | Picks the row, or opens it when it is not a searchable type. |
Escape | Clears the query. |
Backspace goes up only when the query is empty; otherwise it edits the query.
API behaviour
Section titled “API behaviour”The tree is read one level per call: the answer names the paths of its children and says which of them are worth opening (post_hierarchy_expand).
A path runs through field names, such as sg_sequence between a project’s shots and a sequence, so
the shape of the tree is the site’s own navigation configuration rather than a fixed hierarchy
(post_hierarchy_search).
Searching does not go through the hierarchy endpoints. hierarchy/_search takes an entity and
answers where it sits; its criteria accepts the single key entity and any other key answers
search_criteria size must be 1, which counts the keys it recognises rather than the ones sent
(post_hierarchy_search). So the words are matched by _text_search and each hit is then asked for
its path. That endpoint has no fields parameter either, so the picture and whatever the row props
name are a second read of the hits (post_entity_text_search).
The hierarchy endpoints take application/json and refuse the vendor content types every other POST
on this API demands (046_search_without_a_path).
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.