Merging dev into main #49

Merged
8464960 merged 260 commits from dev into main 2026-01-28 10:48:52 +00:00
4 changed files with 63 additions and 37 deletions
Showing only changes of commit fe13017f2d - Show all commits

View File

@@ -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.

View 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]);
}

View File

@@ -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);
});
});

View File

@@ -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,