@logicflow/extension 2.2.0 → 2.2.1

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 (201) hide show
  1. package/README.md +16 -0
  2. package/dist/index.css +1 -1
  3. package/dist/index.min.js +1 -1
  4. package/dist/index.min.js.map +1 -1
  5. package/es/NodeResize/node/RectResize.d.ts +0 -6
  6. package/es/NodeResize/node/RectResize.js +2 -11
  7. package/es/bpmn-elements-adapter/json2xml.d.ts +2 -1
  8. package/es/bpmn-elements-adapter/json2xml.js +18 -4
  9. package/es/bpmn-elements-adapter/xml2json.js +2 -7
  10. package/es/components/control/index.d.ts +1 -0
  11. package/es/components/control/index.js +24 -11
  12. package/es/components/mini-map/index.js +1 -1
  13. package/es/dynamic-group/model.d.ts +2 -1
  14. package/es/dynamic-group/model.js +28 -11
  15. package/es/dynamic-group/node.js +1 -0
  16. package/es/index.css +1 -1
  17. package/es/index.d.ts +1 -0
  18. package/es/index.js +2 -0
  19. package/es/insert-node-in-polyline/index.js +11 -35
  20. package/es/materials/curved-edge/index.js +49 -21
  21. package/es/materials/group/GroupNode.d.ts +0 -6
  22. package/es/materials/group/GroupNode.js +1 -6
  23. package/es/pool/LaneModel.d.ts +90 -0
  24. package/es/pool/LaneModel.js +252 -0
  25. package/es/pool/LaneView.d.ts +40 -0
  26. package/es/pool/LaneView.js +202 -0
  27. package/es/pool/PoolModel.d.ts +120 -0
  28. package/es/pool/PoolModel.js +586 -0
  29. package/es/pool/PoolView.d.ts +17 -0
  30. package/es/pool/PoolView.js +76 -0
  31. package/es/pool/constant.d.ts +15 -0
  32. package/es/pool/constant.js +17 -0
  33. package/es/pool/index.d.ts +89 -0
  34. package/es/pool/index.js +524 -0
  35. package/es/pool/utils.d.ts +19 -0
  36. package/es/pool/utils.js +33 -0
  37. package/es/style/index.css +1 -1
  38. package/es/style/raw.d.ts +1 -1
  39. package/es/style/raw.js +1 -1
  40. package/es/tools/label/LabelOverlay.js +3 -3
  41. package/es/tools/label/mediumEditor.d.ts +1 -1
  42. package/es/tools/label/mediumEditor.js +89 -52
  43. package/es/tools/snapshot/index.d.ts +7 -3
  44. package/es/tools/snapshot/index.js +72 -77
  45. package/lib/NodeResize/node/RectResize.d.ts +0 -6
  46. package/lib/NodeResize/node/RectResize.js +1 -10
  47. package/lib/bpmn-elements-adapter/json2xml.d.ts +2 -1
  48. package/lib/bpmn-elements-adapter/json2xml.js +19 -4
  49. package/lib/bpmn-elements-adapter/xml2json.js +2 -7
  50. package/lib/components/control/index.d.ts +1 -0
  51. package/lib/components/control/index.js +24 -11
  52. package/lib/components/mini-map/index.js +1 -1
  53. package/lib/dynamic-group/model.d.ts +2 -1
  54. package/lib/dynamic-group/model.js +28 -11
  55. package/lib/dynamic-group/node.js +1 -0
  56. package/lib/index.css +1 -1
  57. package/lib/index.d.ts +1 -0
  58. package/lib/index.js +2 -0
  59. package/lib/insert-node-in-polyline/index.js +10 -34
  60. package/lib/materials/curved-edge/index.js +49 -21
  61. package/lib/materials/group/GroupNode.d.ts +0 -6
  62. package/lib/materials/group/GroupNode.js +1 -6
  63. package/lib/pool/LaneModel.d.ts +90 -0
  64. package/lib/pool/LaneModel.js +255 -0
  65. package/lib/pool/LaneView.d.ts +40 -0
  66. package/lib/pool/LaneView.js +205 -0
  67. package/lib/pool/PoolModel.d.ts +120 -0
  68. package/lib/pool/PoolModel.js +589 -0
  69. package/lib/pool/PoolView.d.ts +17 -0
  70. package/lib/pool/PoolView.js +79 -0
  71. package/lib/pool/constant.d.ts +15 -0
  72. package/lib/pool/constant.js +20 -0
  73. package/lib/pool/index.d.ts +89 -0
  74. package/lib/pool/index.js +527 -0
  75. package/lib/pool/utils.d.ts +19 -0
  76. package/lib/pool/utils.js +38 -0
  77. package/lib/style/index.css +1 -1
  78. package/lib/style/raw.d.ts +1 -1
  79. package/lib/style/raw.js +1 -1
  80. package/lib/tools/label/LabelOverlay.js +2 -2
  81. package/lib/tools/label/mediumEditor.d.ts +1 -1
  82. package/lib/tools/label/mediumEditor.js +91 -53
  83. package/lib/tools/snapshot/index.d.ts +7 -3
  84. package/lib/tools/snapshot/index.js +72 -77
  85. package/package.json +10 -7
  86. package/.turbo/turbo-build.log +0 -38
  87. package/CHANGELOG.md +0 -1766
  88. package/__test__/bpmn-adapter.test.js +0 -227
  89. package/es/materials/curved-edge/__test__/curved-edge.test.d.ts +0 -1
  90. package/es/materials/curved-edge/__test__/curved-edge.test.js +0 -18
  91. package/jest.config.js +0 -198
  92. package/lib/materials/curved-edge/__test__/curved-edge.test.d.ts +0 -1
  93. package/lib/materials/curved-edge/__test__/curved-edge.test.js +0 -20
  94. package/rollup.config.js +0 -52
  95. package/src/NodeResize/BasicShape/Ellipse.tsx +0 -22
  96. package/src/NodeResize/BasicShape/Polygon.tsx +0 -24
  97. package/src/NodeResize/BasicShape/Rect.tsx +0 -44
  98. package/src/NodeResize/control/Control.tsx +0 -537
  99. package/src/NodeResize/control/ControlGroup.tsx +0 -76
  100. package/src/NodeResize/control/Util.ts +0 -206
  101. package/src/NodeResize/index.ts +0 -26
  102. package/src/NodeResize/node/DiamondResize.tsx +0 -149
  103. package/src/NodeResize/node/EllipseResize.tsx +0 -140
  104. package/src/NodeResize/node/HtmlResize.tsx +0 -125
  105. package/src/NodeResize/node/RectResize.tsx +0 -146
  106. package/src/NodeResize/node/index.ts +0 -4
  107. package/src/bpmn/constant.ts +0 -56
  108. package/src/bpmn/events/EndEvent.ts +0 -73
  109. package/src/bpmn/events/StartEvent.ts +0 -52
  110. package/src/bpmn/events/index.ts +0 -2
  111. package/src/bpmn/flow/SequenceFlow.ts +0 -25
  112. package/src/bpmn/flow/index.ts +0 -1
  113. package/src/bpmn/gateways/ExclusiveGateway.ts +0 -71
  114. package/src/bpmn/gateways/index.ts +0 -1
  115. package/src/bpmn/getBpmnId.ts +0 -31
  116. package/src/bpmn/index.ts +0 -60
  117. package/src/bpmn/tasks/ServiceTask.ts +0 -63
  118. package/src/bpmn/tasks/UserTask.ts +0 -64
  119. package/src/bpmn/tasks/index.ts +0 -2
  120. package/src/bpmn-adapter/bpmnIds.ts +0 -31
  121. package/src/bpmn-adapter/index.ts +0 -835
  122. package/src/bpmn-adapter/json2xml.ts +0 -127
  123. package/src/bpmn-adapter/xml2json.ts +0 -544
  124. package/src/bpmn-elements/README.md +0 -223
  125. package/src/bpmn-elements/__tests__/definition.test.js +0 -72
  126. package/src/bpmn-elements/index.d.ts +0 -26
  127. package/src/bpmn-elements/index.ts +0 -107
  128. package/src/bpmn-elements/presets/Event/EndEventFactory.ts +0 -114
  129. package/src/bpmn-elements/presets/Event/IntermediateCatchEvent.ts +0 -108
  130. package/src/bpmn-elements/presets/Event/IntermediateThrowEvent.ts +0 -109
  131. package/src/bpmn-elements/presets/Event/StartEventFactory.ts +0 -114
  132. package/src/bpmn-elements/presets/Event/boundaryEventFactory.ts +0 -117
  133. package/src/bpmn-elements/presets/Event/index.ts +0 -14
  134. package/src/bpmn-elements/presets/Flow/flow.d.ts +0 -6
  135. package/src/bpmn-elements/presets/Flow/index.ts +0 -8
  136. package/src/bpmn-elements/presets/Flow/manhattan.ts +0 -691
  137. package/src/bpmn-elements/presets/Flow/sequenceFlow.ts +0 -65
  138. package/src/bpmn-elements/presets/Gateway/gateway.ts +0 -107
  139. package/src/bpmn-elements/presets/Gateway/index.ts +0 -23
  140. package/src/bpmn-elements/presets/Pool/Lane.ts +0 -211
  141. package/src/bpmn-elements/presets/Pool/Pool.ts +0 -284
  142. package/src/bpmn-elements/presets/Pool/index.ts +0 -89
  143. package/src/bpmn-elements/presets/Task/index.ts +0 -122
  144. package/src/bpmn-elements/presets/Task/subProcess.ts +0 -189
  145. package/src/bpmn-elements/presets/Task/task.ts +0 -193
  146. package/src/bpmn-elements/presets/icons.ts +0 -155
  147. package/src/bpmn-elements/utils.ts +0 -52
  148. package/src/bpmn-elements-adapter/README.md +0 -293
  149. package/src/bpmn-elements-adapter/__tests__/adapter_in.test.js +0 -528
  150. package/src/bpmn-elements-adapter/__tests__/adapter_out.test.js +0 -569
  151. package/src/bpmn-elements-adapter/constant.ts +0 -76
  152. package/src/bpmn-elements-adapter/index.ts +0 -1134
  153. package/src/bpmn-elements-adapter/json2xml.ts +0 -91
  154. package/src/bpmn-elements-adapter/xml2json.ts +0 -548
  155. package/src/components/context-menu/index.ts +0 -253
  156. package/src/components/control/index.ts +0 -141
  157. package/src/components/dnd-panel/index.ts +0 -137
  158. package/src/components/highlight/index.ts +0 -227
  159. package/src/components/menu/index.ts +0 -748
  160. package/src/components/mini-map/index.ts +0 -686
  161. package/src/components/selection-select/index.ts +0 -387
  162. package/src/dynamic-group/index.ts +0 -775
  163. package/src/dynamic-group/model.ts +0 -562
  164. package/src/dynamic-group/node.ts +0 -288
  165. package/src/dynamic-group/utils.ts +0 -46
  166. package/src/index.less +0 -1
  167. package/src/index.ts +0 -45
  168. package/src/insert-node-in-polyline/edge.ts +0 -175
  169. package/src/insert-node-in-polyline/index.ts +0 -187
  170. package/src/materials/curved-edge/__test__/curved-edge.test.ts +0 -46
  171. package/src/materials/curved-edge/index.ts +0 -185
  172. package/src/materials/group/GroupNode.ts +0 -442
  173. package/src/materials/group/index.ts +0 -542
  174. package/src/materials/node-selection/index.ts +0 -380
  175. package/src/mindmap/fakerRoot.ts +0 -19
  176. package/src/mindmap/index.ts +0 -328
  177. package/src/mindmap/markContent.ts +0 -81
  178. package/src/mindmap/markContentOption.ts +0 -81
  179. package/src/mindmap/markEntity.ts +0 -82
  180. package/src/mindmap/markRoot.ts +0 -83
  181. package/src/mindmap/theme.ts +0 -11
  182. package/src/rect-label-node/RectLabelNodeView.ts +0 -33
  183. package/src/rect-label-node/index.ts +0 -15
  184. package/src/style/index.less +0 -342
  185. package/src/style/raw.ts +0 -295
  186. package/src/tools/auto-layout/index.ts +0 -282
  187. package/src/tools/flow-path/index.ts +0 -233
  188. package/src/tools/label/Label.tsx +0 -357
  189. package/src/tools/label/LabelModel.ts +0 -83
  190. package/src/tools/label/LabelOverlay.tsx +0 -158
  191. package/src/tools/label/algorithm.ts +0 -42
  192. package/src/tools/label/index.ts +0 -479
  193. package/src/tools/label/mediumEditor.ts +0 -94
  194. package/src/tools/label/utils.ts +0 -395
  195. package/src/tools/proximity-connect/index.ts +0 -435
  196. package/src/tools/snapshot/README.md +0 -145
  197. package/src/tools/snapshot/index.ts +0 -718
  198. package/src/tools/snapshot/utils.ts +0 -163
  199. package/src/turbo-adapter/index.ts +0 -212
  200. package/stats.html +0 -4842
  201. package/tsconfig.json +0 -18
@@ -1,748 +0,0 @@
1
- import LogicFlow, { EditConfigModel } from '@logicflow/core'
2
-
3
- import GraphData = LogicFlow.GraphData
4
- import NodeData = LogicFlow.NodeData
5
- import EdgeData = LogicFlow.EdgeData
6
- import Position = LogicFlow.Position
7
-
8
- type SetType = 'add' | 'reset'
9
-
10
- export type MenuItem = {
11
- text?: string
12
- className?: string
13
- icon?: boolean | string
14
- disabled?: boolean
15
- callback: (element: any) => void
16
- }
17
-
18
- export type MenuConfig = {
19
- nodeMenu?: MenuItem[] | false
20
- edgeMenu?: MenuItem[] | false
21
- graphMenu?: MenuItem[] | false
22
- selectionMenu?: MenuItem[] | false
23
- }
24
-
25
- export type MenuType = 'nodeMenu' | 'edgeMenu' | 'graphMenu' | 'selectionMenu'
26
-
27
- const DefaultNodeMenuKey = 'lf:defaultNodeMenu'
28
- const DefaultEdgeMenuKey = 'lf:defaultEdgeMenu'
29
- const DefaultGraphMenuKey = 'lf:defaultGraphMenu'
30
- const DefaultSelectionMenuKey = 'lf:defaultSelectionMenu'
31
-
32
- const menuKeyMap: Record<MenuType, string> = {
33
- nodeMenu: DefaultNodeMenuKey,
34
- edgeMenu: DefaultEdgeMenuKey,
35
- graphMenu: DefaultGraphMenuKey,
36
- selectionMenu: DefaultSelectionMenuKey,
37
- }
38
-
39
- const defaultMenuConfig: MenuConfig = {
40
- nodeMenu: [],
41
- edgeMenu: [],
42
- graphMenu: [],
43
- selectionMenu: [],
44
- }
45
-
46
- class Menu {
47
- lf: LogicFlow
48
- private __container?: HTMLElement
49
- private __menuDOM?: HTMLElement
50
- private menuTypeMap?: Map<string, MenuItem[]>
51
- private __currentData: EdgeData | NodeData | GraphData | Position | null =
52
- null
53
- private __isSilentMode: boolean = false
54
- private __editConfigChangeHandler?: ({
55
- data,
56
- }: {
57
- data: EditConfigModel
58
- }) => void
59
- static pluginName = 'menu'
60
-
61
- constructor({ lf }) {
62
- this.lf = lf
63
- this.__menuDOM = document.createElement('ul')
64
- this.__isSilentMode = lf.graphModel.editConfigModel.isSilentMode
65
-
66
- this.menuTypeMap = new Map()
67
- this.init()
68
- this.lf.setMenuConfig = (config) => {
69
- this.setMenuConfig(config)
70
- }
71
- this.lf.addMenuConfig = (config) => {
72
- this.addMenuConfig(config)
73
- }
74
- this.lf.setMenuByType = (config) => {
75
- this.setMenuByType(config)
76
- }
77
- this.lf.changeMenuItemDisableStatus = (menuKey, text, disabled) => {
78
- this.changeMenuItemDisableStatus(menuKey, text, disabled)
79
- }
80
- this.lf.getMenuConfig = (menuKey) => {
81
- return this.getMenuConfig(menuKey)
82
- }
83
- this.lf.resetMenuConfigByType = (menuType) => {
84
- this.resetMenuConfigByType(menuType)
85
- }
86
- }
87
-
88
- /**
89
- * 初始化设置默认内置菜单栏
90
- */
91
- private init() {
92
- defaultMenuConfig.nodeMenu = [
93
- {
94
- text: '删除',
95
- callback: (node) => {
96
- this.lf.deleteNode(node.id)
97
- },
98
- },
99
- {
100
- text: '编辑文本',
101
- callback: (node) => {
102
- this.lf.graphModel.editText(node.id)
103
- },
104
- },
105
- {
106
- text: '复制',
107
- callback: (node) => {
108
- this.lf.cloneNode(node.id)
109
- },
110
- },
111
- ]
112
- this.menuTypeMap?.set(DefaultNodeMenuKey, defaultMenuConfig.nodeMenu)
113
-
114
- defaultMenuConfig.edgeMenu = [
115
- {
116
- text: '删除',
117
- callback: (edge) => {
118
- this.lf.deleteEdge(edge.id)
119
- },
120
- },
121
- {
122
- text: '编辑文本',
123
- callback: (edge) => {
124
- this.lf.graphModel.editText(edge.id)
125
- },
126
- },
127
- ]
128
- this.menuTypeMap?.set(DefaultEdgeMenuKey, defaultMenuConfig.edgeMenu)
129
-
130
- defaultMenuConfig.graphMenu = []
131
-
132
- defaultMenuConfig.selectionMenu = [
133
- {
134
- text: '删除',
135
- callback: (elements) => {
136
- this.lf.clearSelectElements()
137
- elements.edges.forEach((edge) => this.lf.deleteEdge(edge.id))
138
- elements.nodes.forEach((node) => this.lf.deleteNode(node.id))
139
- },
140
- },
141
- ]
142
- this.menuTypeMap?.set(
143
- DefaultSelectionMenuKey,
144
- defaultMenuConfig.selectionMenu,
145
- )
146
- }
147
-
148
- private showMenu(x, y, menuList, options?) {
149
- // 在静默模式下不显示菜单
150
- if (this.__isSilentMode) return
151
-
152
- if (!menuList || !menuList.length) return
153
- const { __menuDOM: menu } = this
154
- if (menu) {
155
- // 菜单容器不变,需要先清空内部的菜单项
156
- menu.innerHTML = ''
157
- menu.append(...this.__getMenuDom(menuList))
158
- // 菜单中没有项,不显示
159
- if (!menu.children.length) return
160
- menu.style.display = 'block'
161
- if (!options) {
162
- menu.style.top = `${y}px`
163
- menu.style.left = `${x}px`
164
- return
165
- }
166
-
167
- // https://github.com/didi/LogicFlow/issues/1019
168
- // 根据边界判断菜单的left 和 top
169
- const { width, height, clientX, clientY } = options
170
- const { graphModel } = this.lf
171
-
172
- const menuWidth = menu.offsetWidth
173
- let menuIsRightShow = true
174
- // ======先进行可视屏幕范围的判断=======
175
- // 浏览器窗口可视区域兼容性写法
176
- // eslint-disable-next-line max-len
177
- const windowMaxX =
178
- window.innerWidth ||
179
- document.documentElement.clientWidth ||
180
- document.body.clientWidth
181
- let rightDistance = windowMaxX - clientX
182
- // ======先进行可视屏幕范围的判断=======
183
- // ========再进行画布范围的判断========
184
- const graphRect = graphModel.rootEl.getBoundingClientRect()
185
- const graphMaxX = graphRect.left + graphRect.width
186
- if (graphMaxX < windowMaxX) {
187
- // 画布右边小于可视屏幕范围的最右边,取画布右边作为极限值,计算出当前触摸点距离右边极限值的距离
188
- rightDistance = graphMaxX - clientX
189
- }
190
- // ========再进行画布范围的判断========
191
- // 根据当前触摸点距离右边的距离 跟 menuWidth进行比较
192
- if (rightDistance < menuWidth) {
193
- // 空间不足够,显示在左边
194
- menuIsRightShow = false
195
- }
196
- if (menuIsRightShow) {
197
- menu.style.left = `${x}px`
198
- } else {
199
- menu.style.left = `${x - width}px`
200
- }
201
-
202
- const menuHeight = menu.offsetHeight
203
- let menuIsBottomShow = true
204
- // ======先进行可视屏幕范围的判断=======
205
- // 浏览器窗口可视区域兼容性写法
206
- // eslint-disable-next-line max-len
207
- const windowMaxY =
208
- window.innerHeight ||
209
- document.documentElement.clientHeight ||
210
- document.body.clientHeight
211
- let bottomDistance = windowMaxY - clientY
212
- // ======先进行可视屏幕范围的判断=======
213
- // ========再进行画布范围的判断========
214
- const graphMaxY = graphRect.top + graphRect.height
215
- if (graphMaxY < windowMaxY) {
216
- // 画布底部小于可视屏幕范围的最底边,取画布底部作为极限值,计算出当前触摸点距离底部极限值的距离
217
- bottomDistance = graphMaxY - clientY
218
- }
219
- // ========再进行画布范围的判断========
220
- if (bottomDistance < menuHeight) {
221
- // 如果下边距离太小,无法显示menu,则向上显示
222
- menuIsBottomShow = false
223
- }
224
- if (menuIsBottomShow) {
225
- menu.style.top = `${y}px`
226
- } else {
227
- menu.style.top = `${y - height}px`
228
- }
229
- }
230
- }
231
-
232
- /**
233
- * 通用的菜单配置处理方法
234
- */
235
- private processMenuConfig(config: MenuConfig, operation: 'set' | 'add') {
236
- if (!config) return
237
-
238
- const menuTypes: MenuType[] = [
239
- 'nodeMenu',
240
- 'edgeMenu',
241
- 'graphMenu',
242
- 'selectionMenu',
243
- ]
244
-
245
- menuTypes.forEach((menuType) => {
246
- const menuConfig = config[menuType]
247
- const menuKey = menuKeyMap[menuType]
248
-
249
- if (menuConfig === undefined) return
250
-
251
- if (operation === 'set') {
252
- // 设置菜单配置
253
- this.menuTypeMap?.set(menuKey, menuConfig ? menuConfig : [])
254
- } else if (operation === 'add' && Array.isArray(menuConfig)) {
255
- // 追加菜单配置(只支持数组类型)
256
- const existingMenuList = this.menuTypeMap?.get(menuKey) ?? []
257
- this.menuTypeMap?.set(menuKey, existingMenuList.concat(menuConfig))
258
- }
259
- })
260
- }
261
-
262
- /**
263
- * 创建图片元素
264
- */
265
- private createImageElement(src: string, alt: string): HTMLImageElement {
266
- const img = document.createElement('img')
267
- img.src = src
268
- img.alt = alt
269
- img.style.width = '16px'
270
- img.style.height = '16px'
271
- img.style.objectFit = 'contain'
272
- return img
273
- }
274
-
275
- /**
276
- * 检查是否为图片文件路径
277
- */
278
- private isImageFile(iconString: string): boolean {
279
- const imageExtensions = [
280
- '.png',
281
- '.jpg',
282
- '.jpeg',
283
- '.gif',
284
- '.svg',
285
- '.webp',
286
- '.ico',
287
- '.bmp',
288
- ]
289
- return imageExtensions.some((ext) => iconString.toLowerCase().includes(ext))
290
- }
291
-
292
- /**
293
- * 处理图标逻辑
294
- */
295
- private processIcon(
296
- iconContainer: HTMLElement,
297
- icon: boolean | string,
298
- text?: string,
299
- ) {
300
- if (typeof icon !== 'string') {
301
- // 如果icon是true,保持原有逻辑(创建空的图标容器)
302
- return
303
- }
304
-
305
- const iconString = icon as string
306
-
307
- // 1. base64格式的图片数据
308
- if (iconString.startsWith('data:image/')) {
309
- const img = this.createImageElement(iconString, text || 'icon')
310
- iconContainer.appendChild(img)
311
- return
312
- }
313
-
314
- // 2. 图片文件路径
315
- if (this.isImageFile(iconString)) {
316
- const img = this.createImageElement(iconString, text || 'icon')
317
- iconContainer.appendChild(img)
318
- return
319
- }
320
-
321
- // 3. HTML内容(包含< >标签)
322
- if (iconString.includes('<') && iconString.includes('>')) {
323
- iconContainer.innerHTML = iconString
324
- return
325
- }
326
-
327
- // 4. CSS类名(以空格分隔的多个类名或以.开头)
328
- if (iconString.includes(' ') || iconString.startsWith('.')) {
329
- const iconClasses = iconString.replace(/^\./, '').split(' ')
330
- iconContainer.classList.add(...iconClasses)
331
- return
332
- }
333
-
334
- // 5. 单个CSS类名
335
- iconContainer.classList.add(iconString)
336
- }
337
-
338
- /**
339
- * 获取 Menu DOM
340
- * @param list 菜单项
341
- * @return 菜单项 DOM
342
- */
343
- private __getMenuDom(list): HTMLElement[] {
344
- const menuList: any = []
345
- list &&
346
- list.length > 0 &&
347
- list.forEach((item) => {
348
- const element = document.createElement('li')
349
- if (item.className) {
350
- element.className = `lf-menu-item ${item.disabled ? 'lf-menu-item__disabled' : ''} ${item.className}`
351
- } else {
352
- element.className = `lf-menu-item ${item.disabled ? 'lf-menu-item__disabled' : ''}`
353
- }
354
- if (item.icon) {
355
- const icon = document.createElement('span')
356
- icon.className = 'lf-menu-item-icon'
357
- this.processIcon(icon, item.icon, item.text)
358
- element.appendChild(icon)
359
- }
360
- const text = document.createElement('span')
361
- text.className = 'lf-menu-item-text'
362
- if (item.text) {
363
- text.innerText = item.text
364
- }
365
- element.appendChild(text)
366
- if (item.disabled) {
367
- element.setAttribute('disabled', 'true')
368
- }
369
- ;(element as any).onclickCallback = item.callback
370
- menuList.push(element)
371
- })
372
- return menuList
373
- }
374
-
375
- /**
376
- * 更新菜单项DOM元素的禁用状态
377
- * @param text 菜单项文本
378
- * @param disabled 是否禁用
379
- */
380
- private updateMenuItemDOMStatus(text: string, disabled: boolean) {
381
- if (!this.__menuDOM || this.__menuDOM.style.display === 'none') {
382
- return
383
- }
384
-
385
- // 查找对应的菜单项DOM元素
386
- const menuItems = Array.from(
387
- this.__menuDOM.querySelectorAll('.lf-menu-item'),
388
- )
389
-
390
- const targetMenuItem = menuItems.find((menuItemElement) => {
391
- const textElement = menuItemElement.querySelector('.lf-menu-item-text')
392
- return textElement?.textContent === text
393
- })
394
-
395
- if (targetMenuItem) {
396
- const element = targetMenuItem as HTMLElement
397
-
398
- if (disabled) {
399
- element.classList.add('lf-menu-item__disabled')
400
- element.setAttribute('disabled', 'true')
401
- } else {
402
- element.classList.remove('lf-menu-item__disabled')
403
- element.removeAttribute('disabled')
404
- }
405
- }
406
- }
407
-
408
- /**
409
- * 设置静默模式监听器
410
- * 当 isSilentMode 变化时,动态更新菜单的显隐状态
411
- */
412
- private setupSilentModeListener() {
413
- // 创建并保存事件处理器引用
414
- this.__editConfigChangeHandler = ({ data }) => {
415
- const newIsSilentMode = data.isSilentMode
416
- if (newIsSilentMode !== this.__isSilentMode) {
417
- this.__isSilentMode = newIsSilentMode
418
- this.updateMenuVisibility(!newIsSilentMode)
419
- }
420
- }
421
- // 监听编辑配置变化
422
- this.lf.on('editConfig:changed', this.__editConfigChangeHandler)
423
- }
424
-
425
- /**
426
- * 更新菜单显隐状态
427
- */
428
- private updateMenuVisibility(visible: boolean) {
429
- if (!this.__menuDOM) return
430
-
431
- if (visible) {
432
- if (this.__currentData) {
433
- this.__menuDOM.style.display = 'block'
434
- }
435
- } else {
436
- this.__menuDOM.style.display = 'none'
437
- this.__currentData = null // 清除当前数据
438
- }
439
- }
440
-
441
- /**
442
- * 检查菜单是否正在显示并重新渲染
443
- */
444
- private refreshCurrentMenu() {
445
- if (
446
- !this.__menuDOM ||
447
- this.__menuDOM.style.display === 'none' ||
448
- !this.__currentData
449
- ) {
450
- return
451
- }
452
-
453
- // 保存当前菜单的位置
454
- const { left, top } = this.__menuDOM.style
455
-
456
- // 根据当前数据类型获取对应的菜单配置
457
- let menuList: any[] = []
458
-
459
- // 判断当前数据类型并获取相应菜单
460
- if (this.__currentData && typeof this.__currentData === 'object') {
461
- if (
462
- 'sourceNodeId' in this.__currentData &&
463
- 'targetNodeId' in this.__currentData
464
- ) {
465
- // 边菜单
466
- const model = this.lf.graphModel.getEdgeModelById(
467
- (this.__currentData as any).id,
468
- )
469
- if (model) {
470
- const typeMenus = this.menuTypeMap?.get(model.type)
471
- if (model.menu && Array.isArray(model.menu)) {
472
- menuList = model.menu
473
- } else if (typeMenus) {
474
- menuList = typeMenus
475
- } else {
476
- menuList = this.menuTypeMap?.get(DefaultEdgeMenuKey) ?? []
477
- }
478
- }
479
- } else if ('id' in this.__currentData && 'type' in this.__currentData) {
480
- // 节点菜单
481
- const model = this.lf.graphModel.getNodeModelById(
482
- (this.__currentData as any).id,
483
- )
484
- if (model) {
485
- const typeMenus = this.menuTypeMap?.get(model.type)
486
- if (model.menu && Array.isArray(model.menu)) {
487
- menuList = model.menu
488
- } else if (typeMenus) {
489
- menuList = typeMenus
490
- } else {
491
- menuList = this.menuTypeMap?.get(DefaultNodeMenuKey) ?? []
492
- }
493
- }
494
- } else if (
495
- 'nodes' in this.__currentData &&
496
- 'edges' in this.__currentData
497
- ) {
498
- // 选区菜单
499
- menuList = this.menuTypeMap?.get(DefaultSelectionMenuKey) ?? []
500
- } else {
501
- // 画布菜单
502
- menuList = this.menuTypeMap?.get(DefaultGraphMenuKey) ?? []
503
- }
504
- }
505
-
506
- // 重新渲染菜单
507
- if (menuList && menuList.length > 0) {
508
- this.__menuDOM.innerHTML = ''
509
- this.__menuDOM.append(...this.__getMenuDom(menuList))
510
-
511
- // 恢复菜单位置(如果有的话)
512
- if (left) this.__menuDOM.style.left = left
513
- if (top) this.__menuDOM.style.top = top
514
- } else {
515
- // 如果没有菜单项,隐藏菜单
516
- this.__menuDOM.style.display = 'none'
517
- this.__currentData = null
518
- }
519
- }
520
-
521
- /**
522
- * 设置指定类型元素的菜单
523
- */
524
- setMenuByType({ type, menu }: { type: string; menu: MenuItem[] }) {
525
- if (!type || !menu) {
526
- return
527
- }
528
- this.menuTypeMap?.set(type, menu)
529
- this.refreshCurrentMenu() // 实时更新DOM
530
- }
531
-
532
- getMenuConfig(menuKey: MenuType) {
533
- return this.menuTypeMap?.get(menuKeyMap[menuKey]) ?? []
534
- }
535
-
536
- resetMenuConfigByType(menuKey: MenuType) {
537
- this.setMenuConfig({
538
- [menuKey]: defaultMenuConfig[menuKey],
539
- })
540
- // setMenuConfig 已经包含了 refreshCurrentMenu 调用
541
- }
542
-
543
- resetAllMenuConfig() {
544
- this.setMenuConfig(defaultMenuConfig)
545
- // setMenuConfig 已经包含了 refreshCurrentMenu 调用
546
- }
547
-
548
- // 复写菜单
549
- setMenuConfig(config: MenuConfig) {
550
- this.processMenuConfig(config, 'set')
551
- this.refreshCurrentMenu() // 实时更新DOM
552
- }
553
-
554
- // 在默认菜单后面追加菜单项
555
- addMenuConfig(config: MenuConfig) {
556
- this.processMenuConfig(config, 'add')
557
- this.refreshCurrentMenu() // 实时更新DOM
558
- }
559
-
560
- /**
561
- * @deprecated
562
- * 复写添加
563
- */
564
- changeMenuItem(type: SetType, config: MenuConfig) {
565
- if (type === 'add') {
566
- this.addMenuConfig(config)
567
- } else if (type === 'reset') {
568
- this.setMenuConfig(config)
569
- } else {
570
- throw new Error(
571
- "The first parameter of changeMenuConfig should be 'add' or 'reset'",
572
- )
573
- }
574
- // addMenuConfig 和 setMenuConfig 已经包含了 refreshCurrentMenu 调用
575
- }
576
-
577
- changeMenuItemDisableStatus(
578
- menuKey: MenuType,
579
- text: string,
580
- disabled: boolean,
581
- ) {
582
- if (!menuKey || !text) {
583
- console.warn('params is vaild')
584
- return
585
- }
586
- const menuList = this.menuTypeMap?.get(menuKeyMap[menuKey]) ?? []
587
- if (!menuList.length) {
588
- console.warn(`menuMap: ${menuKey} is not exist`)
589
- return
590
- }
591
- const menuItem = menuList.find((item) => item.text === text)
592
- if (!menuItem) {
593
- console.warn(`menuItem: ${text} is not exist`)
594
- return
595
- }
596
- menuItem.disabled = disabled
597
-
598
- // 如果菜单当前正在显示,则同时更新DOM元素的样式
599
- this.updateMenuItemDOMStatus(text, disabled)
600
- }
601
-
602
- render(lf: LogicFlow, container: HTMLElement) {
603
- if (lf.graphModel.editConfigModel.isSilentMode) return
604
- this.__container = container
605
- this.__currentData = null // 当前展示的菜单所属元素的model数据
606
- // 监听 isSilentMode 变化
607
- this.setupSilentModeListener()
608
-
609
- if (this.__menuDOM) {
610
- this.__menuDOM.className = 'lf-menu'
611
- container.appendChild(this.__menuDOM)
612
- // 将选项的click事件委托至menu容器
613
- // 在捕获阶段拦截并执行
614
- this.__menuDOM.addEventListener(
615
- 'click',
616
- (event) => {
617
- event.stopPropagation()
618
- let target = event.target as HTMLElement
619
-
620
- // 菜单有多层dom,需要精确获取菜单项所对应的dom
621
- // 除菜单项dom外,应考虑两种情况
622
- // 1. 菜单项的子元素 2. 菜单外层容器
623
- while (
624
- Array.from(target.classList).indexOf('lf-menu-item') === -1 &&
625
- Array.from(target.classList).indexOf('lf-menu') === -1
626
- ) {
627
- target = target?.parentElement as HTMLElement
628
- }
629
- if (
630
- Array.from(target.classList).indexOf('lf-menu-item__disabled') > -1
631
- )
632
- return
633
- if (Array.from(target.classList).indexOf('lf-menu-item') > -1) {
634
- // 如果菜单项被禁用,则不执行回调
635
- ;(target as any).onclickCallback(this.__currentData, target)
636
- // 点击后隐藏menu
637
- if (this.__menuDOM) {
638
- this.__menuDOM.style.display = 'none'
639
- }
640
- this.__currentData = null
641
- } else {
642
- // 如果点击区域不在菜单项内
643
- console.warn('点击区域不在菜单项内,请检查代码!')
644
- }
645
- },
646
- true,
647
- )
648
- }
649
- // 通过事件控制菜单的显示和隐藏
650
- this.lf.on('node:contextmenu', ({ data, position, e }) => {
651
- const {
652
- domOverlayPosition: { x, y },
653
- } = position
654
- const { id } = data
655
- const model = this.lf.graphModel.getNodeModelById(id)
656
-
657
- if (!model || this.__isSilentMode) return
658
- let menuList: any = []
659
- const typeMenus = this.menuTypeMap?.get(model.type)
660
- // 1.如果单个节点自定义了菜单,以单个节点自定义为准
661
- if (model && model.menu && Array.isArray(model.menu)) {
662
- menuList = model.menu
663
- } else if (typeMenus) {
664
- // 2.如果当前节点类型定义了菜单,再取该配置
665
- menuList = typeMenus
666
- } else {
667
- // 3.最后取全局默认
668
- menuList = this.menuTypeMap?.get(DefaultNodeMenuKey)
669
- }
670
- this.__currentData = data
671
- this.showMenu(x, y, menuList, {
672
- width: model.width,
673
- height: model.height,
674
- clientX: e.clientX,
675
- clientY: e.clientY,
676
- })
677
- })
678
- this.lf.on('edge:contextmenu', ({ data, position, e }) => {
679
- const {
680
- domOverlayPosition: { x, y },
681
- } = position
682
- const { id } = data
683
- const model = this.lf.graphModel.getEdgeModelById(id)
684
- if (!model || this.__isSilentMode) return
685
- let menuList: any = []
686
- const typeMenus = this.menuTypeMap?.get(model.type)
687
- // 菜单优先级: model.menu > typeMenus > defaultEdgeMenu,注释同上节点
688
- if (model && model.menu && Array.isArray(model.menu)) {
689
- menuList = model.menu
690
- } else if (typeMenus) {
691
- menuList = typeMenus
692
- } else {
693
- menuList = this.menuTypeMap?.get(DefaultEdgeMenuKey) ?? []
694
- }
695
- this.__currentData = data
696
- this.showMenu(x, y, menuList, {
697
- width: model.width,
698
- height: model.height,
699
- clientX: e.clientX,
700
- clientY: e.clientY,
701
- })
702
- })
703
- this.lf.on('blank:contextmenu', ({ position }) => {
704
- if (this.__isSilentMode) return
705
- const menuList = this.menuTypeMap?.get(DefaultGraphMenuKey) ?? []
706
- const {
707
- domOverlayPosition: { x, y },
708
- } = position
709
- this.__currentData = { ...position.canvasOverlayPosition }
710
- this.showMenu(x, y, menuList)
711
- })
712
- this.lf.on('selection:contextmenu', ({ data, position }) => {
713
- if (this.__isSilentMode) return
714
- const menuList = this.menuTypeMap?.get(DefaultSelectionMenuKey)
715
- const {
716
- domOverlayPosition: { x, y },
717
- } = position
718
- this.__currentData = data
719
- this.showMenu(x, y, menuList)
720
- })
721
-
722
- this.lf.on('node:mousedown', () => {
723
- this.__menuDOM!.style.display = 'none'
724
- })
725
- this.lf.on('edge:click', () => {
726
- this.__menuDOM!.style.display = 'none'
727
- })
728
- this.lf.on('blank:click', () => {
729
- this.__menuDOM!.style.display = 'none'
730
- })
731
- }
732
-
733
- destroy() {
734
- // 清理事件监听器
735
- if (this.__editConfigChangeHandler) {
736
- this.lf.off('editConfig:changed', this.__editConfigChangeHandler)
737
- }
738
-
739
- if (this.__menuDOM) {
740
- this?.__container?.removeChild(this.__menuDOM)
741
- this.__menuDOM = undefined
742
- }
743
- }
744
- }
745
-
746
- export default Menu
747
-
748
- export { Menu }