Skip to content

Commit 2c2b485

Browse files
authored
fix(workflow): update container dimensions on keyboard movement (#3043)
* fix(workflow): update container dimensions on keyboard movement * fix(workflow): avoid duplicate container updates during drag Add !change.dragging check to only handle keyboard movements in onNodesChange, since mouse drags are already handled by onNodeDrag. * fix(workflow): persist keyboard movements to backend Keyboard arrow key movements now call collaborativeBatchUpdatePositions to sync position changes to the backend for persistence and real-time collaboration. * improvement(cmdk): refactor search modal to use cmdk + fix icon SVG IDs (#3044) * improvement(cmdk): refactor search modal to use cmdk + fix icon SVG IDs * chore: remove unrelated workflow.tsx changes * chore: remove comments * chore: add devtools middleware to search modal store * fix: allow search data re-initialization when permissions change * fix: include keywords in search filter + show service name in tool operations * fix: correct filterBlocks type signature * fix: move generic to function parameter position * fix(mcp): correct event handler type for onInput * perf: always render command palette for instant opening * fix: clear search input when modal reopens * fix(helm): move rotationPolicy under privateKey for cert-manager compatibility (#3046) * fix(helm): move rotationPolicy under privateKey for cert-manager compatibility * docs(helm): add reclaimPolicy Retain guidance for production database storage * fix(helm): prevent empty branding ConfigMap creation * fix(workflow): avoid duplicate position updates on drag end Check isInDragOperation before persisting in onNodesChange to prevent duplicate calls. Drag-end events have dragStartPosition still set, while keyboard movements don't, allowing proper distinction.
1 parent 01e0723 commit 2c2b485

File tree

1 file changed

+56
-28
lines changed
  • apps/sim/app/workspace/[workspaceId]/w/[workflowId]

1 file changed

+56
-28
lines changed

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

Lines changed: 56 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2302,41 +2302,20 @@ const WorkflowContent = React.memo(() => {
23022302
window.removeEventListener('remove-from-subflow', handleRemoveFromSubflow as EventListener)
23032303
}, [blocks, edgesForDisplay, getNodeAbsolutePosition, collaborativeBatchUpdateParent])
23042304

2305-
/** Handles node changes - applies changes and resolves parent-child selection conflicts. */
2306-
const onNodesChange = useCallback(
2307-
(changes: NodeChange[]) => {
2308-
selectedIdsRef.current = null
2309-
setDisplayNodes((nds) => {
2310-
const updated = applyNodeChanges(changes, nds)
2311-
const hasSelectionChange = changes.some((c) => c.type === 'select')
2312-
if (!hasSelectionChange) return updated
2313-
const resolved = resolveParentChildSelectionConflicts(updated, blocks)
2314-
selectedIdsRef.current = resolved.filter((node) => node.selected).map((node) => node.id)
2315-
return resolved
2316-
})
2317-
const selectedIds = selectedIdsRef.current as string[] | null
2318-
if (selectedIds !== null) {
2319-
syncPanelWithSelection(selectedIds)
2320-
}
2321-
},
2322-
[blocks]
2323-
)
2324-
23252305
/**
2326-
* Updates container dimensions in displayNodes during drag.
2327-
* This allows live resizing of containers as their children are dragged.
2306+
* Updates container dimensions in displayNodes during drag or keyboard movement.
23282307
*/
2329-
const updateContainerDimensionsDuringDrag = useCallback(
2330-
(draggedNodeId: string, draggedNodePosition: { x: number; y: number }) => {
2331-
const parentId = blocks[draggedNodeId]?.data?.parentId
2308+
const updateContainerDimensionsDuringMove = useCallback(
2309+
(movedNodeId: string, movedNodePosition: { x: number; y: number }) => {
2310+
const parentId = blocks[movedNodeId]?.data?.parentId
23322311
if (!parentId) return
23332312

23342313
setDisplayNodes((currentNodes) => {
23352314
const childNodes = currentNodes.filter((n) => n.parentId === parentId)
23362315
if (childNodes.length === 0) return currentNodes
23372316

23382317
const childPositions = childNodes.map((node) => {
2339-
const nodePosition = node.id === draggedNodeId ? draggedNodePosition : node.position
2318+
const nodePosition = node.id === movedNodeId ? movedNodePosition : node.position
23402319
const { width, height } = getBlockDimensions(node.id)
23412320
return { x: nodePosition.x, y: nodePosition.y, width, height }
23422321
})
@@ -2367,6 +2346,55 @@ const WorkflowContent = React.memo(() => {
23672346
[blocks, getBlockDimensions]
23682347
)
23692348

2349+
/** Handles node changes - applies changes and resolves parent-child selection conflicts. */
2350+
const onNodesChange = useCallback(
2351+
(changes: NodeChange[]) => {
2352+
selectedIdsRef.current = null
2353+
setDisplayNodes((nds) => {
2354+
const updated = applyNodeChanges(changes, nds)
2355+
const hasSelectionChange = changes.some((c) => c.type === 'select')
2356+
if (!hasSelectionChange) return updated
2357+
const resolved = resolveParentChildSelectionConflicts(updated, blocks)
2358+
selectedIdsRef.current = resolved.filter((node) => node.selected).map((node) => node.id)
2359+
return resolved
2360+
})
2361+
const selectedIds = selectedIdsRef.current as string[] | null
2362+
if (selectedIds !== null) {
2363+
syncPanelWithSelection(selectedIds)
2364+
}
2365+
2366+
// Handle position changes (e.g., from keyboard arrow key movement)
2367+
// Update container dimensions when child nodes are moved and persist to backend
2368+
// Only persist if not in a drag operation (drag-end is handled by onNodeDragStop)
2369+
const isInDragOperation =
2370+
getDragStartPosition() !== null || multiNodeDragStartRef.current.size > 0
2371+
const keyboardPositionUpdates: Array<{ id: string; position: { x: number; y: number } }> = []
2372+
for (const change of changes) {
2373+
if (
2374+
change.type === 'position' &&
2375+
!change.dragging &&
2376+
'position' in change &&
2377+
change.position
2378+
) {
2379+
updateContainerDimensionsDuringMove(change.id, change.position)
2380+
if (!isInDragOperation) {
2381+
keyboardPositionUpdates.push({ id: change.id, position: change.position })
2382+
}
2383+
}
2384+
}
2385+
// Persist keyboard movements to backend for collaboration sync
2386+
if (keyboardPositionUpdates.length > 0) {
2387+
collaborativeBatchUpdatePositions(keyboardPositionUpdates)
2388+
}
2389+
},
2390+
[
2391+
blocks,
2392+
updateContainerDimensionsDuringMove,
2393+
collaborativeBatchUpdatePositions,
2394+
getDragStartPosition,
2395+
]
2396+
)
2397+
23702398
/**
23712399
* Effect to resize loops when nodes change (add/remove/position change).
23722400
* Runs on structural changes only - not during drag (position-only changes).
@@ -2611,7 +2639,7 @@ const WorkflowContent = React.memo(() => {
26112639

26122640
// If the node is inside a container, update container dimensions during drag
26132641
if (currentParentId) {
2614-
updateContainerDimensionsDuringDrag(node.id, node.position)
2642+
updateContainerDimensionsDuringMove(node.id, node.position)
26152643
}
26162644

26172645
// Check if this is a starter block - starter blocks should never be in containers
@@ -2728,7 +2756,7 @@ const WorkflowContent = React.memo(() => {
27282756
blocks,
27292757
getNodeAbsolutePosition,
27302758
getNodeDepth,
2731-
updateContainerDimensionsDuringDrag,
2759+
updateContainerDimensionsDuringMove,
27322760
highlightContainerNode,
27332761
]
27342762
)

0 commit comments

Comments
 (0)