Merging dev into main #49
@@ -1,9 +1,9 @@
|
|||||||
import { useDraggable } from '@neodrag/react';
|
import { useDraggable } from '@neodrag/react';
|
||||||
import { useReactFlow, type XYPosition } from '@xyflow/react';
|
import { useReactFlow, type XYPosition } from '@xyflow/react';
|
||||||
import { type ReactNode, useCallback, useRef, useState } from 'react';
|
import { type ReactNode, useCallback, useRef, useState } from 'react';
|
||||||
import useFlowStore from '../VisProgStores';
|
|
||||||
import styles from '../../VisProg.module.css';
|
import styles from '../../VisProg.module.css';
|
||||||
import { NodeDefaults, type NodeTypes } from '../NodeRegistry'
|
import { NodeDefaults, type NodeTypes } from '../NodeRegistry'
|
||||||
|
import addNode from '../utils/AddNode';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DraggableNodeProps dictates the type properties of a DraggableNode
|
* DraggableNodeProps dictates the type properties of a DraggableNode
|
||||||
@@ -47,41 +47,6 @@ function DraggableNode({ className, children, nodeType, onDrop }: DraggableNodeP
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* addNode — adds a new node to the flow using the unified class-based system.
|
|
||||||
* Keeps numbering logic for phase/norm nodes.
|
|
||||||
*/
|
|
||||||
function addNode(nodeType: keyof typeof NodeTypes, position: XYPosition) {
|
|
||||||
const { nodes, setNodes } = useFlowStore.getState();
|
|
||||||
|
|
||||||
// Find out if there's any default data about our ndoe
|
|
||||||
const defaultData = NodeDefaults[nodeType] ?? {}
|
|
||||||
|
|
||||||
// Currently, we find out what the Id is by checking the last node and adding one
|
|
||||||
const sameTypeNodes = nodes.filter((node) => node.type === nodeType);
|
|
||||||
const nextNumber =
|
|
||||||
sameTypeNodes.length > 0
|
|
||||||
? (() => {
|
|
||||||
const lastNode = sameTypeNodes[sameTypeNodes.length - 1];
|
|
||||||
const parts = lastNode.id.split('-');
|
|
||||||
const lastNum = Number(parts[1]);
|
|
||||||
return Number.isNaN(lastNum) ? sameTypeNodes.length + 1 : lastNum + 1;
|
|
||||||
})()
|
|
||||||
: 1;
|
|
||||||
const id = `${nodeType}-${nextNumber}`;
|
|
||||||
|
|
||||||
// Create new node
|
|
||||||
const newNode = {
|
|
||||||
id: id,
|
|
||||||
type: nodeType,
|
|
||||||
position,
|
|
||||||
// Deep copy using JSON because thats how things work:
|
|
||||||
// Ref: https://developer.mozilla.org/en-US/docs/Glossary/Deep_copy
|
|
||||||
data: structuredClone(defaultData)
|
|
||||||
}
|
|
||||||
setNodes([...nodes, newNode]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DndToolbar defines how the drag and drop toolbar component works
|
* DndToolbar defines how the drag and drop toolbar component works
|
||||||
* and includes the default onDrop behavior.
|
* and includes the default onDrop behavior.
|
||||||
|
|||||||
39
src/pages/VisProgPage/visualProgrammingUI/utils/AddNode.ts
Normal file
39
src/pages/VisProgPage/visualProgrammingUI/utils/AddNode.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import type { XYPosition } from "@xyflow/react";
|
||||||
|
import { NodeDefaults, type NodeTypes } from "../NodeRegistry";
|
||||||
|
import useFlowStore from "../VisProgStores";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* addNode — adds a new node to the flow using the unified class-based system.
|
||||||
|
* Keeps numbering logic for phase/norm nodes.
|
||||||
|
*/
|
||||||
|
export default function addNode(nodeType: keyof typeof NodeTypes, position: XYPosition) {
|
||||||
|
const { nodes, setNodes } = useFlowStore.getState();
|
||||||
|
|
||||||
|
// Find out if there's any default data about our ndoe
|
||||||
|
const defaultData = NodeDefaults[nodeType] ?? {}
|
||||||
|
|
||||||
|
// Currently, we find out what the Id is by checking the last node and adding one
|
||||||
|
const sameTypeNodes = nodes.filter((node) => node.type === nodeType);
|
||||||
|
const nextNumber =
|
||||||
|
sameTypeNodes.length > 0
|
||||||
|
? (() => {
|
||||||
|
const lastNode = sameTypeNodes[sameTypeNodes.length - 1];
|
||||||
|
const parts = lastNode.id.split('-');
|
||||||
|
const lastNum = Number(parts[1]);
|
||||||
|
return Number.isNaN(lastNum) ? sameTypeNodes.length + 1 : lastNum + 1;
|
||||||
|
})()
|
||||||
|
: 1;
|
||||||
|
const id = `${nodeType}-${nextNumber}`;
|
||||||
|
|
||||||
|
// Create new node
|
||||||
|
const newNode = {
|
||||||
|
id: id,
|
||||||
|
type: nodeType,
|
||||||
|
position,
|
||||||
|
// Deep copy using JSON because thats how things work:
|
||||||
|
// Ref: https://developer.mozilla.org/en-US/docs/Glossary/Deep_copy
|
||||||
|
data: JSON.parse(JSON.stringify(defaultData))
|
||||||
|
}
|
||||||
|
setNodes([...nodes, newNode]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { resetFlowStore } from "../../../../test-utils/test-utils";
|
||||||
|
import useFlowStore from '../../../../../src/pages/VisProgPage/visualProgrammingUI/VisProgStores';
|
||||||
|
import addNode from "../../../../../src/pages/VisProgPage/visualProgrammingUI/utils/AddNode";
|
||||||
|
|
||||||
|
|
||||||
|
describe('PhaseNode', () => {
|
||||||
|
beforeEach(() => resetFlowStore());
|
||||||
|
|
||||||
|
it('each created phase gets its own children array (store-level)', () => {
|
||||||
|
addNode("phase", {x:10,y:10})
|
||||||
|
addNode("phase", {x:20,y:20})
|
||||||
|
|
||||||
|
const nodes = useFlowStore.getState().nodes;
|
||||||
|
const p1 = nodes.find((x) => x.id === 'phase-1')!;
|
||||||
|
const p2 = nodes.find((x) => x.id === 'phase-2')!;
|
||||||
|
|
||||||
|
// not the same reference
|
||||||
|
expect(p1.data.children).not.toBe(p2.data.children);
|
||||||
|
// but same initial value
|
||||||
|
expect(p1.data.children).toEqual(p2.data.children);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -15,7 +15,7 @@ describe('NormNode', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function createNode(id: string, type: string, position: XYPosition, data: Record<string, unknown>, deletable?: boolean) {
|
function createNode(id: string, type: string, position: XYPosition, data: Record<string, unknown>, deletable?: boolean) {
|
||||||
const defaultData = NodeDefaults[type as keyof typeof NodeDefaults]
|
const defaultData = JSON.parse(JSON.stringify(NodeDefaults[type as keyof typeof NodeDefaults]))
|
||||||
const newData = {
|
const newData = {
|
||||||
id: id,
|
id: id,
|
||||||
type: type,
|
type: type,
|
||||||
|
|||||||
Reference in New Issue
Block a user