Skip to content

Commit 85f2b7a

Browse files
committed
Add Node Manager to Node Creation Methods
1 parent 279521f commit 85f2b7a

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,
@@ -19,6 +20,8 @@ import {
1920

2021
import { duplicateNodes } from "./duplicateNodes";
2122

23+
const createMockNodeManager = () => new NodeManager();
24+
2225
// Mock utility functions
2326
const mockTaskSpec: TaskSpec = {
2427
componentRef: { name: "test-component" },
@@ -155,8 +158,9 @@ describe("duplicateNodes", () => {
155158
};
156159

157160
const nodes: Node[] = [];
161+
const nodeManager = createMockNodeManager();
158162

159-
expect(() => duplicateNodes(componentSpec, nodes)).toThrow(
163+
expect(() => duplicateNodes(componentSpec, nodes, nodeManager)).toThrow(
160164
"ComponentSpec does not contain a graph implementation.",
161165
);
162166
});
@@ -180,7 +184,8 @@ describe("duplicateNodes", () => {
180184
y: 100,
181185
});
182186

183-
const result = duplicateNodes(componentSpec, [taskNode]);
187+
const nodeManager = createMockNodeManager();
188+
const result = duplicateNodes(componentSpec, [taskNode], nodeManager);
184189

185190
expect(result.newNodes).toHaveLength(1);
186191
expect(result.newNodes[0].type).toBe("task");
@@ -210,7 +215,8 @@ describe("duplicateNodes", () => {
210215

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

213-
const result = duplicateNodes(componentSpec, [inputNode]);
218+
const nodeManager = createMockNodeManager();
219+
const result = duplicateNodes(componentSpec, [inputNode], nodeManager);
214220

215221
expect(result.newNodes).toHaveLength(1);
216222
expect(result.newNodes[0].type).toBe("input");
@@ -247,7 +253,8 @@ describe("duplicateNodes", () => {
247253
y: 300,
248254
});
249255

250-
const result = duplicateNodes(componentSpec, [outputNode]);
256+
const nodeManager = createMockNodeManager();
257+
const result = duplicateNodes(componentSpec, [outputNode], nodeManager);
251258

252259
expect(result.newNodes).toHaveLength(1);
253260
expect(result.newNodes[0].type).toBe("output");
@@ -278,7 +285,8 @@ describe("duplicateNodes", () => {
278285
createMockTaskNode("task2", taskSpec2, { x: 200, y: 200 }),
279286
];
280287

281-
const result = duplicateNodes(componentSpec, nodes);
288+
const nodeManager = createMockNodeManager();
289+
const result = duplicateNodes(componentSpec, nodes, nodeManager);
282290

283291
expect(result.newNodes).toHaveLength(2);
284292
expect(result.newNodes.map((n) => n.id)).toEqual([
@@ -297,7 +305,8 @@ describe("duplicateNodes", () => {
297305
const taskNode = createMockTaskNode("original-task", mockTaskSpec);
298306
taskNode.selected = true;
299307

300-
const result = duplicateNodes(componentSpec, [taskNode], {
308+
const nodeManager = createMockNodeManager();
309+
const result = duplicateNodes(componentSpec, [taskNode], nodeManager, {
301310
selected: false,
302311
});
303312

@@ -321,7 +330,8 @@ describe("duplicateNodes", () => {
321330

322331
const taskNode = createMockTaskNode("original-task", taskSpecWithStatus);
323332

324-
const result = duplicateNodes(componentSpec, [taskNode], {
333+
const nodeManager = createMockNodeManager();
334+
const result = duplicateNodes(componentSpec, [taskNode], nodeManager, {
325335
status: false,
326336
});
327337

@@ -347,7 +357,8 @@ describe("duplicateNodes", () => {
347357
createMockTaskNode("task2", mockTaskSpec, { x: 200, y: 200 }),
348358
];
349359

350-
const result = duplicateNodes(componentSpec, nodes, {
360+
const nodeManager = createMockNodeManager();
361+
const result = duplicateNodes(componentSpec, nodes, nodeManager, {
351362
position: { x: 500, y: 500 },
352363
});
353364

@@ -403,7 +414,8 @@ describe("duplicateNodes", () => {
403414
createMockTaskNode("task2", task2),
404415
];
405416

406-
const result = duplicateNodes(componentSpec, nodes, {
417+
const nodeManager = createMockNodeManager();
418+
const result = duplicateNodes(componentSpec, nodes, nodeManager, {
407419
connection: "none",
408420
});
409421

@@ -426,7 +438,8 @@ describe("duplicateNodes", () => {
426438
createMockTaskNode("task2", task2),
427439
];
428440

429-
const result = duplicateNodes(componentSpec, nodes, {
441+
const nodeManager = createMockNodeManager();
442+
const result = duplicateNodes(componentSpec, nodes, nodeManager, {
430443
connection: "internal",
431444
});
432445

@@ -478,7 +491,8 @@ describe("duplicateNodes", () => {
478491
createMockTaskNode("task2", task2WithConnections),
479492
];
480493

481-
const result = duplicateNodes(componentSpec, nodes, {
494+
const nodeManager = createMockNodeManager();
495+
const result = duplicateNodes(componentSpec, nodes, nodeManager, {
482496
connection: "external",
483497
});
484498

@@ -511,7 +525,8 @@ describe("duplicateNodes", () => {
511525
createMockTaskNode("task2", task2),
512526
];
513527

514-
const result = duplicateNodes(componentSpec, nodes, {
528+
const nodeManager = createMockNodeManager();
529+
const result = duplicateNodes(componentSpec, nodes, nodeManager, {
515530
connection: "all",
516531
});
517532

@@ -553,7 +568,8 @@ describe("duplicateNodes", () => {
553568
createMockTaskNode("task1", taskSpec),
554569
];
555570

556-
const result = duplicateNodes(componentSpec, nodes, {
571+
const nodeManager = createMockNodeManager();
572+
const result = duplicateNodes(componentSpec, nodes, nodeManager, {
557573
connection: "all",
558574
});
559575

@@ -590,7 +606,8 @@ describe("duplicateNodes", () => {
590606
createMockOutputNode("graph-output"),
591607
];
592608

593-
const result = duplicateNodes(componentSpec, nodes, {
609+
const nodeManager = createMockNodeManager();
610+
const result = duplicateNodes(componentSpec, nodes, nodeManager, {
594611
connection: "all",
595612
});
596613

@@ -613,7 +630,8 @@ describe("duplicateNodes", () => {
613630
describe("edge cases", () => {
614631
it("should handle empty node array", () => {
615632
const componentSpec = createMockComponentSpec();
616-
const result = duplicateNodes(componentSpec, []);
633+
const nodeManager = createMockNodeManager();
634+
const result = duplicateNodes(componentSpec, [], nodeManager);
617635

618636
expect(result.newNodes).toHaveLength(0);
619637
expect(result.nodeIdMap).toEqual({});
@@ -627,7 +645,8 @@ describe("duplicateNodes", () => {
627645
const taskNode = createMockTaskNode("original-task", mockTaskSpec);
628646
taskNode.measured = { width: 300, height: 200 };
629647

630-
const result = duplicateNodes(componentSpec, [taskNode]);
648+
const nodeManager = createMockNodeManager();
649+
const result = duplicateNodes(componentSpec, [taskNode], nodeManager);
631650

632651
expect(result.newNodes[0].measured).toEqual({ width: 300, height: 200 });
633652
});
@@ -647,7 +666,8 @@ describe("duplicateNodes", () => {
647666
taskSpecWithoutPosition,
648667
);
649668

650-
const result = duplicateNodes(componentSpec, [taskNode]);
669+
const nodeManager = createMockNodeManager();
670+
const result = duplicateNodes(componentSpec, [taskNode], nodeManager);
651671

652672
expect(result.newNodes).toHaveLength(1);
653673
expect(result.newNodes[0].position).toEqual({ x: 110, y: 110 });
@@ -662,7 +682,8 @@ describe("duplicateNodes", () => {
662682

663683
const taskNode = createMockTaskNode("original-task", mockTaskSpec);
664684

665-
const result = duplicateNodes(componentSpec, [taskNode]);
685+
const nodeManager = createMockNodeManager();
686+
const result = duplicateNodes(componentSpec, [taskNode], nodeManager);
666687

667688
expect(result).toHaveProperty("updatedComponentSpec");
668689
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,
@@ -48,6 +49,7 @@ type ConnectionMode = "none" | "internal" | "external" | "all";
4849
export const duplicateNodes = (
4950
componentSpec: ComponentSpec,
5051
nodesToDuplicate: Node[],
52+
nodeManager: NodeManager,
5153
config?: {
5254
selected?: boolean;
5355
position?: XYPosition;
@@ -295,6 +297,7 @@ export const duplicateNodes = (
295297
readOnly: taskData.readOnly,
296298
connectable: taskData.connectable,
297299
callbacks: convertTaskCallbacksToNodeCallbacks(taskData.callbacks),
300+
nodeManager,
298301
};
299302

300303
const newNode = createTaskNode([newTaskId, newTaskSpec], nodeData);
@@ -326,6 +329,7 @@ export const duplicateNodes = (
326329
const inputData = originalNode.data as IONodeData;
327330
const nodeData: NodeData = {
328331
readOnly: inputData.readOnly,
332+
nodeManager,
329333
};
330334

331335
const newNode = createInputNode(newInputSpec, nodeData);
@@ -357,6 +361,7 @@ export const duplicateNodes = (
357361
const outputData = originalNode.data as IONodeData;
358362
const nodeData: NodeData = {
359363
readOnly: outputData.readOnly,
364+
nodeManager,
360365
};
361366

362367
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 { extractPositionFromAnnotations } from "./extractPositionFromAnnotations
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 inputId = inputNameToInputId(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 { extractPositionFromAnnotations } from "./extractPositionFromAnnotations
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 outputId = outputNameToOutputId(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)