@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.
- package/README.md +16 -0
- package/package.json +10 -7
- package/.turbo/turbo-build.log +0 -38
- package/CHANGELOG.md +0 -1829
- package/__test__/bpmn-adapter.test.js +0 -227
- package/es/materials/curved-edge/__test__/curved-edge.test.d.ts +0 -1
- package/es/materials/curved-edge/__test__/curved-edge.test.js +0 -18
- package/jest.config.js +0 -198
- package/lib/materials/curved-edge/__test__/curved-edge.test.d.ts +0 -1
- package/lib/materials/curved-edge/__test__/curved-edge.test.js +0 -20
- package/rollup.config.js +0 -52
- package/src/NodeResize/BasicShape/Ellipse.tsx +0 -22
- package/src/NodeResize/BasicShape/Polygon.tsx +0 -24
- package/src/NodeResize/BasicShape/Rect.tsx +0 -44
- package/src/NodeResize/control/Control.tsx +0 -537
- package/src/NodeResize/control/ControlGroup.tsx +0 -76
- package/src/NodeResize/control/Util.ts +0 -206
- package/src/NodeResize/index.ts +0 -26
- package/src/NodeResize/node/DiamondResize.tsx +0 -149
- package/src/NodeResize/node/EllipseResize.tsx +0 -140
- package/src/NodeResize/node/HtmlResize.tsx +0 -125
- package/src/NodeResize/node/RectResize.tsx +0 -126
- package/src/NodeResize/node/index.ts +0 -4
- package/src/bpmn/constant.ts +0 -56
- package/src/bpmn/events/EndEvent.ts +0 -73
- package/src/bpmn/events/StartEvent.ts +0 -52
- package/src/bpmn/events/index.ts +0 -2
- package/src/bpmn/flow/SequenceFlow.ts +0 -25
- package/src/bpmn/flow/index.ts +0 -1
- package/src/bpmn/gateways/ExclusiveGateway.ts +0 -71
- package/src/bpmn/gateways/index.ts +0 -1
- package/src/bpmn/getBpmnId.ts +0 -31
- package/src/bpmn/index.ts +0 -60
- package/src/bpmn/tasks/ServiceTask.ts +0 -63
- package/src/bpmn/tasks/UserTask.ts +0 -64
- package/src/bpmn/tasks/index.ts +0 -2
- package/src/bpmn-adapter/bpmnIds.ts +0 -31
- package/src/bpmn-adapter/index.ts +0 -835
- package/src/bpmn-adapter/json2xml.ts +0 -127
- package/src/bpmn-adapter/xml2json.ts +0 -544
- package/src/bpmn-elements/README.md +0 -223
- package/src/bpmn-elements/__tests__/definition.test.js +0 -72
- package/src/bpmn-elements/index.d.ts +0 -26
- package/src/bpmn-elements/index.ts +0 -107
- package/src/bpmn-elements/presets/Event/EndEventFactory.ts +0 -114
- package/src/bpmn-elements/presets/Event/IntermediateCatchEvent.ts +0 -108
- package/src/bpmn-elements/presets/Event/IntermediateThrowEvent.ts +0 -109
- package/src/bpmn-elements/presets/Event/StartEventFactory.ts +0 -114
- package/src/bpmn-elements/presets/Event/boundaryEventFactory.ts +0 -117
- package/src/bpmn-elements/presets/Event/index.ts +0 -14
- package/src/bpmn-elements/presets/Flow/flow.d.ts +0 -6
- package/src/bpmn-elements/presets/Flow/index.ts +0 -8
- package/src/bpmn-elements/presets/Flow/manhattan.ts +0 -691
- package/src/bpmn-elements/presets/Flow/sequenceFlow.ts +0 -65
- package/src/bpmn-elements/presets/Gateway/gateway.ts +0 -107
- package/src/bpmn-elements/presets/Gateway/index.ts +0 -23
- package/src/bpmn-elements/presets/Pool/Lane.ts +0 -211
- package/src/bpmn-elements/presets/Pool/Pool.ts +0 -284
- package/src/bpmn-elements/presets/Pool/index.ts +0 -89
- package/src/bpmn-elements/presets/Task/index.ts +0 -122
- package/src/bpmn-elements/presets/Task/subProcess.ts +0 -189
- package/src/bpmn-elements/presets/Task/task.ts +0 -193
- package/src/bpmn-elements/presets/icons.ts +0 -155
- package/src/bpmn-elements/utils.ts +0 -52
- package/src/bpmn-elements-adapter/README.md +0 -293
- package/src/bpmn-elements-adapter/__tests__/adapter_in.test.js +0 -528
- package/src/bpmn-elements-adapter/__tests__/adapter_out.test.js +0 -569
- package/src/bpmn-elements-adapter/constant.ts +0 -76
- package/src/bpmn-elements-adapter/index.ts +0 -1134
- package/src/bpmn-elements-adapter/json2xml.ts +0 -105
- package/src/bpmn-elements-adapter/xml2json.ts +0 -542
- package/src/components/context-menu/index.ts +0 -253
- package/src/components/control/index.ts +0 -155
- package/src/components/dnd-panel/index.ts +0 -137
- package/src/components/highlight/index.ts +0 -227
- package/src/components/menu/index.ts +0 -748
- package/src/components/mini-map/index.ts +0 -686
- package/src/components/selection-select/index.ts +0 -387
- package/src/dynamic-group/index.ts +0 -774
- package/src/dynamic-group/model.ts +0 -580
- package/src/dynamic-group/node.ts +0 -288
- package/src/dynamic-group/utils.ts +0 -46
- package/src/index.less +0 -1
- package/src/index.ts +0 -47
- package/src/insert-node-in-polyline/edge.ts +0 -175
- package/src/insert-node-in-polyline/index.ts +0 -193
- package/src/materials/curved-edge/__test__/curved-edge.test.ts +0 -46
- package/src/materials/curved-edge/index.ts +0 -217
- package/src/materials/group/GroupNode.ts +0 -437
- package/src/materials/group/index.ts +0 -542
- package/src/materials/node-selection/index.ts +0 -380
- package/src/mindmap/fakerRoot.ts +0 -19
- package/src/mindmap/index.ts +0 -328
- package/src/mindmap/markContent.ts +0 -81
- package/src/mindmap/markContentOption.ts +0 -81
- package/src/mindmap/markEntity.ts +0 -82
- package/src/mindmap/markRoot.ts +0 -83
- package/src/mindmap/theme.ts +0 -11
- package/src/pool/LaneModel.ts +0 -226
- package/src/pool/LaneView.ts +0 -220
- package/src/pool/PoolModel.ts +0 -631
- package/src/pool/PoolView.ts +0 -75
- package/src/pool/constant.ts +0 -19
- package/src/pool/index.ts +0 -621
- package/src/pool/utils.ts +0 -46
- package/src/rect-label-node/RectLabelNodeView.ts +0 -33
- package/src/rect-label-node/index.ts +0 -15
- package/src/style/index.less +0 -381
- package/src/style/raw.ts +0 -328
- package/src/tools/auto-layout/index.ts +0 -282
- package/src/tools/flow-path/index.ts +0 -233
- package/src/tools/label/Label.tsx +0 -357
- package/src/tools/label/LabelModel.ts +0 -83
- package/src/tools/label/LabelOverlay.tsx +0 -162
- package/src/tools/label/algorithm.ts +0 -42
- package/src/tools/label/index.ts +0 -479
- package/src/tools/label/mediumEditor.ts +0 -121
- package/src/tools/label/utils.ts +0 -395
- package/src/tools/proximity-connect/index.ts +0 -435
- package/src/tools/snapshot/README.md +0 -145
- package/src/tools/snapshot/index.ts +0 -701
- package/src/tools/snapshot/utils.ts +0 -163
- package/src/turbo-adapter/index.ts +0 -212
- package/stats.html +0 -4842
- 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 }
|