balibabu
commited on
Commit
·
5365cac
1
Parent(s):
3b1b360
feat: fixed issue with threshold translation #882 and add NodeContextMenu (#906)
Browse files### What problem does this PR solve?
feat: fixed issue with threshold translation #882
feat: add NodeContextMenu
### Type of change
- [ ] New Feature (non-breaking change which adds functionality)
- web/src/locales/zh-traditional.ts +2 -2
- web/src/locales/zh.ts +2 -2
- web/src/pages/flow/canvas/context-menu/index.less +18 -0
- web/src/pages/flow/canvas/context-menu/index.tsx +108 -0
- web/src/pages/flow/canvas/index.tsx +32 -9
- web/src/pages/flow/flow-drawer/index.tsx +20 -0
- web/src/pages/flow/flow-sider/index.tsx +9 -6
- web/src/pages/flow/hooks.ts +59 -31
- web/src/pages/flow/index.tsx +13 -7
web/src/locales/zh-traditional.ts
CHANGED
@@ -247,8 +247,8 @@ export default {
|
|
247 |
以上就是你需要總結的內容。`,
|
248 |
maxToken: '最大token數',
|
249 |
maxTokenMessage: '最大token數是必填項',
|
250 |
-
threshold: '
|
251 |
-
thresholdMessage: '
|
252 |
maxCluster: '最大聚類數',
|
253 |
maxClusterMessage: '最大聚類數是必填項',
|
254 |
randomSeed: '隨機種子',
|
|
|
247 |
以上就是你需要總結的內容。`,
|
248 |
maxToken: '最大token數',
|
249 |
maxTokenMessage: '最大token數是必填項',
|
250 |
+
threshold: '閾值',
|
251 |
+
thresholdMessage: '閾值是必填項',
|
252 |
maxCluster: '最大聚類數',
|
253 |
maxClusterMessage: '最大聚類數是必填項',
|
254 |
randomSeed: '隨機種子',
|
web/src/locales/zh.ts
CHANGED
@@ -264,8 +264,8 @@ export default {
|
|
264 |
以上就是你需要总结的内容。`,
|
265 |
maxToken: '最大token数',
|
266 |
maxTokenMessage: '最大token数是必填项',
|
267 |
-
threshold: '
|
268 |
-
thresholdMessage: '
|
269 |
maxCluster: '最大聚类数',
|
270 |
maxClusterMessage: '最大聚类数是必填项',
|
271 |
randomSeed: '随机种子',
|
|
|
264 |
以上就是你需要总结的内容。`,
|
265 |
maxToken: '最大token数',
|
266 |
maxTokenMessage: '最大token数是必填项',
|
267 |
+
threshold: '阈值',
|
268 |
+
thresholdMessage: '阈值是必填项',
|
269 |
maxCluster: '最大聚类数',
|
270 |
maxClusterMessage: '最大聚类数是必填项',
|
271 |
randomSeed: '随机种子',
|
web/src/pages/flow/canvas/context-menu/index.less
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.contextMenu {
|
2 |
+
background: white;
|
3 |
+
border-style: solid;
|
4 |
+
box-shadow: 10px 19px 20px rgba(0, 0, 0, 10%);
|
5 |
+
position: absolute;
|
6 |
+
z-index: 10;
|
7 |
+
button {
|
8 |
+
border: none;
|
9 |
+
display: block;
|
10 |
+
padding: 0.5em;
|
11 |
+
text-align: left;
|
12 |
+
width: 100%;
|
13 |
+
}
|
14 |
+
|
15 |
+
button:hover {
|
16 |
+
background: white;
|
17 |
+
}
|
18 |
+
}
|
web/src/pages/flow/canvas/context-menu/index.tsx
ADDED
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useCallback, useRef, useState } from 'react';
|
2 |
+
import { NodeMouseHandler, useReactFlow } from 'reactflow';
|
3 |
+
|
4 |
+
import styles from './index.less';
|
5 |
+
|
6 |
+
export interface INodeContextMenu {
|
7 |
+
id: string;
|
8 |
+
top: number;
|
9 |
+
left: number;
|
10 |
+
right?: number;
|
11 |
+
bottom?: number;
|
12 |
+
[key: string]: unknown;
|
13 |
+
}
|
14 |
+
|
15 |
+
export function NodeContextMenu({
|
16 |
+
id,
|
17 |
+
top,
|
18 |
+
left,
|
19 |
+
right,
|
20 |
+
bottom,
|
21 |
+
...props
|
22 |
+
}: INodeContextMenu) {
|
23 |
+
const { getNode, setNodes, addNodes, setEdges } = useReactFlow();
|
24 |
+
|
25 |
+
const duplicateNode = useCallback(() => {
|
26 |
+
const node = getNode(id);
|
27 |
+
const position = {
|
28 |
+
x: node?.position?.x || 0 + 50,
|
29 |
+
y: node?.position?.y || 0 + 50,
|
30 |
+
};
|
31 |
+
|
32 |
+
addNodes({
|
33 |
+
...(node || {}),
|
34 |
+
data: node?.data,
|
35 |
+
selected: false,
|
36 |
+
dragging: false,
|
37 |
+
id: `${node?.id}-copy`,
|
38 |
+
position,
|
39 |
+
});
|
40 |
+
}, [id, getNode, addNodes]);
|
41 |
+
|
42 |
+
const deleteNode = useCallback(() => {
|
43 |
+
setNodes((nodes) => nodes.filter((node) => node.id !== id));
|
44 |
+
setEdges((edges) => edges.filter((edge) => edge.source !== id));
|
45 |
+
}, [id, setNodes, setEdges]);
|
46 |
+
|
47 |
+
return (
|
48 |
+
<div
|
49 |
+
style={{ top, left, right, bottom }}
|
50 |
+
className={styles.contextMenu}
|
51 |
+
{...props}
|
52 |
+
>
|
53 |
+
<p style={{ margin: '0.5em' }}>
|
54 |
+
<small>node: {id}</small>
|
55 |
+
</p>
|
56 |
+
<button onClick={duplicateNode} type={'button'}>
|
57 |
+
duplicate
|
58 |
+
</button>
|
59 |
+
<button onClick={deleteNode} type={'button'}>
|
60 |
+
delete
|
61 |
+
</button>
|
62 |
+
</div>
|
63 |
+
);
|
64 |
+
}
|
65 |
+
|
66 |
+
export const useHandleNodeContextMenu = (sideWidth: number) => {
|
67 |
+
const [menu, setMenu] = useState<INodeContextMenu>({} as INodeContextMenu);
|
68 |
+
const ref = useRef<any>(null);
|
69 |
+
|
70 |
+
const onNodeContextMenu: NodeMouseHandler = useCallback(
|
71 |
+
(event, node) => {
|
72 |
+
// Prevent native context menu from showing
|
73 |
+
event.preventDefault();
|
74 |
+
|
75 |
+
// Calculate position of the context menu. We want to make sure it
|
76 |
+
// doesn't get positioned off-screen.
|
77 |
+
const pane = ref.current?.getBoundingClientRect();
|
78 |
+
// setMenu({
|
79 |
+
// id: node.id,
|
80 |
+
// top: event.clientY < pane.height - 200 ? event.clientY : 0,
|
81 |
+
// left: event.clientX < pane.width - 200 ? event.clientX : 0,
|
82 |
+
// right: event.clientX >= pane.width - 200 ? pane.width - event.clientX : 0,
|
83 |
+
// bottom:
|
84 |
+
// event.clientY >= pane.height - 200 ? pane.height - event.clientY : 0,
|
85 |
+
// });
|
86 |
+
|
87 |
+
console.info('clientX:', event.clientX);
|
88 |
+
console.info('clientY:', event.clientY);
|
89 |
+
|
90 |
+
setMenu({
|
91 |
+
id: node.id,
|
92 |
+
top: event.clientY - 72,
|
93 |
+
left: event.clientX - sideWidth,
|
94 |
+
// top: event.clientY < pane.height - 200 ? event.clientY - 72 : 0,
|
95 |
+
// left: event.clientX < pane.width - 200 ? event.clientX : 0,
|
96 |
+
});
|
97 |
+
},
|
98 |
+
[sideWidth],
|
99 |
+
);
|
100 |
+
|
101 |
+
// Close the context menu if it's open whenever the window is clicked.
|
102 |
+
const onPaneClick = useCallback(
|
103 |
+
() => setMenu({} as INodeContextMenu),
|
104 |
+
[setMenu],
|
105 |
+
);
|
106 |
+
|
107 |
+
return { onNodeContextMenu, menu, onPaneClick, ref };
|
108 |
+
};
|
web/src/pages/flow/canvas/index.tsx
CHANGED
@@ -4,6 +4,7 @@ import ReactFlow, {
|
|
4 |
Controls,
|
5 |
Edge,
|
6 |
Node,
|
|
|
7 |
OnConnect,
|
8 |
OnEdgesChange,
|
9 |
OnNodesChange,
|
@@ -13,7 +14,10 @@ import ReactFlow, {
|
|
13 |
} from 'reactflow';
|
14 |
import 'reactflow/dist/style.css';
|
15 |
|
16 |
-
import {
|
|
|
|
|
|
|
17 |
import { TextUpdaterNode } from './node';
|
18 |
|
19 |
const nodeTypes = { textUpdater: TextUpdaterNode };
|
@@ -42,9 +46,17 @@ const initialEdges = [
|
|
42 |
{ id: '1-2', source: '1', target: '2', label: 'to the', type: 'step' },
|
43 |
];
|
44 |
|
45 |
-
|
|
|
|
|
|
|
|
|
|
|
46 |
const [nodes, setNodes] = useState<Node[]>(initialNodes);
|
47 |
const [edges, setEdges] = useState<Edge[]>(initialEdges);
|
|
|
|
|
|
|
48 |
|
49 |
const onNodesChange: OnNodesChange = useCallback(
|
50 |
(changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
|
@@ -60,7 +72,11 @@ function FlowCanvas() {
|
|
60 |
[],
|
61 |
);
|
62 |
|
63 |
-
const
|
|
|
|
|
|
|
|
|
64 |
|
65 |
useEffect(() => {
|
66 |
console.info('nodes:', nodes);
|
@@ -68,23 +84,30 @@ function FlowCanvas() {
|
|
68 |
}, [nodes, edges]);
|
69 |
|
70 |
return (
|
71 |
-
<div
|
72 |
-
style={{ height: '100%', width: '100%' }}
|
73 |
-
onDrop={handleDrop}
|
74 |
-
onDragOver={allowDrop}
|
75 |
-
>
|
76 |
<ReactFlow
|
|
|
77 |
nodes={nodes}
|
78 |
onNodesChange={onNodesChange}
|
|
|
79 |
edges={edges}
|
80 |
onEdgesChange={onEdgesChange}
|
81 |
-
|
82 |
onConnect={onConnect}
|
83 |
nodeTypes={nodeTypes}
|
|
|
|
|
|
|
|
|
|
|
84 |
>
|
85 |
<Background />
|
86 |
<Controls />
|
|
|
|
|
|
|
87 |
</ReactFlow>
|
|
|
88 |
</div>
|
89 |
);
|
90 |
}
|
|
|
4 |
Controls,
|
5 |
Edge,
|
6 |
Node,
|
7 |
+
NodeMouseHandler,
|
8 |
OnConnect,
|
9 |
OnEdgesChange,
|
10 |
OnNodesChange,
|
|
|
14 |
} from 'reactflow';
|
15 |
import 'reactflow/dist/style.css';
|
16 |
|
17 |
+
import { NodeContextMenu, useHandleNodeContextMenu } from './context-menu';
|
18 |
+
|
19 |
+
import FlowDrawer from '../flow-drawer';
|
20 |
+
import { useHandleDrop, useShowDrawer } from '../hooks';
|
21 |
import { TextUpdaterNode } from './node';
|
22 |
|
23 |
const nodeTypes = { textUpdater: TextUpdaterNode };
|
|
|
46 |
{ id: '1-2', source: '1', target: '2', label: 'to the', type: 'step' },
|
47 |
];
|
48 |
|
49 |
+
interface IProps {
|
50 |
+
sideWidth: number;
|
51 |
+
showDrawer(): void;
|
52 |
+
}
|
53 |
+
|
54 |
+
function FlowCanvas({ sideWidth }: IProps) {
|
55 |
const [nodes, setNodes] = useState<Node[]>(initialNodes);
|
56 |
const [edges, setEdges] = useState<Edge[]>(initialEdges);
|
57 |
+
const { ref, menu, onNodeContextMenu, onPaneClick } =
|
58 |
+
useHandleNodeContextMenu(sideWidth);
|
59 |
+
const { drawerVisible, hideDrawer, showDrawer } = useShowDrawer();
|
60 |
|
61 |
const onNodesChange: OnNodesChange = useCallback(
|
62 |
(changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
|
|
|
72 |
[],
|
73 |
);
|
74 |
|
75 |
+
const onNodeClick: NodeMouseHandler = useCallback(() => {
|
76 |
+
showDrawer();
|
77 |
+
}, [showDrawer]);
|
78 |
+
|
79 |
+
const { onDrop, onDragOver, setReactFlowInstance } = useHandleDrop(setNodes);
|
80 |
|
81 |
useEffect(() => {
|
82 |
console.info('nodes:', nodes);
|
|
|
84 |
}, [nodes, edges]);
|
85 |
|
86 |
return (
|
87 |
+
<div style={{ height: '100%', width: '100%' }}>
|
|
|
|
|
|
|
|
|
88 |
<ReactFlow
|
89 |
+
ref={ref}
|
90 |
nodes={nodes}
|
91 |
onNodesChange={onNodesChange}
|
92 |
+
onNodeContextMenu={onNodeContextMenu}
|
93 |
edges={edges}
|
94 |
onEdgesChange={onEdgesChange}
|
95 |
+
fitView
|
96 |
onConnect={onConnect}
|
97 |
nodeTypes={nodeTypes}
|
98 |
+
onPaneClick={onPaneClick}
|
99 |
+
onDrop={onDrop}
|
100 |
+
onDragOver={onDragOver}
|
101 |
+
onNodeClick={onNodeClick}
|
102 |
+
onInit={setReactFlowInstance}
|
103 |
>
|
104 |
<Background />
|
105 |
<Controls />
|
106 |
+
{Object.keys(menu).length > 0 && (
|
107 |
+
<NodeContextMenu onClick={onPaneClick} {...(menu as any)} />
|
108 |
+
)}
|
109 |
</ReactFlow>
|
110 |
+
<FlowDrawer visible={drawerVisible} hideModal={hideDrawer}></FlowDrawer>
|
111 |
</div>
|
112 |
);
|
113 |
}
|
web/src/pages/flow/flow-drawer/index.tsx
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { IModalProps } from '@/interfaces/common';
|
2 |
+
import { Drawer } from 'antd';
|
3 |
+
|
4 |
+
const FlowDrawer = ({ visible, hideModal }: IModalProps<any>) => {
|
5 |
+
return (
|
6 |
+
<Drawer
|
7 |
+
title="Basic Drawer"
|
8 |
+
placement="right"
|
9 |
+
// closable={false}
|
10 |
+
onClose={hideModal}
|
11 |
+
open={visible}
|
12 |
+
getContainer={false}
|
13 |
+
mask={false}
|
14 |
+
>
|
15 |
+
<p>Some contents...</p>
|
16 |
+
</Drawer>
|
17 |
+
);
|
18 |
+
};
|
19 |
+
|
20 |
+
export default FlowDrawer;
|
web/src/pages/flow/flow-sider/index.tsx
CHANGED
@@ -1,6 +1,5 @@
|
|
1 |
import { Avatar, Card, Flex, Layout, Space } from 'antd';
|
2 |
import classNames from 'classnames';
|
3 |
-
import { useState } from 'react';
|
4 |
import { componentList } from '../mock';
|
5 |
|
6 |
import { useHandleDrag } from '../hooks';
|
@@ -8,9 +7,13 @@ import styles from './index.less';
|
|
8 |
|
9 |
const { Sider } = Layout;
|
10 |
|
11 |
-
|
12 |
-
|
13 |
-
|
|
|
|
|
|
|
|
|
14 |
|
15 |
return (
|
16 |
<Sider
|
@@ -27,7 +30,7 @@ const FlowSider = () => {
|
|
27 |
hoverable
|
28 |
draggable
|
29 |
className={classNames(styles.operatorCard)}
|
30 |
-
onDragStart={
|
31 |
>
|
32 |
<Flex justify="space-between" align="center">
|
33 |
<Space size={15}>
|
@@ -45,4 +48,4 @@ const FlowSider = () => {
|
|
45 |
);
|
46 |
};
|
47 |
|
48 |
-
export default
|
|
|
1 |
import { Avatar, Card, Flex, Layout, Space } from 'antd';
|
2 |
import classNames from 'classnames';
|
|
|
3 |
import { componentList } from '../mock';
|
4 |
|
5 |
import { useHandleDrag } from '../hooks';
|
|
|
7 |
|
8 |
const { Sider } = Layout;
|
9 |
|
10 |
+
interface IProps {
|
11 |
+
setCollapsed: (width: boolean) => void;
|
12 |
+
collapsed: boolean;
|
13 |
+
}
|
14 |
+
|
15 |
+
const FlowSide = ({ setCollapsed, collapsed }: IProps) => {
|
16 |
+
const { handleDragStart } = useHandleDrag();
|
17 |
|
18 |
return (
|
19 |
<Sider
|
|
|
30 |
hoverable
|
31 |
draggable
|
32 |
className={classNames(styles.operatorCard)}
|
33 |
+
onDragStart={handleDragStart(x.name)}
|
34 |
>
|
35 |
<Flex justify="space-between" align="center">
|
36 |
<Space size={15}>
|
|
|
48 |
);
|
49 |
};
|
50 |
|
51 |
+
export default FlowSide;
|
web/src/pages/flow/hooks.ts
CHANGED
@@ -1,47 +1,75 @@
|
|
1 |
-
import
|
2 |
-
import {
|
|
|
|
|
3 |
|
4 |
export const useHandleDrag = () => {
|
5 |
-
const
|
6 |
(operatorId: string) => (ev: React.DragEvent<HTMLDivElement>) => {
|
7 |
-
|
8 |
-
ev.dataTransfer.
|
9 |
-
ev.dataTransfer.setData('startClientX', ev.clientX.toString());
|
10 |
-
ev.dataTransfer.setData('startClientY', ev.clientY.toString());
|
11 |
},
|
12 |
[],
|
13 |
);
|
14 |
|
15 |
-
return {
|
16 |
};
|
17 |
|
18 |
export const useHandleDrop = (setNodes: Dispatch<SetStateAction<Node[]>>) => {
|
19 |
-
const
|
20 |
-
|
21 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
const
|
27 |
-
|
28 |
-
|
29 |
-
console.info(operatorId);
|
30 |
-
console.info(ev.pageX, ev.pageY);
|
31 |
-
console.info(ev.clientX, ev.clientY);
|
32 |
-
console.info(ev.movementX, ev.movementY);
|
33 |
-
const x = ev.clientX - 200;
|
34 |
-
const y = ev.clientY - 72;
|
35 |
-
setNodes((pre) => {
|
36 |
-
return pre.concat({
|
37 |
-
id: operatorId,
|
38 |
-
position: { x, y },
|
39 |
-
data: { label: operatorId },
|
40 |
-
});
|
41 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
},
|
43 |
-
[setNodes],
|
44 |
);
|
45 |
|
46 |
-
return {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
};
|
|
|
1 |
+
import { useSetModalState } from '@/hooks/commonHooks';
|
2 |
+
import React, { Dispatch, SetStateAction, useCallback, useState } from 'react';
|
3 |
+
import { Node, ReactFlowInstance } from 'reactflow';
|
4 |
+
import { v4 as uuidv4 } from 'uuid';
|
5 |
|
6 |
export const useHandleDrag = () => {
|
7 |
+
const handleDragStart = useCallback(
|
8 |
(operatorId: string) => (ev: React.DragEvent<HTMLDivElement>) => {
|
9 |
+
ev.dataTransfer.setData('application/reactflow', operatorId);
|
10 |
+
ev.dataTransfer.effectAllowed = 'move';
|
|
|
|
|
11 |
},
|
12 |
[],
|
13 |
);
|
14 |
|
15 |
+
return { handleDragStart };
|
16 |
};
|
17 |
|
18 |
export const useHandleDrop = (setNodes: Dispatch<SetStateAction<Node[]>>) => {
|
19 |
+
const [reactFlowInstance, setReactFlowInstance] =
|
20 |
+
useState<ReactFlowInstance<any, any>>();
|
21 |
+
|
22 |
+
const onDragOver = useCallback((event: React.DragEvent<HTMLDivElement>) => {
|
23 |
+
event.preventDefault();
|
24 |
+
event.dataTransfer.dropEffect = 'move';
|
25 |
+
}, []);
|
26 |
+
|
27 |
+
const onDrop = useCallback(
|
28 |
+
(event: React.DragEvent<HTMLDivElement>) => {
|
29 |
+
event.preventDefault();
|
30 |
+
|
31 |
+
const type = event.dataTransfer.getData('application/reactflow');
|
32 |
+
|
33 |
+
// check if the dropped element is valid
|
34 |
+
if (typeof type === 'undefined' || !type) {
|
35 |
+
return;
|
36 |
+
}
|
37 |
|
38 |
+
// reactFlowInstance.project was renamed to reactFlowInstance.screenToFlowPosition
|
39 |
+
// and you don't need to subtract the reactFlowBounds.left/top anymore
|
40 |
+
// details: https://reactflow.dev/whats-new/2023-11-10
|
41 |
+
const position = reactFlowInstance?.screenToFlowPosition({
|
42 |
+
x: event.clientX,
|
43 |
+
y: event.clientY,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
44 |
});
|
45 |
+
const newNode = {
|
46 |
+
id: uuidv4(),
|
47 |
+
type,
|
48 |
+
position: position || {
|
49 |
+
x: 0,
|
50 |
+
y: 0,
|
51 |
+
},
|
52 |
+
data: { label: `${type} node` },
|
53 |
+
};
|
54 |
+
|
55 |
+
setNodes((nds) => nds.concat(newNode));
|
56 |
},
|
57 |
+
[reactFlowInstance, setNodes],
|
58 |
);
|
59 |
|
60 |
+
return { onDrop, onDragOver, setReactFlowInstance };
|
61 |
+
};
|
62 |
+
|
63 |
+
export const useShowDrawer = () => {
|
64 |
+
const {
|
65 |
+
visible: drawerVisible,
|
66 |
+
hideModal: hideDrawer,
|
67 |
+
showModal: showDrawer,
|
68 |
+
} = useSetModalState();
|
69 |
+
|
70 |
+
return {
|
71 |
+
drawerVisible,
|
72 |
+
hideDrawer,
|
73 |
+
showDrawer,
|
74 |
+
};
|
75 |
};
|
web/src/pages/flow/index.tsx
CHANGED
@@ -1,18 +1,24 @@
|
|
1 |
import { Layout } from 'antd';
|
|
|
|
|
2 |
import FlowCanvas from './canvas';
|
3 |
import Sider from './flow-sider';
|
4 |
|
5 |
const { Content } = Layout;
|
6 |
|
7 |
function RagFlow() {
|
|
|
|
|
8 |
return (
|
9 |
-
<Layout
|
10 |
-
<
|
11 |
-
|
12 |
-
<
|
13 |
-
<
|
14 |
-
|
15 |
-
|
|
|
|
|
16 |
</Layout>
|
17 |
);
|
18 |
}
|
|
|
1 |
import { Layout } from 'antd';
|
2 |
+
import { useState } from 'react';
|
3 |
+
import { ReactFlowProvider } from 'reactflow';
|
4 |
import FlowCanvas from './canvas';
|
5 |
import Sider from './flow-sider';
|
6 |
|
7 |
const { Content } = Layout;
|
8 |
|
9 |
function RagFlow() {
|
10 |
+
const [collapsed, setCollapsed] = useState(false);
|
11 |
+
|
12 |
return (
|
13 |
+
<Layout>
|
14 |
+
<ReactFlowProvider>
|
15 |
+
<Sider setCollapsed={setCollapsed} collapsed={collapsed}></Sider>
|
16 |
+
<Layout>
|
17 |
+
<Content style={{ margin: '0 16px' }}>
|
18 |
+
<FlowCanvas sideWidth={collapsed ? 0 : 200}></FlowCanvas>
|
19 |
+
</Content>
|
20 |
+
</Layout>
|
21 |
+
</ReactFlowProvider>
|
22 |
</Layout>
|
23 |
);
|
24 |
}
|