api reference
Tree API Reference
createTree: a standalone tree over a real grid, with expand and collapse, selection, tri-state checkboxes and drag to reparent.
API reference › The tree module
All 29 pages Everything on one page → Developer guide →
The tree module
modules/tree is an opt-in, standalone tree - expand, select, tri-state
checkboxes and drag to reparent - built on the grid's own tree mode (see
Tree data) rather than a hierarchy of its own:
createTree configures one generated column, no header, no grid lines, its single
visible column drawing the expander, an optional per-node icon, an optional checkbox and the
label. Expansion, selection and drag-to-reparent are the grid's; this module's own surface is
the data shape (flat parentKey rows or nested items), the tri-state
checkbox rollup, and the thin tree.on event vocabulary a standalone tree is
expected to speak.
import { createTree } from '@toclocoinc/lattice-grid/modules/tree';
const tree = createTree(document.querySelector('#tree'), {
data: [
{ id: 1, label: 'Documents' },
{ id: 2, parentKey: 1, label: 'Report.docx' },
],
checkboxes: true,
selection: 'single',
dragMode: 'both',
open: [1],
});
Two data shapes, the grid's own. data is flat rows each naming
its parent by parentKey (default field name 'parentKey', default id
field 'id'); items is the same tree nested under a
childrenField array (default 'children') and is flattened once, on
construction. A node's label comes from labelField (default 'label')
or a template(node) function, which wins when given.
Checkboxes are tri-state and roll up. With checkboxes: true,
checking or unchecking a node checks or unchecks every node under it - the usual
tree-checkbox convention - and every ancestor's own box repaints to
'checked' when every descendant leaf is, 'unchecked' when none are,
and 'mixed' (drawn indeterminate) otherwise. getChecked()
lists the ids whose rolled-up state is fully checked; a mixed branch is never among them.
Drag to reparent is two already-shipped grid primitives, not a third drag
implementation. dragMode ('source', 'target' or
'both'; false, the default, leaves the handle undrawn) turns on the
grid's own rowReorder over a field-form tree.parentKey, which drops on
the upper or lower quarter of a node as a new sibling before or after it, and on the middle half
as that node's new last child - see Drag to
reparent for the mechanism itself, shared with a plain grid's tree. A node cannot be dropped
into one of its own descendants; the drop is refused outright, before any data changes.
Lazy children. hasChildren(node) draws an expander for a branch
not yet in the data, and loadChildren(node, signal) fetches it the first time it is
opened - fetched once, aborted through signal if closed before it resolves,
the same contract as the grid's own tree.loadChildren.
| Member | Description |
|---|---|
| createTree(el, config) | Create the tree. |
| data.add(item, parentId, index) / data.remove(id) | Add a node under a parent (root when omitted) at a display index (default the end); remove a node and its whole subtree. |
| data.update(id, changes) / data.getItem(id) | Shallow-merge fields into a node, leaving its place in the hierarchy alone; read a node's own data. |
| data.getParentId(id) / data.getChildren(id?) | A node's parent id (null for a root); a node's direct children's ids (the roots when omitted). |
| data.serialize(id?) | The whole tree, or one subtree, nested under childrenField. |
| expand(id) / collapse(id) / toggle(id) / expandAll() / collapseAll() | Open or close one node, flip it, or open or close every node. |
| select(id) / getSelected() | Select a node (adds to the selection under 'multi'); the selected ids, always an array. |
| check(id) / uncheck(id) / getChecked() | Check or uncheck a node and everything under it; the ids whose rolled-up state is fully checked. |
| focusItem(id) | Move keyboard focus to a node. |
| on(name, fn) | Events: itemClick, itemDoubleClick, expand, collapse, selectionChange, check, the cancellable beforeDrop, afterDrop. Every one carries the node id. |
| grid | The grid the tree is built on, for anything this API does not cover. |
| destroy() | Destroy the inner grid and release every handler. The host still owns the element. |
The data API, selection and the checkbox rollup, executed
A four-node tree, a collapse/re-expand, a selection, a checkbox rollup
through a mixed state to fully checked, an added node read back through every data method, and a
removal that leaves the hierarchy consistent - run headless on every build with the suite's
own DOM test double, since this module always needs a real element and createGrid
behind it.
const { createTestDom } = await import('../packages/dom/src/renderer/testdom.js');
const { createGrid } = await import('../packages/dom/src/index.js');
const { createTree } = await import('../packages/modules/tree/index.js');
const seen = [];
const host = createTestDom().root;
const tree = createTree(host, {
data: [
{ id: '1', label: 'Documents' },
{ id: '2', parentKey: '1', label: 'Report.docx' },
{ id: '3', parentKey: '1', label: 'Budget.xlsx' },
{ id: '4', label: 'Photos' },
],
checkboxes: true,
selection: 'single',
open: ['1'],
});
tree.on('expand', (e) => seen.push(`expand:${e.id}`));
tree.on('collapse', (e) => seen.push(`collapse:${e.id}`));
tree.on('selectionChange', (e) => seen.push(`sel:${e.ids.join(',')}`));
tree.on('check', (e) => seen.push(`check:${e.id}:${e.checked}`));
tree.collapse('1');
tree.expand('1');
tree.select('2');
const selected = tree.getSelected().join(',');
tree.check('2');
const mixedCheck = tree.getChecked().join(','); // '2' -- '1' is mixed, '3' unchecked
tree.check('3');
const rolledUp = tree.getChecked().join(','); // '1,2,3' -- both children checked rolls '1' up
tree.data.add({ id: '5', label: 'Appendix.docx' }, '2');
const childrenOf2 = tree.data.getChildren('2').join(',');
tree.data.update('5', { label: 'Appendix (final).docx' });
const updatedLabel = tree.data.getItem('5').label;
const parentOf5 = tree.data.getParentId('5');
tree.data.remove('3');
const rootsNow = tree.data.getChildren().join(',');
const nested = tree.data.serialize('1').map((n) => n.id).join(',');
tree.destroy();
return [selected, mixedCheck, rolledUp, childrenOf2, updatedLabel, parentOf5, rootsNow, nested, seen.join('|')].join(' || ');
Nested items, a label template, a per-node icon and initial checks, executed
The other data shape: items nested under childrenField,
flattened once on construction. template wins over labelField when both
are given; icon and dragMode are per-node and drag config a headless run
cannot show on screen, so this only proves they are accepted and read.
const { createTestDom } = await import('../packages/dom/src/renderer/testdom.js');
const { createGrid } = await import('../packages/dom/src/index.js');
const { createTree } = await import('../packages/modules/tree/index.js');
const host = createTestDom().root;
const tree = createTree(host, {
items: [
{ id: 'a', name: 'Root', children: [{ id: 'b', name: 'Child' }] },
],
childrenField: 'children',
labelField: 'name', // loses to template, below, when both are given
template: (node) => `#${node.id}`,
icon: (node) => (node.id === 'a' ? 'folder' : 'file'),
checked: ['b'], // rolls 'a' up to fully checked too
dragMode: 'source',
});
const nestedBack = tree.data.serialize().map((n) => n.name).join(','); // Root
const childChecked = tree.getChecked().join(','); // a,b
tree.destroy();
return `${nestedBack} | ${childChecked}`;
Type reference
Generated from the type declarations, so it always matches the release. Each surface lists its properties, its methods and the events it raises as three tables; an option or value type lists its members once.
The tree module
TreeModuleConfig
The configuration for {@link createTree}. Built on the grid's own tree mode (one generated column, no header, no grid lines) rather than a hierarchy of its own - `data`/`items`, `key`, `parentKey` and `childrenField` describe the same two row shapes `GridConfig.tree` reads from a plain grid.
| Property | Type | Description |
|---|---|---|
| data | TreeNode[] | Flat rows, each naming its parent by `parentKey` (or none, for a root). (optional) |
| items | TreeNode[] | Nested nodes, each with a `childrenField` array, instead of `data`. Takes priority over `data` when both are given. (optional) |
| key | string | The field holding a node's own identity. Default `'id'`. (optional) |
| parentKey | string | The field a flat row names its parent in. Default `'parentKey'`. (optional) |
| childrenField | string | The array property nesting a node's children, for `items`. Default `'children'`. (optional) |
| labelField | string | The field a node's label comes from, when `template` is not given. Default `'label'`. (optional) |
| iconClass | string | ((node: TreeNode) => string | null | undefined) | An icon-font class (or classes) per node, drawn as an `<i>` in the icon slot alongside the registered-SVG icon from `icon`. A string applies to every node; a function reads it per node. (optional) |
| open | Array<string | number> | The ids open at start. Default none - every other branch starts closed, however many children it already has. (optional) |
| selection | false | 'single' | 'multi' | Selection mode. Default `false`. (optional) |
| checkboxes | boolean | Draw a tri-state checkbox per node, checked/unchecked/mixed rolled up from its children. Default `false`. (optional) |
| checked | Array<string | number> | The ids checked at start. Default none. (optional) |
| dragMode | false | 'source' | 'target' | 'both' | This tree's role in a drag: reorder and reparent within itself either way; the split is the host's own declaration of intent, since a standalone tree has nothing else to send to or receive from. Default `false`. (optional) |
| contextMenu | boolean | The grid's own cell right-click menu (Paste, Clear, Fill down). Default `false` - a right-click on a node is `itemContextMenu`, not a menu meant for spreadsheet cells. (optional) |
| Method | Signature | Parameters | Returns | Description |
|---|---|---|---|---|
| template | (node: TreeNode) => string | HTMLElement | node: TreeNode | => string | HTMLElement | Draw a node's label yourself; wins over `labelField`. A string is HTML sanitised by the shared sanitiser (`modules/shared/sanitise-html.js`): script elements, `on*` attributes and `javascript:` URLs removed, everything else - including an icon-font element's classes - kept. An element is mounted as is. (optional) |
| icon | (node: TreeNode) => string | null | undefined | node: TreeNode | => string | null | undefined | An icon name per node, or none. (optional) |
| hasChildren | (node: TreeNode) => boolean | node: TreeNode | => boolean | Say that a node has children not yet in `data`/`items`, so it draws an expander before anything is fetched. (optional) |
| loadChildren | (node: TreeNode, signal: AbortSignal) => Promise<TreeNode[]> | node: TreeNodesignal: AbortSignal | => Promise<TreeNode[]> | Fetch a branch's children when it is first opened. Fetched once; a rejection leaves the branch unloaded. (optional) |
TreeDataApi
`tree.data`: add, remove, change and read nodes, and read the hierarchy back out.
| Method | Signature | Parameters | Returns | Description |
|---|---|---|---|---|
| add | (item: TreeNode, parentId?: unknown, index?: number): void | item: TreeNodeparentId?: unknownindex?: number | void | Add a node. `parentId` omitted (or null) makes it a root; `index` omitted appends among its new siblings. |
| remove | (id: unknown): void | id: unknown | void | Remove a node and its whole subtree. |
| update | (id: unknown, changes: Partial<TreeNode>): void | id: unknownchanges: Partial<TreeNode> | void | Shallow-merge `changes` into a node's own fields, leaving its place in the hierarchy alone. |
| getItem | (id: unknown): TreeNode | null | id: unknown | TreeNode | null | A node's own data, or null when not found. |
| getParentId | (id: unknown): string | null | id: unknown | string | null | A node's parent id, or null for a root. |
| getChildren | (id?: unknown): string[] | id?: unknown | string[] | A node's direct children's ids; the roots when `id` is omitted. |
| serialize | (id?: unknown): TreeNode[] | id?: unknown | TreeNode[] | The whole tree (or, with `id`, one subtree), nested under `childrenField`. |
TreeItemEvent
The payload `itemClick`, `itemDoubleClick`, `expand` and `collapse` all carry.
| Property | Type | Description |
|---|---|---|
| id | string | The node's id. |
TreeCheckEvent
The `check` payload: a node's own checkbox was explicitly set, independently of what its rolled-up state then reads as.
| Property | Type | Description |
|---|---|---|
| id | string | The node's id. |
| checked | boolean | The state it was set to. |
TreeSelectionChangeEvent
The `selectionChange` payload.
| Property | Type | Description |
|---|---|---|
| ids | string[] | The selected ids. Always an array, regardless of `TreeModuleConfig.selection` mode. |
TreeBeforeDropEvent
The cancellable `beforeDrop` payload: a node is about to be dropped before, after or inside another. Call `preventDefault(reason?)` (or return `false`) to veto, leaving the node exactly where it was.
| Property | Type | Description |
|---|---|---|
| id | string | The id of the node being dropped. |
| parent | unknown | The parent id the node will have after the drop. |
| index | number | The index the node will land at. |
| position | 'before' | 'after' | 'inside' | Which band of the target node the drop landed in. |
| target | string | The id of the node the drag was dropped onto. |
| Method | Signature | Parameters | Returns | Description |
|---|---|---|---|---|
| preventDefault | (reason?: string): void | reason?: string | void | Veto the drop, with an optional reason. |
TreeAfterDropEvent
The `afterDrop` payload: a drop that was not vetoed has committed.
| Property | Type | Description |
|---|---|---|
| id | string | The id of the node that was dropped. |
| parent | unknown | The parent id the node now has. |
| index | number | The index the node now occupies. |
| position | 'before' | 'after' | 'inside' | Which band of the target node the drop landed in. |
| target | string | The id of the node the drag was dropped onto. |
TreeItemContextMenuEvent
The `itemContextMenu` payload: a right-click on a node (and the context-menu key on a focused one).
| Property | Type | Description |
|---|---|---|
| id | string | The node's id. |
| node | TreeNode | null | The node's own data, or null. |
| event | Event | undefined | The originating DOM event, when the right-click came from the pointer (absent from the keyboard route). Call `preventDefault()` on it to suppress the browser's own menu; left alone otherwise. |
TreeEventPayloads
The payload each tree event name carries.
| Property | Type | Description |
|---|---|---|
| itemClick | TreeItemEvent | A node was clicked. |
| itemDoubleClick | TreeItemEvent | A node was double-clicked. |
| expand | TreeItemEvent | A node was opened. |
| collapse | TreeItemEvent | A node was closed. |
| selectionChange | TreeSelectionChangeEvent | The selection changed. |
| check | TreeCheckEvent | A checkbox was explicitly checked or unchecked. |
| beforeDrop | TreeBeforeDropEvent | A drop is about to land; veto it with `preventDefault(reason?)`. |
| afterDrop | TreeAfterDropEvent | A drop committed. |
| itemContextMenu | TreeItemContextMenuEvent | A node was right-clicked, or its context-menu key pressed while focused. |
Tree
The tree instance {@link createTree} returns.
Properties
| Property | Type | Description |
|---|---|---|
| grid | unknown | The grid the tree is built on, for anything this API does not cover. |
| data | TreeDataApi | The Data API. |
Methods
| Method | Signature | Parameters | Returns | Description |
|---|---|---|---|---|
| expand | (id: unknown): void | id: unknown | void | Open a node. |
| collapse | (id: unknown): void | id: unknown | void | Close a node. |
| expandAll | (): void | - | void | Open every node. |
| collapseAll | (): void | - | void | Close every node. |
| toggle | (id: unknown): void | id: unknown | void | Open a closed node, or close an open one. |
| select | (id: unknown): void | id: unknown | void | Select a node (or, under `selection: 'multi'`, add it to the selection). |
| getSelected | (): string[] | - | string[] | The selected ids. |
| check | (id: unknown): void | id: unknown | void | Check a node, and by the usual tree-checkbox convention every node under it. |
| uncheck | (id: unknown): void | id: unknown | void | Uncheck a node and every node under it. |
| getChecked | (): string[] | - | string[] | The ids whose rolled-up state is fully checked - never a mixed branch. |
| focusItem | (id: unknown): void | id: unknown | void | Move keyboard focus to a node. |
| on | <K extends TreeEventName>(name: K, fn: (payload: TreeEventPayloads[K]) => void): () => void | name: Kfn: (payload: TreeEventPayloads[K]) => void | () => void | Subscribe to a tree event; returns an unsubscribe function. |
| destroy | (): void | - | void | Tear the tree down: destroy the inner grid, release every listener. The host still owns the element. |
Events
This surface raises events, but the names are not declared: on() accepts any string. They cannot be listed here until the declaration names them.