Skip to content

Commit 1bf9f04

Browse files
author
昔梦
committed
feat:注释节点
1 parent 298db86 commit 1bf9f04

File tree

8 files changed

+208
-37
lines changed

8 files changed

+208
-37
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110
"typescript": "^5.2.2",
111111
"vite": "^3.2.5",
112112
"vitest": "^0.25.6",
113-
"yorkie": "^2.0.0"
113+
"yorkie": "^2.0.0",
114+
"braft-editor": "^2.3.9"
114115
}
115116
}

packages/x-flow/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
"@uiw/react-codemirror": "^4.23.7",
5252
"@xyflow/react": "^12.3.2",
5353
"ahooks": "^3.7.5",
54+
"braft-editor": "^2.3.9",
5455
"classnames": "^2.3.1",
5556
"dayjs": "^1.11.7",
5657
"form-render": "^2.3.4",

packages/x-flow/src/components/CustomNode/index.less

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,9 @@
9393
display: none;
9494
}
9595
}
96+
97+
98+
.xflow-node-container-note{
99+
border: none;
100+
padding: 2px 6px 6px 6px;
101+
}

packages/x-flow/src/components/CustomNode/index.tsx

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,23 @@ export default memo((props: any) => {
2929
widgets[`${capitalize(type)}Node`] || widgets['CommonNode'];
3030
const [isHovered, setIsHovered] = useState(false);
3131
const reactflow = useReactFlow();
32-
const { addNodes, addEdges, copyNode, pasteNode, deleteNode, mousePosition } = useStore(
33-
(state: any) => ({
34-
nodes: state.nodes,
35-
edges: state.edges,
36-
mousePosition: state.mousePosition,
37-
addNodes: state.addNodes,
38-
addEdges: state.addEdges,
39-
copyNode: state.copyNode,
40-
pasteNode: state.pasteNode,
41-
deleteNode: state.deleteNode,
42-
onEdgesChange: state.onEdgesChange,
43-
}),
44-
shallow
45-
);
46-
const isSwitchNode = type === 'Switch' || type === 'Parallel'; // 判断是否为条件节点/并行节点
32+
const { addNodes, addEdges, copyNode, pasteNode, deleteNode, mousePosition } =
33+
useStore(
34+
(state: any) => ({
35+
nodes: state.nodes,
36+
edges: state.edges,
37+
mousePosition: state.mousePosition,
38+
addNodes: state.addNodes,
39+
addEdges: state.addEdges,
40+
copyNode: state.copyNode,
41+
pasteNode: state.pasteNode,
42+
deleteNode: state.deleteNode,
43+
onEdgesChange: state.onEdgesChange,
44+
}),
45+
shallow
46+
);
47+
const isNote = type === 'Note';
48+
const isSwitchNode = type === 'Switch' || type === 'Parallel' || isNote; // 判断是否为条件节点/并行节点/注释节点
4749
// 增加节点并进行联系
4850
const handleAddNode = (data: any, sourceHandle?: string) => {
4951
const { screenToFlowPosition } = reactflow;
@@ -231,18 +233,18 @@ export default memo((props: any) => {
231233
overlay: menu,
232234
};
233235
}, [menuItem]);
234-
235236
return (
236237
<div
237238
className={classNames('xflow-node-container', {
238239
['xflow-node-container-selected']: !!selected,
239240
['xflow-node-container-tb']: layout === 'TB',
241+
['xflow-node-container-note']: isNote,
240242
})}
241243
onMouseEnter={() => setIsHovered(true)}
242244
onMouseLeave={() => setIsHovered(false)}
243245
style={{ '--nodeBorderColor': nodeBorderColor } as React.CSSProperties}
244246
>
245-
{!settingMap?.[type]?.targetHandleHidden && (
247+
{!settingMap?.[type]?.targetHandleHidden && !isNote && (
246248
<Handle
247249
type="target"
248250
position={targetPosition}
@@ -289,4 +291,3 @@ export default memo((props: any) => {
289291
</div>
290292
);
291293
});
292-

packages/x-flow/src/nodes/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ export { default as SwitchNode } from './node-switch';
55
export { default as SwitchNodeSettingWidget } from './node-switch/setting';
66
export { default as ParallelNode } from './node-parallel';
77
export { default as ParallelNodeSettingWidget } from './node-parallel/setting';
8+
export { default as NoteNode } from './node-note';
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
.node-note-wrap {
2+
.node-note-braft {
3+
border: 1px solid rgb(132, 202, 255);
4+
border-radius: 8px;
5+
width: 240px;
6+
height: 88px;
7+
background: rgb(239, 248, 255);
8+
9+
.node-note-braft-content {
10+
height: 100px !important;
11+
overflow-y: auto;
12+
}
13+
14+
.node-note-braft-control {
15+
.control-item.button {
16+
font-size: 12px;
17+
min-width: 8px;
18+
padding: 0px 6px;
19+
}
20+
}
21+
}
22+
23+
.bf-dropdown .dropdown-content-inner {
24+
background-color: rgb(239, 248, 255);
25+
color: black;
26+
border-radius: 8px;
27+
}
28+
.text-color-dropdown .bf-color-switch-buttons button {
29+
color: black;
30+
border-bottom: 1px solid #e4e9ec;
31+
}
32+
.text-color-dropdown .bf-color-switch-buttons button.active {
33+
border-bottom-color: #3498db;
34+
color: #3498db;
35+
}
36+
.bf-dropdown .dropdown-content .menu-item:not(.active) {
37+
color: black;
38+
border-bottom: 1px solid #e4e9ec;
39+
}
40+
.bf-font-sizes li {
41+
color: black;
42+
}
43+
.bf-dropdown .dropdown-content .dropdown-arrow {
44+
background-color: rgb(239, 248, 255);
45+
}
46+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import BraftEditor from 'braft-editor';
2+
import 'braft-editor/dist/index.css';
3+
import { debounce } from 'lodash';
4+
import React, { memo, useRef, useState } from 'react';
5+
import { shallow } from 'zustand/shallow';
6+
import { useStore } from '../../hooks/useStore';
7+
import './index.less';
8+
9+
export default memo((props: any) => {
10+
const { onClick, type, data, id } = props;
11+
const { nodes, setNodes } = useStore(
12+
s => ({
13+
nodes: s.nodes,
14+
setNodes: s.setNodes,
15+
}),
16+
shallow
17+
);
18+
19+
const editorRef = useRef(null);
20+
const [editorState, setEditorState] = useState(
21+
BraftEditor.createEditorState(data?.value)
22+
);
23+
24+
// 当编辑器内容发生变化时触发
25+
const handleEditorChange = newEditorState => {
26+
setEditorState(newEditorState);
27+
handleNodeValueChange({ value: newEditorState.toHTML() });
28+
};
29+
30+
const handleNodeValueChange = debounce((data: any) => {
31+
for (let node of nodes) {
32+
if (node.id === id) {
33+
node.data = {
34+
...node?.data,
35+
...data,
36+
};
37+
break;
38+
}
39+
}
40+
setNodes([...nodes], false);
41+
}, 200);
42+
43+
return (
44+
<div
45+
className="node-note-wrap"
46+
onMouseDown={e => e.stopPropagation()}
47+
onClick={e => e.stopPropagation()}
48+
>
49+
<BraftEditor
50+
ref={editorRef}
51+
value={editorState}
52+
onChange={handleEditorChange}
53+
placeholder="请输入内容..."
54+
className="nodrag nopan nowheel node-note-braft"
55+
language="zh"
56+
style={{
57+
width: '240px',
58+
height: '160px',
59+
backgroundColor: 'rgb(239, 248, 255)',
60+
border: '1px solid rgb(132, 202, 255)',
61+
borderRadius: '8px',
62+
cursor: 'text',
63+
userSelect: 'text',
64+
}}
65+
controls={[
66+
// 'headings',
67+
// 'font-size',
68+
'bold',
69+
'italic',
70+
'underline',
71+
'text-color',
72+
// 'link',
73+
// 'media',
74+
'strike-through',
75+
'list-ol',
76+
]}
77+
contentStyle={{ height: '100px', overflowY: 'auto' }}
78+
contentClassName="node-note-braft-content"
79+
controlBarClassName="node-note-braft-control"
80+
/>
81+
</div>
82+
);
83+
});

packages/x-flow/src/operator/Control/index.tsx

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,17 @@ const Control = (props: any) => {
1818
const hideAddNode = globalConfig?.controls?.hideAddNode ?? false;
1919
const hideAnnotate = globalConfig?.controls?.hideAnnotate ?? false;
2020

21+
const { setIsAddingNode, panOnDrag } = useStore(s => ({
22+
setIsAddingNode: s.setIsAddingNode,
23+
panOnDrag: s.panOnDrag,
24+
}));
25+
2126
const addNote = (e: MouseEvent<HTMLDivElement>) => {
2227
e.stopPropagation();
28+
setIsAddingNode(true);
29+
addNode({ _nodeType: 'Note' });
2330
};
2431
const storeApi = useStoreApi();
25-
const panOnDrag = useStore(s => s.panOnDrag);
2632

2733
const { eventEmitter } = useEventEmitterContextContext();
2834

@@ -31,7 +37,7 @@ const Control = (props: any) => {
3137
};
3238

3339
return (
34-
<div className='fai-reactflow-control'>
40+
<div className="fai-reactflow-control">
3541
{!hideAddNode && (
3642
<NodeSelectPopover addNode={addNode}>
3743
<Tooltip
@@ -63,14 +69,19 @@ const Control = (props: any) => {
6369
/>
6470
</Tooltip>
6571
)}
66-
{!(hideAddNode && hideAnnotate) && <div className='separator'></div>}
67-
<Tooltip title='指针模式' getPopupContainer={() => document.getElementById('xflow-container') as HTMLElement}>
72+
{!(hideAddNode && hideAnnotate) && <div className="separator"></div>}
73+
<Tooltip
74+
title="指针模式"
75+
getPopupContainer={() =>
76+
document.getElementById('xflow-container') as HTMLElement
77+
}
78+
>
6879
<Button
69-
type='text'
80+
type="text"
7081
icon={
7182
<IconView
72-
type='icon-zhizhen'
73-
className='icon'
83+
type="icon-zhizhen"
84+
className="icon"
7485
style={{
7586
color: !panOnDrag ? 'rgb(21,94,239)' : '#666F83',
7687
fontSize: '14px',
@@ -81,13 +92,18 @@ const Control = (props: any) => {
8192
style={{ backgroundColor: !panOnDrag ? 'rgb(239,244,255)' : '' }}
8293
/>
8394
</Tooltip>
84-
<Tooltip title='手模式' getPopupContainer={() => document.getElementById('xflow-container') as HTMLElement}>
95+
<Tooltip
96+
title="手模式"
97+
getPopupContainer={() =>
98+
document.getElementById('xflow-container') as HTMLElement
99+
}
100+
>
85101
<Button
86-
type='text'
102+
type="text"
87103
icon={
88104
<IconView
89-
type='icon-xianxingshouzhangtubiao'
90-
className='icon'
105+
type="icon-xianxingshouzhangtubiao"
106+
className="icon"
91107
style={{
92108
color: panOnDrag ? 'rgb(21,94,239)' : '#666F83',
93109
}}
@@ -100,20 +116,36 @@ const Control = (props: any) => {
100116
}}
101117
/>
102118
</Tooltip>
103-
<div className='separator'></div>
104-
<Tooltip title='整理画布' getPopupContainer={() => document.getElementById('xflow-container') as HTMLElement}>
119+
<div className="separator"></div>
120+
<Tooltip
121+
title="整理画布"
122+
getPopupContainer={() =>
123+
document.getElementById('xflow-container') as HTMLElement
124+
}
125+
>
105126
<Button
106-
type='text'
107-
icon={<IconView type='icon-function-add-line1' className='icon' />}
127+
type="text"
128+
icon={<IconView type="icon-function-add-line1" className="icon" />}
108129
onClick={() => {
109130
eventEmitter?.emit({ type: 'auto-layout-nodes' } as any);
110131
}}
111132
/>
112133
</Tooltip>
113-
<Tooltip title='画布全屏' getPopupContainer={() => document.getElementById('xflow-container') as HTMLElement}>
134+
<Tooltip
135+
title="画布全屏"
136+
getPopupContainer={() =>
137+
document.getElementById('xflow-container') as HTMLElement
138+
}
139+
>
114140
<Button
115-
type='text'
116-
icon={<IconView type={isFullscreen ? 'icon-fullscreen-exit' : 'icon-fullscreen'} className='icon' style={{fontSize:'14px'}}/>}
141+
type="text"
142+
icon={
143+
<IconView
144+
type={isFullscreen ? 'icon-fullscreen-exit' : 'icon-fullscreen'}
145+
className="icon"
146+
style={{ fontSize: '14px' }}
147+
/>
148+
}
117149
onClick={toggleFullscreen}
118150
/>
119151
</Tooltip>

0 commit comments

Comments
 (0)