JavaScript is required

Connected Component Layout and Theme Switcher

This example loads two disconnected graph datasets into one canvas and lets the user click a node to select its full connected component. The selected component can then switch theme and layout presets without changing the rest of the canvas, and the viewport is adjusted so the active group stays beside the editing overlay.

Switch Theme and Layout for One Connected Component

What This Example Builds

This example builds a full-screen graph workbench that mounts two disconnected relation-graph datasets on the same canvas. The user clicks any node to select its entire connected component, then uses a floating toolbar that follows that selection to switch only that component’s theme or layout. A minimap stays visible in the graph overlay, and a draggable helper window exposes canvas settings and image export.

The important result is local editing inside a mixed canvas. The demo does not restyle or relayout the whole graph at once. It keeps the other disconnected network island unchanged while the selected component is recolored, reshaped, rerouted, and reframed.

How the Data Is Organized

MyGraph.tsx defines two inline RGJsonData objects, network1JsonData and network2JsonData, and each one contains its own rootId, nodes, and lines. Both datasets are pushed into the same graph instance with separate addNodes() and addLines() calls, so the canvas starts as one viewer containing two disconnected subnetworks.

There is no heavy preprocessing before the first layout pass. The real runtime preprocessing happens after selection: the clicked node id is stored in checkedNodeId, then getNetworkNodesByNode(...) expands that seed node into the whole connected component, and getLinesBetweenNodes(...) derives the matching internal edges for batch updates. Theme metadata lives in exampleThemes, where each entry maps a label to an item className and a swatch color. Layout metadata lives in exampleLayouts, where each preset bundles layoutOptions, default node and line overrides, and, for io-tree variants, an optional afterLayoutCallback.

In a real application, the same structure could represent multiple service clusters on one dependency map, several account groups in an investigation workspace, separate workflow fragments on one board, or multiple org-chart islands that need local presentation tuning without rebuilding the whole canvas.

How relation-graph Is Used

The demo is wrapped in RGProvider, and MyGraph.tsx uses RGHooks.useGraphInstance() as the control surface for all graph behavior. The initial graph options disable debug mode, set rectangular nodes and gray 2px lines as the baseline style, and start the canvas with a tree layout. On mount, the graph instance receives both inline datasets, runs doLayout(), then recenters and fits the combined view.

Selection is managed through relation-graph instance APIs rather than a separate data model. onNodeClick stores the clicked id, getNodeById(...) resolves the live node object, getNetworkNodesByNode(...) finds the connected component, and setEditingNodes(...) passes that component to RGEditingNodeController. Because the controller and the custom toolbar are rendered inside RGSlotOnView, the controls stay attached to the selected component instead of living in a fixed sidebar. RGMiniView is mounted in the same slot, so navigation remains available while editing.

Theme switching is implemented through class reassignment on already loaded graph items. The handler collects the selected component’s nodes and lines, then calls updateNode(...) and updateLine(...) with the preset itemsClassName. The SCSS file defines .my-theme-1 through .my-theme-7, and those selectors restyle node fills, borders, checked halos, line strokes, line text, and line-label blocks. The theme change therefore affects presentation only, not graph structure.

Layout switching is more involved. The handler resets node geometry and line routing to known defaults, creates a runtime layout object from the chosen preset, and calls placeNodes(groupNodes, checkedNode) so only the selected component is reflowed. IO-tree presets attach afterLayoutCallback functions that inspect links with getLinksBetweenNodes(...) and rewrite junction points after placement. The graph then uses enableCanvasAnimation(), zoomToFit(groupNodes), getNodesRectBox(...), getOptions(), and setCanvasCenter(...) to keep the edited component visible beside the open toolbar.

The helper window adds a second layer of runtime graph control. Its settings panel reads live drag and wheel mode from RGHooks.useGraphStore(), updates those modes with setOptions(...), and exports the current canvas through prepareForImageGeneration(), domToImageByModernScreenshot(...), and restoreAfterImageGeneration().

Key Interactions

  • Clicking a node does not change styles immediately. It stores checkedNodeId, then a follow-up effect resolves the full connected component and activates the editing controller for that group.
  • Clicking the Theme button opens a swatch popover. Choosing a swatch rewrites node and line className values only inside the selected component.
  • Clicking the Layout button opens a preset gallery with remote preview thumbnails and a badge showing the current selected node id. Choosing a preset changes node geometry, connector style, and layout only for the selected component.
  • IO-tree presets add another interaction step after placement by rewriting connector anchors and offsets so the rerouted lines match the chosen io-tree direction.
  • Clicking empty canvas clears checked state, empties the editing-node selection, and closes the active popover.
  • The floating helper window can be dragged, minimized, switched into a settings panel, and used to change wheel and drag behavior or download an image.
  • Line clicks are wired, but they only log to the console and are not part of the editing workflow.

Key Code Fragments

This fragment shows that the page merges two disconnected datasets into one live graph instance before any component-scoped editing begins.

graphInstance.addNodes(network1JsonData.nodes);
graphInstance.addLines(network1JsonData.lines);
graphInstance.addNodes(network2JsonData.nodes);
graphInstance.addLines(network2JsonData.lines);
await graphInstance.doLayout();
graphInstance.moveToCenter();
graphInstance.zoomToFit();

This fragment shows that selection expands from one checked node to its whole connected component before the overlay controller is updated.

const checkedNode = graphInstance.getNodeById(checkedNodeId);
if (checkedNode) {
    const groupNodes = getGroupNodes(checkedNode);
    graphInstance.setEditingNodes(groupNodes);
}

This fragment shows that theme switching is implemented as batch class reassignment on the selected nodes and their internal lines.

const groupNodes = getGroupNodes(checkedNode);
const groupLines = graphInstance.getLinesBetweenNodes(groupNodes);
groupNodes.forEach(n => {
    graphInstance.updateNode(n, { className: themeInfo.themeOptions.itemsClassName });
});
groupLines.forEach(l => {
    graphInstance.updateLine(l, { className: themeInfo.themeOptions.itemsClassName });
});

This fragment shows that an io-tree layout preset is more than a layoutName: it bundles geometry defaults and a post-layout routing callback.

{
    label: 'IO-Tree-1',
    layoutOptions: {
        layoutName: 'io-tree',
        from: 'left',
        treeNodeGapH: 50,
        treeNodeGapV: 10
    },
    defaultOptions: {
        node: { nodeShape: RGNodeShape.circle, width: 50, height: 50 },
        line: { lineShape: RGLineShape.StandardOrthogonal, fromJunctionPoint: RGJunctionPoint.right, toJunctionPoint: RGJunctionPoint.top }
    },
    afterLayoutCallback: (graphInstance, nodes) => { updateLinesForIOTree(graphInstance, nodes, 'left') }
}

This fragment shows that relayout is followed by a viewport adjustment that keeps the edited component beside the toolbar instead of simply refitting the whole canvas.

graphInstance.enableCanvasAnimation();
graphInstance.zoomToFit(groupNodes);
const nodesRect = graphInstance.getNodesRectBox(groupNodes);
const nodesLeftCenterOnCanvas = {
    x: nodesRect.minX,
    y: nodesRect.minY + nodesRect.height / 2
}
const options = graphInstance.getOptions();
const scale = options.canvasZoom / 100;
graphInstance.setCanvasCenter(nodesLeftCenterOnCanvas.x + 100 / scale, nodesLeftCenterOnCanvas.y);

What Makes This Example Distinct

Compared with switch-network-layout, this example puts more emphasis on presentation mutation than on partial relayout alone. Both demos use node-click selection, connected-component discovery, and RGEditingNodeController, but this one lets the same selected component go through both theme reassignment and preset-driven relayout.

Compared with switch-layout-pro, the important difference is scope. That neighbor uses a similar theme and layout toolkit for one whole graph, while this example applies the same style catalog to only the connected component around the clicked node. The two-network canvas matters because it proves the inactive island can remain mounted and untouched.

Compared with the Dagre-based neighbors, this is not an external-layout integration example. Its distinctive combination is built-in preset switching across center, tree, folder, circle, and io-tree layouts, class-based runtime theming, IO-tree routing callbacks, a selection-following overlay, and a viewport offset that keeps the active component beside the controls. That combination makes it a stronger starting point when a product needs local subgraph restyling and relayout inside a larger shared canvas.

Where Else This Pattern Applies

  • A dependency analysis tool can let users restyle or relayout one service cluster without disturbing other disconnected systems on the same map.
  • An investigation workspace can let analysts click a seed entity, isolate its connected neighborhood, and switch that neighborhood’s visual preset for review or export.
  • A process or org-chart tool can keep several graph islands on one canvas while allowing per-island presentation tuning instead of forcing one global layout mode.
  • A graph design system can package layout family, node geometry, connector style, and item classes into reusable component-scoped presets rather than treating those settings as unrelated toggles.