{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "tree-table",
  "title": "Tree Table",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-angular/components/tree-table/tree-table.component.ts",
      "content": "import { Component, EventEmitter, Input, Output, booleanAttribute, ChangeDetectionStrategy } from '@angular/core'\nimport { cn } from '@/lib/utils'\n\nexport interface TreeTableColumn<T = Record<string, unknown>> {\n  key: string\n  title: string\n  width?: string\n  render?: (row: T) => string\n}\n\nexport interface TreeTableRow<T = Record<string, unknown>> {\n  id: string\n  data: T\n  children?: TreeTableRow<T>[]\n  disabled?: boolean\n}\n\nexport interface FlatTreeRow<T = Record<string, unknown>> {\n  row: TreeTableRow<T>\n  depth: number\n}\n\n/**\n * Angular port of UIPKGE TreeTable — hierarchical table with expandable\n * rows, per-level indent, selection checkboxes, loading overlay and empty\n * state. Same props/events as Vue.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-tree-table, [ui-tree-table]',\n  standalone: true,\n  host: {\n    '[attr.data-slot]': '\"tree-table\"',\n    '[attr.data-uipkge]': '\"\"',\n    '[attr.data-loading]': 'loading ? \"\" : null',\n    '[class]': 'hostClass',\n  },\n  template: `<ng-content />`,\n})\nexport class UiTreeTableComponent<T = Record<string, unknown>> {\n  @Input() data: TreeTableRow<T>[] = []\n  @Input() columns: TreeTableColumn<T>[] = []\n  @Input() indent = 24\n  @Input({ transform: booleanAttribute }) defaultExpanded = false\n  @Input({ transform: booleanAttribute }) selectable = false\n  @Input({ transform: booleanAttribute }) loading = false\n  @Input() emptyText = 'No data.'\n  /** Controlled selected row ids. Leave unset for uncontrolled use (React `selected`). */\n  @Input() selected?: string[]\n  @Input('class') className?: string\n  @Output() selectedChange = new EventEmitter<string[]>()\n  @Output() select = new EventEmitter<string[]>()\n  /** React `onExpand(id, expanded)` — single object payload (Angular emitters carry one value). */\n  @Output() expand = new EventEmitter<{ id: string; expanded: boolean }>()\n\n  expanded = new Set<string>()\n  private initialized = false\n  private internalSelected = new Set<string>()\n\n  get hostClass(): string {\n    return cn('block relative w-full overflow-auto', this.className)\n  }\n\n  private ensureInit(): void {\n    if (this.initialized) return\n    this.initialized = true\n    if (this.defaultExpanded) this.expanded = this.collectExpandable(this.data)\n  }\n\n  collectExpandable(rows: TreeTableRow<T>[]): Set<string> {\n    const s = new Set<string>()\n    const walk = (items: TreeTableRow<T>[]) => {\n      for (const r of items) {\n        if (r.children?.length) {\n          s.add(r.id)\n          walk(r.children)\n        }\n      }\n    }\n    walk(rows)\n    return s\n  }\n\n  isExpanded(id: string): boolean {\n    this.ensureInit()\n    return this.expanded.has(id)\n  }\n\n  toggle(id: string): void {\n    this.ensureInit()\n    const next = new Set(this.expanded)\n    const on = !next.has(id)\n    if (on) next.add(id)\n    else next.delete(id)\n    this.expanded = next\n    this.expand.emit({ id, expanded: on })\n  }\n\n  visibleRows(): FlatTreeRow<T>[] {\n    this.ensureInit()\n    const out: FlatTreeRow<T>[] = []\n    const walk = (items: TreeTableRow<T>[], depth: number) => {\n      for (const r of items) {\n        out.push({ row: r, depth })\n        if (r.children?.length && this.expanded.has(r.id)) walk(r.children, depth + 1)\n      }\n    }\n    walk(this.data, 0)\n    return out\n  }\n\n  indentStyle(depth: number): Record<string, string> {\n    return { 'padding-left': `${depth * this.indent + 12}px` }\n  }\n\n  /** Controlled `selected` wins when provided; otherwise internal state (React parity). */\n  get selectedSet(): Set<string> {\n    return this.selected ? new Set(this.selected) : this.internalSelected\n  }\n\n  isSelected(id: string): boolean {\n    return this.selectedSet.has(id)\n  }\n\n  toggleSelect(id: string): void {\n    const set = new Set(this.selectedSet)\n    if (set.has(id)) set.delete(id)\n    else set.add(id)\n    const next = [...set]\n    if (this.selected === undefined) this.internalSelected = set\n    this.selectedChange.emit(next)\n    this.select.emit(next)\n  }\n\n  cellText(row: TreeTableRow<T>, col: TreeTableColumn<T>): string {\n    if (col.render) return col.render(row.data)\n    const v = (row.data as Record<string, unknown>)[col.key]\n    return v == null ? '' : String(v)\n  }\n}\n",
      "type": "registry:ui",
      "target": "~/src/app/components/ui/tree-table/tree-table.component.ts"
    },
    {
      "path": "packages/registry-angular/components/tree-table/index.ts",
      "content": "export { UiTreeTableComponent, type TreeTableColumn, type TreeTableRow, type FlatTreeRow } from './tree-table.component'\n",
      "type": "registry:ui",
      "target": "~/src/app/components/ui/tree-table/index.ts"
    }
  ],
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Hierarchical data table with expandable parent/child rows. Column config, per-level indent, row selection checkboxes, loading overlay, and empty state. Built on the table primitives.",
  "categories": [
    "data",
    "display"
  ]
}