@logicflow/extension 2.2.0-alpha.7 → 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 (125) hide show
  1. package/README.md +16 -0
  2. package/package.json +10 -7
  3. package/.turbo/turbo-build.log +0 -38
  4. package/CHANGELOG.md +0 -1829
  5. package/__test__/bpmn-adapter.test.js +0 -227
  6. package/es/materials/curved-edge/__test__/curved-edge.test.d.ts +0 -1
  7. package/es/materials/curved-edge/__test__/curved-edge.test.js +0 -18
  8. package/jest.config.js +0 -198
  9. package/lib/materials/curved-edge/__test__/curved-edge.test.d.ts +0 -1
  10. package/lib/materials/curved-edge/__test__/curved-edge.test.js +0 -20
  11. package/rollup.config.js +0 -52
  12. package/src/NodeResize/BasicShape/Ellipse.tsx +0 -22
  13. package/src/NodeResize/BasicShape/Polygon.tsx +0 -24
  14. package/src/NodeResize/BasicShape/Rect.tsx +0 -44
  15. package/src/NodeResize/control/Control.tsx +0 -537
  16. package/src/NodeResize/control/ControlGroup.tsx +0 -76
  17. package/src/NodeResize/control/Util.ts +0 -206
  18. package/src/NodeResize/index.ts +0 -26
  19. package/src/NodeResize/node/DiamondResize.tsx +0 -149
  20. package/src/NodeResize/node/EllipseResize.tsx +0 -140
  21. package/src/NodeResize/node/HtmlResize.tsx +0 -125
  22. package/src/NodeResize/node/RectResize.tsx +0 -126
  23. package/src/NodeResize/node/index.ts +0 -4
  24. package/src/bpmn/constant.ts +0 -56
  25. package/src/bpmn/events/EndEvent.ts +0 -73
  26. package/src/bpmn/events/StartEvent.ts +0 -52
  27. package/src/bpmn/events/index.ts +0 -2
  28. package/src/bpmn/flow/SequenceFlow.ts +0 -25
  29. package/src/bpmn/flow/index.ts +0 -1
  30. package/src/bpmn/gateways/ExclusiveGateway.ts +0 -71
  31. package/src/bpmn/gateways/index.ts +0 -1
  32. package/src/bpmn/getBpmnId.ts +0 -31
  33. package/src/bpmn/index.ts +0 -60
  34. package/src/bpmn/tasks/ServiceTask.ts +0 -63
  35. package/src/bpmn/tasks/UserTask.ts +0 -64
  36. package/src/bpmn/tasks/index.ts +0 -2
  37. package/src/bpmn-adapter/bpmnIds.ts +0 -31
  38. package/src/bpmn-adapter/index.ts +0 -835
  39. package/src/bpmn-adapter/json2xml.ts +0 -127
  40. package/src/bpmn-adapter/xml2json.ts +0 -544
  41. package/src/bpmn-elements/README.md +0 -223
  42. package/src/bpmn-elements/__tests__/definition.test.js +0 -72
  43. package/src/bpmn-elements/index.d.ts +0 -26
  44. package/src/bpmn-elements/index.ts +0 -107
  45. package/src/bpmn-elements/presets/Event/EndEventFactory.ts +0 -114
  46. package/src/bpmn-elements/presets/Event/IntermediateCatchEvent.ts +0 -108
  47. package/src/bpmn-elements/presets/Event/IntermediateThrowEvent.ts +0 -109
  48. package/src/bpmn-elements/presets/Event/StartEventFactory.ts +0 -114
  49. package/src/bpmn-elements/presets/Event/boundaryEventFactory.ts +0 -117
  50. package/src/bpmn-elements/presets/Event/index.ts +0 -14
  51. package/src/bpmn-elements/presets/Flow/flow.d.ts +0 -6
  52. package/src/bpmn-elements/presets/Flow/index.ts +0 -8
  53. package/src/bpmn-elements/presets/Flow/manhattan.ts +0 -691
  54. package/src/bpmn-elements/presets/Flow/sequenceFlow.ts +0 -65
  55. package/src/bpmn-elements/presets/Gateway/gateway.ts +0 -107
  56. package/src/bpmn-elements/presets/Gateway/index.ts +0 -23
  57. package/src/bpmn-elements/presets/Pool/Lane.ts +0 -211
  58. package/src/bpmn-elements/presets/Pool/Pool.ts +0 -284
  59. package/src/bpmn-elements/presets/Pool/index.ts +0 -89
  60. package/src/bpmn-elements/presets/Task/index.ts +0 -122
  61. package/src/bpmn-elements/presets/Task/subProcess.ts +0 -189
  62. package/src/bpmn-elements/presets/Task/task.ts +0 -193
  63. package/src/bpmn-elements/presets/icons.ts +0 -155
  64. package/src/bpmn-elements/utils.ts +0 -52
  65. package/src/bpmn-elements-adapter/README.md +0 -293
  66. package/src/bpmn-elements-adapter/__tests__/adapter_in.test.js +0 -528
  67. package/src/bpmn-elements-adapter/__tests__/adapter_out.test.js +0 -569
  68. package/src/bpmn-elements-adapter/constant.ts +0 -76
  69. package/src/bpmn-elements-adapter/index.ts +0 -1134
  70. package/src/bpmn-elements-adapter/json2xml.ts +0 -105
  71. package/src/bpmn-elements-adapter/xml2json.ts +0 -542
  72. package/src/components/context-menu/index.ts +0 -253
  73. package/src/components/control/index.ts +0 -155
  74. package/src/components/dnd-panel/index.ts +0 -137
  75. package/src/components/highlight/index.ts +0 -227
  76. package/src/components/menu/index.ts +0 -748
  77. package/src/components/mini-map/index.ts +0 -686
  78. package/src/components/selection-select/index.ts +0 -387
  79. package/src/dynamic-group/index.ts +0 -774
  80. package/src/dynamic-group/model.ts +0 -580
  81. package/src/dynamic-group/node.ts +0 -288
  82. package/src/dynamic-group/utils.ts +0 -46
  83. package/src/index.less +0 -1
  84. package/src/index.ts +0 -47
  85. package/src/insert-node-in-polyline/edge.ts +0 -175
  86. package/src/insert-node-in-polyline/index.ts +0 -193
  87. package/src/materials/curved-edge/__test__/curved-edge.test.ts +0 -46
  88. package/src/materials/curved-edge/index.ts +0 -217
  89. package/src/materials/group/GroupNode.ts +0 -437
  90. package/src/materials/group/index.ts +0 -542
  91. package/src/materials/node-selection/index.ts +0 -380
  92. package/src/mindmap/fakerRoot.ts +0 -19
  93. package/src/mindmap/index.ts +0 -328
  94. package/src/mindmap/markContent.ts +0 -81
  95. package/src/mindmap/markContentOption.ts +0 -81
  96. package/src/mindmap/markEntity.ts +0 -82
  97. package/src/mindmap/markRoot.ts +0 -83
  98. package/src/mindmap/theme.ts +0 -11
  99. package/src/pool/LaneModel.ts +0 -226
  100. package/src/pool/LaneView.ts +0 -220
  101. package/src/pool/PoolModel.ts +0 -631
  102. package/src/pool/PoolView.ts +0 -75
  103. package/src/pool/constant.ts +0 -19
  104. package/src/pool/index.ts +0 -621
  105. package/src/pool/utils.ts +0 -46
  106. package/src/rect-label-node/RectLabelNodeView.ts +0 -33
  107. package/src/rect-label-node/index.ts +0 -15
  108. package/src/style/index.less +0 -381
  109. package/src/style/raw.ts +0 -328
  110. package/src/tools/auto-layout/index.ts +0 -282
  111. package/src/tools/flow-path/index.ts +0 -233
  112. package/src/tools/label/Label.tsx +0 -357
  113. package/src/tools/label/LabelModel.ts +0 -83
  114. package/src/tools/label/LabelOverlay.tsx +0 -162
  115. package/src/tools/label/algorithm.ts +0 -42
  116. package/src/tools/label/index.ts +0 -479
  117. package/src/tools/label/mediumEditor.ts +0 -121
  118. package/src/tools/label/utils.ts +0 -395
  119. package/src/tools/proximity-connect/index.ts +0 -435
  120. package/src/tools/snapshot/README.md +0 -145
  121. package/src/tools/snapshot/index.ts +0 -701
  122. package/src/tools/snapshot/utils.ts +0 -163
  123. package/src/turbo-adapter/index.ts +0 -212
  124. package/stats.html +0 -4842
  125. 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 }