Skip to content

Commit d738a0a

Browse files
committed
Add Node Manager
1 parent 11d5dce commit d738a0a

File tree

7 files changed

+215
-34
lines changed

7 files changed

+215
-34
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

@@ -280,8 +285,9 @@ const FlowCanvas = ({
280285
connectable: !readOnly && !!nodesConnectable,
281286
readOnly: !!readOnly,
282287
nodeCallbacks,
288+
nodeManager,
283289
}),
284-
[readOnly, nodesConnectable, nodeCallbacks],
290+
[readOnly, nodesConnectable, nodeCallbacks, nodeManager],
285291
);
286292

287293
const onConnect = useCallback(
@@ -622,6 +628,7 @@ const FlowCanvas = ({
622628
const { updatedComponentSpec, newNodes, updatedNodes } = duplicateNodes(
623629
componentSpec,
624630
selectedNodes,
631+
nodeManager,
625632
{ selected: true },
626633
);
627634

@@ -631,7 +638,7 @@ const FlowCanvas = ({
631638
updatedNodes,
632639
newNodes,
633640
});
634-
}, [componentSpec, selectedNodes, setComponentSpec, setNodes]);
641+
}, [componentSpec, selectedNodes, setComponentSpec, setNodes, nodeManager]);
635642

636643
const onUpgradeNodes = useCallback(async () => {
637644
let newGraphSpec = graphSpec;
@@ -790,6 +797,7 @@ const FlowCanvas = ({
790797
const { newNodes, updatedComponentSpec } = duplicateNodes(
791798
componentSpec,
792799
nodesToPaste,
800+
nodeManager,
793801
{ position: reactFlowCenter, connection: "internal" },
794802
);
795803

@@ -815,6 +823,7 @@ const FlowCanvas = ({
815823
nodes,
816824
reactFlowInstance,
817825
store,
826+
nodeManager,
818827
updateOrAddNodes,
819828
setComponentSpec,
820829
readOnly,

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

Lines changed: 55 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,9 @@ describe("duplicateNodes", () => {
178182
y: 100,
179183
});
180184

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

183189
expect(result.newNodes).toHaveLength(1);
184190
expect(result.newNodes[0].type).toBe("task");
@@ -208,7 +214,9 @@ describe("duplicateNodes", () => {
208214

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

211-
const result = duplicateNodes(componentSpec, [inputNode]);
217+
const nodeManager = createMockNodeManager();
218+
219+
const result = duplicateNodes(componentSpec, [inputNode], nodeManager);
212220

213221
expect(result.newNodes).toHaveLength(1);
214222
expect(result.newNodes[0].type).toBe("input");
@@ -243,7 +251,9 @@ describe("duplicateNodes", () => {
243251
y: 300,
244252
});
245253

246-
const result = duplicateNodes(componentSpec, [outputNode]);
254+
const nodeManager = createMockNodeManager();
255+
256+
const result = duplicateNodes(componentSpec, [outputNode], nodeManager);
247257

248258
expect(result.newNodes).toHaveLength(1);
249259
expect(result.newNodes[0].type).toBe("output");
@@ -274,7 +284,9 @@ describe("duplicateNodes", () => {
274284
createMockTaskNode("task2", taskSpec2, { x: 200, y: 200 }),
275285
];
276286

277-
const result = duplicateNodes(componentSpec, nodes);
287+
const nodeManager = createMockNodeManager();
288+
289+
const result = duplicateNodes(componentSpec, nodes, nodeManager);
278290

279291
expect(result.newNodes).toHaveLength(2);
280292
expect(result.newNodes.map((n) => n.id)).toEqual([
@@ -293,7 +305,9 @@ describe("duplicateNodes", () => {
293305
const taskNode = createMockTaskNode("original-task", mockTaskSpec);
294306
taskNode.selected = true;
295307

296-
const result = duplicateNodes(componentSpec, [taskNode], {
308+
const nodeManager = createMockNodeManager();
309+
310+
const result = duplicateNodes(componentSpec, [taskNode], nodeManager, {
297311
selected: false,
298312
});
299313

@@ -317,7 +331,9 @@ describe("duplicateNodes", () => {
317331

318332
const taskNode = createMockTaskNode("original-task", taskSpecWithStatus);
319333

320-
const result = duplicateNodes(componentSpec, [taskNode], {
334+
const nodeManager = createMockNodeManager();
335+
336+
const result = duplicateNodes(componentSpec, [taskNode], nodeManager, {
321337
status: false,
322338
});
323339

@@ -343,7 +359,9 @@ describe("duplicateNodes", () => {
343359
createMockTaskNode("task2", mockTaskSpec, { x: 200, y: 200 }),
344360
];
345361

346-
const result = duplicateNodes(componentSpec, nodes, {
362+
const nodeManager = createMockNodeManager();
363+
364+
const result = duplicateNodes(componentSpec, nodes, nodeManager, {
347365
position: { x: 500, y: 500 },
348366
});
349367

@@ -399,7 +417,9 @@ describe("duplicateNodes", () => {
399417
createMockTaskNode("task2", task2),
400418
];
401419

402-
const result = duplicateNodes(componentSpec, nodes, {
420+
const nodeManager = createMockNodeManager();
421+
422+
const result = duplicateNodes(componentSpec, nodes, nodeManager, {
403423
connection: "none",
404424
});
405425

@@ -422,7 +442,9 @@ describe("duplicateNodes", () => {
422442
createMockTaskNode("task2", task2),
423443
];
424444

425-
const result = duplicateNodes(componentSpec, nodes, {
445+
const nodeManager = createMockNodeManager();
446+
447+
const result = duplicateNodes(componentSpec, nodes, nodeManager, {
426448
connection: "internal",
427449
});
428450

@@ -474,7 +496,9 @@ describe("duplicateNodes", () => {
474496
createMockTaskNode("task2", task2WithConnections),
475497
];
476498

477-
const result = duplicateNodes(componentSpec, nodes, {
499+
const nodeManager = createMockNodeManager();
500+
501+
const result = duplicateNodes(componentSpec, nodes, nodeManager, {
478502
connection: "external",
479503
});
480504

@@ -507,7 +531,9 @@ describe("duplicateNodes", () => {
507531
createMockTaskNode("task2", task2),
508532
];
509533

510-
const result = duplicateNodes(componentSpec, nodes, {
534+
const nodeManager = createMockNodeManager();
535+
536+
const result = duplicateNodes(componentSpec, nodes, nodeManager, {
511537
connection: "all",
512538
});
513539

@@ -549,7 +575,9 @@ describe("duplicateNodes", () => {
549575
createMockTaskNode("task1", taskSpec),
550576
];
551577

552-
const result = duplicateNodes(componentSpec, nodes, {
578+
const nodeManager = createMockNodeManager();
579+
580+
const result = duplicateNodes(componentSpec, nodes, nodeManager, {
553581
connection: "all",
554582
});
555583

@@ -586,7 +614,9 @@ describe("duplicateNodes", () => {
586614
createMockOutputNode("graph-output"),
587615
];
588616

589-
const result = duplicateNodes(componentSpec, nodes, {
617+
const nodeManager = createMockNodeManager();
618+
619+
const result = duplicateNodes(componentSpec, nodes, nodeManager, {
590620
connection: "all",
591621
});
592622

@@ -609,7 +639,8 @@ describe("duplicateNodes", () => {
609639
describe("edge cases", () => {
610640
it("should handle empty node array", () => {
611641
const componentSpec = createMockComponentSpec();
612-
const result = duplicateNodes(componentSpec, []);
642+
const nodeManager = createMockNodeManager();
643+
const result = duplicateNodes(componentSpec, [], nodeManager);
613644

614645
expect(result.newNodes).toHaveLength(0);
615646
expect(result.nodeIdMap).toEqual({});
@@ -623,7 +654,9 @@ describe("duplicateNodes", () => {
623654
const taskNode = createMockTaskNode("original-task", mockTaskSpec);
624655
taskNode.measured = { width: 300, height: 200 };
625656

626-
const result = duplicateNodes(componentSpec, [taskNode]);
657+
const nodeManager = createMockNodeManager();
658+
659+
const result = duplicateNodes(componentSpec, [taskNode], nodeManager);
627660

628661
expect(result.newNodes[0].measured).toEqual({ width: 300, height: 200 });
629662
});
@@ -643,7 +676,9 @@ describe("duplicateNodes", () => {
643676
taskSpecWithoutPosition,
644677
);
645678

646-
const result = duplicateNodes(componentSpec, [taskNode]);
679+
const nodeManager = createMockNodeManager();
680+
681+
const result = duplicateNodes(componentSpec, [taskNode], nodeManager);
647682

648683
expect(result.newNodes).toHaveLength(1);
649684
expect(result.newNodes[0].position).toEqual({ x: 110, y: 110 });
@@ -658,7 +693,9 @@ describe("duplicateNodes", () => {
658693

659694
const taskNode = createMockTaskNode("original-task", mockTaskSpec);
660695

661-
const result = duplicateNodes(componentSpec, [taskNode]);
696+
const nodeManager = createMockNodeManager();
697+
698+
const result = duplicateNodes(componentSpec, [taskNode], nodeManager);
662699

663700
expect(result).toHaveProperty("updatedComponentSpec");
664701
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(

0 commit comments

Comments
 (0)