@logicflow/extension 2.0.0-beta.5 → 2.0.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +750 -34
- package/dist/index.css +63 -0
- package/dist/index.min.js +32 -2
- package/es/NodeResize/control/Control.js +3 -2
- package/es/NodeResize/control/Control.js.map +1 -1
- package/es/bpmn-elements/presets/Pool/Pool.d.ts +21 -1
- package/es/components/mini-map/index.js +2 -2
- package/es/components/mini-map/index.js.map +1 -1
- package/es/components/selection-select/index.js.map +1 -1
- package/es/index.css +63 -0
- package/es/index.d.ts +1 -0
- package/es/index.js +1 -0
- package/es/index.js.map +1 -1
- package/es/materials/group/GroupNode.d.ts +6 -6
- package/es/materials/group/GroupNode.js +7 -6
- package/es/materials/group/GroupNode.js.map +1 -1
- package/es/materials/group/index.js +20 -25
- package/es/materials/group/index.js.map +1 -1
- package/es/style/index.css +63 -0
- package/es/style/index.less +73 -0
- package/es/style/raw.d.ts +1 -1
- package/es/style/raw.js +1 -1
- package/es/style/raw.js.map +1 -1
- package/es/tools/flow-path/index.js +0 -1
- package/es/tools/flow-path/index.js.map +1 -1
- package/es/tools/label/Label.d.ts +30 -0
- package/es/tools/label/Label.js +241 -0
- package/es/tools/label/Label.js.map +1 -0
- package/es/tools/label/LabelModel.d.ts +26 -0
- package/es/tools/label/LabelModel.js +86 -0
- package/es/tools/label/LabelModel.js.map +1 -0
- package/es/tools/label/LabelOverlay.d.ts +28 -0
- package/es/tools/label/LabelOverlay.js +161 -0
- package/es/tools/label/LabelOverlay.js.map +1 -0
- package/es/tools/label/algorithm.d.ts +16 -0
- package/es/tools/label/algorithm.js +27 -0
- package/es/tools/label/algorithm.js.map +1 -0
- package/es/tools/label/index.d.ts +59 -0
- package/es/tools/label/index.js +292 -0
- package/es/tools/label/index.js.map +1 -0
- package/es/tools/label/mediumEditor.d.ts +16 -0
- package/es/tools/label/mediumEditor.js +91 -0
- package/es/tools/label/mediumEditor.js.map +1 -0
- package/es/tools/label/utils.d.ts +64 -0
- package/es/tools/label/utils.js +336 -0
- package/es/tools/label/utils.js.map +1 -0
- package/es/tools/snapshot/index.js +0 -2
- package/es/tools/snapshot/index.js.map +1 -1
- package/lib/NodeResize/control/Control.js +3 -2
- package/lib/NodeResize/control/Control.js.map +1 -1
- package/lib/bpmn-elements/presets/Pool/Pool.d.ts +21 -1
- package/lib/components/mini-map/index.js +2 -2
- package/lib/components/mini-map/index.js.map +1 -1
- package/lib/components/selection-select/index.js.map +1 -1
- package/lib/index.css +63 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/materials/group/GroupNode.d.ts +6 -6
- package/lib/materials/group/GroupNode.js +7 -6
- package/lib/materials/group/GroupNode.js.map +1 -1
- package/lib/materials/group/index.js +19 -24
- package/lib/materials/group/index.js.map +1 -1
- package/lib/style/index.css +63 -0
- package/lib/style/index.less +73 -0
- package/lib/style/raw.d.ts +1 -1
- package/lib/style/raw.js +1 -1
- package/lib/style/raw.js.map +1 -1
- package/lib/tools/flow-path/index.js +0 -1
- package/lib/tools/flow-path/index.js.map +1 -1
- package/lib/tools/label/Label.d.ts +30 -0
- package/lib/tools/label/Label.js +247 -0
- package/lib/tools/label/Label.js.map +1 -0
- package/lib/tools/label/LabelModel.d.ts +26 -0
- package/lib/tools/label/LabelModel.js +89 -0
- package/lib/tools/label/LabelModel.js.map +1 -0
- package/lib/tools/label/LabelOverlay.d.ts +28 -0
- package/lib/tools/label/LabelOverlay.js +167 -0
- package/lib/tools/label/LabelOverlay.js.map +1 -0
- package/lib/tools/label/algorithm.d.ts +16 -0
- package/lib/tools/label/algorithm.js +32 -0
- package/lib/tools/label/algorithm.js.map +1 -0
- package/lib/tools/label/index.d.ts +59 -0
- package/lib/tools/label/index.js +298 -0
- package/lib/tools/label/index.js.map +1 -0
- package/lib/tools/label/mediumEditor.d.ts +16 -0
- package/lib/tools/label/mediumEditor.js +97 -0
- package/lib/tools/label/mediumEditor.js.map +1 -0
- package/lib/tools/label/utils.d.ts +64 -0
- package/lib/tools/label/utils.js +349 -0
- package/lib/tools/label/utils.js.map +1 -0
- package/lib/tools/snapshot/index.js +0 -2
- package/lib/tools/snapshot/index.js.map +1 -1
- package/package.json +9 -9
- package/src/NodeResize/control/Control.tsx +3 -2
- package/src/components/mini-map/index.ts +2 -2
- package/src/components/selection-select/index.ts +5 -1
- package/src/index.ts +1 -0
- package/src/materials/group/GroupNode.ts +11 -8
- package/src/materials/group/index.ts +33 -38
- package/src/style/index.less +73 -0
- package/src/style/raw.ts +64 -1
- package/src/tools/flow-path/index.ts +0 -1
- package/src/tools/label/Label.tsx +297 -0
- package/src/tools/label/LabelModel.ts +82 -0
- package/src/tools/label/LabelOverlay.tsx +159 -0
- package/src/tools/label/algorithm.ts +42 -0
- package/src/tools/label/index.ts +401 -0
- package/src/tools/label/mediumEditor.ts +93 -0
- package/src/tools/label/utils.ts +395 -0
- package/src/tools/snapshot/README.md +27 -16
- package/src/tools/snapshot/index.ts +0 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { forEach } from 'lodash-es'
|
|
1
|
+
import { forEach, isEmpty, isObject } from 'lodash-es'
|
|
2
2
|
import LogicFlow, {
|
|
3
3
|
BaseEdgeModel,
|
|
4
4
|
BaseNodeModel,
|
|
@@ -12,6 +12,7 @@ import EdgeConfig = LogicFlow.EdgeConfig
|
|
|
12
12
|
import NodeData = LogicFlow.NodeData
|
|
13
13
|
import Point = LogicFlow.Point
|
|
14
14
|
import BoxBoundsPoint = Model.BoxBoundsPoint
|
|
15
|
+
import NodeConfig = LogicFlow.NodeConfig
|
|
15
16
|
|
|
16
17
|
const DEFAULT_TOP_Z_INDEX = -1000
|
|
17
18
|
const DEFAULT_BOTTOM_Z_INDEX = -10000
|
|
@@ -40,7 +41,7 @@ export class Group {
|
|
|
40
41
|
) as GroupNodeModel
|
|
41
42
|
if (groupModel && groupModel.isRestrict) {
|
|
42
43
|
// 如果移动的节点存在分组中,且这个分组禁止子节点移出去。
|
|
43
|
-
const { x1, y1, x2, y2 } = model.getBounds()
|
|
44
|
+
const { minX: x1, minY: y1, maxX: x2, maxY: y2 } = model.getBounds()
|
|
44
45
|
return groupModel.isAllowMoveTo({
|
|
45
46
|
x1: x1 + deltaX,
|
|
46
47
|
y1: y1 + deltaY,
|
|
@@ -104,18 +105,18 @@ export class Group {
|
|
|
104
105
|
this.createEdgeModel(edge, nodeIdMap, distance)
|
|
105
106
|
})
|
|
106
107
|
// 构建的时候直接偏移,这里不需要再进行再度偏移
|
|
107
|
-
// groupInnerChildren.nodes.forEach(node => this.
|
|
108
|
-
// groupInnerChildren.edges.forEach(edge => this.
|
|
108
|
+
// groupInnerChildren.nodes.forEach(node => this.translateNodeData(node, distance));
|
|
109
|
+
// groupInnerChildren.edges.forEach(edge => this. translateEdgeData(edge, distance));
|
|
109
110
|
|
|
110
111
|
// 最外层的edges继续执行创建edgeModel的流程
|
|
111
|
-
// 由于最外层会调用
|
|
112
|
+
// 由于最外层会调用 translateEdgeData(),因此这里不用传入distance进行偏移
|
|
112
113
|
forEach(selectedEdges, (edge) => {
|
|
113
114
|
const edgeModel = this.createEdgeModel(edge, nodeIdMap, 0)
|
|
114
115
|
elements.edges.push(edgeModel)
|
|
115
116
|
})
|
|
116
117
|
|
|
117
118
|
// 返回elements进行选中效果,即触发element.selectElementById()
|
|
118
|
-
// shortcut.ts也会对最外层的nodes和edges进行偏移,即
|
|
119
|
+
// shortcut.ts也会对最外层的nodes和edges进行偏移,即translateNodeData()
|
|
119
120
|
return elements
|
|
120
121
|
}
|
|
121
122
|
}
|
|
@@ -142,32 +143,25 @@ export class Group {
|
|
|
142
143
|
y,
|
|
143
144
|
properties,
|
|
144
145
|
type,
|
|
145
|
-
text,
|
|
146
146
|
rotate,
|
|
147
147
|
children,
|
|
148
148
|
// incoming,
|
|
149
149
|
// outgoing,
|
|
150
150
|
} = childNodeModel
|
|
151
|
+
const nodeConfig: NodeConfig = {
|
|
152
|
+
x: x + distance,
|
|
153
|
+
y: y + distance,
|
|
154
|
+
properties,
|
|
155
|
+
type,
|
|
156
|
+
rotate,
|
|
157
|
+
// 如果不传递type,会自动触发NODE_ADD
|
|
158
|
+
// 有概率触发appendToGroup
|
|
159
|
+
}
|
|
151
160
|
|
|
152
161
|
const eventType =
|
|
153
162
|
EventType.NODE_GROUP_COPY || ('node:group-copy-add' as EventType)
|
|
154
|
-
const newChildModel = lf.addNode(
|
|
155
|
-
|
|
156
|
-
x: x + distance,
|
|
157
|
-
y: y + distance,
|
|
158
|
-
properties,
|
|
159
|
-
type,
|
|
160
|
-
text: {
|
|
161
|
-
...text,
|
|
162
|
-
x: text.x + distance,
|
|
163
|
-
y: text.y + distance,
|
|
164
|
-
},
|
|
165
|
-
rotate,
|
|
166
|
-
// 如果不传递type,会自动触发NODE_ADD
|
|
167
|
-
// 有概率触发appendToGroup
|
|
168
|
-
},
|
|
169
|
-
eventType,
|
|
170
|
-
)
|
|
163
|
+
const newChildModel = lf.addNode(nodeConfig, eventType)
|
|
164
|
+
|
|
171
165
|
;(current as GroupNodeModel).addChild(newChildModel.id)
|
|
172
166
|
nodeIdMap[childId] = newChildModel.id
|
|
173
167
|
nodesArray.push(newChildModel)
|
|
@@ -220,7 +214,7 @@ export class Group {
|
|
|
220
214
|
if (nodeIdMap[sourceId]) sourceId = nodeIdMap[sourceId]
|
|
221
215
|
if (nodeIdMap[targetId]) targetId = nodeIdMap[targetId]
|
|
222
216
|
const { type, startPoint, endPoint, pointsList, text } = edge
|
|
223
|
-
// ====== 仿造shortcut.ts的
|
|
217
|
+
// ====== 仿造shortcut.ts的 translateEdgeData()逻辑 ======
|
|
224
218
|
const newStartPoint = {
|
|
225
219
|
x: (startPoint?.x || 0) + distance,
|
|
226
220
|
y: (startPoint?.y || 0) + distance,
|
|
@@ -237,25 +231,26 @@ export class Group {
|
|
|
237
231
|
return point
|
|
238
232
|
})
|
|
239
233
|
}
|
|
240
|
-
const
|
|
241
|
-
if (text && typeof text !== 'string') {
|
|
242
|
-
;(newText as { x: number; y: number; value: string }).x =
|
|
243
|
-
text.x + distance
|
|
244
|
-
;(newText as { x: number; y: number; value: string }).y =
|
|
245
|
-
text.y + distance
|
|
246
|
-
}
|
|
247
|
-
// ====== 仿造shortcut.ts的translationEdgeData()逻辑 ======
|
|
248
|
-
|
|
249
|
-
// 简化复制时的参数传入,防止创建出两个edge属于同个group这种情况
|
|
250
|
-
return lf.graphModel.addEdge({
|
|
234
|
+
const edgeConfig: EdgeConfig = {
|
|
251
235
|
type,
|
|
252
236
|
startPoint: newStartPoint,
|
|
253
237
|
endPoint: newEndPoint,
|
|
254
238
|
sourceNodeId: sourceId,
|
|
255
239
|
targetNodeId: targetId,
|
|
256
240
|
pointsList: newPointsList,
|
|
257
|
-
|
|
258
|
-
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (isObject(text) && !isEmpty(text)) {
|
|
244
|
+
edgeConfig.text = {
|
|
245
|
+
...text,
|
|
246
|
+
x: text?.x + distance,
|
|
247
|
+
y: text?.y + distance,
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// ====== 仿造shortcut.ts的 translateEdgeData()逻辑 ======
|
|
251
|
+
|
|
252
|
+
// 简化复制时的参数传入,防止创建出两个edge属于同个group这种情况
|
|
253
|
+
return lf.graphModel.addEdge(edgeConfig)
|
|
259
254
|
}
|
|
260
255
|
|
|
261
256
|
/**
|
package/src/style/index.less
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
@import url('medium-editor/dist/css/medium-editor.min.css');
|
|
2
|
+
//@import url('medium-editor/dist/css/themes/bootstrap.min.css');
|
|
3
|
+
@import url('medium-editor/dist/css/themes/beagle.min.css');
|
|
4
|
+
@import url('vanilla-picker/dist/vanilla-picker.csp.css');
|
|
5
|
+
|
|
1
6
|
.lf-control {
|
|
2
7
|
position: absolute;
|
|
3
8
|
top: 0;
|
|
@@ -253,3 +258,71 @@
|
|
|
253
258
|
.lf-mindmap_addIcon {
|
|
254
259
|
margin-top: 10px;
|
|
255
260
|
}
|
|
261
|
+
|
|
262
|
+
/* label */
|
|
263
|
+
.lf-label-overlay {
|
|
264
|
+
width: 0;
|
|
265
|
+
height: 0;
|
|
266
|
+
overflow: visible;
|
|
267
|
+
|
|
268
|
+
.lf-label-editor {
|
|
269
|
+
//box-sizing: content-box;
|
|
270
|
+
padding: 4px;
|
|
271
|
+
background: #fff;
|
|
272
|
+
border-radius: 5px;
|
|
273
|
+
|
|
274
|
+
&-container {
|
|
275
|
+
position: absolute;
|
|
276
|
+
display: flex;
|
|
277
|
+
align-items: center;
|
|
278
|
+
justify-content: center;
|
|
279
|
+
overflow: visible;
|
|
280
|
+
text-align: center;
|
|
281
|
+
|
|
282
|
+
p {
|
|
283
|
+
margin: 0;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
&-dragging {
|
|
288
|
+
cursor: move;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
&-editing {
|
|
292
|
+
border: 2px solid #275dc5;
|
|
293
|
+
outline: none;
|
|
294
|
+
cursor: text;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
&-hover {
|
|
298
|
+
border: 2px dashed #acacac;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// textOverflowMode
|
|
302
|
+
&-clip {
|
|
303
|
+
width: 100px; /* 根据需要调整宽度 */
|
|
304
|
+
overflow: hidden;
|
|
305
|
+
white-space: nowrap;
|
|
306
|
+
text-overflow: clip;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
&-ellipsis {
|
|
310
|
+
width: 100px; /* 根据需要调整宽度 */
|
|
311
|
+
overflow: hidden;
|
|
312
|
+
white-space: nowrap;
|
|
313
|
+
text-overflow: ellipsis;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
&-wrap {
|
|
317
|
+
width: 100px; /* 根据需要调整宽度 */
|
|
318
|
+
white-space: normal;
|
|
319
|
+
overflow-wrap: break-word; /* 允许单词内换行 */
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
&-nowrap {
|
|
323
|
+
width: 100px; /* 根据需要调整宽度 */
|
|
324
|
+
overflow: visible;
|
|
325
|
+
white-space: nowrap;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
package/src/style/raw.ts
CHANGED
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
* Auto generated file, do not modify it!
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
export const content =
|
|
7
|
+
export const content = `@import url('medium-editor/dist/css/medium-editor.min.css');
|
|
8
|
+
@import url('medium-editor/dist/css/themes/beagle.min.css');
|
|
9
|
+
@import url('vanilla-picker/dist/vanilla-picker.csp.css');
|
|
10
|
+
.lf-control {
|
|
8
11
|
position: absolute;
|
|
9
12
|
top: 0;
|
|
10
13
|
right: 10px;
|
|
@@ -219,4 +222,64 @@ export const content = `.lf-control {
|
|
|
219
222
|
.lf-mindmap_addIcon {
|
|
220
223
|
margin-top: 10px;
|
|
221
224
|
}
|
|
225
|
+
/* label */
|
|
226
|
+
.lf-label-overlay {
|
|
227
|
+
width: 0;
|
|
228
|
+
height: 0;
|
|
229
|
+
overflow: visible;
|
|
230
|
+
}
|
|
231
|
+
.lf-label-overlay .lf-label-editor {
|
|
232
|
+
padding: 4px;
|
|
233
|
+
background: #fff;
|
|
234
|
+
border-radius: 5px;
|
|
235
|
+
}
|
|
236
|
+
.lf-label-overlay .lf-label-editor-container {
|
|
237
|
+
position: absolute;
|
|
238
|
+
display: flex;
|
|
239
|
+
align-items: center;
|
|
240
|
+
justify-content: center;
|
|
241
|
+
overflow: visible;
|
|
242
|
+
text-align: center;
|
|
243
|
+
}
|
|
244
|
+
.lf-label-overlay .lf-label-editor-container p {
|
|
245
|
+
margin: 0;
|
|
246
|
+
}
|
|
247
|
+
.lf-label-overlay .lf-label-editor-dragging {
|
|
248
|
+
cursor: move;
|
|
249
|
+
}
|
|
250
|
+
.lf-label-overlay .lf-label-editor-editing {
|
|
251
|
+
border: 2px solid #275dc5;
|
|
252
|
+
outline: none;
|
|
253
|
+
cursor: text;
|
|
254
|
+
}
|
|
255
|
+
.lf-label-overlay .lf-label-editor-hover {
|
|
256
|
+
border: 2px dashed #acacac;
|
|
257
|
+
}
|
|
258
|
+
.lf-label-overlay .lf-label-editor-clip {
|
|
259
|
+
width: 100px;
|
|
260
|
+
/* 根据需要调整宽度 */
|
|
261
|
+
overflow: hidden;
|
|
262
|
+
white-space: nowrap;
|
|
263
|
+
text-overflow: clip;
|
|
264
|
+
}
|
|
265
|
+
.lf-label-overlay .lf-label-editor-ellipsis {
|
|
266
|
+
width: 100px;
|
|
267
|
+
/* 根据需要调整宽度 */
|
|
268
|
+
overflow: hidden;
|
|
269
|
+
white-space: nowrap;
|
|
270
|
+
text-overflow: ellipsis;
|
|
271
|
+
}
|
|
272
|
+
.lf-label-overlay .lf-label-editor-wrap {
|
|
273
|
+
width: 100px;
|
|
274
|
+
/* 根据需要调整宽度 */
|
|
275
|
+
white-space: normal;
|
|
276
|
+
overflow-wrap: break-word;
|
|
277
|
+
/* 允许单词内换行 */
|
|
278
|
+
}
|
|
279
|
+
.lf-label-overlay .lf-label-editor-nowrap {
|
|
280
|
+
width: 100px;
|
|
281
|
+
/* 根据需要调整宽度 */
|
|
282
|
+
overflow: visible;
|
|
283
|
+
white-space: nowrap;
|
|
284
|
+
}
|
|
222
285
|
`
|
|
@@ -136,7 +136,6 @@ export class FlowPath {
|
|
|
136
136
|
// 由于循环路径不包括开始,所以存在重复的情况,此处去重。
|
|
137
137
|
const LoopSet = new Set()
|
|
138
138
|
pathElements.forEach((elements) => {
|
|
139
|
-
console.log('elements', elements)
|
|
140
139
|
const routeId = this.getNewId('path')
|
|
141
140
|
const name = this.getNewId('路径')
|
|
142
141
|
const isLoop = this.isLoopPath(elements)
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import LogicFlow, {
|
|
2
|
+
BaseEdgeModel,
|
|
3
|
+
BaseNodeModel,
|
|
4
|
+
Component,
|
|
5
|
+
createRef,
|
|
6
|
+
ElementState,
|
|
7
|
+
GraphModel,
|
|
8
|
+
IDragParams,
|
|
9
|
+
observer,
|
|
10
|
+
StepDrag,
|
|
11
|
+
} from '@logicflow/core'
|
|
12
|
+
import classNames from 'classnames'
|
|
13
|
+
import LabelModel from './LabelModel'
|
|
14
|
+
import { findIndex } from 'lodash-es'
|
|
15
|
+
|
|
16
|
+
import LabelConfig = LogicFlow.LabelConfig
|
|
17
|
+
|
|
18
|
+
export interface ILabelProps {
|
|
19
|
+
label: LabelModel
|
|
20
|
+
element: BaseNodeModel | BaseEdgeModel
|
|
21
|
+
graphModel: GraphModel
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ILabelState {
|
|
25
|
+
isEditing: boolean
|
|
26
|
+
isHovered: boolean
|
|
27
|
+
isDragging: boolean
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@observer
|
|
31
|
+
export class Label extends Component<ILabelProps, ILabelState> {
|
|
32
|
+
textRef = createRef<HTMLDivElement>()
|
|
33
|
+
stepDrag: StepDrag
|
|
34
|
+
|
|
35
|
+
constructor(props: ILabelProps) {
|
|
36
|
+
super(props)
|
|
37
|
+
const {
|
|
38
|
+
label,
|
|
39
|
+
graphModel: { gridSize, eventCenter },
|
|
40
|
+
} = props
|
|
41
|
+
|
|
42
|
+
this.stepDrag = new StepDrag({
|
|
43
|
+
onDragging: this.handleDragging,
|
|
44
|
+
onDragEnd: this.handleDragEnd,
|
|
45
|
+
step: gridSize,
|
|
46
|
+
eventType: 'LABEL',
|
|
47
|
+
model: label,
|
|
48
|
+
eventCenter,
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
this.state = {
|
|
52
|
+
isEditing: false,
|
|
53
|
+
isHovered: false,
|
|
54
|
+
isDragging: false,
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
setHoverOn = () => {
|
|
59
|
+
const { element } = this.props
|
|
60
|
+
if (element.isDragging || this.state.isHovered) return // 当节点或边在拖拽中时,不触发 hover 态
|
|
61
|
+
|
|
62
|
+
this.setState({ isHovered: true })
|
|
63
|
+
element.setHovered(true)
|
|
64
|
+
}
|
|
65
|
+
setHoverOff = () => {
|
|
66
|
+
const { element } = this.props
|
|
67
|
+
if (!this.state.isHovered) return
|
|
68
|
+
|
|
69
|
+
this.setState({ isHovered: false })
|
|
70
|
+
element.setHovered(false)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
handleMouseDown = (e: MouseEvent) => {
|
|
74
|
+
const { label, graphModel } = this.props
|
|
75
|
+
const {
|
|
76
|
+
editConfigModel: { nodeTextDraggable },
|
|
77
|
+
} = graphModel
|
|
78
|
+
|
|
79
|
+
// 当 label 允许拖拽 且不处于拖拽状态时, StepDrag 开启拖拽
|
|
80
|
+
if ((label.draggable ?? nodeTextDraggable) && !this.state.isDragging) {
|
|
81
|
+
this.setState({ isDragging: true })
|
|
82
|
+
this.stepDrag.handleMouseDown(e)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
handleDragging = ({ deltaX, deltaY }: IDragParams) => {
|
|
86
|
+
const { label, element, graphModel } = this.props
|
|
87
|
+
|
|
88
|
+
// DONE: 添加缩放时拖拽的逻辑,对 deltaX 和 deltaY 进行按比例缩放
|
|
89
|
+
const { transformModel } = graphModel
|
|
90
|
+
const [curDeltaX, curDeltaY] = transformModel.fixDeltaXY(deltaX, deltaY)
|
|
91
|
+
|
|
92
|
+
// DONE:更新 label 位置,触发 LABEL:DRAG 事件,并抛出相关的数据
|
|
93
|
+
const {
|
|
94
|
+
properties: { _label },
|
|
95
|
+
} = element
|
|
96
|
+
const elementLabel = _label as LabelConfig[]
|
|
97
|
+
const idx = findIndex(elementLabel, (cur) => cur.id === label.id)
|
|
98
|
+
|
|
99
|
+
const target = elementLabel[idx]
|
|
100
|
+
elementLabel[idx] = {
|
|
101
|
+
...target,
|
|
102
|
+
x: target.x + curDeltaX,
|
|
103
|
+
y: target.y + curDeltaY,
|
|
104
|
+
}
|
|
105
|
+
const targetElem = graphModel.getElement(element.id)
|
|
106
|
+
targetElem?.setProperty('_label', elementLabel)
|
|
107
|
+
|
|
108
|
+
graphModel.eventCenter.emit('label:drag', {
|
|
109
|
+
data: label.getData(),
|
|
110
|
+
model: label,
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
handleDragEnd = () => {
|
|
114
|
+
this.setState({ isDragging: false })
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
handleDbClick = (e: MouseEvent) => {
|
|
118
|
+
const { label, element, graphModel } = this.props
|
|
119
|
+
graphModel.eventCenter.emit('label:dblclick', {
|
|
120
|
+
data: label.getData(),
|
|
121
|
+
e,
|
|
122
|
+
model: element,
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
if (!label.editable) {
|
|
126
|
+
element.setSelected(true)
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
element.setSelected()
|
|
130
|
+
element.setElementState(ElementState.TEXT_EDIT)
|
|
131
|
+
|
|
132
|
+
this.setState({ isEditing: true })
|
|
133
|
+
|
|
134
|
+
// DONE: 触发当前 label 的 focus 事件,设置内容可编辑,且在文本最后添加光标
|
|
135
|
+
if (this.textRef.current) {
|
|
136
|
+
this.textRef.current.contentEditable = 'true'
|
|
137
|
+
this.textRef.current.focus()
|
|
138
|
+
|
|
139
|
+
const range = document.createRange()
|
|
140
|
+
const selection = window.getSelection()
|
|
141
|
+
range.selectNodeContents(this.textRef.current)
|
|
142
|
+
range.collapse(false)
|
|
143
|
+
selection?.removeAllRanges()
|
|
144
|
+
selection?.addRange(range)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
handleBlur = (e: FocusEvent) => {
|
|
148
|
+
const {
|
|
149
|
+
label,
|
|
150
|
+
element,
|
|
151
|
+
graphModel: { eventCenter },
|
|
152
|
+
} = this.props
|
|
153
|
+
|
|
154
|
+
// DONE: 触发 LABEL:BLUR 事件,并抛出相关的事件
|
|
155
|
+
eventCenter.emit('label:blur', {
|
|
156
|
+
e,
|
|
157
|
+
model: element,
|
|
158
|
+
data: label.getData(),
|
|
159
|
+
element: this.textRef.current,
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
this.setState({
|
|
163
|
+
isDragging: false,
|
|
164
|
+
isHovered: false,
|
|
165
|
+
})
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// 重新计算 Label 大小
|
|
169
|
+
reCalcLabelSize = () => {}
|
|
170
|
+
|
|
171
|
+
// TODO:如何处理 Label zIndex 的问题, Label 永远会比节点层级高
|
|
172
|
+
// 当 Label 被元素遮盖时,隐藏它
|
|
173
|
+
|
|
174
|
+
componentDidMount() {
|
|
175
|
+
const { label, element, graphModel } = this.props
|
|
176
|
+
|
|
177
|
+
// 在点击元素、边或者画布 时,结束 Label 的编辑态
|
|
178
|
+
graphModel.eventCenter.on('blank:click,node:click,edge:click', () => {
|
|
179
|
+
// 如果当前 label 处于编辑态,则结束编辑态
|
|
180
|
+
if (this.state.isEditing) {
|
|
181
|
+
this.setState({ isEditing: false })
|
|
182
|
+
|
|
183
|
+
const value = this.textRef.current?.innerText ?? ''
|
|
184
|
+
const content = this.textRef.current?.innerHTML ?? ''
|
|
185
|
+
|
|
186
|
+
const {
|
|
187
|
+
properties: { _label },
|
|
188
|
+
} = element
|
|
189
|
+
const elementLabel = _label as LabelConfig[]
|
|
190
|
+
const idx = findIndex(elementLabel, (cur) => cur.id === label.id)
|
|
191
|
+
|
|
192
|
+
const target = elementLabel[idx]
|
|
193
|
+
elementLabel[idx] = {
|
|
194
|
+
...target,
|
|
195
|
+
value,
|
|
196
|
+
content,
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const targetElem = graphModel.getElement(element.id)
|
|
200
|
+
targetElem?.setProperty('_label', elementLabel)
|
|
201
|
+
|
|
202
|
+
element.setElementState(ElementState.DEFAULT)
|
|
203
|
+
}
|
|
204
|
+
if (this.textRef.current) {
|
|
205
|
+
this.textRef.current.contentEditable = 'false'
|
|
206
|
+
}
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
// TODO: 节点拖拽结束后,更新 Label 的位置
|
|
210
|
+
// eventCenter.on('node:drag', () => {})
|
|
211
|
+
// eventCenter.on('node:drop', () => {})
|
|
212
|
+
// eventCenter.on('node:mousemove', () => {})
|
|
213
|
+
//
|
|
214
|
+
// eventCenter.on('node:properties-change,node:properties-delete', () => {})
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
componentDidUpdate() {
|
|
218
|
+
// snapshot: any, // previousState: Readonly<ILabelState>, // previousProps: Readonly<ILabelProps>,
|
|
219
|
+
console.log('Label componentDidUpdate')
|
|
220
|
+
// console.log('previousProps', previousProps)
|
|
221
|
+
// console.log('previousState', previousState)
|
|
222
|
+
// console.log('snapshot', snapshot)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
componentWillUnmount() {
|
|
226
|
+
const { graphModel } = this.props
|
|
227
|
+
graphModel.eventCenter.off('blank:click,node:click,edge:click')
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// TODO: 当某一个标签更新时,如何避免其它标签的更新
|
|
231
|
+
// shouldComponentUpdate or memo
|
|
232
|
+
|
|
233
|
+
render() {
|
|
234
|
+
const { label, element, graphModel } = this.props
|
|
235
|
+
const { isDragging, isHovered, isEditing } = this.state
|
|
236
|
+
const { transformModel } = graphModel
|
|
237
|
+
const { transform } = transformModel.getTransformStyle()
|
|
238
|
+
const {
|
|
239
|
+
id,
|
|
240
|
+
x,
|
|
241
|
+
y,
|
|
242
|
+
zIndex,
|
|
243
|
+
vertical,
|
|
244
|
+
style,
|
|
245
|
+
rotate,
|
|
246
|
+
content,
|
|
247
|
+
labelWidth,
|
|
248
|
+
textOverflowMode,
|
|
249
|
+
} = label
|
|
250
|
+
|
|
251
|
+
const maxLabelWidth: number =
|
|
252
|
+
labelWidth ?? (element.BaseType === 'node' ? element.width - 20 : 80)
|
|
253
|
+
const containerStyle = {
|
|
254
|
+
left: `${x - maxLabelWidth / 2}px`,
|
|
255
|
+
top: `${y - 10}px`,
|
|
256
|
+
width: `${maxLabelWidth}px`,
|
|
257
|
+
height: '20px',
|
|
258
|
+
zIndex: zIndex ?? 1,
|
|
259
|
+
transform: rotate
|
|
260
|
+
? `${transform} rotate(${rotate}deg)`
|
|
261
|
+
: `${transform} rotate(${vertical ? -0.25 : 0}turn)`,
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return (
|
|
265
|
+
<div
|
|
266
|
+
id={`element-container-${id}`}
|
|
267
|
+
className={classNames('lf-label-editor-container')}
|
|
268
|
+
style={containerStyle}
|
|
269
|
+
onMouseDown={this.handleMouseDown}
|
|
270
|
+
onDblClick={this.handleDbClick}
|
|
271
|
+
onBlur={this.handleBlur}
|
|
272
|
+
onMouseEnter={this.setHoverOn}
|
|
273
|
+
onMouseOver={this.setHoverOn}
|
|
274
|
+
onMouseLeave={this.setHoverOff}
|
|
275
|
+
>
|
|
276
|
+
<div
|
|
277
|
+
ref={this.textRef}
|
|
278
|
+
id={`editor-container-${id}`}
|
|
279
|
+
className={classNames('lf-label-editor', {
|
|
280
|
+
'lf-label-editor-dragging': isDragging,
|
|
281
|
+
'lf-label-editor-editing': isEditing,
|
|
282
|
+
'lf-label-editor-hover': !isEditing && isHovered,
|
|
283
|
+
[`lf-label-editor-${textOverflowMode}`]: !isEditing,
|
|
284
|
+
})}
|
|
285
|
+
style={{
|
|
286
|
+
maxWidth: `${maxLabelWidth}px`,
|
|
287
|
+
width: `${maxLabelWidth}px`,
|
|
288
|
+
...style,
|
|
289
|
+
}}
|
|
290
|
+
dangerouslySetInnerHTML={{ __html: content }}
|
|
291
|
+
></div>
|
|
292
|
+
</div>
|
|
293
|
+
)
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export default Label
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import LogicFlow, {
|
|
2
|
+
BaseEdgeModel,
|
|
3
|
+
BaseNodeModel,
|
|
4
|
+
GraphModel,
|
|
5
|
+
LogicFlowUtil,
|
|
6
|
+
h,
|
|
7
|
+
observable,
|
|
8
|
+
toJS,
|
|
9
|
+
} from '@logicflow/core'
|
|
10
|
+
import { assign } from 'lodash-es'
|
|
11
|
+
|
|
12
|
+
import LabelData = LogicFlow.LabelData
|
|
13
|
+
import LabelConfig = LogicFlow.LabelConfig
|
|
14
|
+
import GraphElement = LogicFlow.GraphElement
|
|
15
|
+
|
|
16
|
+
// export type ILabelConfig = {}
|
|
17
|
+
const { createUuid } = LogicFlowUtil
|
|
18
|
+
|
|
19
|
+
export class LabelModel {
|
|
20
|
+
id: string
|
|
21
|
+
type: string = 'label' // 目前写死,后续可以根据业务需求进行扩展
|
|
22
|
+
|
|
23
|
+
@observable x!: number
|
|
24
|
+
@observable y!: number
|
|
25
|
+
@observable content: string = ''
|
|
26
|
+
@observable value: string = ''
|
|
27
|
+
@observable rotate?: number
|
|
28
|
+
@observable style: h.JSX.CSSProperties = {}
|
|
29
|
+
|
|
30
|
+
@observable zIndex?: number
|
|
31
|
+
@observable vertical: boolean = false // 文字是否垂直显示
|
|
32
|
+
@observable editable: boolean = true // label 是否可编辑
|
|
33
|
+
@observable draggable: boolean = true // label 是否可拖拽
|
|
34
|
+
@observable labelWidth?: number
|
|
35
|
+
@observable textOverflowMode:
|
|
36
|
+
| 'ellipsis'
|
|
37
|
+
| 'wrap'
|
|
38
|
+
| 'clip'
|
|
39
|
+
| 'nowrap'
|
|
40
|
+
| 'default' = 'default' // Label 节点的文本溢出模式
|
|
41
|
+
|
|
42
|
+
// TODO: 后续看 label 是否可以独立存在,不依赖节点 or 边
|
|
43
|
+
element: BaseNodeModel | BaseEdgeModel // 当前节点关联的元素 Model
|
|
44
|
+
graphModel: GraphModel
|
|
45
|
+
|
|
46
|
+
constructor(
|
|
47
|
+
config: LabelConfig,
|
|
48
|
+
element: GraphElement,
|
|
49
|
+
graphModel: GraphModel,
|
|
50
|
+
) {
|
|
51
|
+
this.element = element
|
|
52
|
+
this.graphModel = graphModel
|
|
53
|
+
this.id = config.id ?? createUuid()
|
|
54
|
+
|
|
55
|
+
this.initLabelData(config)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
initLabelData(config: LabelConfig): void {
|
|
59
|
+
assign(this, config)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
getData(): LabelData {
|
|
63
|
+
return {
|
|
64
|
+
id: this.id,
|
|
65
|
+
x: this.x,
|
|
66
|
+
y: this.y,
|
|
67
|
+
content: this.content,
|
|
68
|
+
value: this.value,
|
|
69
|
+
rotate: this.rotate,
|
|
70
|
+
style: toJS(this.style),
|
|
71
|
+
|
|
72
|
+
draggable: this.draggable,
|
|
73
|
+
editable: this.editable,
|
|
74
|
+
labelWidth: this.labelWidth,
|
|
75
|
+
textOverflowMode: this.textOverflowMode,
|
|
76
|
+
|
|
77
|
+
vertical: this.vertical,
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export default LabelModel
|