feat: when Categorize establishes a connection with other operators, it adds the target node to the to field. #918 (#1418)

### What problem does this PR solve?
feat: when Categorize establishes a connection with other operators, it
adds the target node to the to field. #918

feat: modify the Chinese text of loop #918

### Type of change

- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu 2024-07-08 16:29:54 +08:00 committed by GitHub
parent 61557a101a
commit fce3f6df8e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 68 additions and 24 deletions

3
web/package-lock.json generated
View File

@ -22,6 +22,7 @@
"human-id": "^4.1.1", "human-id": "^4.1.1",
"i18next": "^23.7.16", "i18next": "^23.7.16",
"i18next-browser-languagedetector": "^8.0.0", "i18next-browser-languagedetector": "^8.0.0",
"immer": "^10.1.1",
"js-base64": "^3.7.5", "js-base64": "^3.7.5",
"jsencrypt": "^3.3.2", "jsencrypt": "^3.3.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",
@ -13635,8 +13636,6 @@
"version": "10.1.1", "version": "10.1.1",
"resolved": "https://registry.npmmirror.com/immer/-/immer-10.1.1.tgz", "resolved": "https://registry.npmmirror.com/immer/-/immer-10.1.1.tgz",
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==", "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
"optional": true,
"peer": true,
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
"url": "https://opencollective.com/immer" "url": "https://opencollective.com/immer"

View File

@ -33,6 +33,7 @@
"human-id": "^4.1.1", "human-id": "^4.1.1",
"i18next": "^23.7.16", "i18next": "^23.7.16",
"i18next-browser-languagedetector": "^8.0.0", "i18next-browser-languagedetector": "^8.0.0",
"immer": "^10.1.1",
"js-base64": "^3.7.5", "js-base64": "^3.7.5",
"jsencrypt": "^3.3.2", "jsencrypt": "^3.3.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",

View File

@ -517,7 +517,7 @@ export default {
messagePlaceholder: '訊息', messagePlaceholder: '訊息',
messageMsg: '請輸入訊息或刪除此欄位。', messageMsg: '請輸入訊息或刪除此欄位。',
addField: '新增字段', addField: '新增字段',
loop: '環', loop: '上限',
createFlow: '创建工作流', createFlow: '创建工作流',
yes: '是', yes: '是',
no: '否', no: '否',

View File

@ -536,7 +536,7 @@ export default {
messagePlaceholder: '消息', messagePlaceholder: '消息',
messageMsg: '请输入消息或删除此字段。', messageMsg: '请输入消息或删除此字段。',
addField: '新增字段', addField: '新增字段',
loop: '环', loop: '上限',
createFlow: '创建工作流', createFlow: '创建工作流',
yes: '是', yes: '是',
no: '否', no: '否',

View File

@ -49,15 +49,18 @@ export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) {
className={styles.handle} className={styles.handle}
id={'c'} id={'c'}
></Handle> ></Handle>
{Object.keys(categoryData).map((x, idx) => ( {Object.keys(categoryData).map((x, idx) => {
<CategorizeHandle console.info(categoryData, id, data);
top={CategorizeAnchorPointPositions[idx].top} return (
right={CategorizeAnchorPointPositions[idx].right} <CategorizeHandle
key={idx} top={CategorizeAnchorPointPositions[idx].top}
text={x} right={CategorizeAnchorPointPositions[idx].right}
idx={idx} key={idx}
></CategorizeHandle> text={x}
))} idx={idx}
></CategorizeHandle>
);
})}
<Flex vertical align="center" justify="center" gap={6}> <Flex vertical align="center" justify="center" gap={6}>
<OperatorIcon <OperatorIcon
name={data.label as Operator} name={data.label as Operator}

View File

@ -18,6 +18,7 @@ import {
} 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';
@ -32,7 +33,7 @@ export type RFState = {
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;
@ -55,7 +56,7 @@ export type RFState = {
// 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[],
@ -108,6 +109,8 @@ const useGraphStore = create<RFState>()(
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);
@ -115,8 +118,23 @@ const useGraphStore = create<RFState>()(
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) { const operatorType = getOperatorTypeFromId(source);
updateNodeForm(source, { [sourceHandle as string]: target }); 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) => {
@ -166,13 +184,30 @@ const useGraphStore = create<RFState>()(
}); });
}, },
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, { switch (operatorType) {
[currentEdge.sourceHandle as string]: undefined, 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),
@ -203,7 +238,7 @@ const useGraphStore = create<RFState>()(
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) {
@ -211,11 +246,17 @@ const useGraphStore = create<RFState>()(
// ...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;
} }
@ -247,7 +288,7 @@ const useGraphStore = create<RFState>()(
setClickedNodeId: (id?: string) => { setClickedNodeId: (id?: string) => {
set({ clickedNodeId: id }); set({ clickedNodeId: id });
}, },
}), })),
{ name: 'graph' }, { name: 'graph' },
), ),
); );