Skip to content

Commit 78ea04f

Browse files
committed
improvement(sidebar): dragging hover handler; closed folders by default minus active path
1 parent e290391 commit 78ea04f

File tree

4 files changed

+95
-12
lines changed

4 files changed

+95
-12
lines changed

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,21 @@ import { useItemDrag } from '../../../../hooks/use-item-drag'
1010
interface FolderItemProps {
1111
folder: FolderTreeNode
1212
level: number
13+
hoverHandlers?: {
14+
onDragEnter?: (e: React.DragEvent<HTMLElement>) => void
15+
onDragLeave?: (e: React.DragEvent<HTMLElement>) => void
16+
}
1317
}
1418

1519
/**
1620
* FolderItem component displaying a single folder with drag and expand/collapse support.
1721
* Uses item drag and folder expand hooks for unified behavior.
22+
* Supports hover-to-expand during drag operations via hoverHandlers.
1823
*
1924
* @param props - Component props
2025
* @returns Folder item with drag and expand support
2126
*/
22-
export function FolderItem({ folder, level }: FolderItemProps) {
27+
export function FolderItem({ folder, level, hoverHandlers }: FolderItemProps) {
2328
// Folder expand hook
2429
const { isExpanded, handleToggleExpanded, handleKeyDown } = useFolderExpand({
2530
folderId: folder.id,
@@ -76,6 +81,7 @@ export function FolderItem({ folder, level }: FolderItemProps) {
7681
draggable
7782
onDragStart={handleDragStart}
7883
onDragEnd={handleDragEnd}
84+
{...hoverHandlers}
7985
>
8086
<ChevronRight
8187
className={clsx(

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export function WorkflowList({
6969
createFolderDragHandlers,
7070
createItemDragHandlers,
7171
createRootDragHandlers,
72+
createFolderHeaderHoverHandlers,
7273
} = useDragDrop()
7374

7475
// Workflow import hook
@@ -194,7 +195,11 @@ export function WorkflowList({
194195
style={{ paddingLeft: `${level * TREE_SPACING.INDENT_PER_LEVEL}px` }}
195196
{...createItemDragHandlers(folder.id)}
196197
>
197-
<FolderItem folder={folder} level={level} />
198+
<FolderItem
199+
folder={folder}
200+
level={level}
201+
hoverHandlers={createFolderHeaderHoverHandlers(folder.id)}
202+
/>
198203
</div>
199204

200205
{isExpanded && hasChildren && (
@@ -247,6 +252,7 @@ export function WorkflowList({
247252
isDragging,
248253
createFolderDragHandlers,
249254
createItemDragHandlers,
255+
createFolderHeaderHoverHandlers,
250256
calculateVerticalLineHeight,
251257
renderWorkflowItem,
252258
]

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/hooks/use-drag-drop.ts

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,27 @@ const logger = createLogger('WorkflowList:DragDrop')
1111
const SCROLL_THRESHOLD = 60 // Distance from edge to trigger scroll
1212
const SCROLL_SPEED = 8 // Pixels per frame
1313

14+
/**
15+
* Constants for folder auto-expand on hover during drag
16+
*/
17+
const HOVER_EXPAND_DELAY = 400 // Milliseconds to wait before expanding folder
18+
1419
/**
1520
* Custom hook for handling drag and drop operations for workflows and folders.
16-
* Includes auto-scrolling and drop target highlighting.
21+
* Includes auto-scrolling, drop target highlighting, and hover-to-expand.
1722
*
1823
* @returns Drag and drop state and event handlers
1924
*/
2025
export function useDragDrop() {
2126
const [dropTargetId, setDropTargetId] = useState<string | null>(null)
2227
const [isDragging, setIsDragging] = useState(false)
28+
const [hoverFolderId, setHoverFolderId] = useState<string | null>(null)
2329
const scrollContainerRef = useRef<HTMLDivElement | null>(null)
2430
const scrollIntervalRef = useRef<number | null>(null)
31+
const hoverExpandTimerRef = useRef<number | null>(null)
2532
const lastDragYRef = useRef<number>(0)
2633

27-
const { updateFolderAPI, getFolderPath } = useFolderStore()
34+
const { updateFolderAPI, getFolderPath, setExpanded, expandedFolders } = useFolderStore()
2835
const { updateWorkflow } = useWorkflowRegistry()
2936

3037
/**
@@ -85,6 +92,49 @@ export function useDragDrop() {
8592
}
8693
}, [isDragging, handleAutoScroll])
8794

95+
/**
96+
* Handle hover folder changes - start/clear expand timer
97+
*/
98+
useEffect(() => {
99+
// Clear existing timer when hover folder changes
100+
if (hoverExpandTimerRef.current) {
101+
clearTimeout(hoverExpandTimerRef.current)
102+
hoverExpandTimerRef.current = null
103+
}
104+
105+
// Don't start timer if not dragging or no folder is hovered
106+
if (!isDragging || !hoverFolderId) {
107+
return
108+
}
109+
110+
// Don't expand if folder is already expanded
111+
if (expandedFolders.has(hoverFolderId)) {
112+
return
113+
}
114+
115+
// Start timer to expand folder after delay
116+
hoverExpandTimerRef.current = window.setTimeout(() => {
117+
setExpanded(hoverFolderId, true)
118+
logger.info(`Auto-expanded folder ${hoverFolderId} during drag`)
119+
}, HOVER_EXPAND_DELAY)
120+
121+
return () => {
122+
if (hoverExpandTimerRef.current) {
123+
clearTimeout(hoverExpandTimerRef.current)
124+
hoverExpandTimerRef.current = null
125+
}
126+
}
127+
}, [hoverFolderId, isDragging, expandedFolders, setExpanded])
128+
129+
/**
130+
* Cleanup hover state when dragging stops
131+
*/
132+
useEffect(() => {
133+
if (!isDragging) {
134+
setHoverFolderId(null)
135+
}
136+
}, [isDragging])
137+
88138
/**
89139
* Moves one or more workflows to a target folder
90140
*
@@ -269,6 +319,32 @@ export function useDragDrop() {
269319
[handleFolderDrop]
270320
)
271321

322+
/**
323+
* Creates drag event handlers for folder header (the clickable part)
324+
* These handlers trigger folder expansion on hover during drag
325+
*
326+
* @param folderId - Folder ID to handle hover for
327+
* @returns Object containing drag event handlers for folder header
328+
*/
329+
const createFolderHeaderHoverHandlers = useCallback(
330+
(folderId: string) => ({
331+
onDragEnter: (e: React.DragEvent<HTMLElement>) => {
332+
if (isDragging) {
333+
setHoverFolderId(folderId)
334+
}
335+
},
336+
onDragLeave: (e: React.DragEvent<HTMLElement>) => {
337+
const relatedTarget = e.relatedTarget as HTMLElement | null
338+
const currentTarget = e.currentTarget as HTMLElement
339+
// Only clear if we're leaving the folder header completely
340+
if (!relatedTarget || !currentTarget.contains(relatedTarget)) {
341+
setHoverFolderId(null)
342+
}
343+
},
344+
}),
345+
[isDragging]
346+
)
347+
272348
/**
273349
* Set the scroll container ref for auto-scrolling
274350
*
@@ -285,5 +361,6 @@ export function useDragDrop() {
285361
createFolderDragHandlers,
286362
createItemDragHandlers,
287363
createRootDragHandlers,
364+
createFolderHeaderHoverHandlers,
288365
}
289366
}

apps/sim/stores/folders/store.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -242,14 +242,8 @@ export const useFolderStore = create<FolderState>()(
242242

243243
get().setFolders(processedFolders)
244244

245-
// Initialize expanded state from folder data
246-
const expandedSet = new Set<string>()
247-
processedFolders.forEach((folder: WorkflowFolder) => {
248-
if (folder.isExpanded) {
249-
expandedSet.add(folder.id)
250-
}
251-
})
252-
set({ expandedFolders: expandedSet })
245+
// Start with all folders collapsed - only active workflow path will be expanded by the UI
246+
set({ expandedFolders: new Set() })
253247
} catch (error) {
254248
logger.error('Error fetching folders:', error)
255249
} finally {

0 commit comments

Comments
 (0)