Skip to content

Commit d79cad4

Browse files
committed
feat(sidebar-controls): added ability to expand on hover (#343)
1 parent a92ee8b commit d79cad4

File tree

11 files changed

+301
-72
lines changed

11 files changed

+301
-72
lines changed

apps/sim/app/api/workflows/[id]/status/route.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,7 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
2626
validation.workflow.deployedState as any
2727
)
2828
}
29-
30-
logger.info(`[${requestId}] Retrieved status for workflow: ${id}`, {
31-
isDeployed: validation.workflow.isDeployed,
32-
isPublished: validation.workflow.isPublished,
33-
needsRedeployment,
34-
})
35-
29+
3630
return createSuccessResponse({
3731
isDeployed: validation.workflow.isDeployed,
3832
deployedAt: validation.workflow.deployedAt,

apps/sim/app/globals.css

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@
140140

141141
/* Custom Animations */
142142
@layer utilities {
143+
143144
/* Animation containment to avoid layout shifts */
144145
.animation-container {
145146
contain: paint layout style;
@@ -150,9 +151,11 @@
150151
0% {
151152
box-shadow: 0 0 0 0 hsl(var(--border));
152153
}
154+
153155
50% {
154156
box-shadow: 0 0 0 8px hsl(var(--border));
155157
}
158+
156159
100% {
157160
box-shadow: 0 0 0 0 hsl(var(--border));
158161
}
@@ -195,34 +198,37 @@
195198
0% {
196199
transform: translateX(-100%);
197200
}
201+
198202
100% {
199203
transform: translateX(100%);
200204
}
201205
}
202206

203207
@keyframes orbit {
204208
0% {
205-
transform: rotate(calc(var(--angle) * 1deg)) translateY(calc(var(--radius) * 1px))
206-
rotate(calc(var(--angle) * -1deg));
209+
transform: rotate(calc(var(--angle) * 1deg)) translateY(calc(var(--radius) * 1px)) rotate(calc(var(--angle) * -1deg));
207210
}
211+
208212
100% {
209-
transform: rotate(calc(var(--angle) * 1deg + 360deg)) translateY(calc(var(--radius) * 1px))
210-
rotate(calc((var(--angle) * -1deg) - 360deg));
213+
transform: rotate(calc(var(--angle) * 1deg + 360deg)) translateY(calc(var(--radius) * 1px)) rotate(calc((var(--angle) * -1deg) - 360deg));
211214
}
212215
}
213216

214217
@keyframes marquee {
215218
from {
216219
transform: translateX(0);
217220
}
221+
218222
to {
219223
transform: translateX(calc(-100% - var(--gap)));
220224
}
221225
}
226+
222227
@keyframes marquee-vertical {
223228
from {
224229
transform: translateY(0);
225230
}
231+
226232
to {
227233
transform: translateY(calc(-100% - var(--gap)));
228234
}
@@ -234,30 +240,27 @@
234240

235241
.streaming-effect::after {
236242
content: '';
237-
@apply pointer-events-none absolute top-0 left-0 h-full w-full;
238-
background: linear-gradient(
239-
90deg,
240-
rgba(128, 128, 128, 0) 0%,
241-
rgba(128, 128, 128, 0.1) 50%,
242-
rgba(128, 128, 128, 0) 100%
243-
);
243+
@apply pointer-events-none absolute left-0 top-0 h-full w-full;
244+
background: linear-gradient(90deg,
245+
rgba(128, 128, 128, 0) 0%,
246+
rgba(128, 128, 128, 0.1) 50%,
247+
rgba(128, 128, 128, 0) 100%);
244248
animation: code-shimmer 1.5s infinite;
245249
z-index: 10;
246250
}
247251

248252
.dark .streaming-effect::after {
249-
background: linear-gradient(
250-
90deg,
251-
rgba(180, 180, 180, 0) 0%,
252-
rgba(180, 180, 180, 0.1) 50%,
253-
rgba(180, 180, 180, 0) 100%
254-
);
253+
background: linear-gradient(90deg,
254+
rgba(180, 180, 180, 0) 0%,
255+
rgba(180, 180, 180, 0.1) 50%,
256+
rgba(180, 180, 180, 0) 100%);
255257
}
256258

257259
@keyframes fadeIn {
258260
from {
259261
opacity: 0;
260262
}
263+
261264
to {
262265
opacity: 1;
263266
}
@@ -270,8 +273,10 @@
270273

271274
/* Dark mode error badge styling */
272275
.dark .error-badge {
273-
background-color: hsl(0, 70%, 20%) !important; /* Darker red background for dark mode */
274-
color: hsl(0, 0%, 100%) !important; /* Pure white text for better contrast */
276+
background-color: hsl(0, 70%, 20%) !important;
277+
/* Darker red background for dark mode */
278+
color: hsl(0, 0%, 100%) !important;
279+
/* Pure white text for better contrast */
275280
}
276281

277282
/* Input Overrides */
@@ -293,10 +298,12 @@ input[type='search']::-ms-clear {
293298

294299
/* Code Prompt Bar Placeholder Animation */
295300
@keyframes placeholder-pulse {
301+
296302
0%,
297303
100% {
298304
opacity: 0.5;
299305
}
306+
300307
50% {
301308
opacity: 0.8;
302309
}
@@ -319,3 +326,9 @@ input[type='search']::-ms-clear {
319326
.font-geist-mono {
320327
font-family: var(--font-geist-mono);
321328
}
329+
330+
/* Sidebar overlay styles */
331+
.main-content-overlay {
332+
z-index: 40;
333+
/* Higher z-index to appear above content */
334+
}

apps/sim/app/w/[id]/components/control-bar/control-bar.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ const RUN_COUNT_OPTIONS = [1, 5, 10, 25, 50, 100]
7676
export function ControlBar() {
7777
const router = useRouter()
7878
const { data: session } = useSession()
79-
const { isCollapsed: isSidebarCollapsed } = useSidebarStore()
8079

8180
// Store hooks
8281
const {

apps/sim/app/w/[id]/components/toolbar/toolbar.tsx

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,13 @@ import { ToolbarTabs } from './components/toolbar-tabs/toolbar-tabs'
1414
export function Toolbar() {
1515
const [activeTab, setActiveTab] = useState<BlockCategory>('blocks')
1616
const [searchQuery, setSearchQuery] = useState('')
17-
const [isCollapsed, setIsCollapsed] = useState(false)
18-
const { isCollapsed: isSidebarCollapsed } = useSidebarStore()
17+
const { mode, isExpanded } = useSidebarStore()
18+
// In hover mode, act as if sidebar is always collapsed for layout purposes
19+
const isSidebarCollapsed =
20+
mode === 'expanded' ? !isExpanded : mode === 'collapsed' || mode === 'hover'
21+
22+
// State to track if toolbar is open - independent of sidebar state
23+
const [isToolbarOpen, setIsToolbarOpen] = useState(true)
1924

2025
const blocks = useMemo(() => {
2126
const filteredBlocks = !searchQuery.trim() ? getBlocksByCategory(activeTab) : getAllBlocks()
@@ -31,13 +36,14 @@ export function Toolbar() {
3136
})
3237
}, [searchQuery, activeTab])
3338

34-
if (isCollapsed) {
39+
// Show toolbar button when it's closed, regardless of sidebar state
40+
if (!isToolbarOpen) {
3541
return (
3642
<Tooltip>
3743
<TooltipTrigger asChild>
3844
<button
39-
onClick={() => setIsCollapsed(false)}
40-
className={`fixed transition-left duration-200 ${isSidebarCollapsed ? 'left-20' : 'left-64'} bottom-[18px] z-10 flex h-9 w-9 items-center justify-center rounded-lg bg-background text-muted-foreground hover:text-foreground hover:bg-accent border`}
45+
onClick={() => setIsToolbarOpen(true)}
46+
className={`fixed transition-all duration-200 ${isSidebarCollapsed ? 'left-20' : 'left-64'} bottom-[18px] z-10 flex h-9 w-9 items-center justify-center rounded-lg bg-background text-muted-foreground hover:text-foreground hover:bg-accent border`}
4147
>
4248
<PanelRight className="h-5 w-5" />
4349
<span className="sr-only">Open Toolbar</span>
@@ -50,7 +56,7 @@ export function Toolbar() {
5056

5157
return (
5258
<div
53-
className={`fixed transition-left duration-200 ${isSidebarCollapsed ? 'left-14' : 'left-60'} top-16 z-10 h-[calc(100vh-4rem)] w-60 border-r bg-background sm:block`}
59+
className={`fixed transition-all duration-200 ${isSidebarCollapsed ? 'left-14' : 'left-60'} top-16 z-10 h-[calc(100vh-4rem)] w-60 border-r bg-background sm:block`}
5460
>
5561
<div className="flex flex-col h-full">
5662
<div className="px-4 pt-4 pb-1 sticky top-0 bg-background z-20">
@@ -89,7 +95,7 @@ export function Toolbar() {
8995
<Tooltip>
9096
<TooltipTrigger asChild>
9197
<button
92-
onClick={() => setIsCollapsed(true)}
98+
onClick={() => setIsToolbarOpen(false)}
9399
className="absolute right-4 bottom-[18px] flex h-9 w-9 items-center justify-center rounded-lg text-muted-foreground hover:text-foreground hover:bg-accent"
94100
>
95101
<PanelLeftClose className="h-5 w-5" />

apps/sim/app/w/[id]/workflow.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ function WorkflowContent() {
4848
// State
4949
const [selectedEdgeId, setSelectedEdgeId] = useState<string | null>(null)
5050
const [isInitialized, setIsInitialized] = useState(false)
51-
const { isCollapsed: isSidebarCollapsed } = useSidebarStore()
51+
const { mode, isExpanded } = useSidebarStore()
52+
// In hover mode, act as if sidebar is always collapsed for layout purposes
53+
const isSidebarCollapsed =
54+
mode === 'expanded' ? !isExpanded : mode === 'collapsed' || mode === 'hover'
5255

5356
// Hooks
5457
const params = useParams()
@@ -478,8 +481,8 @@ function WorkflowContent() {
478481
<div className="flex flex-col h-screen w-full overflow-hidden">
479482
<div className={`transition-all duration-200 ${isSidebarCollapsed ? 'ml-14' : 'ml-60'}`}>
480483
<ControlBar />
481-
<Toolbar />
482484
</div>
485+
<Toolbar />
483486
<div
484487
className={`flex-1 relative w-full h-full transition-all duration-200 ${isSidebarCollapsed ? 'pl-14' : 'pl-60'}`}
485488
>
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
'use client'
2+
3+
import { useState } from 'react'
4+
import { PanelRight } from 'lucide-react'
5+
import { Button } from '@/components/ui/button'
6+
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
7+
import { cn } from '@/lib/utils'
8+
import { SidebarMode, useSidebarStore } from '@/stores/sidebar/store'
9+
10+
// This component ONLY controls sidebar state, not toolbar state
11+
export function SidebarControl() {
12+
const { mode, setMode, toggleExpanded, isExpanded } = useSidebarStore()
13+
const [open, setOpen] = useState(false)
14+
15+
const handleModeChange = (value: SidebarMode) => {
16+
// When selecting expanded mode, ensure it's expanded
17+
if (value === 'expanded' && !isExpanded) {
18+
toggleExpanded()
19+
}
20+
21+
// Set the new mode
22+
setMode(value)
23+
setOpen(false)
24+
}
25+
26+
return (
27+
<Popover open={open} onOpenChange={setOpen}>
28+
<PopoverTrigger asChild>
29+
<Button
30+
variant="ghost"
31+
size="icon"
32+
className="flex h-8 w-8 p-0 items-center justify-center rounded-md text-muted-foreground hover:bg-accent/50 cursor-pointer"
33+
>
34+
<PanelRight className="h-[18px] w-[18px] text-muted-foreground" />
35+
<span className="sr-only text-sm">Sidebar control</span>
36+
</Button>
37+
</PopoverTrigger>
38+
<PopoverContent
39+
className="w-44 p-0 shadow-md border overflow-hidden rounded-lg bg-background"
40+
side="top"
41+
align="start"
42+
sideOffset={5}
43+
>
44+
<div className="border-b py-[10px] px-4">
45+
<h4 className="text-xs font-[480] text-muted-foreground">Sidebar control</h4>
46+
</div>
47+
<div className="px-2 pt-1 pb-2">
48+
<div className="flex flex-col gap-[1px]">
49+
<button
50+
className={cn(
51+
'w-full text-left py-1.5 px-2 text-xs rounded hover:bg-accent/50 text-muted-foreground font-medium'
52+
)}
53+
onClick={() => handleModeChange('expanded')}
54+
>
55+
<span className="flex items-center">
56+
<span
57+
className={cn(
58+
'h-1 w-1 rounded-full mr-1.5',
59+
mode === 'expanded' ? 'bg-muted-foreground' : 'bg-transparent'
60+
)}
61+
></span>
62+
Expanded
63+
</span>
64+
</button>
65+
<button
66+
className={cn(
67+
'w-full text-left py-1.5 px-2 text-xs rounded hover:bg-accent/50 text-muted-foreground font-medium'
68+
)}
69+
onClick={() => handleModeChange('collapsed')}
70+
>
71+
<span className="flex items-center">
72+
<span
73+
className={cn(
74+
'h-1 w-1 rounded-full mr-1.5',
75+
mode === 'collapsed' ? 'bg-muted-foreground' : 'bg-transparent'
76+
)}
77+
></span>
78+
Collapsed
79+
</span>
80+
</button>
81+
<button
82+
className={cn(
83+
'w-full text-left py-1.5 px-2 text-xs rounded hover:bg-accent/50 text-muted-foreground font-medium'
84+
)}
85+
onClick={() => handleModeChange('hover')}
86+
>
87+
<span className="flex items-center">
88+
<span
89+
className={cn(
90+
'h-1 w-1 rounded-full mr-1.5',
91+
mode === 'hover' ? 'bg-muted-foreground' : 'bg-transparent'
92+
)}
93+
></span>
94+
Expand on hover
95+
</span>
96+
</button>
97+
</div>
98+
</div>
99+
</PopoverContent>
100+
</Popover>
101+
)
102+
}

0 commit comments

Comments
 (0)