Corporate Group Relationship Tree
This example renders a Tencent-centered corporate relationship tree from a static local RGJsonData payload and fits it into a full-height relation-graph canvas. It customizes node rendering with a root banner and vertical company badges, lazy-loads one placeholder branch through append APIs, and reuses the shared floating helper window for canvas settings and image export.
Corporate Relationship Tree with Lazy Branch Expansion
What This Example Builds
This example builds a read-focused corporate relationship tree around one focal company, rendered on a full-height white canvas. The root company appears as a wide banner, downstream companies appear as narrow vertical badges, and most lines carry percentage investment labels from a large local dataset. Users can inspect the hierarchy, expand one prepared placeholder branch to append extra children, open a floating utility window, switch canvas interaction modes, and export the current graph as an image. The most useful implementation detail is that the tree grows incrementally through relation-graph append APIs instead of reloading the whole dataset.
How the Data Is Organized
The data comes from my-data.ts as a static inline RGJsonData object returned by getMyJsonData(). It has a rootId, a nodes array, and a lines array. Most relationships flow from the Tencent root to subsidiaries with 出资 percentage labels, while a few upstream people nodes connect into the root with empty labels. That makes the scene read more like a corporate ownership or affiliation tree than a classic employee org chart.
Before setJsonData(...), initializeGraph() preprocesses the dataset in place. It scans the node list, keeps the Tencent root auto-sized by forcing width and height to 0, and rewrites one orange node into a lazy placeholder by adding data.asyncChild, loaded: false, expandHolderPosition: 'bottom', and expanded: false. In a production system, the same structure could represent ownership hierarchies, affiliate-company trees, controller-to-subsidiary views, or due-diligence relationship overviews.
How relation-graph Is Used
The entry file wraps the demo in RGProvider, so both the main graph component and the shared helper window can resolve the active graph through relation-graph hooks. MyGraph.tsx uses RGHooks.useGraphInstance() to load the prepared dataset, center the viewport, fit the tree to the screen, show loading state during lazy expansion, append new nodes and lines, and rerun layout after the new branch is inserted.
The graph itself uses a built-in top-down tree layout with treeNodeGapV: 100 and treeNodeGapH: 10. Its options pair RGLineShape.SimpleOrthogonal with RGJunctionPoint.tb, so connectors behave like structured elbow links in a downward hierarchy. Node geometry stays slot-measured because defaultNodeWidth and defaultNodeHeight are both 0, built-in borders are removed, and bottom-positioned expand controls stay compatible with automatic relayout.
RGSlotOnNode is the main rendering technique. It gives the Tencent root a dedicated horizontal banner with a large building icon, while every other node renders as a compact vertical badge that reuses var(--rg-node-color) for both fill and border. Local SCSS keeps the canvas background white and adds a checked-state halo around selected nodes. The floating DraggableWindow and its CanvasSettingsPanel use RGHooks.useGraphStore() plus setOptions(...) to switch wheel and drag behavior on the live graph, and the same shared panel drives image export through prepareForImageGeneration(), modern-screenshot, and restoreAfterImageGeneration().
Key Interactions
Expanding the single prepared placeholder node triggers a one-time delayed branch load. After a simulated async wait, the example appends three children and three labeled lines, then re-runs layout.
The floating helper window can be dragged, minimized, and switched into a settings overlay from the gear button.
Inside the settings overlay, wheel handling can switch between scroll, zoom, and none, while canvas dragging can switch between selection, move, and none.
The Download Image action prepares the graph DOM, captures the current canvas as a blob, downloads it, and restores the graph state afterward.
Key Code Fragments
This fragment shows the core graph configuration: a top-down tree, orthogonal connectors, bottom expand controls, and auto-sized slot-measured nodes.
const graphOptions: RGOptions = {
defaultNodeShape: RGNodeShape.rect,
defaultLineShape: RGLineShape.SimpleOrthogonal,
defaultLineColor: '#666666',
defaultJunctionPoint: RGJunctionPoint.tb,
defaultNodeBorderWidth: 0,
defaultExpandHolderPosition: 'bottom',
reLayoutWhenExpandedOrCollapsed: true,
defaultNodeWidth: 0,
defaultNodeHeight: 0,
defaultNodeColor: '#3B82F6',
layout: {
This fragment proves that the example rewrites two specific nodes before loading the dataset into relation-graph.
const myJsonData: RGJsonData = await getMyJsonData();
myJsonData.nodes.forEach(thisNode => {
if (thisNode.text === '深圳市腾讯计算机系统有限公司') {
thisNode.width = 0;
thisNode.height = 0;
}
if (thisNode.text === '这个节点原本是没有子节点的') {
thisNode.data = { asyncChild: true, loaded: false };
thisNode.expandHolderPosition = 'bottom';
thisNode.expanded = false;
}
});
This fragment shows that branch growth happens through append APIs after expansion instead of rebuilding the full graph JSON.
if (nodeObject.data && nodeObject.data.asyncChild === true && nodeObject.data.loaded === false) {
nodeObject.data.loaded = true;
graphInstance.loading('Loading data from remote server...')
setTimeout(async () => {
const newJsonData: RGJsonData = {
nodes: [
{ id: nodeObject.id + '-child-1', text: nodeObject.id + '-Child Node 1' },
{ id: nodeObject.id + '-child-2', text: nodeObject.id + '-Child Node 2' },
{ id: nodeObject.id + '-child-3', text: nodeObject.id + '-Child Node 3' }
],
This fragment shows how RGSlotOnNode separates the root banner from the vertical child badges.
<RGSlotOnNode>
{({ node }: RGNodeSlotProps) => {
return (
node.text === '深圳市腾讯计算机系统有限公司' ?
<div>
<div className="rounded bg-blue-500 text-white flex items-center justify-center gap-2 px-4 py-4">
<Building2Icon size={40} />
<div className="w-fit text-2xl">{node.text}</div>
</div>
</div>
:
This fragment comes from the shared helper panel and shows the live interaction-mode controls plus the export action.
<SettingRow
label="Wheel Event:"
options={[
{ label: 'Scroll', value: 'scroll' },
{ label: 'Zoom', value: 'zoom' },
{ label: 'None', value: 'none' },
]}
value={wheelMode}
onChange={(newValue: string) => { graphInstance.setOptions({ wheelEventAction: newValue }); }}
/>
const canvasDom = await graphInstance.prepareForImageGeneration();
let graphBackgroundColor = graphInstance.getOptions().backgroundColor;
if (!graphBackgroundColor || graphBackgroundColor === 'transparent') {
graphBackgroundColor = '#ffffff';
}
const imageBlob = await domToImageByModernScreenshot(canvasDom, {
backgroundColor: graphBackgroundColor
});
if (imageBlob) {
downloadBlob(imageBlob, 'my-image-name');
}
What Makes This Example Distinct
Compared with nearby business examples such as scene-group and investment, this example stays deliberately lighter. It keeps one focal company at the top of a downward tree, avoids dense cross-links and post-layout cleanup logic, and uses a simpler white-canvas presentation with one fixed node-skin pattern instead of multiple runtime visual modes.
Compared with progressive-loading neighbors such as show-more-nodes-by-page, investment-penetration, and show-more-nodes-front, the lazy behavior here is intentionally narrow. It uses one placeholder node that loads once, rather than typed branch navigation, pagination, recenter-on-expand flows, or hidden-node reveal strategies.
The comparison data marks the combination itself as unusual: a prepared ownership-style dataset with many percentage labels, top-down orthogonal layout, root-versus-child slot styling, one-time async branch insertion through addNodes(...) and addLines(...), and a shared floating settings and export window on the same page. That makes this example a strong starting point for read-only corporate hierarchy viewers that need modest progressive disclosure without turning into a full exploration tool.
Where Else This Pattern Applies
This pattern transfers well to shareholder trees, affiliate-company maps, subsidiary ownership views, and compliance dashboards that need one main corporate hierarchy with a small amount of deferred expansion.
It also fits presentation-oriented graph viewers where the root entity needs a stronger visual identity than downstream nodes, but the implementation should still rely on one built-in relation-graph tree layout.
The append-and-relayout approach is useful anywhere a product needs to reveal extra graph detail on demand without reconstructing the whole dataset, especially when the page also needs lightweight viewer utilities such as mode switching and screenshot export.