Replace fixed sidebar with floating action buttons (menu + add node) in bottom-left corner. Sidebar and node panel now slide in as overlay drawers with backdrop dismiss, capped width for small screens, and slide animations.
75 lines
2.5 KiB
TypeScript
75 lines
2.5 KiB
TypeScript
import { useState } from 'react';
|
|
import { useNodes } from '../hooks/useNodes';
|
|
import SearchBar from './SearchBar';
|
|
import type { NodeKind } from '../types';
|
|
|
|
const KINDS: (NodeKind | '')[] = ['', 'memory', 'component', 'task', 'decision'];
|
|
|
|
export default function Sidebar({
|
|
selectedId,
|
|
onSelect,
|
|
onAddNode,
|
|
onClose,
|
|
}: {
|
|
selectedId: string | null;
|
|
onSelect: (id: string) => void;
|
|
onAddNode: () => void;
|
|
onClose: () => void;
|
|
}) {
|
|
const [kindFilter, setKindFilter] = useState('');
|
|
const { data: nodes, isLoading } = useNodes(kindFilter ? { kind: kindFilter } : undefined);
|
|
|
|
return (
|
|
<aside className="h-full bg-gray-900 border-r border-gray-800 flex flex-col">
|
|
<div className="p-4 border-b border-gray-800">
|
|
<div className="flex items-center justify-between mb-3">
|
|
<h1 className="text-lg font-bold text-white">Cortex Portal</h1>
|
|
<button onClick={onClose} className="text-gray-500 hover:text-white text-xl leading-none">×</button>
|
|
</div>
|
|
<SearchBar onSelect={onSelect} />
|
|
<div className="flex gap-1 mt-3 flex-wrap">
|
|
{KINDS.map((k) => (
|
|
<button
|
|
key={k}
|
|
onClick={() => setKindFilter(k)}
|
|
className={`px-2 py-0.5 text-xs rounded ${
|
|
kindFilter === k
|
|
? 'bg-indigo-600 text-white'
|
|
: 'bg-gray-800 text-gray-400 hover:bg-gray-700'
|
|
}`}
|
|
>
|
|
{k || 'All'}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
<div className="flex-1 overflow-y-auto p-2">
|
|
{isLoading && <p className="text-gray-500 text-sm p-2">Loading...</p>}
|
|
{nodes?.map((n) => (
|
|
<button
|
|
key={n.id}
|
|
onClick={() => onSelect(n.id)}
|
|
className={`w-full text-left p-2 rounded mb-1 text-sm ${
|
|
selectedId === n.id
|
|
? 'bg-indigo-600/30 text-white'
|
|
: 'hover:bg-gray-800 text-gray-300'
|
|
}`}
|
|
>
|
|
<span className="text-[10px] uppercase tracking-wide text-gray-500">{n.kind}</span>
|
|
<div className="truncate">{n.title}</div>
|
|
{n.status && <span className="text-[10px] text-gray-500">{n.status}</span>}
|
|
</button>
|
|
))}
|
|
</div>
|
|
<div className="p-3 border-t border-gray-800">
|
|
<button
|
|
onClick={onAddNode}
|
|
className="w-full py-2 bg-indigo-600 hover:bg-indigo-500 text-white rounded text-sm font-medium"
|
|
>
|
|
+ Add Node
|
|
</button>
|
|
</div>
|
|
</aside>
|
|
);
|
|
}
|