InputsReact
A button that opens a tree of options in a popup and shows the one picked, such as a folder or a category picker.
Rendered by React 19.3.1
Installation
npm i @hamur/reactNot on npm yet. The package goes out under the chosen name.
Anatomy
Import the component and assemble its parts:
<TreeSelect.Item>
<TreeSelect.ItemContent>
<TreeSelect.ItemToggle />
<TreeSelect.ItemText />
</TreeSelect.ItemContent>
<TreeSelect.Group />
</TreeSelect.Item>
<TreeSelect.Root>
<TreeSelect.Trigger>
<TreeSelect.Value />
<TreeSelect.Icon />
</TreeSelect.Trigger>
<TreeSelect.Portal>
<TreeSelect.Positioner>
<TreeSelect.Popup>
<TreeSelect.Tree />
</TreeSelect.Popup>
</TreeSelect.Positioner>
</TreeSelect.Portal>
</TreeSelect.Root>
// Not in this example: TreeSelect.Arrow, TreeSelect.Backdrop, TreeSelect.Clear, TreeSelect.Empty, TreeSelect.Input, TreeSelect.ItemIndicator, TreeSelect.StatusExamples
Filter
An Input at the top of the popup: typing keeps the matching cities with their countries, marks the matches in bold, and shows "No places found" when nothing matches.
API reference
77 props across 20 parts, in React syntax. className, style, render are listed once, under Shared props.
ArrowTreeSelect.Arrow
Displays an element positioned against the popover anchor. Renders a <div>.
| Data attribute | Description |
|---|---|
data-open | Present when the popup is open. |
data-closed | Present when the popup is closed. |
data-side | Indicates which side the popup is positioned relative to the trigger. |
data-align | Indicates how the popup is aligned relative to specified side. |
data-uncentered | Present when the popover arrow is uncentered. |
BackdropTreeSelect.Backdrop
An overlay displayed beneath the popover. Renders a <div>.
| Data attribute | Description |
|---|---|
data-open | Present when the popup is open. |
data-closed | Present when the popup is closed. |
data-starting-style | Present when the popup begins animating in. |
data-ending-style | Present when the popup is animating out. |
ClearTreeSelect.Clear
Empties the value. Place it outside the trigger. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
EmptyTreeSelect.Empty
Content shown while the tree has no items, or the Input's text matches none. Renders nothing otherwise. Renders a <div>.
GroupTreeSelect.Group
The children of an item, shown while it is open. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
keepMountedWhether to keep the element in the DOM while the item is closed. | boolean | false |
hiddenUntilFoundAllows the browser's built-in page search to find and open the group's items. | boolean | false |
IconTreeSelect.Icon
An icon that shows the trigger opens a popup. Hidden from assistive technology. Renders a <span>.
InputTreeSelect.Input
A text input that filters the tree. It keeps focus while the keys move the highlight through the tree, named by aria-activedescendant. Renders an <input>.
ItemTreeSelect.Item
An item of the tree, with its row (Tree.ItemContent) and its children (Tree.Group). Renders a <div>.
| Prop | Type | Default |
|---|---|---|
children | React.ReactNode | — |
valueA unique value that identifies this item. | any | null |
labelOverrides the text that typeahead matches. | string | — |
disabledWhether the component should ignore user interaction. | boolean | false |
selectableWhether the item can be selected. | boolean | true |
expandableWhether the item is a parent whose Tree.Group is not rendered yet, so expanding it calls | boolean | false |
activeWhether the item is the current one (the page shown), as aria-current="page". | boolean | false |
indexVirtualized only: the position of the row among the visible rows. | number | — |
| Data attribute | Description |
|---|---|
data-expanded | Present when the item is open. |
data-selected | Present when the item is selected (without cascade). |
data-checked | Present when the item is checked (with cascade). |
data-indeterminate | Present when some of the item's descendants are checked, not all (with cascade). |
data-disabled | Present when the item is disabled. |
data-highlighted | Present when the item is highlighted (holds focus, or the input's active descendant). |
data-active | Present when the item is the current one (active). |
data-loading | Present while the item's children load. |
data-load-error | Present when the last load of the item's children failed. |
data-leaf | Present when the item has no children. |
data-level | The depth of the item, from 1 at the top level. |
data-match | Present when the item matches TreeSelect.Input's query. |
data-moving | Present while the item is being moved. |
data-drop-target | Where a dragged item would land: before, after or inside this one. |
ItemContentTreeSelect.ItemContent
The row of an item: its toggle, indicator and text. Its text names the item. Renders a <div>.
ItemIndicatorTreeSelect.ItemIndicator
Shows whether the item is selected or checked. A press toggles the item in a multiple tree. Renders a <span>.
| Prop | Type | Default |
|---|---|---|
children | React.ReactNode | — |
keepMountedWhether to keep the element in the DOM when the item is not selected. | boolean | false |
ItemTextTreeSelect.ItemText
The text of an item: what typeahead matches and what TreeSelect.Value shows. Renders a <span>.
ItemToggleTreeSelect.ItemToggle
Opens and closes its item without selecting it. Hidden from assistive technology and not focusable: the item's keys do the same. Renders a <span>.
| Prop | Type | Default |
|---|---|---|
keepMountedWhether to render the toggle for leaves too, to align the rows. | boolean | false |
PopupTreeSelect.Popup
The container of the tree and the optional Input. Focus moves to the Input, else to the selected item, when it opens, and returns to the trigger when it closes. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
initialFocusDetermines the element to focus when the popover is opened. | boolean | React.RefObject<HTMLElement | null> | ((openType: InteractionType) => boolean | void | HTMLElement | null) | — |
finalFocusDetermines the element to focus when the popover is closed. | boolean | React.RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | void | HTMLElement | null) | — |
PortalTreeSelect.Portal
A portal element that moves the popup to a different part of the DOM. By default, the portal element is appended to <body>. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
keepMountedWhether to keep the portal mounted in the DOM while the popup is hidden. | boolean | false |
containerA parent element to render the portal element into. | HTMLElement | ShadowRoot | React.RefObject<HTMLElement | ShadowRoot | null> | null | — |
PositionerTreeSelect.Positioner
Positions the popup against the trigger. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
anchorAn element to position the popup against. | Element | VirtualElement | React.RefObject<Element | null> | (() => Element | VirtualElement | null) | null | — |
positionMethodDetermines which CSS position property to use. | "absolute" | "fixed" | 'absolute' |
sideWhich side of the anchor element to align the popup against. | Side | 'bottom' |
sideOffsetDistance between the anchor and the popup in pixels. | number | OffsetFunction | 0 |
alignOffsetAdditional offset along the alignment axis in pixels. | number | OffsetFunction | 0 |
collisionBoundaryAn element or a rectangle that delimits the area that the popup is confined to. | Boundary | 'clipping-ancestors' |
collisionPaddingAdditional space to maintain from the edge of the collision boundary. | Padding | 5 |
stickyWhether to maintain the popup in the viewport after | boolean | false |
arrowPaddingMinimum distance to maintain between the arrow and the edges of the popup. | number | 5 |
disableAnchorTrackingWhether to disable the popup from tracking any layout shift of its positioning anchor. | boolean | false |
collisionAvoidanceDetermines how to handle collisions when positioning the popup. | CollisionAvoidance | — |
alignHow to align the popup relative to the specified side. | Align | 'start' |
RootTreeSelect.Root
Groups all parts of the tree select: a trigger that opens a tree in a popup., only the form inputs. Doesn’t render its own element.
| Prop | Type | Default |
|---|---|---|
children | React.ReactNode | — |
idThe id of the field control, for the hidden input. | string | — |
valueThe value of the tree select. | TreeSelectInputValue<Value, Multiple> | null | — |
defaultValueThe uncontrolled value when initially rendered. | TreeSelectInputValue<Value, Multiple> | null | — |
onValueChangeEvent handler called when the value changes. | ((value: TreeSelectOutputValue<Value, Multiple>, eventDetails: TreeSelectRootChangeEventDetails) => void) | — |
multipleWhether multiple items can be selected. | Multiple | false |
cascadeWith multiple: checking an item checks its enabled descendants. | boolean | false |
cascadeValueWhat the value holds under cascade: 'all', 'parents' or 'leaves'. | TreeCascadeValue | 'all' |
selectableWhether items can be selected. | boolean | true |
disallowEmptySelectionWhether the last selected item of a multiple tree select cannot be deselected. | boolean | false |
expandedThe values of the open items. | readonly Value[] | — |
defaultExpandedThe values of the items open when initially rendered. | readonly Value[] | — |
onExpandedChangeEvent handler called when items open or close. | ((expanded: Value[], eventDetails: TreeRootExpandedChangeEventDetails) => void) | — |
expandOnPressWhether a press on a parent's row toggles it as well as selecting it. | boolean | true |
loadChildrenCalled when an item whose children are not loaded is expanded. | ((item: Value, options: { signal: AbortSignal; }) => Promise<void | readonly Value[]>) | — |
onLoadChildrenErrorCalled when loadChildren rejects. | ((item: Value, error: unknown) => void) | — |
itemsThe whole tree as data, so the value text and the filter know unmounted items. | readonly Value[] | — |
getItemChildrenThe children of an item of items: an array, null (not loaded) or undefined (a leaf). | ((item: Value) => readonly Value[] | null | undefined) | — |
virtualizedWhether the app renders only some rows (a virtualizer). | boolean | false |
onItemHighlightedVirtualized only: called when navigation moves to a row that is not mounted. | ((item: Value, eventDetails: TreeRootHighlightEventDetails) => void) | — |
itemToStringLabelConverts an item to its text, for the value text, typeahead and the filter. | ((item: Value) => string) | — |
itemToStringValueConverts an item value to a string for form submission. | ((item: Value) => string) | — |
isItemEqualToValueCustom comparison logic used to determine if an item value matches the current value. | ((itemValue: Value, value: Value) => boolean) | — |
isItemDisabledWhether an item of items is disabled, for items that are not mounted. | ((item: Value) => boolean) | — |
onHighlightChangeEvent handler called when the highlighted item changes. | ((value: Value | null) => void) | — |
loopFocusWhether Down on the last item moves to the first, and Up on the first to the last. | boolean | false |
openWhether the popup is open. | boolean | — |
defaultOpenWhether the popup is initially open. | boolean | false |
onOpenChangeEvent handler called when the popup opens or closes. | ((open: boolean, eventDetails: TreeSelectRootOpenChangeEventDetails) => void) | — |
onOpenChangeCompleteEvent handler called after any animations complete when the popup opens or closes. | ((open: boolean) => void) | — |
actionsRefA ref to imperative actions. | React.RefObject<PopoverRootActions | null> | — |
modalWhether the rest of the page is inert while the popup is open. | boolean | "trap-focus" | true |
inputValueThe text of TreeSelect.Input. | string | — |
defaultInputValueThe text of TreeSelect.Input when initially rendered. | string | — |
onInputValueChangeEvent handler called when the text of TreeSelect.Input changes. | ((value: string, eventDetails: TreeSelectRootInputValueChangeEventDetails) => void) | — |
filterWhether an item matches the Input's text. | ((item: Value, query: string, itemToString?: ((item: Value) => string) | undefined) => boolean) | null | — |
disabledWhether the component should ignore user interaction. | boolean | false |
readOnlyWhether the user should be unable to change the value. | boolean | false |
requiredWhether the user must choose a value before submitting a form. | boolean | false |
nameIdentifies the field when a form is submitted. | string | — |
formIdentifies the form that owns the hidden inputs. | string | — |
inputRefA ref to access the hidden input element. | React.Ref<HTMLInputElement> | — |
labelsThe texts of the tree's status region. | Partial<TreeLabels> | — |
StatusTreeSelect.Status
A polite status region for the filter's result, such as the number of matches. Renders a <div>.
TreeTreeSelect.Tree
The tree inside the popup, followed by its status region. It takes the value, the expansion and the items from TreeSelect.Root. Renders a <div>.
TriggerTreeSelect.Trigger
A button that opens the popup and shows the value. It is a combobox whose popup is a tree. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
idID of the trigger. | string | — |
ValueTreeSelect.Value
The text of the selected value: the item's label, or the labels joined in multiple. Renders a <span>.
| Prop | Type | Default |
|---|---|---|
childrenWhat to show for the value instead of the item labels: a node, or a function of the value. | React.ReactNode | ((value: any) => React.ReactNode) | — |
placeholderWhat to show while no value is selected. | React.ReactNode | — |
Shared props
Every part that renders an element takes these.
| Prop | Type |
|---|---|
classNameCSS class applied to the element, or a function that
returns a class based on the component's state. | string | ((state: State) => string | undefined) |
styleStyle applied to the element, or a function that
returns a style object based on the component's state. | React.CSSProperties | ((state: State) => React.CSSProperties | undefined) |
renderAllows you to replace the component's HTML element
with a different tag, or compose it with another component. | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, State> |
Keyboard and accessibility
Keyboard
| Keys | Does | Part |
|---|---|---|
| EnterSpaceArrowDownArrowUp | Opens the popup from the Trigger | Trigger |
| HomeEnd | Move through the tree and open and close parents, as in a Tree | Tree |
| Enter | Picks the focused item, closes the popup and returns focus to the Trigger | Item |
| EnterSpacemultiple | Toggles the focused item and keeps the popup open | Item |
| Escape | Closes the popup and returns focus to the Trigger | Popup |
| Tab | Closes the popup | Popup |
| ArrowDownArrowUpInput | Moves the highlight while focus stays in the Input | Input |
| EnterInput | Picks the highlighted item and closes the popup | Input |
| ArrowLeftInput | Moves the caret, and at the start of the text closes the highlighted parent | Input |
| ArrowRightInput | At the end of the text, opens the highlighted parent | Input |
| Input | Type into it and filter the tree; no typeahead | Input |
Accessibility
| Part | Sets | So that |
|---|---|---|
| Trigger | role="combobox", aria-haspopup="tree", aria-expanded and, while open, aria-controls; labelled by Field.Label or aria-labelledby | the button says it opens a tree, and whether it is open |
| Icon | aria-hidden | the chevron is for sight |
| Tree | role="tree" with the roles and states of a Tree: treeitem, aria-level, aria-expanded, aria-selected or aria-checked | the popup is read as the tree it is |
| Input | aria-controls the tree and aria-activedescendant the highlighted item | a screen reader follows the highlight while focus stays in the text |
| Item | data-match on a row that matches the Input | the examples set it in bold, with the ancestors of the match in normal weight |
| Empty | rendered only while nothing matches | "No places found" is there exactly when it is true |
| Status | a polite role="status" region | a count or a hint is announced as the filter changes |
| Clear | a button named "Clear" when it has no content, or by its text or aria-label | the value is emptied from the keyboard, with reason clear-press |
Focus order
- The
Triggeris the Tab stop, thenClearwhen there is one. Nothing takes focus on mount. - Opening moves focus to the selected item, its nearest visible ancestor, or the first item; with an
Input, to theInput. Enter,Escapeor a pick returns focus to theTrigger;Tabcloses the popup.- A required, empty tree select in a Form gets focus on submit, on its
Trigger.