Skip to content

Commit 24b918a

Browse files
icecrasher321waleedlatif1
authored andcommitted
fix subflow resizing
1 parent abf46da commit 24b918a

File tree

3 files changed

+119
-45
lines changed

3 files changed

+119
-45
lines changed

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-node-utilities.ts

Lines changed: 39 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { createLogger } from '@sim/logger'
33
import { useReactFlow } from 'reactflow'
44
import { BLOCK_DIMENSIONS, CONTAINER_DIMENSIONS } from '@/lib/workflows/blocks/block-dimensions'
55
import { getBlock } from '@/blocks/registry'
6+
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
67

78
const 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
/**

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

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ const WorkflowContent = React.memo(() => {
303303
const {
304304
getNodeDepth,
305305
getNodeAbsolutePosition,
306+
calculateRelativePosition,
306307
isPointInLoopNode,
307308
resizeLoopNodes,
308309
updateNodeParent: updateNodeParentUtil,
@@ -2454,20 +2455,54 @@ const WorkflowContent = React.memo(() => {
24542455
})
24552456

24562457
if (validNodes.length > 0) {
2457-
// Build updates for all valid nodes
2458-
const updates = validNodes.map((n) => {
2458+
const rawUpdates = validNodes.map((n) => {
24592459
const edgesToRemove = edgesForDisplay.filter(
24602460
(e) => e.source === n.id || e.target === n.id
24612461
)
2462+
const newPosition = calculateRelativePosition(n.id, potentialParentId, true)
24622463
return {
24632464
blockId: n.id,
24642465
newParentId: potentialParentId,
2466+
newPosition,
24652467
affectedEdges: edgesToRemove,
24662468
}
24672469
})
24682470

2471+
const minX = Math.min(...rawUpdates.map((u) => u.newPosition.x))
2472+
const minY = Math.min(...rawUpdates.map((u) => u.newPosition.y))
2473+
2474+
const targetMinX = CONTAINER_DIMENSIONS.LEFT_PADDING
2475+
const targetMinY = CONTAINER_DIMENSIONS.HEADER_HEIGHT + CONTAINER_DIMENSIONS.TOP_PADDING
2476+
2477+
const shiftX = minX < targetMinX ? targetMinX - minX : 0
2478+
const shiftY = minY < targetMinY ? targetMinY - minY : 0
2479+
2480+
const updates = rawUpdates.map((u) => ({
2481+
...u,
2482+
newPosition: {
2483+
x: u.newPosition.x + shiftX,
2484+
y: u.newPosition.y + shiftY,
2485+
},
2486+
}))
2487+
24692488
collaborativeBatchUpdateParent(updates)
24702489

2490+
setDisplayNodes((nodes) =>
2491+
nodes.map((node) => {
2492+
const update = updates.find((u) => u.blockId === node.id)
2493+
if (update) {
2494+
return {
2495+
...node,
2496+
position: update.newPosition,
2497+
parentId: update.newParentId,
2498+
}
2499+
}
2500+
return node
2501+
})
2502+
)
2503+
2504+
resizeLoopNodesWrapper()
2505+
24712506
logger.info('Batch moved nodes into subflow', {
24722507
targetParentId: potentialParentId,
24732508
nodeCount: validNodes.length,
@@ -2613,6 +2648,8 @@ const WorkflowContent = React.memo(() => {
26132648
edgesForDisplay,
26142649
removeEdgesForNode,
26152650
getNodeAbsolutePosition,
2651+
calculateRelativePosition,
2652+
resizeLoopNodesWrapper,
26162653
getDragStartPosition,
26172654
setDragStartPosition,
26182655
addNotification,
@@ -2805,19 +2842,54 @@ const WorkflowContent = React.memo(() => {
28052842
})
28062843

28072844
if (validNodes.length > 0) {
2808-
const updates = validNodes.map((n: Node) => {
2845+
const rawUpdates = validNodes.map((n: Node) => {
28092846
const edgesToRemove = edgesForDisplay.filter(
28102847
(e) => e.source === n.id || e.target === n.id
28112848
)
2849+
const newPosition = calculateRelativePosition(n.id, potentialParentId, true)
28122850
return {
28132851
blockId: n.id,
28142852
newParentId: potentialParentId,
2853+
newPosition,
28152854
affectedEdges: edgesToRemove,
28162855
}
28172856
})
28182857

2858+
const minX = Math.min(...rawUpdates.map((u) => u.newPosition.x))
2859+
const minY = Math.min(...rawUpdates.map((u) => u.newPosition.y))
2860+
2861+
const targetMinX = CONTAINER_DIMENSIONS.LEFT_PADDING
2862+
const targetMinY = CONTAINER_DIMENSIONS.HEADER_HEIGHT + CONTAINER_DIMENSIONS.TOP_PADDING
2863+
2864+
const shiftX = minX < targetMinX ? targetMinX - minX : 0
2865+
const shiftY = minY < targetMinY ? targetMinY - minY : 0
2866+
2867+
const updates = rawUpdates.map((u) => ({
2868+
...u,
2869+
newPosition: {
2870+
x: u.newPosition.x + shiftX,
2871+
y: u.newPosition.y + shiftY,
2872+
},
2873+
}))
2874+
28192875
collaborativeBatchUpdateParent(updates)
28202876

2877+
setDisplayNodes((nodes) =>
2878+
nodes.map((node) => {
2879+
const update = updates.find((u) => u.blockId === node.id)
2880+
if (update) {
2881+
return {
2882+
...node,
2883+
position: update.newPosition,
2884+
parentId: update.newParentId,
2885+
}
2886+
}
2887+
return node
2888+
})
2889+
)
2890+
2891+
resizeLoopNodesWrapper()
2892+
28212893
logger.info('Batch moved selection into subflow', {
28222894
targetParentId: potentialParentId,
28232895
nodeCount: validNodes.length,
@@ -2835,6 +2907,8 @@ const WorkflowContent = React.memo(() => {
28352907
getNodes,
28362908
collaborativeBatchUpdatePositions,
28372909
collaborativeBatchUpdateParent,
2910+
calculateRelativePosition,
2911+
resizeLoopNodesWrapper,
28382912
potentialParentId,
28392913
dragStartParentId,
28402914
edgesForDisplay,

apps/sim/hooks/use-collaborative-workflow.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,7 @@ export function useCollaborativeWorkflow() {
929929
updates: Array<{
930930
blockId: string
931931
newParentId: string | null
932+
newPosition: { x: number; y: number }
932933
affectedEdges: Edge[]
933934
}>
934935
) => {
@@ -943,14 +944,13 @@ export function useCollaborativeWorkflow() {
943944
const block = workflowStore.blocks[u.blockId]
944945
const oldParentId = block?.data?.parentId
945946
const oldPosition = block?.position || { x: 0, y: 0 }
946-
const newPosition = oldPosition
947947

948948
return {
949949
blockId: u.blockId,
950950
oldParentId,
951951
newParentId: u.newParentId || undefined,
952952
oldPosition,
953-
newPosition,
953+
newPosition: u.newPosition,
954954
affectedEdges: u.affectedEdges,
955955
}
956956
})
@@ -959,6 +959,7 @@ export function useCollaborativeWorkflow() {
959959
if (update.affectedEdges.length > 0) {
960960
update.affectedEdges.forEach((e) => workflowStore.removeEdge(e.id))
961961
}
962+
workflowStore.updateBlockPosition(update.blockId, update.newPosition)
962963
if (update.newParentId) {
963964
workflowStore.updateParentId(update.blockId, update.newParentId, 'parent')
964965
}

0 commit comments

Comments
 (0)