142 lines
3.4 KiB
TypeScript
142 lines
3.4 KiB
TypeScript
import {create} from 'zustand';
|
|
import {
|
|
applyNodeChanges,
|
|
applyEdgeChanges,
|
|
addEdge,
|
|
reconnectEdge, type Edge, type Connection
|
|
} from '@xyflow/react';
|
|
|
|
import {type AppNode, type FlowState} from './VisProgTypes.tsx';
|
|
|
|
/**
|
|
* contains the nodes that are created when the editor is loaded,
|
|
* should contain at least a start and an end node
|
|
*/
|
|
const initialNodes = [
|
|
{
|
|
id: 'start',
|
|
type: 'start',
|
|
position: {x: 0, y: 0},
|
|
data: {label: 'start'}
|
|
},
|
|
{
|
|
id: 'phase-1',
|
|
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'}
|
|
}
|
|
];
|
|
|
|
/**
|
|
* contains the initial edges that are created when the editor is loaded
|
|
*/
|
|
const initialEdges = [
|
|
{
|
|
id: 'start-phase-1',
|
|
source: 'start',
|
|
target: 'phase-1',
|
|
},
|
|
{
|
|
id: 'phase-1-end',
|
|
source: 'phase-1',
|
|
target: 'end',
|
|
}
|
|
];
|
|
|
|
/**
|
|
* The useFlowStore hook contains the implementation for editor functionality and state
|
|
* we can use this inside our editor component to access the current state
|
|
* and use any implemented functionality
|
|
*/
|
|
const useFlowStore = 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)
|
|
});
|
|
},
|
|
// handles connection of newly created 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
|
|
});
|
|
},
|
|
deleteNode: (nodeId: string) => {
|
|
set({
|
|
nodes: get().nodes.filter((n) => n.id !== nodeId),
|
|
edges: get().edges.filter((e) => e.source !== nodeId && e.target !== nodeId)
|
|
});
|
|
},
|
|
setNodes: (nodes) => {
|
|
set({nodes});
|
|
},
|
|
setEdges: (edges) => {
|
|
set({edges});
|
|
},
|
|
/**
|
|
* handles updating the data component of a node,
|
|
* if the provided data object contains entries that aren't present in the updated node's data component
|
|
* those entries are added to the data component,
|
|
* entries that do exist within the node's data component,
|
|
* are simply updated to contain the new value
|
|
*
|
|
* the data object
|
|
* @param {string} nodeId
|
|
* @param {object} data
|
|
*/
|
|
updateNodeData: (nodeId: string, data) => {
|
|
set({
|
|
nodes: get().nodes.map((node) : AppNode => {
|
|
if (node.id === nodeId) {
|
|
return {
|
|
...node,
|
|
data: {
|
|
...node.data,
|
|
...data
|
|
}
|
|
};
|
|
} else { return node; }
|
|
})
|
|
});
|
|
}
|
|
}),
|
|
);
|
|
|
|
export default useFlowStore; |