JavaScript is required

Automatic Layout Preset Switching

This example shows how to load one branching graph once and automatically rotate it through the built-in center, tree, circle, and force layouts. A floating helper window provides play-stop control, shared canvas settings, and image export so the page works as a compact layout-comparison workspace.

Automatically Rotate One Graph Across Four Built-in Layouts

What This Example Builds

This example builds a full-height relation-graph canvas that turns one fixed branching dataset into a layout comparison screen. The same nodes and lines are shown as center, left-to-right tree, circle, and force layouts while a small floating window stays above the canvas.

Users can pause or resume the automatic rotation, drag or minimize the helper window, open the shared canvas settings overlay, change wheel and drag behavior, and export the current graph as an image. The main point is not custom rendering or data editing. It is a compact pattern for showing how one mounted graph changes shape when layout options are updated at runtime.

How the Data Is Organized

The data lives inside initializeGraph() as one inline RGJsonData object with rootId: 'a', a flat nodes array, and a flat lines array with explicit line ids. The graph forms one root with four first-level branches and several leaf nodes under each branch.

There is no preprocessing step before setJsonData(). The example constructs the full payload in place, loads it once, and then keeps reusing that same in-memory graph while only the layout options change.

In real projects, this structure can stand in for an organization chart, a service dependency tree, a product taxonomy, or any rooted graph where the same entities need to be reviewed under different layout families.

How relation-graph Is Used

index.tsx wraps the page in RGProvider, and MyGraph.tsx gets the mounted graph through RGHooks.useGraphInstance(). The graph options stay intentionally small: debug mode is off, rectangular nodes are used by default, and the initial layout is the first preset in the exampleLayouts array.

Initialization is imperative. After setJsonData() loads the graph, the example calls moveToCenter() and zoomToFit() so the first view is readable, then it flips React state to playing so autoplay begins. The runtime switching loop updates layout on the live instance with updateOptions({ layout }), waits for doLayout(), and then recenters and fits again after each preset.

The example does not define graph event handlers, slots, or editing APIs. The local SCSS file only contains empty selector placeholders, so the visible result comes mostly from built-in relation-graph rendering and the shared floating helper component.

That floating window is not a graph slot. It is a separate overlay component that shares graph context through RGProvider. Its settings panel uses RGHooks.useGraphStore() to read current wheel and drag modes, calls setOptions() to change those canvas behaviors, and wraps screenshot export with prepareForImageGeneration() and restoreAfterImageGeneration().

Key Interactions

  • The page starts autoplay immediately after the first data load.
  • The play-stop button toggles the playing state; when active, a 2-second timeout keeps relayouting the same graph through the preset array.
  • Each timed switch updates the layout, awaits doLayout(), then recenters and zoom-fits so each composition stays readable.
  • The floating helper window can be dragged, minimized, and switched into the shared settings overlay.
  • The settings overlay changes wheel and drag behavior and exposes image export.
  • There is no direct manual picker for an individual layout preset in this example.

Key Code Fragments

This fragment shows that the example defines a preset array instead of building layouts dynamically at runtime.

const exampleLayouts: { label: string, layoutOptions: RGLayoutOptions }[] = [
    {
        label: 'Center',
        layoutOptions: {
            layoutName: 'center'
        }
    },
    // ...
    {
        label: 'Force',
        layoutOptions: {
            layoutName: 'force'
        }
    }
];

This fragment shows that the graph data is assembled inline as one RGJsonData payload and loaded once.

const myJsonData: RGJsonData = {
    rootId: 'a',
    nodes: [
        { id: 'a', text: 'a' },
        { id: 'b', text: 'b' },
        { id: 'b1', text: 'b1' },
        // ...
    ],
    lines: [
        { id: 'l1', from: 'a', to: 'b' },
        // ...
    ]
};

This fragment is the mount-time bootstrap that loads the graph, resets the viewport, and starts autoplay.

await graphInstance.setJsonData(myJsonData);
graphInstance.moveToCenter();
graphInstance.zoomToFit();
setPlaying(true);

This fragment is the core relayout loop: rewrite the current layout, run layout again, then schedule the next preset.

if (currentLayoutIndexRef.current > exampleLayouts.length - 1) currentLayoutIndexRef.current = 0;
graphInstance.updateOptions({
    layout: exampleLayouts[currentLayoutIndexRef.current].layoutOptions
});
await graphInstance.doLayout();
graphInstance.moveToCenter();
graphInstance.zoomToFit();
playNextLayoutTimer.current = setTimeout(() => {
    currentLayoutIndexRef.current++;
    switchToNextLayout();
}, 2000);

This fragment shows that the shared helper window also exposes canvas export through the mounted graph instance.

const downloadImage = async () => {
    const canvasDom = await graphInstance.prepareForImageGeneration();
    let graphBackgroundColor = graphInstance.getOptions().backgroundColor;
    if (!graphBackgroundColor || graphBackgroundColor === 'transparent') {
        graphBackgroundColor = '#ffffff';
    }
    const imageBlob = await domToImageByModernScreenshot(canvasDom, {
        backgroundColor: graphBackgroundColor
    });
    await graphInstance.restoreAfterImageGeneration();
};

What Makes This Example Distinct

The comparison data frames this example as a layout comparison playground rather than a manual tuning panel. Its rare combination is one inline branching dataset, an exampleLayouts preset array, autoplay that starts after the first load, and a recurring updateOptions({ layout }) plus doLayout() loop that recenters the graph after every switch.

Compared with graph-angle-offset and center-layout-options, this example puts more emphasis on unattended preset rotation across layout families than on sliders, angle changes, or spacing controls inside one family. Compared with ever-changing-tree, it removes direction, spacing, and line-geometry tuning so the lesson stays focused on broad silhouette changes across several built-in layouts.

The comparison record also shows that the shared floating workspace is secondary here. Unlike drag-and-wheel-event, the main subject is not canvas input behavior. Unlike node-style2, the main subject is not CSS styling. This example is narrower and more useful when the requirement is simply to show one graph reflowing through several built-in layouts without reloading data.

Where Else This Pattern Applies

This pattern transfers well to demo screens, documentation pages, and internal review tools where teams need to compare several layout families against the same graph before choosing a default presentation.

It also fits organization maps, dependency graphs, taxonomy trees, and knowledge structures that keep the same entities but need different silhouette comparisons. The reusable technique is the mounted-instance control flow: load data once, update layout options, rerun layout, and reset the viewport without rebuilding the graph.