JavaScript is required

Highlight Line and Endpoints on Click

This example shows a top-down tree where clicking a relation line highlights that line and its two endpoint nodes. It uses relation-graph instance APIs to reset previous state, apply temporary classes at runtime, and clear the emphasis again when the canvas background is clicked.

Highlight a Clicked Line and Its Endpoint Nodes

What This Example Builds

This example builds a compact top-down tree viewer where the selectable object is the relation line itself. The screen shows a wide-spaced orthogonal hierarchy with small purple node chips, visible line labels, and a floating helper window above the full-height canvas.

When a user clicks one line, the example widens that line, switches its label chip to a dark purple treatment, and adds a ring highlight to the line’s two endpoint nodes. Clicking the empty canvas clears that temporary emphasis state. The most important part is that the highlight starts from the edge and then propagates to exactly two nodes, instead of starting from a node hover or expanding to a whole branch.

How the Data Is Organized

The graph is assembled inline as one RGJsonData object with rootId, a flat nodes array, and a flat lines array. Each node has a stable id and text, and each line has its own id, from, to, and label text. The example does not run any structural preprocessing before setJsonData; it prepares the dataset directly inside initializeGraph() and loads it as-is.

That structure maps cleanly to real relationship datasets such as service dependencies, approval chains, organizational reporting lines, or equipment connections. In production code, the same shape could be built from API records, but the interaction logic would stay the same as long as line endpoints keep stable ids.

How relation-graph Is Used

The demo uses RGProvider so both the example component and the shared floating utility panel can resolve the same graph instance through hooks. Inside MyGraph, RGHooks.useGraphInstance() is used to load data, fit the graph into view, reset classes, and update live nodes and lines after user actions.

The graph configuration keeps the layout intentionally stable: it uses the built-in tree layout, flows from top to bottom, spaces branches at 150 pixels horizontally and vertically, and routes connectors with RGLineShape.SimpleOrthogonal plus RGJunctionPoint.tb. Default node chrome is made transparent because the visible node body comes from RGSlotOnNode, which renders a custom rounded chip for each node label.

The example also uses relation-graph runtime APIs instead of rebuilding the dataset after every interaction. getLines() and updateLine() are used to clear the previous line state and style the clicked relation, while getNodes() and updateNode() apply or remove the endpoint highlight class. The shared CanvasSettingsPanel uses useGraphInstance() and useGraphStore() to expose wheel mode, drag mode, and image export, but those controls are supporting scaffolding rather than the main lesson of the example.

Key Interactions

Clicking a relation line is the primary interaction. The handler first clears any previous custom highlight, then finds the clicked line by id, increases its width, applies the my-line-highlight class, and highlights only the two connected nodes.

Clicking the canvas background is the reset interaction. It clears relation-graph’s checked state and runs the same highlight reset pass so temporary emphasis does not accumulate across multiple selections.

The floating helper window is draggable and minimizable, and its settings button opens shared canvas controls for wheel behavior, drag behavior, and image download. Those controls affect the viewing environment, but they do not change the example’s graph data or highlight scope.

Key Code Fragments

This options block proves that the example is a fixed top-down tree with orthogonal connectors and transparent default node chrome.

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,
    defaultJunctionPoint: RGJunctionPoint.tb,
    // ...
};

This dataset fragment shows that the graph uses one inline tree payload with stable node ids and explicit line endpoints.

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' },
        { id: 'l2', data: { 'myLineId': 'line2' }, from: 'b', to: 'b1', text: 'Relation Description' },
        // ...
    ],
};

This event flow is the core pattern: clear previous state, style one clicked edge, then propagate the highlight to its endpoints.

const onLineClick = (clickedLine: RGLine) => {
    clearHighlightStatus();
    graphInstance.getLines().forEach(line => {
        if (clickedLine.id === line.id) {
            graphInstance.updateLine(line, {
                className: 'my-line-highlight',
                lineWidth: 3
            });
        }
    });
    graphInstance.getNodes().forEach(node => {
        if (clickedLine.from === node.id || clickedLine.to === node.id) {
            graphInstance.updateNode(node, { className: 'my-node-highlight' });
        }
    });
};

This slot and SCSS pairing shows how the example replaces the default node body and attaches the visible endpoint emphasis to the rendered node shell.

<RelationGraph options={graphOptions} onLineClick={onLineClick} onCanvasClick={onCanvasClick}>
    <RGSlotOnNode>
        {({ node }: RGNodeSlotProps) => (
            <div className="px-2 bg-purple-200 rounded">
                <div className="my-node">
                    {node.text}
                </div>
            </div>
        )}
    </RGSlotOnNode>
</RelationGraph>
.rg-node-peel.my-node-highlight {
    .rg-node {
        box-shadow: 0px 0px 0px 5px rgba(75, 28, 119, 1);
    }
}

.rg-line-peel.my-line-highlight {
    .rg-line-label {
        color: #ffffff;
        background-color: rgba(75, 28, 119, 1);
    }
}

What Makes This Example Distinct

Compared with nearby examples such as line-hightlight, deep-each, and custom-line-style, this one is most useful when the relation itself must be the interaction target. It does not start from node hover, recurse through a subtree, or switch a graph-wide line skin. Instead, it keeps the layout fixed and turns one clicked built-in line into a temporary inspection state for that line and exactly two nodes.

The comparison data also shows that the explicit canvas-reset flow is part of what makes this example stand out. In a small viewer demo with shared utility scaffolding, it combines a wide-spaced orthogonal tree, purple slot-rendered node chips, edge-first selection, endpoint node rings, and background-click reset into a very focused relation-inspection pattern.

That makes it a stronger starting point than line-hightlight when the selectable object is an edge, and a tighter reference than deep-each when the desired scope is local endpoint emphasis rather than branch-wide focus.

Where Else This Pattern Applies

This pattern transfers well to dependency maps where clicking a connection should reveal only the two services directly bound by that dependency. It also fits workflow or approval diagrams where the edge represents a handoff that needs to be inspected without changing the rest of the graph state.

The same approach also works for data lineage viewers, network link inspection, and troubleshooting UIs where users need a quick answer to “which two entities does this connection bind?” without expanding a whole neighborhood. If a product later needs more context, this edge-first highlight can be extended with side panels, detail popovers, or multi-step drill-down while keeping the same getLines() and getNodes() update pattern.