Skip to content

Commit c3f104b

Browse files
committed
feat(sidebar): added search UI, updated workflows styling
1 parent a30c704 commit c3f104b

File tree

10 files changed

+531
-189
lines changed

10 files changed

+531
-189
lines changed

apps/sim/app/(auth)/sso/sso-form.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { quickValidateEmail } from '@/lib/email/validation'
1111
import { env, isFalsy } from '@/lib/env'
1212
import { createLogger } from '@/lib/logs/console/logger'
1313
import { cn } from '@/lib/utils'
14-
import { inter } from '@/app/fonts/inter'
14+
import { inter } from '@/app/fonts/inter/inter'
1515
import { soehne } from '@/app/fonts/soehne/soehne'
1616

1717
const logger = createLogger('SSOForm')

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ export default function WorkspaceLayout({ children }: { children: React.ReactNod
55
return (
66
<Providers>
77
<div className='flex min-h-screen w-full'>
8-
<div className='z-20'>
9-
<SidebarNew />
10-
</div>
8+
<SidebarNew />
119
<div className='flex flex-1 flex-col'>{children}</div>
1210
</div>
1311
</Providers>

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components-new/workflow-list/folder-item.tsx renamed to apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components-new/workflow-list/components/folder-item/folder-item.tsx

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export function FolderItem({ folder, level, onDragOver, onDragLeave, onDrop }: F
1717
const { expandedFolders, toggleExpanded } = useFolderStore()
1818
const isExpanded = expandedFolders.has(folder.id)
1919
const [isDragging, setIsDragging] = useState(false)
20-
const dragStartedRef = useRef(false)
20+
const shouldPreventClickRef = useRef(false)
2121

2222
const handleToggleExpanded = useCallback(() => {
2323
toggleExpanded(folder.id)
@@ -27,7 +27,7 @@ export function FolderItem({ folder, level, onDragOver, onDragLeave, onDrop }: F
2727
(e: React.MouseEvent) => {
2828
e.stopPropagation()
2929

30-
if (dragStartedRef.current) {
30+
if (shouldPreventClickRef.current) {
3131
e.preventDefault()
3232
return
3333
}
@@ -36,39 +36,44 @@ export function FolderItem({ folder, level, onDragOver, onDragLeave, onDrop }: F
3636
[handleToggleExpanded]
3737
)
3838

39+
const handleKeyDown = useCallback(
40+
(e: React.KeyboardEvent) => {
41+
if (e.key === 'Enter' || e.key === ' ') {
42+
e.preventDefault()
43+
handleToggleExpanded()
44+
}
45+
},
46+
[handleToggleExpanded]
47+
)
48+
3949
const handleDragStart = (e: React.DragEvent) => {
40-
dragStartedRef.current = true
50+
shouldPreventClickRef.current = true
4151
setIsDragging(true)
4252

4353
e.dataTransfer.setData('folder-id', folder.id)
4454
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-
}
5055
}
5156

5257
const handleDragEnd = () => {
5358
setIsDragging(false)
5459
requestAnimationFrame(() => {
55-
dragStartedRef.current = false
60+
shouldPreventClickRef.current = false
5661
})
57-
58-
// Clear global drag state
59-
if (typeof window !== 'undefined') {
60-
;(window as any).currentDragFolderId = null
61-
}
6262
}
6363

6464
return (
65-
<div className='mb-[2px]' onDragOver={onDragOver} onDragLeave={onDragLeave} onDrop={onDrop}>
65+
<div onDragOver={onDragOver} onDragLeave={onDragLeave} onDrop={onDrop}>
6666
<div
67+
role='button'
68+
tabIndex={0}
69+
aria-expanded={isExpanded}
70+
aria-label={`${folder.name} folder, ${isExpanded ? 'expanded' : 'collapsed'}`}
6771
className={clsx(
6872
'flex h-[25px] cursor-pointer items-center rounded-[8px] text-[14px]',
6973
isDragging ? 'opacity-50' : ''
7074
)}
7175
onClick={handleClick}
76+
onKeyDown={handleKeyDown}
7277
draggable
7378
onDragStart={handleDragStart}
7479
onDragEnd={handleDragEnd}
@@ -78,11 +83,18 @@ export function FolderItem({ folder, level, onDragOver, onDragLeave, onDrop }: F
7883
'mr-[8px] h-[10px] w-[10px] flex-shrink-0 text-[#787878] transition-all dark:text-[#787878]',
7984
isExpanded ? 'rotate-90' : ''
8085
)}
86+
aria-hidden='true'
8187
/>
8288
{isExpanded ? (
83-
<FolderOpen className='mr-[10px] h-[16px] w-[16px] flex-shrink-0 text-[#787878] dark:text-[#787878]' />
89+
<FolderOpen
90+
className='mr-[10px] h-[16px] w-[16px] flex-shrink-0 text-[#787878] dark:text-[#787878]'
91+
aria-hidden='true'
92+
/>
8493
) : (
85-
<Folder className='mr-[10px] h-[16px] w-[16px] flex-shrink-0 text-[#787878] dark:text-[#787878]' />
94+
<Folder
95+
className='mr-[10px] h-[16px] w-[16px] flex-shrink-0 text-[#787878] dark:text-[#787878]'
96+
aria-hidden='true'
97+
/>
8698
)}
8799
<span className='truncate font-medium text-[#AEAEAE] dark:text-[#AEAEAE]'>
88100
{folder.name}

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components-new/workflow-list/workflow-item.tsx renamed to apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components-new/workflow-list/components/workflow-item/workflow-item.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,14 @@ export function WorkflowItem({ workflow, active, level }: WorkflowItemProps) {
1717
const params = useParams()
1818
const workspaceId = params.workspaceId as string
1919
const [isDragging, setIsDragging] = useState(false)
20-
const dragStartedRef = useRef(false)
20+
const shouldPreventClickRef = useRef(false)
2121
const { selectedWorkflows, selectOnly, toggleWorkflowSelection } = useFolderStore()
2222
const isSelected = selectedWorkflows.has(workflow.id)
2323

2424
const handleClick = (e: React.MouseEvent) => {
25-
// Don't propagate click to parent elements
2625
e.stopPropagation()
2726

28-
if (isDragging) {
27+
if (shouldPreventClickRef.current) {
2928
e.preventDefault()
3029
return
3130
}
@@ -41,7 +40,7 @@ export function WorkflowItem({ workflow, active, level }: WorkflowItemProps) {
4140
}
4241

4342
const handleDragStart = (e: React.DragEvent) => {
44-
dragStartedRef.current = true
43+
shouldPreventClickRef.current = true
4544
setIsDragging(true)
4645

4746
let workflowIds: string[]
@@ -58,7 +57,7 @@ export function WorkflowItem({ workflow, active, level }: WorkflowItemProps) {
5857
const handleDragEnd = () => {
5958
setIsDragging(false)
6059
requestAnimationFrame(() => {
61-
dragStartedRef.current = false
60+
shouldPreventClickRef.current = false
6261
})
6362
}
6463

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import { useCallback, useState } from 'react'
2+
import { createLogger } from '@/lib/logs/console/logger'
3+
import { useFolderStore } from '@/stores/folders/store'
4+
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
5+
6+
const logger = createLogger('WorkflowList:DragDrop')
7+
8+
const MAX_NESTING_LEVELS = 2
9+
10+
/**
11+
* Custom hook for handling drag and drop operations for workflows and folders
12+
*/
13+
export function useDragDrop() {
14+
const [dragOverFolderId, setDragOverFolderId] = useState<string | null>(null)
15+
const [rootDragOver, setRootDragOver] = useState(false)
16+
17+
const { updateFolderAPI, getFolderPath } = useFolderStore()
18+
const { updateWorkflow } = useWorkflowRegistry()
19+
20+
/**
21+
* Moves one or more workflows to a target folder
22+
*/
23+
const handleWorkflowDrop = useCallback(
24+
async (workflowIds: string[], targetFolderId: string | null) => {
25+
try {
26+
for (const workflowId of workflowIds) {
27+
await updateWorkflow(workflowId, { folderId: targetFolderId })
28+
}
29+
logger.info(`Moved ${workflowIds.length} workflow(s)`)
30+
} catch (error) {
31+
logger.error('Failed to move workflows:', error)
32+
}
33+
},
34+
[updateWorkflow]
35+
)
36+
37+
/**
38+
* Moves a folder to a new parent folder, with validation
39+
*/
40+
const handleFolderMove = useCallback(
41+
async (draggedFolderId: string, targetFolderId: string | null) => {
42+
try {
43+
const folderStore = useFolderStore.getState()
44+
const draggedFolderPath = folderStore.getFolderPath(draggedFolderId)
45+
46+
// Prevent moving folder into its own descendant
47+
if (
48+
targetFolderId &&
49+
draggedFolderPath.some((ancestor) => ancestor.id === targetFolderId)
50+
) {
51+
logger.info('Cannot move folder into its own descendant')
52+
return
53+
}
54+
55+
// Validate nesting level
56+
const targetFolderPath = targetFolderId ? folderStore.getFolderPath(targetFolderId) : []
57+
if (targetFolderPath.length >= MAX_NESTING_LEVELS) {
58+
logger.info(`Maximum ${MAX_NESTING_LEVELS} levels of nesting allowed`)
59+
return
60+
}
61+
62+
await updateFolderAPI(draggedFolderId, { parentId: targetFolderId })
63+
logger.info(`Moved folder to ${targetFolderId ? `folder ${targetFolderId}` : 'root'}`)
64+
} catch (error) {
65+
logger.error('Failed to move folder:', error)
66+
}
67+
},
68+
[updateFolderAPI]
69+
)
70+
71+
/**
72+
* Handles drop events for both workflows and folders
73+
*/
74+
const handleFolderDrop = useCallback(
75+
async (e: React.DragEvent, targetFolderId: string | null) => {
76+
e.preventDefault()
77+
e.stopPropagation()
78+
setDragOverFolderId(null)
79+
setRootDragOver(false)
80+
81+
// Check if dropping workflows
82+
const workflowIdsData = e.dataTransfer.getData('workflow-ids')
83+
if (workflowIdsData) {
84+
const workflowIds = JSON.parse(workflowIdsData) as string[]
85+
await handleWorkflowDrop(workflowIds, targetFolderId)
86+
return
87+
}
88+
89+
// Check if dropping a folder
90+
const folderIdData = e.dataTransfer.getData('folder-id')
91+
if (folderIdData && targetFolderId !== folderIdData) {
92+
await handleFolderMove(folderIdData, targetFolderId)
93+
}
94+
},
95+
[handleWorkflowDrop, handleFolderMove]
96+
)
97+
98+
/**
99+
* Creates drag event handlers for a specific folder
100+
*/
101+
const createFolderDragHandlers = useCallback(
102+
(folderId: string) => ({
103+
onDragOver: (e: React.DragEvent) => {
104+
e.preventDefault()
105+
e.stopPropagation()
106+
setDragOverFolderId(folderId)
107+
},
108+
onDragLeave: (e: React.DragEvent) => {
109+
e.preventDefault()
110+
e.stopPropagation()
111+
setDragOverFolderId(null)
112+
},
113+
onDrop: (e: React.DragEvent) => handleFolderDrop(e, folderId),
114+
}),
115+
[handleFolderDrop]
116+
)
117+
118+
/**
119+
* Creates drag event handlers for the root drop zone
120+
*/
121+
const createRootDragHandlers = useCallback(
122+
() => ({
123+
onDragOver: (e: React.DragEvent) => {
124+
e.preventDefault()
125+
setRootDragOver(true)
126+
},
127+
onDragLeave: (e: React.DragEvent) => {
128+
e.preventDefault()
129+
setRootDragOver(false)
130+
},
131+
onDrop: (e: React.DragEvent) => handleFolderDrop(e, null),
132+
}),
133+
[handleFolderDrop]
134+
)
135+
136+
return {
137+
dragOverFolderId,
138+
rootDragOver,
139+
createFolderDragHandlers,
140+
createRootDragHandlers,
141+
}
142+
}

0 commit comments

Comments
 (0)