// Sidebar — search + collapsible tree, dense Windows-explorer style. const Sidebar = ({ tree, activeNodeId, expanded, setExpanded, onNodeClick, query, setQuery }) => { return (
{/* Search bar */}
setQuery(e.target.value)} placeholder="请输入您想要搜索的关键字" style={{ width: "100%", height: 26, paddingLeft: 8, paddingRight: 26, border: "1px solid var(--border-input)", background: "var(--bg-input)", fontSize: 12, color: "var(--text)", }} />
{/* Tree */}
{tree.map((node) => ( ))}
); }; const matches = (node, q) => { if (!q) return true; const t = q.toLowerCase(); if ((node.label || "").toLowerCase().includes(t)) return true; if (node.children) return node.children.some((c) => matches(c, q)); return false; }; const TreeNode = ({ node, depth, activeNodeId, expanded, setExpanded, onNodeClick, query }) => { const hasChildren = node.children && node.children.length > 0; const isExpanded = expanded[node.id] || (query && matches(node, query) && hasChildren); const isActive = activeNodeId === node.id; const visible = matches(node, query); if (!visible) return null; const click = () => { if (hasChildren) { setExpanded((s) => ({ ...s, [node.id]: !isExpanded })); } if (node.leaf || !hasChildren) { onNodeClick(node); } }; return (
{ if (!isActive) e.currentTarget.style.background = "var(--bg-row-hover)"; }} onMouseLeave={(e) => { if (!isActive) e.currentTarget.style.background = "transparent"; }} > {hasChildren ? ( {isExpanded ? : } ) : ( )} {node.label}
{isExpanded && hasChildren ? (
{node.children.map((child) => ( ))}
) : null}
); }; window.Sidebar = Sidebar;