@logicflow/extension 2.0.15 → 2.0.16

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logicflow/extension",
3
- "version": "2.0.15",
3
+ "version": "2.0.16",
4
4
  "description": "LogicFlow Extensions",
5
5
  "main": "lib/index.js",
6
6
  "module": "es/index.js",
@@ -20,7 +20,7 @@
20
20
  "author": "Logicflow-Team",
21
21
  "license": "Apache-2.0",
22
22
  "peerDependencies": {
23
- "@logicflow/core": "2.0.11"
23
+ "@logicflow/core": "2.0.12"
24
24
  },
25
25
  "dependencies": {
26
26
  "@antv/hierarchy": "^0.6.11",
@@ -31,7 +31,7 @@
31
31
  "preact": "^10.17.1",
32
32
  "rangy": "^1.3.1",
33
33
  "vanilla-picker": "^2.12.3",
34
- "@logicflow/core": "2.0.11"
34
+ "@logicflow/core": "2.0.12"
35
35
  },
36
36
  "devDependencies": {
37
37
  "less": "^4.1.1",
@@ -3,6 +3,7 @@ import LogicFlow from '@logicflow/core'
3
3
  import GraphData = LogicFlow.GraphData
4
4
  import NodeData = LogicFlow.NodeData
5
5
  import EdgeData = LogicFlow.EdgeData
6
+ import Position = LogicFlow.Position
6
7
 
7
8
  type SetType = 'add' | 'reset'
8
9
 
@@ -29,7 +30,8 @@ class Menu {
29
30
  private __container?: HTMLElement
30
31
  private __menuDOM?: HTMLElement
31
32
  private menuTypeMap?: Map<string, MenuItem[]>
32
- private __currentData: EdgeData | NodeData | GraphData | null = null
33
+ private __currentData: EdgeData | NodeData | GraphData | Position | null =
34
+ null
33
35
  static pluginName = 'menu'
34
36
 
35
37
  constructor({ lf }) {
@@ -209,6 +211,7 @@ class Menu {
209
211
  const {
210
212
  domOverlayPosition: { x, y },
211
213
  } = position
214
+ this.__currentData = { ...position.canvasOverlayPosition }
212
215
  this.showMenu(x, y, menuList)
213
216
  })
214
217
  this.lf.on('selection:contextmenu', ({ data, position }) => {
@@ -11,19 +11,17 @@ export class SelectionSelect {
11
11
  private startPoint?: Position
12
12
  private endPoint?: Position
13
13
  private disabled = true
14
- private isDefaultStopMoveGraph:
15
- | boolean
16
- | 'horizontal'
17
- | 'vertical'
18
- | [number, number, number, number] = false
19
14
  private isWholeNode = true
20
15
  private isWholeEdge = true
16
+ // 用于区分选区和点击事件
17
+ private mouseDownInfo: {
18
+ x: number
19
+ y: number
20
+ time: number
21
+ } | null = null
21
22
 
22
23
  constructor({ lf }: LogicFlow.IExtensionProps) {
23
24
  this.lf = lf
24
- // 初始化isDefaultStopMoveGraph取值
25
- const { stopMoveGraph } = lf.getEditConfig()
26
- this.isDefaultStopMoveGraph = stopMoveGraph!
27
25
  // TODO: 有没有既能将方法挂载到lf上,又能提供类型提示的方法?
28
26
  lf.openSelectionSelect = () => {
29
27
  this.openSelectionSelect()
@@ -33,42 +31,71 @@ export class SelectionSelect {
33
31
  }
34
32
  }
35
33
 
36
- render(lf: LogicFlow, domContainer: HTMLElement) {
34
+ render(_: LogicFlow, domContainer: HTMLElement) {
37
35
  this.container = domContainer
38
- lf.on('blank:mousedown', ({ e }: { e: MouseEvent }) => {
39
- const config = lf.getEditConfig()
40
- // 鼠标控制滚动移动画布的时候,不能选区。
41
- if (!config.stopMoveGraph || this.disabled) {
42
- return
43
- }
44
- // 禁用右键框选,修复可能导致画布出现多个框选框不消失的问题,见https://github.com/didi/LogicFlow/issues/985
45
- const isRightClick = e.button === 2
46
- if (isRightClick) {
47
- return
48
- }
49
- const {
50
- domOverlayPosition: { x, y },
51
- } = lf.getPointByClient(e.clientX, e.clientY)
52
- this.startPoint = {
53
- x,
54
- y,
55
- }
56
- this.endPoint = {
57
- x,
58
- y,
59
- }
60
- const wrapper = document.createElement('div')
61
- wrapper.className = 'lf-selection-select'
62
- wrapper.oncontextmenu = function prevent(ev: MouseEvent) {
63
- ev.preventDefault()
36
+ }
37
+
38
+ onToolContainerMouseDown = (e: MouseEvent) => {
39
+ // 避免在其他插件元素上点击时开启选区
40
+ if (e.target !== this.container) {
41
+ return
42
+ }
43
+ this.mouseDownInfo = {
44
+ x: e.clientX,
45
+ y: e.clientY,
46
+ time: Date.now(),
47
+ }
48
+ const lf = this.lf
49
+ const domContainer = this.container
50
+ if (!domContainer) {
51
+ return
52
+ }
53
+ if (this.disabled) {
54
+ return
55
+ }
56
+ // 禁用右键框选,修复可能导致画布出现多个框选框不消失的问题,见https://github.com/didi/LogicFlow/issues/985
57
+ const isRightClick = e.button === 2
58
+ if (isRightClick) {
59
+ return
60
+ }
61
+ const {
62
+ domOverlayPosition: { x, y },
63
+ } = lf.getPointByClient(e.clientX, e.clientY)
64
+ this.startPoint = {
65
+ x,
66
+ y,
67
+ }
68
+ this.endPoint = {
69
+ x,
70
+ y,
71
+ }
72
+ const wrapper = document.createElement('div')
73
+ wrapper.className = 'lf-selection-select'
74
+ wrapper.oncontextmenu = function prevent(ev: MouseEvent) {
75
+ ev.preventDefault()
76
+ }
77
+ wrapper.style.top = `${this.startPoint.y}px`
78
+ wrapper.style.left = `${this.startPoint.x}px`
79
+ domContainer.appendChild(wrapper)
80
+ this.wrapper = wrapper
81
+ document.addEventListener('mousemove', this.draw)
82
+ document.addEventListener('mouseup', this.drawOff)
83
+ }
84
+
85
+ onToolContainerMouseUp = (e: MouseEvent) => {
86
+ if (this.mouseDownInfo) {
87
+ const { x, y, time } = this.mouseDownInfo
88
+ const now = Date.now()
89
+ // 用 mouseDown 和 mouseUp 的位置偏移及时间间隔来判断是否是点击事件
90
+ const isClickEvent =
91
+ Math.abs(e.clientX - x) < 10 &&
92
+ Math.abs(e.clientY - y) < 10 &&
93
+ now - time < 100
94
+ if (isClickEvent) {
95
+ this.lf.clearSelectElements()
64
96
  }
65
- wrapper.style.top = `${this.startPoint.y}px`
66
- wrapper.style.left = `${this.startPoint.x}px`
67
- domContainer.appendChild(wrapper)
68
- this.wrapper = wrapper
69
- document.addEventListener('mousemove', this.draw)
70
- document.addEventListener('mouseup', this.drawOff)
71
- })
97
+ this.mouseDownInfo = null
98
+ }
72
99
  }
73
100
 
74
101
  /**
@@ -85,13 +112,17 @@ export class SelectionSelect {
85
112
  * 开启选区
86
113
  */
87
114
  openSelectionSelect() {
88
- const { stopMoveGraph } = this.lf.getEditConfig()
89
- if (!stopMoveGraph) {
90
- this.isDefaultStopMoveGraph = false
91
- this.lf.updateEditConfig({
92
- stopMoveGraph: true,
93
- })
115
+ if (!this.disabled) {
116
+ this.closeSelectionSelect()
117
+ }
118
+ if (!this.container) {
119
+ return
94
120
  }
121
+ this.mouseDownInfo = null
122
+ this.container.addEventListener('mousedown', this.onToolContainerMouseDown)
123
+ this.container.addEventListener('mouseup', this.onToolContainerMouseUp)
124
+ // 取消点击事件的穿透,只让 ToolOverlay 接收事件,避免与图形元素的事件冲突
125
+ this.container.style.pointerEvents = 'auto'
95
126
  this.open()
96
127
  }
97
128
 
@@ -99,11 +130,16 @@ export class SelectionSelect {
99
130
  * 关闭选区
100
131
  */
101
132
  closeSelectionSelect() {
102
- if (!this.isDefaultStopMoveGraph) {
103
- this.lf.updateEditConfig({
104
- stopMoveGraph: false,
105
- })
133
+ if (!this.container) {
134
+ return
106
135
  }
136
+ this.container.style.pointerEvents = 'none'
137
+ this.mouseDownInfo = null
138
+ this.container.removeEventListener(
139
+ 'mousedown',
140
+ this.onToolContainerMouseDown,
141
+ )
142
+ this.container.removeEventListener('mouseup', this.onToolContainerMouseUp)
107
143
  this.close()
108
144
  }
109
145
 
@@ -169,11 +205,19 @@ export class SelectionSelect {
169
205
  const nonGroupedElements: typeof elements = []
170
206
  elements.forEach((element) => {
171
207
  // 如果节点属于分组,则不选中节点,此处兼容旧版 Group 插件
172
- if (group && group.getNodeGroup(element.id)) {
173
- return
208
+ if (group) {
209
+ const elementGroup = group.getNodeGroup(element.id)
210
+ if (elements.includes(elementGroup)) {
211
+ // 当被选中的元素的父分组被选中时,不选中该元素
212
+ return
213
+ }
174
214
  }
175
- if (dynamicGroup && dynamicGroup.getGroupByNodeId(element.id)) {
176
- return
215
+ if (dynamicGroup) {
216
+ const elementGroup = dynamicGroup.getGroupByNodeId(element.id)
217
+ if (elements.includes(elementGroup)) {
218
+ // 当被选中的元素的父分组被选中时,不选中该元素
219
+ return
220
+ }
177
221
  }
178
222
  this.lf.selectElementById(element.id, true)
179
223
  nonGroupedElements.push(element)
@@ -178,8 +178,18 @@ export class DynamicGroup {
178
178
  this.topGroupZIndex = max
179
179
  }
180
180
 
181
- // 监听 LogicFlow 的相关事件,做对应的处理
182
- addNodeToGroup = ({ data: node }: CallbackArgs<'node:add'>) => {
181
+ onSelectionDrop = () => {
182
+ const { nodes: selectedNodes } = this.lf.graphModel.getSelectElements()
183
+ selectedNodes.forEach((node) => {
184
+ this.addNodeToGroup(node)
185
+ })
186
+ }
187
+
188
+ onNodeAddOrDrop = ({ data: node }: CallbackArgs<'node:add'>) => {
189
+ this.addNodeToGroup(node)
190
+ }
191
+
192
+ addNodeToGroup = (node: LogicFlow.NodeData) => {
183
193
  // 1. 如果该节点之前已经在 group 中了,则将其从之前的 group 移除
184
194
  const preGroupId = this.nodeGroupMap.get(node.id)
185
195
 
@@ -230,8 +240,7 @@ export class DynamicGroup {
230
240
  if (isAllowAppendIn) {
231
241
  group.addChild(node.id)
232
242
  // 建立节点与 group 的映射关系放在了 group.addChild 触发的事件中,与直接调用 addChild 的行为保持一致
233
- // TODO 下面这个是干什么的,是否需要一起移动到事件的逻辑中?
234
- group.setAllowAppendChild(true)
243
+ group.setAllowAppendChild(false)
235
244
  } else {
236
245
  // 抛出不允许插入的事件
237
246
  this.lf.emit('group:not-allowed', {
@@ -273,7 +282,17 @@ export class DynamicGroup {
273
282
  }
274
283
  }
275
284
 
276
- setActiveGroup = ({ data: node }: CallbackArgs<'node:drag'>) => {
285
+ onSelectionDrag = () => {
286
+ const { nodes: selectedNodes } = this.lf.graphModel.getSelectElements()
287
+ selectedNodes.forEach((node) => {
288
+ this.setActiveGroup(node)
289
+ })
290
+ }
291
+ onNodeDrag = ({ data: node }: CallbackArgs<'node:drag'>) => {
292
+ this.setActiveGroup(node)
293
+ }
294
+
295
+ setActiveGroup = (node: LogicFlow.NodeData) => {
277
296
  const nodeModel = this.lf.getNodeModelById(node.id)
278
297
  const bounds = nodeModel?.getBounds()
279
298
 
@@ -666,9 +685,11 @@ export class DynamicGroup {
666
685
 
667
686
  graphModel.dynamicGroup = this
668
687
 
669
- lf.on('node:add,node:drop,node:dnd-add', this.addNodeToGroup)
688
+ lf.on('node:add,node:drop,node:dnd-add', this.onNodeAddOrDrop)
689
+ lf.on('selection:drop', this.onSelectionDrop)
670
690
  lf.on('node:delete', this.removeNodeFromGroup)
671
- lf.on('node:drag,node:dnd-drag', this.setActiveGroup)
691
+ lf.on('node:drag,node:dnd-drag', this.onNodeDrag)
692
+ lf.on('selection:drag', this.onSelectionDrag)
672
693
  lf.on('node:click', this.onNodeSelect)
673
694
  lf.on('node:mousemove', this.onNodeMove)
674
695
  lf.on('graph:rendered', this.onGraphRendered)
@@ -736,9 +757,11 @@ export class DynamicGroup {
736
757
 
737
758
  destroy() {
738
759
  // 销毁监听的事件,并移除渲染的 dom 内容
739
- this.lf.off('node:add,node:drop,node:dnd-add', this.addNodeToGroup)
760
+ this.lf.off('node:add,node:drop,node:dnd-add', this.onNodeAddOrDrop)
761
+ this.lf.off('selection:drop', this.onSelectionDrop)
740
762
  this.lf.off('node:delete', this.removeNodeFromGroup)
741
- this.lf.off('node:drag,node:dnd-drag', this.setActiveGroup)
763
+ this.lf.off('node:drag,node:dnd-drag', this.onNodeDrag)
764
+ this.lf.off('selection:drag', this.onSelectionDrag)
742
765
  this.lf.off('node:click', this.onNodeSelect)
743
766
  this.lf.off('node:mousemove', this.onNodeMove)
744
767
  this.lf.off('graph:rendered', this.onGraphRendered)