Skip to content

Commit 9d029bf

Browse files
authored
feat(auto-layout): support custom layout config (bytedance#378)
1 parent 50fa5a8 commit 9d029bf

File tree

10 files changed

+64
-10
lines changed

10 files changed

+64
-10
lines changed

cspell.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@
77
"closebracket",
88
"codesandbox",
99
"douyinfe",
10+
"edgesep",
1011
"flowgram",
1112
"flowgram.ai",
1213
"gedit",
1314
"Hoverable",
1415
"langchain",
16+
"marginx",
17+
"marginy",
1518
"openbracket",
1619
"rsbuild",
1720
"rspack",
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
import { definePluginCreator } from '@flowgram.ai/core';
22

3+
import { AutoLayoutOptions } from './type';
34
import { AutoLayoutService } from './services';
45

5-
export const createFreeAutoLayoutPlugin = definePluginCreator({
6+
export const createFreeAutoLayoutPlugin = definePluginCreator<AutoLayoutOptions>({
67
onBind: ({ bind }) => {
78
bind(AutoLayoutService).toSelf().inSingletonScope();
89
},
10+
onInit: (ctx, opts) => {
11+
ctx.get(AutoLayoutService).init(opts);
12+
},
13+
singleton: true,
914
});
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
1-
export const DagreLayoutOptions = {
1+
import { LayoutConfig } from './type';
2+
3+
export const DefaultLayoutConfig: LayoutConfig = {
24
rankdir: 'LR',
5+
align: undefined,
36
nodesep: 100,
7+
edgesep: 10,
48
ranksep: 100,
9+
marginx: 0,
10+
marginy: 0,
11+
acyclicer: undefined,
512
ranker: 'network-simplex',
613
};

packages/plugins/free-auto-layout-plugin/src/layout/dagre.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { Graph as DagreGraph } from '@dagrejs/graphlib';
44
import { dagreLib } from '../dagre-lib/index';
55
import { DagreNode, LayoutNode } from './type';
66
import { LayoutStore } from './store';
7-
import { DagreLayoutOptions } from './constant';
87

98
export class DagreLayout {
109
private readonly graph: DagreGraph;
@@ -59,7 +58,7 @@ export class DagreLayout {
5958
private createGraph(): DagreGraph {
6059
const graph = new DagreGraph({ multigraph: true });
6160
graph.setDefaultEdgeLabel(() => ({}));
62-
graph.setGraph(DagreLayoutOptions);
61+
graph.setGraph(this.store.config);
6362
return graph;
6463
}
6564

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export { Layout } from './layout';
22
export type { LayoutNode, LayoutEdge, GetFollowNode, LayoutOptions } from './type';
33
export type { LayoutStore } from './store';
4+
export { DefaultLayoutConfig } from './constant';

packages/plugins/free-auto-layout-plugin/src/layout/layout.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { GetFollowNode, LayoutOptions, LayoutParams } from './type';
1+
import { GetFollowNode, LayoutConfig, LayoutOptions, LayoutParams } from './type';
22
import { LayoutStore } from './store';
33
import { LayoutPosition } from './position';
44
import { DagreLayout } from './dagre';
@@ -10,8 +10,8 @@ export class Layout {
1010

1111
private readonly _position: LayoutPosition;
1212

13-
constructor() {
14-
this._store = new LayoutStore();
13+
constructor(config: LayoutConfig) {
14+
this._store = new LayoutStore(config);
1515
this._layout = new DagreLayout(this._store);
1616
this._position = new LayoutPosition(this._store);
1717
}

packages/plugins/free-auto-layout-plugin/src/layout/store.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
} from '@flowgram.ai/free-layout-core';
66
import { FlowNodeBaseType, FlowNodeTransformData } from '@flowgram.ai/document';
77

8-
import { LayoutEdge, LayoutNode, LayoutParams } from './type';
8+
import type { LayoutConfig, LayoutEdge, LayoutNode, LayoutParams } from './type';
99

1010
interface LayoutStoreData {
1111
nodes: Map<string, LayoutNode>;
@@ -21,6 +21,8 @@ export class LayoutStore {
2121

2222
private container: WorkflowNodeEntity;
2323

24+
constructor(public readonly config: LayoutConfig) {}
25+
2426
public get initialized(): boolean {
2527
return this.init;
2628
}

packages/plugins/free-auto-layout-plugin/src/layout/type.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,27 @@ export interface LayoutOptions {
6868
getFollowNode?: GetFollowNode;
6969
}
7070

71+
export interface LayoutConfig {
72+
/** Direction for rank nodes. Can be TB, BT, LR, or RL, where T = top, B = bottom, L = left, and R = right. */
73+
rankdir: 'TB' | 'BT' | 'LR' | 'RL';
74+
/** Alignment for rank nodes. Can be UL, UR, DL, or DR, where U = up, D = down, L = left, and R = right. */
75+
align: 'UL' | 'UR' | 'DL' | 'DR' | undefined;
76+
/** Number of pixels that separate nodes horizontally in the layout. */
77+
nodesep: number;
78+
/** Number of pixels that separate edges horizontally in the layout. */
79+
edgesep: number;
80+
/** Number of pixels that separate edges horizontally in the layout. */
81+
ranksep: number;
82+
/** Number of pixels to use as a margin around the left and right of the graph. */
83+
marginx: number;
84+
/** Number of pixels to use as a margin around the top and bottom of the graph. */
85+
marginy: number;
86+
/** If set to greedy, uses a greedy heuristic for finding a feedback arc set for a graph. A feedback arc set is a set of edges that can be removed to make a graph acyclic. */
87+
acyclicer: 'greedy' | undefined;
88+
/** Type of algorithm to assigns a rank to each node in the input graph. Possible values: network-simplex, tight-tree or longest-path */
89+
ranker: 'network-simplex' | 'tight-tree' | 'longest-path';
90+
}
91+
7192
export type GetFollowNode = (
7293
node: LayoutNode,
7394
context: {

packages/plugins/free-auto-layout-plugin/src/services.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,23 @@ import {
66
WorkflowNodeLinesData,
77
} from '@flowgram.ai/free-layout-core';
88

9-
import { Layout, type LayoutOptions } from './layout';
9+
import { AutoLayoutOptions } from './type';
10+
import { LayoutConfig } from './layout/type';
11+
import { DefaultLayoutConfig, Layout, type LayoutOptions } from './layout';
1012

1113
@injectable()
1214
export class AutoLayoutService {
1315
@inject(WorkflowDocument) private readonly document: WorkflowDocument;
1416

17+
private layoutConfig: LayoutConfig = DefaultLayoutConfig;
18+
19+
public init(options: AutoLayoutOptions) {
20+
this.layoutConfig = {
21+
...this.layoutConfig,
22+
...options.layoutConfig,
23+
};
24+
}
25+
1526
public async layout(options: LayoutOptions = {}): Promise<void> {
1627
await this.layoutNode(this.document.root, options);
1728
}
@@ -29,7 +40,7 @@ export class AutoLayoutService {
2940
// 先递归执行子节点 autoLayout
3041
await Promise.all(nodes.map(async (child) => this.layoutNode(child, options)));
3142

32-
const layout = new Layout();
43+
const layout = new Layout(this.layoutConfig);
3344
layout.init({ nodes, edges, container: node }, options);
3445
layout.layout();
3546
await layout.position();
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { LayoutConfig } from './layout/type';
2+
3+
export interface AutoLayoutOptions {
4+
layoutConfig?: Partial<LayoutConfig>;
5+
}

0 commit comments

Comments
 (0)