# LiveComponents Nested Components System

Comprehensive guide for building nested component hierarchies with parent-child relationships, event bubbling, and state synchronization.

## Overview

The Nested Components System enables complex UI compositions through parent-child component relationships. Parents can manage global state while children handle localized behavior, with events bubbling up for coordination.

## Architecture

```
┌─────────────────────────────────────────────────────────────┐
│                    Parent Component                         │
│  ┌─────────────────────────────────────────────────────┐   │
│  │           Global State Management                    │   │
│  │  • Manages list of items                            │   │
│  │  • Provides data to children                        │   │
│  │  • Handles child events                             │   │
│  └─────────────────────────────────────────────────────┘   │
│                           │                                  │
│              ┌────────────┴────────────┐                   │
│              │                         │                   │
│    ┌─────────▼────────┐    ┌──────────▼────────┐         │
│    │  Child Component  │    │  Child Component  │         │
│    │  • Local State    │    │  • Local State    │         │
│    │  • Dispatch Events│    │  • Dispatch Events│         │
│    └──────────────────┘    └───────────────────┘         │
└─────────────────────────────────────────────────────────────┘
```

## Core Concepts

### 1. Component Hierarchy

**ComponentHierarchy Value Object** - Represents parent-child relationships:

```php
use App\Framework\LiveComponents\ValueObjects\ComponentHierarchy;
use App\Framework\LiveComponents\ValueObjects\ComponentId;

// Root component (no parent)
$rootHierarchy = ComponentHierarchy::root();
// depth=0, path=[]

// First-level child
$childHierarchy = ComponentHierarchy::fromParent(
    parentId: ComponentId::fromString('parent:main'),
    childId: ComponentId::fromString('child:1')
);
// depth=1, path=['parent:main', 'child:1']

// Add another level
$grandchildHierarchy = $childHierarchy->withChild(
    ComponentId::fromString('grandchild:1')
);
// depth=2, path=['parent:main', 'child:1', 'grandchild:1']
```

**Hierarchy Queries:**
```php
$hierarchy->isRoot();  // true if no parent
$hierarchy->isChild(); // true if has parent
$hierarchy->getLevel(); // nesting depth (0, 1, 2, ...)
$hierarchy->isDescendantOf($componentId); // check ancestry
```

### 2. NestedComponentManager

**Server-Side Hierarchy Management:**

```php
use App\Framework\LiveComponents\NestedComponentManager;

$manager = new NestedComponentManager();

// Register root component
$parentId = ComponentId::fromString('todo-list:main');
$manager->registerHierarchy($parentId, ComponentHierarchy::root());

// Register child
$childId = ComponentId::fromString('todo-item:1');
$childHierarchy = ComponentHierarchy::fromParent($parentId, $childId);
$manager->registerHierarchy($childId, $childHierarchy);

// Query hierarchy
$manager->hasChildren($parentId); // true
$manager->getChildIds($parentId); // [ComponentId('todo-item:1')]
$manager->getParentId($childId);  // ComponentId('todo-list:main')
$manager->isRoot($parentId);      // true
$manager->getDepth($childId);     // 1

// Get all ancestors/descendants
$ancestors = $manager->getAncestors($childId); // [parentId]
$descendants = $manager->getDescendants($parentId); // [childId]

// Statistics
$stats = $manager->getStats();
// ['total_components' => 2, 'root_components' => 1, ...]
```

### 3. SupportsNesting Interface

**Parent components must implement this interface:**

```php
use App\Framework\LiveComponents\Contracts\SupportsNesting;

interface SupportsNesting
{
    /**
     * Get list of child component IDs
     */
    public function getChildComponents(): array;

    /**
     * Handle event from child component
     *
     * @return bool Return false to stop event bubbling, true to continue
     */
    public function onChildEvent(ComponentId $childId, string $eventName, array $payload): bool;

    /**
     * Validate child component compatibility
     */
    public function canHaveChild(ComponentId $childId): bool;
}
```

### 4. Event Bubbling

**Events flow from child to parent:**

```
┌─────────────────────────┐
│   Child Component        │
│   • User clicks button   │
│   • Dispatches event     │
└───────────┬─────────────┘
            │ Event Bubbles Up
            ▼
┌─────────────────────────┐
│   Parent Component       │
│   • Receives event       │
│   • Updates state        │
│   • Re-renders children  │
└─────────────────────────┘
```

**Event Dispatcher:**
```php
use App\Framework\LiveComponents\NestedComponentEventDispatcher;

$dispatcher = new NestedComponentEventDispatcher();

// Child dispatches event
$dispatcher->dispatch(
    componentId: ComponentId::fromString('todo-item:1'),
    eventName: 'todo-completed',
    payload: [
        'todo_id' => '1',
        'completed' => true
    ]
);

// Check dispatched events
$dispatcher->hasEvents(); // true
$dispatcher->count(); // 1
$events = $dispatcher->getEvents();
```

## Implementation Guide

### Creating a Parent Component

**1. Implement SupportsNesting:**

```php
use App\Framework\LiveComponents\Contracts\LiveComponentContract;
use App\Framework\LiveComponents\Contracts\SupportsNesting;
use App\Framework\LiveComponents\Attributes\LiveComponent;

#[LiveComponent('todo-list')]
final readonly class TodoListComponent implements LiveComponentContract, SupportsNesting
{
    private ComponentId $id;
    private TodoListState $state;

    public function __construct(
        ComponentId $id,
        ?ComponentData $initialData = null,
        array $todos = []
    ) {
        $this->id = $id;
        $this->state = $initialData
            ? TodoListState::fromComponentData($initialData)
            : new TodoListState(todos: $todos);
    }

    // LiveComponentContract methods
    public function getId(): ComponentId { return $this->id; }
    public function getData(): ComponentData { return $this->state->toComponentData(); }
    public function getRenderData(): ComponentRenderData { /* ... */ }

    // SupportsNesting methods

    public function getChildComponents(): array
    {
        // Return array of child component IDs
        $childIds = [];
        foreach ($this->state->todos as $todo) {
            $childIds[] = "todo-item:{$todo['id']}";
        }
        return $childIds;
    }

    public function onChildEvent(ComponentId $childId, string $eventName, array $payload): bool
    {
        // Handle events from children
        match ($eventName) {
            'todo-completed' => $this->handleTodoCompleted($payload),
            'todo-deleted' => $this->handleTodoDeleted($payload),
            default => null
        };

        return true; // Continue bubbling
    }

    public function canHaveChild(ComponentId $childId): bool
    {
        // Only accept TodoItem components
        return str_starts_with($childId->name, 'todo-item');
    }

    private function handleTodoCompleted(array $payload): void
    {
        $todoId = $payload['todo_id'];
        $completed = $payload['completed'];

        // Log or trigger side effects
        error_log("Todo {$todoId} marked as " . ($completed ? 'completed' : 'active'));

        // Note: State updates happen through Actions, not event handlers
        // Event handlers are for logging, analytics, side effects
    }
}
```

**2. Create Parent State:**

```php
final readonly class TodoListState
{
    public function __construct(
        public array $todos = [],
        public string $filter = 'all'
    ) {}

    public static function fromComponentData(ComponentData $data): self
    {
        $array = $data->toArray();
        return new self(
            todos: $array['todos'] ?? [],
            filter: $array['filter'] ?? 'all'
        );
    }

    public function toComponentData(): ComponentData
    {
        return ComponentData::fromArray([
            'todos' => $this->todos,
            'filter' => $this->filter
        ]);
    }

    public function withTodoAdded(array $todo): self
    {
        return new self(
            todos: [...$this->todos, $todo],
            filter: $this->filter
        );
    }

    // More transformation methods...
}
```

**3. Create Parent Template:**

```html
<!-- todo-list.view.php -->
<div class="todo-list">
    <!-- Parent UI -->
    <h2>My Todos ({total_count})</h2>

    <!-- Child Components -->
    <for items="todos" as="todo">
        <div
            data-live-component="todo-item:{todo.id}"
            data-parent-component="{component_id}"
            data-nesting-depth="1"
        >
            <!-- TodoItemComponent renders here -->
        </div>
    </for>
</div>
```

### Creating a Child Component

**1. Implement Component with Event Dispatcher:**

```php
use App\Framework\LiveComponents\NestedComponentEventDispatcher;

#[LiveComponent('todo-item')]
final readonly class TodoItemComponent implements LiveComponentContract
{
    private ComponentId $id;
    private TodoItemState $state;

    public function __construct(
        ComponentId $id,
        private NestedComponentEventDispatcher $eventDispatcher,
        ?ComponentData $initialData = null,
        ?array $todoData = null
    ) {
        $this->id = $id;
        $this->state = $initialData
            ? TodoItemState::fromComponentData($initialData)
            : TodoItemState::fromTodoArray($todoData ?? []);
    }

    #[Action]
    public function toggle(): ComponentData
    {
        $newState = $this->state->withToggled();

        // Dispatch event to parent
        $this->eventDispatcher->dispatch(
            componentId: $this->id,
            eventName: 'todo-completed',
            payload: [
                'todo_id' => $this->state->id,
                'completed' => $newState->completed
            ]
        );

        return $newState->toComponentData();
    }

    #[Action]
    public function delete(): ComponentData
    {
        // Dispatch delete event to parent
        $this->eventDispatcher->dispatch(
            componentId: $this->id,
            eventName: 'todo-deleted',
            payload: ['todo_id' => $this->state->id]
        );

        return $this->state->toComponentData();
    }
}
```

**2. Create Child State:**

```php
final readonly class TodoItemState
{
    public function __construct(
        public string $id,
        public string $title,
        public bool $completed = false,
        public int $createdAt = 0
    ) {}

    public static function fromTodoArray(array $todo): self
    {
        return new self(
            id: $todo['id'] ?? '',
            title: $todo['title'] ?? '',
            completed: $todo['completed'] ?? false,
            createdAt: $todo['created_at'] ?? time()
        );
    }

    public function withToggled(): self
    {
        return new self(
            id: $this->id,
            title: $this->title,
            completed: !$this->completed,
            createdAt: $this->createdAt
        );
    }
}
```

**3. Create Child Template:**

```html
<!-- todo-item.view.php -->
<div class="todo-item {completed|then:todo-item--completed}">
    <button data-livecomponent-action="toggle">
        <if condition="completed">✓</if>
    </button>

    <div class="todo-item__title">{title}</div>

    <button data-livecomponent-action="delete">✕</button>
</div>
```

## Client-Side Integration

**Automatic Initialization:**

```javascript
// NestedComponentHandler automatically initializes with LiveComponentManager
import { LiveComponentManager } from './livecomponent/index.js';

// Scans DOM for nested components
const nestedHandler = liveComponentManager.nestedHandler;

// Get hierarchy info
const parentId = nestedHandler.getParentId('todo-item:1'); // 'todo-list:main'
const childIds = nestedHandler.getChildIds('todo-list:main'); // ['todo-item:1', ...]

// Event bubbling
nestedHandler.bubbleEvent('todo-item:1', 'todo-completed', {
    todo_id: '1',
    completed: true
});

// Statistics
const stats = nestedHandler.getStats();
// { total_components: 5, root_components: 1, max_nesting_depth: 2, ... }
```

## Best Practices

### 1. State Management

**✅ Parent owns the data:**
```php
// Parent manages list
private TodoListState $state; // Contains all todos

// Child manages display state only
private TodoItemState $state; // id, title, completed, isEditing
```

**❌ Don't duplicate state:**
```php
// Bad: Both parent and child store todo data
// This leads to synchronization issues
```

### 2. Event Handling

**✅ Use event handlers for side effects:**
```php
public function onChildEvent(ComponentId $childId, string $eventName, array $payload): bool
{
    // ✅ Logging
    error_log("Child event: {$eventName}");

    // ✅ Analytics
    $this->analytics->track($eventName, $payload);

    // ✅ External system updates
    $this->cache->invalidate($payload['todo_id']);

    return true; // Continue bubbling
}
```

**❌ Don't modify state in event handlers:**
```php
// ❌ Bad: Event handlers shouldn't modify component state
// State changes happen through Actions that return new ComponentData
```

### 3. Child Compatibility

**✅ Validate child types:**
```php
public function canHaveChild(ComponentId $childId): bool
{
    // Only accept specific component types
    return str_starts_with($childId->name, 'todo-item');
}
```

### 4. Circular Dependencies

**✅ Framework automatically prevents:**
```php
// This will throw InvalidArgumentException:
$manager->registerHierarchy($componentId, $hierarchy);
// "Circular dependency detected: Component cannot be its own ancestor"
```

## Performance Considerations

### Hierarchy Depth

- **Recommended:** Max 3-4 levels deep
- **Reason:** Each level adds overhead for event bubbling
- **Alternative:** Flatten hierarchy when possible

### Event Bubbling

- **Cost:** O(depth) for each event
- **Optimization:** Stop bubbling early when not needed
- **Pattern:** Return `false` from `onChildEvent()` to stop

```php
public function onChildEvent(ComponentId $childId, string $eventName, array $payload): bool
{
    if ($eventName === 'internal-event') {
        // Handle locally, don't bubble further
        return false;
    }

    // Let other events bubble
    return true;
}
```

### State Synchronization

- **Pattern:** Parent as single source of truth
- **Benefit:** Avoids synchronization bugs
- **Trade-off:** More re-renders, but simpler logic

## Testing

### Unit Tests

```php
describe('NestedComponentManager', function () {
    it('tracks parent-child relationships', function () {
        $manager = new NestedComponentManager();

        $parentId = ComponentId::fromString('parent:1');
        $childId = ComponentId::fromString('child:1');

        $manager->registerHierarchy($parentId, ComponentHierarchy::root());
        $manager->registerHierarchy(
            $childId,
            ComponentHierarchy::fromParent($parentId, $childId)
        );

        expect($manager->hasChildren($parentId))->toBeTrue();
        expect($manager->getParentId($childId))->toEqual($parentId);
    });

    it('prevents circular dependencies', function () {
        $manager = new NestedComponentManager();
        $id = ComponentId::fromString('self:1');

        expect(fn() => $manager->registerHierarchy(
            $id,
            ComponentHierarchy::fromParent($id, $id)
        ))->toThrow(InvalidArgumentException::class);
    });
});
```

### Integration Tests

```php
describe('TodoList with nested TodoItems', function () {
    it('handles child events', function () {
        $todoList = new TodoListComponent(
            id: ComponentId::fromString('todo-list:test'),
            todos: [
                ['id' => '1', 'title' => 'Test', 'completed' => false]
            ]
        );

        $childId = ComponentId::fromString('todo-item:1');

        // Simulate child event
        $result = $todoList->onChildEvent(
            $childId,
            'todo-completed',
            ['todo_id' => '1', 'completed' => true]
        );

        expect($result)->toBeTrue(); // Event bubbled successfully
    });
});
```

## Troubleshooting

### Problem: Children not rendering

**Cause:** Missing `data-parent-component` attribute

**Solution:**
```html
<!-- ✅ Correct -->
<div
    data-live-component="child:1"
    data-parent-component="parent:main"
    data-nesting-depth="1"
>
</div>
```

### Problem: Events not bubbling

**Cause:** Wrong ComponentId or event name

**Solution:**
```php
// ✅ Use exact component ID
$this->eventDispatcher->dispatch(
    componentId: $this->id,  // ✅ Correct: use component's own ID
    eventName: 'todo-completed',
    payload: [...]
);
```

### Problem: Circular dependency error

**Cause:** Component trying to be its own ancestor

**Solution:**
```php
// ❌ Wrong: Same component as parent and child
$hierarchy = ComponentHierarchy::fromParent($sameId, $sameId);

// ✅ Correct: Different components
$hierarchy = ComponentHierarchy::fromParent($parentId, $childId);
```

## Advanced Patterns

### Multi-Level Nesting

```php
// Grandparent → Parent → Child
$grandparent = ComponentHierarchy::root();

$parent = ComponentHierarchy::fromParent(
    ComponentId::fromString('grandparent:1'),
    ComponentId::fromString('parent:1')
);

$child = $parent->withChild(
    ComponentId::fromString('child:1')
);
// depth=2, path=['grandparent:1', 'parent:1', 'child:1']
```

### Conditional Children

```php
public function getChildComponents(): array
{
    // Only show children if filter matches
    $filteredTodos = $this->state->getFilteredTodos();

    return array_map(
        fn($todo) => "todo-item:{$todo['id']}",
        $filteredTodos
    );
}
```

### Dynamic Child Addition

```php
#[Action]
public function addTodo(string $title): ComponentData
{
    $newTodo = [
        'id' => uniqid('todo_', true),
        'title' => $title,
        'completed' => false
    ];

    // State includes new todo
    $newState = $this->state->withTodoAdded($newTodo);

    // Framework automatically creates child component
    // based on getChildComponents() result

    return $newState->toComponentData();
}
```

## Summary

**Nested Components enable:**
- ✅ Complex UI compositions
- ✅ Parent-child communication via events
- ✅ Hierarchical state management
- ✅ Reusable component patterns
- ✅ Type-safe relationships

**Key Classes:**
- `ComponentHierarchy` - Relationship value object
- `NestedComponentManager` - Server-side hierarchy
- `NestedComponentHandler` - Client-side hierarchy
- `NestedComponentEventDispatcher` - Event bubbling
- `SupportsNesting` - Parent component interface

**Next Steps:**
- Implement Slot System for flexible composition
- Add SSE integration for real-time updates
- Explore advanced caching strategies
