import { ChevronRight, FolderIcon, FolderOpenIcon, StarIcon, } from "lucide-react"; import { Fragment, useEffect, useMemo, useRef, useState } from "react"; import type { BrowseTreeNode } from "../../browse-tree"; import { cn } from "../lib/utils"; import { ScrollArea } from "./ui/scroll-area"; interface BrowseTreeSidebarProps { collections: BrowseTreeNode[]; expandedNodeIds: string[]; favoriteCollections: string[]; selectedNodeId: string | null; onSelect: (collection: string, path?: string) => void; onToggle: (nodeId: string) => void; onToggleFavoriteCollection: (collection: string) => void; } interface VisibleBrowseNode { node: BrowseTreeNode; parentId: string | null; } function flattenVisibleNodes( nodes: BrowseTreeNode[], expandedNodeIds: Set, parentId: string | null = null ): VisibleBrowseNode[] { const flat: VisibleBrowseNode[] = []; for (const node of nodes) { flat.push({ node, parentId }); if (node.children.length > 0 && expandedNodeIds.has(node.id)) { flat.push( ...flattenVisibleNodes(node.children, expandedNodeIds, node.id) ); } } return flat; } export function BrowseTreeSidebar({ collections, expandedNodeIds, favoriteCollections, selectedNodeId, onSelect, onToggle, onToggleFavoriteCollection, }: BrowseTreeSidebarProps) { const nodeRefs = useRef(new Map()); const [focusedNodeId, setFocusedNodeId] = useState( selectedNodeId ); const expandedNodeSet = useMemo( () => new Set(expandedNodeIds), [expandedNodeIds] ); const visibleNodes = useMemo( () => flattenVisibleNodes(collections, expandedNodeSet), [collections, expandedNodeSet] ); useEffect(() => { setFocusedNodeId(selectedNodeId); }, [selectedNodeId]); const favoriteRoots = collections.filter((node) => favoriteCollections.includes(node.collection) ); const moveFocus = (nodeId: string | null) => { if (!nodeId) { return; } setFocusedNodeId(nodeId); nodeRefs.current.get(nodeId)?.focus(); }; const getVisibleIndex = (nodeId: string | null) => visibleNodes.findIndex((entry) => entry.node.id === nodeId); return (
{favoriteRoots.length > 0 && (
Pinned
{favoriteRoots.map((node) => ( ))}
)}
Workspace Tree
{visibleNodes.map(({ node, parentId }, index) => { const isExpanded = expandedNodeSet.has(node.id); const isSelected = node.id === selectedNodeId; const isFocused = node.id === focusedNodeId; const hasChildren = node.children.length > 0; return (
{node.kind === "collection" && ( )}
); })}
); }