@flowgram.ai/free-auto-layout-plugin 0.1.0-alpha.15 → 0.1.0-alpha.16

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/index.js CHANGED
@@ -26,8 +26,8 @@ var __decorateClass = (decorators, target, key, kind) => {
26
26
  };
27
27
 
28
28
  // src/index.ts
29
- var src_exports = {};
30
- __export(src_exports, {
29
+ var index_exports = {};
30
+ __export(index_exports, {
31
31
  AutoLayoutService: () => AutoLayoutService,
32
32
  DagreGraph: () => import_graphlib10.Graph,
33
33
  DefaultLayoutConfig: () => DefaultLayoutConfig,
@@ -35,7 +35,7 @@ __export(src_exports, {
35
35
  createFreeAutoLayoutPlugin: () => createFreeAutoLayoutPlugin,
36
36
  dagreLib: () => dagreLib
37
37
  });
38
- module.exports = __toCommonJS(src_exports);
38
+ module.exports = __toCommonJS(index_exports);
39
39
 
40
40
  // src/dagre-lib/greedy-fas.js
41
41
  var import_graphlib = require("@dagrejs/graphlib");
@@ -2105,11 +2105,13 @@ var dagreLib = {
2105
2105
  };
2106
2106
 
2107
2107
  // src/create-auto-layout-plugin.tsx
2108
- var import_core2 = require("@flowgram.ai/core");
2108
+ var import_core3 = require("@flowgram.ai/core");
2109
2109
 
2110
2110
  // src/services.ts
2111
2111
  var import_inversify = require("inversify");
2112
- var import_free_layout_core2 = require("@flowgram.ai/free-layout-core");
2112
+ var import_utils = require("@flowgram.ai/utils");
2113
+ var import_free_layout_core = require("@flowgram.ai/free-layout-core");
2114
+ var import_core2 = require("@flowgram.ai/core");
2113
2115
 
2114
2116
  // src/layout/constant.ts
2115
2117
  var DefaultLayoutConfig = {
@@ -2125,11 +2127,12 @@ var DefaultLayoutConfig = {
2125
2127
  };
2126
2128
  var DefaultLayoutOptions = {
2127
2129
  getFollowNode: void 0,
2128
- enableAnimation: false
2130
+ disableFitView: false,
2131
+ enableAnimation: false,
2132
+ animationDuration: 300
2129
2133
  };
2130
2134
 
2131
2135
  // src/layout/store.ts
2132
- var import_free_layout_core = require("@flowgram.ai/free-layout-core");
2133
2136
  var import_document = require("@flowgram.ai/document");
2134
2137
  var LayoutStore = class {
2135
2138
  constructor(config) {
@@ -2159,6 +2162,7 @@ var LayoutStore = class {
2159
2162
  return Array.from(this.store.edges.values());
2160
2163
  }
2161
2164
  create(params, options) {
2165
+ this.container = params.container;
2162
2166
  this.store = this.createStore(params);
2163
2167
  this.indexMap = this.createIndexMap();
2164
2168
  this.setOptions(options);
@@ -2166,10 +2170,7 @@ var LayoutStore = class {
2166
2170
  }
2167
2171
  /** 创建布局数据 */
2168
2172
  createStore(params) {
2169
- const { nodes, edges, container } = params;
2170
- this.container = container;
2171
- const layoutNodes = this.createLayoutNodes(nodes);
2172
- const layoutEdges = this.createEdgesStore(edges);
2173
+ const { layoutNodes, layoutEdges } = params;
2173
2174
  const virtualEdges = this.createVirtualEdges(params);
2174
2175
  const store = {
2175
2176
  nodes: /* @__PURE__ */ new Map(),
@@ -2179,53 +2180,11 @@ var LayoutStore = class {
2179
2180
  layoutEdges.concat(virtualEdges).forEach((edge) => store.edges.set(edge.id, edge));
2180
2181
  return store;
2181
2182
  }
2182
- /** 创建节点布局数据 */
2183
- createLayoutNodes(nodes) {
2184
- const layoutNodes = nodes.map((node, index) => {
2185
- const { bounds } = node.getData(import_document.FlowNodeTransformData);
2186
- const layoutNode = {
2187
- id: node.id,
2188
- entity: node,
2189
- index: "",
2190
- // 初始化时,index 未计算
2191
- rank: -1,
2192
- // 初始化时,节点还未布局,层级为-1
2193
- order: -1,
2194
- // 初始化时,节点还未布局,顺序为-1
2195
- position: { x: bounds.center.x, y: bounds.center.y },
2196
- offset: { x: 0, y: 0 },
2197
- size: { width: bounds.width, height: bounds.height },
2198
- hasChildren: node.collapsedChildren?.length > 0
2199
- };
2200
- return layoutNode;
2201
- });
2202
- return layoutNodes;
2203
- }
2204
- /** 创建线条布局数据 */
2205
- createEdgesStore(edges) {
2206
- const layoutEdges = edges.map((edge) => {
2207
- const { from, to } = edge.info;
2208
- if (!from || !to || edge.vertical) {
2209
- return;
2210
- }
2211
- const layoutEdge = {
2212
- id: edge.id,
2213
- entity: edge,
2214
- from,
2215
- to,
2216
- fromIndex: "",
2217
- // 初始化时,index 未计算
2218
- toIndex: "",
2219
- // 初始化时,index 未计算
2220
- name: edge.id
2221
- };
2222
- return layoutEdge;
2223
- }).filter(Boolean);
2224
- return layoutEdges;
2225
- }
2226
2183
  /** 创建虚拟线条数据 */
2227
2184
  createVirtualEdges(params) {
2228
- const { nodes, edges } = params;
2185
+ const { layoutNodes, layoutEdges } = params;
2186
+ const nodes = layoutNodes.map((layoutNode) => layoutNode.entity);
2187
+ const edges = layoutEdges.map((layoutEdge) => layoutEdge.entity);
2229
2188
  const groupNodes = nodes.filter((n) => n.flowNodeType === import_document.FlowNodeBaseType.GROUP);
2230
2189
  const virtualEdges = groupNodes.map((group) => {
2231
2190
  const { id: groupId, blocks = [] } = group;
@@ -2312,7 +2271,7 @@ var LayoutStore = class {
2312
2271
  });
2313
2272
  const visited = /* @__PURE__ */ new Set();
2314
2273
  const visit = (node) => {
2315
- if (visited.has(node.id)) {
2274
+ if (!node || visited.has(node.id)) {
2316
2275
  return;
2317
2276
  }
2318
2277
  visited.add(node.id);
@@ -2320,7 +2279,7 @@ var LayoutStore = class {
2320
2279
  node.blocks.forEach((child) => {
2321
2280
  visit(child);
2322
2281
  });
2323
- const { outputLines } = node.getData(import_free_layout_core.WorkflowNodeLinesData);
2282
+ const { outputLines } = node.lines;
2324
2283
  const sortedLines = outputLines.sort((a, b) => {
2325
2284
  const aNode = this.getNode(a.to?.id);
2326
2285
  const bNode = this.getNode(b.to?.id);
@@ -2342,7 +2301,7 @@ var LayoutStore = class {
2342
2301
  visit(to);
2343
2302
  });
2344
2303
  };
2345
- visit(this.container);
2304
+ visit(this.container.entity);
2346
2305
  const uniqueNodeIds = nodeIdList.reduceRight((acc, nodeId) => {
2347
2306
  if (!acc.includes(nodeId)) {
2348
2307
  acc.unshift(nodeId);
@@ -2396,7 +2355,7 @@ var LayoutPosition = class {
2396
2355
  (0, import_core.startTween)({
2397
2356
  from: { d: 0 },
2398
2357
  to: { d: 100 },
2399
- duration: 300,
2358
+ duration: this.store.options.animationDuration ?? 0,
2400
2359
  onUpdate: (v) => {
2401
2360
  this.store.nodes.forEach((layoutNode) => {
2402
2361
  this.updateNodePosition({ layoutNode, step: v.d });
@@ -2410,18 +2369,20 @@ var LayoutPosition = class {
2410
2369
  }
2411
2370
  updateNodePosition(params) {
2412
2371
  const { layoutNode, step } = params;
2413
- const transform = layoutNode.entity.getData(import_core.TransformData);
2414
- const position2 = {
2372
+ const { transform } = layoutNode.entity.transform;
2373
+ const layoutPosition = {
2415
2374
  x: layoutNode.position.x + layoutNode.offset.x,
2416
- y: layoutNode.position.y + layoutNode.offset.y
2375
+ // layoutNode.position.y 是中心点,但画布节点原点在上边沿的中间,所以 y 坐标需要转化后一下
2376
+ y: layoutNode.position.y - layoutNode.size.height / 2 + layoutNode.offset.y
2377
+ };
2378
+ const deltaX = (layoutPosition.x - transform.position.x) * step / 100;
2379
+ const deltaY = (layoutPosition.y - transform.position.y) * step / 100;
2380
+ const position2 = {
2381
+ x: transform.position.x + deltaX,
2382
+ y: transform.position.y + deltaY
2417
2383
  };
2418
- const deltaX = (position2.x - transform.position.x) * step / 100;
2419
- const deltaY = (position2.y - transform.bounds.height / 2 - transform.position.y) * step / 100;
2420
2384
  transform.update({
2421
- position: {
2422
- x: transform.position.x + deltaX,
2423
- y: transform.position.y + deltaY
2424
- }
2385
+ position: position2
2425
2386
  });
2426
2387
  const document = layoutNode.entity.document;
2427
2388
  document.layout.updateAffectedTransform(layoutNode.entity);
@@ -2429,7 +2390,6 @@ var LayoutPosition = class {
2429
2390
  };
2430
2391
 
2431
2392
  // src/layout/dagre.ts
2432
- var import_document2 = require("@flowgram.ai/document");
2433
2393
  var import_graphlib9 = require("@dagrejs/graphlib");
2434
2394
  var DagreLayout = class {
2435
2395
  constructor(store) {
@@ -2483,13 +2443,7 @@ var DagreLayout = class {
2483
2443
  return graph;
2484
2444
  }
2485
2445
  graphSetData() {
2486
- const nodes = Array.from(this.store.nodes.values());
2487
- const edges = Array.from(this.store.edges.values()).sort((next, prev) => {
2488
- if (next.fromIndex === prev.fromIndex) {
2489
- return next.toIndex < prev.toIndex ? -1 : 1;
2490
- }
2491
- return next.fromIndex < prev.fromIndex ? -1 : 1;
2492
- });
2446
+ const { nodes, edges } = this.store;
2493
2447
  nodes.forEach((layoutNode) => {
2494
2448
  this.graph.setNode(layoutNode.index, {
2495
2449
  originID: layoutNode.id,
@@ -2497,7 +2451,12 @@ var DagreLayout = class {
2497
2451
  height: layoutNode.size.height
2498
2452
  });
2499
2453
  });
2500
- edges.forEach((layoutEdge) => {
2454
+ edges.sort((next, prev) => {
2455
+ if (next.fromIndex === prev.fromIndex) {
2456
+ return next.toIndex < prev.toIndex ? -1 : 1;
2457
+ }
2458
+ return next.fromIndex < prev.fromIndex ? -1 : 1;
2459
+ }).forEach((layoutEdge) => {
2501
2460
  this.graph.setEdge({
2502
2461
  v: layoutEdge.fromIndex,
2503
2462
  w: layoutEdge.toIndex,
@@ -2528,12 +2487,11 @@ var DagreLayout = class {
2528
2487
  return Number.isNaN(number) ? 0 : number;
2529
2488
  }
2530
2489
  getOffsetX(layoutNode) {
2531
- if (!layoutNode.hasChildren) {
2490
+ if (layoutNode.layoutNodes.length === 0) {
2532
2491
  return 0;
2533
2492
  }
2534
- const nodeTransform = layoutNode.entity.getData(import_document2.FlowNodeTransformData);
2535
- const { bounds, padding } = nodeTransform;
2536
- const leftOffset = -bounds.width / 2 + padding.left;
2493
+ const { padding } = layoutNode;
2494
+ const leftOffset = -layoutNode.size.width / 2 + padding.left;
2537
2495
  return leftOffset;
2538
2496
  }
2539
2497
  setOrderAndRank(g) {
@@ -2635,42 +2593,131 @@ var AutoLayoutService = class {
2635
2593
  };
2636
2594
  }
2637
2595
  async layout(options = {}) {
2638
- await this.layoutNode(this.document.root, {
2596
+ const layoutOptions = {
2639
2597
  ...DefaultLayoutOptions,
2640
2598
  ...options
2641
- });
2642
- }
2643
- async layoutNode(node, options) {
2644
- const nodes = node.blocks;
2645
- if (!nodes || !Array.isArray(nodes) || !nodes.length) {
2599
+ };
2600
+ const containerNode = layoutOptions.containerNode ?? this.document.root;
2601
+ const container = this.createLayoutNode(containerNode, options);
2602
+ const layouts = await this.layoutNode(container, layoutOptions);
2603
+ const rect = this.getLayoutNodeRect(container);
2604
+ const positionPromise = layouts.map((layout2) => layout2.position());
2605
+ const fitViewPromise = this.fitView(layoutOptions, rect);
2606
+ await Promise.all([...positionPromise, fitViewPromise]);
2607
+ }
2608
+ async fitView(options, rect) {
2609
+ if (options.disableFitView === true) {
2646
2610
  return;
2647
2611
  }
2648
- const edges = this.getNodesAllLines(nodes);
2649
- await Promise.all(nodes.map(async (child) => this.layoutNode(child, options)));
2612
+ return this.playground.config.fitView(rect, options.enableAnimation, 30);
2613
+ }
2614
+ async layoutNode(container, options) {
2615
+ const { layoutNodes, layoutEdges } = container;
2616
+ if (layoutNodes.length === 0) {
2617
+ return [];
2618
+ }
2619
+ const childrenLayouts = (await Promise.all(layoutNodes.map((n) => this.layoutNode(n, options)))).flat();
2650
2620
  const layout2 = new Layout(this.layoutConfig);
2651
- layout2.init({ nodes, edges, container: node }, options);
2621
+ layout2.init({ container, layoutNodes, layoutEdges }, options);
2652
2622
  layout2.layout();
2653
- await layout2.position();
2623
+ const rect = this.getLayoutNodeRect(container);
2624
+ container.size = {
2625
+ width: rect.width,
2626
+ height: rect.height
2627
+ };
2628
+ return [...childrenLayouts, layout2];
2629
+ }
2630
+ createLayoutNodes(nodes, options) {
2631
+ return nodes.map((node) => this.createLayoutNode(node, options));
2632
+ }
2633
+ /** 创建节点布局数据 */
2634
+ createLayoutNode(node, options) {
2635
+ const { blocks } = node;
2636
+ const edges = this.getNodesAllLines(blocks);
2637
+ const layoutNodes = this.createLayoutNodes(blocks, options);
2638
+ const layoutEdges = this.createLayoutEdges(edges);
2639
+ const { bounds, padding } = node.transform;
2640
+ const { width: width2, height, center } = bounds;
2641
+ const { x, y } = center;
2642
+ const layoutNode = {
2643
+ id: node.id,
2644
+ entity: node,
2645
+ index: "",
2646
+ // 初始化时,index 未计算
2647
+ rank: -1,
2648
+ // 初始化时,节点还未布局,层级为-1
2649
+ order: -1,
2650
+ // 初始化时,节点还未布局,顺序为-1
2651
+ position: { x, y },
2652
+ offset: { x: 0, y: 0 },
2653
+ padding,
2654
+ size: { width: width2, height },
2655
+ layoutNodes,
2656
+ layoutEdges
2657
+ };
2658
+ return layoutNode;
2659
+ }
2660
+ createLayoutEdges(edges) {
2661
+ const layoutEdges = edges.map((edge) => this.createLayoutEdge(edge)).filter(Boolean);
2662
+ return layoutEdges;
2663
+ }
2664
+ /** 创建线条布局数据 */
2665
+ createLayoutEdge(edge) {
2666
+ const { from, to } = edge.info;
2667
+ if (!from || !to || edge.vertical) {
2668
+ return;
2669
+ }
2670
+ const layoutEdge = {
2671
+ id: edge.id,
2672
+ entity: edge,
2673
+ from,
2674
+ to,
2675
+ fromIndex: "",
2676
+ // 初始化时,index 未计算
2677
+ toIndex: "",
2678
+ // 初始化时,index 未计算
2679
+ name: edge.id
2680
+ };
2681
+ return layoutEdge;
2654
2682
  }
2655
2683
  getNodesAllLines(nodes) {
2656
2684
  const lines = nodes.map((node) => {
2657
- const linesData = node.getData(import_free_layout_core2.WorkflowNodeLinesData);
2685
+ const linesData = node.getData(import_free_layout_core.WorkflowNodeLinesData);
2658
2686
  const outputLines = linesData.outputLines.filter(Boolean);
2659
2687
  const inputLines = linesData.inputLines.filter(Boolean);
2660
2688
  return [...outputLines, ...inputLines];
2661
2689
  }).flat();
2662
2690
  return lines;
2663
2691
  }
2692
+ getLayoutNodeRect(layoutNode) {
2693
+ const rects = layoutNode.layoutNodes.map((node) => this.layoutNodeRect(node));
2694
+ const rect = import_utils.Rectangle.enlarge(rects);
2695
+ const { padding } = layoutNode;
2696
+ const width2 = rect.width + padding.left + padding.right;
2697
+ const height = rect.height + padding.top + padding.bottom;
2698
+ const x = rect.x - padding.left;
2699
+ const y = rect.y - padding.top;
2700
+ return new import_utils.Rectangle(x, y, width2, height);
2701
+ }
2702
+ layoutNodeRect(layoutNode) {
2703
+ const { width: width2, height } = layoutNode.size;
2704
+ const x = layoutNode.position.x - width2 / 2;
2705
+ const y = layoutNode.position.y - height / 2;
2706
+ return new import_utils.Rectangle(x, y, width2, height);
2707
+ }
2664
2708
  };
2665
2709
  __decorateClass([
2666
- (0, import_inversify.inject)(import_free_layout_core2.WorkflowDocument)
2710
+ (0, import_inversify.inject)(import_core2.Playground)
2711
+ ], AutoLayoutService.prototype, "playground", 2);
2712
+ __decorateClass([
2713
+ (0, import_inversify.inject)(import_free_layout_core.WorkflowDocument)
2667
2714
  ], AutoLayoutService.prototype, "document", 2);
2668
2715
  AutoLayoutService = __decorateClass([
2669
2716
  (0, import_inversify.injectable)()
2670
2717
  ], AutoLayoutService);
2671
2718
 
2672
2719
  // src/create-auto-layout-plugin.tsx
2673
- var createFreeAutoLayoutPlugin = (0, import_core2.definePluginCreator)({
2720
+ var createFreeAutoLayoutPlugin = (0, import_core3.definePluginCreator)({
2674
2721
  onBind: ({ bind }) => {
2675
2722
  bind(AutoLayoutService).toSelf().inSingletonScope();
2676
2723
  },