Highlight Related Lines on Node Hover
This example renders a top-down tree and highlights only the hovered node together with its directly connected lines. It demonstrates `RGSlotOnNode` hover handlers, runtime neighbor lookup through `getLines()`, temporary node and line class updates, animated line styling in SCSS, and a shared floating utility panel for canvas settings and image export.
Highlight Related Lines on Node Hover
What This Example Builds
This example builds a full-height tree viewer that emphasizes a node’s immediate relationships when the pointer moves over that node. The screen shows a small top-down hierarchy of letter-labeled items, rounded purple node chips, and orthogonal connectors, with a floating helper window layered above the canvas.
The main visible effect is transient relation highlighting. Hovering a node adds a strong halo to that node and animates only the lines that connect directly to it, while the rest of the tree stays in its baseline style. Moving the pointer away clears the temporary state immediately.
The key point is that the example does not rebuild the graph or replace the built-in edge renderer. It uses slot-level hover handlers plus runtime graph-instance updates to restyle the already loaded nodes and lines.
How the Data Is Organized
The graph data is declared inline inside initializeGraph as one RGJsonData object with a rootId, a flat nodes array, and a flat lines array. Each node has an id, display text, and a small data.icon payload. Each line has its own id, from, to, label text, and an extra data.myLineId field.
There is no preprocessing step before setJsonData(). The data is assembled directly in the component, loaded once, and then all later behavior is driven from the runtime graph objects returned by getNodes() and getLines(). The neighbor lookup happens on hover by checking whether a line’s from or to matches the active node id.
In a production graph, this same shape can represent reporting relationships, dependency trees, workflow branches, category hierarchies, or any other directed parent-child structure where users need quick one-hop context instead of deep traversal.
How relation-graph Is Used
index.tsx wraps the example in RGProvider, which lets both the main graph component and the shared helper window read the same graph context through hooks. Inside MyGraph, RGHooks.useGraphInstance() is used to load data with setJsonData(), fit the viewport with zoomToFit(), inspect current graph objects with getNodes() and getLines(), and apply temporary visual changes through updateNode() and updateLine().
The graph options fix the example into a downward tree. The configuration uses layoutName: 'tree', from: 'top', and 150-pixel gaps in both directions so the highlighted connectors have enough space to read clearly. It also switches the built-in line renderer to rounded orthogonal segments with RGLineShape.SimpleOrthogonal, anchors those lines on top and bottom junction points, and places the built-in toolbar at the bottom-right.
The node chrome is intentionally minimized. Default node fill and border are made transparent, and RGSlotOnNode supplies the visible node body instead. That slot renders a rounded purple wrapper around node.text and attaches onMouseEnter and onMouseLeave handlers, so standard DOM pointer events become the trigger for graph-wide relation highlighting.
Styling is split between runtime state and SCSS overrides. The runtime code writes className values such as my-node-highlight and my-line-highlight onto relation-graph’s internal node and line wrappers, while my-relation-graph.scss turns those classes into a node halo, animated line strokes, and a dark label background. The shared DraggableWindow component uses RGHooks.useGraphStore() and useGraphInstance() to expose canvas drag mode, wheel mode, and image export, but those utilities are supporting scaffolding rather than the main lesson.
Key Interactions
The primary interaction is node hover. Entering a node clears any previous highlight, marks the hovered node, finds every incident line in the current graph instance, and increases those lines to a thicker animated state.
The reset path is node leave. As soon as the pointer leaves the slotted node body, the example removes highlight classes from all nodes and all lines and restores the default line width, so only one node’s neighborhood can be active at a time.
The floating helper window adds secondary controls. It can be dragged, minimized, switched into a settings panel, used to change wheel and canvas-drag behavior at runtime, and used to export an image after relation-graph prepares the canvas for capture.
Key Code Fragments
This fragment shows the combination of transparent node chrome, orthogonal lines, and a fixed toolbar placement that defines the baseline graph behavior.
const graphOptions: RGOptions = {
defaultNodeColor: 'transparent',
defaultNodeBorderWidth: 0,
defaultNodeBorderColor: 'transparent',
defaultLineColor: 'rgba(128, 128, 255)',
defaultNodeShape: RGNodeShape.rect,
toolBarDirection: 'h',
toolBarPositionH: 'right',
toolBarPositionV: 'bottom',
defaultPolyLineRadius: 10,
defaultLineShape: RGLineShape.SimpleOrthogonal,
defaultLineWidth: 1,
This fragment fixes the graph into a wide top-down tree instead of switching layouts at runtime.
defaultJunctionPoint: RGJunctionPoint.tb,
layout: {
layoutName: 'tree',
from: 'top',
treeNodeGapH: 150,
treeNodeGapV: 150,
}
This fragment shows that the data is loaded as one inline tree with explicit line ids and custom per-line metadata.
const myJsonData: RGJsonData = {
rootId: 'a',
nodes: [
{ id: 'a', text: 'a', data: { icon: 'align_bottom' } },
{ id: 'b', text: 'b', data: { icon: 'basketball' } },
// ...
],
lines: [
{ id: 'l1', data: { 'myLineId': 'line1' }, from: 'a', to: 'b', text: 'Relation Description' },
// ...
],
};
This fragment proves that hover highlighting is based on runtime line inspection rather than precomputed adjacency data.
const nodeMouseOver = (currentNode: RGNode) => {
clearHighlightStatus();
graphInstance.updateNode(currentNode, { className: 'my-node-highlight' });
graphInstance.getLines().forEach(line => {
if (line.from === currentNode.id || line.to === currentNode.id) {
graphInstance.updateLine(line, {
className: 'my-line-highlight',
lineWidth: 3
});
}
});
};
This fragment shows that reset is a full graph-wide pass over nodes and lines, which guarantees the next hover starts from a clean baseline.
const clearHighlightStatus = () => {
graphInstance.getNodes().forEach(node => {
graphInstance.updateNode(node, { className: '' });
});
graphInstance.getLines().forEach(line => {
graphInstance.updateLine(line, {
className: '',
lineWidth: graphOptions.defaultLineWidth
});
});
};
This fragment shows how RGSlotOnNode turns the node body into the DOM surface that drives the highlight behavior.
<RGSlotOnNode>
{({ node }: RGNodeSlotProps) => (
<div
className="px-2 bg-purple-200 rounded"
onMouseEnter={() => nodeMouseOver(node)}
onMouseLeave={() => nodeMouseOut(node)}
>
<div className="my-node">
{node.text}
</div>
</div>
)}
</RGSlotOnNode>
This fragment shows that the visual emphasis is finalized in SCSS by styling relation-graph’s generated line wrapper classes.
.rg-line-peel.my-line-highlight {
.rg-line {
animation: my-line-anm2 2s infinite;
}
.rg-line-label {
color: #ffffff;
background-color: rgba(75, 28, 119, 1);
}
}
What Makes This Example Distinct
Comparison data puts this example closest to line-hightlight-pro, deep-each, custom-line-style, and layout-tree, but its focus is narrower than each of them. Against line-hightlight-pro, the difference is both trigger and scope: this example starts from node hover and highlights every incident line, while the other example starts from line click and selects one edge together with its endpoints.
Against deep-each, this example stays at one-hop inspection instead of recursively traversing descendants. Against custom-line-style, it is not a gallery of persistent line themes; the animated class appears only as temporary feedback. Against layout-tree, it keeps one fixed top-down layout rather than turning orientation changes into the subject of the demo.
The distinctive combination is therefore a slotted node hover handler, direct-neighbor line lookup through getLines(), runtime updateNode() and updateLine() styling, and animated orthogonal connectors in a wide top-down tree. The floating helper window, settings panel, and image export are useful, but they come from shared demo scaffolding and are not the unique part of this example.
Where Else This Pattern Applies
This pattern transfers well to org charts, dependency trees, approval chains, manufacturing assemblies, topic hierarchies, and filesystem-like explorers where users need to inspect immediate relationships without committing to a click selection or expanding a larger focus state.
A production variant could keep the same hover-to-neighbor pipeline while swapping in business labels, icons, status colors, or tooltips. It could also extend the scope from direct neighbors to multi-hop paths, but the core technique would stay the same: use slot-rendered node events to drive runtime styling of the already loaded graph.