"use client"; import React from "react"; import * as AccordionPrimitive from "@radix-ui/react-accordion"; // import { ScrollArea } from "@acme/components/ui/scroll-area"; // import { cn } from "@acme/components/lib/utils"; import { ChevronRight, type LucideIcon } from "lucide-react"; import useResizeObserver from "use-resize-observer"; import { ScrollArea } from "./scroll-area"; import { cn } from "@/lib/utils"; interface TreeDataItem { id: string; name: string; icon?: LucideIcon; type: "file" | "directory"; children?: TreeDataItem[]; } type TreeProps = React.HTMLAttributes & { data: TreeDataItem[] | TreeDataItem; initialSlelectedItemId?: string; onSelectChange?: (item: TreeDataItem | undefined) => void; expandAll?: boolean; folderIcon?: LucideIcon; itemIcon?: LucideIcon; }; const Tree = React.forwardRef( ( { data, initialSlelectedItemId, onSelectChange, expandAll, folderIcon, itemIcon, className, ...props }, ref, ) => { const [selectedItemId, setSelectedItemId] = React.useState< string | undefined >(initialSlelectedItemId); const handleSelectChange = React.useCallback( (item: TreeDataItem | undefined) => { setSelectedItemId(item?.id); if (onSelectChange && item?.type === "file") { onSelectChange(item); } }, [onSelectChange], ); const expandedItemIds = React.useMemo(() => { if (!initialSlelectedItemId) { return [] as string[]; } const ids: string[] = []; function walkTreeItems( items: TreeDataItem[] | TreeDataItem, targetId: string, ) { if (Array.isArray(items)) { // eslint-disable-next-line @typescript-eslint/prefer-for-of for (let i = 0; i < items.length; i++) { ids.push(items[i]!.id); if (walkTreeItems(items[i]!, targetId) && !expandAll) { return true; } if (!expandAll) ids.pop(); } } else if (!expandAll && items.id === targetId) { return true; } else if (items.children) { return walkTreeItems(items.children, targetId); } } walkTreeItems(data, initialSlelectedItemId); return ids; }, [data, initialSlelectedItemId]); const { ref: refRoot, width, height } = useResizeObserver(); return (
{/* style={{ width, height }} */}
); }, ); Tree.displayName = "Tree"; type TreeItemProps = TreeProps & { selectedItemId?: string; handleSelectChange: (item: TreeDataItem | undefined) => void; expandedItemIds: string[]; FolderIcon?: LucideIcon; ItemIcon?: LucideIcon; }; const TreeItem = React.forwardRef( ( { className, data, selectedItemId, handleSelectChange, expandedItemIds, FolderIcon, ItemIcon, ...props }, ref, ) => { return (
    {Array.isArray(data) ? ( data.map((item) => (
  • {item.children ? ( handleSelectChange(item)} > {item.icon && ( {item.children.length === 0 && (
    No items
    )}
    ) : ( handleSelectChange(item)} Icon={ItemIcon} /> )}
  • )) ) : (
  • handleSelectChange(data)} Icon={ItemIcon} />
  • )}
); }, ); TreeItem.displayName = "TreeItem"; const Leaf = React.forwardRef< HTMLDivElement, React.HTMLAttributes & { item: TreeDataItem; isSelected?: boolean; Icon?: LucideIcon; } >(({ className, item, isSelected, Icon, ...props }, ref) => { return (
{item.icon && (
); }); Leaf.displayName = "Leaf"; const AccordionTrigger = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => ( svg]:rotate-90", className, )} {...props} > {children} )); AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName; const AccordionContent = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => (
{children}
)); AccordionContent.displayName = AccordionPrimitive.Content.displayName; export { Tree, type TreeDataItem };