balibabu
commited on
Commit
·
3d90fa3
1
Parent(s):
6ac2722
feat: when Categorize establishes a connection with other operators, it adds the target node to the to field. #918 (#1418)
Browse files### 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)
- web/package-lock.json +1 -2
- web/package.json +1 -0
- web/src/locales/zh-traditional.ts +1 -1
- web/src/locales/zh.ts +1 -1
- web/src/pages/flow/canvas/node/categorize-node.tsx +12 -9
- web/src/pages/flow/store.ts +52 -11
web/package-lock.json
CHANGED
|
@@ -22,6 +22,7 @@
|
|
| 22 |
"human-id": "^4.1.1",
|
| 23 |
"i18next": "^23.7.16",
|
| 24 |
"i18next-browser-languagedetector": "^8.0.0",
|
|
|
|
| 25 |
"js-base64": "^3.7.5",
|
| 26 |
"jsencrypt": "^3.3.2",
|
| 27 |
"lodash": "^4.17.21",
|
|
@@ -13635,8 +13636,6 @@
|
|
| 13635 |
"version": "10.1.1",
|
| 13636 |
"resolved": "https://registry.npmmirror.com/immer/-/immer-10.1.1.tgz",
|
| 13637 |
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
|
| 13638 |
-
"optional": true,
|
| 13639 |
-
"peer": true,
|
| 13640 |
"funding": {
|
| 13641 |
"type": "opencollective",
|
| 13642 |
"url": "https://opencollective.com/immer"
|
|
|
|
| 22 |
"human-id": "^4.1.1",
|
| 23 |
"i18next": "^23.7.16",
|
| 24 |
"i18next-browser-languagedetector": "^8.0.0",
|
| 25 |
+
"immer": "^10.1.1",
|
| 26 |
"js-base64": "^3.7.5",
|
| 27 |
"jsencrypt": "^3.3.2",
|
| 28 |
"lodash": "^4.17.21",
|
|
|
|
| 13636 |
"version": "10.1.1",
|
| 13637 |
"resolved": "https://registry.npmmirror.com/immer/-/immer-10.1.1.tgz",
|
| 13638 |
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
|
|
|
|
|
|
|
| 13639 |
"funding": {
|
| 13640 |
"type": "opencollective",
|
| 13641 |
"url": "https://opencollective.com/immer"
|
web/package.json
CHANGED
|
@@ -33,6 +33,7 @@
|
|
| 33 |
"human-id": "^4.1.1",
|
| 34 |
"i18next": "^23.7.16",
|
| 35 |
"i18next-browser-languagedetector": "^8.0.0",
|
|
|
|
| 36 |
"js-base64": "^3.7.5",
|
| 37 |
"jsencrypt": "^3.3.2",
|
| 38 |
"lodash": "^4.17.21",
|
|
|
|
| 33 |
"human-id": "^4.1.1",
|
| 34 |
"i18next": "^23.7.16",
|
| 35 |
"i18next-browser-languagedetector": "^8.0.0",
|
| 36 |
+
"immer": "^10.1.1",
|
| 37 |
"js-base64": "^3.7.5",
|
| 38 |
"jsencrypt": "^3.3.2",
|
| 39 |
"lodash": "^4.17.21",
|
web/src/locales/zh-traditional.ts
CHANGED
|
@@ -517,7 +517,7 @@ export default {
|
|
| 517 |
messagePlaceholder: '訊息',
|
| 518 |
messageMsg: '請輸入訊息或刪除此欄位。',
|
| 519 |
addField: '新增字段',
|
| 520 |
-
loop: '
|
| 521 |
createFlow: '创建工作流',
|
| 522 |
yes: '是',
|
| 523 |
no: '否',
|
|
|
|
| 517 |
messagePlaceholder: '訊息',
|
| 518 |
messageMsg: '請輸入訊息或刪除此欄位。',
|
| 519 |
addField: '新增字段',
|
| 520 |
+
loop: '循環上限',
|
| 521 |
createFlow: '创建工作流',
|
| 522 |
yes: '是',
|
| 523 |
no: '否',
|
web/src/locales/zh.ts
CHANGED
|
@@ -536,7 +536,7 @@ export default {
|
|
| 536 |
messagePlaceholder: '消息',
|
| 537 |
messageMsg: '请输入消息或删除此字段。',
|
| 538 |
addField: '新增字段',
|
| 539 |
-
loop: '
|
| 540 |
createFlow: '创建工作流',
|
| 541 |
yes: '是',
|
| 542 |
no: '否',
|
|
|
|
| 536 |
messagePlaceholder: '消息',
|
| 537 |
messageMsg: '请输入消息或删除此字段。',
|
| 538 |
addField: '新增字段',
|
| 539 |
+
loop: '循环上限',
|
| 540 |
createFlow: '创建工作流',
|
| 541 |
yes: '是',
|
| 542 |
no: '否',
|
web/src/pages/flow/canvas/node/categorize-node.tsx
CHANGED
|
@@ -49,15 +49,18 @@ export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) {
|
|
| 49 |
className={styles.handle}
|
| 50 |
id={'c'}
|
| 51 |
></Handle>
|
| 52 |
-
{Object.keys(categoryData).map((x, idx) =>
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
|
|
|
|
|
|
|
|
|
| 61 |
<Flex vertical align="center" justify="center" gap={6}>
|
| 62 |
<OperatorIcon
|
| 63 |
name={data.label as Operator}
|
|
|
|
| 49 |
className={styles.handle}
|
| 50 |
id={'c'}
|
| 51 |
></Handle>
|
| 52 |
+
{Object.keys(categoryData).map((x, idx) => {
|
| 53 |
+
console.info(categoryData, id, data);
|
| 54 |
+
return (
|
| 55 |
+
<CategorizeHandle
|
| 56 |
+
top={CategorizeAnchorPointPositions[idx].top}
|
| 57 |
+
right={CategorizeAnchorPointPositions[idx].right}
|
| 58 |
+
key={idx}
|
| 59 |
+
text={x}
|
| 60 |
+
idx={idx}
|
| 61 |
+
></CategorizeHandle>
|
| 62 |
+
);
|
| 63 |
+
})}
|
| 64 |
<Flex vertical align="center" justify="center" gap={6}>
|
| 65 |
<OperatorIcon
|
| 66 |
name={data.label as Operator}
|
web/src/pages/flow/store.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
| 18 |
} from 'reactflow';
|
| 19 |
import { create } from 'zustand';
|
| 20 |
import { devtools } from 'zustand/middleware';
|
|
|
|
| 21 |
import { Operator } from './constant';
|
| 22 |
import { NodeData } from './interface';
|
| 23 |
|
|
@@ -32,7 +33,7 @@ export type RFState = {
|
|
| 32 |
onConnect: OnConnect;
|
| 33 |
setNodes: (nodes: Node[]) => void;
|
| 34 |
setEdges: (edges: Edge[]) => void;
|
| 35 |
-
updateNodeForm: (nodeId: string, values: any) => void;
|
| 36 |
onSelectionChange: OnSelectionChangeFunc;
|
| 37 |
addNode: (nodes: Node) => void;
|
| 38 |
getNode: (id?: string | null) => Node<NodeData> | undefined;
|
|
@@ -55,7 +56,7 @@ export type RFState = {
|
|
| 55 |
// this is our useStore hook that we can use in our components to get parts of the store and call actions
|
| 56 |
const useGraphStore = create<RFState>()(
|
| 57 |
devtools(
|
| 58 |
-
(set, get) => ({
|
| 59 |
nodes: [] as Node[],
|
| 60 |
edges: [] as Edge[],
|
| 61 |
selectedNodeIds: [] as string[],
|
|
@@ -108,6 +109,8 @@ const useGraphStore = create<RFState>()(
|
|
| 108 |
edges: addEdge(connection, get().edges),
|
| 109 |
});
|
| 110 |
get().deletePreviousEdgeOfClassificationNode(connection);
|
|
|
|
|
|
|
| 111 |
},
|
| 112 |
getEdge: (id: string) => {
|
| 113 |
return get().edges.find((x) => x.id === id);
|
|
@@ -115,8 +118,23 @@ const useGraphStore = create<RFState>()(
|
|
| 115 |
updateFormDataOnConnect: (connection: Connection) => {
|
| 116 |
const { getOperatorTypeFromId, updateNodeForm } = get();
|
| 117 |
const { source, target, sourceHandle } = connection;
|
| 118 |
-
|
| 119 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
}
|
| 121 |
},
|
| 122 |
deletePreviousEdgeOfClassificationNode: (connection: Connection) => {
|
|
@@ -166,13 +184,30 @@ const useGraphStore = create<RFState>()(
|
|
| 166 |
});
|
| 167 |
},
|
| 168 |
deleteEdgeById: (id: string) => {
|
| 169 |
-
const { edges, updateNodeForm } = get();
|
| 170 |
const currentEdge = edges.find((x) => x.id === id);
|
|
|
|
| 171 |
if (currentEdge) {
|
|
|
|
|
|
|
| 172 |
// After deleting the edge, set the corresponding field in the node's form field to undefined
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 176 |
}
|
| 177 |
set({
|
| 178 |
edges: edges.filter((edge) => edge.id !== id),
|
|
@@ -203,7 +238,7 @@ const useGraphStore = create<RFState>()(
|
|
| 203 |
findNodeByName: (name: Operator) => {
|
| 204 |
return get().nodes.find((x) => x.data.label === name);
|
| 205 |
},
|
| 206 |
-
updateNodeForm: (nodeId: string, values: any) => {
|
| 207 |
set({
|
| 208 |
nodes: get().nodes.map((node) => {
|
| 209 |
if (node.id === nodeId) {
|
|
@@ -211,11 +246,17 @@ const useGraphStore = create<RFState>()(
|
|
| 211 |
// ...node.data,
|
| 212 |
// form: { ...node.data.form, ...values },
|
| 213 |
// };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 214 |
return {
|
| 215 |
...node,
|
| 216 |
data: {
|
| 217 |
...node.data,
|
| 218 |
-
form:
|
| 219 |
},
|
| 220 |
} as any;
|
| 221 |
}
|
|
@@ -247,7 +288,7 @@ const useGraphStore = create<RFState>()(
|
|
| 247 |
setClickedNodeId: (id?: string) => {
|
| 248 |
set({ clickedNodeId: id });
|
| 249 |
},
|
| 250 |
-
}),
|
| 251 |
{ name: 'graph' },
|
| 252 |
),
|
| 253 |
);
|
|
|
|
| 18 |
} from 'reactflow';
|
| 19 |
import { create } from 'zustand';
|
| 20 |
import { devtools } from 'zustand/middleware';
|
| 21 |
+
import { immer } from 'zustand/middleware/immer';
|
| 22 |
import { Operator } from './constant';
|
| 23 |
import { NodeData } from './interface';
|
| 24 |
|
|
|
|
| 33 |
onConnect: OnConnect;
|
| 34 |
setNodes: (nodes: Node[]) => void;
|
| 35 |
setEdges: (edges: Edge[]) => void;
|
| 36 |
+
updateNodeForm: (nodeId: string, values: any, path?: string[]) => void;
|
| 37 |
onSelectionChange: OnSelectionChangeFunc;
|
| 38 |
addNode: (nodes: Node) => void;
|
| 39 |
getNode: (id?: string | null) => Node<NodeData> | undefined;
|
|
|
|
| 56 |
// this is our useStore hook that we can use in our components to get parts of the store and call actions
|
| 57 |
const useGraphStore = create<RFState>()(
|
| 58 |
devtools(
|
| 59 |
+
immer((set, get) => ({
|
| 60 |
nodes: [] as Node[],
|
| 61 |
edges: [] as Edge[],
|
| 62 |
selectedNodeIds: [] as string[],
|
|
|
|
| 109 |
edges: addEdge(connection, get().edges),
|
| 110 |
});
|
| 111 |
get().deletePreviousEdgeOfClassificationNode(connection);
|
| 112 |
+
// TODO: This may not be reasonable. You need to choose between listening to changes in the form.
|
| 113 |
+
get().updateFormDataOnConnect(connection);
|
| 114 |
},
|
| 115 |
getEdge: (id: string) => {
|
| 116 |
return get().edges.find((x) => x.id === id);
|
|
|
|
| 118 |
updateFormDataOnConnect: (connection: Connection) => {
|
| 119 |
const { getOperatorTypeFromId, updateNodeForm } = get();
|
| 120 |
const { source, target, sourceHandle } = connection;
|
| 121 |
+
const operatorType = getOperatorTypeFromId(source);
|
| 122 |
+
if (source) {
|
| 123 |
+
switch (operatorType) {
|
| 124 |
+
case Operator.Relevant:
|
| 125 |
+
updateNodeForm(source, { [sourceHandle as string]: target });
|
| 126 |
+
break;
|
| 127 |
+
case Operator.Categorize:
|
| 128 |
+
if (sourceHandle)
|
| 129 |
+
updateNodeForm(source, target, [
|
| 130 |
+
'category_description',
|
| 131 |
+
sourceHandle,
|
| 132 |
+
'to',
|
| 133 |
+
]);
|
| 134 |
+
break;
|
| 135 |
+
default:
|
| 136 |
+
break;
|
| 137 |
+
}
|
| 138 |
}
|
| 139 |
},
|
| 140 |
deletePreviousEdgeOfClassificationNode: (connection: Connection) => {
|
|
|
|
| 184 |
});
|
| 185 |
},
|
| 186 |
deleteEdgeById: (id: string) => {
|
| 187 |
+
const { edges, updateNodeForm, getOperatorTypeFromId } = get();
|
| 188 |
const currentEdge = edges.find((x) => x.id === id);
|
| 189 |
+
|
| 190 |
if (currentEdge) {
|
| 191 |
+
const { source, sourceHandle } = currentEdge;
|
| 192 |
+
const operatorType = getOperatorTypeFromId(source);
|
| 193 |
// After deleting the edge, set the corresponding field in the node's form field to undefined
|
| 194 |
+
switch (operatorType) {
|
| 195 |
+
case Operator.Relevant:
|
| 196 |
+
updateNodeForm(source, {
|
| 197 |
+
[sourceHandle as string]: undefined,
|
| 198 |
+
});
|
| 199 |
+
break;
|
| 200 |
+
case Operator.Categorize:
|
| 201 |
+
if (sourceHandle)
|
| 202 |
+
updateNodeForm(source, undefined, [
|
| 203 |
+
'category_description',
|
| 204 |
+
sourceHandle,
|
| 205 |
+
'to',
|
| 206 |
+
]);
|
| 207 |
+
break;
|
| 208 |
+
default:
|
| 209 |
+
break;
|
| 210 |
+
}
|
| 211 |
}
|
| 212 |
set({
|
| 213 |
edges: edges.filter((edge) => edge.id !== id),
|
|
|
|
| 238 |
findNodeByName: (name: Operator) => {
|
| 239 |
return get().nodes.find((x) => x.data.label === name);
|
| 240 |
},
|
| 241 |
+
updateNodeForm: (nodeId: string, values: any, path: string[] = []) => {
|
| 242 |
set({
|
| 243 |
nodes: get().nodes.map((node) => {
|
| 244 |
if (node.id === nodeId) {
|
|
|
|
| 246 |
// ...node.data,
|
| 247 |
// form: { ...node.data.form, ...values },
|
| 248 |
// };
|
| 249 |
+
let nextForm: Record<string, unknown> = { ...node.data.form };
|
| 250 |
+
if (path.length === 0) {
|
| 251 |
+
nextForm = Object.assign(nextForm, values);
|
| 252 |
+
} else {
|
| 253 |
+
lodashSet(nextForm, path, values);
|
| 254 |
+
}
|
| 255 |
return {
|
| 256 |
...node,
|
| 257 |
data: {
|
| 258 |
...node.data,
|
| 259 |
+
form: nextForm,
|
| 260 |
},
|
| 261 |
} as any;
|
| 262 |
}
|
|
|
|
| 288 |
setClickedNodeId: (id?: string) => {
|
| 289 |
set({ clickedNodeId: id });
|
| 290 |
},
|
| 291 |
+
})),
|
| 292 |
{ name: 'graph' },
|
| 293 |
),
|
| 294 |
);
|