Skip to content

Commit 5d8cb2f

Browse files
committed
feat(sidebar): created new sidebar with header and workflow list
1 parent 2b5810f commit 5d8cb2f

File tree

8 files changed

+1155
-38
lines changed

8 files changed

+1155
-38
lines changed

apps/sim/app/globals.css

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,27 @@
22
@tailwind components;
33
@tailwind utilities;
44

5+
/* ==========================================================================
6+
SIDEBAR WIDTH PERSISTENCE
7+
========================================================================== */
8+
/**
9+
* CSS-based sidebar width to prevent SSR hydration mismatches.
10+
*
11+
* How it works:
12+
* 1. Default width set here in CSS (232px)
13+
* 2. Blocking script in layout.tsx updates this before React hydrates
14+
* 3. Store updates this variable when user resizes
15+
*
16+
* This approach ensures server and client always render identical HTML.
17+
*/
18+
:root {
19+
--sidebar-width: 232px;
20+
}
21+
22+
.sidebar-container {
23+
width: var(--sidebar-width);
24+
}
25+
526
/* ==========================================================================
627
WORKFLOW COMPONENT Z-INDEX FIXES
728
========================================================================== */
@@ -110,7 +131,6 @@
110131

111132
/* Workflow Properties */
112133
--workflow-background: 0 0% 100%;
113-
--workflow-dots: 0 0% 94.5%;
114134
--card-background: 0 0% 99.2%;
115135
--card-border: 0 0% 89.8%;
116136
--card-text: 0 0% 3.9%;
@@ -179,8 +199,7 @@
179199
--scrollbar-thumb-hover: 0 0% 40%;
180200

181201
/* Workflow Properties */
182-
--workflow-background: 0 0% 3.9%;
183-
--workflow-dots: 0 0% 7.5%;
202+
--workflow-background: 0 0% 10.6%;
184203
--card-background: 0 0% 9.0%;
185204
--card-border: 0 0% 22.7%;
186205
--card-text: 0 0% 98%;
@@ -215,14 +234,16 @@
215234
}
216235

217236
body {
218-
@apply bg-background text-foreground;
237+
@apply bg-background text-foreground antialiased;
219238
overscroll-behavior-x: none;
220239
overscroll-behavior-y: none;
221240
min-height: 100vh;
222241
/* Prevent layout shifts when scrollbar appears/disappears */
223242
scrollbar-gutter: stable;
224243
/* Improve animation performance */
225244
text-rendering: optimizeSpeed;
245+
/* Default text styles */
246+
letter-spacing: 0.28px;
226247
}
227248

228249
/* Global Scrollbar Styling */
@@ -251,17 +272,6 @@
251272
}
252273
}
253274

254-
/* ==========================================================================
255-
TYPOGRAPHY
256-
========================================================================== */
257-
.font-geist-sans {
258-
font-family: var(--font-geist-sans);
259-
}
260-
261-
.font-geist-mono {
262-
font-family: var(--font-geist-mono);
263-
}
264-
265275
/* ==========================================================================
266276
PANEL STYLES
267277
========================================================================== */

apps/sim/app/workspace/[workspaceId]/layout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import Providers from '@/app/workspace/[workspaceId]/providers/providers'
2-
import { Sidebar } from '@/app/workspace/[workspaceId]/w/components/sidebar/sidebar'
2+
import { SidebarNew } from '@/app/workspace/[workspaceId]/w/components/sidebar/sidebar-new'
33

44
export default function WorkspaceLayout({ children }: { children: React.ReactNode }) {
55
return (
66
<Providers>
77
<div className='flex min-h-screen w-full'>
88
<div className='z-20'>
9-
<Sidebar />
9+
<SidebarNew />
1010
</div>
1111
<div className='flex flex-1 flex-col'>{children}</div>
1212
</div>

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
44
import { useParams, useRouter } from 'next/navigation'
55
import ReactFlow, {
6-
Background,
76
ConnectionLineType,
87
type Edge,
98
type EdgeTypes,
@@ -1927,14 +1926,7 @@ const WorkflowContent = React.memo(() => {
19271926
<Panel />
19281927
</div>
19291928
<ControlBar hasValidationErrors={nestedSubflowErrors.size > 0} />
1930-
<div className='workflow-container h-full'>
1931-
<Background
1932-
color='hsl(var(--workflow-dots))'
1933-
size={4}
1934-
gap={40}
1935-
style={{ backgroundColor: 'hsl(var(--workflow-background))' }}
1936-
/>
1937-
</div>
1929+
<div className='workflow-container h-full' />
19381930
</div>
19391931
</div>
19401932
)
@@ -2006,14 +1998,7 @@ const WorkflowContent = React.memo(() => {
20061998
elevateNodesOnSelect={true}
20071999
autoPanOnConnect={effectivePermissions.canEdit}
20082000
autoPanOnNodeDrag={effectivePermissions.canEdit}
2009-
>
2010-
<Background
2011-
color='hsl(var(--workflow-dots))'
2012-
size={4}
2013-
gap={40}
2014-
style={{ backgroundColor: 'hsl(var(--workflow-background))' }}
2015-
/>
2016-
</ReactFlow>
2001+
/>
20172002

20182003
{/* Show DiffControls if diff is available (regardless of current view mode) */}
20192004
<DiffControls />
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
'use client'
2+
3+
import { useCallback, useRef, useState } from 'react'
4+
import clsx from 'clsx'
5+
import { ChevronRight, Folder, FolderOpen } from 'lucide-react'
6+
import { type FolderTreeNode, useFolderStore } from '@/stores/folders/store'
7+
8+
interface FolderItemProps {
9+
folder: FolderTreeNode
10+
level: number
11+
onDragOver: (e: React.DragEvent) => void
12+
onDragLeave: (e: React.DragEvent) => void
13+
onDrop: (e: React.DragEvent) => void
14+
}
15+
16+
export function FolderItem({ folder, level, onDragOver, onDragLeave, onDrop }: FolderItemProps) {
17+
const { expandedFolders, toggleExpanded } = useFolderStore()
18+
const isExpanded = expandedFolders.has(folder.id)
19+
const [isDragging, setIsDragging] = useState(false)
20+
const dragStartedRef = useRef(false)
21+
22+
const handleToggleExpanded = useCallback(() => {
23+
toggleExpanded(folder.id)
24+
}, [folder.id, toggleExpanded])
25+
26+
const handleClick = useCallback(
27+
(e: React.MouseEvent) => {
28+
e.stopPropagation()
29+
30+
if (dragStartedRef.current) {
31+
e.preventDefault()
32+
return
33+
}
34+
handleToggleExpanded()
35+
},
36+
[handleToggleExpanded]
37+
)
38+
39+
const handleDragStart = (e: React.DragEvent) => {
40+
dragStartedRef.current = true
41+
setIsDragging(true)
42+
43+
e.dataTransfer.setData('folder-id', folder.id)
44+
e.dataTransfer.effectAllowed = 'move'
45+
46+
// Set global drag state for validation in other components
47+
if (typeof window !== 'undefined') {
48+
;(window as any).currentDragFolderId = folder.id
49+
}
50+
}
51+
52+
const handleDragEnd = () => {
53+
setIsDragging(false)
54+
requestAnimationFrame(() => {
55+
dragStartedRef.current = false
56+
})
57+
58+
// Clear global drag state
59+
if (typeof window !== 'undefined') {
60+
;(window as any).currentDragFolderId = null
61+
}
62+
}
63+
64+
return (
65+
<div className='mb-[2px]' onDragOver={onDragOver} onDragLeave={onDragLeave} onDrop={onDrop}>
66+
<div
67+
className={clsx(
68+
'flex h-[25px] cursor-pointer items-center rounded-[8px] text-small',
69+
isDragging ? 'opacity-50' : ''
70+
)}
71+
onClick={handleClick}
72+
draggable
73+
onDragStart={handleDragStart}
74+
onDragEnd={handleDragEnd}
75+
>
76+
<ChevronRight
77+
className={clsx(
78+
'mr-[8px] h-[10px] w-[10px] flex-shrink-0 text-[#787878] transition-all dark:text-[#787878]',
79+
isExpanded ? 'rotate-90' : ''
80+
)}
81+
/>
82+
{isExpanded ? (
83+
<FolderOpen className='mr-[10px] h-[16px] w-[16px] flex-shrink-0 text-[#787878] dark:text-[#787878]' />
84+
) : (
85+
<Folder className='mr-[10px] h-[16px] w-[16px] flex-shrink-0 text-[#787878] dark:text-[#787878]' />
86+
)}
87+
<span className='truncate font-medium text-[#AEAEAE] dark:text-[#AEAEAE]'>
88+
{folder.name}
89+
</span>
90+
</div>
91+
</div>
92+
)
93+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
'use client'
2+
3+
import { useRef, useState } from 'react'
4+
import clsx from 'clsx'
5+
import Link from 'next/link'
6+
import { useParams } from 'next/navigation'
7+
import { useFolderStore } from '@/stores/folders/store'
8+
import type { WorkflowMetadata } from '@/stores/workflows/registry/types'
9+
10+
interface WorkflowItemProps {
11+
workflow: WorkflowMetadata
12+
active: boolean
13+
level: number
14+
}
15+
16+
export function WorkflowItem({ workflow, active, level }: WorkflowItemProps) {
17+
const params = useParams()
18+
const workspaceId = params.workspaceId as string
19+
const [isDragging, setIsDragging] = useState(false)
20+
const dragStartedRef = useRef(false)
21+
const { selectedWorkflows, selectOnly, toggleWorkflowSelection } = useFolderStore()
22+
const isSelected = selectedWorkflows.has(workflow.id)
23+
24+
const handleClick = (e: React.MouseEvent) => {
25+
// Don't propagate click to parent elements
26+
e.stopPropagation()
27+
28+
if (isDragging) {
29+
e.preventDefault()
30+
return
31+
}
32+
33+
if (e.shiftKey) {
34+
e.preventDefault()
35+
toggleWorkflowSelection(workflow.id)
36+
} else {
37+
if (!isSelected || selectedWorkflows.size > 1) {
38+
selectOnly(workflow.id)
39+
}
40+
}
41+
}
42+
43+
const handleDragStart = (e: React.DragEvent) => {
44+
dragStartedRef.current = true
45+
setIsDragging(true)
46+
47+
let workflowIds: string[]
48+
if (isSelected && selectedWorkflows.size > 1) {
49+
workflowIds = Array.from(selectedWorkflows)
50+
} else {
51+
workflowIds = [workflow.id]
52+
}
53+
54+
e.dataTransfer.setData('workflow-ids', JSON.stringify(workflowIds))
55+
e.dataTransfer.effectAllowed = 'move'
56+
}
57+
58+
const handleDragEnd = () => {
59+
setIsDragging(false)
60+
requestAnimationFrame(() => {
61+
dragStartedRef.current = false
62+
})
63+
}
64+
65+
return (
66+
<Link
67+
href={`/workspace/${workspaceId}/w/${workflow.id}`}
68+
className={clsx(
69+
'group flex h-[25px] items-center gap-[8px] rounded-[8px] px-[5px] text-small',
70+
active ? 'bg-[#2C2C2C] dark:bg-[#2C2C2C]' : 'hover:bg-[#2C2C2C] dark:hover:bg-[#2C2C2C]',
71+
isSelected && selectedWorkflows.size > 1 && !active ? 'bg-[#2C2C2C] dark:bg-[#2C2C2C]' : '',
72+
isDragging ? 'opacity-50' : ''
73+
)}
74+
draggable
75+
onDragStart={handleDragStart}
76+
onDragEnd={handleDragEnd}
77+
onClick={handleClick}
78+
>
79+
<div
80+
className='h-[16px] w-[16px] flex-shrink-0 rounded-[4px]'
81+
style={{ backgroundColor: workflow.color }}
82+
/>
83+
<span
84+
className={clsx(
85+
'truncate font-medium',
86+
active
87+
? 'text-[#E6E6E6] dark:text-[#E6E6E6]'
88+
: 'text-[#AEAEAE] group-hover:text-[#E6E6E6] dark:text-[#AEAEAE] dark:group-hover:text-[#E6E6E6]'
89+
)}
90+
>
91+
{workflow.name}
92+
</span>
93+
</Link>
94+
)
95+
}

0 commit comments

Comments
 (0)