@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.
Files changed (112) hide show
  1. package/.turbo/turbo-build.log +750 -34
  2. package/dist/index.css +63 -0
  3. package/dist/index.min.js +32 -2
  4. package/es/NodeResize/control/Control.js +3 -2
  5. package/es/NodeResize/control/Control.js.map +1 -1
  6. package/es/bpmn-elements/presets/Pool/Pool.d.ts +21 -1
  7. package/es/components/mini-map/index.js +2 -2
  8. package/es/components/mini-map/index.js.map +1 -1
  9. package/es/components/selection-select/index.js.map +1 -1
  10. package/es/index.css +63 -0
  11. package/es/index.d.ts +1 -0
  12. package/es/index.js +1 -0
  13. package/es/index.js.map +1 -1
  14. package/es/materials/group/GroupNode.d.ts +6 -6
  15. package/es/materials/group/GroupNode.js +7 -6
  16. package/es/materials/group/GroupNode.js.map +1 -1
  17. package/es/materials/group/index.js +20 -25
  18. package/es/materials/group/index.js.map +1 -1
  19. package/es/style/index.css +63 -0
  20. package/es/style/index.less +73 -0
  21. package/es/style/raw.d.ts +1 -1
  22. package/es/style/raw.js +1 -1
  23. package/es/style/raw.js.map +1 -1
  24. package/es/tools/flow-path/index.js +0 -1
  25. package/es/tools/flow-path/index.js.map +1 -1
  26. package/es/tools/label/Label.d.ts +30 -0
  27. package/es/tools/label/Label.js +241 -0
  28. package/es/tools/label/Label.js.map +1 -0
  29. package/es/tools/label/LabelModel.d.ts +26 -0
  30. package/es/tools/label/LabelModel.js +86 -0
  31. package/es/tools/label/LabelModel.js.map +1 -0
  32. package/es/tools/label/LabelOverlay.d.ts +28 -0
  33. package/es/tools/label/LabelOverlay.js +161 -0
  34. package/es/tools/label/LabelOverlay.js.map +1 -0
  35. package/es/tools/label/algorithm.d.ts +16 -0
  36. package/es/tools/label/algorithm.js +27 -0
  37. package/es/tools/label/algorithm.js.map +1 -0
  38. package/es/tools/label/index.d.ts +59 -0
  39. package/es/tools/label/index.js +292 -0
  40. package/es/tools/label/index.js.map +1 -0
  41. package/es/tools/label/mediumEditor.d.ts +16 -0
  42. package/es/tools/label/mediumEditor.js +91 -0
  43. package/es/tools/label/mediumEditor.js.map +1 -0
  44. package/es/tools/label/utils.d.ts +64 -0
  45. package/es/tools/label/utils.js +336 -0
  46. package/es/tools/label/utils.js.map +1 -0
  47. package/es/tools/snapshot/index.js +0 -2
  48. package/es/tools/snapshot/index.js.map +1 -1
  49. package/lib/NodeResize/control/Control.js +3 -2
  50. package/lib/NodeResize/control/Control.js.map +1 -1
  51. package/lib/bpmn-elements/presets/Pool/Pool.d.ts +21 -1
  52. package/lib/components/mini-map/index.js +2 -2
  53. package/lib/components/mini-map/index.js.map +1 -1
  54. package/lib/components/selection-select/index.js.map +1 -1
  55. package/lib/index.css +63 -0
  56. package/lib/index.d.ts +1 -0
  57. package/lib/index.js +1 -0
  58. package/lib/index.js.map +1 -1
  59. package/lib/materials/group/GroupNode.d.ts +6 -6
  60. package/lib/materials/group/GroupNode.js +7 -6
  61. package/lib/materials/group/GroupNode.js.map +1 -1
  62. package/lib/materials/group/index.js +19 -24
  63. package/lib/materials/group/index.js.map +1 -1
  64. package/lib/style/index.css +63 -0
  65. package/lib/style/index.less +73 -0
  66. package/lib/style/raw.d.ts +1 -1
  67. package/lib/style/raw.js +1 -1
  68. package/lib/style/raw.js.map +1 -1
  69. package/lib/tools/flow-path/index.js +0 -1
  70. package/lib/tools/flow-path/index.js.map +1 -1
  71. package/lib/tools/label/Label.d.ts +30 -0
  72. package/lib/tools/label/Label.js +247 -0
  73. package/lib/tools/label/Label.js.map +1 -0
  74. package/lib/tools/label/LabelModel.d.ts +26 -0
  75. package/lib/tools/label/LabelModel.js +89 -0
  76. package/lib/tools/label/LabelModel.js.map +1 -0
  77. package/lib/tools/label/LabelOverlay.d.ts +28 -0
  78. package/lib/tools/label/LabelOverlay.js +167 -0
  79. package/lib/tools/label/LabelOverlay.js.map +1 -0
  80. package/lib/tools/label/algorithm.d.ts +16 -0
  81. package/lib/tools/label/algorithm.js +32 -0
  82. package/lib/tools/label/algorithm.js.map +1 -0
  83. package/lib/tools/label/index.d.ts +59 -0
  84. package/lib/tools/label/index.js +298 -0
  85. package/lib/tools/label/index.js.map +1 -0
  86. package/lib/tools/label/mediumEditor.d.ts +16 -0
  87. package/lib/tools/label/mediumEditor.js +97 -0
  88. package/lib/tools/label/mediumEditor.js.map +1 -0
  89. package/lib/tools/label/utils.d.ts +64 -0
  90. package/lib/tools/label/utils.js +349 -0
  91. package/lib/tools/label/utils.js.map +1 -0
  92. package/lib/tools/snapshot/index.js +0 -2
  93. package/lib/tools/snapshot/index.js.map +1 -1
  94. package/package.json +9 -9
  95. package/src/NodeResize/control/Control.tsx +3 -2
  96. package/src/components/mini-map/index.ts +2 -2
  97. package/src/components/selection-select/index.ts +5 -1
  98. package/src/index.ts +1 -0
  99. package/src/materials/group/GroupNode.ts +11 -8
  100. package/src/materials/group/index.ts +33 -38
  101. package/src/style/index.less +73 -0
  102. package/src/style/raw.ts +64 -1
  103. package/src/tools/flow-path/index.ts +0 -1
  104. package/src/tools/label/Label.tsx +297 -0
  105. package/src/tools/label/LabelModel.ts +82 -0
  106. package/src/tools/label/LabelOverlay.tsx +159 -0
  107. package/src/tools/label/algorithm.ts +42 -0
  108. package/src/tools/label/index.ts +401 -0
  109. package/src/tools/label/mediumEditor.ts +93 -0
  110. package/src/tools/label/utils.ts +395 -0
  111. package/src/tools/snapshot/README.md +27 -16
  112. 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.translationNodeData(node, distance));
108
- // groupInnerChildren.edges.forEach(edge => this.translationEdgeData(edge, distance));
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
- // 由于最外层会调用translationEdgeData(),因此这里不用传入distance进行偏移
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进行偏移,即translationNodeData()
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的translationEdgeData()逻辑 ======
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 newText = text
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
- text: newText,
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
  /**
@@ -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 = `.lf-control {
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