Tree
elements man ui/templates/treeA hierarchical list that opens with the arrow keys, selects one node or many, and loads children on demand.
Exampleimport { Tree, TreeNode, visibleNodes } from "@elements/ui/tree"; <Tree label="Files" items={files} selected={state.selected} expanded={state.open} /> const files: TreeNode[] = [ { id: "src", label: "src", icon: "folder", children: [ { id: "app", label: "app.ts", icon: "file" }, ] }, { id: "readme", label: "README.md", icon: "file" }, ];
Parts
| Part | What it is |
|---|---|
Tree |
The role="tree" list. Renders every level from items. |
.tree-item |
A node: li[role=treeitem] holding its row and, when open, its group. |
.tree-row |
The visible row: toggle, icon, label. Indented by --tree-level. |
.tree-group |
The role="group" list of an open node's children. |
visibleNodes(items, expanded, filter?, state?) |
The shown nodes in order, each with its level, set position, index path and parent: the model a virtual tree renders a slice of, for a count or a search. |
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
items |
TreeNode[] |
(required) | The nodes: { id, label, children?, hasChildren?, disabled?, icon?, href? }. A node with href is a link. |
selected |
string[] (two-way) |
[] |
Ids of the selected nodes. |
expanded |
string[] (two-way) |
[] |
Ids of the open nodes. |
selection |
"single" | "multiple" | "none" |
"single" |
How many nodes can be selected. |
selectionFollowsFocus |
boolean |
false |
In single mode, moving focus selects the node. |
expandOnClick |
boolean |
true |
A click on a parent's row also opens or closes it. The chevron always does. |
label |
string |
"" |
aria-label of the tree. |
checkboxes |
boolean |
false |
Checkbox tree: a box per node, aria-checked, parents mixed when some leaves are checked. |
checked |
string[] |
[] |
Checked leaf ids. Two-way. |
filter |
string |
"" |
Shows the nodes whose label contains it and the parents that lead to them, opened. |
typeahead |
boolean |
true |
Letters move focus to the next node by label. |
indentGuides |
boolean |
false |
Draws a rule down each open group, under its parent's chevron. |
canRename |
(node: TreeNode) => boolean |
() => false |
F2 renames a node it allows. |
renameLabel |
string |
"Name" |
aria-label of the rename input. |
renaming |
string (two-way) |
"" |
The node being renamed, or empty. Write a node id to start a rename (a Rename button), empty to cancel; F2, Enter and Escape write it back. |
focused |
string (two-way) |
"" |
The node that holds the tab stop. Focus writes it; write a node id to move the tab stop there, and focus too while the tree has it. |
orientation |
"vertical" | "horizontal" |
"vertical" |
horizontal: aria-orientation=horizontal, top-level nodes side by side; Right and Left move between nodes, Down opens, Up closes. |
onFocusChange |
(id: string) => void |
() => {} |
A node took focus. Not called for the caller's writes. |
onRename |
(node: TreeNode, label: string) => boolean | void |
() => {} |
A rename was saved; update items with the new label. Return false to refuse it: Enter keeps the input open, leaving it discards. |
onCheckedChange |
(ids: string[]) => void |
() => {} |
Called when the user checks or clears nodes. |
onLoadChildrenComplete |
(node: TreeNode, children: TreeNode[]) => void |
() => {} |
Called when loadChildren resolves. |
loadChildren |
(node: TreeNode) => TreeNode[] | Promise<TreeNode[]> |
() => [] |
Loads the children of a node with hasChildren the first time it opens, by the user or through expanded. |
onSelectedChange |
(ids: string[]) => void |
() => {} |
Called when the user changes the selection. |
onExpandedChange |
(ids: string[]) => void |
() => {} |
Called when the user opens or closes a node. |
onAction |
(node: TreeNode) => void |
() => {} |
Called on Enter: open the file, follow the link. |
onLoadChildrenError |
(node: TreeNode, error: unknown) => void |
() => {} |
Called when loadChildren fails. The node closes and the next open tries again. |
ids |
{ root?: string; node?: (node: TreeNode) => string } |
{} |
The tree's id and each node's, for a label or a link elsewhere on the page. |
virtual |
boolean |
false |
Renders only the rows in and near the view, so ten thousand nodes render, scroll and take keys as fast as ten. The tree scrolls itself: give it a height in CSS. |
estimateSize |
number |
32 |
virtual: a row's height in px until rows are measured. |
overscan |
number |
8 |
virtual: rows rendered past each edge of the view. |
activeDescendant |
boolean |
false |
Focus stays on the tree and aria-activedescendant names the node, APG's alternative to moving focus. The keys are the same. |
class |
string |
"" |
Extra classes on the tree. |
Keyboard
| Keys | Action |
|---|---|
ArrowDown / ArrowUp |
Next or previous visible node. |
ArrowRight |
Opens a closed parent; on an open parent, moves to its first child. |
ArrowLeft |
Closes an open parent; on a child, moves to its parent. |
Home / End |
First or last visible node. |
Enter |
Opens or closes a parent; selects a leaf (single); calls onAction. |
Space |
Selects (single) or toggles (multiple) the focused node. |
Shift + Space |
Selects from the last selected node to this one (multiple). |
Shift + ArrowDown / ArrowUp |
Moves and toggles the node reached (multiple). |
Ctrl + Shift + Home / End |
Selects from here to the first or last node (multiple). |
Ctrl + A |
Selects every visible node; again to unselect (multiple). |
* |
Opens every sibling of the focused node. |
F2 |
Renames the focused node (canRename); Enter saves, Escape cancels. |
Ctrl + ArrowDown / ArrowUp |
Moves without selecting; Ctrl + Space toggles the focused node (multiple). |
a-z |
Focuses the next node whose label starts with the typed text. |
Data attributes
| Attribute | Values | On | Description |
|---|---|---|---|
data-state |
"open" | "closed" |
tree-item (parents) | |
data-selected |
present when selected | tree-item | |
data-disabled |
present when disabled | tree-item | |
data-loading |
present while children load | tree-item | |
data-checked / data-indeterminate |
checkbox tree: all or some leaves checked | tree-item | |
data-renaming |
present while renaming | tree-item | |
data-depth, data-value, data-path |
the level, the id, the index path ("0/2") | tree-item | |
data-focus |
present on the focused node | tree-item | |
data-scope, data-part |
tree-view; tree, branch, item, branch-control, branch-indicator, branch-text, item-text, branch-content, node-checkbox | every part | |
data-branch |
the branch's id | tree-group | |
--tree-level |
1, 2, ... | tree-row | Depth, for indentation |
data-index, --tree-start |
virtual: the row's place among the shown nodes, and its offset in px | tree-item | |
--tree-total |
virtual: the height of every shown row, in px | tree |
Accessibility
- Follows the WAI-ARIA tree view pattern: role="tree" (aria-multiselectable in multiple mode), role="treeitem" nodes, role="group" children.
- Parents carry aria-expanded and leaves do not; selectable nodes carry aria-selected true or false.
- Every node has aria-level, aria-setsize and aria-posinset, since closed branches are not in the page.
- One tab stop: entering the tree focuses the selected node, or the first. Disabled nodes take focus but cannot be chosen.
- A node loading its children is aria-busy.
- virtual: the rendered treeitems are flat children of the tree, and aria-level, aria-setsize and aria-posinset give each one its place in the whole tree. The focused node stays rendered while the view scrolls, so focus and the tab stop never fall out.
See also
- Accordion (opens in a new tab): A stack of headings that each show and hide a panel, one or several at a time.
- Collapsible (opens in a new tab): A trigger that shows and hides a region, with its height animated.
- Tabs (opens in a new tab): Layered panels, one shown at a time, chosen from a list of tabs.
- Breadcrumb (opens in a new tab): The path to the current page as a trail of links, composed from parts or built from a list.
- Pagination (opens in a new tab): Page numbers with previous and next, from a page count or composed from parts.