wuhp commited on
Commit
781de58
·
verified ·
1 Parent(s): 0c701bd

Update components/Sidebar.tsx

Browse files
Files changed (1) hide show
  1. components/Sidebar.tsx +148 -122
components/Sidebar.tsx CHANGED
@@ -1,5 +1,5 @@
1
 
2
- import React, { useState } from 'react';
3
  import { LAYER_DEFINITIONS } from '../constants';
4
  import { LayerType } from '../types';
5
  import { Box, Sparkles, LayoutTemplate, Circle, Search, X, ChevronLeft, ChevronRight } from 'lucide-react';
@@ -8,11 +8,12 @@ interface SidebarProps {
8
  onOpenAIBuilder: () => void;
9
  onSelectTemplate: (templateId: string) => void;
10
  isConnected: boolean;
 
 
11
  }
12
 
13
- const Sidebar: React.FC<SidebarProps> = ({ onOpenAIBuilder, onSelectTemplate, isConnected }) => {
14
- const [searchQuery, setSearchQuery] = useState('');
15
- const [isOpen, setIsOpen] = useState(true);
16
 
17
  const onDragStart = (event: React.DragEvent, layerType: LayerType) => {
18
  event.dataTransfer.setData('application/reactflow', layerType);
@@ -42,138 +43,163 @@ const Sidebar: React.FC<SidebarProps> = ({ onOpenAIBuilder, onSelectTemplate, is
42
  );
43
 
44
  return (
45
- <aside className={`${isOpen ? 'w-64 border-r' : 'w-0 border-none'} bg-slate-900 border-slate-800 flex flex-col h-full z-20 transition-all duration-300 relative`}>
46
- {/* Toggle Button */}
47
- <button
48
- onClick={() => setIsOpen(!isOpen)}
49
- className="absolute -right-3 top-6 bg-slate-800 border border-slate-700 text-slate-400 p-0.5 rounded-full hover:text-white hover:bg-slate-700 cursor-pointer z-50 w-6 h-6 flex items-center justify-center shadow-md transition-transform"
50
- title={isOpen ? "Collapse Sidebar" : "Expand Sidebar"}
51
- >
52
- {isOpen ? <ChevronLeft size={14} /> : <ChevronRight size={14} />}
53
- </button>
54
 
55
- {/* Content Container */}
56
- <div className={`flex flex-col h-full overflow-hidden ${!isOpen ? 'invisible opacity-0' : 'visible opacity-100'} transition-opacity duration-200`}>
57
- <div className="p-4 border-b border-slate-800 bg-slate-900 space-y-4 min-w-[16rem]">
58
- <div>
59
- <h1 className="text-xl font-bold bg-gradient-to-r from-blue-400 to-violet-400 bg-clip-text text-transparent">
60
- Architecture Agents
61
- </h1>
62
- <p className="text-xs text-slate-500 mt-1">AI-Powered Model Builder</p>
63
- </div>
64
-
65
- <div className="grid grid-cols-2 gap-2">
66
- <button
67
- onClick={onOpenAIBuilder}
68
- className="flex flex-col items-center justify-center p-2 bg-purple-500/10 hover:bg-purple-500/20 border border-purple-500/30 rounded-lg text-purple-300 transition-colors group"
69
- >
70
- <Sparkles size={18} className="mb-1 group-hover:scale-110 transition-transform" />
71
- <span className="text-[10px] font-bold">AI Builder</span>
72
- </button>
73
- <button
74
- onClick={() => onSelectTemplate('menu')}
75
- className="flex flex-col items-center justify-center p-2 bg-blue-500/10 hover:bg-blue-500/20 border border-blue-500/30 rounded-lg text-blue-300 transition-colors group"
76
- >
77
- <LayoutTemplate size={18} className="mb-1 group-hover:scale-110 transition-transform" />
78
- <span className="text-[10px] font-bold">Templates</span>
79
- </button>
80
- </div>
81
 
82
- {/* Search Bar */}
83
- <div className="relative">
84
- <Search className="absolute left-3 top-2.5 text-slate-500" size={14} />
85
- <input
86
- type="text"
87
- placeholder="Search layers..."
88
- className="w-full bg-slate-950 border border-slate-700 rounded-lg pl-9 pr-8 py-2 text-xs text-slate-200 focus:outline-none focus:border-blue-500 transition-colors placeholder-slate-600"
89
- value={searchQuery}
90
- onChange={(e) => setSearchQuery(e.target.value)}
91
- />
92
- {searchQuery && (
 
 
 
 
 
 
93
  <button
94
- onClick={() => setSearchQuery('')}
95
- className="absolute right-2 top-2 text-slate-500 hover:text-slate-300"
96
  >
97
- <X size={14} />
 
98
  </button>
99
- )}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  </div>
101
- </div>
102
-
103
- <div className="flex-1 overflow-y-auto p-4 space-y-6 scrollbar-thin scrollbar-thumb-slate-700 min-w-[16rem]">
104
 
105
- {searchQuery ? (
106
- // Search Results View
107
- <div className="space-y-2">
108
- <h3 className="text-xs font-semibold text-slate-500 uppercase tracking-widest mb-3">
109
- Search Results ({filteredLayers.length})
110
- </h3>
111
- {filteredLayers.length > 0 ? (
112
- filteredLayers.map(layer => (
113
- <div
114
- key={layer.type}
115
- className="bg-slate-800 hover:bg-slate-750 p-3 rounded border border-slate-700 cursor-grab active:cursor-grabbing transition-colors group relative overflow-hidden"
116
- onDragStart={(event) => onDragStart(event, layer.type)}
117
- draggable
118
- >
119
- <div className="flex items-center gap-3 relative z-10">
120
- <div className={`p-1.5 rounded transition-colors group-hover:bg-slate-900 bg-slate-900/50`}>
121
- <Box size={14} className="text-slate-400 group-hover:text-blue-400" />
122
- </div>
123
- <div>
124
- <div className="text-sm font-medium text-slate-200 group-hover:text-white">{layer.label}</div>
125
- <div className="text-xs text-slate-500 leading-tight group-hover:text-slate-400 line-clamp-2">{layer.description}</div>
 
 
 
126
  </div>
127
  </div>
 
 
 
 
128
  </div>
129
- ))
130
- ) : (
131
- <div className="text-center text-slate-500 py-8 text-xs italic">
132
- No layers found matching "{searchQuery}"
133
- </div>
134
- )}
135
- </div>
136
- ) : (
137
- // Categorized View
138
- categories.map(category => (
139
- <div key={category}>
140
- <h3 className="text-xs font-semibold text-slate-500 uppercase tracking-widest mb-3 flex items-center gap-2">
141
- {category}
142
- <div className="h-px flex-1 bg-slate-800"></div>
143
- </h3>
144
- <div className="grid grid-cols-1 gap-2">
145
- {Object.values(LAYER_DEFINITIONS)
146
- .filter(l => l.category === category)
147
- .map(layer => (
148
- <div
149
- key={layer.type}
150
- className="bg-slate-800 hover:bg-slate-750 p-3 rounded border border-slate-700 cursor-grab active:cursor-grabbing transition-colors group relative overflow-hidden"
151
- onDragStart={(event) => onDragStart(event, layer.type)}
152
- draggable
153
- >
154
- <div className="flex items-center gap-3 relative z-10">
155
- <div className={`p-1.5 rounded transition-colors group-hover:bg-slate-900 bg-slate-900/50`}>
156
- <Box size={14} className="text-slate-400 group-hover:text-blue-400" />
157
- </div>
158
- <div>
159
- <div className="text-sm font-medium text-slate-200 group-hover:text-white">{layer.label}</div>
160
- <div className="text-xs text-slate-500 leading-tight group-hover:text-slate-400">{layer.description}</div>
161
  </div>
162
  </div>
163
- </div>
164
- ))}
165
  </div>
166
- </div>
167
- ))
168
- )}
169
- </div>
170
-
171
- <div className="p-4 border-t border-slate-800 text-[10px] text-slate-500 text-center flex items-center justify-center gap-2 min-w-[16rem]">
172
- <span>v1.2.0 Powered by Gemini 2.5</span>
173
- <Circle size={8} className={isConnected ? "fill-emerald-500 text-emerald-500" : "fill-red-500 text-red-500"} />
174
  </div>
175
- </div>
176
- </aside>
177
  );
178
  };
179
 
 
1
 
2
+ import React from 'react';
3
  import { LAYER_DEFINITIONS } from '../constants';
4
  import { LayerType } from '../types';
5
  import { Box, Sparkles, LayoutTemplate, Circle, Search, X, ChevronLeft, ChevronRight } from 'lucide-react';
 
8
  onOpenAIBuilder: () => void;
9
  onSelectTemplate: (templateId: string) => void;
10
  isConnected: boolean;
11
+ isOpen: boolean;
12
+ onToggle: () => void;
13
  }
14
 
15
+ const Sidebar: React.FC<SidebarProps> = ({ onOpenAIBuilder, onSelectTemplate, isConnected, isOpen, onToggle }) => {
16
+ const [searchQuery, setSearchQuery] = React.useState('');
 
17
 
18
  const onDragStart = (event: React.DragEvent, layerType: LayerType) => {
19
  event.dataTransfer.setData('application/reactflow', layerType);
 
43
  );
44
 
45
  return (
46
+ <>
47
+ {/* Mobile Backdrop */}
48
+ {isOpen && (
49
+ <div
50
+ className="fixed inset-0 bg-black/60 z-30 md:hidden backdrop-blur-sm transition-opacity"
51
+ onClick={onToggle}
52
+ />
53
+ )}
 
54
 
55
+ {/* Sidebar Container */}
56
+ <aside className={`
57
+ fixed inset-y-0 left-0 z-40 bg-slate-900 border-r border-slate-800 flex flex-col h-full shadow-2xl transition-all duration-300
58
+ ${isOpen ? 'translate-x-0 w-64' : '-translate-x-full w-64 md:translate-x-0 md:w-0 md:border-none'}
59
+ md:relative md:shadow-none
60
+ `}>
61
+
62
+ {/* Desktop Toggle Button (Chevron) */}
63
+ <button
64
+ onClick={onToggle}
65
+ className={`
66
+ hidden md:flex absolute -right-3 top-6 bg-slate-800 border border-slate-700 text-slate-400 p-0.5 rounded-full hover:text-white hover:bg-slate-700 cursor-pointer z-50 w-6 h-6 items-center justify-center shadow-md transition-opacity duration-300
67
+ ${isOpen ? 'opacity-100' : 'opacity-100 translate-x-3'}
68
+ `}
69
+ title={isOpen ? "Collapse Sidebar" : "Expand Sidebar"}
70
+ >
71
+ {isOpen ? <ChevronLeft size={14} /> : <ChevronRight size={14} />}
72
+ </button>
 
 
 
 
 
 
 
 
73
 
74
+ {/* Content Container */}
75
+ <div className={`flex flex-col h-full overflow-hidden whitespace-nowrap ${!isOpen ? 'md:opacity-0 md:invisible' : 'opacity-100 visible'} transition-all duration-200`}>
76
+ <div className="p-4 border-b border-slate-800 bg-slate-900 space-y-4 min-w-[16rem]">
77
+ <div className="flex justify-between items-start">
78
+ <div>
79
+ <h1 className="text-xl font-bold bg-gradient-to-r from-blue-400 to-violet-400 bg-clip-text text-transparent">
80
+ Architecture Agents
81
+ </h1>
82
+ <p className="text-xs text-slate-500 mt-1">Visual AI Architect</p>
83
+ </div>
84
+ {/* Mobile Close Button */}
85
+ <button onClick={onToggle} className="md:hidden text-slate-500 hover:text-white">
86
+ <X size={20} />
87
+ </button>
88
+ </div>
89
+
90
+ <div className="grid grid-cols-2 gap-2">
91
  <button
92
+ onClick={onOpenAIBuilder}
93
+ className="flex flex-col items-center justify-center p-2 bg-purple-500/10 hover:bg-purple-500/20 border border-purple-500/30 rounded-lg text-purple-300 transition-colors group"
94
  >
95
+ <Sparkles size={18} className="mb-1 group-hover:scale-110 transition-transform" />
96
+ <span className="text-[10px] font-bold">AI Builder</span>
97
  </button>
98
+ <button
99
+ onClick={() => onSelectTemplate('menu')}
100
+ className="flex flex-col items-center justify-center p-2 bg-blue-500/10 hover:bg-blue-500/20 border border-blue-500/30 rounded-lg text-blue-300 transition-colors group"
101
+ >
102
+ <LayoutTemplate size={18} className="mb-1 group-hover:scale-110 transition-transform" />
103
+ <span className="text-[10px] font-bold">Templates</span>
104
+ </button>
105
+ </div>
106
+
107
+ {/* Search Bar */}
108
+ <div className="relative">
109
+ <Search className="absolute left-3 top-2.5 text-slate-500" size={14} />
110
+ <input
111
+ type="text"
112
+ placeholder="Search layers..."
113
+ className="w-full bg-slate-950 border border-slate-700 rounded-lg pl-9 pr-8 py-2 text-xs text-slate-200 focus:outline-none focus:border-blue-500 transition-colors placeholder-slate-600"
114
+ value={searchQuery}
115
+ onChange={(e) => setSearchQuery(e.target.value)}
116
+ />
117
+ {searchQuery && (
118
+ <button
119
+ onClick={() => setSearchQuery('')}
120
+ className="absolute right-2 top-2 text-slate-500 hover:text-slate-300"
121
+ >
122
+ <X size={14} />
123
+ </button>
124
+ )}
125
+ </div>
126
  </div>
 
 
 
127
 
128
+ <div className="flex-1 overflow-y-auto p-4 space-y-6 scrollbar-thin scrollbar-thumb-slate-700 min-w-[16rem]">
129
+
130
+ {searchQuery ? (
131
+ // Search Results View
132
+ <div className="space-y-2">
133
+ <h3 className="text-xs font-semibold text-slate-500 uppercase tracking-widest mb-3">
134
+ Search Results ({filteredLayers.length})
135
+ </h3>
136
+ {filteredLayers.length > 0 ? (
137
+ filteredLayers.map(layer => (
138
+ <div
139
+ key={layer.type}
140
+ className="bg-slate-800 hover:bg-slate-750 p-3 rounded border border-slate-700 cursor-grab active:cursor-grabbing transition-colors group relative overflow-hidden"
141
+ onDragStart={(event) => onDragStart(event, layer.type)}
142
+ draggable
143
+ >
144
+ <div className="flex items-center gap-3 relative z-10">
145
+ <div className={`p-1.5 rounded transition-colors group-hover:bg-slate-900 bg-slate-900/50`}>
146
+ <Box size={14} className="text-slate-400 group-hover:text-blue-400" />
147
+ </div>
148
+ <div>
149
+ <div className="text-sm font-medium text-slate-200 group-hover:text-white">{layer.label}</div>
150
+ <div className="text-xs text-slate-500 leading-tight group-hover:text-slate-400 line-clamp-2">{layer.description}</div>
151
+ </div>
152
  </div>
153
  </div>
154
+ ))
155
+ ) : (
156
+ <div className="text-center text-slate-500 py-8 text-xs italic">
157
+ No layers found matching "{searchQuery}"
158
  </div>
159
+ )}
160
+ </div>
161
+ ) : (
162
+ // Categorized View
163
+ categories.map(category => (
164
+ <div key={category}>
165
+ <h3 className="text-xs font-semibold text-slate-500 uppercase tracking-widest mb-3 flex items-center gap-2">
166
+ {category}
167
+ <div className="h-px flex-1 bg-slate-800"></div>
168
+ </h3>
169
+ <div className="grid grid-cols-1 gap-2">
170
+ {Object.values(LAYER_DEFINITIONS)
171
+ .filter(l => l.category === category)
172
+ .map(layer => (
173
+ <div
174
+ key={layer.type}
175
+ className="bg-slate-800 hover:bg-slate-750 p-3 rounded border border-slate-700 cursor-grab active:cursor-grabbing transition-colors group relative overflow-hidden"
176
+ onDragStart={(event) => onDragStart(event, layer.type)}
177
+ draggable
178
+ >
179
+ <div className="flex items-center gap-3 relative z-10">
180
+ <div className={`p-1.5 rounded transition-colors group-hover:bg-slate-900 bg-slate-900/50`}>
181
+ <Box size={14} className="text-slate-400 group-hover:text-blue-400" />
182
+ </div>
183
+ <div>
184
+ <div className="text-sm font-medium text-slate-200 group-hover:text-white">{layer.label}</div>
185
+ <div className="text-xs text-slate-500 leading-tight group-hover:text-slate-400">{layer.description}</div>
186
+ </div>
 
 
 
 
187
  </div>
188
  </div>
189
+ ))}
190
+ </div>
191
  </div>
192
+ ))
193
+ )}
194
+ </div>
195
+
196
+ <div className="p-4 border-t border-slate-800 text-[10px] text-slate-500 text-center flex items-center justify-center gap-2 min-w-[16rem]">
197
+ <span>v1.2.0 Powered by Gemini 2.5</span>
198
+ <Circle size={8} className={isConnected ? "fill-emerald-500 text-emerald-500" : "fill-red-500 text-red-500"} />
199
+ </div>
200
  </div>
201
+ </aside>
202
+ </>
203
  );
204
  };
205