Organization Tree Folder Layout
This example renders a left-to-right organizational hierarchy with relation-graph's folder layout and custom company-card nodes. It preprocesses nested tree data into RGJsonData, then lets users tune connector offset and folder spacing at runtime while keeping canvas settings and image export in a shared floating utility window.
Organization Tree in Folder Layout
What This Example Builds
This example builds a left-to-right organizational hierarchy viewer with relation-graph’s folder layout. Instead of plain text rows, each node is rendered as a company card with a colored side bar, tag chips, and a truncated location line.
Users can tune the presentation while the graph is running. The floating helper window exposes one slider for connector start offset, two sliders for horizontal and vertical folder spacing, and a settings panel for canvas wheel mode, drag mode, and image export. The most important takeaway is that the example turns a folder-style hierarchy layout into a business-facing org tree without changing the underlying relation-graph layout engine.
How the Data Is Organized
The source data starts as a nested literal in data.ts. Each item has an id, text, data payload, and optional children, where the data object carries the card metadata that the node slot later renders.
Before the graph loads, the example explicitly flattens that tree into RGJsonData with rootId, nodes, and lines. That preprocessing happens in flattenTreeData(...), so the demo does not rely on setJsonData(...) to discover and flatten children automatically. During initialization, the code then normalizes each line with explicit junction settings and an initial fromJunctionPointOffsetX value before calling setJsonData(...).
In a real product, the same structure could represent departments, subsidiaries, franchise branches, regional teams, or any other hierarchy where each node needs both a label and compact business metadata.
How relation-graph Is Used
The example is mounted inside RGProvider, then renders a single RelationGraph instance. Its core graph options keep the layout fixed to folder with from: 'left', while treeNodeGapH and treeNodeGapV are bound to React state so spacing can be changed at runtime.
The graph styling is intentionally opinionated. Nodes use RGNodeShape.rect with defaultNodeBorderWidth: 0, while lines use RGLineShape.StandardOrthogonal, a 4 pixel polyline radius, and a blue default color. defaultExpandHolderPosition: 'right' and reLayoutWhenExpandedOrCollapsed: true keep branch toggles aligned with the folder-layout reading direction.
The example relies on RGHooks.useGraphInstance() rather than a ref. That graph instance drives the full lifecycle: setJsonData(...) on mount, updateOptions(...) plus doLayout() when spacing changes, getLines() plus updateLine(...) when the connector offset slider changes, and moveToCenter() plus zoomToFit() after layout work.
Node rendering is replaced with RGSlotOnNode, which turns every graph node into a custom card. SCSS in my-relation-graph.scss then finishes the presentation by styling the card shell, the expand holder, and the checked-line state. The floating DraggableWindow is a shared helper component, but in this example it still matters because it contributes the movable control surface, the canvas settings panel, and the screenshot export flow.
Key Interactions
The primary interaction is runtime presentation tuning. Moving the connector offset slider updates fromJunctionPointOffsetX across all loaded lines without rebuilding the dataset.
The horizontal and vertical distance sliders update the folder layout options, trigger doLayout(), and then recenter and refit the graph so spacing changes stay readable.
The floating helper window can be dragged, minimized, and switched into a settings overlay. Inside that overlay, users can change wheel behavior, change canvas drag behavior, and export the current graph view as an image.
Branch expansion and collapse are also part of the experience because the graph is configured to relayout when branch visibility changes. The node-click and line-click handlers only log objects to the console, so they are not central to the example’s behavior.
Key Code Fragments
This fragment shows that the example starts from nested hierarchy data and explicitly flattens it into RGJsonData.
const myJsonData: RGJsonData = {
rootId: 'a',
nodes: [],
lines: []
};
flattenTreeData(rootNodeJson, null, myJsonData.nodes, myJsonData.lines);
return myJsonData;
This recursive helper proves that parent-child relationships are converted into flat node and line arrays before the graph is loaded.
treeNodes.forEach((nodeData) => {
const node: JsonNode = {
id: nodeData.id,
text: nodeData.text,
data: nodeData.data
};
nodes.push(node);
if (parent) {
const line: JsonLine = {
from: parent.id,
to: nodeData.id
};
lines.push(line);
}
This option block establishes the folder-layout direction and the visual defaults that make the hierarchy read like an organization tree instead of a plain file tree.
const graphOptions: RGOptions = {
debug: false,
layout: {
layoutName: 'folder',
from: 'left',
treeNodeGapH: rangeHorizontal,
treeNodeGapV: rangeVertical
},
defaultExpandHolderPosition: 'right',
defaultNodeShape: RGNodeShape.rect,
defaultLineShape: RGLineShape.StandardOrthogonal,
This initialization step normalizes each loaded line for bottom-to-left routing before the dataset is handed to relation-graph.
myJsonData.lines.forEach((line, index) => {
if (!line.id) {
line.id = `l${index + 1}`;
}
line.fromJunctionPoint = RGJunctionPoint.bottom;
line.fromJunctionPointOffsetX = fromOffsetX;
line.toJunctionPoint = RGJunctionPoint.left;
});
await graphInstance.setJsonData(myJsonData);
This pair of update functions demonstrates the two runtime tuning paths: relayout for spacing changes and in-place line mutation for connector offset changes.
graphInstance.updateOptions({
layout: {
layoutName: 'folder',
from: 'left',
treeNodeGapH: rangeHorizontal,
treeNodeGapV: rangeVertical
}
});
await graphInstance.doLayout();
graphInstance.moveToCenter();
graphInstance.zoomToFit();
graphInstance.getLines().forEach(line => {
graphInstance.updateLine(line, {
fromJunctionPointOffsetX: fromOffsetX
});
});
This slot fragment shows how relation-graph nodes are replaced with card-style business content.
<RGSlotOnNode>
{({ node }: RGNodeSlotProps) => (
<div className="my-card-node">
<div className="card-left" />
<div className="card-right">
<div className="card-name">{node.text}</div>
<div className="card-tags">
{node.data?.tags.map((tag: string) => (
<div key={tag} className="card-tag">
This shared helper fragment is what gives the example its canvas settings and image export controls.
<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 }); }}
/>
What Makes This Example Distinct
According to the comparison data, this example is closest to layout-folder, but it pushes that pattern further in two ways: it swaps generic folder-style nodes for company cards, and it adds live horizontal and vertical spacing controls on top of the shared connector-offset pattern. That makes it a stronger starting point when the goal is a business hierarchy viewer rather than a generic folder tree.
Compared with ever-changing-tree and io-tree-layout, the runtime tuning here is narrower and more scene-specific. The controls do not turn the graph into a broad layout laboratory. Instead, they preserve a fixed left-to-right folder composition and focus on the handful of adjustments that matter for this organizational presentation.
Compared with investment, this example is simpler and more static. It does not teach lazy-loaded branches, ownership semantics, or post-layout cleanup rules. Its distinctive combination is explicit hierarchy flattening, custom company cards, blue orthogonal folder connectors, and live spacing plus connector-offset tuning inside one viewer. The floating utility window helps, but the comparison records make clear that the shared window itself is not the unique part.
Where Else This Pattern Applies
This pattern transfers well to internal organization charts, regional office trees, subsidiary maps, partner hierarchies, and franchise structures where each node needs compact metadata instead of a single label.
It also fits category trees, approval chains, and service ownership maps when teams need to tune spacing for readability before taking screenshots or embedding the graph in documentation.
More generally, it is a good reference whenever source data arrives as a nested business tree but the final viewer needs explicit preprocessing, card-based node rendering, and a small runtime control surface rather than a full editor.