JavaScript is required

Circle Layout Isolated Nodes with Green Straight Links

This example shows how to keep isolated-looking nodes visible in a relation-graph `circle` layout by deriving a root from the first node and injecting hidden helper edges before load. It also makes visible link styling part of the lesson by using straight green default lines, then centers and fits the graph automatically on mount.

Keeping Isolated Nodes Visible in a Styled Circle Layout

What This Example Builds

This example builds a full-height relation-graph viewer that places a sparse dataset into one circular arrangement. The visible result is a ring of 60 by 60 circular nodes, a small number of straight green links, and many nodes that look disconnected even though they still participate in the loaded graph. The main point is the combination of two small techniques: inject hidden structural lines before loading, then use graph options to make the visible links look deliberate without adding custom rendering.

How the Data Is Organized

The graph data is declared inline inside initializeGraph() as one RGJsonData object with 19 nodes and 5 visible lines. Before setJsonData() runs, the code derives rootId from the first node (a), writes that value back to myJsonData.rootId, and appends one invisible root-to-node helper line for every other node by generating sequential l6, l7, and later ids with opacity: 0.

That means the loaded graph is structurally more connected than the visible graph. In a real application, the same pattern can represent people, accounts, devices, products, or knowledge-graph entities that still need placement in one layout even when explicit visible relationships are missing or incomplete.

How relation-graph Is Used

index.tsx wraps the example in RGProvider, and MyGraph.tsx retrieves the mounted graph instance through RGHooks.useGraphInstance(). The graph options define almost all of the visible behavior: layoutName: 'circle', RGNodeShape.circle, RGLineShape.StandardStraight, border junction points, 60 by 60 default node size, 2-pixel lines, and a green default line color.

The runtime flow is minimal and explicit. A mount-only useEffect() calls initializeGraph(), the inline dataset is augmented in memory, and then the graph instance runs setJsonData(), moveToCenter(), and zoomToFit(). The example does not use slots, editor features, viewport tools, or extra graph subcomponents beyond RGProvider and RelationGraph. It imports a local SCSS file and wraps the canvas in .my-graph, but the reviewed stylesheet is mostly empty selector scaffolding, so the visual result comes primarily from the graph options and the height: '100vh' wrapper.

Key Interactions

  • The graph loads automatically on mount, then centers and fits itself to the viewport.
  • Clicking a node triggers onNodeClick, but the handler only logs the clicked node object.
  • Clicking a line triggers onLineClick, but the handler only logs the clicked line object.
  • There is no editing flow, no expand or collapse behavior, no selection workflow, and no toolbar interaction in this example.

Key Code Fragments

This options block shows that the example teaches a circle layout plus explicit visible line styling through RGOptions.

const graphOptions: RGOptions = {
    debug: false,
    defaultJunctionPoint: RGJunctionPoint.border,
    defaultNodeShape: RGNodeShape.circle,
    defaultLineShape: RGLineShape.StandardStraight,
    defaultNodeWidth: 60,
    defaultNodeHeight: 60,
    defaultLineWidth: 2,
    defaultLineColor: 'green',
    layout: {
        layoutName: 'circle'
    }
};

This inline dataset is small and static before preprocessing, which makes the later augmentation step easy to see.

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

This preprocessing pass is the core workaround: it derives a root and appends invisible helper edges before the graph is loaded.

const rootId = myJsonData.nodes[0].id;
myJsonData.rootId = rootId;

let lineCounter = myJsonData.lines.length + 1;
myJsonData.nodes.forEach(n => {
    if (n.id !== rootId) {
        myJsonData.lines.push({
            id: `l${lineCounter++}`,
            from: rootId,
            to: n.id,
            opacity: 0
        });
    }
});

This bootstrap sequence proves that the augmented data is loaded and framed automatically, and that user interaction stays inspection-only.

useEffect(() => {
    initializeGraph();
}, []);

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

const onNodeClick = (nodeObject: RGNode, $event: RGUserEvent) => {
    console.log('onNodeClick:', nodeObject);
};

What Makes This Example Distinct

The prepared comparison data positions this example as the style-explicit sibling of the isolated-node workaround demos. Like show-single-nodes and show-single-nodes2, it derives a root from the first node and injects hidden helper edges so isolated-looking nodes still participate in layout. What makes this version stand out is that it also turns visible line styling into part of the lesson by using global options for straight 2-pixel green links in a circle layout.

Compared with show-single-nodes, this version is less of a plain baseline and more of a compact reference for option-level line styling on top of the same workaround. Compared with show-single-nodes2, it keeps orbit-like circle placement instead of switching to a force layout with repulsion tuning. Compared with advanced-line-usage, its line configuration supports a disconnected-data layout workaround rather than serving as a broader visible line-behavior reference board.

Where Else This Pattern Applies

This pattern transfers well to graph views that must keep incomplete or currently unlinked records visible without inventing visible business relationships: unassigned employees in org data, devices with partial topology imports, customer or product records waiting for link resolution, or knowledge-graph entities that still need stable placement during review.

It is also a practical starting point when a team wants a small example that combines structural preprocessing with readable default edge styling. The hidden helper lines can remain an internal loading tactic while labels, panels, filters, or richer interactions are added later.