@flowgram.ai/free-auto-layout-plugin 0.1.0-alpha.3 → 0.1.0-alpha.30
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/dist/esm/index.js +308 -127
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.mts +152 -31
- package/dist/index.d.ts +152 -31
- package/dist/index.js +312 -130
- package/dist/index.js.map +1 -1
- package/package.json +15 -16
package/dist/esm/index.js
CHANGED
|
@@ -2081,21 +2081,47 @@ import { definePluginCreator } from "@flowgram.ai/core";
|
|
|
2081
2081
|
|
|
2082
2082
|
// src/services.ts
|
|
2083
2083
|
import { inject, injectable } from "inversify";
|
|
2084
|
+
import { Rectangle } from "@flowgram.ai/utils";
|
|
2084
2085
|
import {
|
|
2085
2086
|
WorkflowDocument,
|
|
2086
2087
|
WorkflowNodeLinesData
|
|
2087
2088
|
} from "@flowgram.ai/free-layout-core";
|
|
2089
|
+
import { Playground } from "@flowgram.ai/core";
|
|
2090
|
+
|
|
2091
|
+
// src/layout/constant.ts
|
|
2092
|
+
var DefaultLayoutConfig = {
|
|
2093
|
+
rankdir: "LR",
|
|
2094
|
+
align: void 0,
|
|
2095
|
+
nodesep: 100,
|
|
2096
|
+
edgesep: 10,
|
|
2097
|
+
ranksep: 100,
|
|
2098
|
+
marginx: 0,
|
|
2099
|
+
marginy: 0,
|
|
2100
|
+
acyclicer: void 0,
|
|
2101
|
+
ranker: "network-simplex"
|
|
2102
|
+
};
|
|
2103
|
+
var DefaultLayoutOptions = {
|
|
2104
|
+
filterNode: void 0,
|
|
2105
|
+
getFollowNode: void 0,
|
|
2106
|
+
disableFitView: false,
|
|
2107
|
+
enableAnimation: false,
|
|
2108
|
+
animationDuration: 300
|
|
2109
|
+
};
|
|
2088
2110
|
|
|
2089
2111
|
// src/layout/store.ts
|
|
2090
|
-
import {
|
|
2112
|
+
import { FlowNodeBaseType } from "@flowgram.ai/document";
|
|
2091
2113
|
var LayoutStore = class {
|
|
2092
|
-
constructor() {
|
|
2114
|
+
constructor(config) {
|
|
2115
|
+
this.config = config;
|
|
2093
2116
|
this.init = false;
|
|
2094
2117
|
}
|
|
2095
2118
|
get initialized() {
|
|
2096
2119
|
return this.init;
|
|
2097
2120
|
}
|
|
2098
2121
|
getNode(id) {
|
|
2122
|
+
if (!id) {
|
|
2123
|
+
return void 0;
|
|
2124
|
+
}
|
|
2099
2125
|
return this.store.nodes.get(id);
|
|
2100
2126
|
}
|
|
2101
2127
|
getNodeByIndex(index) {
|
|
@@ -2111,56 +2137,76 @@ var LayoutStore = class {
|
|
|
2111
2137
|
get edges() {
|
|
2112
2138
|
return Array.from(this.store.edges.values());
|
|
2113
2139
|
}
|
|
2114
|
-
create(params) {
|
|
2140
|
+
create(params, options) {
|
|
2141
|
+
this.container = params.container;
|
|
2115
2142
|
this.store = this.createStore(params);
|
|
2116
2143
|
this.indexMap = this.createIndexMap();
|
|
2144
|
+
this.setOptions(options);
|
|
2117
2145
|
this.init = true;
|
|
2118
2146
|
}
|
|
2119
2147
|
/** 创建布局数据 */
|
|
2120
2148
|
createStore(params) {
|
|
2121
|
-
const {
|
|
2149
|
+
const { layoutNodes, layoutEdges } = params;
|
|
2150
|
+
const virtualEdges = this.createVirtualEdges(params);
|
|
2122
2151
|
const store = {
|
|
2123
2152
|
nodes: /* @__PURE__ */ new Map(),
|
|
2124
2153
|
edges: /* @__PURE__ */ new Map()
|
|
2125
2154
|
};
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
const layoutNode = {
|
|
2129
|
-
id: node.id,
|
|
2130
|
-
entity: node,
|
|
2131
|
-
index: "",
|
|
2132
|
-
// 初始化时,index 未计算
|
|
2133
|
-
rank: -1,
|
|
2134
|
-
// 初始化时,节点还未布局,层级为-1
|
|
2135
|
-
order: -1,
|
|
2136
|
-
// 初始化时,节点还未布局,顺序为-1
|
|
2137
|
-
position: { x: bounds.center.x, y: bounds.center.y },
|
|
2138
|
-
offset: { x: 0, y: 0 },
|
|
2139
|
-
size: { width: bounds.width, height: bounds.height },
|
|
2140
|
-
hasChildren: node.collapsedChildren?.length > 0
|
|
2141
|
-
};
|
|
2142
|
-
store.nodes.set(layoutNode.id, layoutNode);
|
|
2143
|
-
});
|
|
2144
|
-
edges.forEach((edge) => {
|
|
2145
|
-
const { from, to } = edge.info;
|
|
2146
|
-
if (!from || !to || edge.vertical) {
|
|
2147
|
-
return;
|
|
2148
|
-
}
|
|
2149
|
-
const layoutEdge = {
|
|
2150
|
-
id: edge.id,
|
|
2151
|
-
entity: edge,
|
|
2152
|
-
from,
|
|
2153
|
-
to,
|
|
2154
|
-
fromIndex: "",
|
|
2155
|
-
// 初始化时,index 未计算
|
|
2156
|
-
toIndex: "",
|
|
2157
|
-
// 初始化时,index 未计算
|
|
2158
|
-
name: edge.id
|
|
2159
|
-
};
|
|
2160
|
-
store.edges.set(layoutEdge.id, layoutEdge);
|
|
2161
|
-
});
|
|
2155
|
+
layoutNodes.forEach((node) => store.nodes.set(node.id, node));
|
|
2156
|
+
layoutEdges.concat(virtualEdges).forEach((edge) => store.edges.set(edge.id, edge));
|
|
2162
2157
|
return store;
|
|
2163
2158
|
}
|
|
2159
|
+
/** 创建虚拟线条数据 */
|
|
2160
|
+
createVirtualEdges(params) {
|
|
2161
|
+
const { layoutNodes, layoutEdges } = params;
|
|
2162
|
+
const nodes = layoutNodes.map((layoutNode) => layoutNode.entity);
|
|
2163
|
+
const edges = layoutEdges.map((layoutEdge) => layoutEdge.entity);
|
|
2164
|
+
const groupNodes = nodes.filter((n) => n.flowNodeType === FlowNodeBaseType.GROUP);
|
|
2165
|
+
const virtualEdges = groupNodes.map((group) => {
|
|
2166
|
+
const { id: groupId, blocks = [] } = group;
|
|
2167
|
+
const blockIdSet = new Set(blocks.map((b) => b.id));
|
|
2168
|
+
const groupFromEdges = edges.filter((edge) => blockIdSet.has(edge.to?.id ?? "")).map((edge) => {
|
|
2169
|
+
const { from, to } = edge.info;
|
|
2170
|
+
if (!from || !to || edge.vertical) {
|
|
2171
|
+
return;
|
|
2172
|
+
}
|
|
2173
|
+
const id = `virtual_${groupId}_from_${from}_to_${to}`;
|
|
2174
|
+
const layoutEdge = {
|
|
2175
|
+
id,
|
|
2176
|
+
entity: edge,
|
|
2177
|
+
from,
|
|
2178
|
+
to: groupId,
|
|
2179
|
+
fromIndex: "",
|
|
2180
|
+
// 初始化时,index 未计算
|
|
2181
|
+
toIndex: "",
|
|
2182
|
+
// 初始化时,index 未计算
|
|
2183
|
+
name: id
|
|
2184
|
+
};
|
|
2185
|
+
return layoutEdge;
|
|
2186
|
+
}).filter(Boolean);
|
|
2187
|
+
const groupToEdges = edges.filter((edge) => blockIdSet.has(edge.from?.id ?? "")).map((edge) => {
|
|
2188
|
+
const { from, to } = edge.info;
|
|
2189
|
+
if (!from || !to || edge.vertical) {
|
|
2190
|
+
return;
|
|
2191
|
+
}
|
|
2192
|
+
const id = `virtual_${groupId}_from_${from}_to_${to}`;
|
|
2193
|
+
const layoutEdge = {
|
|
2194
|
+
id,
|
|
2195
|
+
entity: edge,
|
|
2196
|
+
from: groupId,
|
|
2197
|
+
to,
|
|
2198
|
+
fromIndex: "",
|
|
2199
|
+
// 初始化时,index 未计算
|
|
2200
|
+
toIndex: "",
|
|
2201
|
+
// 初始化时,index 未计算
|
|
2202
|
+
name: id
|
|
2203
|
+
};
|
|
2204
|
+
return layoutEdge;
|
|
2205
|
+
}).filter(Boolean);
|
|
2206
|
+
return [...groupFromEdges, ...groupToEdges];
|
|
2207
|
+
}).flat();
|
|
2208
|
+
return virtualEdges;
|
|
2209
|
+
}
|
|
2164
2210
|
/** 创建节点索引映射 */
|
|
2165
2211
|
createIndexMap() {
|
|
2166
2212
|
const nodeIndexes = this.sortNodes();
|
|
@@ -2196,27 +2242,42 @@ var LayoutStore = class {
|
|
|
2196
2242
|
this.nodes.forEach((node) => {
|
|
2197
2243
|
nodeIdList.push(node.id);
|
|
2198
2244
|
});
|
|
2199
|
-
const sameFromEdges = /* @__PURE__ */ new Map();
|
|
2200
2245
|
this.edges.forEach((edge) => {
|
|
2201
2246
|
nodeIdList.push(edge.to);
|
|
2202
|
-
if (edge.entity.info.fromPort) {
|
|
2203
|
-
const edgesForFrom = sameFromEdges.get(edge.from) || [];
|
|
2204
|
-
sameFromEdges.set(edge.from, [...edgesForFrom, edge]);
|
|
2205
|
-
}
|
|
2206
2247
|
});
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2248
|
+
const visited = /* @__PURE__ */ new Set();
|
|
2249
|
+
const visit = (node) => {
|
|
2250
|
+
if (!node || visited.has(node.id)) {
|
|
2251
|
+
return;
|
|
2252
|
+
}
|
|
2253
|
+
visited.add(node.id);
|
|
2254
|
+
nodeIdList.push(node.id);
|
|
2255
|
+
node.blocks.forEach((child) => {
|
|
2256
|
+
visit(child);
|
|
2257
|
+
});
|
|
2258
|
+
const { outputLines } = node.lines;
|
|
2259
|
+
const sortedLines = outputLines.sort((a, b) => {
|
|
2260
|
+
const aNode = this.getNode(a.to?.id);
|
|
2261
|
+
const bNode = this.getNode(b.to?.id);
|
|
2262
|
+
const aPort = a.fromPort;
|
|
2263
|
+
const bPort = b.fromPort;
|
|
2264
|
+
if (aPort === bPort && aNode && bNode) {
|
|
2265
|
+
return aNode.position.y - bNode.position.y;
|
|
2266
|
+
}
|
|
2211
2267
|
if (aPort && bPort) {
|
|
2212
2268
|
return aPort.point.y - bPort.point.y;
|
|
2213
2269
|
}
|
|
2214
2270
|
return 0;
|
|
2215
2271
|
});
|
|
2216
|
-
|
|
2217
|
-
|
|
2272
|
+
sortedLines.forEach((line) => {
|
|
2273
|
+
const { to } = line;
|
|
2274
|
+
if (!to) {
|
|
2275
|
+
return;
|
|
2276
|
+
}
|
|
2277
|
+
visit(to);
|
|
2218
2278
|
});
|
|
2219
|
-
}
|
|
2279
|
+
};
|
|
2280
|
+
visit(this.container.entity);
|
|
2220
2281
|
const uniqueNodeIds = nodeIdList.reduceRight((acc, nodeId) => {
|
|
2221
2282
|
if (!acc.includes(nodeId)) {
|
|
2222
2283
|
acc.unshift(nodeId);
|
|
@@ -2225,21 +2286,52 @@ var LayoutStore = class {
|
|
|
2225
2286
|
}, []);
|
|
2226
2287
|
return uniqueNodeIds;
|
|
2227
2288
|
}
|
|
2289
|
+
/** 记录运行选项 */
|
|
2290
|
+
setOptions(options) {
|
|
2291
|
+
this.options = options;
|
|
2292
|
+
this.setFollowNode(options.getFollowNode);
|
|
2293
|
+
}
|
|
2294
|
+
/** 设置跟随节点配置 */
|
|
2295
|
+
setFollowNode(getFollowNode) {
|
|
2296
|
+
if (!getFollowNode) return;
|
|
2297
|
+
const context = { store: this };
|
|
2298
|
+
this.nodes.forEach((node) => {
|
|
2299
|
+
const followTo = getFollowNode(node, context)?.followTo;
|
|
2300
|
+
if (!followTo) return;
|
|
2301
|
+
const followToNode = this.getNode(followTo);
|
|
2302
|
+
if (!followToNode) return;
|
|
2303
|
+
if (!followToNode.followedBy) {
|
|
2304
|
+
followToNode.followedBy = [];
|
|
2305
|
+
}
|
|
2306
|
+
followToNode.followedBy.push(node.id);
|
|
2307
|
+
node.followTo = followTo;
|
|
2308
|
+
});
|
|
2309
|
+
}
|
|
2228
2310
|
};
|
|
2229
2311
|
|
|
2230
2312
|
// src/layout/position.ts
|
|
2231
|
-
import {
|
|
2232
|
-
import { startTween, TransformData } from "@flowgram.ai/core";
|
|
2313
|
+
import { startTween } from "@flowgram.ai/core";
|
|
2233
2314
|
var LayoutPosition = class {
|
|
2234
2315
|
constructor(store) {
|
|
2235
2316
|
this.store = store;
|
|
2236
2317
|
}
|
|
2237
2318
|
async position() {
|
|
2319
|
+
if (this.store.options.enableAnimation) {
|
|
2320
|
+
return this.positionWithAnimation();
|
|
2321
|
+
}
|
|
2322
|
+
return this.positionDirectly();
|
|
2323
|
+
}
|
|
2324
|
+
positionDirectly() {
|
|
2325
|
+
this.store.nodes.forEach((layoutNode) => {
|
|
2326
|
+
this.updateNodePosition({ layoutNode, step: 100 });
|
|
2327
|
+
});
|
|
2328
|
+
}
|
|
2329
|
+
async positionWithAnimation() {
|
|
2238
2330
|
return new Promise((resolve) => {
|
|
2239
2331
|
startTween({
|
|
2240
2332
|
from: { d: 0 },
|
|
2241
2333
|
to: { d: 100 },
|
|
2242
|
-
duration:
|
|
2334
|
+
duration: this.store.options.animationDuration ?? 0,
|
|
2243
2335
|
onUpdate: (v) => {
|
|
2244
2336
|
this.store.nodes.forEach((layoutNode) => {
|
|
2245
2337
|
this.updateNodePosition({ layoutNode, step: v.d });
|
|
@@ -2253,41 +2345,28 @@ var LayoutPosition = class {
|
|
|
2253
2345
|
}
|
|
2254
2346
|
updateNodePosition(params) {
|
|
2255
2347
|
const { layoutNode, step } = params;
|
|
2256
|
-
const transform = layoutNode.entity.
|
|
2257
|
-
const
|
|
2348
|
+
const { transform } = layoutNode.entity.transform;
|
|
2349
|
+
const layoutPosition = {
|
|
2258
2350
|
x: layoutNode.position.x + layoutNode.offset.x,
|
|
2259
|
-
|
|
2351
|
+
// layoutNode.position.y 是中心点,但画布节点原点在上边沿的中间,所以 y 坐标需要转化后一下
|
|
2352
|
+
y: layoutNode.position.y + layoutNode.offset.y - (layoutNode.size.height - layoutNode.padding.top - layoutNode.padding.bottom) / 2
|
|
2353
|
+
};
|
|
2354
|
+
const deltaX = (layoutPosition.x - transform.position.x) * step / 100;
|
|
2355
|
+
const deltaY = (layoutPosition.y - transform.position.y) * step / 100;
|
|
2356
|
+
const position2 = {
|
|
2357
|
+
x: transform.position.x + deltaX,
|
|
2358
|
+
y: transform.position.y + deltaY
|
|
2260
2359
|
};
|
|
2261
|
-
const deltaX = (position2.x - transform.position.x) * step / 100;
|
|
2262
|
-
const deltaY = (position2.y - transform.bounds.height / 2 - transform.position.y) * step / 100;
|
|
2263
|
-
if (layoutNode.hasChildren) {
|
|
2264
|
-
layoutNode.entity.collapsedChildren.forEach((childNode) => {
|
|
2265
|
-
const childNodeTransformData = childNode.getData(FlowNodeTransformData2);
|
|
2266
|
-
childNodeTransformData.fireChange();
|
|
2267
|
-
});
|
|
2268
|
-
}
|
|
2269
2360
|
transform.update({
|
|
2270
|
-
position:
|
|
2271
|
-
x: transform.position.x + deltaX,
|
|
2272
|
-
y: transform.position.y + deltaY
|
|
2273
|
-
}
|
|
2361
|
+
position: position2
|
|
2274
2362
|
});
|
|
2363
|
+
const document = layoutNode.entity.document;
|
|
2364
|
+
document.layout.updateAffectedTransform(layoutNode.entity);
|
|
2275
2365
|
}
|
|
2276
2366
|
};
|
|
2277
2367
|
|
|
2278
2368
|
// src/layout/dagre.ts
|
|
2279
|
-
import { FlowNodeTransformData as FlowNodeTransformData3 } from "@flowgram.ai/document";
|
|
2280
2369
|
import { Graph as DagreGraph } from "@dagrejs/graphlib";
|
|
2281
|
-
|
|
2282
|
-
// src/layout/constant.ts
|
|
2283
|
-
var DagreLayoutOptions = {
|
|
2284
|
-
rankdir: "LR",
|
|
2285
|
-
nodesep: 100,
|
|
2286
|
-
ranksep: 100,
|
|
2287
|
-
ranker: "network-simplex"
|
|
2288
|
-
};
|
|
2289
|
-
|
|
2290
|
-
// src/layout/dagre.ts
|
|
2291
2370
|
var DagreLayout = class {
|
|
2292
2371
|
constructor(store) {
|
|
2293
2372
|
this.store = store;
|
|
@@ -2336,17 +2415,11 @@ var DagreLayout = class {
|
|
|
2336
2415
|
createGraph() {
|
|
2337
2416
|
const graph = new DagreGraph({ multigraph: true });
|
|
2338
2417
|
graph.setDefaultEdgeLabel(() => ({}));
|
|
2339
|
-
graph.setGraph(
|
|
2418
|
+
graph.setGraph(this.store.config);
|
|
2340
2419
|
return graph;
|
|
2341
2420
|
}
|
|
2342
2421
|
graphSetData() {
|
|
2343
|
-
const nodes =
|
|
2344
|
-
const edges = Array.from(this.store.edges.values()).sort((next, prev) => {
|
|
2345
|
-
if (next.fromIndex === prev.fromIndex) {
|
|
2346
|
-
return next.toIndex < prev.toIndex ? -1 : 1;
|
|
2347
|
-
}
|
|
2348
|
-
return next.fromIndex < prev.fromIndex ? -1 : 1;
|
|
2349
|
-
});
|
|
2422
|
+
const { nodes, edges } = this.store;
|
|
2350
2423
|
nodes.forEach((layoutNode) => {
|
|
2351
2424
|
this.graph.setNode(layoutNode.index, {
|
|
2352
2425
|
originID: layoutNode.id,
|
|
@@ -2354,7 +2427,12 @@ var DagreLayout = class {
|
|
|
2354
2427
|
height: layoutNode.size.height
|
|
2355
2428
|
});
|
|
2356
2429
|
});
|
|
2357
|
-
edges.
|
|
2430
|
+
edges.sort((next, prev) => {
|
|
2431
|
+
if (next.fromIndex === prev.fromIndex) {
|
|
2432
|
+
return next.toIndex < prev.toIndex ? -1 : 1;
|
|
2433
|
+
}
|
|
2434
|
+
return next.fromIndex < prev.fromIndex ? -1 : 1;
|
|
2435
|
+
}).forEach((layoutEdge) => {
|
|
2358
2436
|
this.graph.setEdge({
|
|
2359
2437
|
v: layoutEdge.fromIndex,
|
|
2360
2438
|
w: layoutEdge.toIndex,
|
|
@@ -2385,12 +2463,11 @@ var DagreLayout = class {
|
|
|
2385
2463
|
return Number.isNaN(number) ? 0 : number;
|
|
2386
2464
|
}
|
|
2387
2465
|
getOffsetX(layoutNode) {
|
|
2388
|
-
if (
|
|
2466
|
+
if (layoutNode.layoutNodes.length === 0) {
|
|
2389
2467
|
return 0;
|
|
2390
2468
|
}
|
|
2391
|
-
const
|
|
2392
|
-
const
|
|
2393
|
-
const leftOffset = -bounds.width / 2 + padding.left;
|
|
2469
|
+
const { padding } = layoutNode;
|
|
2470
|
+
const leftOffset = -layoutNode.size.width / 2 + padding.left;
|
|
2394
2471
|
return leftOffset;
|
|
2395
2472
|
}
|
|
2396
2473
|
setOrderAndRank(g) {
|
|
@@ -2458,14 +2535,13 @@ var DagreLayout = class {
|
|
|
2458
2535
|
|
|
2459
2536
|
// src/layout/layout.ts
|
|
2460
2537
|
var Layout = class {
|
|
2461
|
-
constructor() {
|
|
2462
|
-
this._store = new LayoutStore();
|
|
2538
|
+
constructor(config) {
|
|
2539
|
+
this._store = new LayoutStore(config);
|
|
2463
2540
|
this._layout = new DagreLayout(this._store);
|
|
2464
2541
|
this._position = new LayoutPosition(this._store);
|
|
2465
2542
|
}
|
|
2466
|
-
init(params, options
|
|
2467
|
-
this._store.create(params);
|
|
2468
|
-
this.setFollowNode(options.getFollowNode);
|
|
2543
|
+
init(params, options) {
|
|
2544
|
+
this._store.create(params, options);
|
|
2469
2545
|
}
|
|
2470
2546
|
layout() {
|
|
2471
2547
|
if (!this._store.initialized) {
|
|
@@ -2479,44 +2555,144 @@ var Layout = class {
|
|
|
2479
2555
|
}
|
|
2480
2556
|
return await this._position.position();
|
|
2481
2557
|
}
|
|
2482
|
-
setFollowNode(getFollowNode) {
|
|
2483
|
-
if (!getFollowNode) return;
|
|
2484
|
-
const context = { store: this._store };
|
|
2485
|
-
this._store.nodes.forEach((node) => {
|
|
2486
|
-
const followTo = getFollowNode(node, context)?.followTo;
|
|
2487
|
-
if (!followTo) return;
|
|
2488
|
-
const followToNode = this._store.getNode(followTo);
|
|
2489
|
-
if (!followToNode) return;
|
|
2490
|
-
if (!followToNode.followedBy) {
|
|
2491
|
-
followToNode.followedBy = [];
|
|
2492
|
-
}
|
|
2493
|
-
followToNode.followedBy.push(node.id);
|
|
2494
|
-
node.followTo = followTo;
|
|
2495
|
-
});
|
|
2496
|
-
}
|
|
2497
2558
|
};
|
|
2498
2559
|
|
|
2499
2560
|
// src/services.ts
|
|
2500
2561
|
var AutoLayoutService = class {
|
|
2562
|
+
constructor() {
|
|
2563
|
+
this.layoutConfig = DefaultLayoutConfig;
|
|
2564
|
+
}
|
|
2565
|
+
init(options) {
|
|
2566
|
+
this.layoutConfig = {
|
|
2567
|
+
...this.layoutConfig,
|
|
2568
|
+
...options.layoutConfig
|
|
2569
|
+
};
|
|
2570
|
+
}
|
|
2501
2571
|
async layout(options = {}) {
|
|
2502
|
-
|
|
2572
|
+
const layoutOptions = {
|
|
2573
|
+
...DefaultLayoutOptions,
|
|
2574
|
+
...options
|
|
2575
|
+
};
|
|
2576
|
+
const containerNode = layoutOptions.containerNode ?? this.document.root;
|
|
2577
|
+
const container = this.createLayoutNode(containerNode, options);
|
|
2578
|
+
const layouts = await this.layoutNode(container, layoutOptions);
|
|
2579
|
+
const rect = this.getLayoutNodeRect(container);
|
|
2580
|
+
const positionPromise = layouts.map((layout2) => layout2.position());
|
|
2581
|
+
const fitViewPromise = this.fitView(layoutOptions, rect);
|
|
2582
|
+
await Promise.all([...positionPromise, fitViewPromise]);
|
|
2583
|
+
}
|
|
2584
|
+
async fitView(options, rect) {
|
|
2585
|
+
if (options.disableFitView === true) {
|
|
2586
|
+
return;
|
|
2587
|
+
}
|
|
2588
|
+
return this.playground.config.fitView(rect, options.enableAnimation, 30);
|
|
2503
2589
|
}
|
|
2504
|
-
async layoutNode(
|
|
2505
|
-
const
|
|
2506
|
-
if (
|
|
2590
|
+
async layoutNode(container, options) {
|
|
2591
|
+
const { layoutNodes, layoutEdges } = container;
|
|
2592
|
+
if (layoutNodes.length === 0) {
|
|
2593
|
+
return [];
|
|
2594
|
+
}
|
|
2595
|
+
const childrenLayouts = (await Promise.all(layoutNodes.map((n) => this.layoutNode(n, options)))).flat();
|
|
2596
|
+
const layoutConfig = {
|
|
2597
|
+
...this.layoutConfig,
|
|
2598
|
+
...options.layoutConfig
|
|
2599
|
+
};
|
|
2600
|
+
const layout2 = new Layout(layoutConfig);
|
|
2601
|
+
layout2.init({ container, layoutNodes, layoutEdges }, options);
|
|
2602
|
+
layout2.layout();
|
|
2603
|
+
const rect = this.getLayoutNodeRect(container);
|
|
2604
|
+
container.size = {
|
|
2605
|
+
width: rect.width,
|
|
2606
|
+
height: rect.height
|
|
2607
|
+
};
|
|
2608
|
+
return [...childrenLayouts, layout2];
|
|
2609
|
+
}
|
|
2610
|
+
createLayoutNodes(nodes, options) {
|
|
2611
|
+
return nodes.map((node) => this.createLayoutNode(node, options));
|
|
2612
|
+
}
|
|
2613
|
+
/** 创建节点布局数据 */
|
|
2614
|
+
createLayoutNode(node, options) {
|
|
2615
|
+
const blocks = node.blocks.filter(
|
|
2616
|
+
(blockNode) => options.filterNode ? options.filterNode?.({ node: blockNode, parent: node.parent }) : true
|
|
2617
|
+
);
|
|
2618
|
+
const edges = this.getNodesAllLines(blocks).filter(
|
|
2619
|
+
(edge) => options.filterLine ? options.filterLine?.({ line: edge }) : true
|
|
2620
|
+
);
|
|
2621
|
+
const layoutNodes = this.createLayoutNodes(blocks, options);
|
|
2622
|
+
const layoutEdges = this.createLayoutEdges(edges);
|
|
2623
|
+
const { bounds, padding } = node.transform;
|
|
2624
|
+
const { width: width2, height, center } = bounds;
|
|
2625
|
+
const { x, y } = center;
|
|
2626
|
+
const layoutNode = {
|
|
2627
|
+
id: node.id,
|
|
2628
|
+
entity: node,
|
|
2629
|
+
index: "",
|
|
2630
|
+
// 初始化时,index 未计算
|
|
2631
|
+
rank: -1,
|
|
2632
|
+
// 初始化时,节点还未布局,层级为-1
|
|
2633
|
+
order: -1,
|
|
2634
|
+
// 初始化时,节点还未布局,顺序为-1
|
|
2635
|
+
position: { x, y },
|
|
2636
|
+
offset: { x: 0, y: 0 },
|
|
2637
|
+
padding,
|
|
2638
|
+
size: { width: width2, height },
|
|
2639
|
+
layoutNodes,
|
|
2640
|
+
layoutEdges
|
|
2641
|
+
};
|
|
2642
|
+
return layoutNode;
|
|
2643
|
+
}
|
|
2644
|
+
createLayoutEdges(edges) {
|
|
2645
|
+
const layoutEdges = edges.map((edge) => this.createLayoutEdge(edge)).filter(Boolean);
|
|
2646
|
+
return layoutEdges;
|
|
2647
|
+
}
|
|
2648
|
+
/** 创建线条布局数据 */
|
|
2649
|
+
createLayoutEdge(edge) {
|
|
2650
|
+
const { from, to } = edge.info;
|
|
2651
|
+
if (!from || !to) {
|
|
2507
2652
|
return;
|
|
2508
2653
|
}
|
|
2509
|
-
const
|
|
2510
|
-
|
|
2511
|
-
|
|
2654
|
+
const layoutEdge = {
|
|
2655
|
+
id: edge.id,
|
|
2656
|
+
entity: edge,
|
|
2657
|
+
from,
|
|
2658
|
+
to,
|
|
2659
|
+
fromIndex: "",
|
|
2660
|
+
// 初始化时,index 未计算
|
|
2661
|
+
toIndex: "",
|
|
2662
|
+
// 初始化时,index 未计算
|
|
2663
|
+
name: edge.id
|
|
2664
|
+
};
|
|
2665
|
+
return layoutEdge;
|
|
2666
|
+
}
|
|
2667
|
+
getNodesAllLines(nodes) {
|
|
2668
|
+
const lines = nodes.map((node) => {
|
|
2669
|
+
const linesData = node.getData(WorkflowNodeLinesData);
|
|
2670
|
+
const outputLines = linesData.outputLines.filter(Boolean);
|
|
2671
|
+
const inputLines = linesData.inputLines.filter(Boolean);
|
|
2672
|
+
return [...outputLines, ...inputLines];
|
|
2512
2673
|
}).flat();
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2674
|
+
return lines;
|
|
2675
|
+
}
|
|
2676
|
+
getLayoutNodeRect(layoutNode) {
|
|
2677
|
+
const rects = layoutNode.layoutNodes.map((node) => this.layoutNodeRect(node));
|
|
2678
|
+
const rect = Rectangle.enlarge(rects);
|
|
2679
|
+
const { padding } = layoutNode;
|
|
2680
|
+
const width2 = rect.width + padding.left + padding.right;
|
|
2681
|
+
const height = rect.height + padding.top + padding.bottom;
|
|
2682
|
+
const x = rect.x - padding.left;
|
|
2683
|
+
const y = rect.y - padding.top;
|
|
2684
|
+
return new Rectangle(x, y, width2, height);
|
|
2685
|
+
}
|
|
2686
|
+
layoutNodeRect(layoutNode) {
|
|
2687
|
+
const { width: width2, height } = layoutNode.size;
|
|
2688
|
+
const x = layoutNode.position.x - width2 / 2;
|
|
2689
|
+
const y = layoutNode.position.y - height / 2;
|
|
2690
|
+
return new Rectangle(x, y, width2, height);
|
|
2518
2691
|
}
|
|
2519
2692
|
};
|
|
2693
|
+
__decorateClass([
|
|
2694
|
+
inject(Playground)
|
|
2695
|
+
], AutoLayoutService.prototype, "playground", 2);
|
|
2520
2696
|
__decorateClass([
|
|
2521
2697
|
inject(WorkflowDocument)
|
|
2522
2698
|
], AutoLayoutService.prototype, "document", 2);
|
|
@@ -2528,7 +2704,11 @@ AutoLayoutService = __decorateClass([
|
|
|
2528
2704
|
var createFreeAutoLayoutPlugin = definePluginCreator({
|
|
2529
2705
|
onBind: ({ bind }) => {
|
|
2530
2706
|
bind(AutoLayoutService).toSelf().inSingletonScope();
|
|
2531
|
-
}
|
|
2707
|
+
},
|
|
2708
|
+
onInit: (ctx, opts) => {
|
|
2709
|
+
ctx.get(AutoLayoutService).init(opts);
|
|
2710
|
+
},
|
|
2711
|
+
singleton: true
|
|
2532
2712
|
});
|
|
2533
2713
|
|
|
2534
2714
|
// src/index.ts
|
|
@@ -2536,6 +2716,7 @@ import { Graph as Graph8 } from "@dagrejs/graphlib";
|
|
|
2536
2716
|
export {
|
|
2537
2717
|
AutoLayoutService,
|
|
2538
2718
|
Graph8 as DagreGraph,
|
|
2719
|
+
DefaultLayoutConfig,
|
|
2539
2720
|
Layout,
|
|
2540
2721
|
createFreeAutoLayoutPlugin,
|
|
2541
2722
|
dagreLib
|