@@ -18,8 +18,8 @@ import {
1818} from "@xyflow/react" ;
1919import {
2020 ArrowUpToLine ,
21- ChevronDown ,
22- ChevronUp ,
21+ Eye ,
22+ EyeOff ,
2323 PanelLeft ,
2424 PanelLeftClose ,
2525 Play ,
@@ -98,6 +98,53 @@ function CanvasButton({
9898 ) ;
9999}
100100
101+ interface ActionBarButtonProps {
102+ onClick : ( e : React . MouseEvent ) => void ;
103+ disabled ?: boolean ;
104+ className ?: string ;
105+ tooltip : string ;
106+ children : React . ReactNode ;
107+ position ?: "first" | "middle" | "last" | "only" ;
108+ }
109+
110+ function ActionBarButton ( {
111+ onClick,
112+ disabled = false ,
113+ className = "" ,
114+ tooltip,
115+ children,
116+ position = "middle" ,
117+ } : ActionBarButtonProps ) {
118+ const roundingClass = {
119+ first : "rounded-l-lg rounded-r-none" ,
120+ middle : "rounded-none border-l-0" ,
121+ last : "rounded-r-lg rounded-l-none border-l-0" ,
122+ only : "rounded-lg" ,
123+ } [ position ] ;
124+
125+ return (
126+ < Tooltip >
127+ < TooltipTrigger asChild >
128+ < Button
129+ onClick = { onClick }
130+ disabled = { disabled }
131+ className = { cn (
132+ "h-10 px-3 border-neutral-300 shadow-sm" ,
133+ roundingClass ,
134+ className ,
135+ { "opacity-50 cursor-not-allowed" : disabled }
136+ ) }
137+ >
138+ { children }
139+ </ Button >
140+ </ TooltipTrigger >
141+ < TooltipContent >
142+ < p > { tooltip } </ p >
143+ </ TooltipContent >
144+ </ Tooltip >
145+ ) ;
146+ }
147+
101148export interface WorkflowCanvasProps {
102149 nodes : ReactFlowNode < WorkflowNodeType > [ ] ;
103150 edges : ReactFlowEdge < WorkflowEdgeType > [ ] ;
@@ -148,55 +195,57 @@ function ActionButton({
148195} : ActionButtonProps ) {
149196 const statusConfig = {
150197 idle : {
151- icon : < Play className = "!size-5 " /> ,
198+ icon : < Play className = "!size-4 " /> ,
152199 title : "Execute Workflow" ,
153- className : "bg-green-600 hover:bg-green-700 text-white" ,
200+ className : "bg-green-600 hover:bg-green-700 text-white border-green-600 " ,
154201 } ,
155202 submitted : {
156- icon : < Square className = "!size-5 " /> ,
203+ icon : < Square className = "!size-4 " /> ,
157204 title : "Stop Execution" ,
158- className : "bg-red-500 hover:bg-red-600 text-white" ,
205+ className : "bg-red-500 hover:bg-red-600 text-white border-red-500 " ,
159206 } ,
160207 executing : {
161- icon : < Square className = "!size-5 " /> ,
208+ icon : < Square className = "!size-4 " /> ,
162209 title : "Stop Execution" ,
163- className : "bg-red-500 hover:bg-red-600 text-white" ,
210+ className : "bg-red-500 hover:bg-red-600 text-white border-red-500 " ,
164211 } ,
165212 completed : {
166- icon : < X className = "!size-5 " /> ,
213+ icon : < X className = "!size-4 " /> ,
167214 title : "Clear Outputs & Reset" ,
168- className : "bg-emerald-600 hover:bg-emerald-700 text-white" ,
215+ className :
216+ "bg-emerald-600 hover:bg-emerald-700 text-white border-emerald-600" ,
169217 } ,
170218 error : {
171- icon : < X className = "!size-5 " /> ,
219+ icon : < X className = "!size-4 " /> ,
172220 title : "Clear Errors & Reset" ,
173- className : "bg-rose-600 hover:bg-rose-700 text-white" ,
221+ className : "bg-rose-600 hover:bg-rose-700 text-white border-rose-600 " ,
174222 } ,
175223 cancelled : {
176- icon : < Play className = "!size-5 " /> ,
224+ icon : < Play className = "!size-4 " /> ,
177225 title : "Restart Workflow" ,
178- className : "bg-neutral-600 hover:bg-neutral-700 text-white" ,
226+ className :
227+ "bg-neutral-600 hover:bg-neutral-700 text-white border-neutral-600" ,
179228 } ,
180229 paused : {
181- icon : < Play className = "!size-5 " /> ,
230+ icon : < Play className = "!size-4 " /> ,
182231 title : "Resume Workflow" ,
183- className : "bg-blue-500 hover:bg-blue-600 text-white" ,
232+ className : "bg-blue-500 hover:bg-blue-600 text-white border-blue-500 " ,
184233 } ,
185234 } ;
186235
187236 // Use a default config if the status isn't in our mapping
188237 const config = statusConfig [ workflowStatus ] || statusConfig . idle ;
189238
190239 return (
191- < CanvasButton
240+ < ActionBarButton
192241 onClick = { onClick }
193242 disabled = { disabled }
194243 className = { config . className }
195- position = "right-28"
196244 tooltip = { config . title }
245+ position = "first"
197246 >
198247 { config . icon }
199- </ CanvasButton >
248+ </ ActionBarButton >
200249 ) ;
201250}
202251
@@ -208,15 +257,15 @@ function DeployButton({
208257 disabled ?: boolean ;
209258} ) {
210259 return (
211- < CanvasButton
260+ < ActionBarButton
212261 onClick = { onClick }
213262 disabled = { disabled }
214- className = "bg-blue-600 hover:bg-blue-700 text-white"
215- position = "right-16"
263+ className = "bg-blue-600 hover:bg-blue-700 text-white border-blue-600"
216264 tooltip = "Deploy Workflow"
265+ position = "middle"
217266 >
218- < ArrowUpToLine className = "!size-5 " />
219- </ CanvasButton >
267+ < ArrowUpToLine className = "!size-4 " />
268+ </ ActionBarButton >
220269 ) ;
221270}
222271
@@ -227,17 +276,18 @@ type SidebarToggleProps = {
227276
228277function SidebarToggle ( { onClick, isSidebarVisible } : SidebarToggleProps ) {
229278 return (
230- < CanvasButton
279+ < ActionBarButton
231280 onClick = { onClick }
232- position = "right-4"
233281 tooltip = { isSidebarVisible ? "Hide Sidebar" : "Show Sidebar" }
282+ className = "bg-neutral-100 hover:bg-neutral-200 text-neutral-700 border-neutral-300"
283+ position = "last"
234284 >
235285 { isSidebarVisible ? (
236- < PanelLeftClose className = "!size-5 rotate-180" />
286+ < PanelLeftClose className = "!size-4 rotate-180" />
237287 ) : (
238- < PanelLeft className = "!size-5 rotate-180" />
288+ < PanelLeft className = "!size-4 rotate-180" />
239289 ) }
240- </ CanvasButton >
290+ </ ActionBarButton >
241291 ) ;
242292}
243293
@@ -251,25 +301,25 @@ function OutputsToggle({
251301 disabled ?: boolean ;
252302} ) {
253303 return (
254- < CanvasButton
304+ < ActionBarButton
255305 onClick = { onClick }
256306 disabled = { disabled }
257- className = "bg-neutral-600 hover:bg-neutral-700 text-white"
258- position = "right-40"
307+ className = "bg-neutral-600 hover:bg-neutral-700 text-white border-neutral-600"
259308 tooltip = {
260309 disabled
261310 ? "No outputs to show"
262311 : expandedOutputs
263- ? "Collapse All Outputs"
264- : "Expand All Outputs"
312+ ? "Hide All Outputs"
313+ : "Show All Outputs"
265314 }
315+ position = "middle"
266316 >
267317 { expandedOutputs ? (
268- < ChevronUp className = "!size-5 " />
318+ < EyeOff className = "!size-4 " />
269319 ) : (
270- < ChevronDown className = "!size-5 " />
320+ < Eye className = "!size-4 " />
271321 ) }
272- </ CanvasButton >
322+ </ ActionBarButton >
273323 ) ;
274324}
275325
@@ -353,6 +403,45 @@ export function WorkflowCanvas({
353403 </ div >
354404 ) }
355405
406+ { /* Main Action Bar */ }
407+ { ! readonly &&
408+ ( onAction ||
409+ onDeploy ||
410+ onToggleExpandedOutputs ||
411+ onToggleSidebar ) && (
412+ < div className = "absolute top-4 right-4 flex items-center rounded-lg shadow-lg bg-background z-50 overflow-hidden" >
413+ { onAction && (
414+ < ActionButton
415+ onClick = { onAction }
416+ workflowStatus = { workflowStatus }
417+ disabled = { nodes . length === 0 }
418+ />
419+ ) }
420+
421+ { onToggleExpandedOutputs && (
422+ < OutputsToggle
423+ onClick = { onToggleExpandedOutputs }
424+ expandedOutputs = { expandedOutputs }
425+ disabled = { ! hasAnyOutputs }
426+ />
427+ ) }
428+
429+ { onDeploy && (
430+ < DeployButton
431+ onClick = { onDeploy }
432+ disabled = { nodes . length === 0 }
433+ />
434+ ) }
435+
436+ { onToggleSidebar && isSidebarVisible !== undefined && (
437+ < SidebarToggle
438+ onClick = { onToggleSidebar }
439+ isSidebarVisible = { isSidebarVisible }
440+ />
441+ ) }
442+ </ div >
443+ ) }
444+
356445 { onAddNode && ! readonly && (
357446 < CanvasButton
358447 onClick = { ( e ) => {
@@ -366,33 +455,6 @@ export function WorkflowCanvas({
366455 < Plus className = "!size-5" />
367456 </ CanvasButton >
368457 ) }
369-
370- { onToggleExpandedOutputs && (
371- < OutputsToggle
372- onClick = { onToggleExpandedOutputs }
373- expandedOutputs = { expandedOutputs }
374- disabled = { ! hasAnyOutputs }
375- />
376- ) }
377-
378- { onAction && ! readonly && (
379- < ActionButton
380- onClick = { onAction }
381- workflowStatus = { workflowStatus }
382- disabled = { nodes . length === 0 }
383- />
384- ) }
385-
386- { onDeploy && ! readonly && (
387- < DeployButton onClick = { onDeploy } disabled = { nodes . length === 0 } />
388- ) }
389-
390- { onToggleSidebar && isSidebarVisible !== undefined && (
391- < SidebarToggle
392- onClick = { onToggleSidebar }
393- isSidebarVisible = { isSidebarVisible }
394- />
395- ) }
396458 </ ReactFlow >
397459 </ TooltipProvider >
398460 ) ;
0 commit comments