Skip to content

Commit 1bd49fc

Browse files
authored
docs: auto layout plugin (bytedance#894)
1 parent 88f9383 commit 1bd49fc

File tree

6 files changed

+556
-12
lines changed

6 files changed

+556
-12
lines changed

apps/demo-free-layout/src/initial-data.ts

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export const initialData: FlowDocumentJSON = {
1313
meta: {
1414
position: {
1515
x: 180,
16-
y: 586.2,
16+
y: 601.2,
1717
},
1818
},
1919
data: {
@@ -53,7 +53,7 @@ export const initialData: FlowDocumentJSON = {
5353
meta: {
5454
position: {
5555
x: 1100,
56-
y: 531.2,
56+
y: 546.2,
5757
},
5858
},
5959
data: {
@@ -81,8 +81,8 @@ export const initialData: FlowDocumentJSON = {
8181
type: 'end',
8282
meta: {
8383
position: {
84-
x: 2928,
85-
y: 586.2,
84+
x: 2968,
85+
y: 601.2,
8686
},
8787
},
8888
data: {
@@ -119,7 +119,7 @@ export const initialData: FlowDocumentJSON = {
119119
meta: {
120120
position: {
121121
x: 180,
122-
y: 760.2,
122+
y: 775.2,
123123
},
124124
},
125125
data: {
@@ -136,7 +136,7 @@ export const initialData: FlowDocumentJSON = {
136136
meta: {
137137
position: {
138138
x: 640,
139-
y: 406.35,
139+
y: 421.35,
140140
},
141141
},
142142
data: {
@@ -176,8 +176,8 @@ export const initialData: FlowDocumentJSON = {
176176
type: 'loop',
177177
meta: {
178178
position: {
179-
x: 1440,
180-
y: 90,
179+
x: 1460,
180+
y: 0,
181181
},
182182
},
183183
data: {
@@ -192,6 +192,18 @@ export const initialData: FlowDocumentJSON = {
192192
content: ['llm_6aSyo', 'result'],
193193
},
194194
},
195+
outputs: {
196+
type: 'object',
197+
required: [],
198+
properties: {
199+
acm: {
200+
type: 'array',
201+
items: {
202+
type: 'string',
203+
},
204+
},
205+
},
206+
},
195207
},
196208
blocks: [
197209
{
@@ -391,8 +403,8 @@ export const initialData: FlowDocumentJSON = {
391403
type: 'group',
392404
meta: {
393405
position: {
394-
x: 1604,
395-
y: 738.2,
406+
x: 1624,
407+
y: 698.2,
396408
},
397409
},
398410
data: {
@@ -587,4 +599,13 @@ export const initialData: FlowDocumentJSON = {
587599
targetNodeID: 'llm_vTyMa',
588600
},
589601
],
602+
globalVariable: {
603+
type: 'object',
604+
required: [],
605+
properties: {
606+
userId: {
607+
type: 'string',
608+
},
609+
},
610+
},
590611
};
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
import { PackageManagerTabs } from '@theme';
2+
3+
# Auto Layout Plugin
4+
5+
An automatic layout plugin based on the Dagre algorithm that provides intelligent node arrangement functionality for free layout canvases.
6+
7+
## Features
8+
9+
- Based on Dagre directed graph layout algorithm, automatically calculates optimal node positions
10+
- Supports multiple layout directions (left-to-right, top-to-bottom, etc.)
11+
- Configurable node spacing, margins, and other layout parameters
12+
- Supports recursive layout for nested containers
13+
- Provides animation effects and view adaptation functionality
14+
- Integrates with history system, supports undo/redo operations
15+
16+
![Preview](@/public/plugin/auto-layout.gif)
17+
18+
## Quick Start
19+
20+
1. Installation
21+
22+
<PackageManagerTabs command="install @flowgram.ai/free-auto-layout-plugin" />
23+
24+
2. Register Plugin
25+
26+
The plugin registration method is basically the same as other flowgram plugins. Just ensure not to create duplicates and pass it to the corresponding FreeLayoutEditorProvider.
27+
28+
```tsx
29+
import { createFreeAutoLayoutPlugin } from '@flowgram.ai/free-auto-layout-plugin';
30+
31+
const editorProps = useMemo(() => ({
32+
plugins: () => [
33+
createFreeAutoLayoutPlugin({
34+
layoutConfig: {
35+
rankdir: 'LR', // Layout direction: left to right
36+
nodesep: 100, // Node spacing
37+
ranksep: 100, // Rank spacing
38+
}
39+
})
40+
]
41+
}), []);
42+
43+
return (
44+
<FreeLayoutEditorProvider {...editorProps}>
45+
<EditorRenderer />
46+
</FreeLayoutEditorProvider>
47+
)
48+
```
49+
50+
3. Use in React Components
51+
52+
You can also trigger auto layout in components using utility classes:
53+
54+
```tsx
55+
import { WorkflowAutoLayoutTool } from '@flowgram.ai/free-layout-editor';
56+
57+
const AutoLayoutButton = () => {
58+
const tools = usePlaygroundTools();
59+
const playground = usePlayground();
60+
61+
const handleAutoLayout = async () => {
62+
await tools.autoLayout({
63+
enableAnimation: true, // Enable animation effects
64+
animationDuration: 1000, // Animation duration
65+
disableFitView: false, // Auto fit view after layout
66+
});
67+
}
68+
69+
return (
70+
<button onClick={handleAutoLayout}>
71+
Auto Layout
72+
</button>
73+
);
74+
};
75+
```
76+
77+
4. Use Auto Layout Service
78+
79+
Execute layout by obtaining AutoLayoutService instance through dependency injection:
80+
81+
```tsx
82+
import { AutoLayoutService } from '@flowgram.ai/free-auto-layout-plugin';
83+
84+
class MyLayoutService {
85+
@inject(AutoLayoutService)
86+
private autoLayoutService: AutoLayoutService;
87+
88+
async performAutoLayout() {
89+
await this.autoLayoutService.layout({
90+
enableAnimation: true, // Enable animation effects
91+
animationDuration: 1000, // Animation duration
92+
disableFitView: false, // Auto fit view after layout
93+
});
94+
}
95+
}
96+
```
97+
98+
## Configuration Options
99+
100+
### LayoutConfig
101+
102+
Configuration parameters for the layout algorithm:
103+
104+
```typescript
105+
interface LayoutConfig {
106+
/** Layout direction */
107+
rankdir?: 'TB' | 'BT' | 'LR' | 'RL';
108+
/** Alignment */
109+
align?: 'UL' | 'UR' | 'DL' | 'DR';
110+
/** Node separation within same rank */
111+
nodesep?: number;
112+
/** Edge separation */
113+
edgesep?: number;
114+
/** Rank separation */
115+
ranksep?: number;
116+
/** Horizontal margin */
117+
marginx?: number;
118+
/** Vertical margin */
119+
marginy?: number;
120+
/** Cycle removal algorithm */
121+
acyclicer?: string;
122+
/** Ranking algorithm */
123+
ranker?: 'network-simplex' | 'tight-tree' | 'longest-path';
124+
}
125+
```
126+
127+
### LayoutOptions
128+
129+
Options for layout execution:
130+
131+
```typescript
132+
interface LayoutOptions {
133+
/** Container node, defaults to root node */
134+
containerNode?: WorkflowNodeEntity;
135+
/** Function to get follow node */
136+
getFollowNode?: GetFollowNode;
137+
/** Disable auto fit view */
138+
disableFitView?: boolean;
139+
/** Enable animation effects */
140+
enableAnimation?: boolean;
141+
/** Animation duration (milliseconds) */
142+
animationDuration?: number;
143+
}
144+
```
145+
146+
## Layout Algorithm
147+
148+
### Dagre Algorithm
149+
150+
The plugin is implemented based on the Dagre library, which is a JavaScript library specifically designed for directed graph layout. Algorithm features:
151+
152+
- **Hierarchical Layout**: Organizes nodes into different levels based on dependency relationships
153+
- **Minimize Crossings**: Attempts to minimize line crossings
154+
- **Even Distribution**: Evenly distributes nodes while satisfying constraints
155+
156+
### Layout Process
157+
158+
1. **Graph Construction**: Converts workflow nodes and connections into Dagre graph structure
159+
2. **Rank Calculation**: Calculates levels (ranks) based on node dependencies
160+
3. **Order Optimization**: Optimizes node order within each level to reduce crossings
161+
4. **Position Calculation**: Calculates final coordinate positions for each node
162+
5. **Animation Execution**: If animation is enabled, smoothly transitions to new positions
163+
164+
## Advanced Usage
165+
166+
### Custom Follow Nodes
167+
168+
You can customize node following relationships through the `getFollowNode` function:
169+
170+
```typescript
171+
const layoutOptions: LayoutOptions = {
172+
getFollowNode: (node: LayoutNode) => {
173+
// Return the node ID that should follow the current node
174+
if (node.flowNodeType.type === 'comment') {
175+
return getNearestNode(node);
176+
}
177+
return undefined;
178+
}
179+
};
180+
```
181+
182+
### Nested Container Layout
183+
184+
The plugin supports recursive layout for nested containers and automatically handles node arrangement within containers:
185+
186+
```typescript
187+
// Execute layout for specific container
188+
await autoLayoutService.layout({
189+
containerNode: specificContainerNode,
190+
enableAnimation: true,
191+
});
192+
```
193+
194+
## FAQ
195+
196+
### Q: How to trigger auto layout during initialization?
197+
198+
A: Call the `ctx.tool.autoLayout()` method after the canvas rendering is complete to trigger auto layout.
199+
200+
```typescript
201+
const editorProps = useMemo(() => ({
202+
onAllLayersRendered: (ctx) => {
203+
ctx.tool.autoLayout({
204+
enableAnimation: false, // Disable animation during initialization for better user experience
205+
}
206+
);
207+
}
208+
}), []);
209+
```
210+
211+
### Q: How to implement custom layout directions?
212+
213+
A: Method 1: Register AutoLayout plugin in EditorProps and control layout direction through the `rankdir` parameter:
214+
215+
```typescript
216+
217+
import { createFreeAutoLayoutPlugin } from '@flowgram.ai/free-auto-layout-plugin';
218+
219+
const editorProps = useMemo(() => ({
220+
plugins: () => [
221+
createFreeAutoLayoutPlugin({
222+
layoutConfig: {
223+
rankdir: 'TB', // Top to bottom
224+
// rankdir: 'LR', // Left to right (default)
225+
// rankdir: 'RL', // Right to left
226+
// rankdir: 'BT', // Bottom to top
227+
}
228+
})
229+
]
230+
}), []);
231+
```
232+
233+
Method 2: Pass layout configuration through the `layoutConfig` parameter when calling the `autoLayout` method:
234+
235+
```typescript
236+
const tools = usePlaygroundTools();
237+
const playground = usePlayground();
238+
239+
const handleAutoLayout = async () => {
240+
await tools.autoLayout({
241+
layoutConfig: {
242+
rankdir: 'TB', // Top to bottom
243+
// rankdir: 'LR', // Left to right (default)
244+
// rankdir: 'RL', // Right to left
245+
// rankdir: 'BT', // Bottom to top
246+
}
247+
});
248+
}
249+
```
250+
251+
### Q: How to optimize layout animation stuttering?
252+
253+
A: For complex graphics, it's recommended to disable animation or reduce animation duration:
254+
255+
```typescript
256+
layoutOptions: {
257+
enableAnimation: false, // Disable animation
258+
// or
259+
animationDuration: 150, // Reduce animation duration
260+
}
261+
```
1.01 MB
Loading
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
[
22
"custom-plugin",
3-
"panel-manager-plugin"
3+
"panel-manager-plugin",
4+
"free-auto-layout-plugin"
45
]

0 commit comments

Comments
 (0)