mirror of
https://gitee.com/ByteDance/flowgram.ai.git
synced 2025-07-07 17:43:29 +08:00
feat(auto-layout): support custom layout config (#378)
This commit is contained in:
parent
50fa5a8eba
commit
9d029bf335
@ -7,11 +7,14 @@
|
||||
"closebracket",
|
||||
"codesandbox",
|
||||
"douyinfe",
|
||||
"edgesep",
|
||||
"flowgram",
|
||||
"flowgram.ai",
|
||||
"gedit",
|
||||
"Hoverable",
|
||||
"langchain",
|
||||
"marginx",
|
||||
"marginy",
|
||||
"openbracket",
|
||||
"rsbuild",
|
||||
"rspack",
|
||||
|
||||
@ -1,9 +1,14 @@
|
||||
import { definePluginCreator } from '@flowgram.ai/core';
|
||||
|
||||
import { AutoLayoutOptions } from './type';
|
||||
import { AutoLayoutService } from './services';
|
||||
|
||||
export const createFreeAutoLayoutPlugin = definePluginCreator({
|
||||
export const createFreeAutoLayoutPlugin = definePluginCreator<AutoLayoutOptions>({
|
||||
onBind: ({ bind }) => {
|
||||
bind(AutoLayoutService).toSelf().inSingletonScope();
|
||||
},
|
||||
onInit: (ctx, opts) => {
|
||||
ctx.get(AutoLayoutService).init(opts);
|
||||
},
|
||||
singleton: true,
|
||||
});
|
||||
|
||||
@ -1,6 +1,13 @@
|
||||
export const DagreLayoutOptions = {
|
||||
import { LayoutConfig } from './type';
|
||||
|
||||
export const DefaultLayoutConfig: LayoutConfig = {
|
||||
rankdir: 'LR',
|
||||
align: undefined,
|
||||
nodesep: 100,
|
||||
edgesep: 10,
|
||||
ranksep: 100,
|
||||
marginx: 0,
|
||||
marginy: 0,
|
||||
acyclicer: undefined,
|
||||
ranker: 'network-simplex',
|
||||
};
|
||||
|
||||
@ -4,7 +4,6 @@ import { Graph as DagreGraph } from '@dagrejs/graphlib';
|
||||
import { dagreLib } from '../dagre-lib/index';
|
||||
import { DagreNode, LayoutNode } from './type';
|
||||
import { LayoutStore } from './store';
|
||||
import { DagreLayoutOptions } from './constant';
|
||||
|
||||
export class DagreLayout {
|
||||
private readonly graph: DagreGraph;
|
||||
@ -59,7 +58,7 @@ export class DagreLayout {
|
||||
private createGraph(): DagreGraph {
|
||||
const graph = new DagreGraph({ multigraph: true });
|
||||
graph.setDefaultEdgeLabel(() => ({}));
|
||||
graph.setGraph(DagreLayoutOptions);
|
||||
graph.setGraph(this.store.config);
|
||||
return graph;
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
export { Layout } from './layout';
|
||||
export type { LayoutNode, LayoutEdge, GetFollowNode, LayoutOptions } from './type';
|
||||
export type { LayoutStore } from './store';
|
||||
export { DefaultLayoutConfig } from './constant';
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { GetFollowNode, LayoutOptions, LayoutParams } from './type';
|
||||
import { GetFollowNode, LayoutConfig, LayoutOptions, LayoutParams } from './type';
|
||||
import { LayoutStore } from './store';
|
||||
import { LayoutPosition } from './position';
|
||||
import { DagreLayout } from './dagre';
|
||||
@ -10,8 +10,8 @@ export class Layout {
|
||||
|
||||
private readonly _position: LayoutPosition;
|
||||
|
||||
constructor() {
|
||||
this._store = new LayoutStore();
|
||||
constructor(config: LayoutConfig) {
|
||||
this._store = new LayoutStore(config);
|
||||
this._layout = new DagreLayout(this._store);
|
||||
this._position = new LayoutPosition(this._store);
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ import {
|
||||
} from '@flowgram.ai/free-layout-core';
|
||||
import { FlowNodeBaseType, FlowNodeTransformData } from '@flowgram.ai/document';
|
||||
|
||||
import { LayoutEdge, LayoutNode, LayoutParams } from './type';
|
||||
import type { LayoutConfig, LayoutEdge, LayoutNode, LayoutParams } from './type';
|
||||
|
||||
interface LayoutStoreData {
|
||||
nodes: Map<string, LayoutNode>;
|
||||
@ -21,6 +21,8 @@ export class LayoutStore {
|
||||
|
||||
private container: WorkflowNodeEntity;
|
||||
|
||||
constructor(public readonly config: LayoutConfig) {}
|
||||
|
||||
public get initialized(): boolean {
|
||||
return this.init;
|
||||
}
|
||||
|
||||
@ -68,6 +68,27 @@ export interface LayoutOptions {
|
||||
getFollowNode?: GetFollowNode;
|
||||
}
|
||||
|
||||
export interface LayoutConfig {
|
||||
/** Direction for rank nodes. Can be TB, BT, LR, or RL, where T = top, B = bottom, L = left, and R = right. */
|
||||
rankdir: 'TB' | 'BT' | 'LR' | 'RL';
|
||||
/** Alignment for rank nodes. Can be UL, UR, DL, or DR, where U = up, D = down, L = left, and R = right. */
|
||||
align: 'UL' | 'UR' | 'DL' | 'DR' | undefined;
|
||||
/** Number of pixels that separate nodes horizontally in the layout. */
|
||||
nodesep: number;
|
||||
/** Number of pixels that separate edges horizontally in the layout. */
|
||||
edgesep: number;
|
||||
/** Number of pixels that separate edges horizontally in the layout. */
|
||||
ranksep: number;
|
||||
/** Number of pixels to use as a margin around the left and right of the graph. */
|
||||
marginx: number;
|
||||
/** Number of pixels to use as a margin around the top and bottom of the graph. */
|
||||
marginy: number;
|
||||
/** 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. */
|
||||
acyclicer: 'greedy' | undefined;
|
||||
/** Type of algorithm to assigns a rank to each node in the input graph. Possible values: network-simplex, tight-tree or longest-path */
|
||||
ranker: 'network-simplex' | 'tight-tree' | 'longest-path';
|
||||
}
|
||||
|
||||
export type GetFollowNode = (
|
||||
node: LayoutNode,
|
||||
context: {
|
||||
|
||||
@ -6,12 +6,23 @@ import {
|
||||
WorkflowNodeLinesData,
|
||||
} from '@flowgram.ai/free-layout-core';
|
||||
|
||||
import { Layout, type LayoutOptions } from './layout';
|
||||
import { AutoLayoutOptions } from './type';
|
||||
import { LayoutConfig } from './layout/type';
|
||||
import { DefaultLayoutConfig, Layout, type LayoutOptions } from './layout';
|
||||
|
||||
@injectable()
|
||||
export class AutoLayoutService {
|
||||
@inject(WorkflowDocument) private readonly document: WorkflowDocument;
|
||||
|
||||
private layoutConfig: LayoutConfig = DefaultLayoutConfig;
|
||||
|
||||
public init(options: AutoLayoutOptions) {
|
||||
this.layoutConfig = {
|
||||
...this.layoutConfig,
|
||||
...options.layoutConfig,
|
||||
};
|
||||
}
|
||||
|
||||
public async layout(options: LayoutOptions = {}): Promise<void> {
|
||||
await this.layoutNode(this.document.root, options);
|
||||
}
|
||||
@ -29,7 +40,7 @@ export class AutoLayoutService {
|
||||
// 先递归执行子节点 autoLayout
|
||||
await Promise.all(nodes.map(async (child) => this.layoutNode(child, options)));
|
||||
|
||||
const layout = new Layout();
|
||||
const layout = new Layout(this.layoutConfig);
|
||||
layout.init({ nodes, edges, container: node }, options);
|
||||
layout.layout();
|
||||
await layout.position();
|
||||
|
||||
5
packages/plugins/free-auto-layout-plugin/src/type.ts
Normal file
5
packages/plugins/free-auto-layout-plugin/src/type.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { LayoutConfig } from './layout/type';
|
||||
|
||||
export interface AutoLayoutOptions {
|
||||
layoutConfig?: Partial<LayoutConfig>;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user