JavaScript is required

Expand/Collapse Relayout Toggle (Center Layout)

A relation-graph demo that compares whether built-in expand or collapse should trigger automatic relayout in a center-layout hierarchy. It loads a fixed 39-node inline dataset, exposes a `Re-layout` or `Do not re-layout` selector in a draggable helper window, and inherits secondary canvas settings plus image export controls from a shared helper.

Live Expand/Collapse Relayout in a Centered Hierarchy

What This Example Builds

This example builds a full-height centered hierarchy viewer for comparing what happens after a user expands or collapses branches with relation-graph’s built-in expand holder. The canvas shows one root-centered subsystem-style graph with large cyan circular nodes, teal connectors, repeated Subsystem labels rendered on the line path, and a floating white helper window layered above the graph.

Users can expand or collapse branches directly on the graph, switch the follow-up behavior between Re-layout and Do not re-layout, drag the helper window, minimize it, and open a secondary settings overlay. That overlay comes from a shared local helper and adds wheel-mode, drag-mode, and image-download controls, but the main point of the example is the live toggle for reLayoutWhenExpandedOrCollapsed in a center layout rather than any custom animation-tuning logic.

How the Data Is Organized

The data is declared inline inside initializeGraph() as one RGJsonData object. It uses rootId: '2', defines 39 hard-coded nodes, and connects them with 38 explicit lines. The labels mix subsystem names, manufacturing steps, and component names, but the structure itself stays simple: nodes only need id and text, while lines only need from, to, and the repeated edge label text.

There is no preprocessing step before setJsonData(...). The component does not fetch data, normalize records, or derive a second layout structure. In a real application, the same pattern could represent a product architecture map, an equipment decomposition, a manufacturing capability breakdown, a technical subsystem tree, or any other root-centered hierarchy where the team needs to decide whether expansion should trigger a fresh layout pass.

How relation-graph Is Used

index.tsx wraps the example in RGProvider, and MyGraph.tsx uses RGHooks.useGraphInstance() to control the graph through the instance API. The graph options configure layoutName: 'center' with distanceCoefficient: 1.5, circular nodes sized to 100x100, cyan node fill, teal line color, bottom-positioned expand holders, and line text rendered directly on the connector path. The result is a root-centered diagram where the visual impact of post-expand relayout is easy to compare because branches can redistribute in multiple directions around the root.

The component loads its inline hierarchy with setJsonData(...), then immediately calls moveToCenter() and zoomToFit() so the full graph starts framed in view. A second effect watches the React relayout state and pushes it into the live graph instance through updateOptions({ reLayoutWhenExpandedOrCollapsed }). That runtime synchronization is the main relation-graph technique in this sample: the graph behavior changes without rebuilding the dataset or remounting the component.

There are no custom node, line, canvas, or viewport slots in this example, and there are no example-specific graph event handlers. Expand and collapse behavior comes from relation-graph’s built-in holder, not from custom slots or manual branch visibility code. The example also does not implement editing or authoring features.

The floating control surface comes from the shared local DraggableWindow helper. That helper is not unique to this example, but it matters because it makes the relayout selector movable and gives the demo a secondary settings overlay. Inside that overlay, CanvasSettingsPanel uses RGHooks.useGraphStore() to read wheelEventAction and dragEventAction, calls graphInstance.setOptions(...) to update those values on the live graph, and uses prepareForImageGeneration() plus restoreAfterImageGeneration() to support image export. The local SCSS file only contains empty wrapper selectors, so nearly all visible styling comes from graph options and the shared floating window instead of from custom CSS overrides.

Key Interactions

  • Clicking a built-in expand holder opens or closes a branch in the centered hierarchy.
  • Clicking Re-layout or Do not re-layout updates reLayoutWhenExpandedOrCollapsed on the mounted graph, so the next expand or collapse action either repacks the visible hierarchy or preserves the current branch placement.
  • The helper window can be dragged by its title bar, which keeps the control surface movable instead of fixed to one screen position.
  • The helper window can be minimized, which lets the example switch between a teaching mode with visible controls and a cleaner viewing mode.
  • The settings button opens a shared overlay that can change wheel behavior, change canvas drag behavior, and download the graph as an image. Those controls are secondary shared utilities rather than the example’s main lesson.

Key Code Fragments

This fragment shows that the example is deliberately configured as a centered hierarchy with larger spacing, large circular nodes, and a runtime relayout option:

const graphOptions: RGOptions = {
    layout: {
        layoutName: 'center',
        distanceCoefficient: 1.5
    },
    defaultNodeBorderWidth: 1,
    defaultNodeShape: RGNodeShape.circle,
    defaultNodeWidth: 100,
    defaultNodeHeight: 100,
    defaultLineColor: 'rgba(0, 186, 189, 1)',
    defaultNodeColor: 'rgba(0, 206, 209, 1)',
    reLayoutWhenExpandedOrCollapsed: relayout,
    defaultExpandHolderPosition: 'bottom',
    defaultLineTextOnPath: true
};

This fragment shows that the hierarchy is assembled inline and passed directly to relation-graph without any transformation step:

const myJsonData: RGJsonData = {
    rootId: '2',
    nodes: [
        { id: '2', text: 'ALTXX' },
        { id: '3', text: 'CH2 TTN' },
        { id: '4', text: 'CH1 AlCu' },
        // ... additional nodes omitted
    ],
    lines: [
        { from: '2', to: '5', text: 'Subsystem' },
        { from: '2', to: '6', text: 'Subsystem' },
        // ... additional lines omitted
    ]
};

This fragment shows the mount-time loading sequence: load the hierarchy once, then center and fit it in the viewport:

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

This fragment shows the runtime option-synchronization pattern that makes the example useful:

const syncOptionsToGraph = () => {
    graphInstance.updateOptions({
        reLayoutWhenExpandedOrCollapsed: relayout
    });
};

useEffect(() => {
    syncOptionsToGraph();
}, [relayout]);

This fragment shows that the user-facing control is a direct boolean selector instead of a reset, rebuild, or script-driven action:

<SimpleUISelect
    data={[
        { value: true, text: 'Re-layout' },
        { value: false, text: 'Do not re-layout' }
    ]}
    currentValue={relayout}
    onChange={(newValue: boolean) => {
        setRelayout(newValue);
    }}
/>

This fragment shows that the shared overlay can still reconfigure live canvas behavior through the graph instance:

const { options } = RGHooks.useGraphStore();
const dragMode = options.dragEventAction;
const wheelMode = options.wheelEventAction;

<SettingRow
    label="Wheel Event:"
    value={wheelMode}
    onChange={(newValue: string) => { graphInstance.setOptions({ wheelEventAction: newValue }); }}
/>

What Makes This Example Distinct

The comparison data shows that expand-animation is the closest direct neighbor because both examples use the same floating selector pattern and the same updateOptions({ reLayoutWhenExpandedOrCollapsed }) technique. The difference is the layout context. This example uses a centered hierarchy with distanceCoefficient: 1.5, large cyan circular nodes, bottom expand holders, and path-labeled teal connectors, so branch redistribution happens around one root instead of along a single vertical direction.

Compared with open-all-close-all, this sample does not script recursive whole-graph playback, timed expansion, or animation choreography. It isolates the simpler decision of whether ordinary user-driven expand or collapse actions should trigger relayout. Compared with multiple-expand-buttons, it relies on the built-in expand holder rather than custom node slots and manual left-or-right branch visibility logic.

What makes it especially useful is the combination of a 39-node centered subsystem hierarchy, live synchronization of reLayoutWhenExpandedOrCollapsed, a floating helper window, and path-based line labels on one screen. That makes it a stronger starting point for root-centered hierarchy products than the top-down tree version when the team needs to evaluate how much spatial movement should happen after disclosure. The comparison artifacts also make one boundary clear: this is not a true animation-parameter demo, because the reviewed source does not configure custom timing, easing, or transition code.

Where Else This Pattern Applies

This pattern transfers well to subsystem maps, product structure viewers, equipment trees, capability maps, and technical knowledge hierarchies where the root sits near the middle of the canvas and users repeatedly open or close branches while trying to keep orientation. It is especially relevant when the product team needs to decide whether expansion should preserve local positions or trigger a full visual rebalance.

The same approach can also be reused in settings-heavy graph tools, QA harnesses, or admin interfaces that expose layout behavior as a runtime preference. Instead of hard-coding one disclosure policy, a product can let users compare both modes on the same dataset before committing to the default.