{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "tree-select",
  "title": "Tree Select",
  "description": "Hierarchical dropdown with expandable nodes and checkbox selection.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "button",
    "popover",
    "scroll-area"
  ],
  "files": [
    {
      "path": "components/ui/tree-select.tsx",
      "content": "'use client'\n\nimport * as React from 'react'\nimport { ChevronDown, ChevronRight } from 'lucide-react'\n\nimport { buttonVariants } from '@/components/ui/button'\nimport { cn } from '@/lib/utils'\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from '@/components/ui/popover'\nimport { ScrollArea } from '@/components/ui/scroll-area'\n\nexport type TreeOption = {\n  id: string\n  label: string\n  children?: TreeOption[]\n}\n\ntype TreeSelectProps = {\n  options: TreeOption[]\n  value: string[]\n  onChange: (value: string[]) => void\n  placeholder?: string\n}\n\nfunction flatten(options: TreeOption[]): TreeOption[] {\n  return options.flatMap((option) => [option, ...(option.children ? flatten(option.children) : [])])\n}\n\nfunction TreeNode({\n  node,\n  depth,\n  expanded,\n  selected,\n  toggleExpand,\n  toggleSelect,\n}: {\n  node: TreeOption\n  depth: number\n  expanded: Set<string>\n  selected: Set<string>\n  toggleExpand: (id: string) => void\n  toggleSelect: (id: string) => void\n}) {\n  const hasChildren = Boolean(node.children?.length)\n  const isExpanded = expanded.has(node.id)\n\n  return (\n    <div>\n      <div className=\"flex items-center gap-2 rounded px-2 py-1 hover:bg-muted\" style={{ paddingLeft: `${depth * 14 + 8}px` }}>\n        {hasChildren ? (\n          <button type=\"button\" onClick={() => toggleExpand(node.id)}>\n            {isExpanded ? <ChevronDown className=\"h-4 w-4\" /> : <ChevronRight className=\"h-4 w-4\" />}\n          </button>\n        ) : (\n          <span className=\"inline-block w-4\" />\n        )}\n        <input type=\"checkbox\" checked={selected.has(node.id)} onChange={() => toggleSelect(node.id)} />\n        <span className=\"text-sm\">{node.label}</span>\n      </div>\n      {hasChildren && isExpanded && (\n        <div className=\"space-y-1\">\n          {node.children?.map((child) => (\n            <TreeNode\n              key={child.id}\n              node={child}\n              depth={depth + 1}\n              expanded={expanded}\n              selected={selected}\n              toggleExpand={toggleExpand}\n              toggleSelect={toggleSelect}\n            />\n          ))}\n        </div>\n      )}\n    </div>\n  )\n}\n\nexport function TreeSelect({\n  options,\n  value,\n  onChange,\n  placeholder = 'Select items',\n}: TreeSelectProps) {\n  const [open, setOpen] = React.useState(false)\n  const [expanded, setExpanded] = React.useState<Set<string>>(new Set())\n\n  const selected = new Set(value)\n  const allNodes = flatten(options)\n  const selectedLabels = allNodes.filter((node) => selected.has(node.id)).map((node) => node.label)\n\n  const toggleSelect = (id: string) => {\n    const next = new Set(selected)\n    if (next.has(id)) next.delete(id)\n    else next.add(id)\n    onChange([...next])\n  }\n\n  return (\n    <Popover open={open} onOpenChange={setOpen}>\n      <PopoverTrigger\n        type=\"button\"\n        className={cn(buttonVariants({ variant: 'outline' }), 'w-full justify-between')}\n      >\n        <span className=\"truncate\">\n          {selectedLabels.length ? selectedLabels.join(', ') : placeholder}\n        </span>\n      </PopoverTrigger>\n      <PopoverContent className=\"w-[340px] p-2\">\n        <ScrollArea className=\"h-64\">\n          <div className=\"space-y-1\">\n            {options.map((option) => (\n              <TreeNode\n                key={option.id}\n                node={option}\n                depth={0}\n                expanded={expanded}\n                selected={selected}\n                toggleExpand={(id) => {\n                  setExpanded((prev) => {\n                    const next = new Set(prev)\n                    if (next.has(id)) next.delete(id)\n                    else next.add(id)\n                    return next\n                  })\n                }}\n                toggleSelect={toggleSelect}\n              />\n            ))}\n          </div>\n        </ScrollArea>\n      </PopoverContent>\n    </Popover>\n  )\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "input",
    "picker"
  ],
  "type": "registry:ui"
}