import { useState, useCallback, useRef, useEffect, memo } from 'react' import { Folder, FolderOpen, CaretDown, CaretRight, Plus } from '@phosphor-icons/react' import type { FolderNode, SidebarSelection } from '../types' import { cn } from '@/lib/utils' interface FolderTreeProps { folders: FolderNode[] selection: SidebarSelection onSelect: (selection: SidebarSelection) => void onCreateFolder?: (name: string) => void collapsed?: boolean onToggle?: () => void } function FolderItem({ node, depth, selection, expanded, onToggle, onSelect, }: { node: FolderNode depth: number selection: SidebarSelection expanded: Record onToggle: (path: string) => void onSelect: (selection: SidebarSelection) => void }) { const isSelected = selection.kind === 'folder' && selection.path === node.path const isExpanded = expanded[node.path] ?? false const hasChildren = node.children.length > 0 const handleClick = () => { onSelect({ kind: 'folder', path: node.path }) if (hasChildren) onToggle(node.path) } return ( <> {isExpanded && hasChildren && (
{node.children.map((child) => ( ))}
)} ) } export const FolderTree = memo(function FolderTree({ folders, selection, onSelect, onCreateFolder, collapsed: externalCollapsed, onToggle }: FolderTreeProps) { const [internalCollapsed, setInternalCollapsed] = useState(false) const sectionCollapsed = externalCollapsed ?? internalCollapsed const [expanded, setExpanded] = useState>({}) const [isCreating, setIsCreating] = useState(false) const [newFolderName, setNewFolderName] = useState('') const inputRef = useRef(null) const toggleFolder = useCallback((path: string) => { setExpanded((prev) => ({ ...prev, [path]: !prev[path] })) }, []) useEffect(() => { if (isCreating) inputRef.current?.focus() }, [isCreating]) const handleCreateFolder = () => { const name = newFolderName.trim() if (name && onCreateFolder) { onCreateFolder(name) } setIsCreating(false) setNewFolderName('') } if (folders.length === 0 && !isCreating) return null return (
{/* Header */} {/* Tree */} {!sectionCollapsed && (
{folders.map((node) => ( ))} {isCreating && (
setNewFolderName(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') handleCreateFolder() if (e.key === 'Escape') { setIsCreating(false); setNewFolderName('') } }} onBlur={handleCreateFolder} placeholder="Folder name" data-testid="new-folder-input" />
)}
)}
) })