Skip to content

Commit 2d2c0cc

Browse files
committed
feat(sidebar): improved workflow/folder dragging UI/UX; refactored logic into hooks
1 parent 05149f7 commit 2d2c0cc

File tree

11 files changed

+1013
-609
lines changed

11 files changed

+1013
-609
lines changed

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

Lines changed: 59 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
'use client'
22

3-
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
3+
import { useCallback, useMemo, useRef } from 'react'
44
import clsx from 'clsx'
55
import { getBlocksForSidebar } from '@/lib/workflows/trigger-utils'
66
import { LoopTool } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/loop/loop-config'
77
import { ParallelTool } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/parallel/parallel-config'
88
import type { BlockConfig } from '@/blocks/types'
9-
import { useSidebarStore } from '@/stores/sidebar/store'
9+
import { usePanelResize } from '../../hooks/use-panel-resize'
1010

1111
interface BlocksProps {
1212
disabled?: boolean
@@ -22,19 +22,21 @@ interface BlockItem {
2222
}
2323

2424
/**
25-
* Constants for blocks panel sizing
25+
* Blocks panel component displaying available block types.
26+
* Uses the panel resize hook for shared resize/toggle functionality.
27+
*
28+
* @param props - Component props
29+
* @returns Blocks panel with resizable functionality
2630
*/
27-
const DEFAULT_HEIGHT = 200
28-
const MIN_HEIGHT = 28
29-
const HEADER_HEIGHT = 28
30-
3131
export function Blocks({ disabled = false }: BlocksProps) {
32-
const [isResizing, setIsResizing] = useState(false)
33-
const { blocksHeight, setBlocksHeight, setTriggersHeight } = useSidebarStore()
34-
const startYRef = useRef<number>(0)
35-
const startHeightRef = useRef<number>(0)
3632
const containerRef = useRef<HTMLDivElement>(null)
3733

34+
// Panel resize hook
35+
const { handleMouseDown, handleToggle } = usePanelResize({
36+
panelType: 'blocks',
37+
containerRef,
38+
})
39+
3840
const blocks = useMemo(() => {
3941
const allBlocks = getBlocksForSidebar()
4042

@@ -87,143 +89,58 @@ export function Blocks({ disabled = false }: BlocksProps) {
8789
return [...regularBlockItems, ...toolItems]
8890
}, [])
8991

90-
const handleDragStart = (e: React.DragEvent, item: BlockItem) => {
91-
if (disabled) {
92-
e.preventDefault()
93-
return
94-
}
95-
e.dataTransfer.setData(
96-
'application/json',
97-
JSON.stringify({
98-
type: item.type,
99-
enableTriggerMode: false,
100-
})
101-
)
102-
e.dataTransfer.effectAllowed = 'move'
103-
}
104-
105-
const handleClick = (item: BlockItem) => {
106-
if (item.type === 'connectionBlock' || disabled) return
107-
108-
const event = new CustomEvent('add-block-from-toolbar', {
109-
detail: {
110-
type: item.type,
111-
enableTriggerMode: false,
112-
},
113-
})
114-
window.dispatchEvent(event)
115-
}
116-
117-
const handleMouseDown = useCallback((e: React.MouseEvent) => {
118-
setIsResizing(true)
119-
startYRef.current = e.clientY
120-
const currentHeight = Number.parseInt(
121-
getComputedStyle(document.documentElement).getPropertyValue('--blocks-height')
122-
)
123-
startHeightRef.current = currentHeight
124-
}, [])
125-
126-
const handleToggle = useCallback(() => {
127-
if (blocksHeight <= MIN_HEIGHT) {
128-
// Expanding: set to default height, and ensure triggers has enough space for both sections
129-
const currentTriggersHeight = Number.parseInt(
130-
getComputedStyle(document.documentElement).getPropertyValue('--triggers-height')
131-
)
132-
133-
// Calculate what blocks height we want
134-
const desiredBlocksHeight = DEFAULT_HEIGHT
135-
136-
// Calculate minimum triggers height needed to show blocks content
137-
const minRequiredTriggersHeight = desiredBlocksHeight + HEADER_HEIGHT
138-
139-
// Calculate ideal triggers height to show both blocks and triggers content reasonably
140-
// This gives DEFAULT_HEIGHT visible space for triggers content above blocks
141-
const idealTriggersHeight = desiredBlocksHeight + HEADER_HEIGHT + DEFAULT_HEIGHT
142-
143-
// If current triggers height is below ideal, expand triggers to show both sections properly
144-
if (currentTriggersHeight < idealTriggersHeight) {
145-
if (containerRef.current?.parentElement) {
146-
const parentHeight = containerRef.current.parentElement.getBoundingClientRect().height
147-
setTriggersHeight(Math.min(idealTriggersHeight, parentHeight))
148-
} else {
149-
setTriggersHeight(idealTriggersHeight)
150-
}
151-
}
152-
153-
// Now expand blocks (store constraints will handle the sizing)
154-
setBlocksHeight(desiredBlocksHeight)
155-
} else {
156-
// Collapsing: simply collapse to minimum
157-
setBlocksHeight(MIN_HEIGHT)
158-
}
159-
}, [blocksHeight, setBlocksHeight, setTriggersHeight])
160-
16192
/**
162-
* Setup resize event listeners and body styles when resizing
163-
* Event handlers are defined inline to avoid stale closure issues
93+
* Handle drag start for block items
94+
*
95+
* @param e - React drag event
96+
* @param item - Block item configuration
16497
*/
165-
useEffect(() => {
166-
if (!isResizing || !containerRef.current) return
167-
168-
const handleMouseMove = (e: MouseEvent) => {
169-
const deltaY = startYRef.current - e.clientY
170-
let newHeight = startHeightRef.current + deltaY
98+
const handleDragStart = useCallback(
99+
(e: React.DragEvent<HTMLElement>, item: BlockItem) => {
100+
if (disabled) {
101+
e.preventDefault()
102+
return
103+
}
171104

172-
const parentContainer = containerRef.current?.parentElement
173-
if (parentContainer) {
174-
const parentHeight = parentContainer.getBoundingClientRect().height
175-
const currentTriggersHeight = Number.parseInt(
176-
getComputedStyle(document.documentElement).getPropertyValue('--triggers-height')
105+
try {
106+
e.dataTransfer.setData(
107+
'application/json',
108+
JSON.stringify({
109+
type: item.type,
110+
enableTriggerMode: false,
111+
})
177112
)
178-
const currentBlocksHeight = Number.parseInt(
179-
getComputedStyle(document.documentElement).getPropertyValue('--blocks-height')
180-
)
181-
182-
const maxAllowedHeight = currentTriggersHeight - HEADER_HEIGHT
183-
184-
// Special case: if blocks is at max height and user is expanding blocks (deltaY > 0)
185-
// then expand both blocks and triggers together
186-
const isAtMaxHeight = Math.abs(currentBlocksHeight - maxAllowedHeight) <= 2
187-
const isExpandingBlocks = deltaY > 0
188-
189-
if (isAtMaxHeight && isExpandingBlocks) {
190-
// Calculate how much more the user wants to expand
191-
const requestedIncrease = newHeight - currentBlocksHeight
192-
193-
// Expand triggers by the same amount (respecting parent height limit)
194-
const newTriggersHeight = Math.min(
195-
currentTriggersHeight + requestedIncrease,
196-
parentHeight
197-
)
198-
setTriggersHeight(newTriggersHeight)
199-
200-
// Blocks will expand proportionally through the store constraint
201-
setBlocksHeight(newHeight)
202-
} else {
203-
// Normal behavior: constrain blocks within current triggers space
204-
newHeight = Math.min(newHeight, maxAllowedHeight)
205-
newHeight = Math.max(newHeight, MIN_HEIGHT)
206-
setBlocksHeight(newHeight)
207-
}
113+
e.dataTransfer.effectAllowed = 'move'
114+
} catch (error) {
115+
console.error('Failed to set drag data:', error)
208116
}
209-
}
210-
211-
const handleMouseUp = () => {
212-
setIsResizing(false)
213-
}
214-
215-
document.addEventListener('mousemove', handleMouseMove)
216-
document.addEventListener('mouseup', handleMouseUp)
217-
document.body.style.cursor = 'ns-resize'
218-
document.body.style.userSelect = 'none'
117+
},
118+
[disabled]
119+
)
219120

220-
return () => {
221-
document.removeEventListener('mousemove', handleMouseMove)
222-
document.removeEventListener('mouseup', handleMouseUp)
223-
document.body.style.cursor = ''
224-
document.body.style.userSelect = ''
225-
}
226-
}, [isResizing, setBlocksHeight, setTriggersHeight])
121+
/**
122+
* Handle click on block item to add to canvas
123+
*
124+
* @param item - Block item configuration
125+
*/
126+
const handleClick = useCallback(
127+
(item: BlockItem) => {
128+
if (item.type === 'connectionBlock' || disabled) return
129+
130+
try {
131+
const event = new CustomEvent('add-block-from-toolbar', {
132+
detail: {
133+
type: item.type,
134+
enableTriggerMode: false,
135+
},
136+
})
137+
window.dispatchEvent(event)
138+
} catch (error) {
139+
console.error('Failed to dispatch add-block event:', error)
140+
}
141+
},
142+
[disabled]
143+
)
227144

228145
return (
229146
<div

0 commit comments

Comments
 (0)