Merging dev into main #49

Merged
8464960 merged 260 commits from dev into main 2026-01-28 10:48:52 +00:00
3 changed files with 138 additions and 57 deletions
Showing only changes of commit 1a5b1e7617 - Show all commits

View File

@@ -0,0 +1,92 @@
import { create } from 'zustand';
import {
applyNodeChanges,
applyEdgeChanges,
addEdge,
reconnectEdge, type Edge, type Connection
} from '@xyflow/react';
import { type FlowState } from './VisProgTypes.tsx';
const initialNodes = [
{
id: 'start',
type: 'start',
position: {x: 0, y: 0},
data: {label: 'start'}
},
{
id: 'genericPhase',
type: 'phase',
position: {x: 0, y: 150},
data: {label: 'Generic Phase', number: 1},
},
{
id: 'end',
type: 'end',
position: {x: 0, y: 300},
data: {label: 'End'}
}
];
const initialEdges = [
{
id: 'start-end',
source: 'start',
target: 'end'
}
];
// this is our useStore hook that we can use in our components to get parts of the store and call actions
const useStore = create<FlowState>((set, get) => ({
nodes: initialNodes,
edges: initialEdges,
edgeReconnectSuccessful: true,
onNodesChange: (changes) => {
set({
nodes: applyNodeChanges(changes, get().nodes)
});
},
onEdgesChange: (changes) => {
set({
edges: applyEdgeChanges(changes, get().edges)
});
},
onConnect: (connection) => {
set({
edges: addEdge(connection, get().edges)
});
},
// handles attempted reconnections of a previously disconnected edge
onReconnect: (oldEdge: Edge, newConnection: Connection) => {
get().edgeReconnectSuccessful = true;
set({
edges: reconnectEdge(oldEdge, newConnection, get().edges)
});
},
// Handles initiation of reconnection of edges that are manually disconnected from a node
onReconnectStart: () => {
set({
edgeReconnectSuccessful: false
});
},
// Drops the edge from the set of edges, removing it from the flow, if no successful reconnection occurred
onReconnectEnd: (_: unknown, edge: { id: string; }) => {
if (!get().edgeReconnectSuccessful) {
set({
edges: get().edges.filter((e) => e.id !== edge.id),
});
}
set({
edgeReconnectSuccessful: true
});
},
setNodes: (nodes) => {
set({ nodes });
},
setEdges: (edges) => {
set({ edges });
},
}));
export default useStore;

View File

@@ -0,0 +1,25 @@
import {
type Edge,
type Node,
type OnNodesChange,
type OnEdgesChange,
type OnConnect,
type OnReconnect,
} from '@xyflow/react';
export type AppNode = Node;
export type FlowState = {
nodes: Node[];
edges: Edge[];
edgeReconnectSuccessful: boolean;
onNodesChange: OnNodesChange;
onEdgesChange: OnEdgesChange;
onConnect: OnConnect;
onReconnect: OnReconnect;
onReconnectStart: () => void;
onReconnectEnd: (_: unknown, edge: { id: string }) => void;
setNodes: (nodes: AppNode[]) => void;
setEdges: (edges: Edge[]) => void;
};

View File

@@ -1,21 +1,13 @@
import './VisProgUI.css'
import {
useCallback,
useRef
} from 'react';
import {
Background,
Controls,
ReactFlow,
ReactFlowProvider,
useNodesState,
useEdgesState,
reconnectEdge,
addEdge,
MarkerType,
type Edge,
type Connection, Panel,
Panel,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import {
@@ -27,6 +19,10 @@ import {
import { Sidebar } from './components/DragDropSidebar.tsx';
import useStore from "./VisProgStores.tsx";
import {useShallow} from "zustand/react/shallow";
import type {FlowState} from "./VisProgTypes.tsx";
// --| config starting params for flow |--
@@ -39,28 +35,7 @@ const nodeTypes = {
const initialNodes = [
{
id: 'start',
type: 'start',
position: {x: 0, y: 0},
data: {label: 'start'}
},
{
id: 'genericPhase',
type: 'phase',
position: {x: 0, y: 150},
data: {label: 'Generic Phase', number: 1},
},
{
id: 'end',
type: 'end',
position: {x: 0, y: 300},
data: {label: 'End'}
}
];
const initialEdges = [{id: 'start-end', source: 'start', target: 'end'}];
const defaultEdgeOptions = {
type: 'default',
@@ -70,38 +45,27 @@ const defaultEdgeOptions = {
},
};
const selector = (state: FlowState) => ({
nodes: state.nodes,
edges: state.edges,
onNodesChange: state.onNodesChange,
onEdgesChange: state.onEdgesChange,
onConnect: state.onConnect,
onReconnectStart: state.onReconnectStart,
onReconnectEnd: state.onReconnectEnd,
onReconnect: state.onReconnect
});
// --| define ReactFlow editor |--
const VisProgUI = ()=> {
const edgeReconnectSuccessful = useRef(true);
const [nodes, , onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
// handles connection of newly created edges
const onConnect = useCallback(
(params: Edge | Connection) => setEdges((els) => addEdge(params, els)),
[setEdges],
const { nodes, edges, onNodesChange, onEdgesChange, onConnect, onReconnect, onReconnectStart, onReconnectEnd } = useStore(
useShallow(selector),
);
// handles connection of newly created edges
// Handles initiation of reconnection of edges that are manually disconnected from a node
const onReconnectStart = useCallback(() => {
edgeReconnectSuccessful.current = false;
}, []);
// handles attempted reconnections of a previously disconnected edge
const onReconnect = useCallback((oldEdge: Edge, newConnection: Connection) => {
edgeReconnectSuccessful.current = true;
setEdges((els) => reconnectEdge(oldEdge, newConnection, els));
}, [setEdges]);
// Drops the edge from the set of edges, removing it from the flow, if no successful reconnection occurred
const onReconnectEnd = useCallback((_: unknown, edge: { id: string; }) => {
if (!edgeReconnectSuccessful.current) {
setEdges((eds) => eds.filter((e) => e.id !== edge.id));
}
edgeReconnectSuccessful.current = true;
}, [setEdges]);
return (
<div style={{marginInline: 'auto',display: 'flex',justifySelf: 'center', padding:'10px', alignItems: 'center', width: '80vw', height: '80vh'}}>
@@ -113,11 +77,11 @@ const VisProgUI = ()=> {
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
nodeTypes={nodeTypes}
snapToGrid
onReconnect={onReconnect}
onReconnectStart={onReconnectStart}
onReconnectEnd={onReconnectEnd}
onConnect={onConnect}
snapToGrid
fitView
proOptions={{hideAttribution: true }}
>