Tree

elements man ui/templates/tree

A hierarchical list that opens with the arrow keys, selects one node or many, and loads children on demand.

Example
import { 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