@logicflow/core 2.0.9 → 2.0.11
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$colon$dev.log +2 -2
- package/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +26 -0
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/es/LogicFlow.js +21 -1
- package/es/event/eventEmitter.d.ts +1 -0
- package/es/event/eventEmitter.js +3 -0
- package/es/history/index.d.ts +1 -0
- package/es/history/index.js +6 -0
- package/es/keyboard/index.d.ts +1 -0
- package/es/keyboard/index.js +3 -0
- package/es/model/EditConfigModel.d.ts +3 -1
- package/es/model/EditConfigModel.js +5 -0
- package/es/model/GraphModel.d.ts +13 -1
- package/es/model/GraphModel.js +41 -41
- package/es/model/edge/BaseEdgeModel.js +6 -3
- package/es/model/edge/BezierEdgeModel.js +2 -2
- package/es/model/edge/PolylineEdgeModel.d.ts +1 -0
- package/es/model/edge/PolylineEdgeModel.js +9 -2
- package/es/model/node/BaseNodeModel.js +7 -2
- package/es/tool/index.d.ts +1 -0
- package/es/tool/index.js +6 -2
- package/es/util/geometry.d.ts +1 -1
- package/es/util/geometry.js +4 -1
- package/es/util/node.d.ts +1 -1
- package/es/util/node.js +32 -16
- package/es/view/Graph.js +19 -2
- package/es/view/behavior/dnd.js +3 -2
- package/es/view/edge/BaseEdge.js +9 -2
- package/es/view/node/BaseNode.js +20 -11
- package/es/view/node/HtmlNode.js +1 -0
- package/es/view/overlay/CanvasOverlay.js +6 -4
- package/es/view/text/BaseText.js +1 -0
- package/lib/LogicFlow.js +20 -0
- package/lib/event/eventEmitter.d.ts +1 -0
- package/lib/event/eventEmitter.js +3 -0
- package/lib/history/index.d.ts +1 -0
- package/lib/history/index.js +6 -0
- package/lib/keyboard/index.d.ts +1 -0
- package/lib/keyboard/index.js +3 -0
- package/lib/model/EditConfigModel.d.ts +3 -1
- package/lib/model/EditConfigModel.js +5 -0
- package/lib/model/GraphModel.d.ts +13 -1
- package/lib/model/GraphModel.js +40 -40
- package/lib/model/edge/BaseEdgeModel.js +6 -3
- package/lib/model/edge/BezierEdgeModel.js +1 -1
- package/lib/model/edge/PolylineEdgeModel.d.ts +1 -0
- package/lib/model/edge/PolylineEdgeModel.js +8 -1
- package/lib/model/node/BaseNodeModel.js +7 -2
- package/lib/tool/index.d.ts +1 -0
- package/lib/tool/index.js +6 -2
- package/lib/util/geometry.d.ts +1 -1
- package/lib/util/geometry.js +4 -1
- package/lib/util/node.d.ts +1 -1
- package/lib/util/node.js +32 -16
- package/lib/view/Graph.js +19 -2
- package/lib/view/behavior/dnd.js +3 -2
- package/lib/view/edge/BaseEdge.js +9 -2
- package/lib/view/node/BaseNode.js +19 -10
- package/lib/view/node/HtmlNode.js +1 -0
- package/lib/view/overlay/CanvasOverlay.js +6 -4
- package/lib/view/text/BaseText.js +1 -0
- package/package.json +1 -1
- package/src/LogicFlow.tsx +25 -1
- package/src/event/eventEmitter.ts +4 -0
- package/src/history/index.ts +7 -0
- package/src/keyboard/index.ts +4 -0
- package/src/model/EditConfigModel.ts +4 -0
- package/src/model/GraphModel.ts +62 -70
- package/src/model/edge/BaseEdgeModel.ts +7 -2
- package/src/model/edge/BezierEdgeModel.ts +2 -2
- package/src/model/edge/PolylineEdgeModel.ts +10 -2
- package/src/model/node/BaseNodeModel.ts +9 -2
- package/src/tool/index.ts +7 -2
- package/src/util/geometry.ts +3 -1
- package/src/util/node.ts +19 -18
- package/src/view/Graph.tsx +19 -2
- package/src/view/behavior/dnd.ts +5 -2
- package/src/view/edge/BaseEdge.tsx +9 -2
- package/src/view/node/BaseNode.tsx +20 -12
- package/src/view/node/HtmlNode.tsx +1 -0
- package/src/view/overlay/CanvasOverlay.tsx +6 -5
- package/src/view/text/BaseText.tsx +1 -0
- package/stats.html +1 -1
package/src/model/GraphModel.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
find,
|
|
3
|
+
forEach,
|
|
4
|
+
map,
|
|
5
|
+
merge,
|
|
6
|
+
isBoolean,
|
|
7
|
+
debounce,
|
|
8
|
+
isNil,
|
|
9
|
+
} from 'lodash-es'
|
|
2
10
|
import { action, computed, observable } from 'mobx'
|
|
3
11
|
import {
|
|
4
12
|
BaseEdgeModel,
|
|
@@ -41,7 +49,6 @@ import Position = LogicFlow.Position
|
|
|
41
49
|
import PointTuple = LogicFlow.PointTuple
|
|
42
50
|
import GraphData = LogicFlow.GraphData
|
|
43
51
|
import NodeConfig = LogicFlow.NodeConfig
|
|
44
|
-
import AnchorConfig = Model.AnchorConfig
|
|
45
52
|
import BaseNodeModelCtor = LogicFlow.BaseNodeModelCtor
|
|
46
53
|
import BaseEdgeModelCtor = LogicFlow.BaseEdgeModelCtor
|
|
47
54
|
|
|
@@ -99,6 +106,15 @@ export class GraphModel {
|
|
|
99
106
|
*/
|
|
100
107
|
customTrajectory: LFOptions.Definition['customTrajectory']
|
|
101
108
|
|
|
109
|
+
/**
|
|
110
|
+
* 判断是否使用的是容器的宽度
|
|
111
|
+
*/
|
|
112
|
+
isContainerWidth: boolean
|
|
113
|
+
/**
|
|
114
|
+
* 判断是否使用的是容器的高度
|
|
115
|
+
*/
|
|
116
|
+
isContainerHeight: boolean
|
|
117
|
+
|
|
102
118
|
// 在图上操作创建边时,默认使用的边类型.
|
|
103
119
|
@observable edgeType: string
|
|
104
120
|
// 当前图上所有节点的model
|
|
@@ -144,7 +160,9 @@ export class GraphModel {
|
|
|
144
160
|
this.rootEl = container
|
|
145
161
|
this.partial = !!partial
|
|
146
162
|
this.background = background
|
|
147
|
-
if (typeof grid === 'object') {
|
|
163
|
+
if (typeof grid === 'object' && options.snapGrid) {
|
|
164
|
+
// 开启网格对齐时才根据网格尺寸设置步长
|
|
165
|
+
// TODO:需要让用户设置成 0 吗?后面可以讨论一下
|
|
148
166
|
this.gridSize = grid.size || 1 // 默认 gridSize 设置为 1
|
|
149
167
|
}
|
|
150
168
|
this.theme = setupTheme(options.style)
|
|
@@ -153,8 +171,10 @@ export class GraphModel {
|
|
|
153
171
|
this.animation = setupAnimation(animation)
|
|
154
172
|
this.overlapMode = options.overlapMode || OverlapMode.DEFAULT
|
|
155
173
|
|
|
156
|
-
this.width = options.width
|
|
157
|
-
this.
|
|
174
|
+
this.width = options.width ?? this.rootEl.getBoundingClientRect().width
|
|
175
|
+
this.isContainerWidth = isNil(options.width)
|
|
176
|
+
this.height = options.height ?? this.rootEl.getBoundingClientRect().height
|
|
177
|
+
this.isContainerHeight = isNil(options.height)
|
|
158
178
|
|
|
159
179
|
const resizeObserver = new ResizeObserver(
|
|
160
180
|
debounce(
|
|
@@ -438,18 +458,25 @@ export class GraphModel {
|
|
|
438
458
|
* @param { object } graphData 图数据
|
|
439
459
|
*/
|
|
440
460
|
graphDataToModel(graphData: Partial<LogicFlow.GraphConfigData>) {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
461
|
+
// 宽度必然存在,取消重新计算
|
|
462
|
+
// if (!this.width || !this.height) {
|
|
463
|
+
// this.resize()
|
|
464
|
+
// }
|
|
444
465
|
if (!graphData) {
|
|
445
|
-
this.
|
|
446
|
-
this.edges = []
|
|
466
|
+
this.clearData()
|
|
447
467
|
return
|
|
448
468
|
}
|
|
469
|
+
this.elementsModelMap.clear()
|
|
470
|
+
this.nodeModelMap.clear()
|
|
471
|
+
this.edgeModelMap.clear()
|
|
472
|
+
|
|
449
473
|
if (graphData.nodes) {
|
|
450
|
-
this.nodes = map(graphData.nodes, (node: NodeConfig) =>
|
|
451
|
-
this.getModelAfterSnapToGrid(node)
|
|
452
|
-
|
|
474
|
+
this.nodes = map(graphData.nodes, (node: NodeConfig) => {
|
|
475
|
+
const nodeModel = this.getModelAfterSnapToGrid(node)
|
|
476
|
+
this.elementsModelMap.set(nodeModel.id, nodeModel)
|
|
477
|
+
this.nodeModelMap.set(nodeModel.id, nodeModel)
|
|
478
|
+
return nodeModel
|
|
479
|
+
})
|
|
453
480
|
} else {
|
|
454
481
|
this.nodes = []
|
|
455
482
|
}
|
|
@@ -463,58 +490,6 @@ export class GraphModel {
|
|
|
463
490
|
throw new Error(`找不到${edge.type}对应的边。`)
|
|
464
491
|
}
|
|
465
492
|
const edgeModel = new Model(edge, this)
|
|
466
|
-
// 根据edgeModel中存储的数据找到当前画布上的起终锚点坐标
|
|
467
|
-
// 判断当前起终锚点数据和Model中存储的起终点数据是否一致,不一致更新起终点信息
|
|
468
|
-
const {
|
|
469
|
-
sourceNodeId,
|
|
470
|
-
targetNodeId,
|
|
471
|
-
sourceAnchorId = '',
|
|
472
|
-
targetAnchorId = '',
|
|
473
|
-
startPoint,
|
|
474
|
-
endPoint,
|
|
475
|
-
text,
|
|
476
|
-
textPosition,
|
|
477
|
-
} = edgeModel
|
|
478
|
-
const updateAnchorPoint = (
|
|
479
|
-
node: BaseNodeModel | undefined,
|
|
480
|
-
anchorId: string,
|
|
481
|
-
point: Position,
|
|
482
|
-
updatePoint: (anchor: AnchorConfig) => void,
|
|
483
|
-
) => {
|
|
484
|
-
const anchor = node?.anchors.find((anchor) => anchor.id === anchorId)
|
|
485
|
-
if (anchor && !isEqual(anchor, point)) {
|
|
486
|
-
updatePoint(anchor)
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
const sourceNode = this.getNodeModelById(sourceNodeId)
|
|
491
|
-
const targetNode = this.getNodeModelById(targetNodeId)
|
|
492
|
-
|
|
493
|
-
updateAnchorPoint(
|
|
494
|
-
sourceNode,
|
|
495
|
-
sourceAnchorId,
|
|
496
|
-
startPoint,
|
|
497
|
-
edgeModel.updateStartPoint.bind(edgeModel),
|
|
498
|
-
)
|
|
499
|
-
updateAnchorPoint(
|
|
500
|
-
targetNode,
|
|
501
|
-
targetAnchorId,
|
|
502
|
-
endPoint,
|
|
503
|
-
edgeModel.updateEndPoint.bind(edgeModel),
|
|
504
|
-
)
|
|
505
|
-
|
|
506
|
-
// 而文本需要先算一下文本与默认文本位置之间的相对位置差
|
|
507
|
-
// 再计算新路径的文本默认位置,加上相对位置差,得到调整后边的文本的位置
|
|
508
|
-
if (text) {
|
|
509
|
-
const { x, y } = text
|
|
510
|
-
const { x: defaultX, y: defaultY } = textPosition
|
|
511
|
-
if (x && y && defaultX && defaultY) {
|
|
512
|
-
const deltaX = x - defaultX
|
|
513
|
-
const deltaY = y - defaultY
|
|
514
|
-
edgeModel.resetTextPosition()
|
|
515
|
-
edgeModel.moveText(deltaX, deltaY)
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
493
|
this.edgeModelMap.set(edgeModel.id, edgeModel)
|
|
519
494
|
this.elementsModelMap.set(edgeModel.id, edgeModel)
|
|
520
495
|
|
|
@@ -873,6 +848,7 @@ export class GraphModel {
|
|
|
873
848
|
*/
|
|
874
849
|
getModelAfterSnapToGrid(node: NodeConfig) {
|
|
875
850
|
const Model = this.getModel(node.type) as BaseNodeModelCtor
|
|
851
|
+
const { snapGrid } = this.editConfigModel
|
|
876
852
|
if (!Model) {
|
|
877
853
|
throw new Error(
|
|
878
854
|
`找不到${node.type}对应的节点,请确认是否已注册此类型节点。`,
|
|
@@ -881,8 +857,8 @@ export class GraphModel {
|
|
|
881
857
|
const { x: nodeX, y: nodeY } = node
|
|
882
858
|
// 根据 grid 修正节点的 x, y
|
|
883
859
|
if (nodeX && nodeY) {
|
|
884
|
-
node.x = snapToGrid(nodeX, this.gridSize)
|
|
885
|
-
node.y = snapToGrid(nodeY, this.gridSize)
|
|
860
|
+
node.x = snapToGrid(nodeX, this.gridSize, snapGrid)
|
|
861
|
+
node.y = snapToGrid(nodeY, this.gridSize, snapGrid)
|
|
886
862
|
if (typeof node.text === 'object' && node.text !== null) {
|
|
887
863
|
// 原来的处理是:node.text.x -= getGridOffset(nodeX, this.gridSize)
|
|
888
864
|
// 由于snapToGrid()使用了Math.round()四舍五入的做法,因此无法判断需要执行
|
|
@@ -1499,7 +1475,14 @@ export class GraphModel {
|
|
|
1499
1475
|
}
|
|
1500
1476
|
|
|
1501
1477
|
/**
|
|
1502
|
-
*
|
|
1478
|
+
* 更新网格尺寸
|
|
1479
|
+
*/
|
|
1480
|
+
updateGridSize(size: number) {
|
|
1481
|
+
this.gridSize = size
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
/**
|
|
1485
|
+
* 更新背景配置
|
|
1503
1486
|
*/
|
|
1504
1487
|
updateBackgroundOptions(
|
|
1505
1488
|
options: boolean | Partial<LFOptions.BackgroundConfig>,
|
|
@@ -1518,8 +1501,11 @@ export class GraphModel {
|
|
|
1518
1501
|
* 重新设置画布的宽高
|
|
1519
1502
|
*/
|
|
1520
1503
|
@action resize(width?: number, height?: number): void {
|
|
1521
|
-
this.width = width
|
|
1522
|
-
this.
|
|
1504
|
+
this.width = width ?? this.rootEl.getBoundingClientRect().width
|
|
1505
|
+
this.isContainerWidth = isNil(width)
|
|
1506
|
+
this.height = height ?? this.rootEl.getBoundingClientRect().height
|
|
1507
|
+
this.isContainerHeight = isNil(height)
|
|
1508
|
+
|
|
1523
1509
|
if (!this.width || !this.height) {
|
|
1524
1510
|
console.warn(
|
|
1525
1511
|
'渲染画布的时候无法获取画布宽高,请确认在container已挂载到DOM。@see https://github.com/didi/LogicFlow/issues/675',
|
|
@@ -1533,6 +1519,11 @@ export class GraphModel {
|
|
|
1533
1519
|
@action clearData(): void {
|
|
1534
1520
|
this.nodes = []
|
|
1535
1521
|
this.edges = []
|
|
1522
|
+
|
|
1523
|
+
// 清除对已清除节点的引用
|
|
1524
|
+
this.edgeModelMap.clear()
|
|
1525
|
+
this.nodeModelMap.clear()
|
|
1526
|
+
this.elementsModelMap.clear()
|
|
1536
1527
|
}
|
|
1537
1528
|
|
|
1538
1529
|
/**
|
|
@@ -1680,6 +1671,7 @@ export class GraphModel {
|
|
|
1680
1671
|
console.warn('error on destroy GraphModel', err)
|
|
1681
1672
|
}
|
|
1682
1673
|
this.waitCleanEffects.length = 0
|
|
1674
|
+
this.eventCenter.destroy()
|
|
1683
1675
|
}
|
|
1684
1676
|
}
|
|
1685
1677
|
|
|
@@ -392,6 +392,8 @@ export class BaseEdgeModel<P extends PropertiesType = PropertiesType>
|
|
|
392
392
|
properties,
|
|
393
393
|
sourceNodeId: this.sourceNodeId,
|
|
394
394
|
targetNodeId: this.targetNodeId,
|
|
395
|
+
sourceAnchorId: this.sourceAnchorId,
|
|
396
|
+
targetAnchorId: this.targetAnchorId,
|
|
395
397
|
startPoint: assign({}, this.startPoint),
|
|
396
398
|
endPoint: assign({}, this.endPoint),
|
|
397
399
|
}
|
|
@@ -515,14 +517,17 @@ export class BaseEdgeModel<P extends PropertiesType = PropertiesType>
|
|
|
515
517
|
* 内部方法,处理初始化文本格式
|
|
516
518
|
*/
|
|
517
519
|
@action formatText(data: EdgeConfig) {
|
|
520
|
+
const {
|
|
521
|
+
editConfigModel: { edgeTextDraggable, edgeTextEdit },
|
|
522
|
+
} = this.graphModel
|
|
518
523
|
const { x, y } = this.textPosition
|
|
519
524
|
const { text } = data
|
|
520
525
|
let textConfig: Required<TextConfig> = {
|
|
521
526
|
value: '',
|
|
522
527
|
x,
|
|
523
528
|
y,
|
|
524
|
-
draggable:
|
|
525
|
-
editable:
|
|
529
|
+
draggable: edgeTextDraggable,
|
|
530
|
+
editable: edgeTextEdit,
|
|
526
531
|
}
|
|
527
532
|
|
|
528
533
|
if (text) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { assign, cloneDeep } from 'lodash-es'
|
|
1
|
+
import { get, assign, cloneDeep } from 'lodash-es'
|
|
2
2
|
import { action, observable } from 'mobx'
|
|
3
3
|
import BaseEdgeModel from './BaseEdgeModel'
|
|
4
4
|
import { BaseNodeModel } from '../node'
|
|
@@ -21,7 +21,7 @@ export class BezierEdgeModel extends BaseEdgeModel {
|
|
|
21
21
|
this.setAttributes()
|
|
22
22
|
}
|
|
23
23
|
initEdgeData(data: EdgeConfig): void {
|
|
24
|
-
this.offset = 100
|
|
24
|
+
this.offset = get(data, 'properties.offset', 100)
|
|
25
25
|
super.initEdgeData(data)
|
|
26
26
|
}
|
|
27
27
|
getEdgeStyle() {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { assign, cloneDeep } from 'lodash-es'
|
|
1
|
+
import { get, assign, cloneDeep } from 'lodash-es'
|
|
2
2
|
import { observable, action } from 'mobx'
|
|
3
3
|
import { BaseEdgeModel } from '.'
|
|
4
4
|
import { BaseNodeModel, RectNodeModel, CircleNodeModel, Model } from '..'
|
|
@@ -34,7 +34,10 @@ export class PolylineEdgeModel extends BaseEdgeModel {
|
|
|
34
34
|
@observable dbClickPosition?: Point
|
|
35
35
|
|
|
36
36
|
initEdgeData(data: LogicFlow.EdgeConfig): void {
|
|
37
|
-
this.offset = 30
|
|
37
|
+
this.offset = get(data, 'properties.offset', 30)
|
|
38
|
+
if (data.pointsList) {
|
|
39
|
+
this.pointsList = data.pointsList
|
|
40
|
+
}
|
|
38
41
|
super.initEdgeData(data)
|
|
39
42
|
}
|
|
40
43
|
|
|
@@ -315,6 +318,11 @@ export class PolylineEdgeModel extends BaseEdgeModel {
|
|
|
315
318
|
return list
|
|
316
319
|
}
|
|
317
320
|
|
|
321
|
+
updatePath(pointList: Point[]) {
|
|
322
|
+
this.pointsList = pointList
|
|
323
|
+
this.points = this.getPath(this.pointsList)
|
|
324
|
+
}
|
|
325
|
+
|
|
318
326
|
getData() {
|
|
319
327
|
const data = super.getData()
|
|
320
328
|
const pointsList = this.pointsList.map(({ x, y }) => ({
|
|
@@ -246,13 +246,16 @@ export class BaseNodeModel<P extends PropertiesType = PropertiesType>
|
|
|
246
246
|
* 始化文本属性
|
|
247
247
|
*/
|
|
248
248
|
private formatText(data: NodeConfig): void {
|
|
249
|
+
const {
|
|
250
|
+
editConfigModel: { nodeTextDraggable, nodeTextEdit },
|
|
251
|
+
} = this.graphModel
|
|
249
252
|
const { x, y, text } = data
|
|
250
253
|
let textConfig: TextConfig = {
|
|
251
254
|
value: '',
|
|
252
255
|
x,
|
|
253
256
|
y,
|
|
254
|
-
draggable:
|
|
255
|
-
editable:
|
|
257
|
+
draggable: nodeTextDraggable,
|
|
258
|
+
editable: nodeTextEdit,
|
|
256
259
|
}
|
|
257
260
|
if (text) {
|
|
258
261
|
if (typeof text === 'string') {
|
|
@@ -737,6 +740,10 @@ export class BaseNodeModel<P extends PropertiesType = PropertiesType>
|
|
|
737
740
|
this.text && this.moveText(0, deltaY)
|
|
738
741
|
moveY = deltaY
|
|
739
742
|
}
|
|
743
|
+
this.transform = new TranslateMatrix(-this.x, -this.y)
|
|
744
|
+
.rotate(this.rotate)
|
|
745
|
+
.translate(this.x, this.y)
|
|
746
|
+
.toString()
|
|
740
747
|
return [moveX, moveY]
|
|
741
748
|
}
|
|
742
749
|
|
package/src/tool/index.ts
CHANGED
|
@@ -70,7 +70,7 @@ export class Tool {
|
|
|
70
70
|
this.toolMap.delete(name)
|
|
71
71
|
return true
|
|
72
72
|
}
|
|
73
|
-
throw new Error(
|
|
73
|
+
throw new Error(`禁用失败,不存在名为 ${tool} 的工具`)
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
@action
|
|
@@ -81,7 +81,7 @@ export class Tool {
|
|
|
81
81
|
this.disabledToolMap.delete(name)
|
|
82
82
|
return true
|
|
83
83
|
}
|
|
84
|
-
throw new Error(
|
|
84
|
+
throw new Error(`不存在名为 ${tool} 的工具`)
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
getTools() {
|
|
@@ -91,6 +91,11 @@ export class Tool {
|
|
|
91
91
|
getInstance() {
|
|
92
92
|
return this.instance
|
|
93
93
|
}
|
|
94
|
+
|
|
95
|
+
destroy() {
|
|
96
|
+
this.toolMap.clear()
|
|
97
|
+
this.disabledToolMap.clear()
|
|
98
|
+
}
|
|
94
99
|
}
|
|
95
100
|
|
|
96
101
|
export default Tool
|
package/src/util/geometry.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import LogicFlow from '../LogicFlow'
|
|
2
2
|
import PointTuple = LogicFlow.PointTuple
|
|
3
3
|
|
|
4
|
-
export function snapToGrid(point: number, gridSize: number) {
|
|
4
|
+
export function snapToGrid(point: number, gridSize: number, snapGrid: boolean) {
|
|
5
|
+
// 开启节网格对齐时才根据网格尺寸校准坐标
|
|
6
|
+
if (!snapGrid) return point
|
|
5
7
|
// 保证 x, y 的值为 gridSize 的整数倍
|
|
6
8
|
return gridSize * Math.round(point / gridSize) || point
|
|
7
9
|
}
|
package/src/util/node.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from '../model'
|
|
14
14
|
import { SegmentDirection } from '../constant'
|
|
15
15
|
import { isInSegment } from '../algorithm/edge'
|
|
16
|
+
import { Matrix } from './matrix'
|
|
16
17
|
|
|
17
18
|
import Point = LogicFlow.Point
|
|
18
19
|
import Direction = LogicFlow.Direction
|
|
@@ -112,33 +113,33 @@ export const distance = (
|
|
|
112
113
|
): number => Math.hypot(x1 - x2, y1 - y2)
|
|
113
114
|
|
|
114
115
|
/* 是否在某个节点内,手否进行连接,有offset控制粒度,与outline有关,可以优化 */
|
|
115
|
-
export const isInNode = (
|
|
116
|
+
export const isInNode = (
|
|
117
|
+
position: Point,
|
|
118
|
+
node: BaseNodeModel,
|
|
119
|
+
offset = 0,
|
|
120
|
+
): boolean => {
|
|
116
121
|
let inNode = false
|
|
117
|
-
const offset = 0
|
|
118
122
|
const bBox = getNodeBBox(node)
|
|
123
|
+
const [x, y] = new Matrix([position.x, position.y, 1])
|
|
124
|
+
.translate(-node.x, -node.y)
|
|
125
|
+
.rotate(-node.rotate)
|
|
126
|
+
.translate(node.x, node.y)[0]
|
|
127
|
+
const reverseRotatedPosition = {
|
|
128
|
+
x,
|
|
129
|
+
y,
|
|
130
|
+
}
|
|
119
131
|
if (
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
132
|
+
reverseRotatedPosition.x >= bBox.minX - offset &&
|
|
133
|
+
reverseRotatedPosition.x <= bBox.maxX + offset &&
|
|
134
|
+
reverseRotatedPosition.y >= bBox.minY - offset &&
|
|
135
|
+
reverseRotatedPosition.y <= bBox.maxY + offset
|
|
124
136
|
) {
|
|
125
137
|
inNode = true
|
|
126
138
|
}
|
|
127
139
|
return inNode
|
|
128
140
|
}
|
|
129
141
|
export const isInNodeBbox = (position: Point, node: BaseNodeModel): boolean => {
|
|
130
|
-
|
|
131
|
-
const offset = 5
|
|
132
|
-
const bBox = getNodeBBox(node)
|
|
133
|
-
if (
|
|
134
|
-
position.x >= bBox.minX - offset &&
|
|
135
|
-
position.x <= bBox.maxX + offset &&
|
|
136
|
-
position.y >= bBox.minY - offset &&
|
|
137
|
-
position.y <= bBox.maxY + offset
|
|
138
|
-
) {
|
|
139
|
-
inNode = true
|
|
140
|
-
}
|
|
141
|
-
return inNode
|
|
142
|
+
return isInNode(position, node, 5)
|
|
142
143
|
}
|
|
143
144
|
|
|
144
145
|
export type NodeBBox = {
|
package/src/view/Graph.tsx
CHANGED
|
@@ -39,9 +39,26 @@ type ContainerStyle = {
|
|
|
39
39
|
@observer
|
|
40
40
|
class Graph extends Component<IGraphProps> {
|
|
41
41
|
private handleResize = () => {
|
|
42
|
-
this.props
|
|
42
|
+
const { graphModel, options } = this.props
|
|
43
|
+
const { width, height, isContainerWidth, isContainerHeight } = graphModel
|
|
44
|
+
let resizeWidth: number | undefined = width
|
|
45
|
+
let resizeHeight: number | undefined = height
|
|
46
|
+
let needUpdate = false
|
|
47
|
+
if (isContainerWidth) {
|
|
48
|
+
resizeWidth = undefined
|
|
49
|
+
needUpdate = true
|
|
50
|
+
}
|
|
51
|
+
if (isContainerHeight) {
|
|
52
|
+
resizeHeight = undefined
|
|
53
|
+
needUpdate = true
|
|
54
|
+
}
|
|
55
|
+
if (needUpdate) {
|
|
56
|
+
graphModel.resize(resizeWidth, resizeHeight)
|
|
57
|
+
}
|
|
58
|
+
options.width = width
|
|
59
|
+
options.height = height
|
|
43
60
|
}
|
|
44
|
-
private throttleResize =
|
|
61
|
+
private throttleResize = throttle(this.handleResize, 200)
|
|
45
62
|
|
|
46
63
|
componentDidMount() {
|
|
47
64
|
window.addEventListener('resize', this.throttleResize)
|
package/src/view/behavior/dnd.ts
CHANGED
|
@@ -26,10 +26,13 @@ export class Dnd {
|
|
|
26
26
|
})
|
|
27
27
|
// 处理缩放和偏移
|
|
28
28
|
const { x: x1, y: y1 } = position.canvasOverlayPosition
|
|
29
|
+
const {
|
|
30
|
+
editConfigModel: { snapGrid },
|
|
31
|
+
} = this.lf.graphModel
|
|
29
32
|
// x, y 对齐到网格的 size
|
|
30
33
|
return {
|
|
31
|
-
x: snapToGrid(x1, gridSize),
|
|
32
|
-
y: snapToGrid(y1, gridSize),
|
|
34
|
+
x: snapToGrid(x1, gridSize, snapGrid),
|
|
35
|
+
y: snapToGrid(y1, gridSize, snapGrid),
|
|
33
36
|
}
|
|
34
37
|
}
|
|
35
38
|
|
|
@@ -358,6 +358,7 @@ export abstract class BaseEdge<P extends IProps> extends Component<
|
|
|
358
358
|
clearTimeout(this.clickTimer)
|
|
359
359
|
}
|
|
360
360
|
const { model, graphModel } = this.props
|
|
361
|
+
const { editConfigModel } = graphModel
|
|
361
362
|
const position = graphModel.getPointByClient({
|
|
362
363
|
x: ev.clientX,
|
|
363
364
|
y: ev.clientY,
|
|
@@ -367,7 +368,10 @@ export abstract class BaseEdge<P extends IProps> extends Component<
|
|
|
367
368
|
ElementState.SHOW_MENU,
|
|
368
369
|
position.domOverlayPosition,
|
|
369
370
|
)
|
|
370
|
-
|
|
371
|
+
// 静默模式下点击节点不变更节点层级
|
|
372
|
+
if (!editConfigModel.isSilentMode) {
|
|
373
|
+
this.toFront()
|
|
374
|
+
}
|
|
371
375
|
if (!model.isSelected) {
|
|
372
376
|
graphModel.selectEdgeById(model.id)
|
|
373
377
|
}
|
|
@@ -456,7 +460,10 @@ export abstract class BaseEdge<P extends IProps> extends Component<
|
|
|
456
460
|
}
|
|
457
461
|
const { editConfigModel } = graphModel
|
|
458
462
|
graphModel.selectEdgeById(model.id, isMultipleSelect(e, editConfigModel))
|
|
459
|
-
|
|
463
|
+
// 静默模式下点击节点不变更节点层级
|
|
464
|
+
if (!editConfigModel.isSilentMode) {
|
|
465
|
+
this.toFront()
|
|
466
|
+
}
|
|
460
467
|
}
|
|
461
468
|
|
|
462
469
|
handleFocus = () => {
|
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
isMultipleSelect,
|
|
14
14
|
cancelRaf,
|
|
15
15
|
createRaf,
|
|
16
|
-
TranslateMatrix,
|
|
17
16
|
IDragParams,
|
|
18
17
|
// RotateMatrix,
|
|
19
18
|
} from '../../util'
|
|
@@ -80,6 +79,13 @@ export abstract class BaseNode<P extends IProps = IProps> extends Component<
|
|
|
80
79
|
if (this.modelDisposer) {
|
|
81
80
|
this.modelDisposer()
|
|
82
81
|
}
|
|
82
|
+
|
|
83
|
+
// 以下是 mobx-preact 中 componentWillUnmount 的回调逻辑,但是不知道出于什么考虑,mobx-preact 没有混入这一段逻辑
|
|
84
|
+
// @ts-ignore
|
|
85
|
+
if (this.render.$mobx) {
|
|
86
|
+
// @ts-ignore
|
|
87
|
+
this.render.$mobx.dispose()
|
|
88
|
+
}
|
|
83
89
|
}
|
|
84
90
|
|
|
85
91
|
componentDidMount() {}
|
|
@@ -234,7 +240,7 @@ export abstract class BaseNode<P extends IProps = IProps> extends Component<
|
|
|
234
240
|
onDragging = ({ event }: IDragParams) => {
|
|
235
241
|
const { model, graphModel } = this.props
|
|
236
242
|
const {
|
|
237
|
-
editConfigModel: { stopMoveGraph, autoExpand },
|
|
243
|
+
editConfigModel: { stopMoveGraph, autoExpand, snapGrid },
|
|
238
244
|
transformModel,
|
|
239
245
|
selectNodes,
|
|
240
246
|
width,
|
|
@@ -254,9 +260,9 @@ export abstract class BaseNode<P extends IProps = IProps> extends Component<
|
|
|
254
260
|
// 2. 考虑鼠标位置不再节点中心
|
|
255
261
|
x = x + (this.moveOffset?.dx ?? 0)
|
|
256
262
|
y = y + (this.moveOffset?.dy ?? 0)
|
|
257
|
-
//
|
|
258
|
-
x = snapToGrid(x, gridSize)
|
|
259
|
-
y = snapToGrid(y, gridSize)
|
|
263
|
+
// 校准坐标
|
|
264
|
+
x = snapToGrid(x, gridSize, snapGrid)
|
|
265
|
+
y = snapToGrid(y, gridSize, snapGrid)
|
|
260
266
|
if (!width || !height) {
|
|
261
267
|
graphModel.moveNode2Coordinate(model.id, x, y)
|
|
262
268
|
return
|
|
@@ -290,11 +296,6 @@ export abstract class BaseNode<P extends IProps = IProps> extends Component<
|
|
|
290
296
|
cancelRaf(this.t)
|
|
291
297
|
}
|
|
292
298
|
|
|
293
|
-
model.transform = new TranslateMatrix(-x, -y)
|
|
294
|
-
.rotate(model.rotate)
|
|
295
|
-
.translate(x, y)
|
|
296
|
-
.toString()
|
|
297
|
-
|
|
298
299
|
let moveNodes = selectNodes.map((node) => node.id)
|
|
299
300
|
// 未被选中的节点也可以拖动
|
|
300
301
|
if (moveNodes.indexOf(model.id) === -1) {
|
|
@@ -371,7 +372,10 @@ export abstract class BaseNode<P extends IProps = IProps> extends Component<
|
|
|
371
372
|
} else {
|
|
372
373
|
graphModel.selectNodeById(model.id, isMultiple)
|
|
373
374
|
eventOptions.isSelected = true
|
|
374
|
-
|
|
375
|
+
// 静默模式下点击节点不变更节点层级
|
|
376
|
+
if (!editConfigModel.isSilentMode) {
|
|
377
|
+
this.toFront()
|
|
378
|
+
}
|
|
375
379
|
}
|
|
376
380
|
|
|
377
381
|
// 不是双击的,默认都是单击
|
|
@@ -392,6 +396,7 @@ export abstract class BaseNode<P extends IProps = IProps> extends Component<
|
|
|
392
396
|
handleContextMenu = (ev: MouseEvent) => {
|
|
393
397
|
ev.preventDefault()
|
|
394
398
|
const { model, graphModel } = this.props
|
|
399
|
+
const { editConfigModel } = graphModel
|
|
395
400
|
// 节点数据,多为事件对象数据抛出
|
|
396
401
|
const nodeData = model.getData()
|
|
397
402
|
|
|
@@ -412,7 +417,10 @@ export abstract class BaseNode<P extends IProps = IProps> extends Component<
|
|
|
412
417
|
e: ev,
|
|
413
418
|
position,
|
|
414
419
|
})
|
|
415
|
-
|
|
420
|
+
// 静默模式下点击节点不变更节点层级
|
|
421
|
+
if (!editConfigModel.isSilentMode) {
|
|
422
|
+
this.toFront()
|
|
423
|
+
}
|
|
416
424
|
}
|
|
417
425
|
|
|
418
426
|
handleMouseDown = (ev: MouseEvent) => {
|
|
@@ -65,8 +65,9 @@ export class CanvasOverlay extends Component<IProps, IState> {
|
|
|
65
65
|
graphModel,
|
|
66
66
|
} = this.props
|
|
67
67
|
const { deltaX: eX, deltaY: eY } = ev
|
|
68
|
+
const { stopScrollGraph, stopZoomGraph } = editConfigModel
|
|
68
69
|
// 如果没有禁止滚动移动画布, 并且当前触发的时候ctrl键、cmd键没有按住, 那么移动画布
|
|
69
|
-
if (!
|
|
70
|
+
if (!stopScrollGraph && !ev.ctrlKey && !ev.metaKey) {
|
|
70
71
|
ev.preventDefault()
|
|
71
72
|
this.stepScrollX += eX
|
|
72
73
|
this.stepScrollY += eY
|
|
@@ -85,7 +86,7 @@ export class CanvasOverlay extends Component<IProps, IState> {
|
|
|
85
86
|
return
|
|
86
87
|
}
|
|
87
88
|
// 如果没有禁止缩放画布,那么进行缩放. 在禁止缩放画布后,按住 ctrl、cmd 键也不能缩放了。
|
|
88
|
-
if (!
|
|
89
|
+
if (!stopZoomGraph) {
|
|
89
90
|
ev.preventDefault()
|
|
90
91
|
const position = graphModel.getPointByClient({
|
|
91
92
|
x: ev.clientX,
|
|
@@ -133,11 +134,11 @@ export class CanvasOverlay extends Component<IProps, IState> {
|
|
|
133
134
|
gridSize,
|
|
134
135
|
},
|
|
135
136
|
} = this.props
|
|
137
|
+
const { adjustEdge, adjustNodePosition, stopMoveGraph } = editConfigModel
|
|
136
138
|
const target = ev.target as HTMLElement
|
|
137
|
-
const isFrozenElement =
|
|
138
|
-
!editConfigModel.adjustEdge && !editConfigModel.adjustNodePosition
|
|
139
|
+
const isFrozenElement = !adjustEdge && !adjustNodePosition
|
|
139
140
|
if (target.getAttribute('name') === 'canvas-overlay' || isFrozenElement) {
|
|
140
|
-
if (
|
|
141
|
+
if (stopMoveGraph !== true) {
|
|
141
142
|
this.stepDrag.setStep(gridSize * SCALE_X)
|
|
142
143
|
this.stepDrag.handleMouseDown(ev)
|
|
143
144
|
} else {
|