InputsReact
A hierarchy of items whose parents open and close, such as a file explorer or a table of contents.
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:
<Tree.Item>
<Tree.ItemContent>
<Tree.ItemToggle />
<Tree.ItemText />
</Tree.ItemContent>
<Tree.Group />
</Tree.Item>
<Tree.Root />
// Not in this example: Tree.Empty, Tree.ItemIndicatorExamples
Checkbox
multiple and cascade: Mozzarella starts checked, so Cheese is mixed; checking a group checks its toppings, and the checked values are listed below.
Lazy
loadChildren fetches each folder's files on its first open; the chevron pulses while it loads, and the empty Drafts turns into a leaf.
Move
allowMove: drag a row, or Control+X and Control+V; canMove keeps the folders at the top level and onItemMove applies the move to the app's lists.
Virtualized
50 folders of 20 files as data, virtualized, with about 30 rows mounted; Home, End and the page keys reach rows that are not mounted.
API reference
50 props across 8 parts, in React syntax. className, style, render are listed once, under Shared props.
EmptyTree.Empty
Content shown while the tree has no items. Renders nothing otherwise. It has no ARIA role, since only items may sit inside a tree. Renders a <div>.
GroupTree.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 |
ItemTree.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. |
ItemContentTree.ItemContent
The row of an item: its toggle, indicator and text. Its text names the item. Renders a <div>.
ItemIndicatorTree.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 |
ItemTextTree.ItemText
The text of an item: what typeahead matches and what TreeSelect.Value shows. Renders a <span>.
ItemToggleTree.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 |
RootTree.Root
Groups all parts of the tree: a hierarchical list whose parents expand and collapse., followed by the status region and the form inputs. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
valueThe value of the tree. | TreeInputValue<Value, Multiple> | null | — |
defaultValueThe uncontrolled value of the tree when it's initially rendered. | TreeInputValue<Value, Multiple> | null | — |
onValueChangeEvent handler called when the value changes. | ((value: TreeOutputValue<Value, Multiple>, eventDetails: TreeRootChangeEventDetails) => void) | — |
multipleWhether multiple items can be selected. | Multiple | false |
cascadeWith multiple: checking an item checks its enabled descendants, a parent shows checked, | boolean | false |
cascadeValueWhat the value holds under cascade: every checked item ('all'), only the topmost | TreeCascadeValue | 'all' |
selectableWhether items can be selected. | boolean | true |
disallowEmptySelectionWhether the last selected item of a multiple tree cannot be deselected. | boolean | false |
expandedThe values of the open items. | readonly Value[] | — |
defaultExpandedThe values of the items open when the tree is 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 (an expandable item without | ((item: Value, options: { signal: AbortSignal; }) => Promise<void | readonly Value[]>) | — |
onLoadChildrenErrorCalled when loadChildren rejects. | ((item: Value, error: unknown) => void) | — |
itemsThe whole tree as data, mounted or not. | readonly Value[] | — |
getItemChildrenThe children of an item of items: an array (a parent, possibly empty), null (a parent | ((item: Value) => readonly Value[] | null | undefined) | — |
virtualizedWhether the app renders only some rows (a virtualizer): the visible rows of items as a | boolean | false |
onItemHighlightedVirtualized only: called when navigation is about to move focus to the row at | ((item: Value, eventDetails: TreeRootHighlightEventDetails) => void) | — |
itemToStringLabelConverts an item to its text, for typeahead and items that are not mounted. | ((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) | — |
highlightedValueThe value of the focused item. | Value | null | — |
onHighlightChangeEvent handler called when the focused item changes. | ((value: Value | null) => void) | — |
orientationThe orientation of the tree. | TreeOrientation | 'vertical' |
loopFocusWhether Down on the last item moves to the first, and Up on the first to the last. | boolean | false |
allowMoveWhether items can be moved by dragging their row, or with Ctrl/Cmd+X and Ctrl/Cmd+V. | boolean | false |
canMoveWhether a move may happen. | ((details: TreeMoveDetails<Value>) => boolean) | — |
onItemMoveCalled with a move, in terms of the tree after it. | ((details: TreeMoveDetails<Value>, eventDetails: TreeRootItemMoveEventDetails) => void) | — |
actionsRefA ref to start, complete and cancel a move from the app (a context menu). | React.RefObject<TreeRootActions | null> | — |
disabledWhether the component should ignore user interaction. | boolean | false |
readOnlyWhether the user should be unable to change the selection. | 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 status region. | Partial<TreeLabels> | — |
| Data attribute | Description |
|---|---|
data-disabled | Present when the tree is disabled. |
data-readonly | Present when the tree does not allow changing the selection. |
data-required | Present when the tree is required. |
data-multiple | Present when the tree allows multiple selected items. |
data-cascade | Present when checking an item checks its descendants. |
data-orientation | Indicates the orientation of the tree. |
data-empty | Present when the tree has no items. |
data-moving | Present while a keyboard move is pending. |
data-valid | Present when the tree is in a valid state (when wrapped in Field.Root). |
data-invalid | Present when the tree is in an invalid state (when wrapped in Field.Root). |
data-dirty | Present when the tree's value has changed (when wrapped in Field.Root). |
data-touched | Present when the tree has been touched (when wrapped in Field.Root). |
data-filled | Present when the tree has a value (when wrapped in Field.Root). |
data-focused | Present when an item of the tree is focused (when wrapped in Field.Root). |
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 |
|---|---|---|
| ArrowDownArrowUp | Moves focus to the next or previous visible item, past the children of closed parents | Root |
| ArrowRightArrowLeft | Opens a closed parent, then moves to its first child; moves to the parent, then closes it | Item |
| HomeEnd | Moves focus to the first item, or the last visible one | Root |
| PageDownPageUp | Moves focus ten visible items and stops at the ends | Root |
| * | Opens every closed sibling of the focused item | Root |
| Moves focus to the next visible item whose text starts with what was typed | Root | |
| EnterSpace | Selects the focused item; with multiple, toggles it | Item |
| ShiftArrowDownShiftArrowUp | With multiple, extends the selection from the anchor | Root |
| ControlAMetaA | With multiple, selects the visible enabled items, or clears them when all are selected | Root |
| ControlXControlV | With allowMove, takes the item and puts it into the focused parent, or after a focused leaf | Item |
| Escape | Cancels a move in progress | Root |
| Tab | Leaves the tree from any item | Root |
Accessibility
| Part | Sets | So that |
|---|---|---|
| Root | role="tree", aria-orientation, aria-multiselectable with multiple, aria-required and aria-disabled, never aria-activedescendant; named by aria-label, aria-labelledby or Field.Label | the tree is read as one widget with its name; a development build warns without one |
| Item | role="treeitem" with aria-level, aria-posinset and aria-setsize, from the data when virtualized | each item says where it is, also when its siblings are not mounted |
| Item | aria-expanded on a parent only, aria-selected on a selectable item, aria-checked (true, false, mixed) instead with cascade | the state is read with the item; a leaf says nothing about opening |
| Item | named by its ItemContent, not by the text of its Group | an open folder is read as "src", not as every file inside it |
| Item | aria-busy while loadChildren runs, aria-current="page" with active, aria-disabled on a disabled item and its descendants | loading, the current page and disabled branches are read; a disabled item keeps focus |
| Group | role="group" inside its item | the children belong to their parent |
| ItemToggle | aria-hidden, never focusable | the chevron is for sight; the item's state says it |
| ItemIndicator | aria-hidden, never focusable | the check is for sight; the item's state says it |
| Root | one polite role="status" region next to the tree, always mounted | "Reports loaded, 2 items", "Reports is empty", a load error, *, Control+A and moves are announced; expanding and selecting are silent unless labels sets their texts |
Focus order
- The tree is one Tab stop: the active item, or the selected one, or the first enabled item. Nothing takes focus on mount; with the selected item inside a closed parent, that parent takes the stop.
- Inside, the arrow keys,
Home,End, the page keys and typeahead move focus;Tableaves the tree from any item, and coming back returns to the last focused item. - Closing the parent of the focused item moves focus to that parent; removing the focused item moves it to the next sibling, else the previous one.
- A required, empty tree in a Form gets focus on submit, on its Tab stop item.