# TreePlugin

> _Since v0.1.1_

Tree Data Plugin for tbw-grid

Transforms your flat grid into a hierarchical tree view with expandable parent-child
relationships. Ideal for file explorers, organizational charts, nested categories,
or any data with a natural hierarchy.

## Installation

```ts
import { TreePlugin } from '@toolbox-web/grid/plugins/tree';
```

## CSS Custom Properties

| Property | Default | Description |
|----------|---------|-------------|
| `--tbw-tree-toggle-size` | `1.25em` | Toggle icon width |
| `--tbw-tree-indent-width` | `var(--tbw-tree-toggle-size)` | Indentation per level |
| `--tbw-tree-accent` | `var(--tbw-color-accent)` | Toggle icon hover color |
| `--tbw-animation-duration` | `200ms` | Expand/collapse animation duration |
| `--tbw-animation-easing` | `ease-out` | Animation curve |

## [Configuration Options](/grid/plugins/tree/interfaces/treeconfig.md)

| Option | Type | Description |
| ------ | ---- | ----------- |
| `childrenField?` | <code>string</code> | Field name containing child rows (default: 'children') |
| `autoDetect?` | <code>boolean</code> | Auto-detect tree structure from data (default: true) |
| `defaultExpanded?` | <code>boolean</code> | Whether nodes are expanded by default (default: false) |
| `indentWidth?` | <code>number</code> | Indentation width per level in pixels (default: 20) |
| `showExpandIcons?` | <code>boolean</code> | Show expand/collapse icons (default: true) |
| `treeColumn?` | <code>string</code> | Field name of the column that displays the tree toggle and indentation. Defaults to the first visible column. Use this when the first column is narrow (e.g. an ID column) or when combining with pinned columns. |
| `animation?` | <code><a href="/grid/api/core/types/expandcollapseanimation/">ExpandCollapseAnimation</a></code> | Animation style for expanding/collapsing tree nodes. - `false`: No animation - `'slide'`: Slide animation (default) - `'fade'`: Fade animation |

## Examples

### Basic Tree with Nested Children

```ts
import { queryGrid } from '@toolbox-web/grid';
import { TreePlugin } from '@toolbox-web/grid/plugins/tree';

const grid = queryGrid('tbw-grid');
grid.gridConfig = {
  columns: [
    { field: 'name', header: 'Name' },
    { field: 'type', header: 'Type' },
    { field: 'size', header: 'Size' },
  ],
  plugins: [new TreePlugin({ childrenField: 'children', indentWidth: 24 })],
};
grid.rows = [
  {
    id: 1,
    name: 'Documents',
    type: 'folder',
    children: [
      { id: 2, name: 'Report.docx', type: 'file', size: '24 KB' },
    ],
  },
];
```

### Expanded by Default with Custom Animation

```ts
new TreePlugin({
  defaultExpanded: true,
  animation: 'fade', // 'slide' | 'fade' | false
  indentWidth: 32,
})
```

## See Also

- [`TreeConfig`](/grid/plugins/tree/interfaces/treeconfig.md) for all configuration options
- [`FlattenedTreeRow`](/grid/plugins/tree/interfaces/flattenedtreerow.md) for the flattened row structure

> **Extends** [BaseGridPlugin](/docs/grid-api-plugin-development-classes-basegridplugin--docs)
>
> Inherited methods like `attach()`, `detach()`, `afterRender()`, etc. are documented in the base class.

## Properties

| Property | Type | Description |
| -------- | ---- | ----------- |
| `manifest` | <code><a href="/grid/api/plugin-development/interfaces/pluginmanifest/">PluginManifest</a></code> | Plugin manifest - declares owned properties, config rules, and hook priorities. |
| `dependencies` | <code>object[]</code> | Optional dependency on MultiSort for coordinated sorting. When MultiSort is loaded, Tree defers header click sorting to it and queries the sort model in processRows. When MultiSort is absent, Tree uses its own sort state. |

### Property Details

#### manifest

Plugin manifest - declares owned properties, config rules, and hook priorities.

This is read by the configuration validator to:
- Validate that required plugins are loaded when their properties are used
- Execute configRules to detect invalid/conflicting settings
- Order hook execution based on priority

```typescript
static override readonly manifest: PluginManifest<MyConfig> = {
  ownedProperties: [
    { property: 'myProp', level: 'column', description: 'the "myProp" column property' },
  ],
  configRules: [
    { id: 'myPlugin/conflict', severity: 'warn', message: '...', check: (c) => c.a && c.b },
  ],
};
```

---

## Methods

### detect()

```ts
detect(rows: readonly unknown[]): boolean
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `rows` | <code>readonly unknown[]</code> |  |

***

### expand()

Expand a specific tree node, revealing its children.

If the node is already expanded, this is a no-op.
Does **not** emit a `tree-expand` event (use toggle for event emission).

```ts
expand(key: string): void
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `key` | <code>string</code> | The unique key of the node to expand (from FlattenedTreeRow.key) |

#### Example

```ts
const tree = grid.getPluginByName('tree');
tree.expand('documents');          // Expand a root node
tree.expand('documents||reports');  // Expand a nested node
```

***

### collapse()

Collapse a specific tree node, hiding its children.

If the node is already collapsed, this is a no-op.
Does **not** emit a `tree-expand` event (use toggle for event emission).

```ts
collapse(key: string): void
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `key` | <code>string</code> | The unique key of the node to collapse (from FlattenedTreeRow.key) |

#### Example

```ts
const tree = grid.getPluginByName('tree');
tree.collapse('documents');
```

***

### toggle()

Toggle the expanded state of a tree node.

If the node is expanded it will be collapsed, and vice versa.
Emits a `tree-expand` event (broadcast to both DOM consumers and plugin bus).

```ts
toggle(key: string): void
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `key` | <code>string</code> | The unique key of the node to toggle (from FlattenedTreeRow.key) |

#### Example

```ts
const tree = grid.getPluginByName('tree');
tree.toggle('documents');  // Expand if collapsed, collapse if expanded
```

***

### expandAll()

Expand all tree nodes recursively.

Every node with children will be expanded, revealing the full tree hierarchy.
Emits a `tree-expand` plugin event.

```ts
expandAll(): void
```

#### Example

```ts
const tree = grid.getPluginByName('tree');
tree.expandAll();
```

***

### collapseAll()

Collapse all tree nodes.

Every node will be collapsed, showing only root-level rows.
Emits a `tree-expand` plugin event.

```ts
collapseAll(): void
```

#### Example

```ts
const tree = grid.getPluginByName('tree');
tree.collapseAll();
```

***

### isExpanded()

Check whether a specific tree node is currently expanded.

```ts
isExpanded(key: string): boolean
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `key` | <code>string</code> | The unique key of the node to check |

#### Returns

`boolean` - `true` if the node is expanded, `false` otherwise

***

### getExpandedKeys()

Get the keys of all currently expanded nodes.

Returns a snapshot copy — mutating the returned array does not affect the tree state.

```ts
getExpandedKeys(): string[]
```

#### Returns

`string[]` - Array of expanded node keys

#### Example

```ts
const tree = grid.getPluginByName('tree');
const keys = tree.getExpandedKeys();
localStorage.setItem('treeState', JSON.stringify(keys));
```

***

### getFlattenedRows()

Get the flattened row model used for rendering.

Returns a snapshot copy of the internal flattened tree rows, including
hierarchy metadata (depth, hasChildren, isExpanded, parentKey).

```ts
getFlattenedRows(): FlattenedTreeRow<TreeRow>[]
```

#### Returns

`FlattenedTreeRow<TreeRow>[]` - Array of FlattenedTreeRow objects

***

### getRowMeta()

Get tree metadata (depth, key, hasChildren, isExpanded, parentKey) for a
specific row reference. Returns `undefined` if the row is not part of the
currently-flattened tree (e.g. collapsed under a parent or never processed).

Tree metadata lives in a parallel WeakMap keyed by row identity \\u2014 it is
NOT stored on the row object itself. This preserves the invariant that
`_rows[i]` IS the user's source row reference, so `grid.updateRow(s)`
mutations survive the next ROWS-phase rebuild.

```ts
getRowMeta(row: TreeRow): FlattenedTreeRow<TreeRow> | undefined
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `row` | <code><a href="/grid/plugins/tree/types/treerow/">TreeRow</a></code> |  |

#### Example

```ts
const tree = grid.getPluginByName('tree');
const meta = tree.getRowMeta(grid.rows[0]);
console.log(meta?.depth, meta?.hasChildren);
```

***

### getRowByKey()

Look up an original row data object by its tree key.

```ts
getRowByKey(key: string): TreeRow | undefined
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `key` | <code>string</code> | The unique key of the node |

#### Returns

`TreeRow | undefined` - The original row data, or `undefined` if not found

***

### expandToKey()

Expand all ancestor nodes of the target key, revealing it in the tree.

Useful for "scroll to node" or search-and-reveal scenarios where a deeply
nested node needs to be made visible.

```ts
expandToKey(key: string): void
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `key` | <code>string</code> | The unique key of the node to reveal |

#### Example

```ts
const tree = grid.getPluginByName('tree');
// Reveal a deeply nested node by expanding all its parents
tree.expandToKey('root||child||grandchild');
```

***
