@@ -3,6 +3,7 @@ import { createLogger } from '@sim/logger'
33import { useReactFlow } from 'reactflow'
44import { BLOCK_DIMENSIONS , CONTAINER_DIMENSIONS } from '@/lib/workflows/blocks/block-dimensions'
55import { getBlock } from '@/blocks/registry'
6+ import { useWorkflowStore } from '@/stores/workflows/workflow/store'
67
78const logger = createLogger ( 'NodeUtilities' )
89
@@ -208,28 +209,30 @@ export function useNodeUtilities(blocks: Record<string, any>) {
208209 * to the content area bounds (after header and padding).
209210 * @param nodeId ID of the node being repositioned
210211 * @param newParentId ID of the new parent
212+ * @param skipClamping If true, returns raw relative position without clamping to container bounds
211213 * @returns Relative position coordinates {x, y} within the parent
212214 */
213215 const calculateRelativePosition = useCallback (
214- ( nodeId : string , newParentId : string ) : { x : number ; y : number } => {
216+ ( nodeId : string , newParentId : string , skipClamping ?: boolean ) : { x : number ; y : number } => {
215217 const nodeAbsPos = getNodeAbsolutePosition ( nodeId )
216218 const parentAbsPos = getNodeAbsolutePosition ( newParentId )
217- const parentNode = getNodes ( ) . find ( ( n ) => n . id === newParentId )
218219
219- // Calculate raw relative position (relative to parent origin)
220220 const rawPosition = {
221221 x : nodeAbsPos . x - parentAbsPos . x ,
222222 y : nodeAbsPos . y - parentAbsPos . y ,
223223 }
224224
225- // Get container and block dimensions
225+ if ( skipClamping ) {
226+ return rawPosition
227+ }
228+
229+ const parentNode = getNodes ( ) . find ( ( n ) => n . id === newParentId )
226230 const containerDimensions = {
227231 width : parentNode ?. data ?. width || CONTAINER_DIMENSIONS . DEFAULT_WIDTH ,
228232 height : parentNode ?. data ?. height || CONTAINER_DIMENSIONS . DEFAULT_HEIGHT ,
229233 }
230234 const blockDimensions = getBlockDimensions ( nodeId )
231235
232- // Clamp position to keep block inside content area
233236 return clampPositionToContainer ( rawPosition , containerDimensions , blockDimensions )
234237 } ,
235238 [ getNodeAbsolutePosition , getNodes , getBlockDimensions ]
@@ -298,12 +301,12 @@ export function useNodeUtilities(blocks: Record<string, any>) {
298301 */
299302 const calculateLoopDimensions = useCallback (
300303 ( nodeId : string ) : { width : number ; height : number } => {
301- // Check both React Flow's node.parentId AND blocks store's data.parentId
302- // This ensures we catch children even if React Flow hasn't re-rendered yet
303- const childNodes = getNodes ( ) . filter (
304- ( node ) => node . parentId === nodeId || blocks [ node . id ] ?. data ?. parentId === nodeId
304+ const currentBlocks = useWorkflowStore . getState ( ) . blocks
305+ const childBlockIds = Object . keys ( currentBlocks ) . filter (
306+ ( id ) => currentBlocks [ id ] ?. data ?. parentId === nodeId
305307 )
306- if ( childNodes . length === 0 ) {
308+
309+ if ( childBlockIds . length === 0 ) {
307310 return {
308311 width : CONTAINER_DIMENSIONS . DEFAULT_WIDTH ,
309312 height : CONTAINER_DIMENSIONS . DEFAULT_HEIGHT ,
@@ -313,30 +316,28 @@ export function useNodeUtilities(blocks: Record<string, any>) {
313316 let maxRight = 0
314317 let maxBottom = 0
315318
316- childNodes . forEach ( ( node ) => {
317- const { width : nodeWidth , height : nodeHeight } = getBlockDimensions ( node . id )
318- // Use ReactFlow's node.position which is already in the correct coordinate system
319- // (relative to parent for child nodes). The store's block.position may be stale
320- // or still in absolute coordinates during parent updates.
321- maxRight = Math . max ( maxRight , node . position . x + nodeWidth )
322- maxBottom = Math . max ( maxBottom , node . position . y + nodeHeight )
323- } )
319+ for ( const childId of childBlockIds ) {
320+ const child = currentBlocks [ childId ]
321+ if ( ! child ?. position ) continue
322+
323+ const { width : childWidth , height : childHeight } = getBlockDimensions ( childId )
324+
325+ maxRight = Math . max ( maxRight , child . position . x + childWidth )
326+ maxBottom = Math . max ( maxBottom , child . position . y + childHeight )
327+ }
324328
325329 const width = Math . max (
326330 CONTAINER_DIMENSIONS . DEFAULT_WIDTH ,
327- CONTAINER_DIMENSIONS . LEFT_PADDING + maxRight + CONTAINER_DIMENSIONS . RIGHT_PADDING
331+ maxRight + CONTAINER_DIMENSIONS . RIGHT_PADDING
328332 )
329333 const height = Math . max (
330334 CONTAINER_DIMENSIONS . DEFAULT_HEIGHT ,
331- CONTAINER_DIMENSIONS . HEADER_HEIGHT +
332- CONTAINER_DIMENSIONS . TOP_PADDING +
333- maxBottom +
334- CONTAINER_DIMENSIONS . BOTTOM_PADDING
335+ maxBottom + CONTAINER_DIMENSIONS . BOTTOM_PADDING
335336 )
336337
337338 return { width, height }
338339 } ,
339- [ getNodes , getBlockDimensions , blocks ]
340+ [ getBlockDimensions ]
340341 )
341342
342343 /**
@@ -345,29 +346,27 @@ export function useNodeUtilities(blocks: Record<string, any>) {
345346 */
346347 const resizeLoopNodes = useCallback (
347348 ( updateNodeDimensions : ( id : string , dimensions : { width : number ; height : number } ) => void ) => {
348- const containerNodes = getNodes ( )
349- . filter ( ( node ) => node . type && isContainerType ( node . type ) )
350- . map ( ( node ) => ( {
351- ...node ,
352- depth : getNodeDepth ( node . id ) ,
349+ const currentBlocks = useWorkflowStore . getState ( ) . blocks
350+ const containerBlocks = Object . entries ( currentBlocks )
351+ . filter ( ( [ , block ] ) => block ?. type && isContainerType ( block . type ) )
352+ . map ( ( [ id , block ] ) => ( {
353+ id,
354+ block,
355+ depth : getNodeDepth ( id ) ,
353356 } ) )
354- // Sort by depth descending - process innermost containers first
355- // so their dimensions are correct when outer containers calculate sizes
356357 . sort ( ( a , b ) => b . depth - a . depth )
357358
358- containerNodes . forEach ( ( node ) => {
359- const dimensions = calculateLoopDimensions ( node . id )
360- // Get current dimensions from the blocks store rather than React Flow's potentially stale state
361- const currentWidth = blocks [ node . id ] ?. data ?. width
362- const currentHeight = blocks [ node . id ] ?. data ?. height
359+ for ( const { id, block } of containerBlocks ) {
360+ const dimensions = calculateLoopDimensions ( id )
361+ const currentWidth = block ?. data ?. width
362+ const currentHeight = block ?. data ?. height
363363
364- // Only update if dimensions actually changed to avoid unnecessary re-renders
365364 if ( dimensions . width !== currentWidth || dimensions . height !== currentHeight ) {
366- updateNodeDimensions ( node . id , dimensions )
365+ updateNodeDimensions ( id , dimensions )
367366 }
368- } )
367+ }
369368 } ,
370- [ getNodes , isContainerType , getNodeDepth , calculateLoopDimensions , blocks ]
369+ [ isContainerType , getNodeDepth , calculateLoopDimensions ]
371370 )
372371
373372 /**
0 commit comments