@flowgram.ai/free-auto-layout-plugin 0.1.0-alpha.14 → 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/esm/index.js CHANGED
@@ -2081,10 +2081,12 @@ 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
- WorkflowNodeLinesData as WorkflowNodeLinesData2
2087
+ WorkflowNodeLinesData
2087
2088
  } from "@flowgram.ai/free-layout-core";
2089
+ import { Playground } from "@flowgram.ai/core";
2088
2090
 
2089
2091
  // src/layout/constant.ts
2090
2092
  var DefaultLayoutConfig = {
@@ -2100,14 +2102,13 @@ var DefaultLayoutConfig = {
2100
2102
  };
2101
2103
  var DefaultLayoutOptions = {
2102
2104
  getFollowNode: void 0,
2103
- enableAnimation: false
2105
+ disableFitView: false,
2106
+ enableAnimation: false,
2107
+ animationDuration: 300
2104
2108
  };
2105
2109
 
2106
2110
  // src/layout/store.ts
2107
- import {
2108
- WorkflowNodeLinesData
2109
- } from "@flowgram.ai/free-layout-core";
2110
- import { FlowNodeBaseType, FlowNodeTransformData } from "@flowgram.ai/document";
2111
+ import { FlowNodeBaseType } from "@flowgram.ai/document";
2111
2112
  var LayoutStore = class {
2112
2113
  constructor(config) {
2113
2114
  this.config = config;
@@ -2136,6 +2137,7 @@ var LayoutStore = class {
2136
2137
  return Array.from(this.store.edges.values());
2137
2138
  }
2138
2139
  create(params, options) {
2140
+ this.container = params.container;
2139
2141
  this.store = this.createStore(params);
2140
2142
  this.indexMap = this.createIndexMap();
2141
2143
  this.setOptions(options);
@@ -2143,10 +2145,7 @@ var LayoutStore = class {
2143
2145
  }
2144
2146
  /** 创建布局数据 */
2145
2147
  createStore(params) {
2146
- const { nodes, edges, container } = params;
2147
- this.container = container;
2148
- const layoutNodes = this.createLayoutNodes(nodes);
2149
- const layoutEdges = this.createEdgesStore(edges);
2148
+ const { layoutNodes, layoutEdges } = params;
2150
2149
  const virtualEdges = this.createVirtualEdges(params);
2151
2150
  const store = {
2152
2151
  nodes: /* @__PURE__ */ new Map(),
@@ -2156,53 +2155,11 @@ var LayoutStore = class {
2156
2155
  layoutEdges.concat(virtualEdges).forEach((edge) => store.edges.set(edge.id, edge));
2157
2156
  return store;
2158
2157
  }
2159
- /** 创建节点布局数据 */
2160
- createLayoutNodes(nodes) {
2161
- const layoutNodes = nodes.map((node, index) => {
2162
- const { bounds } = node.getData(FlowNodeTransformData);
2163
- const layoutNode = {
2164
- id: node.id,
2165
- entity: node,
2166
- index: "",
2167
- // 初始化时,index 未计算
2168
- rank: -1,
2169
- // 初始化时,节点还未布局,层级为-1
2170
- order: -1,
2171
- // 初始化时,节点还未布局,顺序为-1
2172
- position: { x: bounds.center.x, y: bounds.center.y },
2173
- offset: { x: 0, y: 0 },
2174
- size: { width: bounds.width, height: bounds.height },
2175
- hasChildren: node.collapsedChildren?.length > 0
2176
- };
2177
- return layoutNode;
2178
- });
2179
- return layoutNodes;
2180
- }
2181
- /** 创建线条布局数据 */
2182
- createEdgesStore(edges) {
2183
- const layoutEdges = edges.map((edge) => {
2184
- const { from, to } = edge.info;
2185
- if (!from || !to || edge.vertical) {
2186
- return;
2187
- }
2188
- const layoutEdge = {
2189
- id: edge.id,
2190
- entity: edge,
2191
- from,
2192
- to,
2193
- fromIndex: "",
2194
- // 初始化时,index 未计算
2195
- toIndex: "",
2196
- // 初始化时,index 未计算
2197
- name: edge.id
2198
- };
2199
- return layoutEdge;
2200
- }).filter(Boolean);
2201
- return layoutEdges;
2202
- }
2203
2158
  /** 创建虚拟线条数据 */
2204
2159
  createVirtualEdges(params) {
2205
- const { nodes, edges } = params;
2160
+ const { layoutNodes, layoutEdges } = params;
2161
+ const nodes = layoutNodes.map((layoutNode) => layoutNode.entity);
2162
+ const edges = layoutEdges.map((layoutEdge) => layoutEdge.entity);
2206
2163
  const groupNodes = nodes.filter((n) => n.flowNodeType === FlowNodeBaseType.GROUP);
2207
2164
  const virtualEdges = groupNodes.map((group) => {
2208
2165
  const { id: groupId, blocks = [] } = group;
@@ -2289,7 +2246,7 @@ var LayoutStore = class {
2289
2246
  });
2290
2247
  const visited = /* @__PURE__ */ new Set();
2291
2248
  const visit = (node) => {
2292
- if (visited.has(node.id)) {
2249
+ if (!node || visited.has(node.id)) {
2293
2250
  return;
2294
2251
  }
2295
2252
  visited.add(node.id);
@@ -2297,7 +2254,7 @@ var LayoutStore = class {
2297
2254
  node.blocks.forEach((child) => {
2298
2255
  visit(child);
2299
2256
  });
2300
- const { outputLines } = node.getData(WorkflowNodeLinesData);
2257
+ const { outputLines } = node.lines;
2301
2258
  const sortedLines = outputLines.sort((a, b) => {
2302
2259
  const aNode = this.getNode(a.to?.id);
2303
2260
  const bNode = this.getNode(b.to?.id);
@@ -2319,7 +2276,7 @@ var LayoutStore = class {
2319
2276
  visit(to);
2320
2277
  });
2321
2278
  };
2322
- visit(this.container);
2279
+ visit(this.container.entity);
2323
2280
  const uniqueNodeIds = nodeIdList.reduceRight((acc, nodeId) => {
2324
2281
  if (!acc.includes(nodeId)) {
2325
2282
  acc.unshift(nodeId);
@@ -2352,7 +2309,7 @@ var LayoutStore = class {
2352
2309
  };
2353
2310
 
2354
2311
  // src/layout/position.ts
2355
- import { startTween, TransformData } from "@flowgram.ai/core";
2312
+ import { startTween } from "@flowgram.ai/core";
2356
2313
  var LayoutPosition = class {
2357
2314
  constructor(store) {
2358
2315
  this.store = store;
@@ -2373,7 +2330,7 @@ var LayoutPosition = class {
2373
2330
  startTween({
2374
2331
  from: { d: 0 },
2375
2332
  to: { d: 100 },
2376
- duration: 300,
2333
+ duration: this.store.options.animationDuration ?? 0,
2377
2334
  onUpdate: (v) => {
2378
2335
  this.store.nodes.forEach((layoutNode) => {
2379
2336
  this.updateNodePosition({ layoutNode, step: v.d });
@@ -2387,18 +2344,20 @@ var LayoutPosition = class {
2387
2344
  }
2388
2345
  updateNodePosition(params) {
2389
2346
  const { layoutNode, step } = params;
2390
- const transform = layoutNode.entity.getData(TransformData);
2391
- const position2 = {
2347
+ const { transform } = layoutNode.entity.transform;
2348
+ const layoutPosition = {
2392
2349
  x: layoutNode.position.x + layoutNode.offset.x,
2393
- y: layoutNode.position.y + layoutNode.offset.y
2350
+ // layoutNode.position.y 是中心点,但画布节点原点在上边沿的中间,所以 y 坐标需要转化后一下
2351
+ y: layoutNode.position.y - layoutNode.size.height / 2 + layoutNode.offset.y
2352
+ };
2353
+ const deltaX = (layoutPosition.x - transform.position.x) * step / 100;
2354
+ const deltaY = (layoutPosition.y - transform.position.y) * step / 100;
2355
+ const position2 = {
2356
+ x: transform.position.x + deltaX,
2357
+ y: transform.position.y + deltaY
2394
2358
  };
2395
- const deltaX = (position2.x - transform.position.x) * step / 100;
2396
- const deltaY = (position2.y - transform.bounds.height / 2 - transform.position.y) * step / 100;
2397
2359
  transform.update({
2398
- position: {
2399
- x: transform.position.x + deltaX,
2400
- y: transform.position.y + deltaY
2401
- }
2360
+ position: position2
2402
2361
  });
2403
2362
  const document = layoutNode.entity.document;
2404
2363
  document.layout.updateAffectedTransform(layoutNode.entity);
@@ -2406,7 +2365,6 @@ var LayoutPosition = class {
2406
2365
  };
2407
2366
 
2408
2367
  // src/layout/dagre.ts
2409
- import { FlowNodeTransformData as FlowNodeTransformData2 } from "@flowgram.ai/document";
2410
2368
  import { Graph as DagreGraph } from "@dagrejs/graphlib";
2411
2369
  var DagreLayout = class {
2412
2370
  constructor(store) {
@@ -2460,13 +2418,7 @@ var DagreLayout = class {
2460
2418
  return graph;
2461
2419
  }
2462
2420
  graphSetData() {
2463
- const nodes = Array.from(this.store.nodes.values());
2464
- const edges = Array.from(this.store.edges.values()).sort((next, prev) => {
2465
- if (next.fromIndex === prev.fromIndex) {
2466
- return next.toIndex < prev.toIndex ? -1 : 1;
2467
- }
2468
- return next.fromIndex < prev.fromIndex ? -1 : 1;
2469
- });
2421
+ const { nodes, edges } = this.store;
2470
2422
  nodes.forEach((layoutNode) => {
2471
2423
  this.graph.setNode(layoutNode.index, {
2472
2424
  originID: layoutNode.id,
@@ -2474,7 +2426,12 @@ var DagreLayout = class {
2474
2426
  height: layoutNode.size.height
2475
2427
  });
2476
2428
  });
2477
- edges.forEach((layoutEdge) => {
2429
+ edges.sort((next, prev) => {
2430
+ if (next.fromIndex === prev.fromIndex) {
2431
+ return next.toIndex < prev.toIndex ? -1 : 1;
2432
+ }
2433
+ return next.fromIndex < prev.fromIndex ? -1 : 1;
2434
+ }).forEach((layoutEdge) => {
2478
2435
  this.graph.setEdge({
2479
2436
  v: layoutEdge.fromIndex,
2480
2437
  w: layoutEdge.toIndex,
@@ -2505,12 +2462,11 @@ var DagreLayout = class {
2505
2462
  return Number.isNaN(number) ? 0 : number;
2506
2463
  }
2507
2464
  getOffsetX(layoutNode) {
2508
- if (!layoutNode.hasChildren) {
2465
+ if (layoutNode.layoutNodes.length === 0) {
2509
2466
  return 0;
2510
2467
  }
2511
- const nodeTransform = layoutNode.entity.getData(FlowNodeTransformData2);
2512
- const { bounds, padding } = nodeTransform;
2513
- const leftOffset = -bounds.width / 2 + padding.left;
2468
+ const { padding } = layoutNode;
2469
+ const leftOffset = -layoutNode.size.width / 2 + padding.left;
2514
2470
  return leftOffset;
2515
2471
  }
2516
2472
  setOrderAndRank(g) {
@@ -2612,33 +2568,122 @@ var AutoLayoutService = class {
2612
2568
  };
2613
2569
  }
2614
2570
  async layout(options = {}) {
2615
- await this.layoutNode(this.document.root, {
2571
+ const layoutOptions = {
2616
2572
  ...DefaultLayoutOptions,
2617
2573
  ...options
2618
- });
2619
- }
2620
- async layoutNode(node, options) {
2621
- const nodes = node.blocks;
2622
- if (!nodes || !Array.isArray(nodes) || !nodes.length) {
2574
+ };
2575
+ const containerNode = layoutOptions.containerNode ?? this.document.root;
2576
+ const container = this.createLayoutNode(containerNode, options);
2577
+ const layouts = await this.layoutNode(container, layoutOptions);
2578
+ const rect = this.getLayoutNodeRect(container);
2579
+ const positionPromise = layouts.map((layout2) => layout2.position());
2580
+ const fitViewPromise = this.fitView(layoutOptions, rect);
2581
+ await Promise.all([...positionPromise, fitViewPromise]);
2582
+ }
2583
+ async fitView(options, rect) {
2584
+ if (options.disableFitView === true) {
2623
2585
  return;
2624
2586
  }
2625
- const edges = this.getNodesAllLines(nodes);
2626
- await Promise.all(nodes.map(async (child) => this.layoutNode(child, options)));
2587
+ return this.playground.config.fitView(rect, options.enableAnimation, 30);
2588
+ }
2589
+ async layoutNode(container, options) {
2590
+ const { layoutNodes, layoutEdges } = container;
2591
+ if (layoutNodes.length === 0) {
2592
+ return [];
2593
+ }
2594
+ const childrenLayouts = (await Promise.all(layoutNodes.map((n) => this.layoutNode(n, options)))).flat();
2627
2595
  const layout2 = new Layout(this.layoutConfig);
2628
- layout2.init({ nodes, edges, container: node }, options);
2596
+ layout2.init({ container, layoutNodes, layoutEdges }, options);
2629
2597
  layout2.layout();
2630
- await layout2.position();
2598
+ const rect = this.getLayoutNodeRect(container);
2599
+ container.size = {
2600
+ width: rect.width,
2601
+ height: rect.height
2602
+ };
2603
+ return [...childrenLayouts, layout2];
2604
+ }
2605
+ createLayoutNodes(nodes, options) {
2606
+ return nodes.map((node) => this.createLayoutNode(node, options));
2607
+ }
2608
+ /** 创建节点布局数据 */
2609
+ createLayoutNode(node, options) {
2610
+ const { blocks } = node;
2611
+ const edges = this.getNodesAllLines(blocks);
2612
+ const layoutNodes = this.createLayoutNodes(blocks, options);
2613
+ const layoutEdges = this.createLayoutEdges(edges);
2614
+ const { bounds, padding } = node.transform;
2615
+ const { width: width2, height, center } = bounds;
2616
+ const { x, y } = center;
2617
+ const layoutNode = {
2618
+ id: node.id,
2619
+ entity: node,
2620
+ index: "",
2621
+ // 初始化时,index 未计算
2622
+ rank: -1,
2623
+ // 初始化时,节点还未布局,层级为-1
2624
+ order: -1,
2625
+ // 初始化时,节点还未布局,顺序为-1
2626
+ position: { x, y },
2627
+ offset: { x: 0, y: 0 },
2628
+ padding,
2629
+ size: { width: width2, height },
2630
+ layoutNodes,
2631
+ layoutEdges
2632
+ };
2633
+ return layoutNode;
2634
+ }
2635
+ createLayoutEdges(edges) {
2636
+ const layoutEdges = edges.map((edge) => this.createLayoutEdge(edge)).filter(Boolean);
2637
+ return layoutEdges;
2638
+ }
2639
+ /** 创建线条布局数据 */
2640
+ createLayoutEdge(edge) {
2641
+ const { from, to } = edge.info;
2642
+ if (!from || !to || edge.vertical) {
2643
+ return;
2644
+ }
2645
+ const layoutEdge = {
2646
+ id: edge.id,
2647
+ entity: edge,
2648
+ from,
2649
+ to,
2650
+ fromIndex: "",
2651
+ // 初始化时,index 未计算
2652
+ toIndex: "",
2653
+ // 初始化时,index 未计算
2654
+ name: edge.id
2655
+ };
2656
+ return layoutEdge;
2631
2657
  }
2632
2658
  getNodesAllLines(nodes) {
2633
2659
  const lines = nodes.map((node) => {
2634
- const linesData = node.getData(WorkflowNodeLinesData2);
2660
+ const linesData = node.getData(WorkflowNodeLinesData);
2635
2661
  const outputLines = linesData.outputLines.filter(Boolean);
2636
2662
  const inputLines = linesData.inputLines.filter(Boolean);
2637
2663
  return [...outputLines, ...inputLines];
2638
2664
  }).flat();
2639
2665
  return lines;
2640
2666
  }
2667
+ getLayoutNodeRect(layoutNode) {
2668
+ const rects = layoutNode.layoutNodes.map((node) => this.layoutNodeRect(node));
2669
+ const rect = Rectangle.enlarge(rects);
2670
+ const { padding } = layoutNode;
2671
+ const width2 = rect.width + padding.left + padding.right;
2672
+ const height = rect.height + padding.top + padding.bottom;
2673
+ const x = rect.x - padding.left;
2674
+ const y = rect.y - padding.top;
2675
+ return new Rectangle(x, y, width2, height);
2676
+ }
2677
+ layoutNodeRect(layoutNode) {
2678
+ const { width: width2, height } = layoutNode.size;
2679
+ const x = layoutNode.position.x - width2 / 2;
2680
+ const y = layoutNode.position.y - height / 2;
2681
+ return new Rectangle(x, y, width2, height);
2682
+ }
2641
2683
  };
2684
+ __decorateClass([
2685
+ inject(Playground)
2686
+ ], AutoLayoutService.prototype, "playground", 2);
2642
2687
  __decorateClass([
2643
2688
  inject(WorkflowDocument)
2644
2689
  ], AutoLayoutService.prototype, "document", 2);