feat: Build the edges of Switch by form data #1739 (#2022)

### What problem does this PR solve?

feat: Build the edges of Switch  by form data #1739

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu 2024-08-20 19:27:49 +08:00 committed by GitHub
parent 7927d80a84
commit 96438ca821
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 128 additions and 37 deletions

View File

@ -4,7 +4,11 @@ import { useEffect, useMemo, useState } from 'react';
import { useUpdateNodeInternals } from 'reactflow';
import { Operator } from '../../constant';
import { IPosition, NodeData } from '../../interface';
import { buildNewPositionMap, isKeysEqual } from '../../utils';
import {
buildNewPositionMap,
generateSwitchHandleText,
isKeysEqual,
} from '../../utils';
export const useBuildCategorizeHandlePositions = ({
data,
@ -32,7 +36,7 @@ export const useBuildCategorizeHandlePositions = ({
const position = positionMap[x];
let text = x;
if (operatorName === Operator.Switch) {
text = `Item ${idx + 1}`;
text = generateSwitchHandleText(idx);
}
return { text, ...position };
})

View File

@ -28,14 +28,15 @@ interface INameInputProps {
const getOtherFieldValues = (
form: FormInstance,
formListName: string = 'items',
field: FormListFieldData,
latestField: string,
) =>
(form.getFieldValue(['items']) ?? [])
(form.getFieldValue([formListName]) ?? [])
.map((x: any) => x[latestField])
.filter(
(x: string) =>
x !== form.getFieldValue(['items', field.name, latestField]),
x !== form.getFieldValue([formListName, field.name, latestField]),
);
const NameInput = ({
@ -132,7 +133,12 @@ const DynamicCategorize = ({ nodeId }: IProps) => {
]}
>
<NameInput
otherNames={getOtherFieldValues(form, field, 'name')}
otherNames={getOtherFieldValues(
form,
'items',
field,
'name',
)}
validate={(errors: string[]) =>
form.setFields([
{
@ -159,7 +165,7 @@ const DynamicCategorize = ({ nodeId }: IProps) => {
<Select
allowClear
options={buildCategorizeToOptions(
getOtherFieldValues(form, field, 'to'),
getOtherFieldValues(form, 'items', field, 'to'),
)}
/>
</Form.Item>
@ -173,14 +179,6 @@ const DynamicCategorize = ({ nodeId }: IProps) => {
);
}}
</Form.List>
{/* <Form.Item noStyle shouldUpdate>
{() => (
<Typography>
<pre>{JSON.stringify(form.getFieldsValue(), null, 2)}</pre>
</Typography>
)}
</Form.Item> */}
</>
);
};

View File

@ -53,10 +53,11 @@ import {
initialSwitchValues,
initialWikipediaValues,
} from './constant';
import { ICategorizeForm, IRelevantForm } from './interface';
import { ICategorizeForm, IRelevantForm, ISwitchForm } from './interface';
import useGraphStore, { RFState } from './store';
import {
buildDslComponentsByGraph,
generateSwitchHandleText,
receiveMessageError,
replaceIdWithText,
} from './utils';
@ -498,6 +499,31 @@ export const useWatchNodeFormDataChange = () => {
[setEdgesByNodeId],
);
const buildSwitchEdgesByFormData = useCallback(
(nodeId: string, form: ISwitchForm) => {
// add
// delete
// edit
const conditions = form.conditions;
const downstreamEdges = conditions.reduce<Edge[]>((pre, _, idx) => {
const target = conditions[idx]?.to;
if (target) {
pre.push({
id: uuid(),
source: nodeId,
target,
sourceHandle: generateSwitchHandleText(idx),
});
}
return pre;
}, []);
setEdgesByNodeId(nodeId, downstreamEdges);
},
[setEdgesByNodeId],
);
useEffect(() => {
nodes.forEach((node) => {
const currentNode = getNode(node.id);
@ -510,6 +536,9 @@ export const useWatchNodeFormDataChange = () => {
case Operator.Categorize:
buildCategorizeEdgesByFormData(node.id, form as ICategorizeForm);
break;
case Operator.Switch:
buildSwitchEdgesByFormData(node.id, form as ISwitchForm);
break;
default:
break;
}
@ -519,5 +548,6 @@ export const useWatchNodeFormDataChange = () => {
buildCategorizeEdgesByFormData,
getNode,
buildRelevantEdgesByFormData,
buildSwitchEdgesByFormData,
]);
};

View File

@ -23,7 +23,7 @@ import { devtools } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
import { Operator } from './constant';
import { NodeData } from './interface';
import { isEdgeEqual } from './utils';
import { getOperatorIndex, isEdgeEqual } from './utils';
export type RFState = {
nodes: Node<NodeData>[];
@ -184,14 +184,19 @@ const useGraphStore = create<RFState>()(
'to',
]);
break;
// case Operator.Switch:
// if (sourceHandle)
// updateNodeForm(source, target, [
// 'conditions',
// sourceHandle,
// 'to',
// ]);
// break;
case Operator.Switch: {
if (sourceHandle) {
const operatorIndex = getOperatorIndex(sourceHandle);
if (operatorIndex) {
updateNodeForm(source, target, [
'conditions',
operatorIndex,
'to',
]);
}
}
break;
}
default:
break;
}
@ -201,7 +206,11 @@ const useGraphStore = create<RFState>()(
// Delete the edge on the classification node or relevant node anchor when the anchor is connected to other nodes
const { edges, getOperatorTypeFromId, deleteEdgeById } = get();
// the node containing the anchor
const anchoredNodes = [Operator.Categorize, Operator.Relevant];
const anchoredNodes = [
Operator.Categorize,
Operator.Relevant,
Operator.Switch,
];
if (
anchoredNodes.some(
(x) => x === getOperatorTypeFromId(connection.source),
@ -265,6 +274,19 @@ const useGraphStore = create<RFState>()(
'to',
]);
break;
case Operator.Switch: {
if (sourceHandle) {
const operatorIndex = getOperatorIndex(sourceHandle);
if (operatorIndex) {
updateNodeForm(source, undefined, [
'conditions',
operatorIndex,
'to',
]);
}
}
break;
}
default:
break;
}

View File

@ -1,10 +1,10 @@
import { CloseOutlined } from '@ant-design/icons';
import { Button, Card, Form, Input, Select, Typography } from 'antd';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Operator } from '../constant';
import { useBuildFormSelectOptions } from '../form-hooks';
import { IOperatorForm } from '../interface';
import { IOperatorForm, ISwitchForm } from '../interface';
import { getOtherFieldValues } from '../utils';
const subLabelCol = {
span: 7,
@ -14,17 +14,20 @@ const subWrapperCol = {
span: 17,
};
const SwitchForm: React.FC = ({
form,
onValuesChange,
nodeId,
}: IOperatorForm) => {
const SwitchForm = ({ onValuesChange, node, form }: IOperatorForm) => {
const { t } = useTranslation();
const buildCategorizeToOptions = useBuildFormSelectOptions(
Operator.Categorize,
nodeId,
Operator.Switch,
node?.id,
);
const getSelectedConditionTos = () => {
const conditions: ISwitchForm['conditions'] =
form?.getFieldValue('conditions');
return conditions?.filter((x) => !!x).map((x) => x?.to) ?? [];
};
return (
<Form
labelCol={{ span: 8 }}
@ -36,7 +39,10 @@ const SwitchForm: React.FC = ({
onValuesChange={onValuesChange}
>
<Form.Item label={t('flow.to')} name={['end_cpn_id']}>
<Select options={buildCategorizeToOptions([])} />
<Select
allowClear
options={buildCategorizeToOptions(getSelectedConditionTos())}
/>
</Form.Item>
<Form.Item label={t('flow.no')} name={['no']}>
<Input />
@ -65,7 +71,13 @@ const SwitchForm: React.FC = ({
</Form.Item>
<Form.Item label={t('flow.to')} name={[field.name, 'to']}>
<Select options={buildCategorizeToOptions([])} />
<Select
allowClear
options={buildCategorizeToOptions([
form?.getFieldValue('end_cpn_id'),
...getOtherFieldValues(form!, 'conditions', field, 'to'),
])}
/>
</Form.Item>
<Form.Item label="Items">
<Form.List name={[field.name, 'items']}>

View File

@ -1,7 +1,8 @@
import { DSLComponents } from '@/interfaces/database/flow';
import { removeUselessFieldsFromValues } from '@/utils/form';
import { FormInstance, FormListFieldData } from 'antd';
import { humanId } from 'human-id';
import { curry, intersectionWith, isEqual, sample } from 'lodash';
import { curry, get, intersectionWith, isEqual, sample } from 'lodash';
import pipe from 'lodash/fp/pipe';
import isObject from 'lodash/isObject';
import { Edge, Node, Position } from 'reactflow';
@ -209,3 +210,27 @@ export const buildNewPositionMap = (
export const isKeysEqual = (currentKeys: string[], previousKeys: string[]) => {
return isEqual(currentKeys.sort(), previousKeys.sort());
};
export const getOperatorIndex = (handleTitle: string) => {
return handleTitle.split(' ').at(-1);
};
// Get the value of other forms except itself
export const getOtherFieldValues = (
form: FormInstance,
formListName: string = 'items',
field: FormListFieldData,
latestField: string,
) =>
(form.getFieldValue([formListName]) ?? [])
.map((x: any) => {
return get(x, latestField);
})
.filter(
(x: string) =>
x !== form.getFieldValue([formListName, field.name, latestField]),
);
export const generateSwitchHandleText = (idx: number) => {
return `Item ${idx + 1}`;
};