Skip to content

Commit 8132fb5

Browse files
committed
Add Node Manager to Node Creation Methods
1 parent 4eed892 commit 8132fb5

File tree

8 files changed

+86
-28
lines changed

8 files changed

+86
-28
lines changed

src/components/shared/ReactFlow/FlowCanvas/FlowCanvas.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,13 @@ const FlowCanvas = ({
103103
const { clearContent } = useContextPanel();
104104
const { setReactFlowInstance: setReactFlowInstanceForOverlay } =
105105
useNodesOverlay();
106-
const { componentSpec, setComponentSpec, graphSpec, updateGraphSpec } =
107-
useComponentSpec();
106+
const {
107+
componentSpec,
108+
setComponentSpec,
109+
graphSpec,
110+
updateGraphSpec,
111+
nodeManager,
112+
} = useComponentSpec();
108113
const { preserveIOSelectionOnSpecChange, resetPrevSpec } =
109114
useIOSelectionPersistence();
110115

@@ -292,8 +297,9 @@ const FlowCanvas = ({
292297
connectable: !readOnly && !!nodesConnectable,
293298
readOnly: !!readOnly,
294299
nodeCallbacks,
300+
nodeManager,
295301
}),
296-
[readOnly, nodesConnectable, nodeCallbacks],
302+
[readOnly, nodesConnectable, nodeCallbacks, nodeManager],
297303
);
298304

299305
const onConnect = useCallback(
@@ -634,6 +640,7 @@ const FlowCanvas = ({
634640
const { updatedComponentSpec, newNodes, updatedNodes } = duplicateNodes(
635641
componentSpec,
636642
selectedNodes,
643+
nodeManager,
637644
{ selected: true },
638645
);
639646

@@ -643,7 +650,7 @@ const FlowCanvas = ({
643650
updatedNodes,
644651
newNodes,
645652
});
646-
}, [componentSpec, selectedNodes, setComponentSpec, setNodes]);
653+
}, [componentSpec, selectedNodes, nodeManager, setComponentSpec, setNodes]);
647654

648655
const onUpgradeNodes = useCallback(async () => {
649656
let newGraphSpec = graphSpec;
@@ -802,6 +809,7 @@ const FlowCanvas = ({
802809
const { newNodes, updatedComponentSpec } = duplicateNodes(
803810
componentSpec,
804811
nodesToPaste,
812+
nodeManager,
805813
{ position: reactFlowCenter, connection: "internal" },
806814
);
807815

@@ -827,6 +835,7 @@ const FlowCanvas = ({
827835
nodes,
828836
reactFlowInstance,
829837
store,
838+
nodeManager,
830839
updateOrAddNodes,
831840
setComponentSpec,
832841
readOnly,

src/components/shared/ReactFlow/FlowCanvas/utils/duplicateNodes.test.ts

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Node } from "@xyflow/react";
22
import { describe, expect, it, vi } from "vitest";
33

4+
import { NodeManager } from "@/nodeManager";
45
import type { TaskNodeData } from "@/types/nodes";
56
import type {
67
ComponentSpec,
@@ -17,6 +18,8 @@ import {
1718

1819
import { duplicateNodes } from "./duplicateNodes";
1920

21+
const createMockNodeManager = () => new NodeManager();
22+
2023
// Mock utility functions
2124
const mockTaskSpec: TaskSpec = {
2225
componentRef: { name: "test-component" },
@@ -153,8 +156,9 @@ describe("duplicateNodes", () => {
153156
};
154157

155158
const nodes: Node[] = [];
159+
const nodeManager = createMockNodeManager();
156160

157-
expect(() => duplicateNodes(componentSpec, nodes)).toThrow(
161+
expect(() => duplicateNodes(componentSpec, nodes, nodeManager)).toThrow(
158162
"ComponentSpec does not contain a graph implementation.",
159163
);
160164
});
@@ -178,7 +182,8 @@ describe("duplicateNodes", () => {
178182
y: 100,
179183
});
180184

181-
const result = duplicateNodes(componentSpec, [taskNode]);
185+
const nodeManager = createMockNodeManager();
186+
const result = duplicateNodes(componentSpec, [taskNode], nodeManager);
182187

183188
expect(result.newNodes).toHaveLength(1);
184189
expect(result.newNodes[0].type).toBe("task");
@@ -208,7 +213,8 @@ describe("duplicateNodes", () => {
208213

209214
const inputNode = createMockInputNode("original-input", { x: 50, y: 50 });
210215

211-
const result = duplicateNodes(componentSpec, [inputNode]);
216+
const nodeManager = createMockNodeManager();
217+
const result = duplicateNodes(componentSpec, [inputNode], nodeManager);
212218

213219
expect(result.newNodes).toHaveLength(1);
214220
expect(result.newNodes[0].type).toBe("input");
@@ -243,7 +249,8 @@ describe("duplicateNodes", () => {
243249
y: 300,
244250
});
245251

246-
const result = duplicateNodes(componentSpec, [outputNode]);
252+
const nodeManager = createMockNodeManager();
253+
const result = duplicateNodes(componentSpec, [outputNode], nodeManager);
247254

248255
expect(result.newNodes).toHaveLength(1);
249256
expect(result.newNodes[0].type).toBe("output");
@@ -274,7 +281,8 @@ describe("duplicateNodes", () => {
274281
createMockTaskNode("task2", taskSpec2, { x: 200, y: 200 }),
275282
];
276283

277-
const result = duplicateNodes(componentSpec, nodes);
284+
const nodeManager = createMockNodeManager();
285+
const result = duplicateNodes(componentSpec, nodes, nodeManager);
278286

279287
expect(result.newNodes).toHaveLength(2);
280288
expect(result.newNodes.map((n) => n.id)).toEqual([
@@ -293,7 +301,8 @@ describe("duplicateNodes", () => {
293301
const taskNode = createMockTaskNode("original-task", mockTaskSpec);
294302
taskNode.selected = true;
295303

296-
const result = duplicateNodes(componentSpec, [taskNode], {
304+
const nodeManager = createMockNodeManager();
305+
const result = duplicateNodes(componentSpec, [taskNode], nodeManager, {
297306
selected: false,
298307
});
299308

@@ -317,7 +326,8 @@ describe("duplicateNodes", () => {
317326

318327
const taskNode = createMockTaskNode("original-task", taskSpecWithStatus);
319328

320-
const result = duplicateNodes(componentSpec, [taskNode], {
329+
const nodeManager = createMockNodeManager();
330+
const result = duplicateNodes(componentSpec, [taskNode], nodeManager, {
321331
status: false,
322332
});
323333

@@ -343,7 +353,8 @@ describe("duplicateNodes", () => {
343353
createMockTaskNode("task2", mockTaskSpec, { x: 200, y: 200 }),
344354
];
345355

346-
const result = duplicateNodes(componentSpec, nodes, {
356+
const nodeManager = createMockNodeManager();
357+
const result = duplicateNodes(componentSpec, nodes, nodeManager, {
347358
position: { x: 500, y: 500 },
348359
});
349360

@@ -399,7 +410,8 @@ describe("duplicateNodes", () => {
399410
createMockTaskNode("task2", task2),
400411
];
401412

402-
const result = duplicateNodes(componentSpec, nodes, {
413+
const nodeManager = createMockNodeManager();
414+
const result = duplicateNodes(componentSpec, nodes, nodeManager, {
403415
connection: "none",
404416
});
405417

@@ -422,7 +434,8 @@ describe("duplicateNodes", () => {
422434
createMockTaskNode("task2", task2),
423435
];
424436

425-
const result = duplicateNodes(componentSpec, nodes, {
437+
const nodeManager = createMockNodeManager();
438+
const result = duplicateNodes(componentSpec, nodes, nodeManager, {
426439
connection: "internal",
427440
});
428441

@@ -474,7 +487,8 @@ describe("duplicateNodes", () => {
474487
createMockTaskNode("task2", task2WithConnections),
475488
];
476489

477-
const result = duplicateNodes(componentSpec, nodes, {
490+
const nodeManager = createMockNodeManager();
491+
const result = duplicateNodes(componentSpec, nodes, nodeManager, {
478492
connection: "external",
479493
});
480494

@@ -507,7 +521,8 @@ describe("duplicateNodes", () => {
507521
createMockTaskNode("task2", task2),
508522
];
509523

510-
const result = duplicateNodes(componentSpec, nodes, {
524+
const nodeManager = createMockNodeManager();
525+
const result = duplicateNodes(componentSpec, nodes, nodeManager, {
511526
connection: "all",
512527
});
513528

@@ -549,7 +564,8 @@ describe("duplicateNodes", () => {
549564
createMockTaskNode("task1", taskSpec),
550565
];
551566

552-
const result = duplicateNodes(componentSpec, nodes, {
567+
const nodeManager = createMockNodeManager();
568+
const result = duplicateNodes(componentSpec, nodes, nodeManager, {
553569
connection: "all",
554570
});
555571

@@ -586,7 +602,8 @@ describe("duplicateNodes", () => {
586602
createMockOutputNode("graph-output"),
587603
];
588604

589-
const result = duplicateNodes(componentSpec, nodes, {
605+
const nodeManager = createMockNodeManager();
606+
const result = duplicateNodes(componentSpec, nodes, nodeManager, {
590607
connection: "all",
591608
});
592609

@@ -609,7 +626,8 @@ describe("duplicateNodes", () => {
609626
describe("edge cases", () => {
610627
it("should handle empty node array", () => {
611628
const componentSpec = createMockComponentSpec();
612-
const result = duplicateNodes(componentSpec, []);
629+
const nodeManager = createMockNodeManager();
630+
const result = duplicateNodes(componentSpec, [], nodeManager);
613631

614632
expect(result.newNodes).toHaveLength(0);
615633
expect(result.nodeIdMap).toEqual({});
@@ -623,7 +641,8 @@ describe("duplicateNodes", () => {
623641
const taskNode = createMockTaskNode("original-task", mockTaskSpec);
624642
taskNode.measured = { width: 300, height: 200 };
625643

626-
const result = duplicateNodes(componentSpec, [taskNode]);
644+
const nodeManager = createMockNodeManager();
645+
const result = duplicateNodes(componentSpec, [taskNode], nodeManager);
627646

628647
expect(result.newNodes[0].measured).toEqual({ width: 300, height: 200 });
629648
});
@@ -643,7 +662,8 @@ describe("duplicateNodes", () => {
643662
taskSpecWithoutPosition,
644663
);
645664

646-
const result = duplicateNodes(componentSpec, [taskNode]);
665+
const nodeManager = createMockNodeManager();
666+
const result = duplicateNodes(componentSpec, [taskNode], nodeManager);
647667

648668
expect(result.newNodes).toHaveLength(1);
649669
expect(result.newNodes[0].position).toEqual({ x: 110, y: 110 });
@@ -658,7 +678,8 @@ describe("duplicateNodes", () => {
658678

659679
const taskNode = createMockTaskNode("original-task", mockTaskSpec);
660680

661-
const result = duplicateNodes(componentSpec, [taskNode]);
681+
const nodeManager = createMockNodeManager();
682+
const result = duplicateNodes(componentSpec, [taskNode], nodeManager);
662683

663684
expect(result).toHaveProperty("updatedComponentSpec");
664685
expect(result).toHaveProperty("nodeIdMap");

src/components/shared/ReactFlow/FlowCanvas/utils/duplicateNodes.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { type Node, type XYPosition } from "@xyflow/react";
22

3+
import type { NodeManager } from "@/nodeManager";
34
import type { IONodeData, NodeData, TaskNodeData } from "@/types/nodes";
45
import {
56
type ComponentSpec,
@@ -44,6 +45,7 @@ type ConnectionMode = "none" | "internal" | "external" | "all";
4445
export const duplicateNodes = (
4546
componentSpec: ComponentSpec,
4647
nodesToDuplicate: Node[],
48+
nodeManager: NodeManager,
4749
config?: {
4850
selected?: boolean;
4951
position?: XYPosition;
@@ -288,6 +290,7 @@ export const duplicateNodes = (
288290
readOnly: taskData.readOnly,
289291
connectable: taskData.connectable,
290292
callbacks: convertTaskCallbacksToNodeCallbacks(taskData.callbacks),
293+
nodeManager,
291294
};
292295

293296
const newNode = createTaskNode([newTaskId, newTaskSpec], nodeData);
@@ -319,6 +322,7 @@ export const duplicateNodes = (
319322
const inputData = originalNode.data as IONodeData;
320323
const nodeData: NodeData = {
321324
readOnly: inputData.readOnly,
325+
nodeManager,
322326
};
323327

324328
const newNode = createInputNode(newInputSpec, nodeData);
@@ -350,6 +354,7 @@ export const duplicateNodes = (
350354
const outputData = originalNode.data as IONodeData;
351355
const nodeData: NodeData = {
352356
readOnly: outputData.readOnly,
357+
nodeManager,
353358
};
354359

355360
const newNode = createOutputNode(newOutputSpec, nodeData);

src/hooks/useNodeCallbacks.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,13 @@ export const useNodeCallbacks = ({
3434
const notify = useToastNotification();
3535
const reactFlowInstance = useReactFlow();
3636

37-
const { graphSpec, updateGraphSpec, componentSpec, setComponentSpec } =
38-
useComponentSpec();
37+
const {
38+
graphSpec,
39+
updateGraphSpec,
40+
componentSpec,
41+
setComponentSpec,
42+
nodeManager,
43+
} = useComponentSpec();
3944

4045
// Workaround for nodes state being stale in task node callbacks
4146
const getNodeById = useCallback(
@@ -139,6 +144,7 @@ export const useNodeCallbacks = ({
139144
const { updatedComponentSpec, newNodes, updatedNodes } = duplicateNodes(
140145
componentSpec,
141146
[node],
147+
nodeManager,
142148
{ selected },
143149
);
144150

@@ -149,7 +155,13 @@ export const useNodeCallbacks = ({
149155
newNodes,
150156
});
151157
},
152-
[componentSpec, getNodeById, setComponentSpec, updateOrAddNodes],
158+
[
159+
componentSpec,
160+
nodeManager,
161+
getNodeById,
162+
setComponentSpec,
163+
updateOrAddNodes,
164+
],
153165
);
154166

155167
const onUpgrade = useCallback(

src/types/nodes.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { NodeManager } from "@/nodeManager";
12
import type {
23
ArgumentType,
34
ComponentReference,
@@ -12,6 +13,7 @@ export interface NodeData extends Record<string, unknown> {
1213
readOnly: boolean;
1314
connectable?: boolean;
1415
callbacks?: NodeCallbacks;
16+
nodeManager: NodeManager;
1517
}
1618

1719
export interface TaskNodeData extends Record<string, unknown> {

src/utils/nodes/createInputNode.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ import { inputNameToNodeId } from "./nodeIdUtils";
88

99
export const createInputNode = (input: InputSpec, nodeData: NodeData) => {
1010
const { name, annotations } = input;
11-
const { readOnly } = nodeData;
11+
const { nodeManager, readOnly } = nodeData;
12+
13+
const newNodeId = nodeManager?.getNodeId(name, "input");
14+
console.log("Creating input node:", { name, nodeId: newNodeId });
1215

1316
const position = extractPositionFromAnnotations(annotations);
1417
const nodeId = inputNameToNodeId(name);

src/utils/nodes/createOutputNode.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ import { outputNameToNodeId } from "./nodeIdUtils";
88

99
export const createOutputNode = (output: OutputSpec, nodeData: NodeData) => {
1010
const { name, annotations } = output;
11-
const { readOnly } = nodeData;
11+
const { nodeManager, readOnly } = nodeData;
12+
13+
const newNodeId = nodeManager?.getNodeId(name, "output");
14+
console.log("Creating output node:", { name, nodeId: newNodeId });
1215

1316
const position = extractPositionFromAnnotations(annotations);
1417
const nodeId = outputNameToNodeId(name);

src/utils/nodes/createTaskNode.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ export const createTaskNode = (
1212
nodeData: NodeData,
1313
) => {
1414
const [taskId, taskSpec] = task;
15-
const { callbacks, connectable, ...data } = nodeData;
15+
const { nodeManager, callbacks, connectable, ...data } = nodeData;
16+
17+
const newNodeId = nodeManager?.getNodeId(taskId, "task");
18+
console.log("Creating task node:", { taskId, nodeId: newNodeId });
1619

1720
const position = extractPositionFromAnnotations(taskSpec.annotations);
1821
const nodeId = taskIdToNodeId(taskId);

0 commit comments

Comments
 (0)