Orthogonal Line Label Styling
A compact tree viewer that uses simple orthogonal lines with detached boxed labels on a gradient canvas. Users can rotate the tree, tune global line-label offsets, and reuse the same pattern for readable value badges or annotations on connectors.
Detached Background Labels on Orthogonal Tree Lines
What This Example Builds
This example builds a small tree viewer whose links use orthogonal routing and detached text badges instead of text drawn directly on the path. The canvas is deliberately stylized with a warm gradient background, translucent nodes, and bordered line labels, so the main visual lesson is readability as much as geometry.
Users can rotate the tree to the left, right, top, or bottom, then fine-tune the x and y offset of every built-in line label from a floating control panel. The same shared window also exposes generic canvas settings and screenshot export, but the core example is the combination of orthogonal routing, boxed labels, and direction-aware layout presets.
How the Data Is Organized
The graph data is declared inline as one RGJsonData object with rootId: 'a', a flat nodes array, and a flat lines array. Each line carries a text value such as ¥30.23 K, so the labels come from normal relation data rather than from a custom slot renderer.
There is no fetch layer and no preprocessing step before the initial setJsonData() call. The only runtime transformation happens after the graph is loaded: when the layout direction changes, the component computes a new pair of default label offsets and then reapplies graph options. In a real project, the same structure could represent approval amounts, transfer values, dependency annotations, or any other short label that needs to stay readable on connectors.
How relation-graph Is Used
The demo is wrapped in RGProvider, then MyGraph retrieves the active instance through RGHooks.useGraphInstance(). The RelationGraph component receives one options object that fixes node size, line width, and RGLineShape.SimpleOrthogonal, while also setting defaultLineTextOnPath to false so labels render as detached elements that CSS can style.
At runtime, the example uses graphInstance.updateOptions() to push new label offsets, a direction-aware defaultJunctionPoint, and new tree gaps when the orientation changes. After that it calls doLayout(), moveToCenter(), and zoomToFit() to rebuild the tree and reframe the viewport. This is a viewer example, not an editor: it does not create nodes, mutate individual lines, or provide custom node or line slots.
Most of the visual customization comes from stylesheet overrides instead of custom render components. The SCSS targets built-in relation-graph classes such as .rg-map, .rg-node, and .rg-line-label to produce the gradient canvas, glass-like nodes, and badge-style labels. The shared DraggableWindow helper uses graph hooks again for wheel-mode, drag-mode, and image-export utilities, but that overlay is secondary scaffolding rather than the unique technique being demonstrated here.
Key Interactions
The layout direction selector is the most important interaction. Switching among left, right, top, and bottom changes the tree orientation, swaps the orthogonal junction mode between horizontal and vertical, adjusts the tree gaps, and recenters the view after relayout.
Two sliders let the user tune the detached label position globally through defaultLineTextOffsetX and defaultLineTextOffsetY. That makes the demo useful when the same label style must survive multiple layout directions without editing each line record one by one.
The control panel also includes a polyline-radius slider. In this code, that value is stored in component state and included in the options payload, but the explicit reapply flow is tied to direction and offset updates rather than to a dedicated radius effect.
The shared settings panel adds secondary interactions for wheel behavior, drag behavior, and downloading an image of the graph. Those controls are present in the example, but they come from reusable demo infrastructure rather than from line-label-specific logic.
Key Code Fragments
This fragment shows that the example stays inside built-in relation-graph options instead of replacing line rendering with a custom slot.
const graphOptions: RGOptions = {
defaultLineWidth: 2,
defaultLineShape: RGLineShape.SimpleOrthogonal,
defaultLineTextOffsetX: defaultLineTextOffsetX,
defaultLineTextOffsetY: defaultLineTextOffsetY,
defaultPolyLineRadius: defaultPolyLineRadius,
defaultLineTextOnPath: false,
layout: {
layoutName: 'tree',
from: 'left',
treeNodeGapH: 50,
treeNodeGapV: 50
}
};
This fragment shows that the data is a normal RGJsonData tree with label text embedded directly in the line records.
const myJsonData: RGJsonData = {
rootId: 'a',
nodes: [
{ id: 'a', text: 'a' },
{ id: 'b', text: 'b' },
// ...
],
lines: [
{ from: 'a', to: 'b', text: '¥30.23 K' },
// ...
]
};
await graphInstance?.setJsonData(myJsonData);
This fragment shows the relayout sequence that makes direction changes update both geometry and viewport framing.
const updateOptionAndReLayout = async () => {
await updateOptions();
await graphInstance.doLayout();
graphInstance.moveToCenter();
graphInstance.zoomToFit();
};
This fragment shows that the component assigns different label-offset presets for different tree directions before reapplying options.
useEffect(() => {
let lineTextOffsetX = 0;
let lineTextOffsetY = 0;
if (layoutFrom === 'left') {
lineTextOffsetX = -50;
lineTextOffsetY = 0;
} else if (layoutFrom === 'right') {
lineTextOffsetX = 10;
lineTextOffsetY = 0;
} else if (layoutFrom === 'top') {
lineTextOffsetY = -10;
} else if (layoutFrom === 'bottom') {
lineTextOffsetY = 40;
}
This fragment proves that the boxed labels come from CSS on the built-in .rg-line-label element, not from custom SVG text.
.rg-line-peel {
.rg-line-label {
border: #fff 1px solid;
background-color: #e94057;
color: #fff;
}
}
.rg-line-peel.rg-line-checked {
.rg-line-label {
background-color: rgba(255, 255, 255, 1);
color: #e94057;
}
}
What Makes This Example Distinct
Compared with nearby examples such as text-on-orthogonal and bothway-tree2, this one is more focused on global label styling than on per-line mutations or branch-specific logic. Its main reusable idea is that detached labels can remain readable across multiple tree directions by combining defaultLineTextOnPath = false, CSS on .rg-line-label, and direction-aware offset presets.
The example is also more stylized than a neutral geometry demo. The gradient map background, translucent nodes, and checked-state color inversion turn the built-in line labels into visible badges, which makes this a stronger reference when the goal is a finished presentation style rather than a pure layout experiment.
Relative to broader line showcases such as line, the scope is intentionally narrow. It does not try to compare many arrows, markers, or per-line shapes; it stays on one orthogonal tree pattern and shows how to tune it through graph-level options and relayout calls. Relative to canvas-bg2, the distinct value is that the theme work is tied directly to runtime label-position tuning instead of being only a canvas skin.
Where Else This Pattern Applies
This pattern transfers well to financial or operational trees where every connector needs a short value badge, such as budget breakdowns, commission flows, approval amounts, or dependency costs. It is also useful in org charts or process trees where edge labels must stay visible after users switch layout direction for presentation needs.
The same approach can extend to status chips, SLA labels, or risk scores on connectors without introducing custom line slots. When the label renderer can stay built-in, teams can keep their data model simple and move most of the presentation work into graph options plus CSS.