|
|
|
|
|
|
|
|
} from 'reactflow'; |
|
|
} from 'reactflow'; |
|
|
import { create } from 'zustand'; |
|
|
import { create } from 'zustand'; |
|
|
import { devtools } from 'zustand/middleware'; |
|
|
import { devtools } from 'zustand/middleware'; |
|
|
|
|
|
import { immer } from 'zustand/middleware/immer'; |
|
|
import { Operator } from './constant'; |
|
|
import { Operator } from './constant'; |
|
|
import { NodeData } from './interface'; |
|
|
import { NodeData } from './interface'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
onConnect: OnConnect; |
|
|
onConnect: OnConnect; |
|
|
setNodes: (nodes: Node[]) => void; |
|
|
setNodes: (nodes: Node[]) => void; |
|
|
setEdges: (edges: Edge[]) => void; |
|
|
setEdges: (edges: Edge[]) => void; |
|
|
updateNodeForm: (nodeId: string, values: any) => void; |
|
|
|
|
|
|
|
|
updateNodeForm: (nodeId: string, values: any, path?: string[]) => void; |
|
|
onSelectionChange: OnSelectionChangeFunc; |
|
|
onSelectionChange: OnSelectionChangeFunc; |
|
|
addNode: (nodes: Node) => void; |
|
|
addNode: (nodes: Node) => void; |
|
|
getNode: (id?: string | null) => Node<NodeData> | undefined; |
|
|
getNode: (id?: string | null) => Node<NodeData> | undefined; |
|
|
|
|
|
|
|
|
// this is our useStore hook that we can use in our components to get parts of the store and call actions |
|
|
// this is our useStore hook that we can use in our components to get parts of the store and call actions |
|
|
const useGraphStore = create<RFState>()( |
|
|
const useGraphStore = create<RFState>()( |
|
|
devtools( |
|
|
devtools( |
|
|
(set, get) => ({ |
|
|
|
|
|
|
|
|
immer((set, get) => ({ |
|
|
nodes: [] as Node[], |
|
|
nodes: [] as Node[], |
|
|
edges: [] as Edge[], |
|
|
edges: [] as Edge[], |
|
|
selectedNodeIds: [] as string[], |
|
|
selectedNodeIds: [] as string[], |
|
|
|
|
|
|
|
|
edges: addEdge(connection, get().edges), |
|
|
edges: addEdge(connection, get().edges), |
|
|
}); |
|
|
}); |
|
|
get().deletePreviousEdgeOfClassificationNode(connection); |
|
|
get().deletePreviousEdgeOfClassificationNode(connection); |
|
|
|
|
|
// TODO: This may not be reasonable. You need to choose between listening to changes in the form. |
|
|
|
|
|
get().updateFormDataOnConnect(connection); |
|
|
}, |
|
|
}, |
|
|
getEdge: (id: string) => { |
|
|
getEdge: (id: string) => { |
|
|
return get().edges.find((x) => x.id === id); |
|
|
return get().edges.find((x) => x.id === id); |
|
|
|
|
|
|
|
|
updateFormDataOnConnect: (connection: Connection) => { |
|
|
updateFormDataOnConnect: (connection: Connection) => { |
|
|
const { getOperatorTypeFromId, updateNodeForm } = get(); |
|
|
const { getOperatorTypeFromId, updateNodeForm } = get(); |
|
|
const { source, target, sourceHandle } = connection; |
|
|
const { source, target, sourceHandle } = connection; |
|
|
if (source && getOperatorTypeFromId(source) === Operator.Relevant) { |
|
|
|
|
|
updateNodeForm(source, { [sourceHandle as string]: target }); |
|
|
|
|
|
|
|
|
const operatorType = getOperatorTypeFromId(source); |
|
|
|
|
|
if (source) { |
|
|
|
|
|
switch (operatorType) { |
|
|
|
|
|
case Operator.Relevant: |
|
|
|
|
|
updateNodeForm(source, { [sourceHandle as string]: target }); |
|
|
|
|
|
break; |
|
|
|
|
|
case Operator.Categorize: |
|
|
|
|
|
if (sourceHandle) |
|
|
|
|
|
updateNodeForm(source, target, [ |
|
|
|
|
|
'category_description', |
|
|
|
|
|
sourceHandle, |
|
|
|
|
|
'to', |
|
|
|
|
|
]); |
|
|
|
|
|
break; |
|
|
|
|
|
default: |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
deletePreviousEdgeOfClassificationNode: (connection: Connection) => { |
|
|
deletePreviousEdgeOfClassificationNode: (connection: Connection) => { |
|
|
|
|
|
|
|
|
}); |
|
|
}); |
|
|
}, |
|
|
}, |
|
|
deleteEdgeById: (id: string) => { |
|
|
deleteEdgeById: (id: string) => { |
|
|
const { edges, updateNodeForm } = get(); |
|
|
|
|
|
|
|
|
const { edges, updateNodeForm, getOperatorTypeFromId } = get(); |
|
|
const currentEdge = edges.find((x) => x.id === id); |
|
|
const currentEdge = edges.find((x) => x.id === id); |
|
|
|
|
|
|
|
|
if (currentEdge) { |
|
|
if (currentEdge) { |
|
|
|
|
|
const { source, sourceHandle } = currentEdge; |
|
|
|
|
|
const operatorType = getOperatorTypeFromId(source); |
|
|
// After deleting the edge, set the corresponding field in the node's form field to undefined |
|
|
// After deleting the edge, set the corresponding field in the node's form field to undefined |
|
|
updateNodeForm(currentEdge.source, { |
|
|
|
|
|
[currentEdge.sourceHandle as string]: undefined, |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
switch (operatorType) { |
|
|
|
|
|
case Operator.Relevant: |
|
|
|
|
|
updateNodeForm(source, { |
|
|
|
|
|
[sourceHandle as string]: undefined, |
|
|
|
|
|
}); |
|
|
|
|
|
break; |
|
|
|
|
|
case Operator.Categorize: |
|
|
|
|
|
if (sourceHandle) |
|
|
|
|
|
updateNodeForm(source, undefined, [ |
|
|
|
|
|
'category_description', |
|
|
|
|
|
sourceHandle, |
|
|
|
|
|
'to', |
|
|
|
|
|
]); |
|
|
|
|
|
break; |
|
|
|
|
|
default: |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
set({ |
|
|
set({ |
|
|
edges: edges.filter((edge) => edge.id !== id), |
|
|
edges: edges.filter((edge) => edge.id !== id), |
|
|
|
|
|
|
|
|
findNodeByName: (name: Operator) => { |
|
|
findNodeByName: (name: Operator) => { |
|
|
return get().nodes.find((x) => x.data.label === name); |
|
|
return get().nodes.find((x) => x.data.label === name); |
|
|
}, |
|
|
}, |
|
|
updateNodeForm: (nodeId: string, values: any) => { |
|
|
|
|
|
|
|
|
updateNodeForm: (nodeId: string, values: any, path: string[] = []) => { |
|
|
set({ |
|
|
set({ |
|
|
nodes: get().nodes.map((node) => { |
|
|
nodes: get().nodes.map((node) => { |
|
|
if (node.id === nodeId) { |
|
|
if (node.id === nodeId) { |
|
|
|
|
|
|
|
|
// ...node.data, |
|
|
// ...node.data, |
|
|
// form: { ...node.data.form, ...values }, |
|
|
// form: { ...node.data.form, ...values }, |
|
|
// }; |
|
|
// }; |
|
|
|
|
|
let nextForm: Record<string, unknown> = { ...node.data.form }; |
|
|
|
|
|
if (path.length === 0) { |
|
|
|
|
|
nextForm = Object.assign(nextForm, values); |
|
|
|
|
|
} else { |
|
|
|
|
|
lodashSet(nextForm, path, values); |
|
|
|
|
|
} |
|
|
return { |
|
|
return { |
|
|
...node, |
|
|
...node, |
|
|
data: { |
|
|
data: { |
|
|
...node.data, |
|
|
...node.data, |
|
|
form: { ...node.data.form, ...values }, |
|
|
|
|
|
|
|
|
form: nextForm, |
|
|
}, |
|
|
}, |
|
|
} as any; |
|
|
} as any; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
setClickedNodeId: (id?: string) => { |
|
|
setClickedNodeId: (id?: string) => { |
|
|
set({ clickedNodeId: id }); |
|
|
set({ clickedNodeId: id }); |
|
|
}, |
|
|
}, |
|
|
}), |
|
|
|
|
|
|
|
|
})), |
|
|
{ name: 'graph' }, |
|
|
{ name: 'graph' }, |
|
|
), |
|
|
), |
|
|
); |
|
|
); |