TreeNav
Hierarchical navigation with stable node identities, expansion and keyboard semantics.
Use the web element directly without the semantic runtime.
A registered presentation adapter can bind this component to host-owned meaning and state.
The standard recipe resolver does not select this component automatically.
Import and live example#
A two-level report tree with the reports branch expanded and weekly selected.
Interactive preview requires JavaScript.
View the complete example source
import {registerAeliqoElements} from "@varendo/web";
import {
AeliqoBreadcrumbElement,
AeliqoMenuElement,
AeliqoPaginationElement,
AeliqoTabsElement,
AeliqoTreeNavElement,
type AeliqoBreadcrumbItem,
type AeliqoMenuItem,
type AeliqoTabItem,
type AeliqoTreeNavNode,
} from "@varendo/web/navigation";
const tabs: readonly AeliqoTabItem[] = [
{id: "overview", label: "Overview", content: "Overview content"},
{id: "details", label: "Details", content: "Details content"},
{id: "history", label: "History", content: "History content", disabled: true},
];
const breadcrumb: readonly AeliqoBreadcrumbItem[] = [
{id: "home", label: "Home", href: "/"},
{id: "reports", label: "Reports", href: "/reports"},
{id: "current", label: "Weekly report", current: true},
];
const menuItems: readonly AeliqoMenuItem[] = [
{id: "open", label: "Open report"},
{id: "archive", label: "Archive"},
{id: "disabled", label: "Unavailable", disabled: true},
];
const treeNodes: readonly AeliqoTreeNavNode[] = [
{id: "reports", label: "Reports", children: [{id: "weekly", label: "Weekly"}, {id: "monthly", label: "Monthly"}]},
{id: "settings", label: "Settings", disabled: true},
];
registerAeliqoElements();
const host = document.querySelector<HTMLElement>("#aeliqo-example") ?? (() => {
const created = document.createElement("div");
created.id = "aeliqo-example";
(document.body ?? document.documentElement).append(created);
return created;
})();
function createCatalogRoot(container: HTMLElement): HTMLElement {
const root = document.createElement("div");
root.dataset.catalogSourceRoot = "true";
root.style.display = "grid";
root.style.gap = "12px";
container.append(root);
return root;
}
function createCatalogElement<T extends HTMLElement>(tagName: string, container: HTMLElement): T {
const element = document.createElement(tagName) as T;
element.setAttribute("data-aeliqo-theme", "light");
container.append(element);
return element;
}
function appendSlottedText(root: HTMLElement, slot: string, textContent: string): void {
const content = document.createElement("span");
content.slot = slot;
content.textContent = textContent;
root.append(content);
}
const root = createCatalogRoot(host);
(((root) => {
const element = createCatalogElement<AeliqoTreeNavElement>("aeliqo-tree-nav", root);
element.nodes = treeNodes;
element.expandedIds = ["reports"];
element.selectedId = "weekly";
element.label = "Report navigation";
}))(root);
Purpose#
A bounded tree of navigation nodes with stable IDs and tree keyboard semantics. It tracks expansion locally and emits selection or expansion requests.
When to use it#
- Navigating sections nested more than one level deep.
- Trees users expand, collapse, and select by keyboard.
- Hierarchies keyed by stable, unique node IDs.
When to use a different component#
- Use Breadcrumb for a short path to one current location.
- Use Menu for a flat list of commands, not a hierarchy.
Properties and defaults#
Node IDs are stable. After an uncancelled event, the component updates its selected ID or local expanded set; the host can supply selectedId and expandedIds to synchronize external state.
| Property | Use and ownership | Declared type | Initial value |
|---|---|---|---|
nodes | Supplies the navigation hierarchy with stable node IDs and bounded children. | readonly AeliqoTreeNavNode[] | [] |
expandedIds | Supplies the bounded ordered expanded ids collection used by this component. | readonly string[] | [] |
selectedId | Controls the currently selected navigation node by stable ID. | string | '' |
label | Provides the visible label and accessible name for the component. | string | 'Navigation tree' |
Events#
| Event | Payload | Emitted | Application responsibility |
|---|---|---|---|
aeliqo-tree-nav-select | The selected enabled node ID. | After keyboard or pointer selection. | Route or update controlled selectedId. |
aeliqo-tree-nav-expand | The node ID and proposed expanded state. | After a branch toggle. | Update controlled expandedIds while preserving valid selection. |
Events cross the component boundary as user proposals. Validate their detail and current authority before applying a business effect.
Listen for aeliqo-tree-nav-select
const element = document.querySelector('aeliqo-tree-nav');<p>element?.addEventListener('aeliqo-tree-nav-select', (event) => {
const proposal = (event as CustomEvent<unknown>).detail;
// Validate the proposal, update host state, then assign controlled properties.
applyProposal(proposal);
});Listen for aeliqo-tree-nav-expand
const element = document.querySelector('aeliqo-tree-nav');</p>
<p>element?.addEventListener('aeliqo-tree-nav-expand', (event) => {
const proposal = (event as CustomEvent<unknown>).detail;
// Validate the proposal, update host state, then assign controlled properties.
applyProposal(proposal);
});States and failure handling#
Relevant states:
- ready
- disabled
Expected result: The tree preserves hierarchy and emits typed selection or expansion requests by node ID.
Node IDs must be unique, nonempty, and at most 160 characters. Cycles,
invalid shapes, duplicate IDs, or more than 512 nodes render a status
message instead of the tree. expandedIds sets the expansion set; after an
uncancelled expand event, the element also updates it locally. An
uncancelled selection event updates selectedId. Watch those events to
keep routing aligned, and pass new props to reconcile external navigation
changes.
Keyboard, focus, and accessibility#
Keyboard behavior:
- Tab
- Arrow keys
- Home and End
- Enter
- Space
Exposed semantics:
- aria-disabled
- aria-expanded
- aria-hidden
- aria-label
- aria-level
- aria-selected
- button
- li
- nav
- role
- ul
Responsive behavior#
Allow long node labels to wrap inside the tree. Keep the selected item and expansion state visible after the host narrows the panel.
Style hooks#
- Shadow parts:
item,toggle,tree - Varendo design tokens referenced:
--aeliqo-color-accent,--aeliqo-control-min-target,--aeliqo-radius-small,--aeliqo-space-4,--aeliqo-space-8,--aeliqo-typography-font-weight-semibold
Performance limits#
- The source-defined `MAX_TREE_NODES` limit is 512.
Related components#
Generated TypeScript declaration#
View generated declaration
export declare class AeliqoTreeNavElement extends AeliqoFoundationElement {
static readonly properties: {
nodes: {
attribute: boolean;
};
expandedIds: {
attribute: boolean;
};
selectedId: {
type: StringConstructor;
};
label: {
type: StringConstructor;
};
};
static readonly styles: import("lit").CSSResult[];
nodes: readonly AeliqoTreeNavNode[];
expandedIds: readonly string[];
selectedId: string;
label: string;
private internalExpanded;
private focusedId;
private treeError;
protected willUpdate(changed: Map<string, unknown>): void;
private isExpanded;
private validateNodes;
private flatVisible;
private select;
private toggle;
private toggleFromPointer;
private focusNode;
private keydown;
private handleArrowRight;
private handleArrowLeft;
private focusVisibleIndex;
private selectFromKeyboard;
private focusTreeNode;
private expandedAttribute;
private renderNode;
protected render(): import("lit").TemplateResult<1>;
}Version#
This page documents the Varendo 0.6 component contract.
