# TreeView Component

## Introduction

The TreeView component provides an interactive hierarchical data visualization with full CRUD operations. It's designed for displaying tree-structured data like file systems, organizational charts, or navigation menus with inline editing capabilities.

**Key features:**

- Expand/collapse nodes with visual indicators
- Add child and sibling nodes dynamically
- Inline rename with keyboard support (ESC to cancel)
- Delete nodes (only leaf nodes without children)
- Node selection tracking
- Persistent state per session
- Configurable icons per node type

**Common use cases:**

- File/folder browser
- Category/subcategory management
- Organizational hierarchy viewer
- Navigation menu builder
- Document outline editor

## Quick Start

Here's a minimal example showing a file system tree:

```python
from fasthtml.common import *
from myfasthtml.controls.TreeView import TreeView, TreeNode

# Create TreeView instance
tree = TreeView(parent=root_instance, _id="file-tree")

# Add root folder
root = TreeNode(id="root", label="Documents", type="folder")
tree.add_node(root)

# Add some files
file1 = TreeNode(id="file1", label="report.pdf", type="file")
file2 = TreeNode(id="file2", label="budget.xlsx", type="file")
tree.add_node(file1, parent_id="root")
tree.add_node(file2, parent_id="root")

# Expand root to show children
tree.expand_all()

# Render the tree
return tree
```

This creates an interactive tree where users can:
- Click chevrons to expand/collapse folders
- Click labels to select items
- Use action buttons (visible on hover) to add, rename, or delete nodes

**Note:** All interactions use commands and update via HTMX without page reload.

## Basic Usage

### Creating a TreeView

TreeView is a `MultipleInstance`, allowing multiple trees per session. Create it with a parent instance:

```python
tree = TreeView(parent=root_instance, _id="my-tree")
```

### TreeNode Structure

Nodes are represented by the `TreeNode` dataclass:

```python
from myfasthtml.controls.TreeView import TreeNode

node = TreeNode(
    id="unique-id",           # Auto-generated UUID if not provided
    label="Node Label",       # Display text
    type="default",           # Type for icon mapping
    parent=None,              # Parent node ID (None for root)
    children=[]               # List of child node IDs
)
```

### Adding Nodes

Add nodes using the `add_node()` method:

```python
# Add root node
root = TreeNode(id="root", label="Root", type="folder")
tree.add_node(root)

# Add child node
child = TreeNode(label="Child 1", type="item")
tree.add_node(child, parent_id="root")

# Add with specific position
sibling = TreeNode(label="Child 2", type="item")
tree.add_node(sibling, parent_id="root", insert_index=0)  # Insert at start
```

### Visual Structure

```
TreeView
├── Root Node 1
│   ├── [>] Child 1-1        # Collapsed node with children
│   ├── [ ] Child 1-2        # Leaf node (no children)
│   └── [v] Child 1-3        # Expanded node
│       ├── [ ] Grandchild
│       └── [ ] Grandchild
└── Root Node 2
    └── [>] Child 2-1
```

**Legend:**
- `[>]` - Collapsed node (has children)
- `[v]` - Expanded node (has children)
- `[ ]` - Leaf node (no children)

### Expanding Nodes

Control node expansion programmatically:

```python
# Expand all nodes with children
tree.expand_all()

# Expand specific nodes by adding to opened list
tree._state.opened.append("node-id")
```

**Note:** Users can also toggle nodes by clicking the chevron icon.

## Interactive Features

### Node Selection

Users can select nodes by clicking on labels. The selected node is visually highlighted:

```python
# Programmatically select a node
tree._state.selected = "node-id"

# Check current selection
current = tree._state.selected
```

### Adding Nodes

Users can add nodes via action buttons (visible on hover):

**Add Child:**
- Adds a new node as a child of the target node
- Automatically expands the parent
- Creates node with same type as parent

**Add Sibling:**
- Adds a new node next to the target node (same parent)
- Inserts after the target node
- Cannot add sibling to root nodes

```python
# Programmatically add child
tree._add_child(parent_id="root", new_label="New Child")

# Programmatically add sibling
tree._add_sibling(node_id="child1", new_label="New Sibling")
```

### Renaming Nodes

Users can rename nodes via the edit button:

1. Click the edit icon (visible on hover)
2. Input field appears with current label
3. Press Enter to save (triggers command)
4. Press ESC to cancel (keyboard shortcut)

```python
# Programmatically start rename
tree._start_rename("node-id")

# Save rename
tree._save_rename("node-id", "New Label")

# Cancel rename
tree._cancel_rename()
```

### Deleting Nodes

Users can delete nodes via the delete button:

**Restrictions:**
- Can only delete leaf nodes (no children)
- Attempting to delete a node with children raises an error
- Deleted node is removed from parent's children list

```python
# Programmatically delete node
tree._delete_node("node-id")  # Raises ValueError if node has children
```

## Content System

### Node Types and Icons

Assign types to nodes for semantic grouping and custom icon display:

```python
# Define node types
root = TreeNode(label="Project", type="project")
folder = TreeNode(label="src", type="folder")
file = TreeNode(label="main.py", type="python-file")

# Configure icons for types
tree.set_icon_config({
    "project": "fluent.folder_open",
    "folder": "fluent.folder",
    "python-file": "fluent.document_python"
})
```

**Note:** Icon configuration is stored in state and persists within the session.

### Hierarchical Organization

Nodes automatically maintain parent-child relationships:

```python
# Get node's children
node = tree._state.items["node-id"]
child_ids = node.children

# Get node's parent
parent_id = node.parent

# Navigate tree programmatically
for child_id in node.children:
    child_node = tree._state.items[child_id]
    print(child_node.label)
```

### Finding Root Nodes

Root nodes are nodes without a parent:

```python
root_nodes = [
    node_id for node_id, node in tree._state.items.items()
    if node.parent is None
]
```

## Advanced Features

### Keyboard Shortcuts

TreeView includes keyboard support for common operations:

| Key | Action |
|-----|--------|
| `ESC` | Cancel rename operation |

Additional shortcuts can be added via the Keyboard component:

```python
from myfasthtml.controls.Keyboard import Keyboard

tree = TreeView(parent=root_instance)
# ESC handler is automatically included for cancel rename
```

### State Management

TreeView maintains persistent state within the session:

| State Property | Type | Description |
|----------------|------|-------------|
| `items` | `dict[str, TreeNode]` | All nodes indexed by ID |
| `opened` | `list[str]` | IDs of expanded nodes |
| `selected` | `str \| None` | Currently selected node ID |
| `editing` | `str \| None` | Node being renamed (if any) |
| `icon_config` | `dict[str, str]` | Type-to-icon mapping |

### Dynamic Updates

TreeView updates are handled via commands that return the updated tree:

```python
# Commands automatically target the tree for HTMX swap
cmd = tree.commands.toggle_node("node-id")
# When executed, returns updated TreeView with new state
```

### CSS Customization

TreeView uses CSS classes for styling:

| Class | Element |
|-------|---------|
| `mf-treeview` | Root container |
| `mf-treenode-container` | Container for node and its children |
| `mf-treenode` | Individual node row |
| `mf-treenode.selected` | Selected node highlight |
| `mf-treenode-label` | Node label text |
| `mf-treenode-input` | Input field during rename |
| `mf-treenode-actions` | Action buttons container (hover) |

You can override these classes to customize appearance:

```css
.mf-treenode.selected {
    background-color: #e0f2fe;
    border-left: 3px solid #0284c7;
}

.mf-treenode-actions {
    opacity: 0;
    transition: opacity 0.2s;
}

.mf-treenode:hover .mf-treenode-actions {
    opacity: 1;
}
```

## Examples

### Example 1: File System Browser

A file/folder browser with different node types:

```python
from fasthtml.common import *
from myfasthtml.controls.TreeView import TreeView, TreeNode

# Create tree
tree = TreeView(parent=root_instance, _id="file-browser")

# Configure icons
tree.set_icon_config({
    "folder": "fluent.folder",
    "python": "fluent.document_python",
    "text": "fluent.document_text"
})

# Build file structure
root = TreeNode(id="root", label="my-project", type="folder")
tree.add_node(root)

src = TreeNode(id="src", label="src", type="folder")
tree.add_node(src, parent_id="root")

main = TreeNode(label="main.py", type="python")
utils = TreeNode(label="utils.py", type="python")
tree.add_node(main, parent_id="src")
tree.add_node(utils, parent_id="src")

readme = TreeNode(label="README.md", type="text")
tree.add_node(readme, parent_id="root")

# Expand to show structure
tree.expand_all()

return tree
```

### Example 2: Category Management

Managing product categories with inline editing:

```python
from fasthtml.common import *
from myfasthtml.controls.TreeView import TreeView, TreeNode

tree = TreeView(parent=root_instance, _id="categories")

# Root categories
electronics = TreeNode(id="elec", label="Electronics", type="category")
tree.add_node(electronics)

# Subcategories
computers = TreeNode(label="Computers", type="subcategory")
phones = TreeNode(label="Phones", type="subcategory")
tree.add_node(computers, parent_id="elec")
tree.add_node(phones, parent_id="elec")

# Products (leaf nodes)
laptop = TreeNode(label="Laptops", type="product")
desktop = TreeNode(label="Desktops", type="product")
tree.add_node(laptop, parent_id=computers.id)
tree.add_node(desktop, parent_id=computers.id)

tree.expand_all()
return tree
```

### Example 3: Document Outline Editor

Building a document outline with headings:

```python
from fasthtml.common import *
from myfasthtml.controls.TreeView import TreeView, TreeNode

tree = TreeView(parent=root_instance, _id="outline")

# Document structure
doc = TreeNode(id="doc", label="My Document", type="document")
tree.add_node(doc)

# Chapters
ch1 = TreeNode(id="ch1", label="Chapter 1: Introduction", type="heading1")
ch2 = TreeNode(id="ch2", label="Chapter 2: Methods", type="heading1")
tree.add_node(ch1, parent_id="doc")
tree.add_node(ch2, parent_id="doc")

# Sections
sec1_1 = TreeNode(label="1.1 Background", type="heading2")
sec1_2 = TreeNode(label="1.2 Objectives", type="heading2")
tree.add_node(sec1_1, parent_id="ch1")
tree.add_node(sec1_2, parent_id="ch1")

# Subsections
subsec = TreeNode(label="1.1.1 Historical Context", type="heading3")
tree.add_node(subsec, parent_id=sec1_1.id)

tree.expand_all()
return tree
```

### Example 4: Dynamic Tree with Event Handling

Responding to tree events with custom logic:

```python
from fasthtml.common import *
from myfasthtml.controls.TreeView import TreeView, TreeNode
from myfasthtml.controls.helpers import mk
from myfasthtml.core.commands import Command

tree = TreeView(parent=root_instance, _id="dynamic-tree")

# Initial structure
root = TreeNode(id="root", label="Tasks", type="folder")
tree.add_node(root)

# Function to handle selection
def on_node_selected(node_id):
    # Custom logic when node is selected
    node = tree._state.items[node_id]
    tree._select_node(node_id)

    # Update a detail panel elsewhere in the UI
    return Div(
        H3(f"Selected: {node.label}"),
        P(f"Type: {node.type}"),
        P(f"Children: {len(node.children)}")
    )

# Override select command with custom handler
# (In practice, you'd extend the Commands class or use event callbacks)

tree.expand_all()
return tree
```

---

## Developer Reference

This section contains technical details for developers working on the TreeView component itself.

### State

The TreeView component maintains the following state properties:

| Name | Type | Description | Default |
|------|------|-------------|---------|
| `items` | `dict[str, TreeNode]` | All nodes indexed by ID | `{}` |
| `opened` | `list[str]` | Expanded node IDs | `[]` |
| `selected` | `str \| None` | Selected node ID | `None` |
| `editing` | `str \| None` | Node being renamed | `None` |
| `icon_config` | `dict[str, str]` | Type-to-icon mapping | `{}` |

### Commands

Available commands for programmatic control:

| Name | Description |
|------|-------------|
| `toggle_node(node_id)` | Toggle expand/collapse state |
| `add_child(parent_id)` | Add child node to parent |
| `add_sibling(node_id)` | Add sibling node after target |
| `start_rename(node_id)` | Enter rename mode for node |
| `save_rename(node_id)` | Save renamed node label |
| `cancel_rename()` | Cancel rename operation |
| `delete_node(node_id)` | Delete node (if no children) |
| `select_node(node_id)` | Select a node |

All commands automatically target the TreeView component for HTMX updates.

### Public Methods

| Method | Description |
|--------|-------------|
| `add_node(node, parent_id, insert_index)` | Add a node to the tree |
| `expand_all()` | Expand all nodes with children |
| `set_icon_config(config)` | Configure icons for node types |
| `render()` | Render the complete TreeView |

### TreeNode Dataclass

```python
@dataclass
class TreeNode:
    id: str                      # Unique identifier (auto-generated UUID)
    label: str = ""              # Display text
    type: str = "default"        # Node type for icon mapping
    parent: Optional[str] = None # Parent node ID
    children: list[str] = []     # Child node IDs
```

### High Level Hierarchical Structure

```
Div(id="treeview", cls="mf-treeview")
├── Div(cls="mf-treenode-container", data-node-id="root1")
│   ├── Div(cls="mf-treenode")
│   │   ├── Icon                        # Toggle chevron
│   │   ├── Span(cls="mf-treenode-label") | Input(cls="mf-treenode-input")
│   │   └── Div(cls="mf-treenode-actions")
│   │       ├── Icon                    # Add child
│   │       ├── Icon                    # Rename
│   │       └── Icon                    # Delete
│   └── Div(cls="mf-treenode-container")  # Child nodes (if expanded)
│       └── ...
├── Div(cls="mf-treenode-container", data-node-id="root2")
│   └── ...
└── Keyboard                            # ESC handler
```

### Element IDs and Attributes

| Attribute | Element | Description |
|-----------|---------|-------------|
| `id` | Root Div | TreeView component ID |
| `data-node-id` | Node container | Node's unique ID |

### Internal Methods

These methods are used internally for rendering and state management:

| Method | Description |
|--------|-------------|
| `_toggle_node(node_id)` | Toggle expand/collapse state |
| `_add_child(parent_id, new_label)` | Add child node implementation |
| `_add_sibling(node_id, new_label)` | Add sibling node implementation |
| `_start_rename(node_id)` | Enter rename mode |
| `_save_rename(node_id, node_label)` | Save renamed node |
| `_cancel_rename()` | Cancel rename operation |
| `_delete_node(node_id)` | Delete node if no children |
| `_select_node(node_id)` | Select a node |
| `_render_action_buttons(node_id)` | Render hover action buttons |
| `_render_node(node_id, level)` | Recursively render node and children |

### Commands Class

The `Commands` nested class provides command factory methods:

| Method | Returns |
|--------|---------|
| `toggle_node(node_id)` | Command to toggle node |
| `add_child(parent_id)` | Command to add child |
| `add_sibling(node_id)` | Command to add sibling |
| `start_rename(node_id)` | Command to start rename |
| `save_rename(node_id)` | Command to save rename |
| `cancel_rename()` | Command to cancel rename |
| `delete_node(node_id)` | Command to delete node |
| `select_node(node_id)` | Command to select node |

All commands are automatically configured with HTMX targeting.

### Integration with Keyboard Component

TreeView includes a Keyboard component for ESC key handling:

```python
Keyboard(self, {"esc": self.commands.cancel_rename()}, _id="-keyboard")
```

This enables users to press ESC to cancel rename operations without clicking.
