@logicflow/extension 2.0.0 → 2.0.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/.turbo/turbo-build.log +387 -389
- package/CHANGELOG.md +122 -71
- package/dist/index.css +0 -63
- package/dist/index.min.js +6 -6
- package/es/bpmn-adapter/index.js +1 -1
- package/es/bpmn-adapter/index.js.map +1 -1
- package/es/bpmn-elements-adapter/index.js +1 -1
- package/es/bpmn-elements-adapter/index.js.map +1 -1
- package/es/components/highlight/index.js +4 -0
- package/es/components/highlight/index.js.map +1 -1
- package/es/components/selection-select/index.js +6 -2
- package/es/components/selection-select/index.js.map +1 -1
- package/es/dynamic-group/index.js +2 -3
- package/es/dynamic-group/index.js.map +1 -1
- package/es/dynamic-group/model.d.ts +1 -1
- package/es/dynamic-group/model.js +12 -9
- package/es/dynamic-group/model.js.map +1 -1
- package/es/dynamic-group/node.js +19 -0
- package/es/dynamic-group/node.js.map +1 -1
- package/es/dynamic-group/utils.d.ts +5 -3
- package/es/dynamic-group/utils.js +14 -7
- package/es/dynamic-group/utils.js.map +1 -1
- package/es/index.css +0 -63
- package/es/index.d.ts +1 -0
- package/es/index.js +1 -0
- package/es/index.js.map +1 -1
- package/es/style/index.css +0 -63
- package/es/style/index.less +0 -73
- package/es/style/raw.d.ts +1 -1
- package/es/style/raw.js +1 -1
- package/es/style/raw.js.map +1 -1
- package/es/tools/label/LabelOverlay.js.map +1 -1
- package/es/tools/label/index.d.ts +1 -0
- package/es/tools/label/index.js +10 -0
- package/es/tools/label/index.js.map +1 -1
- package/es/tools/label/style.css +63 -0
- package/es/tools/label/style.less +71 -0
- package/es/tools/snapshot/index.d.ts +9 -9
- package/es/tools/snapshot/index.js +15 -15
- package/es/tools/snapshot/index.js.map +1 -1
- package/lib/bpmn-adapter/index.js +1 -1
- package/lib/bpmn-adapter/index.js.map +1 -1
- package/lib/bpmn-elements-adapter/index.js +1 -1
- package/lib/bpmn-elements-adapter/index.js.map +1 -1
- package/lib/components/highlight/index.js +4 -0
- package/lib/components/highlight/index.js.map +1 -1
- package/lib/components/selection-select/index.js +6 -2
- package/lib/components/selection-select/index.js.map +1 -1
- package/lib/dynamic-group/index.js +2 -3
- package/lib/dynamic-group/index.js.map +1 -1
- package/lib/dynamic-group/model.d.ts +1 -1
- package/lib/dynamic-group/model.js +12 -9
- package/lib/dynamic-group/model.js.map +1 -1
- package/lib/dynamic-group/node.js +19 -0
- package/lib/dynamic-group/node.js.map +1 -1
- package/lib/dynamic-group/utils.d.ts +5 -3
- package/lib/dynamic-group/utils.js +14 -7
- package/lib/dynamic-group/utils.js.map +1 -1
- package/lib/index.css +0 -63
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/style/index.css +0 -63
- package/lib/style/index.less +0 -73
- package/lib/style/raw.d.ts +1 -1
- package/lib/style/raw.js +1 -1
- package/lib/style/raw.js.map +1 -1
- package/lib/tools/label/LabelOverlay.js.map +1 -1
- package/lib/tools/label/index.d.ts +1 -0
- package/lib/tools/label/index.js +10 -0
- package/lib/tools/label/index.js.map +1 -1
- package/lib/tools/label/style.css +63 -0
- package/lib/tools/label/style.less +71 -0
- package/lib/tools/snapshot/index.d.ts +9 -9
- package/lib/tools/snapshot/index.js +15 -15
- package/lib/tools/snapshot/index.js.map +1 -1
- package/package.json +7 -4
- package/rollup.config.js +17 -1
- package/src/bpmn-adapter/index.ts +1 -1
- package/src/bpmn-elements-adapter/index.ts +1 -1
- package/src/components/highlight/index.ts +2 -0
- package/src/components/selection-select/index.ts +6 -2
- package/src/dynamic-group/index.ts +2 -3
- package/src/dynamic-group/model.ts +15 -9
- package/src/dynamic-group/node.ts +25 -1
- package/src/dynamic-group/utils.ts +20 -7
- package/src/index.ts +2 -0
- package/src/style/index.less +0 -73
- package/src/style/raw.ts +1 -64
- package/src/tools/label/LabelOverlay.tsx +0 -1
- package/src/tools/label/index.ts +14 -5
- package/src/tools/label/style.less +71 -0
- package/src/tools/snapshot/index.ts +19 -15
|
@@ -429,7 +429,7 @@ function getLfEdges(value, bpmnEdges) {
|
|
|
429
429
|
|
|
430
430
|
function getEdgeConfig(edgeValue, processValue): EdgeConfig {
|
|
431
431
|
let text
|
|
432
|
-
const textVal = processValue['-name']
|
|
432
|
+
const textVal = `${processValue['-name']}`
|
|
433
433
|
if (textVal) {
|
|
434
434
|
const textBounds = edgeValue['bpmndi:BPMNLabel']['dc:Bounds']
|
|
435
435
|
// 如果边文本换行,则其偏移量应该是最长一行的位置
|
|
@@ -923,7 +923,7 @@ function getLfEdges(value: any, bpmnEdges: any) {
|
|
|
923
923
|
|
|
924
924
|
function getEdgeConfig(edgeValue: any, processValue: any) {
|
|
925
925
|
let text
|
|
926
|
-
const textVal = processValue['-name']
|
|
926
|
+
const textVal = `${processValue['-name']}`
|
|
927
927
|
if (textVal) {
|
|
928
928
|
const textBounds = edgeValue['bpmndi:BPMNLabel']['dc:Bounds']
|
|
929
929
|
// 如果边文本换行,则其偏移量应该是最长一行的位置
|
|
@@ -14,6 +14,7 @@ const getNodePath = (node, lf: LogicFlow) => {
|
|
|
14
14
|
const outgoingPaths: any[] = []
|
|
15
15
|
|
|
16
16
|
const getIncomingPaths = (curNode, path, prevNode?: BaseNodeModel) => {
|
|
17
|
+
if (!curNode) return
|
|
17
18
|
if (prevNode) {
|
|
18
19
|
// * 上个节点和当前节点中间边
|
|
19
20
|
path.unshift(
|
|
@@ -48,6 +49,7 @@ const getNodePath = (node, lf: LogicFlow) => {
|
|
|
48
49
|
|
|
49
50
|
// * 同上逻辑
|
|
50
51
|
const getOutgoingPaths = (curNode, path, prevNode?: BaseNodeModel) => {
|
|
52
|
+
if (!curNode) return
|
|
51
53
|
if (prevNode) {
|
|
52
54
|
path.push(
|
|
53
55
|
...lf
|
|
@@ -165,9 +165,13 @@ export class SelectionSelect {
|
|
|
165
165
|
this.isWholeNode,
|
|
166
166
|
true,
|
|
167
167
|
)
|
|
168
|
-
const { dynamicGroup } = this.lf.graphModel
|
|
168
|
+
const { dynamicGroup, group } = this.lf.graphModel
|
|
169
169
|
elements.forEach((element) => {
|
|
170
|
-
//
|
|
170
|
+
// 如果节点属于分组,则不选中节点,此处兼容旧版 Group 插件
|
|
171
|
+
if (!group || !group.getNodeGroup(element.id)) {
|
|
172
|
+
this.lf.selectElementById(element.id, true)
|
|
173
|
+
}
|
|
174
|
+
// 如果节点属于动态分组,则不不选中节点
|
|
171
175
|
if (!dynamicGroup || !dynamicGroup.getGroupByNodeId(element.id)) {
|
|
172
176
|
this.lf.selectElementById(element.id, true)
|
|
173
177
|
}
|
|
@@ -506,8 +506,8 @@ export class DynamicGroup {
|
|
|
506
506
|
|
|
507
507
|
if (groupModel && groupModel.isRestrict) {
|
|
508
508
|
// 如果移动的节点存在与分组中,且这个分组禁止子节点移出去
|
|
509
|
-
const
|
|
510
|
-
return isAllowMoveTo(
|
|
509
|
+
const groupBounds = groupModel.getBounds()
|
|
510
|
+
return isAllowMoveTo(groupBounds, model, deltaX, deltaY)
|
|
511
511
|
}
|
|
512
512
|
|
|
513
513
|
return true
|
|
@@ -523,7 +523,6 @@ export class DynamicGroup {
|
|
|
523
523
|
lf.on('graph:updated', ({ data }) => console.log('data', data))
|
|
524
524
|
|
|
525
525
|
lf.on('group:add-node', ({ data }) => console.log('group:add-node', data))
|
|
526
|
-
// lf.eventCenter.on('node:resize', this.onGroupResize)
|
|
527
526
|
|
|
528
527
|
// https://github.com/didi/LogicFlow/issues/1346
|
|
529
528
|
// 重写 addElements() 方法,在 addElements() 原有基础上增加对 group 内部所有 nodes 和 edges 的复制功能
|
|
@@ -95,12 +95,13 @@ export class DynamicGroupNodeModel extends RectNodeModel<IGroupNodeProperties> {
|
|
|
95
95
|
@observable groupAddable: boolean = false
|
|
96
96
|
// 缩放或旋转容器时,是否缩放或旋转组内节点
|
|
97
97
|
@observable transformWidthContainer: boolean = true
|
|
98
|
-
childrenLastCollapseStateDict:
|
|
98
|
+
childrenLastCollapseStateDict: Map<string, boolean> = new Map()
|
|
99
99
|
|
|
100
100
|
constructor(data: NodeConfig<IGroupNodeProperties>, graphModel: GraphModel) {
|
|
101
101
|
super(data, graphModel)
|
|
102
|
-
this.
|
|
102
|
+
this.childrenLastCollapseStateDict = new Map()
|
|
103
103
|
|
|
104
|
+
this.initNodeData(data)
|
|
104
105
|
this.setAttributes()
|
|
105
106
|
}
|
|
106
107
|
|
|
@@ -118,6 +119,7 @@ export class DynamicGroupNodeModel extends RectNodeModel<IGroupNodeProperties> {
|
|
|
118
119
|
isCollapsed,
|
|
119
120
|
zIndex,
|
|
120
121
|
isRestrict,
|
|
122
|
+
autoResize,
|
|
121
123
|
autoToFront,
|
|
122
124
|
} = data.properties ?? {}
|
|
123
125
|
|
|
@@ -137,6 +139,7 @@ export class DynamicGroupNodeModel extends RectNodeModel<IGroupNodeProperties> {
|
|
|
137
139
|
this.collapsedHeight = collapsedHeight ?? DEFAULT_GROUP_COLLAPSE_HEIGHT
|
|
138
140
|
|
|
139
141
|
this.isRestrict = isRestrict ?? false
|
|
142
|
+
this.autoResize = autoResize ?? false
|
|
140
143
|
this.collapsible = collapsible ?? true
|
|
141
144
|
this.autoToFront = autoToFront ?? false
|
|
142
145
|
|
|
@@ -217,11 +220,12 @@ export class DynamicGroupNodeModel extends RectNodeModel<IGroupNodeProperties> {
|
|
|
217
220
|
|
|
218
221
|
// step 2
|
|
219
222
|
let allRelatedEdges = [...this.incoming.edges, ...this.outgoing.edges]
|
|
220
|
-
console.log('this -->>', this)
|
|
221
|
-
console.log('this.children -->>', this.children)
|
|
222
223
|
const childrenArr = Array.from(this.children)
|
|
224
|
+
|
|
223
225
|
forEach(childrenArr, (elementId) => {
|
|
224
|
-
|
|
226
|
+
// FIX: 当使用 graphModel.getElement 获取元素时,会因为
|
|
227
|
+
// const model = this.graphModel.getElement(elementId)
|
|
228
|
+
const model = this.graphModel.elementsModelMap.get(elementId)
|
|
225
229
|
|
|
226
230
|
if (model) {
|
|
227
231
|
// TODO: ??? 普通节点有这个属性吗?确认这个代码的意义
|
|
@@ -242,7 +246,7 @@ export class DynamicGroupNodeModel extends RectNodeModel<IGroupNodeProperties> {
|
|
|
242
246
|
if (!collapse) {
|
|
243
247
|
// 当 parent 准备展开时,children 的值应该恢复到折叠前的状态
|
|
244
248
|
const lastCollapseStatus =
|
|
245
|
-
this.childrenLastCollapseStateDict
|
|
249
|
+
this.childrenLastCollapseStateDict?.get(elementId)
|
|
246
250
|
if (
|
|
247
251
|
lastCollapseStatus !== undefined &&
|
|
248
252
|
lastCollapseStatus !== model.isCollapsed
|
|
@@ -255,7 +259,7 @@ export class DynamicGroupNodeModel extends RectNodeModel<IGroupNodeProperties> {
|
|
|
255
259
|
}
|
|
256
260
|
}
|
|
257
261
|
|
|
258
|
-
this.childrenLastCollapseStateDict
|
|
262
|
+
this.childrenLastCollapseStateDict?.set(elementId, !!collapseStatus)
|
|
259
263
|
model.visible = !collapse
|
|
260
264
|
|
|
261
265
|
// 判断,如果是节点时,才去读取节点的 incoming 和 outgoing
|
|
@@ -360,13 +364,15 @@ export class DynamicGroupNodeModel extends RectNodeModel<IGroupNodeProperties> {
|
|
|
360
364
|
graphModel.deleteEdgeById(edge.id)
|
|
361
365
|
}
|
|
362
366
|
// 考虑目标节点也属于分组的情况
|
|
363
|
-
let targetNodeGroup =
|
|
367
|
+
let targetNodeGroup =
|
|
368
|
+
graphModel.dynamicGroup.getGroupByNodeId(targetNodeId)
|
|
364
369
|
if (!targetNodeGroup) {
|
|
365
370
|
targetNodeGroup = graphModel.getNodeModelById(targetNodeId)
|
|
366
371
|
}
|
|
367
372
|
|
|
368
373
|
// 考虑源节点也属于分组的情况
|
|
369
|
-
let sourceNodeGroup =
|
|
374
|
+
let sourceNodeGroup =
|
|
375
|
+
graphModel.dynamicGroup.getGroupByNodeId(sourceNodeId)
|
|
370
376
|
if (!sourceNodeGroup) {
|
|
371
377
|
sourceNodeGroup = graphModel.getNodeModelById(sourceNodeId)
|
|
372
378
|
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import { GraphModel, h, RectNode } from '@logicflow/core'
|
|
1
|
+
import LogicFlow, { GraphModel, h, RectNode } from '@logicflow/core'
|
|
2
2
|
import { forEach } from 'lodash-es'
|
|
3
3
|
import { DynamicGroupNodeModel } from './model'
|
|
4
4
|
import { handleResize } from '@logicflow/core/es/util/resize'
|
|
5
5
|
|
|
6
|
+
import Position = LogicFlow.Position
|
|
7
|
+
import { rotatePointAroundCenter } from '../tools/label/utils'
|
|
8
|
+
|
|
6
9
|
export interface IDynamicGroupNodeProps {
|
|
7
10
|
model: DynamicGroupNodeModel
|
|
8
11
|
graphModel: GraphModel
|
|
@@ -17,12 +20,32 @@ export class DynamicGroupNode<
|
|
|
17
20
|
const { model: curGroup, graphModel } = this.props
|
|
18
21
|
const { eventCenter } = graphModel
|
|
19
22
|
|
|
23
|
+
const childrenPositionMap: Map<string, Position> = new Map()
|
|
24
|
+
|
|
20
25
|
// 在 group 旋转时,对组内的所有子节点也进行对应的旋转计算
|
|
21
26
|
eventCenter.on('node:rotate', ({ model }) => {
|
|
27
|
+
// DONE: 目前操作是对分组内节点以节点中心旋转节点本身,而按照正常逻辑,应该是以分组中心,旋转节点(跟 Label 旋转操作逻辑一致)
|
|
22
28
|
if (model.id === curGroup.id) {
|
|
29
|
+
const center = { x: curGroup.x, y: curGroup.y }
|
|
23
30
|
forEach(Array.from(curGroup.children), (childId) => {
|
|
24
31
|
const child = graphModel.getNodeModelById(childId)
|
|
32
|
+
|
|
25
33
|
if (child) {
|
|
34
|
+
let point: Position = { x: child.x, y: child.y }
|
|
35
|
+
if (childrenPositionMap.has(child.id)) {
|
|
36
|
+
point = childrenPositionMap.get(child.id)!
|
|
37
|
+
} else {
|
|
38
|
+
childrenPositionMap.set(child.id, point)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 弧度转角度
|
|
42
|
+
let theta = model.rotate * (180 / Math.PI)
|
|
43
|
+
if (theta < 0) theta += 360
|
|
44
|
+
const radian = theta * (Math.PI / 180)
|
|
45
|
+
|
|
46
|
+
const newPoint = rotatePointAroundCenter(point, center, radian)
|
|
47
|
+
|
|
48
|
+
child.moveTo(newPoint.x, newPoint.y)
|
|
26
49
|
child.rotate = model.rotate
|
|
27
50
|
}
|
|
28
51
|
})
|
|
@@ -31,6 +54,7 @@ export class DynamicGroupNode<
|
|
|
31
54
|
|
|
32
55
|
// 在 group 缩放时,对组内的所有子节点也进行对应的缩放计算
|
|
33
56
|
eventCenter.on('node:resize', ({ deltaX, deltaY, index, model }) => {
|
|
57
|
+
// TODO: 目前 Resize 的比例值有问题,导致缩放时,节点会变形,需要修复
|
|
34
58
|
if (model.id === curGroup.id) {
|
|
35
59
|
forEach(Array.from(curGroup.children), (childId) => {
|
|
36
60
|
const child = graphModel.getNodeModelById(childId)
|
|
@@ -19,15 +19,28 @@ export function isBoundsInGroup(bounds: BoxBoundsPoint, group: BaseNodeModel) {
|
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* 判断 bounds 是否可以移动到下一个范围
|
|
22
|
-
* @param
|
|
23
|
-
* @param
|
|
22
|
+
* @param groupBounds
|
|
23
|
+
* @param node
|
|
24
|
+
* @param deltaX
|
|
25
|
+
* @param deltaY
|
|
24
26
|
*/
|
|
25
|
-
export function isAllowMoveTo(
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
export function isAllowMoveTo(
|
|
28
|
+
groupBounds: BoxBoundsPoint,
|
|
29
|
+
node: BaseNodeModel,
|
|
30
|
+
deltaX: number,
|
|
31
|
+
deltaY: number,
|
|
32
|
+
) {
|
|
33
|
+
const { minX, minY, maxX, maxY } = groupBounds
|
|
34
|
+
const { x, y, width, height } = node
|
|
35
|
+
|
|
36
|
+
// DONE: 计算节点坐标 (x, y) 可移动的范围,并判断 x + deltaX, y + deltaY 是否在范围内
|
|
37
|
+
const allowMoveMinX = minX + width / 2
|
|
38
|
+
const allowMoveMinY = minY + height / 2
|
|
39
|
+
const allowMoveMaxX = maxX - width / 2
|
|
40
|
+
const allowMoveMaxY = maxY - height / 2
|
|
28
41
|
|
|
29
42
|
return {
|
|
30
|
-
x:
|
|
31
|
-
y:
|
|
43
|
+
x: x + deltaX >= allowMoveMinX && x + deltaX <= allowMoveMaxX,
|
|
44
|
+
y: y + deltaY >= allowMoveMinY && y + deltaY <= allowMoveMaxY,
|
|
32
45
|
}
|
|
33
46
|
}
|
package/src/index.ts
CHANGED
package/src/style/index.less
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
@import url('medium-editor/dist/css/medium-editor.min.css');
|
|
2
|
-
//@import url('medium-editor/dist/css/themes/bootstrap.min.css');
|
|
3
|
-
@import url('medium-editor/dist/css/themes/beagle.min.css');
|
|
4
|
-
@import url('vanilla-picker/dist/vanilla-picker.csp.css');
|
|
5
|
-
|
|
6
1
|
.lf-control {
|
|
7
2
|
position: absolute;
|
|
8
3
|
top: 0;
|
|
@@ -258,71 +253,3 @@
|
|
|
258
253
|
.lf-mindmap_addIcon {
|
|
259
254
|
margin-top: 10px;
|
|
260
255
|
}
|
|
261
|
-
|
|
262
|
-
/* label */
|
|
263
|
-
.lf-label-overlay {
|
|
264
|
-
width: 0;
|
|
265
|
-
height: 0;
|
|
266
|
-
overflow: visible;
|
|
267
|
-
|
|
268
|
-
.lf-label-editor {
|
|
269
|
-
//box-sizing: content-box;
|
|
270
|
-
padding: 4px;
|
|
271
|
-
background: #fff;
|
|
272
|
-
border-radius: 5px;
|
|
273
|
-
|
|
274
|
-
&-container {
|
|
275
|
-
position: absolute;
|
|
276
|
-
display: flex;
|
|
277
|
-
align-items: center;
|
|
278
|
-
justify-content: center;
|
|
279
|
-
overflow: visible;
|
|
280
|
-
text-align: center;
|
|
281
|
-
|
|
282
|
-
p {
|
|
283
|
-
margin: 0;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
&-dragging {
|
|
288
|
-
cursor: move;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
&-editing {
|
|
292
|
-
border: 2px solid #275dc5;
|
|
293
|
-
outline: none;
|
|
294
|
-
cursor: text;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
&-hover {
|
|
298
|
-
border: 2px dashed #acacac;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// textOverflowMode
|
|
302
|
-
&-clip {
|
|
303
|
-
width: 100px; /* 根据需要调整宽度 */
|
|
304
|
-
overflow: hidden;
|
|
305
|
-
white-space: nowrap;
|
|
306
|
-
text-overflow: clip;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
&-ellipsis {
|
|
310
|
-
width: 100px; /* 根据需要调整宽度 */
|
|
311
|
-
overflow: hidden;
|
|
312
|
-
white-space: nowrap;
|
|
313
|
-
text-overflow: ellipsis;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
&-wrap {
|
|
317
|
-
width: 100px; /* 根据需要调整宽度 */
|
|
318
|
-
white-space: normal;
|
|
319
|
-
overflow-wrap: break-word; /* 允许单词内换行 */
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
&-nowrap {
|
|
323
|
-
width: 100px; /* 根据需要调整宽度 */
|
|
324
|
-
overflow: visible;
|
|
325
|
-
white-space: nowrap;
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
}
|
package/src/style/raw.ts
CHANGED
|
@@ -4,10 +4,7 @@
|
|
|
4
4
|
* Auto generated file, do not modify it!
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
export const content =
|
|
8
|
-
@import url('medium-editor/dist/css/themes/beagle.min.css');
|
|
9
|
-
@import url('vanilla-picker/dist/vanilla-picker.csp.css');
|
|
10
|
-
.lf-control {
|
|
7
|
+
export const content = `.lf-control {
|
|
11
8
|
position: absolute;
|
|
12
9
|
top: 0;
|
|
13
10
|
right: 10px;
|
|
@@ -222,64 +219,4 @@ export const content = `@import url('medium-editor/dist/css/medium-editor.min.cs
|
|
|
222
219
|
.lf-mindmap_addIcon {
|
|
223
220
|
margin-top: 10px;
|
|
224
221
|
}
|
|
225
|
-
/* label */
|
|
226
|
-
.lf-label-overlay {
|
|
227
|
-
width: 0;
|
|
228
|
-
height: 0;
|
|
229
|
-
overflow: visible;
|
|
230
|
-
}
|
|
231
|
-
.lf-label-overlay .lf-label-editor {
|
|
232
|
-
padding: 4px;
|
|
233
|
-
background: #fff;
|
|
234
|
-
border-radius: 5px;
|
|
235
|
-
}
|
|
236
|
-
.lf-label-overlay .lf-label-editor-container {
|
|
237
|
-
position: absolute;
|
|
238
|
-
display: flex;
|
|
239
|
-
align-items: center;
|
|
240
|
-
justify-content: center;
|
|
241
|
-
overflow: visible;
|
|
242
|
-
text-align: center;
|
|
243
|
-
}
|
|
244
|
-
.lf-label-overlay .lf-label-editor-container p {
|
|
245
|
-
margin: 0;
|
|
246
|
-
}
|
|
247
|
-
.lf-label-overlay .lf-label-editor-dragging {
|
|
248
|
-
cursor: move;
|
|
249
|
-
}
|
|
250
|
-
.lf-label-overlay .lf-label-editor-editing {
|
|
251
|
-
border: 2px solid #275dc5;
|
|
252
|
-
outline: none;
|
|
253
|
-
cursor: text;
|
|
254
|
-
}
|
|
255
|
-
.lf-label-overlay .lf-label-editor-hover {
|
|
256
|
-
border: 2px dashed #acacac;
|
|
257
|
-
}
|
|
258
|
-
.lf-label-overlay .lf-label-editor-clip {
|
|
259
|
-
width: 100px;
|
|
260
|
-
/* 根据需要调整宽度 */
|
|
261
|
-
overflow: hidden;
|
|
262
|
-
white-space: nowrap;
|
|
263
|
-
text-overflow: clip;
|
|
264
|
-
}
|
|
265
|
-
.lf-label-overlay .lf-label-editor-ellipsis {
|
|
266
|
-
width: 100px;
|
|
267
|
-
/* 根据需要调整宽度 */
|
|
268
|
-
overflow: hidden;
|
|
269
|
-
white-space: nowrap;
|
|
270
|
-
text-overflow: ellipsis;
|
|
271
|
-
}
|
|
272
|
-
.lf-label-overlay .lf-label-editor-wrap {
|
|
273
|
-
width: 100px;
|
|
274
|
-
/* 根据需要调整宽度 */
|
|
275
|
-
white-space: normal;
|
|
276
|
-
overflow-wrap: break-word;
|
|
277
|
-
/* 允许单词内换行 */
|
|
278
|
-
}
|
|
279
|
-
.lf-label-overlay .lf-label-editor-nowrap {
|
|
280
|
-
width: 100px;
|
|
281
|
-
/* 根据需要调整宽度 */
|
|
282
|
-
overflow: visible;
|
|
283
|
-
white-space: nowrap;
|
|
284
|
-
}
|
|
285
222
|
`
|
package/src/tools/label/index.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import LogicFlow, { createUuid, GraphModel, TextMode } from '@logicflow/core'
|
|
2
2
|
import { cloneDeep, forEach, isArray, isObject, map } from 'lodash-es'
|
|
3
3
|
import LabelOverlay, { LabelConfigType } from './LabelOverlay'
|
|
4
|
+
import {
|
|
5
|
+
BBoxInfo,
|
|
6
|
+
calcPointAfterResize,
|
|
7
|
+
rotatePointAroundCenter,
|
|
8
|
+
} from './utils'
|
|
9
|
+
import './style.less'
|
|
4
10
|
|
|
5
11
|
import Position = LogicFlow.Position
|
|
6
12
|
import NodeData = LogicFlow.NodeData
|
|
@@ -8,11 +14,6 @@ import EdgeData = LogicFlow.EdgeData
|
|
|
8
14
|
import Extension = LogicFlow.Extension
|
|
9
15
|
import LabelConfig = LogicFlow.LabelConfig
|
|
10
16
|
import GraphElement = LogicFlow.GraphElement
|
|
11
|
-
import {
|
|
12
|
-
BBoxInfo,
|
|
13
|
-
calcPointAfterResize,
|
|
14
|
-
rotatePointAroundCenter,
|
|
15
|
-
} from './utils'
|
|
16
17
|
|
|
17
18
|
// 类型定义,如果 isMultiple 为 true 的话,maxCount 为数值且大于 1
|
|
18
19
|
export type ILabelOptions = {
|
|
@@ -333,6 +334,14 @@ export class Label implements Extension {
|
|
|
333
334
|
|
|
334
335
|
model.setProperty('_label', newLabelConfig)
|
|
335
336
|
})
|
|
337
|
+
// 监听元素新增事件,元素label格式化
|
|
338
|
+
eventCenter.on('node:add,edge:add', ({ data }) => {
|
|
339
|
+
const element = graphModel.getElement(data.id)
|
|
340
|
+
if (element) {
|
|
341
|
+
const formatedLabel = this.formatConfig(graphModel, data)
|
|
342
|
+
element.setProperty('_label', formatedLabel)
|
|
343
|
+
}
|
|
344
|
+
})
|
|
336
345
|
}
|
|
337
346
|
|
|
338
347
|
/**
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
@import url('medium-editor/dist/css/medium-editor.min.css');
|
|
2
|
+
@import url('medium-editor/dist/css/themes/beagle.min.css');
|
|
3
|
+
@import url('vanilla-picker/dist/vanilla-picker.csp.css');
|
|
4
|
+
|
|
5
|
+
/* label */
|
|
6
|
+
.lf-label-overlay {
|
|
7
|
+
width: 0;
|
|
8
|
+
height: 0;
|
|
9
|
+
overflow: visible;
|
|
10
|
+
|
|
11
|
+
.lf-label-editor {
|
|
12
|
+
//box-sizing: content-box;
|
|
13
|
+
padding: 4px;
|
|
14
|
+
background: #fff;
|
|
15
|
+
border-radius: 5px;
|
|
16
|
+
|
|
17
|
+
&-container {
|
|
18
|
+
position: absolute;
|
|
19
|
+
display: flex;
|
|
20
|
+
align-items: center;
|
|
21
|
+
justify-content: center;
|
|
22
|
+
overflow: visible;
|
|
23
|
+
text-align: center;
|
|
24
|
+
|
|
25
|
+
p {
|
|
26
|
+
margin: 0;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
&-dragging {
|
|
31
|
+
cursor: move;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&-editing {
|
|
35
|
+
border: 2px solid #275dc5;
|
|
36
|
+
outline: none;
|
|
37
|
+
cursor: text;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
&-hover {
|
|
41
|
+
border: 2px dashed #acacac;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// textOverflowMode
|
|
45
|
+
&-clip {
|
|
46
|
+
width: 100px; /* 根据需要调整宽度 */
|
|
47
|
+
overflow: hidden;
|
|
48
|
+
white-space: nowrap;
|
|
49
|
+
text-overflow: clip;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
&-ellipsis {
|
|
53
|
+
width: 100px; /* 根据需要调整宽度 */
|
|
54
|
+
overflow: hidden;
|
|
55
|
+
white-space: nowrap;
|
|
56
|
+
text-overflow: ellipsis;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
&-wrap {
|
|
60
|
+
width: 100px; /* 根据需要调整宽度 */
|
|
61
|
+
white-space: normal;
|
|
62
|
+
overflow-wrap: break-word; /* 允许单词内换行 */
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
&-nowrap {
|
|
66
|
+
width: 100px; /* 根据需要调整宽度 */
|
|
67
|
+
overflow: visible;
|
|
68
|
+
white-space: nowrap;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -4,7 +4,7 @@ import { updateImageSource, copyCanvas } from './utils'
|
|
|
4
4
|
// 导出图片
|
|
5
5
|
export type ToImageOptions = {
|
|
6
6
|
/**
|
|
7
|
-
* 导出图片的格式,可选值为:`png`、`webp`、`
|
|
7
|
+
* 导出图片的格式,可选值为:`png`、`webp`、`jpeg`、`svg`,默认值为 `png`
|
|
8
8
|
*/
|
|
9
9
|
fileType?: string
|
|
10
10
|
/**
|
|
@@ -63,17 +63,17 @@ export class Snapshot {
|
|
|
63
63
|
|
|
64
64
|
// TODO: 设置fileType为gif但是下载下来的还是png
|
|
65
65
|
// TODO: 完善静默模式不允许添加、操作元素能力
|
|
66
|
-
/*
|
|
66
|
+
/* 导出画布快照 */
|
|
67
67
|
lf.getSnapshot = async (
|
|
68
68
|
fileName?: string,
|
|
69
69
|
toImageOptions?: ToImageOptions,
|
|
70
70
|
) => await this.getSnapshot(fileName, toImageOptions)
|
|
71
71
|
|
|
72
|
-
/* 获取Blob
|
|
72
|
+
/* 获取Blob对象 */
|
|
73
73
|
lf.getSnapshotBlob = async (backgroundColor?: string, fileType?: string) =>
|
|
74
74
|
await this.getSnapshotBlob(backgroundColor, fileType)
|
|
75
75
|
|
|
76
|
-
/* 获取Base64
|
|
76
|
+
/* 获取Base64对象 */
|
|
77
77
|
lf.getSnapshotBase64 = async (
|
|
78
78
|
backgroundColor?: string,
|
|
79
79
|
fileType?: string,
|
|
@@ -144,7 +144,7 @@ export class Snapshot {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
/**
|
|
147
|
-
*
|
|
147
|
+
* 导出画布:导出前的处理画布工作,局部渲染模式处理、静默模式处理
|
|
148
148
|
* @param fileName
|
|
149
149
|
* @param toImageOptions
|
|
150
150
|
*/
|
|
@@ -195,7 +195,7 @@ export class Snapshot {
|
|
|
195
195
|
} else {
|
|
196
196
|
this.getCanvasData(svg, toImageOptions ?? {}).then(
|
|
197
197
|
(canvas: HTMLCanvasElement) => {
|
|
198
|
-
// canvas元素 => url image/octet-stream: 确保所有浏览器都能正常下载
|
|
198
|
+
// canvas元素 => base64 url image/octet-stream: 确保所有浏览器都能正常下载
|
|
199
199
|
const imgUrl = canvas
|
|
200
200
|
.toDataURL(`image/${fileType}`, quality)
|
|
201
201
|
.replace(`image/${fileType}`, 'image/octet-stream')
|
|
@@ -211,7 +211,7 @@ export class Snapshot {
|
|
|
211
211
|
* @param fileType
|
|
212
212
|
* @returns
|
|
213
213
|
*/
|
|
214
|
-
|
|
214
|
+
async getSnapshotBase64(
|
|
215
215
|
backgroundColor?: string,
|
|
216
216
|
fileType?: string,
|
|
217
217
|
): Promise<SnapshotResponse> {
|
|
@@ -238,7 +238,7 @@ export class Snapshot {
|
|
|
238
238
|
* @param fileType
|
|
239
239
|
* @returns
|
|
240
240
|
*/
|
|
241
|
-
|
|
241
|
+
async getSnapshotBlob(
|
|
242
242
|
backgroundColor?: string,
|
|
243
243
|
fileType?: string,
|
|
244
244
|
): Promise<SnapshotResponse> {
|
|
@@ -264,7 +264,7 @@ export class Snapshot {
|
|
|
264
264
|
}
|
|
265
265
|
|
|
266
266
|
/**
|
|
267
|
-
* 获取脚本css样式
|
|
267
|
+
* 获取脚本 css 样式
|
|
268
268
|
* @returns
|
|
269
269
|
*/
|
|
270
270
|
private getClassRules(): string {
|
|
@@ -273,7 +273,7 @@ export class Snapshot {
|
|
|
273
273
|
const { styleSheets } = document
|
|
274
274
|
for (let i = 0; i < styleSheets.length; i++) {
|
|
275
275
|
const sheet = styleSheets[i]
|
|
276
|
-
// 这里是为了过滤掉不同源css
|
|
276
|
+
// 这里是为了过滤掉不同源 css 脚本,防止报错终止导出
|
|
277
277
|
try {
|
|
278
278
|
for (let j = 0; j < sheet.cssRules.length; j++) {
|
|
279
279
|
rules += sheet.cssRules[j].cssText
|
|
@@ -292,10 +292,10 @@ export class Snapshot {
|
|
|
292
292
|
}
|
|
293
293
|
|
|
294
294
|
/**
|
|
295
|
-
*
|
|
296
|
-
* @param svg
|
|
297
|
-
* @param toImageOptions
|
|
298
|
-
* @returns
|
|
295
|
+
* 将 svg 转化为 canvas
|
|
296
|
+
* @param svg - svg 元素
|
|
297
|
+
* @param toImageOptions - 图像选项
|
|
298
|
+
* @returns Promise<canvas> - 返回 canvas 对象
|
|
299
299
|
*/
|
|
300
300
|
private async getCanvasData(
|
|
301
301
|
svg: Element,
|
|
@@ -334,15 +334,19 @@ export class Snapshot {
|
|
|
334
334
|
const { graphModel } = this.lf
|
|
335
335
|
const { transformModel } = graphModel
|
|
336
336
|
const { SCALE_X, SCALE_Y, TRANSLATE_X, TRANSLATE_Y } = transformModel
|
|
337
|
+
|
|
338
|
+
// 将导出区域移动到左上角,canvas 绘制的时候是从左上角开始绘制的
|
|
337
339
|
;(copy.lastChild as SVGElement).style.transform = `matrix(1, 0, 0, 1, ${
|
|
338
340
|
(-offsetX + TRANSLATE_X) * (1 / SCALE_X)
|
|
339
341
|
}, ${(-offsetY + TRANSLATE_Y) * (1 / SCALE_Y)})`
|
|
342
|
+
|
|
340
343
|
// 包含所有元素的最小宽高
|
|
341
344
|
const bboxWidth = Math.ceil(bbox.width / SCALE_X)
|
|
342
345
|
const bboxHeight = Math.ceil(bbox.height / SCALE_Y)
|
|
343
346
|
const canvas = document.createElement('canvas')
|
|
344
347
|
canvas.style.width = `${bboxWidth}px`
|
|
345
348
|
canvas.style.height = `${bboxHeight}px`
|
|
349
|
+
|
|
346
350
|
// 宽高值 默认加padding 40,保证图形不会紧贴着下载图片
|
|
347
351
|
canvas.width = bboxWidth * dpr + padding * 2
|
|
348
352
|
canvas.height = bboxHeight * dpr + padding * 2
|
|
@@ -362,7 +366,7 @@ export class Snapshot {
|
|
|
362
366
|
|
|
363
367
|
const img = new Image()
|
|
364
368
|
|
|
365
|
-
//
|
|
369
|
+
// 注入 css 样式
|
|
366
370
|
const style = document.createElement('style')
|
|
367
371
|
style.innerHTML = this.getClassRules()
|
|
368
372
|
const foreignObject = document.createElement('foreignObject')
|