mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-08-15 00:15:57 +08:00
### What problem does this PR solve? feat: add RelevantForm #918 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
parent
25c4c717cb
commit
a7423e3a94
@ -558,6 +558,8 @@ The above is the content you need to summarize.`,
|
|||||||
addField: 'Add field',
|
addField: 'Add field',
|
||||||
loop: 'Loop',
|
loop: 'Loop',
|
||||||
createFlow: 'Create a workflow',
|
createFlow: 'Create a workflow',
|
||||||
|
yes: 'Yes',
|
||||||
|
no: 'No',
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
profile: 'All rights reserved @ React',
|
profile: 'All rights reserved @ React',
|
||||||
|
@ -23,11 +23,13 @@ import ChatDrawer from '../chat/drawer';
|
|||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
import { BeginNode } from './node/begin-node';
|
import { BeginNode } from './node/begin-node';
|
||||||
import { CategorizeNode } from './node/categorize-node';
|
import { CategorizeNode } from './node/categorize-node';
|
||||||
|
import { RelevantNode } from './node/relevant-node';
|
||||||
|
|
||||||
const nodeTypes = {
|
const nodeTypes = {
|
||||||
ragNode: RagNode,
|
ragNode: RagNode,
|
||||||
categorizeNode: CategorizeNode,
|
categorizeNode: CategorizeNode,
|
||||||
beginNode: BeginNode,
|
beginNode: BeginNode,
|
||||||
|
relevantNode: RelevantNode,
|
||||||
};
|
};
|
||||||
|
|
||||||
const edgeTypes = {
|
const edgeTypes = {
|
||||||
|
@ -14,7 +14,7 @@ interface IProps {
|
|||||||
top: number;
|
top: number;
|
||||||
right: number;
|
right: number;
|
||||||
text: string;
|
text: string;
|
||||||
idx: number;
|
idx?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CategorizeHandle = ({ top, right, text, idx }: IProps) => {
|
const CategorizeHandle = ({ top, right, text, idx }: IProps) => {
|
||||||
@ -30,6 +30,7 @@ const CategorizeHandle = ({ top, right, text, idx }: IProps) => {
|
|||||||
top: `${top}%`,
|
top: `${top}%`,
|
||||||
right: `${right}%`,
|
right: `${right}%`,
|
||||||
background: 'red',
|
background: 'red',
|
||||||
|
color: 'black',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className={styles.categorizeAnchorPointText}>{text}</span>
|
<span className={styles.categorizeAnchorPointText}>{text}</span>
|
||||||
|
@ -1,16 +1,10 @@
|
|||||||
import { Flex } from 'antd';
|
import { Flex } from 'antd';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import get from 'lodash/get';
|
|
||||||
import pick from 'lodash/pick';
|
import pick from 'lodash/pick';
|
||||||
import { Handle, NodeProps, Position } from 'reactflow';
|
import { Handle, NodeProps, Position } from 'reactflow';
|
||||||
import {
|
import { Operator, operatorMap } from '../../constant';
|
||||||
CategorizeAnchorPointPositions,
|
|
||||||
Operator,
|
|
||||||
operatorMap,
|
|
||||||
} from '../../constant';
|
|
||||||
import { NodeData } from '../../interface';
|
import { NodeData } from '../../interface';
|
||||||
import OperatorIcon from '../../operator-icon';
|
import OperatorIcon from '../../operator-icon';
|
||||||
import CategorizeHandle from './categorize-handle';
|
|
||||||
import NodeDropdown from './dropdown';
|
import NodeDropdown from './dropdown';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
@ -20,8 +14,6 @@ export function RagNode({
|
|||||||
isConnectable = true,
|
isConnectable = true,
|
||||||
selected,
|
selected,
|
||||||
}: NodeProps<NodeData>) {
|
}: NodeProps<NodeData>) {
|
||||||
const isCategorize = data.label === Operator.Categorize;
|
|
||||||
const categoryData = get(data, 'form.category_description') ?? {};
|
|
||||||
const style = operatorMap[data.label as Operator];
|
const style = operatorMap[data.label as Operator];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -47,16 +39,6 @@ export function RagNode({
|
|||||||
id="b"
|
id="b"
|
||||||
></Handle>
|
></Handle>
|
||||||
<Handle type="source" position={Position.Bottom} id="a" isConnectable />
|
<Handle type="source" position={Position.Bottom} id="a" isConnectable />
|
||||||
{isCategorize &&
|
|
||||||
Object.keys(categoryData).map((x, idx) => (
|
|
||||||
<CategorizeHandle
|
|
||||||
top={CategorizeAnchorPointPositions[idx].top}
|
|
||||||
right={CategorizeAnchorPointPositions[idx].right}
|
|
||||||
key={idx}
|
|
||||||
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}
|
||||||
|
64
web/src/pages/flow/canvas/node/relevant-node.tsx
Normal file
64
web/src/pages/flow/canvas/node/relevant-node.tsx
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import { Flex } from 'antd';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import pick from 'lodash/pick';
|
||||||
|
import { Handle, NodeProps, Position } from 'reactflow';
|
||||||
|
import { Operator, operatorMap } from '../../constant';
|
||||||
|
import { NodeData } from '../../interface';
|
||||||
|
import OperatorIcon from '../../operator-icon';
|
||||||
|
import NodeDropdown from './dropdown';
|
||||||
|
|
||||||
|
import CategorizeHandle from './categorize-handle';
|
||||||
|
import styles from './index.less';
|
||||||
|
|
||||||
|
export function RelevantNode({ id, data, selected }: NodeProps<NodeData>) {
|
||||||
|
const style = operatorMap[data.label as Operator];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
className={classNames(styles.ragNode, {
|
||||||
|
[styles.selectedNode]: selected,
|
||||||
|
})}
|
||||||
|
style={pick(style, ['backgroundColor', 'width', 'height', 'color'])}
|
||||||
|
>
|
||||||
|
<Handle
|
||||||
|
type="target"
|
||||||
|
position={Position.Left}
|
||||||
|
isConnectable
|
||||||
|
className={styles.handle}
|
||||||
|
id={'a'}
|
||||||
|
></Handle>
|
||||||
|
<Handle
|
||||||
|
type="target"
|
||||||
|
position={Position.Top}
|
||||||
|
isConnectable
|
||||||
|
className={styles.handle}
|
||||||
|
id={'b'}
|
||||||
|
></Handle>
|
||||||
|
<Handle
|
||||||
|
type="target"
|
||||||
|
position={Position.Bottom}
|
||||||
|
isConnectable
|
||||||
|
className={styles.handle}
|
||||||
|
id={'c'}
|
||||||
|
></Handle>
|
||||||
|
<CategorizeHandle top={20} right={6} text={'yes'}></CategorizeHandle>
|
||||||
|
<CategorizeHandle top={80} right={6} text={'no'}></CategorizeHandle>
|
||||||
|
<Flex vertical align="center" justify="center">
|
||||||
|
<OperatorIcon
|
||||||
|
name={data.label as Operator}
|
||||||
|
fontSize={style.iconFontSize}
|
||||||
|
></OperatorIcon>
|
||||||
|
<span
|
||||||
|
className={styles.type}
|
||||||
|
style={{ fontSize: style.fontSize ?? 14 }}
|
||||||
|
>
|
||||||
|
{data.label}
|
||||||
|
</span>
|
||||||
|
<NodeDropdown id={id}></NodeDropdown>
|
||||||
|
</Flex>
|
||||||
|
<section className={styles.bottomBox}>
|
||||||
|
<div className={styles.nodeName}>{data.name}</div>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
@ -2,8 +2,12 @@ import { useTranslate } from '@/hooks/commonHooks';
|
|||||||
import { CloseOutlined } from '@ant-design/icons';
|
import { CloseOutlined } from '@ant-design/icons';
|
||||||
import { Button, Card, Form, Input, Select, Typography } from 'antd';
|
import { Button, Card, Form, Input, Select, Typography } from 'antd';
|
||||||
import { useUpdateNodeInternals } from 'reactflow';
|
import { useUpdateNodeInternals } from 'reactflow';
|
||||||
|
import { Operator } from '../constant';
|
||||||
|
import {
|
||||||
|
useBuildFormSelectOptions,
|
||||||
|
useHandleFormSelectChange,
|
||||||
|
} from '../form-hooks';
|
||||||
import { ICategorizeItem } from '../interface';
|
import { ICategorizeItem } from '../interface';
|
||||||
import { useBuildCategorizeToOptions, useHandleToSelectChange } from './hooks';
|
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
nodeId?: string;
|
nodeId?: string;
|
||||||
@ -12,8 +16,11 @@ interface IProps {
|
|||||||
const DynamicCategorize = ({ nodeId }: IProps) => {
|
const DynamicCategorize = ({ nodeId }: IProps) => {
|
||||||
const updateNodeInternals = useUpdateNodeInternals();
|
const updateNodeInternals = useUpdateNodeInternals();
|
||||||
const form = Form.useFormInstance();
|
const form = Form.useFormInstance();
|
||||||
const buildCategorizeToOptions = useBuildCategorizeToOptions();
|
const buildCategorizeToOptions = useBuildFormSelectOptions(
|
||||||
const { handleSelectChange } = useHandleToSelectChange(nodeId);
|
Operator.Categorize,
|
||||||
|
nodeId,
|
||||||
|
);
|
||||||
|
const { handleSelectChange } = useHandleFormSelectChange(nodeId);
|
||||||
const { t } = useTranslate('flow');
|
const { t } = useTranslate('flow');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -2,7 +2,6 @@ import get from 'lodash/get';
|
|||||||
import omit from 'lodash/omit';
|
import omit from 'lodash/omit';
|
||||||
import { useCallback, useEffect } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
import { Edge, Node } from 'reactflow';
|
import { Edge, Node } from 'reactflow';
|
||||||
import { Operator } from '../constant';
|
|
||||||
import {
|
import {
|
||||||
ICategorizeItem,
|
ICategorizeItem,
|
||||||
ICategorizeItemResult,
|
ICategorizeItemResult,
|
||||||
@ -11,28 +10,6 @@ import {
|
|||||||
} from '../interface';
|
} from '../interface';
|
||||||
import useGraphStore from '../store';
|
import useGraphStore from '../store';
|
||||||
|
|
||||||
// exclude some nodes downstream of the classification node
|
|
||||||
const excludedNodes = [Operator.Categorize, Operator.Answer, Operator.Begin];
|
|
||||||
|
|
||||||
export const useBuildCategorizeToOptions = () => {
|
|
||||||
const nodes = useGraphStore((state) => state.nodes);
|
|
||||||
|
|
||||||
const buildCategorizeToOptions = useCallback(
|
|
||||||
(toList: string[]) => {
|
|
||||||
return nodes
|
|
||||||
.filter(
|
|
||||||
(x) =>
|
|
||||||
excludedNodes.every((y) => y !== x.data.label) &&
|
|
||||||
!toList.some((y) => y === x.id), // filter out selected values in other to fields from the current drop-down box options
|
|
||||||
)
|
|
||||||
.map((x) => ({ label: x.data.name, value: x.id }));
|
|
||||||
},
|
|
||||||
[nodes],
|
|
||||||
);
|
|
||||||
|
|
||||||
return buildCategorizeToOptions;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* convert the following object into a list
|
* convert the following object into a list
|
||||||
*
|
*
|
||||||
@ -119,32 +96,3 @@ export const useHandleFormValuesChange = ({
|
|||||||
|
|
||||||
return { handleValuesChange };
|
return { handleValuesChange };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useHandleToSelectChange = (nodeId?: string) => {
|
|
||||||
const { addEdge, deleteEdgeBySourceAndSourceHandle } = useGraphStore(
|
|
||||||
(state) => state,
|
|
||||||
);
|
|
||||||
const handleSelectChange = useCallback(
|
|
||||||
(name?: string) => (value?: string) => {
|
|
||||||
if (nodeId && name) {
|
|
||||||
if (value) {
|
|
||||||
addEdge({
|
|
||||||
source: nodeId,
|
|
||||||
target: value,
|
|
||||||
sourceHandle: name,
|
|
||||||
targetHandle: null,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// clear selected value
|
|
||||||
deleteEdgeBySourceAndSourceHandle({
|
|
||||||
source: nodeId,
|
|
||||||
sourceHandle: name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[addEdge, nodeId, deleteEdgeBySourceAndSourceHandle],
|
|
||||||
);
|
|
||||||
|
|
||||||
return { handleSelectChange };
|
|
||||||
};
|
|
||||||
|
@ -67,7 +67,12 @@ export const operatorMap = {
|
|||||||
},
|
},
|
||||||
[Operator.Relevant]: {
|
[Operator.Relevant]: {
|
||||||
description: 'BranchesOutlined description',
|
description: 'BranchesOutlined description',
|
||||||
backgroundColor: 'white',
|
backgroundColor: '#9fd94d',
|
||||||
|
color: 'white',
|
||||||
|
width: 70,
|
||||||
|
height: 70,
|
||||||
|
fontSize: 12,
|
||||||
|
iconFontSize: 16,
|
||||||
},
|
},
|
||||||
[Operator.RewriteQuestion]: {
|
[Operator.RewriteQuestion]: {
|
||||||
description: 'RewriteQuestion description',
|
description: 'RewriteQuestion description',
|
||||||
@ -136,6 +141,7 @@ export const initialFormValuesMap = {
|
|||||||
[Operator.Generate]: initialGenerateValues,
|
[Operator.Generate]: initialGenerateValues,
|
||||||
[Operator.Answer]: {},
|
[Operator.Answer]: {},
|
||||||
[Operator.Categorize]: {},
|
[Operator.Categorize]: {},
|
||||||
|
[Operator.Relevant]: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CategorizeAnchorPointPositions = [
|
export const CategorizeAnchorPointPositions = [
|
||||||
@ -173,6 +179,6 @@ export const NodeMap = {
|
|||||||
[Operator.Generate]: 'ragNode',
|
[Operator.Generate]: 'ragNode',
|
||||||
[Operator.Answer]: 'ragNode',
|
[Operator.Answer]: 'ragNode',
|
||||||
[Operator.Message]: 'ragNode',
|
[Operator.Message]: 'ragNode',
|
||||||
[Operator.Relevant]: 'ragNode',
|
[Operator.Relevant]: 'relevantNode',
|
||||||
[Operator.RewriteQuestion]: 'ragNode',
|
[Operator.RewriteQuestion]: 'ragNode',
|
||||||
};
|
};
|
||||||
|
62
web/src/pages/flow/form-hooks.ts
Normal file
62
web/src/pages/flow/form-hooks.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { useCallback } from 'react';
|
||||||
|
import { Operator } from './constant';
|
||||||
|
import useGraphStore from './store';
|
||||||
|
|
||||||
|
const ExcludedNodesMap = {
|
||||||
|
// exclude some nodes downstream of the classification node
|
||||||
|
[Operator.Categorize]: [Operator.Categorize, Operator.Answer, Operator.Begin],
|
||||||
|
[Operator.Relevant]: [Operator.Begin],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useBuildFormSelectOptions = (
|
||||||
|
operatorName: Operator,
|
||||||
|
selfId?: string, // exclude the current node
|
||||||
|
) => {
|
||||||
|
const nodes = useGraphStore((state) => state.nodes);
|
||||||
|
|
||||||
|
const buildCategorizeToOptions = useCallback(
|
||||||
|
(toList: string[]) => {
|
||||||
|
const excludedNodes: Operator[] = ExcludedNodesMap[operatorName] ?? [];
|
||||||
|
return nodes
|
||||||
|
.filter(
|
||||||
|
(x) =>
|
||||||
|
excludedNodes.every((y) => y !== x.data.label) &&
|
||||||
|
x.id !== selfId &&
|
||||||
|
!toList.some((y) => y === x.id), // filter out selected values in other to fields from the current drop-down box options
|
||||||
|
)
|
||||||
|
.map((x) => ({ label: x.data.name, value: x.id }));
|
||||||
|
},
|
||||||
|
[nodes, operatorName, selfId],
|
||||||
|
);
|
||||||
|
|
||||||
|
return buildCategorizeToOptions;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useHandleFormSelectChange = (nodeId?: string) => {
|
||||||
|
const { addEdge, deleteEdgeBySourceAndSourceHandle } = useGraphStore(
|
||||||
|
(state) => state,
|
||||||
|
);
|
||||||
|
const handleSelectChange = useCallback(
|
||||||
|
(name?: string) => (value?: string) => {
|
||||||
|
if (nodeId && name) {
|
||||||
|
if (value) {
|
||||||
|
addEdge({
|
||||||
|
source: nodeId,
|
||||||
|
target: value,
|
||||||
|
sourceHandle: name,
|
||||||
|
targetHandle: null,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// clear selected value
|
||||||
|
deleteEdgeBySourceAndSourceHandle({
|
||||||
|
source: nodeId,
|
||||||
|
sourceHandle: name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[addEdge, nodeId, deleteEdgeBySourceAndSourceHandle],
|
||||||
|
);
|
||||||
|
|
||||||
|
return { handleSelectChange };
|
||||||
|
};
|
@ -53,6 +53,11 @@ export interface ICategorizeForm extends IGenerateForm {
|
|||||||
category_description: ICategorizeItemResult;
|
category_description: ICategorizeItemResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IRelevantForm extends IGenerateForm {
|
||||||
|
yes: string;
|
||||||
|
no: string;
|
||||||
|
}
|
||||||
|
|
||||||
export type NodeData = {
|
export type NodeData = {
|
||||||
label: string; // operator type
|
label: string; // operator type
|
||||||
name: string; // operator name
|
name: string; // operator name
|
||||||
|
44
web/src/pages/flow/relevant-form/hooks.ts
Normal file
44
web/src/pages/flow/relevant-form/hooks.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { useCallback, useEffect } from 'react';
|
||||||
|
import { Edge } from 'reactflow';
|
||||||
|
import { IOperatorForm } from '../interface';
|
||||||
|
import useGraphStore from '../store';
|
||||||
|
|
||||||
|
export const useBuildRelevantOptions = () => {
|
||||||
|
const nodes = useGraphStore((state) => state.nodes);
|
||||||
|
|
||||||
|
const buildRelevantOptions = useCallback(
|
||||||
|
(toList: string[]) => {
|
||||||
|
return nodes
|
||||||
|
.filter(
|
||||||
|
(x) => !toList.some((y) => y === x.id), // filter out selected values in other to fields from the current drop-down box options
|
||||||
|
)
|
||||||
|
.map((x) => ({ label: x.data.name, value: x.id }));
|
||||||
|
},
|
||||||
|
[nodes],
|
||||||
|
);
|
||||||
|
|
||||||
|
return buildRelevantOptions;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTargetOfEdge = (edges: Edge[], sourceHandle: string) =>
|
||||||
|
edges.find((x) => x.sourceHandle === sourceHandle)?.target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* monitor changes in the connection and synchronize the target to the yes and no fields of the form
|
||||||
|
* similar to the categorize-form's useHandleFormValuesChange method
|
||||||
|
* @param param0
|
||||||
|
*/
|
||||||
|
export const useWatchConnectionChanges = ({ nodeId, form }: IOperatorForm) => {
|
||||||
|
const edges = useGraphStore((state) => state.edges);
|
||||||
|
|
||||||
|
const watchConnectionChanges = useCallback(() => {
|
||||||
|
const edgeList = edges.filter((x) => x.source === nodeId);
|
||||||
|
const yes = getTargetOfEdge(edgeList, 'yes');
|
||||||
|
const no = getTargetOfEdge(edgeList, 'no');
|
||||||
|
form?.setFieldsValue({ yes, no });
|
||||||
|
}, [edges, nodeId, form]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
watchConnectionChanges();
|
||||||
|
}, [watchConnectionChanges]);
|
||||||
|
};
|
@ -1,12 +1,24 @@
|
|||||||
import LLMSelect from '@/components/llm-select';
|
import LLMSelect from '@/components/llm-select';
|
||||||
import { useTranslate } from '@/hooks/commonHooks';
|
import { useTranslate } from '@/hooks/commonHooks';
|
||||||
import { Form } from 'antd';
|
import { Form, Select } from 'antd';
|
||||||
|
import { Operator } from '../constant';
|
||||||
|
import {
|
||||||
|
useBuildFormSelectOptions,
|
||||||
|
useHandleFormSelectChange,
|
||||||
|
} from '../form-hooks';
|
||||||
import { useSetLlmSetting } from '../hooks';
|
import { useSetLlmSetting } from '../hooks';
|
||||||
import { IOperatorForm } from '../interface';
|
import { IOperatorForm } from '../interface';
|
||||||
|
import { useWatchConnectionChanges } from './hooks';
|
||||||
|
|
||||||
const RelevantForm = ({ onValuesChange, form }: IOperatorForm) => {
|
const RelevantForm = ({ onValuesChange, form, node }: IOperatorForm) => {
|
||||||
const { t } = useTranslate('chat');
|
const { t } = useTranslate('flow');
|
||||||
useSetLlmSetting(form);
|
useSetLlmSetting(form);
|
||||||
|
const buildRelevantOptions = useBuildFormSelectOptions(
|
||||||
|
Operator.Relevant,
|
||||||
|
node?.id,
|
||||||
|
);
|
||||||
|
useWatchConnectionChanges({ nodeId: node?.id, form });
|
||||||
|
const { handleSelectChange } = useHandleFormSelectChange(node?.id);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form
|
||||||
@ -26,6 +38,20 @@ const RelevantForm = ({ onValuesChange, form }: IOperatorForm) => {
|
|||||||
>
|
>
|
||||||
<LLMSelect></LLMSelect>
|
<LLMSelect></LLMSelect>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item label={t('yes')} name={'yes'}>
|
||||||
|
<Select
|
||||||
|
allowClear
|
||||||
|
options={buildRelevantOptions([form?.getFieldValue('no')])}
|
||||||
|
onChange={handleSelectChange('yes')}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label={t('no')} name={'no'}>
|
||||||
|
<Select
|
||||||
|
allowClear
|
||||||
|
options={buildRelevantOptions([form?.getFieldValue('yes')])}
|
||||||
|
onChange={handleSelectChange('no')}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -107,9 +107,15 @@ const useGraphStore = create<RFState>()(
|
|||||||
return get().edges.find((x) => x.id === id);
|
return get().edges.find((x) => x.id === id);
|
||||||
},
|
},
|
||||||
deletePreviousEdgeOfClassificationNode: (connection: Connection) => {
|
deletePreviousEdgeOfClassificationNode: (connection: Connection) => {
|
||||||
// Delete the edge on the classification node anchor when the anchor is connected to other nodes
|
// Delete the edge on the classification node or relevant node anchor when the anchor is connected to other nodes
|
||||||
const { edges, getOperatorTypeFromId } = get();
|
const { edges, getOperatorTypeFromId } = get();
|
||||||
if (getOperatorTypeFromId(connection.source) === Operator.Categorize) {
|
// the node containing the anchor
|
||||||
|
const anchoredNodes = [Operator.Categorize, Operator.Relevant];
|
||||||
|
if (
|
||||||
|
anchoredNodes.some(
|
||||||
|
(x) => x === getOperatorTypeFromId(connection.source),
|
||||||
|
)
|
||||||
|
) {
|
||||||
const previousEdge = edges.find(
|
const previousEdge = edges.find(
|
||||||
(x) =>
|
(x) =>
|
||||||
x.source === connection.source &&
|
x.source === connection.source &&
|
||||||
|
Loading…
x
Reference in New Issue
Block a user