JavaScript is required

Tree Layout Orientation Switcher

This example builds a static hierarchy viewer that switches one loaded tree between left-to-right and top-down presets at runtime. Its main lesson is the relayout flow that reapplies node dimensions, line shape defaults, and vertical-text styling so the same dataset stays readable in both orientations.

Tree Layout Orientation Switching with Live Relayout

What This Example Builds

This example builds a read-only hierarchy viewer for a company wiki directory. The same tree can be shown as a left-to-right tree or a top-down tree without loading a second dataset.

Users can switch the layout direction from a floating selector, inspect the graph through an embedded mini view, and open the shared canvas settings window for drag, wheel, and image-export utilities. The main point of the example is that it relayouts an already loaded graph and reapplies orientation-specific node sizing, connector settings, and text formatting before the next layout pass.

How the Data Is Organized

The data comes from an async local getMockData() function that returns one RGJsonData object with rootId, a flat nodes array, and a flat lines array. The content represents a documentation-style hierarchy: a root wiki directory, several top-level sections, and deeper policy, project, and support branches.

There is no preprocessing before setJsonData(...) runs. The only runtime transformation happens after the data is already mounted: when the orientation changes, the code selects a preset, rewrites each existing node’s width and height, rewrites each existing line’s shape, then calls doLayout().

In a real application, the same structure could represent a knowledge base, policy manual, learning catalog, content taxonomy, or any other tree where the same hierarchy must remain readable in more than one orientation.

How relation-graph Is Used

The example is wrapped in RGProvider, and MyGraph uses RGHooks.useGraphInstance() as the main runtime API surface. Two RGOptions presets drive the graph. Both use layoutName: 'tree', but the horizontal preset uses from: 'left', large horizontal spacing, auto-sized rectangular nodes, left-right junctions, and white nodes with gray lines. The vertical preset uses from: 'top', tall narrow rectangles, top-bottom junctions, and borderless gray nodes.

The graph is initialized by awaiting getMockData() and passing the result to setJsonData(...). After that, a useEffect reacts to layoutFrom changes and runs the relayout sequence. The code calls setOptions(...), iterates over getNodes() plus updateNode(...) to normalize live node dimensions, iterates over getLines() plus updateLine(...) to normalize line shape, waits with sleep(100), then runs doLayout(), moveToCenter(), and zoomToFit().

The example does not use custom node or line slots. Instead, it keeps the built-in rendering and customizes it through options plus SCSS. RGSlotOnView mounts RGMiniView as a viewport overlay, and the wrapper class switches to .my-layout-top so the top-down mode can apply writing-mode: vertical-rl to built-in node labels. The floating DraggableWindow is a shared helper component; inside it, CanvasSettingsPanel uses relation-graph hooks to change wheel and drag behavior and to export the canvas through prepareForImageGeneration() and restoreAfterImageGeneration().

Key Interactions

The primary interaction is layout orientation switching. Clicking the SimpleUISelect control changes layoutFrom, which triggers a full relayout of the mounted graph rather than rebuilding the dataset from scratch.

The floating helper window can be dragged, minimized, and expanded into a settings panel. That panel changes wheel behavior, changes canvas drag behavior, and downloads a generated image of the graph.

Node and line click handlers are present, but they only log the clicked objects. They do not drive layout, navigation, or editing.

Key Code Fragments

This fragment shows that the data model is a single static RGJsonData tree with one root and flat node and line arrays.

export const getMockData = async() => {
    return {
        "rootId": "1",
        "nodes": [
            {
                "id": "1",
                "text": "Company Wiki Directory"
            },

This fragment shows the horizontal preset: built-in tree layout, left-to-right direction, large horizontal spacing, and auto-sized rectangular nodes.

const graphOptionsH: RGOptions = {
    layout: {
        layoutName: 'tree',
        treeNodeGapH: 300,
        treeNodeGapV: 10,
        from: 'left'
    },
    defaultNodeShape: RGNodeShape.rect,
    defaultNodeWidth: 0,
    defaultNodeHeight: 0,

This fragment proves that orientation changes normalize already loaded node and line state before the next layout pass.

const targetOptions = layoutFrom === 'left' ? graphOptionsH : graphOptionsV;
graphInstance.setOptions(targetOptions);
graphInstance.getNodes().forEach((node) => {
    graphInstance.updateNode(node, {
        width: targetOptions.defaultNodeWidth,
        height: targetOptions.defaultNodeHeight
    });
});
graphInstance.getLines().forEach((line) => {
    graphInstance.updateLine(line, {
        lineShape: targetOptions.defaultLineShape
    });
});

This fragment shows that the relayout flow waits for the DOM to settle, then recomputes layout and refits the viewport.

await graphInstance.sleep(100);
await graphInstance.doLayout();
graphInstance.moveToCenter();
graphInstance.zoomToFit();

This fragment shows the runtime control that switches between horizontal and vertical presets.

<SimpleUISelect
    data={[
        { value: 'left', text: 'Horizontal Tree' },
        { value: 'top', text: 'Vertical Tree' }
    ]}
    currentValue={layoutFrom}
    onChange={(newValue: string) => { setLayoutFrom(newValue); }}
/>

This fragment shows the top-down style override that changes built-in node labels to vertical writing.

.my-graph.my-layout-top {
    .relation-graph {
        .rg-node-peel {
            .rg-node {
                .rg-node-text {
                    padding-top: 10px;
                    justify-content: flex-start;
                    writing-mode: vertical-rl;
                }
            }
        }
    }
}

What Makes This Example Distinct

The comparison data shows that this example stands out less for having a layout switcher by itself and more for how it handles the switch. Its main distinguishing pattern is switching one already loaded hierarchy between ordinary tree presets while explicitly rewriting live node widths, heights, and line shapes before doLayout().

That makes it a cleaner reference than io-tree-layout when the goal is ordinary tree orientation switching rather than level-aware IO routing. Compared with bothway-tree2, it focuses on one-direction hierarchy readability and relayout normalization on mounted graph objects instead of bidirectional branches and line-label controls. Compared with industry-chain and layout-folder2, it keeps nodes visually simple so the main lesson stays on orientation-specific sizing, junction defaults, curved connectors, and label flow.

The rarity and comparison records also highlight an unusual visual pairing: horizontal mode uses auto-sized white rectangles, while top-down mode uses 30-by-260 narrow rectangles with vertical text. That combination makes this example a strong starting point when the same hierarchy must remain readable in two substantially different reading directions.

Where Else This Pattern Applies

This pattern applies to documentation trees, policy manuals, course outlines, product taxonomies, and support knowledge bases where the same hierarchy may need both a wide exploratory view and a narrow top-down presentation.

It also transfers to cases where a team must restyle a mounted graph without rebuilding its data model. The same relayout sequence can be adapted for print layouts, language-specific label flow, kiosk displays, or view-mode switches that need different node dimensions and connector defaults on the same underlying tree.