@logicflow/extension 2.0.11 → 2.0.13

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 (63) hide show
  1. package/.turbo/turbo-build.log +34 -34
  2. package/CHANGELOG.md +23 -1
  3. package/dist/index.css +1 -1
  4. package/dist/index.min.js +1 -1
  5. package/dist/index.min.js.map +1 -1
  6. package/es/components/mini-map/index.d.ts +2 -0
  7. package/es/components/mini-map/index.js +10 -0
  8. package/es/dynamic-group/index.d.ts +2 -1
  9. package/es/dynamic-group/index.js +21 -19
  10. package/es/index.css +1 -1
  11. package/es/index.d.ts +1 -0
  12. package/es/index.js +1 -0
  13. package/es/materials/group/GroupNode.d.ts +5 -5
  14. package/es/materials/group/GroupNode.js +3 -3
  15. package/es/materials/group/index.js +5 -5
  16. package/es/materials/node-selection/index.d.ts +6 -6
  17. package/es/materials/node-selection/index.js +90 -10
  18. package/es/style/index.css +1 -1
  19. package/es/style/raw.d.ts +1 -1
  20. package/es/style/raw.js +1 -1
  21. package/es/tools/label/Label.d.ts +4 -0
  22. package/es/tools/label/Label.js +75 -31
  23. package/es/tools/label/LabelModel.js +1 -0
  24. package/es/tools/label/index.d.ts +1 -0
  25. package/es/tools/label/index.js +47 -3
  26. package/es/tools/proximity-connect/index.d.ts +42 -0
  27. package/es/tools/proximity-connect/index.js +337 -0
  28. package/lib/components/mini-map/index.d.ts +2 -0
  29. package/lib/components/mini-map/index.js +10 -0
  30. package/lib/dynamic-group/index.d.ts +2 -1
  31. package/lib/dynamic-group/index.js +21 -19
  32. package/lib/index.css +1 -1
  33. package/lib/index.d.ts +1 -0
  34. package/lib/index.js +1 -0
  35. package/lib/materials/group/GroupNode.d.ts +5 -5
  36. package/lib/materials/group/GroupNode.js +3 -3
  37. package/lib/materials/group/index.js +5 -5
  38. package/lib/materials/node-selection/index.d.ts +6 -6
  39. package/lib/materials/node-selection/index.js +89 -9
  40. package/lib/style/index.css +1 -1
  41. package/lib/style/raw.d.ts +1 -1
  42. package/lib/style/raw.js +1 -1
  43. package/lib/tools/label/Label.d.ts +4 -0
  44. package/lib/tools/label/Label.js +75 -31
  45. package/lib/tools/label/LabelModel.js +1 -0
  46. package/lib/tools/label/index.d.ts +1 -0
  47. package/lib/tools/label/index.js +46 -2
  48. package/lib/tools/proximity-connect/index.d.ts +42 -0
  49. package/lib/tools/proximity-connect/index.js +340 -0
  50. package/package.json +3 -3
  51. package/src/components/mini-map/index.ts +11 -0
  52. package/src/dynamic-group/index.ts +29 -8
  53. package/src/index.ts +1 -0
  54. package/src/materials/group/GroupNode.ts +3 -3
  55. package/src/materials/group/index.ts +5 -5
  56. package/src/materials/node-selection/index.ts +115 -14
  57. package/src/style/index.less +3 -5
  58. package/src/style/raw.ts +3 -7
  59. package/src/tools/label/Label.tsx +106 -55
  60. package/src/tools/label/LabelModel.ts +1 -0
  61. package/src/tools/label/index.ts +64 -3
  62. package/src/tools/proximity-connect/index.ts +399 -0
  63. package/stats.html +1 -1
@@ -195,6 +195,14 @@ export class MiniMap {
195
195
  this.elementAreaBounds = boundsInit
196
196
  this.viewPortBounds = boundsInit
197
197
  this.initMiniMap()
198
+ lf.on('graph:resize', this.onGraphResize)
199
+ }
200
+
201
+ onGraphResize = () => {
202
+ this.updateViewPortBounds()
203
+ if (this.isShow) {
204
+ this.updateViewPort()
205
+ }
198
206
  }
199
207
 
200
208
  render = (_: LogicFlow, container: HTMLElement) => {
@@ -661,6 +669,9 @@ export class MiniMap {
661
669
  },
662
670
  })
663
671
  }
672
+ destroy() {
673
+ this.lf.off('graph:resize', this.onGraphResize)
674
+ }
664
675
  }
665
676
 
666
677
  export default MiniMap
@@ -86,7 +86,9 @@ export class DynamicGroup {
86
86
  } else {
87
87
  let topZIndexGroup = groups[count - 1]
88
88
  for (let i = count - 2; i >= 0; i--) {
89
- topZIndexGroup = groups[i]
89
+ if (groups[i].zIndex > topZIndexGroup.zIndex) {
90
+ topZIndexGroup = groups[i]
91
+ }
90
92
  }
91
93
  return topZIndexGroup as DynamicGroupNodeModel
92
94
  }
@@ -240,8 +242,11 @@ export class DynamicGroup {
240
242
  }
241
243
  }
242
244
 
243
- removeNodeFromGroup = ({ data: node }: CallbackArgs<'node:delete'>) => {
244
- if (node.isGroup && node.children) {
245
+ removeNodeFromGroup = ({
246
+ data: node,
247
+ model,
248
+ }: CallbackArgs<'node:delete'>) => {
249
+ if (model.isGroup && node.children) {
245
250
  forEach(
246
251
  Array.from((node as DynamicGroupNodeModel).children),
247
252
  (childId) => {
@@ -420,6 +425,17 @@ export class DynamicGroup {
420
425
  this.calibrateTopGroupZIndex(data.nodes)
421
426
  }
422
427
 
428
+ removeChildrenInGroupNodeData<
429
+ T extends LogicFlow.NodeData | LogicFlow.NodeConfig,
430
+ >(nodeData: T) {
431
+ const newNodeData = cloneDeep(nodeData)
432
+ delete newNodeData.children
433
+ if (newNodeData.properties?.children) {
434
+ delete newNodeData.properties.children
435
+ }
436
+ return newNodeData
437
+ }
438
+
423
439
  /**
424
440
  * 创建一个 Group 类型节点内部所有子节点的副本
425
441
  * 并且在遍历所有 nodes 的过程中,顺便拿到所有 edges (只在 Group 范围的 edges)
@@ -440,10 +456,14 @@ export class DynamicGroup {
440
456
  forEach(Array.from(children), (childId: string) => {
441
457
  const childNode = this.lf.getNodeModelById(childId)
442
458
  if (childNode) {
459
+ const childNodeChildren = childNode.children
443
460
  const childNodeData = childNode.getData()
444
461
  const eventType = EventType.NODE_GROUP_COPY || 'node:group-copy-add'
445
462
 
446
- const newNodeConfig = transformNodeData(childNodeData, distance)
463
+ const newNodeConfig = transformNodeData(
464
+ this.removeChildrenInGroupNodeData(childNodeData),
465
+ distance,
466
+ )
447
467
  const tempChildNode = this.lf.addNode(newNodeConfig, eventType)
448
468
  curGroup.addChild(tempChildNode.id)
449
469
 
@@ -455,10 +475,10 @@ export class DynamicGroup {
455
475
  ...[...tempChildNode.incoming.edges, ...tempChildNode.outgoing.edges],
456
476
  )
457
477
 
458
- if (children instanceof Set) {
478
+ if (childNodeChildren instanceof Set) {
459
479
  const { childNodes, edgesData } = this.initGroupChildNodes(
460
480
  nodeIdMap,
461
- children,
481
+ childNodeChildren,
462
482
  tempChildNode as DynamicGroupNodeModel,
463
483
  distance,
464
484
  )
@@ -667,8 +687,9 @@ export class DynamicGroup {
667
687
 
668
688
  forEach(selectedNodes, (node) => {
669
689
  const originId = node.id
670
- const { children, ...rest } = node
671
- const model = lf.addNode(rest)
690
+ const children = node.properties?.children ?? node.children
691
+
692
+ const model = lf.addNode(this.removeChildrenInGroupNodeData(node))
672
693
 
673
694
  if (originId) nodeIdMap[originId] = model.id
674
695
  elements.nodes.push(model) // 此时为 group 的 nodeModel
package/src/index.ts CHANGED
@@ -19,6 +19,7 @@ export * from './tools/label'
19
19
  export * from './tools/snapshot'
20
20
  export * from './tools/flow-path'
21
21
  export * from './tools/auto-layout'
22
+ export * from './tools/proximity-connect'
22
23
 
23
24
  // Component -> 流程图中交互组件
24
25
  export * from './components/control'
@@ -278,10 +278,10 @@ export class GroupNodeModel extends RectResizeModel {
278
278
  )
279
279
  }
280
280
 
281
- isAllowMoveTo({ x1, y1, x2, y2 }) {
281
+ isAllowMoveTo({ minX, minY, maxX, maxY }) {
282
282
  return {
283
- x: x1 >= this.x - this.width / 2 && x2 <= this.x + this.width / 2,
284
- y: y1 >= this.y - this.height / 2 && y2 <= this.y + this.height / 2,
283
+ x: minX >= this.x - this.width / 2 && maxX <= this.x + this.width / 2,
284
+ y: minY >= this.y - this.height / 2 && maxY <= this.y + this.height / 2,
285
285
  }
286
286
  }
287
287
 
@@ -45,12 +45,12 @@ export class Group {
45
45
  ) as GroupNodeModel
46
46
  if (groupModel && groupModel.isRestrict) {
47
47
  // 如果移动的节点存在分组中,且这个分组禁止子节点移出去。
48
- const { minX: x1, minY: y1, maxX: x2, maxY: y2 } = model.getBounds()
48
+ const { minX, minY, maxX, maxY } = model.getBounds()
49
49
  return groupModel.isAllowMoveTo({
50
- x1: x1 + deltaX,
51
- y1: y1 + deltaY,
52
- x2: x2 + deltaX,
53
- y2: y2 + deltaY,
50
+ minX: minX + deltaX,
51
+ minY: minY + deltaY,
52
+ maxX: maxX + deltaX,
53
+ maxY: maxY + deltaY,
54
54
  })
55
55
  }
56
56
 
@@ -1,5 +1,11 @@
1
1
  import { get } from 'lodash-es'
2
- import { h, PolygonNode, PolygonNodeModel } from '@logicflow/core'
2
+ import LogicFlow, {
3
+ BaseNodeModel,
4
+ h,
5
+ PolygonNode,
6
+ PolygonNodeModel,
7
+ } from '@logicflow/core'
8
+ import { ResizeControl } from '@logicflow/core/lib/view/Control'
3
9
 
4
10
  export type INodeSelectionProperties = {
5
11
  strokeColor?: string | 'none'
@@ -124,7 +130,7 @@ class NodeSelectionModel extends PolygonNodeModel<INodeSelectionProperties> {
124
130
  * 更新points - 多边形顶点坐标集合
125
131
  * @param points
126
132
  */
127
- updatePoints(points) {
133
+ updatePoints(points: [number, number][]) {
128
134
  this.points = points
129
135
  }
130
136
 
@@ -139,7 +145,7 @@ class NodeSelectionModel extends PolygonNodeModel<INodeSelectionProperties> {
139
145
  /**
140
146
  * 计算新的 points 和 x y
141
147
  */
142
- updatePointsByNodes(nodesIds) {
148
+ updatePointsByNodes(nodesIds: string[]) {
143
149
  const points: [number, number][] = []
144
150
  let minX = Infinity
145
151
  let minY = Infinity
@@ -165,18 +171,74 @@ class NodeSelectionModel extends PolygonNodeModel<INodeSelectionProperties> {
165
171
  y: (maxY + minY) / 2,
166
172
  })
167
173
  }
174
+
175
+ isResize = false
176
+ override resize(
177
+ resizeInfo: ResizeControl.ResizeInfo,
178
+ ): ResizeControl.ResizeNodeData {
179
+ this.isResize = true
180
+ const { width, height } = resizeInfo
181
+ const scale = {
182
+ x: width / this.width,
183
+ y: height / this.height,
184
+ }
185
+ const childIds = (this.properties.node_selection_ids || []).slice()
186
+ const childModels: BaseNodeModel[] = []
187
+ const usedGroupId = new Set<string>()
188
+ while (childIds.length) {
189
+ const id = childIds.shift()!
190
+ const node = this.graphModel.nodesMap[id]?.model
191
+ if (!node) {
192
+ continue
193
+ }
194
+ if (!isNodeSelectionModel(node)) {
195
+ childModels.push(node)
196
+ continue
197
+ }
198
+ // 跳出循环引用
199
+ if (usedGroupId.has(node.id)) {
200
+ continue
201
+ }
202
+ usedGroupId.add(node.id)
203
+ childIds.push(...(node.properties.node_selection_ids || []))
204
+ }
205
+
206
+ const begin = {
207
+ x: this.x - this.width / 2,
208
+ y: this.y - this.height / 2,
209
+ }
210
+
211
+ const res = super.resize(resizeInfo)
212
+
213
+ const end = {
214
+ x: this.x - this.width / 2,
215
+ y: this.y - this.height / 2,
216
+ }
217
+
218
+ childModels.forEach((node) => {
219
+ node.width = node.width * scale.x
220
+ node.height = node.height * scale.y
221
+ const deltaX = (node.x - begin.x) * scale.x + end.x - node.x
222
+ const deltaY = (node.y - begin.y) * scale.y + end.y - node.y
223
+ node.move(deltaX, deltaY, true)
224
+ })
225
+ this.isResize = false
226
+ return res
227
+ }
168
228
  }
169
229
 
230
+ const NODE_SELECTION_TYPE = 'node-selection'
170
231
  class NodeSelection {
171
232
  static pluginName = 'node-selection'
172
- lf // lf 实例
233
+ lf: LogicFlow // lf 实例
173
234
  selectNodes: any[] = [] // 选择的nodes
174
235
  currentClickNode // 当前点击的节点,选中的节点是无序的
175
236
  d = 10
176
237
 
177
- constructor({ lf }) {
238
+ constructor({ lf }: LogicFlow.IExtensionProps) {
239
+ this.lf = lf
178
240
  lf.register({
179
- type: 'node-selection',
241
+ type: NODE_SELECTION_TYPE,
180
242
  view: NodeSelectionView,
181
243
  model: NodeSelectionModel,
182
244
  })
@@ -199,6 +261,8 @@ class NodeSelection {
199
261
  properties: {
200
262
  node_selection_ids: this.selectNodesIds,
201
263
  },
264
+ x: 0,
265
+ y: 0,
202
266
  })
203
267
  node.updatePointsByNodes(this.selectNodesIds)
204
268
  }
@@ -215,7 +279,7 @@ class NodeSelection {
215
279
 
216
280
  this.lf
217
281
  .getNodeModelById(nodeSelection.id)
218
- .updatePointsByNodes(this.selectNodesIds)
282
+ ?.updatePointsByNodes(this.selectNodesIds)
219
283
  }
220
284
 
221
285
  /**
@@ -228,24 +292,44 @@ class NodeSelection {
228
292
  const oldIds = ids.filter((id) => id !== this.currentClickNode.id)
229
293
  return rawData.nodes.find((node) => {
230
294
  if (node.type === 'node-selection') {
231
- const nodeSelectionIds = get(node, 'properties.node_selection_ids', [])
295
+ const nodeSelectionIds = get(
296
+ node,
297
+ 'properties.node_selection_ids',
298
+ [],
299
+ ) as string[]
232
300
  return oldIds.every((id) => nodeSelectionIds.includes(id))
233
301
  }
234
302
  return false
235
303
  })
236
304
  }
237
305
 
238
- render(lf) {
306
+ protected onNodeChange(lf: LogicFlow, model: BaseNodeModel) {
307
+ const connectedSelections = lf.graphModel.nodes.filter((node) => {
308
+ if (!isNodeSelectionModel(node)) {
309
+ return false
310
+ }
311
+ const childIds: string[] = node.properties.node_selection_ids || []
312
+ return childIds.includes(model.id)
313
+ })
314
+
315
+ Promise.resolve().then(() => {
316
+ connectedSelections.forEach((node) => {
317
+ node.updatePointsByNodes(node.properties.node_selection_ids || [])
318
+ })
319
+ })
320
+ }
321
+
322
+ render(lf: LogicFlow) {
239
323
  this.lf = lf
240
324
 
241
325
  lf.on('node:click', (val) => {
242
- if (!val.e.shiftKey || val.data.type === 'node-selection') return
326
+ if (!val.e.shiftKey || val.data.type === NODE_SELECTION_TYPE) return
243
327
  this.currentClickNode = val.data
244
328
 
245
329
  // 如果selectNodesIds中已存在此节点,则取消选中此节点
246
330
  let hasExists = false
247
331
  if (this.selectNodesIds.includes(val.data.id)) {
248
- this.lf.getNodeModelById(val.data.id).setSelected(false)
332
+ this.lf.getNodeModelById(val.data.id)?.setSelected(false)
249
333
  hasExists = true
250
334
  }
251
335
 
@@ -263,17 +347,34 @@ class NodeSelection {
263
347
  }
264
348
  })
265
349
  lf.graphModel.addNodeMoveRules((model, deltaX, deltaY) => {
266
- if (model.type === 'node-selection') {
267
- // 如果移动的是分组,那么分组的子节点也跟着移动。
268
- const nodeIds = model.properties.node_selection_ids
350
+ this.onNodeChange(lf, model)
351
+ /**
352
+ * 如果移动的是分组,那么分组的子节点也跟着移动。
353
+ * 忽略来自分组resize导致的move
354
+ */
355
+ if (isNodeSelectionModel(model) && !model.isResize) {
356
+ const nodeIds = model.properties.node_selection_ids || []
269
357
  lf.graphModel.moveNodes(nodeIds, deltaX, deltaY, true)
270
358
  return true
271
359
  }
272
360
  return true
273
361
  })
362
+
363
+ lf.graphModel.addNodeResizeRules((model) => {
364
+ if (!isNodeSelectionModel(model)) {
365
+ this.onNodeChange(lf, model)
366
+ }
367
+ return true
368
+ })
274
369
  }
275
370
  }
276
371
 
372
+ const isNodeSelectionModel = (
373
+ node: BaseNodeModel,
374
+ ): node is NodeSelectionModel => {
375
+ return !!(node && (node.type as string) === NODE_SELECTION_TYPE)
376
+ }
377
+
277
378
  export default NodeSelection
278
379
 
279
380
  export { NodeSelection }
@@ -32,13 +32,12 @@
32
32
  }
33
33
 
34
34
  &-editing {
35
- border: 2px solid #275dc5;
36
- outline: none;
35
+ outline: 2px solid #275dc5;
37
36
  cursor: text;
38
37
  }
39
38
 
40
39
  &-hover {
41
- border: 2px dashed #acacac;
40
+ outline: 2px dashed #acacac;
42
41
  }
43
42
 
44
43
  // textOverflowMode
@@ -57,13 +56,11 @@
57
56
  }
58
57
 
59
58
  &-wrap {
60
- width: 100px; /* 根据需要调整宽度 */
61
59
  white-space: normal;
62
60
  overflow-wrap: break-word; /* 允许单词内换行 */
63
61
  }
64
62
 
65
63
  &-nowrap {
66
- width: 100px; /* 根据需要调整宽度 */
67
64
  overflow: visible;
68
65
  white-space: nowrap;
69
66
  }
@@ -179,6 +176,7 @@
179
176
  /* dndpanel */
180
177
  .lf-dndpanel {
181
178
  position: absolute;
179
+ z-index: 999;
182
180
  margin: 5px;
183
181
  padding: 15px 5px;
184
182
  background: rgb(255 255 255 / 80%);
package/src/style/raw.ts CHANGED
@@ -33,12 +33,11 @@ export const content = `@import url('medium-editor/dist/css/medium-editor.min.cs
33
33
  cursor: move;
34
34
  }
35
35
  .lf-label-overlay .lf-label-editor-editing {
36
- border: 2px solid #275dc5;
37
- outline: none;
36
+ outline: 2px solid #275dc5;
38
37
  cursor: text;
39
38
  }
40
39
  .lf-label-overlay .lf-label-editor-hover {
41
- border: 2px dashed #acacac;
40
+ outline: 2px dashed #acacac;
42
41
  }
43
42
  .lf-label-overlay .lf-label-editor-clip {
44
43
  width: 100px;
@@ -55,15 +54,11 @@ export const content = `@import url('medium-editor/dist/css/medium-editor.min.cs
55
54
  text-overflow: ellipsis;
56
55
  }
57
56
  .lf-label-overlay .lf-label-editor-wrap {
58
- width: 100px;
59
- /* 根据需要调整宽度 */
60
57
  white-space: normal;
61
58
  overflow-wrap: break-word;
62
59
  /* 允许单词内换行 */
63
60
  }
64
61
  .lf-label-overlay .lf-label-editor-nowrap {
65
- width: 100px;
66
- /* 根据需要调整宽度 */
67
62
  overflow: visible;
68
63
  white-space: nowrap;
69
64
  }
@@ -159,6 +154,7 @@ export const content = `@import url('medium-editor/dist/css/medium-editor.min.cs
159
154
  /* dndpanel */
160
155
  .lf-dndpanel {
161
156
  position: absolute;
157
+ z-index: 999;
162
158
  margin: 5px;
163
159
  padding: 15px 5px;
164
160
  background: rgba(255, 255, 255, 0.8);
@@ -25,6 +25,7 @@ export interface ILabelState {
25
25
  isEditing: boolean
26
26
  isHovered: boolean
27
27
  isDragging: boolean
28
+ isSelected: boolean
28
29
  }
29
30
 
30
31
  @observer
@@ -52,6 +53,7 @@ export class Label extends Component<ILabelProps, ILabelState> {
52
53
  isEditing: false,
53
54
  isHovered: false,
54
55
  isDragging: false,
56
+ isSelected: false,
55
57
  }
56
58
  }
57
59
 
@@ -75,35 +77,34 @@ export class Label extends Component<ILabelProps, ILabelState> {
75
77
  const {
76
78
  editConfigModel: { nodeTextDraggable },
77
79
  } = graphModel
78
-
79
- // 当 label 允许拖拽 且不处于拖拽状态时, StepDrag 开启拖拽
80
- if ((label.draggable ?? nodeTextDraggable) && !this.state.isDragging) {
81
- this.setState({ isDragging: true })
80
+ // 当 label 允许拖拽 且不处于拖拽状态、不处于编辑状态时, StepDrag 开启拖拽
81
+ if (
82
+ (label.draggable ?? nodeTextDraggable) &&
83
+ !this.state.isDragging &&
84
+ !this.state.isEditing
85
+ ) {
82
86
  this.stepDrag.handleMouseDown(e)
83
87
  }
84
88
  }
89
+ handleMouseUp = (e: MouseEvent) => {
90
+ if (this.state.isDragging) {
91
+ this.stepDrag.handleMouseUp(e)
92
+ }
93
+ }
85
94
  handleDragging = ({ deltaX, deltaY }: IDragParams) => {
86
- const { label, element, graphModel } = this.props
95
+ if (!this.state.isDragging) {
96
+ this.setState({ isDragging: true })
97
+ }
98
+ const { label, graphModel } = this.props
87
99
 
88
100
  // DONE: 添加缩放时拖拽的逻辑,对 deltaX 和 deltaY 进行按比例缩放
89
101
  const { transformModel } = graphModel
90
102
  const [curDeltaX, curDeltaY] = transformModel.fixDeltaXY(deltaX, deltaY)
91
103
 
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)
104
+ this.setElementModelLabelInfo({
105
+ x: label.x + curDeltaX,
106
+ y: label.y + curDeltaY,
107
+ })
107
108
 
108
109
  graphModel.eventCenter.emit('label:drag', {
109
110
  data: label.getData(),
@@ -114,6 +115,21 @@ export class Label extends Component<ILabelProps, ILabelState> {
114
115
  this.setState({ isDragging: false })
115
116
  }
116
117
 
118
+ handleClick = (e: MouseEvent) => {
119
+ const { label, element, graphModel } = this.props
120
+ // 更新当前Label选中状态
121
+ element.setSelected(!this.state.isSelected)
122
+ this.setState({ isSelected: !this.state.isSelected })
123
+ this.setElementModelLabelInfo({
124
+ isSelected: true,
125
+ })
126
+ graphModel.eventCenter.emit('label:click', {
127
+ data: label.getData(),
128
+ e,
129
+ model: element,
130
+ })
131
+ }
132
+
117
133
  handleDbClick = (e: MouseEvent) => {
118
134
  const { label, element, graphModel } = this.props
119
135
  graphModel.eventCenter.emit('label:dblclick', {
@@ -162,9 +178,28 @@ export class Label extends Component<ILabelProps, ILabelState> {
162
178
  this.setState({
163
179
  isDragging: false,
164
180
  isHovered: false,
181
+ isSelected: false,
165
182
  })
166
183
  }
167
184
 
185
+ setElementModelLabelInfo(data) {
186
+ const { label, element, graphModel } = this.props
187
+ const {
188
+ properties: { _label },
189
+ } = element
190
+ const elementLabel = _label as LabelConfig[]
191
+ const idx = findIndex(elementLabel, (cur) => cur.id === label.id)
192
+
193
+ const target = elementLabel[idx]
194
+ elementLabel[idx] = {
195
+ ...target,
196
+ ...data,
197
+ }
198
+
199
+ const targetElem = graphModel.getElement(element.id)
200
+ targetElem?.setProperty('_label', elementLabel)
201
+ }
202
+
168
203
  // 重新计算 Label 大小
169
204
  reCalcLabelSize = () => {}
170
205
 
@@ -175,37 +210,49 @@ export class Label extends Component<ILabelProps, ILabelState> {
175
210
  const { label, element, graphModel } = this.props
176
211
 
177
212
  // 在点击元素、边或者画布 时,结束 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,
213
+ graphModel.eventCenter.on(
214
+ 'blank:click,node:click,edge:click,label:click',
215
+ ({ data }) => {
216
+ // 点击的不是label 、点击的不是当前label、点击的是当前label,且当前 label 处于选中态
217
+ // 则取消选中态
218
+ if (
219
+ data?.type !== 'label' ||
220
+ (data.type === 'label' && data.id !== label.id) ||
221
+ this.state.isSelected
222
+ ) {
223
+ this.setState({ isSelected: false })
197
224
  }
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
-
225
+ // 点击的不是label 、点击的不是当前label、点击的是当前label,且当前 label 处于编辑态
226
+ // 则结束编辑态
227
+ if (
228
+ (data?.type !== 'label' ||
229
+ (data.type == 'label' && data.id !== label.id)) &&
230
+ this.state.isEditing
231
+ ) {
232
+ this.setState({ isEditing: false })
233
+
234
+ const value = this.textRef.current?.innerText ?? ''
235
+ const content = this.textRef.current?.innerHTML ?? ''
236
+
237
+ this.setElementModelLabelInfo({
238
+ value,
239
+ content,
240
+ isSelected: false,
241
+ })
242
+
243
+ element.setElementState(ElementState.DEFAULT)
244
+ }
245
+ // 点击的不是label 、点击的不是当前label、点击的是当前label,且当前 label 的文本DOM存在
246
+ // 则结束文本DOM的编辑态
247
+ if (
248
+ (data?.type !== 'label' ||
249
+ (data.type == 'label' && data.id !== label.id)) &&
250
+ this.textRef.current
251
+ ) {
252
+ this.textRef.current.contentEditable = 'false'
253
+ }
254
+ },
255
+ )
209
256
  // TODO: 节点拖拽结束后,更新 Label 的位置
210
257
  // eventCenter.on('node:drag', () => {})
211
258
  // eventCenter.on('node:drop', () => {})
@@ -216,7 +263,7 @@ export class Label extends Component<ILabelProps, ILabelState> {
216
263
 
217
264
  componentDidUpdate() {
218
265
  // snapshot: any, // previousState: Readonly<ILabelState>, // previousProps: Readonly<ILabelProps>,
219
- console.log('Label componentDidUpdate')
266
+ // console.log('Label componentDidUpdate')
220
267
  // console.log('previousProps', previousProps)
221
268
  // console.log('previousState', previousState)
222
269
  // console.log('snapshot', snapshot)
@@ -232,7 +279,7 @@ export class Label extends Component<ILabelProps, ILabelState> {
232
279
 
233
280
  render() {
234
281
  const { label, element, graphModel } = this.props
235
- const { isDragging, isHovered, isEditing } = this.state
282
+ const { isDragging, isHovered, isSelected, isEditing } = this.state
236
283
  const { transformModel } = graphModel
237
284
  const { transform } = transformModel.getTransformStyle()
238
285
  const {
@@ -260,13 +307,14 @@ export class Label extends Component<ILabelProps, ILabelState> {
260
307
  ? `${transform} rotate(${rotate}deg)`
261
308
  : `${transform} rotate(${vertical ? -0.25 : 0}turn)`,
262
309
  }
263
-
264
310
  return (
265
311
  <div
266
312
  id={`element-container-${id}`}
267
313
  className={classNames('lf-label-editor-container')}
268
314
  style={containerStyle}
269
315
  onMouseDown={this.handleMouseDown}
316
+ onMouseUp={this.handleMouseUp}
317
+ onClick={this.handleClick}
270
318
  onDblClick={this.handleDbClick}
271
319
  onBlur={this.handleBlur}
272
320
  onMouseEnter={this.setHoverOn}
@@ -279,12 +327,15 @@ export class Label extends Component<ILabelProps, ILabelState> {
279
327
  className={classNames('lf-label-editor', {
280
328
  'lf-label-editor-dragging': isDragging,
281
329
  'lf-label-editor-editing': isEditing,
282
- 'lf-label-editor-hover': !isEditing && isHovered,
330
+ 'lf-label-editor-hover': !isEditing && (isHovered || isSelected),
283
331
  [`lf-label-editor-${textOverflowMode}`]: !isEditing,
284
332
  })}
285
333
  style={{
286
334
  maxWidth: `${maxLabelWidth}px`,
287
- width: `${maxLabelWidth}px`,
335
+ boxSizing: 'border-box',
336
+ display: 'inline-block',
337
+ background:
338
+ isEditing || element.BaseType === 'edge' ? '#fff' : 'transparent',
288
339
  ...style,
289
340
  }}
290
341
  dangerouslySetInnerHTML={{ __html: content }}