# TreeNode - Tree Node Data Structure

## Overview

`TreeNode` is a class that represents nodes in a tree structure. It encapsulates the node's data, child nodes, expansion state, and provides convenient methods to manipulate the node itself and its children.

Each node created from `TreeNodes` or `CommonTreeNodes` is an instance of the `TreeNode` class with built-in methods.

## TreeNode Class

```typescript
class TreeNode<T> implements ITreeNode<T> {
  /** The node's data object (readonly) */
  public readonly item: T;

  /** Whether the node is expanded (showing children) */
  public expand: boolean;

  /** Whether the node is currently loading (for lazy loading) */
  public loading: boolean;

  /** Parent node (null for root nodes) */
  public readonly parent: TreeNode<T> | null;

  /** Node level (root = 0) */
  get level(): number;

  /** Child nodes (shallow copy, use methods to modify) */
  get children(): Array<TreeNode<T>> | null;

  /** Add child(ren) to the current node */
  append(childItem: T | Array<T>): ITreeNode<T> | Array<ITreeNode<T>>;

  /** Remove the current node (from its parent) */
  detach(): void;

  /** Move the current node to a different parent (returns new node) */
  moveTo(newParent: TreeNode<T>): TreeNode<T>;

  /** Replace the current node's data */
  replace(newItem: T): void;

  /** Remove all child nodes */
  removeChildren(): void;
}
```

## NodeViewOptions Class

The `NodeViewOptions` class handles view configuration for tree nodes, separating display concerns from data:

```typescript
class NodeViewOptions<T> {
  /** Field name for unique identifier */
  keyField: keyof T;

  /** Field name or function for display text */
  textField: keyof T | ((data: T) => string);

  /** Optional: Field name or function for icon */
  iconField?: keyof T | ((data: T) => string);

  /** Optional: Field name or function for CSS class */
  cssClassField?: keyof T | ((data: T) => string);

  /** Optional: Field name or function for inline styles */
  styleField?: keyof T | ((data: T) => Record<string, string>);

  /** Get display text from data */
  getText(data: T): string;

  /** Get icon from data */
  getIcon(data: T): string | undefined;

  /** Get CSS class from data */
  getCssClass(data: T): string | undefined;

  /** Get styles from data */
  getStyle(data: T): Record<string, string> | undefined;

  /** Get unique key from data */
  getKey(data: T): any;
}
```

## TreeNode Methods

### 1. append(childItem)

Add child(ren) to the current node.

**Parameters**:
- `childItem: T | Array<T>` - The child node's data object or array of child objects

**Example**:
```typescript
// Add a single child
parentNode.append({
  id: 123,
  name: "New Child",
  parentId: parentNode.item.id  // Will be set automatically
});

// Add multiple children at once
parentNode.append([
  { id: 124, name: "Child 1", parentId: parentNode.item.id },
  { id: 125, name: "Child 2", parentId: parentNode.item.id }
]);
```

**Returns**:
- `ITreeNode<T> | Array<ITreeNode<T>>` - The added child node(s)

**Notes**:
- Automatically initializes children array if needed
- If a sort function is configured, nodes will be automatically sorted
- Triggers version update for reactive updates

---

### 2. detach()

Remove the current node from its parent.

**Parameter**: None

**Example**:
```typescript
// Remove the node from its parent
node.detach();

// Clear reference after removal
activeNode.detach();
activeNode = null;
```

**Notes**:
- Removes from parent's `children` array
- Deletes from internal map
- If it's a root node, removes from root node list
- Triggers version update for reactive updates

---

### 3. moveTo(newParent)

Move the current node to a different parent node.

**Parameter**:
- `newParent: TreeNode<T>` - The new parent node (not an ID)

**Example**:
```typescript
// Move to a different parent node
node.moveTo(newParentNode);

// Get parent node first
const parentNode = treeNodes.nodeMap.get(parentId);
if (parentNode) {
  node.moveTo(parentNode);
}
```

**Notes**:
- Takes a TreeNode instance, not an ID
- Automatically updates the node's `parentKeyField` in data
- Removes from current parent's `children`
- Adds to new parent's `children`
- If a sort function is configured, will automatically re-sort
- Triggers version update for reactive updates

---

### 4. replace(newItem)

Replace the current node's data.

**Parameter**:
- `newItem: T` - New data object

**Example**:
```typescript
// Update some fields
node.replace({
  ...node.item,        // Keep other fields
  name: "New Name",    // Update name
  status: "active"     // Update status
});

// Or complete replacement
node.replace({
  id: node.item.id,    // Must keep ID
  name: "Completely New Data",
  newField: "value"
});
```

**Notes**:
- Usually need to keep the `id` field unchanged
- If a sort function is configured and sort field changes, will automatically re-sort
- Automatically triggers view update
- Preserves children, expand state, and loading state

---

### 5. removeChildren()

Remove all child nodes.

**Parameter**: None

**Example**:
```typescript
// Clear all children
parentNode.removeChildren();

// Remove and then add new children
parentNode.removeChildren();
parentNode.append(newChildData);
```

**Notes**:
- Removes all children and their descendants
- Node's `expand` state remains unchanged
- Internal map is updated accordingly
- Triggers version update for reactive updates

## CommonTreeNodes Options

When creating a tree, you can configure it with these options:

```typescript
interface TreeNodeOptions<T> {
  /** Field name for unique identifier (default: 'id') */
  keyField?: keyof T;

  /** Field name or function for display text (default: 'text') */
  textField?: keyof T | ((data: T) => string);

  /** Field name for parent reference (default: 'parentId') */
  parentKeyField?: keyof T;

  /** Function to check if data is a root node */
  checkIsRoot: (data: T) => boolean;

  /** Optional: Function to check if node is a branch (has children) */
  checkIsDirectory?: (node: TreeNode<T>) => boolean;

  /** Optional: Comparison function for sorting */
  compareFun?: (o1: T, o2: T) => number | undefined;

  /** Optional: Expand depth (default: 1) */
  expendDepth?: number;
}
```

## Complete Usage Example

```svelte
<script>
  import TreeView from "@ticatec/uniface-element/TreeView";
  import { CommonTreeNodes, type TreeNode } from "@ticatec/uniface-element/lib/TreeNodes";

  // Data type definition
  interface MyData {
    id: number;
    name: string;
    parentId: number | null;
  }

  let activeNode: TreeNode<MyData> | null = null;

  // Create tree structure
  const treeNodes = new CommonTreeNodes<MyData>({
    keyField: 'id',
    textField: 'name',
    parentKeyField: 'parentId',
    checkIsRoot: (item) => item.parentId === null,
    checkIsDirectory: (node) => node.children.length > 0,
    expendDepth: 2
  });

  // Initialize data
  treeNodes.setData([
    { id: 1, name: "Root", parentId: null },
    { id: 2, name: "Child 1", parentId: 1 },
    { id: 3, name: "Child 2", parentId: 1 }
  ]);

  // Add child node
  function addChild() {
    if (!activeNode) return;

    activeNode.append({
      id: Date.now(),
      name: "New Child",
      parentId: activeNode.item.id
    });
  }

  // Delete current node
  function deleteCurrentNode() {
    if (!activeNode) return;
    activeNode.detach();
    activeNode = null;
  }

  // Clear all children
  function clearChildren() {
    if (!activeNode) return;
    activeNode.removeChildren();
  }

  // Rename node
  function renameNode(newName: string) {
    if (!activeNode) return;
    activeNode.replace({
      ...activeNode.item,
      name: newName
    });
  }

  // Move node
  function moveNodeTo(newParentId: number) {
    if (!activeNode) return;
    const newParent = treeNodes.nodeMap.get(newParentId);
    if (newParent) {
      activeNode.moveTo(newParent);
    }
  }
</script>

<div>
  <TreeView
    nodes={treeNodes.nodes}
    version={treeNodes.version}
    textField="name"
    bind:activeNode
    checkIsDirectory={(node) => node.children.length > 0}
  />

  {#if activeNode}
    <div class="node-actions">
      <h3>Selected: {activeNode.item.name}</h3>
      <button on:click={addChild}>Add Child</button>
      <button on:click={deleteCurrentNode}>Delete Node</button>
      <button on:click={() => renameNode("New Name")}>Rename</button>
      <button on:click={() => moveNodeTo(1)}>Move to Root</button>
      <button on:click={clearChildren}>Clear Children</button>
    </div>
  {/if}
</div>
```

## Difference from TreeNodes Class Methods

`TreeNode` instance methods and `CommonTreeNodes`/`TreeNodes` class methods have clear responsibilities:

### TreeNode Instance Methods (For Operating on Specific Node Instances)

```typescript
// Operate directly on a node instance
node.append(childItem);      // Add child(ren) to this node
node.detach();               // Remove this node from its parent
node.replace(newItem);       // Replace this node's data
node.moveTo(newParentNode);  // Move this node to another parent
node.removeChildren();       // Remove all children
```

### CommonTreeNodes/TreeNodes Class Methods (For Tree-Level Operations)

```typescript
// Tree-level operations
treeNodes.setData(data);           // Initialize tree structure
treeNodes.append(item);            // Add new node (auto-find parent)
treeNodes.nodes;                   // Get root node list
treeNodes.version;                 // Get version number
treeNodes.getHierarchyList();      // Get expanded node list (TreeNodes only)
treeNodes.extractDirectories(item); // Extract directory structure (TreeNodes only)
```

## Key Properties

### item (readonly)
The data object stored in the node. Cannot be modified directly. Use `replace()` to update data.

### children (readonly)
Returns a shallow copy of the child nodes array. To modify children, use the methods:
- `append()` - add child(ren)
- `removeChildren()` - remove all children

### expand
Controls whether the node is expanded (showing its children). Can be set directly:
```typescript
node.expand = true;   // Expand the node
node.expand = false;  // Collapse the node
```

### loading
Indicates if the node is currently loading data (used for lazy loading). Can be set directly:
```typescript
node.loading = true;  // Show loading state
node.loading = false; // Hide loading state
```

### parent
Reference to the parent node. Is `null` for root nodes.

```typescript
if (node.parent) {
  console.log("Parent name:", node.parent.item.name);
}
```

## Reactive Updates

All node method operations automatically trigger `version` updates, ensuring Svelte components can respond to data changes:

```svelte
<TreeView
  nodes={treeNodes.nodes}
  version={treeNodes.version}  ← Auto-updated
  ...
/>
```

You don't need to manually trigger updates; node methods handle it automatically.

## FAQ

### Q: How to get a node instance?

```typescript
// Method 1: Through activeNode (selected node)
let activeNode: TreeNode | null = null;
<TreeView bind:activeNode />

// Method 2: Through nodeMap (need to keep TreeNodes reference)
const node = treeNodes.nodeMap.get(nodeId);
```

### Q: How to iterate over child nodes?

```typescript
// children is readonly, but you can iterate over it
node.children.forEach(child => {
  console.log(child.item.name);
});

// Or use for...of
for (const child of node.children) {
  console.log(child.item.name);
}
```

### Q: How to find a specific child node?

```typescript
function findChild(node: TreeNode, childId: number) {
  return node.children.find(child => child.item.id === childId) || null;
}
```

### Q: Can I modify the children array directly?

No. The `children` property returns a shallow copy. Use the provided methods instead:
- `append()` to add child(ren)
- `removeChildren()` to remove all children
- To remove a specific child, iterate through children and call `detach()` on the child node

### Q: How to recursively operate on all descendants?

```typescript
function traverse(node: TreeNode, callback: (node: TreeNode) => void) {
  callback(node);
  for (const child of node.children) {
    traverse(child, callback);
  }
}

// Usage
traverse(rootNode, (node) => {
  console.log(node.item.name);
  node.expand = true;  // Expand all nodes
});
```

### Q: What's the difference between TreeNode and NodeViewOptions?

- **TreeNode**: Represents the data structure. Each node is an instance with methods for manipulation.
- **NodeViewOptions**: Configuration class for how nodes should be displayed (text field, icon, CSS, etc.)

This separation allows TreeNode to be used in different contexts (TreeView, TreeDataGrid, etc.) without coupling to view-specific concerns.

## Best Practices

1. **Prefer node methods**: When you have a node instance, calling node methods directly is more intuitive

2. **Keep ID unchanged**: When using `replace()`, ensure the `id` field remains unchanged

3. **Use moveTo correctly**: The `moveTo()` method takes a TreeNode instance, not an ID:
   ```typescript
   // ❌ Wrong
   node.moveTo(123);

   // ✅ Correct
   const parentNode = treeNodes.nodeMap.get(123);
   node.moveTo(parentNode);
   ```

4. **Version management**: No need to manually manage versions, node methods update automatically

5. **Type safety**: Use TypeScript generics to ensure type safety

```typescript
// ✅ Recommended: Use generics
const treeNodes = new CommonTreeNodes<MyData>({ ... });

// Node type is automatically inferred
node.append({ id: 1, name: "..." });  // Type checking
```

6. **Children is readonly**: Don't try to modify the children array directly. Always use the provided methods.

## Performance Considerations

- All node methods have O(1) or O(n) time complexity, where n is the number of child nodes
- Delete operations also remove from internal map to maintain consistency
- For large batch operations, consider using `setData()` to rebuild the tree structure

## Architecture

### Decoupling from View

TreeNode is designed to be independent of any specific view component:
- No view-specific logic in TreeNode
- NodeViewOptions handles view configuration separately
- Can be used in TreeView, TreeDataGrid, or any other tree-based component

### Data Immutability

The `item` property is readonly to ensure data integrity:
- Prevents accidental mutations
- Makes state changes explicit through `replace()` method
- Helps with debugging and state tracking

## Related Components

- `CommonTreeNodes` - Base tree management class
- `TreeNodes` - Extended tree management with hierarchy features
- `TreeView` - Tree view component
- `NodeViewOptions` - View configuration class