{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "code-tree",
  "type": "registry:ui",
  "description": "Tree component for displaying hierarchical data",
  "files": [
    {
      "path": "packages/code/tree.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { ChevronRight, Folder, File, FolderOpen } from \"lucide-react\";\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport { cn } from \"@repo/shadcn-ui/lib/utils\";\n\n// Tree Context\ninterface TreeContextType {\n    expandedIds: Set<string>;\n    selectedIds: string[];\n    toggleExpanded: (nodeId: string) => void;\n    handleSelection: (nodeId: string, ctrlKey?: boolean) => void;\n    showLines: boolean;\n    showIcons: boolean;\n    selectable: boolean;\n    multiSelect: boolean;\n    animateExpand: boolean;\n    indent: number;\n    onNodeClick?: (nodeId: string, data?: any) => void;\n    onNodeExpand?: (nodeId: string, expanded: boolean) => void;\n}\n\nconst TreeContext = React.createContext<TreeContextType | null>(null);\n\nconst useTree = () => {\n    const context = React.useContext(TreeContext);\n    if (!context) {\n        throw new Error(\"Tree components must be used within a TreeProvider\");\n    }\n    return context;\n};\n\n// Tree variants\nconst treeVariants = cva(\n    \"w-full bg-background border border-border rounded-ele shadow-sm/2\",\n    {\n        variants: {\n            variant: {\n                default: \"\",\n                outline: \"border-2\",\n                ghost: \"border-transparent bg-transparent\",\n            },\n            size: {\n                sm: \"text-sm\",\n                default: \"\",\n                lg: \"text-lg\",\n            },\n        },\n        defaultVariants: {\n            variant: \"default\",\n            size: \"default\",\n        },\n    },\n);\n\nconst treeItemVariants = cva(\n    \"flex items-center py-2 px-3 cursor-pointer transition-all duration-200 relative group rounded-[calc(var(--card-radius)-8px)]\",\n    {\n        variants: {\n            variant: {\n                default:\n                    \"hover:bg-accent focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none\",\n                ghost: \"hover:bg-accent/50\",\n                subtle: \"hover:bg-muted/50\",\n            },\n            selected: {\n                true: \"bg-accent text-accent-foreground\",\n                false: \"\",\n            },\n        },\n        defaultVariants: {\n            variant: \"default\",\n            selected: false,\n        },\n    },\n);\n\n// Provider Props\nexport interface TreeProviderProps\n    extends React.HTMLAttributes<HTMLDivElement>,\n    VariantProps<typeof treeVariants> {\n    defaultExpandedIds?: string[];\n    selectedIds?: string[];\n    onSelectionChange?: (selectedIds: string[]) => void;\n    onNodeClick?: (nodeId: string, data?: any) => void;\n    onNodeExpand?: (nodeId: string, expanded: boolean) => void;\n    showLines?: boolean;\n    showIcons?: boolean;\n    selectable?: boolean;\n    multiSelect?: boolean;\n    animateExpand?: boolean;\n    indent?: number;\n}\n\n// Tree Provider\nconst TreeProvider = React.forwardRef<HTMLDivElement, TreeProviderProps>(\n    (\n        {\n            className,\n            variant,\n            size,\n            children,\n            defaultExpandedIds = [],\n            selectedIds = [],\n            onSelectionChange,\n            onNodeClick,\n            onNodeExpand,\n            showLines = true,\n            showIcons = true,\n            selectable = true,\n            multiSelect = false,\n            animateExpand = true,\n            indent = 20,\n            ...props\n        },\n        ref,\n    ) => {\n        const [expandedIds, setExpandedIds] = React.useState<Set<string>>(\n            new Set(defaultExpandedIds),\n        );\n        const [internalSelectedIds, setInternalSelectedIds] =\n            React.useState<string[]>(selectedIds);\n\n        const isControlled = onSelectionChange !== undefined;\n        const currentSelectedIds = isControlled ? selectedIds : internalSelectedIds;\n\n        const toggleExpanded = React.useCallback(\n            (nodeId: string) => {\n                setExpandedIds((prev) => {\n                    const newSet = new Set(prev);\n                    const isExpanded = newSet.has(nodeId);\n                    isExpanded ? newSet.delete(nodeId) : newSet.add(nodeId);\n                    onNodeExpand?.(nodeId, !isExpanded);\n                    return newSet;\n                });\n            },\n            [onNodeExpand],\n        );\n\n        const handleSelection = React.useCallback(\n            (nodeId: string, ctrlKey = false) => {\n                if (!selectable) return;\n\n                let newSelection: string[];\n\n                if (multiSelect && ctrlKey) {\n                    newSelection = currentSelectedIds.includes(nodeId)\n                        ? currentSelectedIds.filter((id) => id !== nodeId)\n                        : [...currentSelectedIds, nodeId];\n                } else {\n                    newSelection = currentSelectedIds.includes(nodeId) ? [] : [nodeId];\n                }\n\n                isControlled\n                    ? onSelectionChange?.(newSelection)\n                    : setInternalSelectedIds(newSelection);\n            },\n            [\n                selectable,\n                multiSelect,\n                currentSelectedIds,\n                isControlled,\n                onSelectionChange,\n            ],\n        );\n\n        const contextValue: TreeContextType = {\n            expandedIds,\n            selectedIds: currentSelectedIds,\n            toggleExpanded,\n            handleSelection,\n            showLines,\n            showIcons,\n            selectable,\n            multiSelect,\n            animateExpand,\n            indent,\n            onNodeClick,\n            onNodeExpand,\n        };\n        return (\n            <TreeContext.Provider value={contextValue}>\n                <motion.div\n                    className={cn(treeVariants({ variant, size, className }))}\n                    ref={ref}\n                    initial={{ opacity: 0, y: 10 }}\n                    animate={{ opacity: 1, y: 0 }}\n                    transition={{ duration: 0.3, ease: \"easeOut\" }}\n                >\n                    <div className=\"p-2\" {...props}>\n                        {children}\n                    </div>\n                </motion.div>\n            </TreeContext.Provider>\n        );\n    },\n);\n\nTreeProvider.displayName = \"TreeProvider\";\n\n// Tree Props\nexport interface TreeProps extends React.HTMLAttributes<HTMLDivElement> {\n    asChild?: boolean;\n}\n\n// Tree\nconst Tree = React.forwardRef<HTMLDivElement, TreeProps>(\n    ({ className, asChild = false, children, ...props }, ref) => {\n        const Comp = asChild ? Slot : \"div\";\n\n        return (\n            <Comp className={cn(\"space-y-1\", className)} ref={ref} {...props}>\n                {children}\n            </Comp>\n        );\n    },\n);\n\nTree.displayName = \"Tree\";\n\n// Tree Item Props\nexport interface TreeItemProps\n    extends React.HTMLAttributes<HTMLDivElement>,\n    VariantProps<typeof treeItemVariants> {\n    nodeId: string;\n    label: string;\n    icon?: React.ReactNode;\n    data?: any;\n    level?: number;\n    isLast?: boolean;\n    parentPath?: boolean[];\n    hasChildren?: boolean;\n    asChild?: boolean;\n}\n\n// Tree Item\nconst TreeItem = React.forwardRef<HTMLDivElement, TreeItemProps>(\n    (\n        {\n            className,\n            variant,\n            nodeId,\n            label,\n            icon,\n            data,\n            level = 0,\n            isLast = false,\n            parentPath = [],\n            hasChildren = false,\n            asChild = false,\n            children,\n            onClick,\n            ...props\n        },\n        ref,\n    ) => {\n        const {\n            expandedIds,\n            selectedIds,\n            toggleExpanded,\n            handleSelection,\n            showLines,\n            showIcons,\n            animateExpand,\n            indent,\n            onNodeClick,\n        } = useTree();\n\n        const isExpanded = expandedIds.has(nodeId);\n        const isSelected = selectedIds.includes(nodeId);\n        const currentPath = [...parentPath, isLast];\n\n        const getDefaultIcon = () =>\n            hasChildren ? (\n                isExpanded ? (\n                    <FolderOpen className=\"h-4 w-4\" />\n                ) : (\n                    <Folder className=\"h-4 w-4\" />\n                )\n            ) : (\n                <File className=\"h-4 w-4\" />\n            );\n\n        const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {\n            if (hasChildren) toggleExpanded(nodeId);\n            handleSelection(nodeId, e.ctrlKey || e.metaKey);\n            onNodeClick?.(nodeId, data);\n            onClick?.(e);\n        };\n\n        const Comp = asChild ? Slot : \"div\";\n        return (\n            <div className=\"select-none\">\n                <motion.div\n                    className={cn(\n                        treeItemVariants({ variant, selected: isSelected, className }),\n                    )}\n                    style={{ paddingInlineStart: level * indent + 8 }}\n                    onClick={handleClick}\n                    whileTap={{ scale: 0.98, transition: { duration: 0.1 } }}\n                >\n                    {/* Tree Lines */}\n                    {showLines && level > 0 && (\n                        <div className=\"absolute start-0 top-0 bottom-0 pointer-events-none\">\n                            {currentPath.map((isLastInPath, pathIndex) => (\n                                <div\n                                    key={pathIndex}\n                                    className=\"absolute top-0 bottom-0 border-s border-border/40\"\n                                    style={{\n                                        insetInlineStart: pathIndex * indent + 12,\n                                        display:\n                                            pathIndex === currentPath.length - 1 && isLastInPath\n                                                ? \"none\"\n                                                : \"block\",\n                                    }}\n                                />\n                            ))}\n                            <div\n                                className=\"absolute top-1/2 border-t border-border/40\"\n                                style={{\n                                    insetInlineStart: (level - 1) * indent + 12,\n                                    width: indent - 4,\n                                    transform: \"translateY(-1px)\",\n                                }}\n                            />\n                            {isLast && (\n                                <div\n                                    className=\"absolute top-0 border-s border-border/40\"\n                                    style={{\n                                        insetInlineStart: (level - 1) * indent + 12,\n                                        height: \"50%\",\n                                    }}\n                                />\n                            )}\n                        </div>\n                    )}\n\n                    {/* Expand Icon */}\n                    <motion.div\n                        className=\"flex items-center justify-center w-4 h-4 me-1\"\n                        animate={{ rotate: hasChildren && isExpanded ? 90 : 0 }}\n                        transition={{ duration: 0.2, ease: \"easeInOut\" }}\n                    >\n                        {hasChildren && (\n                            <ChevronRight className=\"h-3 w-3 text-muted-foreground rtl:-scale-x-100\" />\n                        )}\n                    </motion.div>\n\n                    {/* Node Icon */}\n                    {showIcons && (\n                        <motion.div\n                            className=\"flex items-center justify-center w-4 h-4 me-2 text-muted-foreground\"\n                            whileHover={{ scale: 1.1 }}\n                            transition={{ duration: 0.15 }}\n                        >\n                            {icon || getDefaultIcon()}\n                        </motion.div>\n                    )}\n\n                    {/* Label */}\n                    <span className=\"text-sm truncate flex-1 text-foreground\">\n                        {label}\n                    </span>\n                </motion.div>\n\n                {/* Children */}\n                <AnimatePresence>\n                    {hasChildren && isExpanded && children && (\n                        <motion.div\n                            initial={{ height: 0, opacity: 0 }}\n                            animate={{ height: \"auto\", opacity: 1 }}\n                            exit={{ height: 0, opacity: 0 }}\n                            transition={{\n                                duration: animateExpand ? 0.3 : 0,\n                                ease: \"easeInOut\",\n                            }}\n                            className=\"overflow-hidden\"\n                        >\n                            <motion.div\n                                initial={{ y: -10 }}\n                                animate={{ y: 0 }}\n                                exit={{ y: -10 }}\n                                transition={{\n                                    duration: animateExpand ? 0.2 : 0,\n                                    delay: animateExpand ? 0.1 : 0,\n                                }}\n                            >\n                                {children}\n                            </motion.div>\n                        </motion.div>\n                    )}\n                </AnimatePresence>\n            </div>\n        );\n    },\n);\n\nTreeItem.displayName = \"TreeItem\";\n\nexport { TreeProvider, Tree, TreeItem, treeVariants, treeItemVariants };",
      "type": "registry:component"
    }
  ]
}