@flowgram.ai/free-auto-layout-plugin

An automatic layout plugin based on the Dagre algorithm that provides intelligent node arrangement functionality for free layout canvases.

Features

  • Based on Dagre directed graph layout algorithm, automatically calculates optimal node positions
  • Supports multiple layout directions (left-to-right, top-to-bottom, etc.)
  • Configurable node spacing, margins, and other layout parameters
  • Supports recursive layout for nested containers
  • Provides animation effects and view adaptation functionality
  • Integrates with history system, supports undo/redo operations

Preview

Quick Start

  1. Installation
npm
yarn
pnpm
bun
npm install @flowgram.ai/free-auto-layout-plugin
  1. Register Plugin

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.

import { createFreeAutoLayoutPlugin } from '@flowgram.ai/free-auto-layout-plugin';

const editorProps = useMemo(() => ({
  plugins: () => [
    createFreeAutoLayoutPlugin({
      layoutConfig: {
        rankdir: 'LR', // Layout direction: left to right
        nodesep: 100,  // Node spacing
        ranksep: 100,  // Rank spacing
      }
    })
  ]
}), []);

return (
  <FreeLayoutEditorProvider {...editorProps}>
    <EditorRenderer />
  </FreeLayoutEditorProvider>
)
  1. Use in React Components

You can also trigger auto layout in components using utility classes:

import { WorkflowAutoLayoutTool } from '@flowgram.ai/free-layout-editor';

const AutoLayoutButton = () => {
  const tools = usePlaygroundTools();
  const playground = usePlayground();

  const handleAutoLayout = async () => {
    await tools.autoLayout({
      enableAnimation: true,      // Enable animation effects
      animationDuration: 1000,     // Animation duration
      disableFitView: false,      // Auto fit view after layout
    });
  }

  return (
    <button onClick={handleAutoLayout}>
      Auto Layout
    </button>
  );
};
  1. Use Auto Layout Service

Execute layout by obtaining AutoLayoutService instance through dependency injection:

import { AutoLayoutService } from '@flowgram.ai/free-auto-layout-plugin';

class MyLayoutService {
  @inject(AutoLayoutService)
  private autoLayoutService: AutoLayoutService;

  async performAutoLayout() {
    await this.autoLayoutService.layout({
      enableAnimation: true,      // Enable animation effects
      animationDuration: 1000,     // Animation duration
      disableFitView: false,      // Auto fit view after layout
    });
  }
}

Configuration Options

LayoutConfig

Configuration parameters for the layout algorithm:

interface LayoutConfig {
  /** Layout direction */
  rankdir?: 'TB' | 'BT' | 'LR' | 'RL';
  /** Alignment */
  align?: 'UL' | 'UR' | 'DL' | 'DR';
  /** Node separation within same rank */
  nodesep?: number;
  /** Edge separation */
  edgesep?: number;
  /** Rank separation */
  ranksep?: number;
  /** Horizontal margin */
  marginx?: number;
  /** Vertical margin */
  marginy?: number;
  /** Cycle removal algorithm */
  acyclicer?: string;
  /** Ranking algorithm */
  ranker?: 'network-simplex' | 'tight-tree' | 'longest-path';
}

LayoutOptions

Options for layout execution:

interface LayoutOptions {
  /** Container node, defaults to root node */
  containerNode?: WorkflowNodeEntity;
  /** Function to get follow node */
  getFollowNode?: GetFollowNode;
  /** Disable auto fit view */
  disableFitView?: boolean;
  /** Enable animation effects */
  enableAnimation?: boolean;
  /** Animation duration (milliseconds) */
  animationDuration?: number;
  /** Node filter function to control which nodes participate in layout calculation */
  filterNode?: (params: { node: WorkflowNodeEntity; parent?: WorkflowNodeEntity }) => boolean;
}

Layout Algorithm

Dagre Algorithm

The plugin is implemented based on the Dagre library, which is a JavaScript library specifically designed for directed graph layout. Algorithm features:

  • Hierarchical Layout: Organizes nodes into different levels based on dependency relationships
  • Minimize Crossings: Attempts to minimize line crossings
  • Even Distribution: Evenly distributes nodes while satisfying constraints

Layout Process

  1. Graph Construction: Converts workflow nodes and connections into Dagre graph structure
  2. Rank Calculation: Calculates levels (ranks) based on node dependencies
  3. Order Optimization: Optimizes node order within each level to reduce crossings
  4. Position Calculation: Calculates final coordinate positions for each node
  5. Animation Execution: If animation is enabled, smoothly transitions to new positions

Advanced Usage

Custom Follow Node

You can customize node following relationships through the getFollowNode function:

const layoutOptions: LayoutOptions = {
  getFollowNode: (node: LayoutNode) => {
    // Return the node ID that should follow the current node
    if (node.flowNodeType === 'comment') {
      return getNearestNode(node);
    }
    return undefined;
  }
};
await tools.autoLayout(layoutOptions);

Node Filtering

You can control which nodes participate in layout calculation through the filterNode function:

const layoutOptions: LayoutOptions = {
  filterNode: ({ node, parent }) => {
    // Filter out specific types of nodes
    if (node.flowNodeType === 'comment') {
      return false;
    }

    // Filter based on parent node conditions
    if (parent && parent.flowNodeType.type === 'group') {
      return false;
    }

    return true; // Include all nodes by default
  }
};

// Execute layout with filter options
await tools.autoLayout(layoutOptions);

Layout Only for the Specified Container

The plugin supports recursive layout for a specified container and automatically handles node arrangement within the container:

// Execute layout for specific container
await autoLayoutService.layout({
  containerNode: specificContainerNode,
  enableAnimation: true,
});

FAQ

Q: How to trigger auto layout during initialization?

A: Call the ctx.tool.autoLayout() method after the canvas rendering is complete to trigger auto layout.

const editorProps = useMemo(() => ({
  onAllLayersRendered: (ctx) => {
    ctx.tool.autoLayout({
      enableAnimation: false, // Disable animation during initialization for better user experience
    }
    );
  }
}), []);

Q: How to implement custom layout directions?

A: Method 1: Register AutoLayout plugin in EditorProps and control layout direction through the rankdir parameter:


import { createFreeAutoLayoutPlugin } from '@flowgram.ai/free-auto-layout-plugin';

const editorProps = useMemo(() => ({
  plugins: () => [
    createFreeAutoLayoutPlugin({
      layoutConfig: {
        rankdir: 'TB', // Top to bottom
        // rankdir: 'LR', // Left to right (default)
        // rankdir: 'RL', // Right to left
        // rankdir: 'BT', // Bottom to top
      }
    })
  ]
}), []);

Method 2: Pass layout configuration through the layoutConfig parameter when calling the autoLayout method:

const tools = usePlaygroundTools();
const playground = usePlayground();

const handleAutoLayout = async () => {
  await tools.autoLayout({
    layoutConfig: {
      rankdir: 'TB', // Top to bottom
      // rankdir: 'LR', // Left to right (default)
      // rankdir: 'RL', // Right to left
      // rankdir: 'BT', // Bottom to top
    }
  });
}

Q: How to optimize layout animation stuttering?

A: For complex workflows, it's recommended to disable animation or reduce animation duration:

layoutOptions: {
  enableAnimation: false, // Disable animation
  // or
  animationDuration: 150, // Reduce animation duration
}