balibabu
commited on
Commit
·
db19895
1
Parent(s):
06a1fe2
feat: set the edge as the data source to achieve two-way linkage betw… (#1299)
Browse files### What problem does this PR solve?
feat: set the edge as the data source to achieve two-way linkage between
the edge and the to field. #918
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
web/src/pages/flow/canvas/node/categorize-handle.tsx
CHANGED
@@ -22,7 +22,8 @@ const CategorizeHandle = ({ top, right, text, idx }: IProps) => {
|
|
22 |
<Handle
|
23 |
type="source"
|
24 |
position={Position.Right}
|
25 |
-
id={`CategorizeHandle${idx}`}
|
|
|
26 |
isConnectable
|
27 |
style={{
|
28 |
...DEFAULT_HANDLE_STYLE,
|
|
|
22 |
<Handle
|
23 |
type="source"
|
24 |
position={Position.Right}
|
25 |
+
// id={`CategorizeHandle${idx}`}
|
26 |
+
id={text}
|
27 |
isConnectable
|
28 |
style={{
|
29 |
...DEFAULT_HANDLE_STYLE,
|
web/src/pages/flow/categorize-form/dynamic-categorize.tsx
CHANGED
@@ -11,10 +11,7 @@ const DynamicCategorize = ({ nodeId }: IProps) => {
|
|
11 |
const updateNodeInternals = useUpdateNodeInternals();
|
12 |
const form = Form.useFormInstance();
|
13 |
const options = useBuildCategorizeToOptions();
|
14 |
-
const { handleSelectChange } = useHandleToSelectChange(
|
15 |
-
options.map((x) => x.value),
|
16 |
-
nodeId,
|
17 |
-
);
|
18 |
|
19 |
return (
|
20 |
<>
|
@@ -64,7 +61,9 @@ const DynamicCategorize = ({ nodeId }: IProps) => {
|
|
64 |
<Select
|
65 |
allowClear
|
66 |
options={options}
|
67 |
-
onChange={handleSelectChange
|
|
|
|
|
68 |
/>
|
69 |
</Form.Item>
|
70 |
</Card>
|
|
|
11 |
const updateNodeInternals = useUpdateNodeInternals();
|
12 |
const form = Form.useFormInstance();
|
13 |
const options = useBuildCategorizeToOptions();
|
14 |
+
const { handleSelectChange } = useHandleToSelectChange(nodeId);
|
|
|
|
|
|
|
15 |
|
16 |
return (
|
17 |
<>
|
|
|
61 |
<Select
|
62 |
allowClear
|
63 |
options={options}
|
64 |
+
onChange={handleSelectChange(
|
65 |
+
form.getFieldValue(['items', field.name, 'name']),
|
66 |
+
)}
|
67 |
/>
|
68 |
</Form.Item>
|
69 |
</Card>
|
web/src/pages/flow/categorize-form/hooks.ts
CHANGED
@@ -1,11 +1,13 @@
|
|
1 |
import get from 'lodash/get';
|
2 |
import omit from 'lodash/omit';
|
3 |
-
import { useCallback, useEffect
|
|
|
4 |
import { Operator } from '../constant';
|
5 |
import {
|
6 |
ICategorizeItem,
|
7 |
ICategorizeItemResult,
|
8 |
IOperatorForm,
|
|
|
9 |
} from '../interface';
|
10 |
import useGraphStore from '../store';
|
11 |
|
@@ -33,10 +35,18 @@ export const useBuildCategorizeToOptions = () => {
|
|
33 |
*/
|
34 |
const buildCategorizeListFromObject = (
|
35 |
categorizeItem: ICategorizeItemResult,
|
|
|
|
|
36 |
) => {
|
|
|
|
|
37 |
return Object.keys(categorizeItem).reduce<Array<ICategorizeItem>>(
|
38 |
(pre, cur) => {
|
39 |
-
|
|
|
|
|
|
|
|
|
40 |
return pre;
|
41 |
},
|
42 |
[],
|
@@ -70,6 +80,8 @@ export const useHandleFormValuesChange = ({
|
|
70 |
form,
|
71 |
node,
|
72 |
}: IOperatorForm) => {
|
|
|
|
|
73 |
const handleValuesChange = useCallback(
|
74 |
(changedValues: any, values: any) => {
|
75 |
console.info(changedValues, values);
|
@@ -85,43 +97,29 @@ export const useHandleFormValuesChange = ({
|
|
85 |
form?.setFieldsValue({
|
86 |
items: buildCategorizeListFromObject(
|
87 |
get(node, 'data.form.category_description', {}),
|
|
|
|
|
88 |
),
|
89 |
});
|
90 |
-
}, [form, node]);
|
91 |
|
92 |
return { handleValuesChange };
|
93 |
};
|
94 |
|
95 |
-
export const useHandleToSelectChange = (
|
96 |
-
|
97 |
-
nodeId?: string,
|
98 |
-
) => {
|
99 |
-
// const [previousTarget, setPreviousTarget] = useState('');
|
100 |
-
const previousTarget = useRef('');
|
101 |
-
const { addEdge, deleteEdgeBySourceAndTarget } = useGraphStore(
|
102 |
-
(state) => state,
|
103 |
-
);
|
104 |
const handleSelectChange = useCallback(
|
105 |
-
(value?: string) => {
|
106 |
-
if (nodeId) {
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
source: nodeId,
|
114 |
-
target: value,
|
115 |
-
sourceHandle: 'b',
|
116 |
-
targetHandle: 'd',
|
117 |
-
});
|
118 |
-
} else {
|
119 |
-
// if the value is empty, delete the edges between the current node and all nodes in the drop-down box.
|
120 |
-
}
|
121 |
-
previousTarget.current = value;
|
122 |
}
|
123 |
},
|
124 |
-
[addEdge, nodeId
|
125 |
);
|
126 |
|
127 |
return { handleSelectChange };
|
|
|
1 |
import get from 'lodash/get';
|
2 |
import omit from 'lodash/omit';
|
3 |
+
import { useCallback, useEffect } from 'react';
|
4 |
+
import { Edge, Node } from 'reactflow';
|
5 |
import { Operator } from '../constant';
|
6 |
import {
|
7 |
ICategorizeItem,
|
8 |
ICategorizeItemResult,
|
9 |
IOperatorForm,
|
10 |
+
NodeData,
|
11 |
} from '../interface';
|
12 |
import useGraphStore from '../store';
|
13 |
|
|
|
35 |
*/
|
36 |
const buildCategorizeListFromObject = (
|
37 |
categorizeItem: ICategorizeItemResult,
|
38 |
+
edges: Edge[],
|
39 |
+
node?: Node<NodeData>,
|
40 |
) => {
|
41 |
+
// Categorize's to field has two data sources, with edges as the data source.
|
42 |
+
// Changes in the edge or to field need to be synchronized to the form field.
|
43 |
return Object.keys(categorizeItem).reduce<Array<ICategorizeItem>>(
|
44 |
(pre, cur) => {
|
45 |
+
// synchronize edge data to the to field
|
46 |
+
const edge = edges.find(
|
47 |
+
(x) => x.source === node?.id && x.sourceHandle === cur,
|
48 |
+
);
|
49 |
+
pre.push({ name: cur, ...categorizeItem[cur], to: edge?.target });
|
50 |
return pre;
|
51 |
},
|
52 |
[],
|
|
|
80 |
form,
|
81 |
node,
|
82 |
}: IOperatorForm) => {
|
83 |
+
const edges = useGraphStore((state) => state.edges);
|
84 |
+
|
85 |
const handleValuesChange = useCallback(
|
86 |
(changedValues: any, values: any) => {
|
87 |
console.info(changedValues, values);
|
|
|
97 |
form?.setFieldsValue({
|
98 |
items: buildCategorizeListFromObject(
|
99 |
get(node, 'data.form.category_description', {}),
|
100 |
+
edges,
|
101 |
+
node,
|
102 |
),
|
103 |
});
|
104 |
+
}, [form, node, edges]);
|
105 |
|
106 |
return { handleValuesChange };
|
107 |
};
|
108 |
|
109 |
+
export const useHandleToSelectChange = (nodeId?: string) => {
|
110 |
+
const { addEdge } = useGraphStore((state) => state);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
111 |
const handleSelectChange = useCallback(
|
112 |
+
(name?: string) => (value?: string) => {
|
113 |
+
if (nodeId && value && name) {
|
114 |
+
addEdge({
|
115 |
+
source: nodeId,
|
116 |
+
target: value,
|
117 |
+
sourceHandle: name,
|
118 |
+
targetHandle: null,
|
119 |
+
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
120 |
}
|
121 |
},
|
122 |
+
[addEdge, nodeId],
|
123 |
);
|
124 |
|
125 |
return { handleSelectChange };
|
web/src/pages/flow/hooks.ts
CHANGED
@@ -169,7 +169,7 @@ export const useWatchGraphChange = () => {
|
|
169 |
const edges = useGraphStore((state) => state.edges);
|
170 |
useDebounceEffect(
|
171 |
() => {
|
172 |
-
console.info('useDebounceEffect');
|
173 |
},
|
174 |
[nodes, edges],
|
175 |
{
|
|
|
169 |
const edges = useGraphStore((state) => state.edges);
|
170 |
useDebounceEffect(
|
171 |
() => {
|
172 |
+
// console.info('useDebounceEffect');
|
173 |
},
|
174 |
[nodes, edges],
|
175 |
{
|
web/src/pages/flow/store.ts
CHANGED
@@ -1,5 +1,6 @@
|
|
1 |
import type {} from '@redux-devtools/extension';
|
2 |
import { humanId } from 'human-id';
|
|
|
3 |
import {
|
4 |
Connection,
|
5 |
Edge,
|
@@ -36,6 +37,7 @@ export type RFState = {
|
|
36 |
addNode: (nodes: Node) => void;
|
37 |
getNode: (id: string) => Node | undefined;
|
38 |
addEdge: (connection: Connection) => void;
|
|
|
39 |
deletePreviousEdgeOfClassificationNode: (connection: Connection) => void;
|
40 |
duplicateNode: (id: string) => void;
|
41 |
deleteEdge: () => void;
|
@@ -43,7 +45,7 @@ export type RFState = {
|
|
43 |
deleteNodeById: (id: string) => void;
|
44 |
deleteEdgeBySourceAndTarget: (source: string, target: string) => void;
|
45 |
findNodeByName: (operatorName: Operator) => Node | undefined;
|
46 |
-
|
47 |
};
|
48 |
|
49 |
// this is our useStore hook that we can use in our components to get parts of the store and call actions
|
@@ -92,6 +94,10 @@ const useGraphStore = create<RFState>()(
|
|
92 |
set({
|
93 |
edges: addEdge(connection, get().edges),
|
94 |
});
|
|
|
|
|
|
|
|
|
95 |
},
|
96 |
deletePreviousEdgeOfClassificationNode: (connection: Connection) => {
|
97 |
// Delete the edge on the classification node anchor when the anchor is connected to other nodes
|
@@ -164,9 +170,6 @@ const useGraphStore = create<RFState>()(
|
|
164 |
findNodeByName: (name: Operator) => {
|
165 |
return get().nodes.find((x) => x.data.label === name);
|
166 |
},
|
167 |
-
findNodeById: (id: string) => {
|
168 |
-
return get().nodes.find((x) => x.id === id);
|
169 |
-
},
|
170 |
updateNodeForm: (nodeId: string, values: any) => {
|
171 |
set({
|
172 |
nodes: get().nodes.map((node) => {
|
@@ -178,6 +181,13 @@ const useGraphStore = create<RFState>()(
|
|
178 |
}),
|
179 |
});
|
180 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
181 |
}),
|
182 |
{ name: 'graph' },
|
183 |
),
|
|
|
1 |
import type {} from '@redux-devtools/extension';
|
2 |
import { humanId } from 'human-id';
|
3 |
+
import lodashSet from 'lodash/set';
|
4 |
import {
|
5 |
Connection,
|
6 |
Edge,
|
|
|
37 |
addNode: (nodes: Node) => void;
|
38 |
getNode: (id: string) => Node | undefined;
|
39 |
addEdge: (connection: Connection) => void;
|
40 |
+
getEdge: (id: string) => Edge | undefined;
|
41 |
deletePreviousEdgeOfClassificationNode: (connection: Connection) => void;
|
42 |
duplicateNode: (id: string) => void;
|
43 |
deleteEdge: () => void;
|
|
|
45 |
deleteNodeById: (id: string) => void;
|
46 |
deleteEdgeBySourceAndTarget: (source: string, target: string) => void;
|
47 |
findNodeByName: (operatorName: Operator) => Node | undefined;
|
48 |
+
updateMutableNodeFormItem: (id: string, field: string, value: any) => void;
|
49 |
};
|
50 |
|
51 |
// this is our useStore hook that we can use in our components to get parts of the store and call actions
|
|
|
94 |
set({
|
95 |
edges: addEdge(connection, get().edges),
|
96 |
});
|
97 |
+
get().deletePreviousEdgeOfClassificationNode(connection);
|
98 |
+
},
|
99 |
+
getEdge: (id: string) => {
|
100 |
+
return get().edges.find((x) => x.id === id);
|
101 |
},
|
102 |
deletePreviousEdgeOfClassificationNode: (connection: Connection) => {
|
103 |
// Delete the edge on the classification node anchor when the anchor is connected to other nodes
|
|
|
170 |
findNodeByName: (name: Operator) => {
|
171 |
return get().nodes.find((x) => x.data.label === name);
|
172 |
},
|
|
|
|
|
|
|
173 |
updateNodeForm: (nodeId: string, values: any) => {
|
174 |
set({
|
175 |
nodes: get().nodes.map((node) => {
|
|
|
181 |
}),
|
182 |
});
|
183 |
},
|
184 |
+
updateMutableNodeFormItem: (id: string, field: string, value: any) => {
|
185 |
+
const { nodes } = get();
|
186 |
+
const idx = nodes.findIndex((x) => x.id === id);
|
187 |
+
if (idx) {
|
188 |
+
lodashSet(nodes, [idx, 'data', 'form', field], value);
|
189 |
+
}
|
190 |
+
},
|
191 |
}),
|
192 |
{ name: 'graph' },
|
193 |
),
|