JavaScript is required

Business Unit Organizational Chart

This example documents a presentation-oriented organizational chart built from one company hierarchy that is split into business-unit subtrees and recomposed under a shared executive root. It uses custom role cards, per-group folder layouts, expand-collapse relayout, a minimap, and a floating settings and export panel to turn a static hierarchy into a structured viewer.

Building a Grouped Organizational Chart with Custom Role Cards

What This Example Builds

This example builds a read-only organizational chart for one company root and several first-level business units. The canvas shows a top executive card above four horizontally arranged business-unit subtrees, with orthogonal connectors, depth-based color accents, a minimap, and a floating utility window around the graph.

Users can expand or collapse departments, navigate with the built-in toolbar and minimap, drag or minimize the helper window, change wheel and drag interaction modes from the settings panel, and export the chart as an image. The main point is not a plain tree rendering. It is the grouped composition: one source hierarchy is split into business-unit trees, laid out separately, and recomposed under a shared executive root whenever visibility changes.

How the Data Is Organized

The source data is a local nested OrgNode tree with id, text, name, title, type, and optional children. The root represents the company leader, while each first-level child represents one business unit such as Cloud & Infrastructure, AI & Data Science, Smart Device BU, or Global Business.

This example does not send that tree directly to setJsonData(...). Instead, analysisGroups(...) takes the root’s first-level children and turns each business unit into a MyTreeGroup. Then flatNodeData(...) recursively converts each subtree into flat nodes and lines arrays, preserving deep, hasChildren, name, title, and type in node.data. During the same preprocessing pass, buildMyTreeGroup(...) adds data.myGroupId and moves expand buttons to the right side for nodes that have children.

In a real application, the same structure could represent corporate divisions, regional sales hierarchies, program portfolios, or any taxonomy where first-level branches need their own local layout while still rolling up to one shared top node.

How relation-graph Is Used

index.tsx wraps the page in RGProvider, and MyGraph.tsx renders RelationGraph with layoutName: 'fixed'. That fixed outer layout is deliberate. The example does not ask relation-graph to place the whole org chart automatically. Instead, it uses RGHooks.useGraphInstance() to load the root and all grouped nodes imperatively, wait for DOM sizing, run a custom layout controller, add display-only root connectors, and finally center and fit the viewport.

The grouped layout logic lives in MyMixTreeLayout. loadData(...) inserts the company root with addNodes(...), inserts every flattened group with addNodes(...) and addLines(...), colors nodes by hierarchy depth, and rewrites group-internal connector anchors with getLinesBetweenNodes(...) plus updateLine(...). layoutGroup(...) creates a non-main folder layout for each group, fixes that group’s root in place, and positions later groups from the previous group’s measured bounds via getNodesRectBox(...). After all groups are placed, applyAllGroupLayout(...) shifts the business-unit roots downward and recenters the company root above the combined row.

The top executive connectors are also manual. connectRootToChildrenTreeRoot() appends thick orthogonal lines marked forDisplayOnly, so the visible root-to-group links are presentation connectors rather than ordinary hierarchy edges loaded from the original tree. Runtime updates stay focused on the viewer experience: onNodeExpand and onNodeCollapse rerun the grouped layout, while node and line clicks only log the clicked objects.

relation-graph slots handle the visible presentation. RGSlotOnNode maps each node role to LeaderCard, BULeaderCard, DirectorCard, or ManagerCard, which turns the chart into a role-card interface instead of a default node shell. RGSlotOnView mounts RGMiniView as a minimap overlay. The shared DraggableWindow adds a floating utility shell with a settings drawer that updates wheelEventAction and dragEventAction through setOptions(...), plus image export through prepareForImageGeneration() and restoreAfterImageGeneration(). The SCSS file finishes the look by keeping the internal node body transparent, styling the expand button with the node color, and highlighting checked nodes and lines.

Key Interactions

  • Expanding or collapsing any branch triggers applyAllGroupLayout(), so visibility changes in one business unit can reflow sibling groups and recenter the top executive node.
  • The built-in toolbar and RGMiniView make the wide chart easier to inspect without adding custom viewport controls.
  • The floating helper window can be dragged and minimized, and its settings button opens an overlay inside the same window.
  • The settings overlay switches wheel behavior between scroll, zoom, and none, switches canvas drag behavior between selection, move, and none, and exposes screenshot export.

Key Code Fragments

This graph configuration shows that the outer canvas stays fixed while the chart uses transparent node shells and orthogonal connectors.

const graphOptions: RGOptions = {
    debug: false,
    layout: {
        layoutName: 'fixed'
    },
    defaultPolyLineRadius: 10,
    defaultNodeShape: RGNodeShape.rect,
    defaultNodeBorderWidth: 0,
    defaultNodeColor: 'transparent',
    defaultLineShape: RGLineShape.StandardOrthogonal,
    defaultLineWidth: 3,
    showToolBar: true,
    defaultLineColor: '#666666'
};

This initialization sequence proves that the example loads data first, then runs the custom composer, then adds root-to-group display lines before fitting the viewport.

const initializeGraph = async () => {
    const myTreeJsonData = await getMyTreeJsonData();
    myMixTreeLayout.current.setLevelColors(['#7e22ce30', '#2564eb3a', '#71c9053d', '#ea5a0c34']);
    await myMixTreeLayout.current.loadData(myTreeJsonData);
    await graphInstance.sleep(300);
    myMixTreeLayout.current.applyAllGroupLayout();
    myMixTreeLayout.current.connectRootToChildrenTreeRoot();
    graphInstance.moveToCenter();
    graphInstance.zoomToFit();
};

This preprocessing step shows how each business-unit subtree is flattened and tagged so later layout passes can query nodes by group.

const myGroupId = 'G-' + treeData.id;
const rootNodeId = treeData.id;
const { nodes, lines } = flatNodeData([treeData], null);
nodes.forEach(node => {
    if (!node.data) node.data = {};
    node.data.myGroupId = myGroupId;
    if (node.data.hasChildren) {
        node.expandHolderPosition = 'right';
    }
});

This fragment shows the sequential placement rule: each business-unit group starts from the previous group’s bounding box instead of sharing one automatic tree layout.

if (group.refs.length > 0) {
    const refedMyGroupId = group.refs[0];
    const refedGroup = this.getGroupById(refedMyGroupId);
    if (!group.layouted) {
        this.layoutGroup(refedGroup);
    }
    const refedGroupView = this.getGroupViewInfo(refedMyGroupId);
    groupRootNodeXy.x = refedGroupView.maxX + 100;
}

This local layout call is the core of the grouped composition, because each business unit gets its own folder pass with a fixed root.

const layoutOptions: RGLayoutOptions = {
    layoutName: 'folder',
    from: 'left',
    layoutExpansionDirection: 'end',
    alignItemsX: 'start',
    treeNodeGapV: 20,
    treeNodeGapH: -80
};
const myGroupLayout = this.graphInstance.createLayout(layoutOptions as RGLayoutOptions);
myGroupLayout.isMainLayouer = false;
myGroupLayout.layoutOptions.fixedRootNode = true;
myGroupLayout.placeNodes(groupNodes, groupRootNode);

This connector definition shows that the top executive links are appended as display-only presentation lines after group layout has already been computed.

const line: JsonLine = {
    id: 'root-to-' + group.rootNodeId,
    from: this.rootId,
    to: group.rootNodeId,
    lineShape: RGLineShape.SimpleOrthogonal,
    fromJunctionPoint: RGJunctionPoint.bottom,
    toJunctionPoint: RGJunctionPoint.top,
    lineWidth: 4,
    forDisplayOnly: true,
    color: '#666666',
    showEndArrow: false
};

This slot mapping proves that the visible nodes are role-specific cards rather than the default node body.

<RGSlotOnNode>
    {({ node }) => {
        const {type, title, name} = node.data;
        switch (type) {
            case 'leader':
                return <LeaderCard name={name} title={title} company={node.text || ''} />;
            case 'bu-leader':
                return <BULeaderCard name={name} title={title} department={node.text || ''} />
            case 'director':
                return <DirectorCard name={name} title={title} department={node.text || ''} />
            case 'manager':
                return <ManagerCard name={name} title={title} team={node.text || ''} />
        }
    }}
</RGSlotOnNode>

What Makes This Example Distinct

Its strongest distinction is the combination of a business org-chart scene and a grouped custom-layout architecture. According to the comparison data, this example is not just another tree viewer. It turns one OrgNode hierarchy into a shared executive root plus several first-level business-unit groups, lays each group out with its own left-to-right folder pass inside a fixed outer graph, and then appends separate display-only root connectors after layout.

Compared with mix-layout-tree, it uses a very similar grouped-tree technique but goes further on presentation. The visible nodes become executive, BU leader, director, and manager cards inside a transparent graph shell, which makes this demo the stronger reference when the grouped-layout pattern needs to ship as a presentation-ready org chart rather than as generic text nodes.

Compared with mix-layout-2 and multiple-layout-mixing-in-single-canvas, the lesson here is different. Those examples emphasize HTML anchor slots, hidden roots, or handoffs between disconnected regions. This one keeps the entire experience inside graph nodes and lines, with one shared hierarchy that is flattened, grouped, and recomposed around a visible executive root whenever branches open or close.

Compared with layout-tree, the distinctive value is not orientation switching or a built-in whole-tree layout. This example is the better starting point when one organization must split into side-by-side business-unit groups, keep role-specific node cards, and still reflow the overall composition after ordinary expand-collapse changes.

Where Else This Pattern Applies

  • A corporate org browser where each division needs its own local spacing and card design but must still roll up to one executive view.
  • A portfolio or program map where top-level domains should render as separate subtree clusters under one steering node.
  • A regional sales or partner hierarchy where expanding one branch should trigger a clean recomposition of sibling groups.
  • A read-only governance, capability, or standards hierarchy that needs minimap navigation, runtime interaction-mode controls, and screenshot export around a wide tree.