# Tree

Use `ui-tree` for file explorers, content maps, structured settings, and other places where parent-child relationships matter. Keep the hierarchy meaningful and make the active branch easy to scan.

## Import
```ts
import { TreeComponent, type TreeNode } from 'ui';
```

## Basic hierarchical content
```ts
import { Component } from '@angular/core';
import { TreeComponent, type TreeNode } from 'ui';

@Component({
  selector: 'app-tree-basic-demo',
  standalone: true,
  imports: [TreeComponent],
  template: `
    <div
      style="width:100%;max-width:22rem;padding:1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
    >
      <ui-tree [nodes]="nodes" [showSelectionIndicator]="true" />
    </div>
  `,
})
export class TreeBasicDemoComponent {
  protected readonly nodes: TreeNode[] = [
    {
      id: 'docs',
      label: 'Documents',
      icon: 'folder',
      hasChildren: true,
      expanded: true,
      children: [
        { id: 'brief', label: 'Project brief.docx', icon: 'document' },
        { id: 'notes', label: 'Meeting notes.md', icon: 'document', selected: true },
      ],
    },
    {
      id: 'assets',
      label: 'Assets',
      icon: 'image',
      hasChildren: true,
      children: [{ id: 'hero', label: 'Hero banner.png', icon: 'image' }],
    },
    { id: 'archive', label: 'Archive', icon: 'archive' },
  ];
}
```

## Selection and interaction behavior
```ts
import { Component } from '@angular/core';
import { TreeComponent, type TreeNode } from 'ui';

@Component({
  selector: 'app-tree-interaction-demo',
  standalone: true,
  imports: [TreeComponent],
  template: `
    <div
      style="display:flex;flex-wrap:wrap;gap:1rem;align-items:flex-start;width:100%;max-width:48rem"
    >
      <div
        style="flex:1 1 18rem;padding:1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
      >
        <p
          style="margin:0 0 0.75rem;font-size:0.875rem;font-weight:600;color:var(--color-neutral-foreground2-rest)"
        >
          File browser style
        </p>
        <ui-tree
          [nodes]="browserNodes"
          [showSelectionIndicator]="true"
          indicatorPosition="vertical"
          chevronPosition="before"
        />
      </div>

      <div
        style="flex:1 1 18rem;padding:1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
      >
        <p
          style="margin:0 0 0.75rem;font-size:0.875rem;font-weight:600;color:var(--color-neutral-foreground2-rest)"
        >
          Select on click navigation
        </p>
        <ui-tree
          [nodes]="navigationNodes"
          [showSelectionIndicator]="true"
          indicatorPosition="horizontal"
          chevronPosition="after"
          [asButton]="true"
          [selectOnClick]="true"
          [expandOnClick]="true"
          variant="secondary"
        />
      </div>
    </div>
  `,
})
export class TreeInteractionDemoComponent {
  protected readonly browserNodes: TreeNode[] = [
    {
      id: 'src',
      label: 'src',
      icon: 'folder',
      hasChildren: true,
      expanded: true,
      children: [
        { id: 'app', label: 'app', icon: 'folder' },
        { id: 'styles', label: 'styles.scss', icon: 'document', selected: true },
      ],
    },
    { id: 'package', label: 'package.json', icon: 'document' },
  ];

  protected readonly navigationNodes: TreeNode[] = [
    {
      id: 'workspace',
      label: 'Workspace',
      icon: 'folder',
      hasChildren: true,
      expanded: true,
      children: [
        { id: 'overview', label: 'Overview', icon: 'home' },
        { id: 'activity', label: 'Activity', icon: 'pulse', selected: true },
      ],
    },
    { id: 'settings', label: 'Settings', icon: 'settings' },
  ];
}
```

## Deeper structures
```ts
import { Component } from '@angular/core';
import { TreeComponent, type TreeNode } from 'ui';

@Component({
  selector: 'app-tree-deep-structure-demo',
  standalone: true,
  imports: [TreeComponent],
  template: `
    <div
      style="display:flex;flex-wrap:wrap;gap:1rem;align-items:flex-start;width:100%;max-width:52rem"
    >
      <div
        style="flex:0 0 22rem;padding:1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
      >
        <ui-tree [nodes]="nodes" [showSelectionIndicator]="true" [selectOnClick]="true" />
      </div>

      <div
        style="flex:1 1 18rem;padding:0.875rem 1rem;border:1px dashed var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background2-rest)"
      >
        <p
          style="margin:0 0 0.5rem;font-size:0.75rem;font-weight:600;letter-spacing:0.06em;text-transform:uppercase;color:var(--color-neutral-foreground2-rest)"
        >
          Suggested use
        </p>
        <div style="display:grid;gap:0.5rem;font-size:0.875rem;line-height:1.5">
          <div>
            Use deeper trees for repositories, operations, or content maps with real nesting.
          </div>
          <div>Keep the active branch expanded so the selected path remains readable.</div>
          <div>Avoid turning a flat list into a tree if the relationships are not meaningful.</div>
        </div>
      </div>
    </div>
  `,
})
export class TreeDeepStructureDemoComponent {
  protected readonly nodes: TreeNode[] = [
    {
      id: 'workspace',
      label: 'Workspace',
      icon: 'folder',
      hasChildren: true,
      expanded: true,
      children: [
        {
          id: 'apps',
          label: 'Apps',
          icon: 'grid',
          hasChildren: true,
          expanded: true,
          children: [
            {
              id: 'showcase',
              label: 'showcase',
              icon: 'folder',
              hasChildren: true,
              expanded: true,
              children: [
                { id: 'app', label: 'app', icon: 'folder' },
                {
                  id: 'components',
                  label: 'components',
                  icon: 'folder',
                  hasChildren: true,
                  expanded: true,
                  children: [
                    { id: 'header', label: 'showcase-doc-header.ts', icon: 'document' },
                    { id: 'page', label: 'showcase-doc-page.ts', icon: 'document', selected: true },
                  ],
                },
              ],
            },
            { id: 'landing', label: 'landing', icon: 'folder' },
          ],
        },
        {
          id: 'packages',
          label: 'Packages',
          icon: 'box',
          hasChildren: true,
          children: [{ id: 'ui', label: 'ui', icon: 'folder' }],
        },
      ],
    },
  ];
}
```

## Custom content templates
```ts
import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { TreeComponent, type TreeNode } from 'ui';

@Component({
  selector: 'app-tree-content-template-demo',
  standalone: true,
  imports: [CommonModule, TreeComponent],
  template: `
    <div
      style="width:100%;max-width:24rem;padding:1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
    >
      <ui-tree [nodes]="nodes">
        <ng-template #content let-node>
          <div
            style="display:flex;align-items:center;justify-content:space-between;gap:0.75rem;width:100%;min-width:0"
          >
            <span style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{{
              node.label
            }}</span>
            <span
              style="flex:none;min-width:2rem;padding:0.125rem 0.45rem;border-radius:999px;background:var(--color-neutral-background3-rest);font-size:0.75rem;text-align:center;color:var(--color-neutral-foreground2-rest)"
            >
              {{ getMeta(node.id) }}
            </span>
          </div>
        </ng-template>
      </ui-tree>
    </div>
  `,
})
export class TreeContentTemplateDemoComponent {
  protected readonly nodes: TreeNode[] = [
    {
      id: 'backlog',
      label: 'Backlog',
      icon: 'list',
      hasChildren: true,
      expanded: true,
      children: [
        { id: 'triage', label: 'Triage', icon: 'document' },
        { id: 'ready', label: 'Ready for build', icon: 'rocket' },
      ],
    },
    { id: 'review', label: 'Review queue', icon: 'edit' },
    { id: 'done', label: 'Done this sprint', icon: 'checkmark' },
  ];

  protected getMeta(id: string): string {
    const values: Record<string, string> = {
      backlog: '12',
      triage: '4',
      ready: '8',
      review: '3',
      done: '24',
    };
    return values[id] ?? '';
  }
}
```

## Quick actions on rows
```ts
import { CommonModule } from '@angular/common';
import { Component, signal } from '@angular/core';
import { ButtonComponent, TreeComponent, type TreeNode } from 'ui';

@Component({
  selector: 'app-tree-quick-actions-demo',
  standalone: true,
  imports: [CommonModule, ButtonComponent, TreeComponent],
  template: `
    <div
      style="display:flex;flex-wrap:wrap;gap:1rem;align-items:flex-start;width:100%;max-width:50rem"
    >
      <div
        style="flex:0 0 22rem;padding:1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
      >
        <ui-tree [nodes]="nodes" [showQuickActions]="true">
          <ng-template #quickActions let-node>
            <div style="display:flex;gap:0.375rem">
              <ui-button
                appearance="tint"
                variant="secondary"
                icon="edit"
                (click)="onQuickAction('Rename', node); $event.stopPropagation()"
              />
              <ui-button
                appearance="tint"
                variant="danger"
                icon="delete"
                (click)="onQuickAction('Archive', node); $event.stopPropagation()"
              />
            </div>
          </ng-template>
        </ui-tree>
      </div>

      <div
        style="flex:1 1 16rem;padding:0.875rem 1rem;border:1px dashed var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background2-rest)"
      >
        <p
          style="margin:0 0 0.5rem;font-size:0.75rem;font-weight:600;letter-spacing:0.06em;text-transform:uppercase;color:var(--color-neutral-foreground2-rest)"
        >
          Last action
        </p>
        <div style="font-size:0.875rem;line-height:1.5;color:var(--color-neutral-foreground-rest)">
          {{ lastAction() || 'No quick action used yet.' }}
        </div>
      </div>
    </div>
  `,
})
export class TreeQuickActionsDemoComponent {
  protected readonly nodes: TreeNode[] = [
    {
      id: 'design',
      label: 'Design',
      icon: 'folder',
      hasChildren: true,
      expanded: true,
      children: [
        { id: 'mocks', label: 'Mockups.fig', icon: 'document' },
        { id: 'tokens', label: 'Tokens.json', icon: 'document' },
      ],
    },
    { id: 'handoff', label: 'Handoff notes', icon: 'document' },
  ];

  protected readonly lastAction = signal('');

  protected onQuickAction(action: string, node: TreeNode): void {
    this.lastAction.set(`${action} on "${node.label}"`);
  }
}
```

## Drag and drop reordering
```ts
import { CommonModule } from '@angular/common';
import { Component, signal } from '@angular/core';
import { ButtonComponent, TreeComponent, type TreeNode } from 'ui';

@Component({
  selector: 'app-tree-drag-drop-demo',
  standalone: true,
  imports: [CommonModule, ButtonComponent, TreeComponent],
  template: `
    <div
      style="display:flex;flex-wrap:wrap;gap:1rem;align-items:flex-start;width:100%;max-width:52rem"
    >
      <div
        style="flex:0 0 22rem;padding:1rem;border:1px solid var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background-rest)"
      >
        <ui-tree
          [nodes]="nodes()"
          [draggable]="true"
          [dropZone]="true"
          [showSelectionIndicator]="true"
          (nodeMoved)="onNodeMoved($event)"
        />
      </div>

      <div
        style="flex:1 1 16rem;display:flex;flex-direction:column;gap:0.75rem;padding:0.875rem 1rem;border:1px dashed var(--color-neutral-stroke-rest);border-radius:1rem;background:var(--color-neutral-background2-rest)"
      >
        <div>
          <p
            style="margin:0 0 0.5rem;font-size:0.75rem;font-weight:600;letter-spacing:0.06em;text-transform:uppercase;color:var(--color-neutral-foreground2-rest)"
          >
            Drag and drop
          </p>
          <div style="font-size:0.875rem;line-height:1.5">
            Reorder folders, move files into groups, then reset the structure.
          </div>
        </div>

        <ui-button variant="secondary" appearance="outline" size="small" (click)="resetTree()">
          Reset tree
        </ui-button>

        <div style="font-size:0.875rem;line-height:1.5;color:var(--color-neutral-foreground-rest)">
          {{ lastAction() || 'No move yet.' }}
        </div>
      </div>
    </div>
  `,
})
export class TreeDragDropDemoComponent {
  protected readonly nodes = signal<TreeNode[]>(this.createTree());
  protected readonly lastAction = signal('');

  protected resetTree(): void {
    this.nodes.set(this.createTree());
    this.lastAction.set('Tree reset to initial state.');
  }

  protected onNodeMoved(event: { node: TreeNode; target: TreeNode; position: string }): void {
    this.lastAction.set(`Moved "${event.node.label}" ${event.position} "${event.target.label}".`);
  }

  private createTree(): TreeNode[] {
    return [
      {
        id: 'inbox',
        label: 'Inbox',
        icon: 'folder',
        hasChildren: true,
        expanded: true,
        children: [
          { id: 'brief', label: 'Brief.pdf', icon: 'document' },
          { id: 'todo', label: 'Todo.md', icon: 'document' },
        ],
      },
      {
        id: 'assets',
        label: 'Assets',
        icon: 'image',
        hasChildren: true,
        expanded: true,
        children: [{ id: 'logo', label: 'Logo.svg', icon: 'image' }],
      },
      { id: 'done', label: 'Done', icon: 'checkmark' },
    ];
  }
}
```

## Accessibility

### Tree semantics
`ui-tree` renders a `role="tree"` container and `ui-tree-node` rows provide tree-item style interaction for hierarchical content. Use clear labels and only model a tree when the structure is truly parent-child.

### Keyboard behavior
Keyboard behavior follows the tree-node model.

| Key | Action |
| --- | --- |
| Tab | Moves focus into or out of the tree according to document order. |
| Enter / Space | Activates the focused node. |
| ArrowRight | Expands a collapsed parent node. |
| ArrowLeft | Collapses an expanded parent node. |
| * | Expands sibling branches at the same level when supported by the current node context. |

### Selection and custom actions
When `selectOnClick` is enabled, keep selection aligned with the current document, item, or active branch. If you add quick actions or custom row content, ensure those controls have their own accessible labels and do not obscure the main node label.
