@logicflow/layout 2.1.0-alpha.2 → 2.1.0-alpha.4
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 +7 -6
- package/CHANGELOG.md +15 -0
- package/README.md +9 -2
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/es/{dagre.d.ts → dagre/index.d.ts} +18 -20
- package/es/dagre/index.js +126 -0
- package/es/dagre/index.js.map +1 -0
- package/es/elkLayout/config.d.ts +26 -0
- package/es/elkLayout/config.js +27 -0
- package/es/elkLayout/config.js.map +1 -0
- package/es/elkLayout/index.d.ts +107 -0
- package/es/elkLayout/index.js +187 -0
- package/es/elkLayout/index.js.map +1 -0
- package/es/index.d.ts +1 -0
- package/es/index.js +1 -0
- package/es/index.js.map +1 -1
- package/es/utils/processEdge.d.ts +3 -0
- package/es/utils/processEdge.js +479 -0
- package/es/utils/processEdge.js.map +1 -0
- package/lib/{dagre.d.ts → dagre/index.d.ts} +18 -20
- package/lib/dagre/index.js +152 -0
- package/lib/dagre/index.js.map +1 -0
- package/lib/elkLayout/config.d.ts +26 -0
- package/lib/elkLayout/config.js +30 -0
- package/lib/elkLayout/config.js.map +1 -0
- package/lib/elkLayout/index.d.ts +107 -0
- package/lib/elkLayout/index.js +193 -0
- package/lib/elkLayout/index.js.map +1 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/utils/processEdge.d.ts +3 -0
- package/lib/utils/processEdge.js +483 -0
- package/lib/utils/processEdge.js.map +1 -0
- package/package.json +3 -2
- package/src/dagre/index.ts +177 -0
- package/src/elkLayout/config.ts +26 -0
- package/src/elkLayout/index.ts +255 -0
- package/src/index.ts +2 -0
- package/src/utils/processEdge.ts +585 -0
- package/stats.html +1 -1
- package/es/dagre.js +0 -376
- package/es/dagre.js.map +0 -1
- package/lib/dagre.js +0 -402
- package/lib/dagre.js.map +0 -1
- package/src/dagre.ts +0 -438
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Dagre布局插件 - 提供自动化图形布局功能
|
|
3
|
+
*
|
|
4
|
+
* 本插件基于dagre.js实现LogicFlow的自动化布局功能,支持多种布局方向
|
|
5
|
+
* 可自动计算节点位置和连线路径,实现整洁的图形展示
|
|
6
|
+
*/
|
|
7
|
+
import LogicFlow, { BaseNodeModel, BaseEdgeModel } from '@logicflow/core'
|
|
8
|
+
import dagre, { GraphLabel, graphlib } from 'dagre'
|
|
9
|
+
import { processEdges } from '../utils/processEdge'
|
|
10
|
+
|
|
11
|
+
import NodeConfig = LogicFlow.NodeConfig
|
|
12
|
+
import EdgeConfig = LogicFlow.EdgeConfig
|
|
13
|
+
|
|
14
|
+
type BaseNodeData = {
|
|
15
|
+
x: number
|
|
16
|
+
y: number
|
|
17
|
+
width: number
|
|
18
|
+
height: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Dagre布局配置选项接口
|
|
23
|
+
* @interface DagreOption
|
|
24
|
+
* @extends GraphLabel - 继承dagre原生配置
|
|
25
|
+
*/
|
|
26
|
+
export interface DagreOption extends GraphLabel {
|
|
27
|
+
/**
|
|
28
|
+
* 是否是默认锚点
|
|
29
|
+
* true: 会根据布局方向自动计算边的路径点
|
|
30
|
+
*/
|
|
31
|
+
isDefaultAnchor?: boolean
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Dagre插件接口定义
|
|
36
|
+
*/
|
|
37
|
+
export interface DagrePlugin {
|
|
38
|
+
/**
|
|
39
|
+
* 执行布局计算
|
|
40
|
+
* @param option - 布局配置选项
|
|
41
|
+
*/
|
|
42
|
+
layout(option: DagreOption): void
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Dagre布局类 - LogicFlow自动布局插件
|
|
47
|
+
* 基于dagre.js提供图的自动布局能力
|
|
48
|
+
*/
|
|
49
|
+
export class Dagre {
|
|
50
|
+
/** 插件名称,用于在LogicFlow中注册 */
|
|
51
|
+
static pluginName = 'dagre'
|
|
52
|
+
|
|
53
|
+
/** LogicFlow实例引用 */
|
|
54
|
+
lf: LogicFlow
|
|
55
|
+
|
|
56
|
+
/** 当前布局配置 */
|
|
57
|
+
option: DagreOption // 使用已定义的DagreOption接口替代重复定义
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 插件初始化方法,由LogicFlow自动调用
|
|
61
|
+
* @param lf - LogicFlow实例
|
|
62
|
+
*/
|
|
63
|
+
render(lf: LogicFlow) {
|
|
64
|
+
this.lf = lf
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 执行布局算法,重新排列图中的节点和边
|
|
69
|
+
* @param option - 布局配置选项
|
|
70
|
+
*/
|
|
71
|
+
layout(option: DagreOption = {}) {
|
|
72
|
+
const { nodes, edges, gridSize } = this.lf.graphModel
|
|
73
|
+
|
|
74
|
+
// 根据网格大小调整节点间距
|
|
75
|
+
let nodesep = 100
|
|
76
|
+
let ranksep = 150
|
|
77
|
+
if (gridSize > 20) {
|
|
78
|
+
nodesep = gridSize * 2
|
|
79
|
+
ranksep = gridSize * 2
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 合并默认配置和用户配置
|
|
83
|
+
this.option = {
|
|
84
|
+
// 默认从左到右布局
|
|
85
|
+
rankdir: 'LR',
|
|
86
|
+
// 默认右下角对齐
|
|
87
|
+
align: 'UL',
|
|
88
|
+
// 紧凑树形排名算法
|
|
89
|
+
ranker: 'tight-tree',
|
|
90
|
+
// 层级间距
|
|
91
|
+
ranksep,
|
|
92
|
+
// 同层节点间距
|
|
93
|
+
nodesep,
|
|
94
|
+
// 图的水平边距
|
|
95
|
+
marginx: 120,
|
|
96
|
+
// 图的垂直边距
|
|
97
|
+
marginy: 120,
|
|
98
|
+
// 用户自定义选项覆盖默认值
|
|
99
|
+
...option,
|
|
100
|
+
}
|
|
101
|
+
this.applyDagreLayout(nodes, edges)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* 使用 Dagre 布局
|
|
106
|
+
* @param nodes - 节点数据
|
|
107
|
+
* @param edges - 边数据
|
|
108
|
+
*/
|
|
109
|
+
applyDagreLayout(nodes: BaseNodeModel[], edges: BaseEdgeModel[]) {
|
|
110
|
+
// 创建dagre图实例
|
|
111
|
+
const g = new graphlib.Graph()
|
|
112
|
+
// dagre布局配置
|
|
113
|
+
g.setGraph(this.option)
|
|
114
|
+
//构造dagre布局数据
|
|
115
|
+
g.setDefaultEdgeLabel(() => ({}))
|
|
116
|
+
nodes.forEach((node: BaseNodeModel) => {
|
|
117
|
+
g.setNode(node.id, {
|
|
118
|
+
width: node.width || 150,
|
|
119
|
+
height: node.height || 50,
|
|
120
|
+
id: node.id,
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
edges.forEach((edge: BaseEdgeModel) => {
|
|
124
|
+
g.setEdge(edge.sourceNodeId, edge.targetNodeId, {
|
|
125
|
+
id: edge.id,
|
|
126
|
+
})
|
|
127
|
+
})
|
|
128
|
+
// 开始dagre布局
|
|
129
|
+
try {
|
|
130
|
+
dagre.layout(g)
|
|
131
|
+
const newGraphData = this.convertLayoutDataToLf(nodes, edges, g)
|
|
132
|
+
this.lf.renderRawData(newGraphData)
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.error('Dagre layout error:', error)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
convertLayoutDataToLf(
|
|
138
|
+
nodes: BaseNodeModel[],
|
|
139
|
+
edges: BaseEdgeModel[],
|
|
140
|
+
layoutData: { node: (id: string) => BaseNodeData },
|
|
141
|
+
) {
|
|
142
|
+
// 存储新的节点和边数据
|
|
143
|
+
const newNodes: NodeConfig[] = []
|
|
144
|
+
|
|
145
|
+
// 更新节点位置
|
|
146
|
+
nodes.forEach((nodeModel) => {
|
|
147
|
+
const lfNode = nodeModel.getData()
|
|
148
|
+
const newNode = layoutData.node(nodeModel.id)
|
|
149
|
+
if (!lfNode || !newNode) {
|
|
150
|
+
throw new Error(`布局错误:找不到ID为 ${nodeModel.id} 的节点`)
|
|
151
|
+
}
|
|
152
|
+
// 更新节点坐标
|
|
153
|
+
lfNode.x = newNode.x
|
|
154
|
+
lfNode.y = newNode.y
|
|
155
|
+
|
|
156
|
+
// 更新节点文本位置
|
|
157
|
+
if (lfNode?.text?.x) {
|
|
158
|
+
lfNode.text.x = newNode.x
|
|
159
|
+
lfNode.text.y = newNode.y
|
|
160
|
+
}
|
|
161
|
+
newNodes.push(lfNode)
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
const newEdges: EdgeConfig[] = processEdges(
|
|
165
|
+
this.lf,
|
|
166
|
+
this.option.rankdir,
|
|
167
|
+
this.option.isDefaultAnchor,
|
|
168
|
+
edges,
|
|
169
|
+
newNodes,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
nodes: newNodes,
|
|
174
|
+
edges: newEdges,
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const elkOptionMap = {
|
|
2
|
+
rankdir: {
|
|
3
|
+
LR: 'RIGHT',
|
|
4
|
+
TB: 'DOWN',
|
|
5
|
+
BT: 'UP',
|
|
6
|
+
RL: 'LEFT',
|
|
7
|
+
default: 'RIGHT',
|
|
8
|
+
},
|
|
9
|
+
align: {
|
|
10
|
+
UL: 'RIGHTDOWN',
|
|
11
|
+
UR: 'RIGHTUP',
|
|
12
|
+
DL: 'LEFTDOWN',
|
|
13
|
+
DR: 'LEFTUP',
|
|
14
|
+
default: 'BALANCED',
|
|
15
|
+
},
|
|
16
|
+
ranker: {
|
|
17
|
+
'network-simplex': 'NETWORK_SIMPLEX',
|
|
18
|
+
'tight-tree': 'NETWORK_SIMPLEX',
|
|
19
|
+
'longest-path': 'LONGEST_PATH',
|
|
20
|
+
default: 'NETWORK_SIMPLEX',
|
|
21
|
+
},
|
|
22
|
+
acyclicer: {
|
|
23
|
+
greedy: 'GREEDY',
|
|
24
|
+
default: 'DEPTH_FIRST',
|
|
25
|
+
},
|
|
26
|
+
}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview ElkLayout布局插件 - 提供自动化图形布局功能
|
|
3
|
+
*
|
|
4
|
+
* 本插件基于elkjs实现LogicFlow的自动化布局功能,支持多种布局方向
|
|
5
|
+
* 可自动计算节点位置和连线路径,实现整洁的图形展示
|
|
6
|
+
*/
|
|
7
|
+
import LogicFlow, { BaseNodeModel, BaseEdgeModel } from '@logicflow/core'
|
|
8
|
+
import elkConstructor from 'elkjs/lib/elk.bundled'
|
|
9
|
+
import { LayoutOptions, ElkNode } from 'elkjs/lib/elk-api'
|
|
10
|
+
import { processEdges } from '../utils/processEdge'
|
|
11
|
+
import { elkOptionMap } from './config'
|
|
12
|
+
|
|
13
|
+
import NodeConfig = LogicFlow.NodeConfig
|
|
14
|
+
import EdgeConfig = LogicFlow.EdgeConfig
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* ElkLayout布局配置选项接口
|
|
18
|
+
* @interface ElkLayoutOption
|
|
19
|
+
*/
|
|
20
|
+
export interface ElkLayoutOption {
|
|
21
|
+
/**
|
|
22
|
+
* 布局方向
|
|
23
|
+
* 'LR' | 'TB' | 'BT' | 'RL'
|
|
24
|
+
*/
|
|
25
|
+
rankdir?: 'LR' | 'TB' | 'BT' | 'RL'
|
|
26
|
+
/**
|
|
27
|
+
* 对齐方式
|
|
28
|
+
* 'UL' | 'UR' | 'DL' | 'DR'
|
|
29
|
+
*/
|
|
30
|
+
align?: 'UL' | 'UR' | 'DL' | 'DR'
|
|
31
|
+
/**
|
|
32
|
+
* 同层节点间距
|
|
33
|
+
*/
|
|
34
|
+
nodesep?: number
|
|
35
|
+
/**
|
|
36
|
+
* 层级间距
|
|
37
|
+
*/
|
|
38
|
+
ranksep?: number
|
|
39
|
+
/**
|
|
40
|
+
* 图的水平边距
|
|
41
|
+
*/
|
|
42
|
+
marginx?: number
|
|
43
|
+
/**
|
|
44
|
+
* 图的垂直边距
|
|
45
|
+
*/
|
|
46
|
+
marginy?: number
|
|
47
|
+
/**
|
|
48
|
+
* 排版算法
|
|
49
|
+
* 'network-simplex' | 'tight-tree' | 'longest-path'
|
|
50
|
+
*/
|
|
51
|
+
ranker?: 'network-simplex' | 'tight-tree' | 'longest-path'
|
|
52
|
+
/**
|
|
53
|
+
* 边间距
|
|
54
|
+
*/
|
|
55
|
+
edgesep?: number
|
|
56
|
+
/**
|
|
57
|
+
* 有向无环图处理算法
|
|
58
|
+
* 'greedy'
|
|
59
|
+
*/
|
|
60
|
+
acyclicer?: 'greedy'
|
|
61
|
+
/**
|
|
62
|
+
* 是否是默认锚点
|
|
63
|
+
* true: 会根据布局方向自动计算边的路径点
|
|
64
|
+
*/
|
|
65
|
+
isDefaultAnchor?: boolean
|
|
66
|
+
/**
|
|
67
|
+
* ELK 原生布局属性,用于覆盖默认配置
|
|
68
|
+
*/
|
|
69
|
+
elkOption?: LayoutOptions
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* ElkLayout插件接口定义
|
|
74
|
+
*/
|
|
75
|
+
export interface ElkLayoutPlugin {
|
|
76
|
+
/**
|
|
77
|
+
* 执行布局计算
|
|
78
|
+
* @param option - 布局配置选项
|
|
79
|
+
*/
|
|
80
|
+
layout(option: ElkLayoutOption): void
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* ElkLayout布局类 - LogicFlow自动布局插件
|
|
85
|
+
* 基于elkjs提供图的自动布局能力
|
|
86
|
+
*/
|
|
87
|
+
export class ElkLayout {
|
|
88
|
+
/** 插件名称,用于在LogicFlow中注册 */
|
|
89
|
+
static pluginName = 'elkLayout'
|
|
90
|
+
|
|
91
|
+
/** LogicFlow实例引用 */
|
|
92
|
+
lf: LogicFlow
|
|
93
|
+
|
|
94
|
+
/** 当前布局配置 */
|
|
95
|
+
option: ElkLayoutOption
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* 插件初始化方法,由LogicFlow自动调用
|
|
99
|
+
* @param lf - LogicFlow实例
|
|
100
|
+
*/
|
|
101
|
+
render(lf: LogicFlow) {
|
|
102
|
+
this.lf = lf
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 执行布局算法,重新排列图中的节点和边
|
|
107
|
+
* @param option - 布局配置选项
|
|
108
|
+
*/
|
|
109
|
+
layout(option: ElkLayoutOption = {}) {
|
|
110
|
+
const { nodes, edges, gridSize } = this.lf.graphModel
|
|
111
|
+
|
|
112
|
+
// 根据网格大小调整节点间距
|
|
113
|
+
let nodesep = 100
|
|
114
|
+
let ranksep = 150
|
|
115
|
+
if (gridSize > 20) {
|
|
116
|
+
nodesep = gridSize * 2
|
|
117
|
+
ranksep = gridSize * 2
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 合并默认配置和用户配置
|
|
121
|
+
this.option = {
|
|
122
|
+
// 默认从左到右布局
|
|
123
|
+
rankdir: 'LR',
|
|
124
|
+
// 默认右下角对齐
|
|
125
|
+
align: 'UL',
|
|
126
|
+
// 紧凑树形排名算法
|
|
127
|
+
ranker: 'tight-tree',
|
|
128
|
+
// 层级间距
|
|
129
|
+
ranksep,
|
|
130
|
+
// 同层节点间距
|
|
131
|
+
nodesep,
|
|
132
|
+
// 图的水平边距
|
|
133
|
+
marginx: 120,
|
|
134
|
+
// 图的垂直边距
|
|
135
|
+
marginy: 120,
|
|
136
|
+
// 用户自定义选项覆盖默认值
|
|
137
|
+
...option,
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
this.applyElkLayout(nodes, edges)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* 使用 ELK 布局
|
|
145
|
+
* @param nodes - 节点数据
|
|
146
|
+
* @param edges - 边数据
|
|
147
|
+
* @param elkOption - ELK 配置选项
|
|
148
|
+
*/
|
|
149
|
+
async applyElkLayout(nodes: BaseNodeModel[], edges: BaseEdgeModel[]) {
|
|
150
|
+
// 创建elk实例
|
|
151
|
+
const elk = new elkConstructor()
|
|
152
|
+
// elk布局配置
|
|
153
|
+
const layoutOptions = this.convertOptionsToElk()
|
|
154
|
+
// 构造elk布局数据
|
|
155
|
+
const elkGraph = {
|
|
156
|
+
id: 'root',
|
|
157
|
+
children: nodes.map((node) => ({
|
|
158
|
+
id: node.id,
|
|
159
|
+
width: node.width || 150,
|
|
160
|
+
height: node.height || 50,
|
|
161
|
+
})),
|
|
162
|
+
edges: edges.map((edge) => ({
|
|
163
|
+
id: edge.id,
|
|
164
|
+
sources: [edge.sourceNodeId],
|
|
165
|
+
targets: [edge.targetNodeId],
|
|
166
|
+
})),
|
|
167
|
+
}
|
|
168
|
+
// 开始elk布局
|
|
169
|
+
try {
|
|
170
|
+
const elkLayoutGraph = await elk.layout(elkGraph, { layoutOptions })
|
|
171
|
+
const newGraphData = this.convertLayoutDataToLf(
|
|
172
|
+
nodes,
|
|
173
|
+
edges,
|
|
174
|
+
elkLayoutGraph,
|
|
175
|
+
)
|
|
176
|
+
this.lf.renderRawData(newGraphData)
|
|
177
|
+
} catch (error) {
|
|
178
|
+
console.error('ELK layout error:', error)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
convertOptionsToElk(): LayoutOptions {
|
|
182
|
+
// elk布局配置
|
|
183
|
+
const rankdir = (this.option.rankdir ||
|
|
184
|
+
'default') as keyof typeof elkOptionMap.rankdir
|
|
185
|
+
const align = (this.option.align ||
|
|
186
|
+
'default') as keyof typeof elkOptionMap.align
|
|
187
|
+
const ranker = (this.option.ranker ||
|
|
188
|
+
'default') as keyof typeof elkOptionMap.ranker
|
|
189
|
+
const acyclicer = (this.option.acyclicer ||
|
|
190
|
+
'default') as keyof typeof elkOptionMap.acyclicer
|
|
191
|
+
|
|
192
|
+
const layoutOptions = {
|
|
193
|
+
'elk.algorithm': 'layered',
|
|
194
|
+
'elk.direction':
|
|
195
|
+
elkOptionMap.rankdir[rankdir] || elkOptionMap.rankdir.default,
|
|
196
|
+
'elk.layered.nodePlacement.bk.fixedAlignment':
|
|
197
|
+
elkOptionMap.align[align] || elkOptionMap.align.default,
|
|
198
|
+
'elk.layered.layering.strategy':
|
|
199
|
+
elkOptionMap.ranker[ranker] || elkOptionMap.ranker.default,
|
|
200
|
+
'elk.layered.cycleBreaking.strategy':
|
|
201
|
+
elkOptionMap.acyclicer[acyclicer] || elkOptionMap.acyclicer.default,
|
|
202
|
+
'elk.padding': `[top=${this.option.marginx || 20}, left=${this.option.marginy || 20}, bottom=${this.option.marginx || 20}, right=${this.option.marginy || 20}]`,
|
|
203
|
+
'elk.spacing.nodeNode': `${this.option.nodesep || 50}`,
|
|
204
|
+
'elk.spacing.edgeEdge': `${this.option.edgesep || 10}`,
|
|
205
|
+
'elk.layered.spacing.nodeNodeBetweenLayers': `${this.option.ranksep || 100}`,
|
|
206
|
+
'elk.layered.considerModelOrder.strategy': 'NODES_AND_EDGES',
|
|
207
|
+
'elk.layered.nodePlacement.strategy': 'BRANDES_KOEPF',
|
|
208
|
+
...this.option.elkOption,
|
|
209
|
+
}
|
|
210
|
+
return layoutOptions
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
convertLayoutDataToLf(
|
|
214
|
+
nodes: BaseNodeModel[],
|
|
215
|
+
edges: BaseEdgeModel[],
|
|
216
|
+
layoutData: ElkNode,
|
|
217
|
+
) {
|
|
218
|
+
// 存储新的节点和边数据
|
|
219
|
+
const newNodes: NodeConfig[] = []
|
|
220
|
+
|
|
221
|
+
// 更新节点位置
|
|
222
|
+
nodes.forEach((nodeModel) => {
|
|
223
|
+
const lfNode = nodeModel.getData()
|
|
224
|
+
const newNode = (layoutData?.children || []).find(
|
|
225
|
+
(n) => n.id === nodeModel.id,
|
|
226
|
+
)
|
|
227
|
+
if (!lfNode || !newNode || !newNode.x || !newNode.y) {
|
|
228
|
+
throw new Error(`布局错误:找不到ID为 ${nodeModel.id} 的节点`)
|
|
229
|
+
}
|
|
230
|
+
// 更新节点坐标
|
|
231
|
+
lfNode.x = newNode.x + nodeModel.width / 2
|
|
232
|
+
lfNode.y = newNode.y + nodeModel.height / 2
|
|
233
|
+
|
|
234
|
+
// 更新节点文本位置
|
|
235
|
+
if (lfNode?.text?.x) {
|
|
236
|
+
lfNode.text.x = newNode.x + nodeModel.width / 2
|
|
237
|
+
lfNode.text.y = newNode.y + nodeModel.height / 2
|
|
238
|
+
}
|
|
239
|
+
newNodes.push(lfNode)
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
const newEdges: EdgeConfig[] = processEdges(
|
|
243
|
+
this.lf,
|
|
244
|
+
this.option.rankdir,
|
|
245
|
+
this.option.isDefaultAnchor,
|
|
246
|
+
edges,
|
|
247
|
+
newNodes,
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
nodes: newNodes,
|
|
252
|
+
edges: newEdges,
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
package/src/index.ts
CHANGED