chore(auto-layout): auto layout animation disabled by default (#449)

This commit is contained in:
Louis Young 2025-07-02 23:01:50 +08:00 committed by GitHub
parent de7f2d3c07
commit 25e20d8c20
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 64 additions and 25 deletions

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: MIT
*/
import { LayoutConfig } from './type';
import { LayoutConfig, LayoutOptions } from './type';
export const DefaultLayoutConfig: LayoutConfig = {
rankdir: 'LR',
@ -16,3 +16,8 @@ export const DefaultLayoutConfig: LayoutConfig = {
acyclicer: undefined,
ranker: 'network-simplex',
};
export const DefaultLayoutOptions: LayoutOptions = {
getFollowNode: undefined,
enableAnimation: false,
};

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: MIT
*/
import { GetFollowNode, LayoutConfig, LayoutOptions, LayoutParams } from './type';
import { LayoutConfig, LayoutOptions, LayoutParams } from './type';
import { LayoutStore } from './store';
import { LayoutPosition } from './position';
import { DagreLayout } from './dagre';
@ -21,9 +21,8 @@ export class Layout {
this._position = new LayoutPosition(this._store);
}
public init(params: LayoutParams, options: LayoutOptions = {}): void {
this._store.create(params);
this.setFollowNode(options.getFollowNode);
public init(params: LayoutParams, options: LayoutOptions): void {
this._store.create(params, options);
}
public layout(): void {
@ -39,20 +38,4 @@ export class Layout {
}
return await this._position.position();
}
public setFollowNode(getFollowNode?: GetFollowNode): void {
if (!getFollowNode) return;
const context = { store: this._store };
this._store.nodes.forEach((node) => {
const followTo = getFollowNode(node, context)?.followTo;
if (!followTo) return;
const followToNode = this._store.getNode(followTo);
if (!followToNode) return;
if (!followToNode.followedBy) {
followToNode.followedBy = [];
}
followToNode.followedBy.push(node.id);
node.followTo = followTo;
});
}
}

View File

@ -13,6 +13,19 @@ export class LayoutPosition {
constructor(private readonly store: LayoutStore) {}
public async position(): Promise<void> {
if (this.store.options.enableAnimation) {
return this.positionWithAnimation();
}
return this.positionDirectly();
}
private positionDirectly(): void {
this.store.nodes.forEach((layoutNode) => {
this.updateNodePosition({ layoutNode, step: 100 });
});
}
private async positionWithAnimation(): Promise<void> {
return new Promise((resolve) => {
startTween({
from: { d: 0 },

View File

@ -10,7 +10,14 @@ import {
} from '@flowgram.ai/free-layout-core';
import { FlowNodeBaseType, FlowNodeTransformData } from '@flowgram.ai/document';
import type { LayoutConfig, LayoutEdge, LayoutNode, LayoutParams } from './type';
import type {
GetFollowNode,
LayoutConfig,
LayoutEdge,
LayoutNode,
LayoutOptions,
LayoutParams,
} from './type';
interface LayoutStoreData {
nodes: Map<string, LayoutNode>;
@ -26,6 +33,8 @@ export class LayoutStore {
private container: WorkflowNodeEntity;
public options: LayoutOptions;
constructor(public readonly config: LayoutConfig) {}
public get initialized(): boolean {
@ -56,9 +65,10 @@ export class LayoutStore {
return Array.from(this.store.edges.values());
}
public create(params: LayoutParams): void {
public create(params: LayoutParams, options: LayoutOptions): void {
this.store = this.createStore(params);
this.indexMap = this.createIndexMap();
this.setOptions(options);
this.init = true;
}
@ -279,4 +289,27 @@ export class LayoutStore {
return uniqueNodeIds;
}
/** 记录运行选项 */
private setOptions(options: LayoutOptions): void {
this.options = options;
this.setFollowNode(options.getFollowNode);
}
/** 设置跟随节点配置 */
private setFollowNode(getFollowNode?: GetFollowNode): void {
if (!getFollowNode) return;
const context = { store: this };
this.nodes.forEach((node) => {
const followTo = getFollowNode(node, context)?.followTo;
if (!followTo) return;
const followToNode = this.getNode(followTo);
if (!followToNode) return;
if (!followToNode.followedBy) {
followToNode.followedBy = [];
}
followToNode.followedBy.push(node.id);
node.followTo = followTo;
});
}
}

View File

@ -71,6 +71,7 @@ export interface LayoutParams {
export interface LayoutOptions {
getFollowNode?: GetFollowNode;
enableAnimation: boolean;
}
export interface LayoutConfig {

View File

@ -13,6 +13,7 @@ import {
import { AutoLayoutOptions } from './type';
import { LayoutConfig } from './layout/type';
import { DefaultLayoutOptions } from './layout/constant';
import { DefaultLayoutConfig, Layout, type LayoutOptions } from './layout';
@injectable()
@ -28,8 +29,11 @@ export class AutoLayoutService {
};
}
public async layout(options: LayoutOptions = {}): Promise<void> {
await this.layoutNode(this.document.root, options);
public async layout(options: Partial<LayoutOptions> = {}): Promise<void> {
await this.layoutNode(this.document.root, {
...DefaultLayoutOptions,
...options,
});
}
private async layoutNode(node: WorkflowNodeEntity, options: LayoutOptions): Promise<void> {