JavaScript is required

Animated Line Slot with Detail Link

This example replaces relation-graph's default line renderer with a custom slot that draws animated pipe-style edges, a path-following dot, and a clickable Detail action. It keeps the normal line click pipeline, uses a left-to-right tree layout, and reuses a shared panel for canvas settings and image export.

Animating Custom Line Slots with Clickable Detail Labels

What This Example Builds

This example builds a left-to-right route tree on a dark canvas, then replaces the default edge renderer with a custom line slot. The result is a set of thick animated pipe-like lines, a magenta dot that travels along each computed path, and a white line label that can append a green Detail action.

Users can inspect the network visually, click lines without losing relation-graph’s normal line event flow, trigger a line-specific detail action from the label, open the floating settings panel, and export the canvas as an image. The main point of the demo is that the line layer is fully customized while relation-graph still supplies the geometry, text positioning, and event pipeline.

How the Data Is Organized

The graph is loaded from an inline RGJsonData object with rootId, nodes, and lines. Each line stores from, to, text, and a data payload with route-style metadata such as myIcon, myCapacity, myWeight, and mySpeed. Before loading the data, the demo iterates over every line and sets showEndArrow = false, which keeps the pipe animation visually clean and avoids mixing the custom stroke with default arrowheads.

That structure can map directly to shipment routes, service dependencies, machine-to-machine transfers, or any directional network where the edge carries operational metrics. The dataset is static in this demo, but the same shape would work with API-driven line metadata as long as each connection can still be expressed as a from and to pair plus per-line details.

How relation-graph Is Used

RGProvider wraps the whole demo so hooks resolve against the active graph instance. Inside MyGraph, RelationGraph is configured with a tree layout that grows from the left, levelGaps of [500, 400], RGJunctionPoint.lr, and RGLineShape.StandardCurve. That gives the custom renderer a predictable curved route shape to decorate.

The main customization point is RGSlotOnLine. Instead of accepting relation-graph’s default line output, the example passes each line’s runtime slot props into MyLineContent. That component calls generateLinePath to get the real SVG path, calls generateLineTextStyle to reuse relation-graph’s computed label positioning, forwards custom clicks back into graphInstance.onLineClick, renders the path through RGLinePath, and conditionally renders the non-SVG label branch through RGLineText.

The graph instance API is also used outside the slot. setJsonData, moveToCenter, and zoomToFit initialize the view on mount. The shared settings panel uses setOptions to switch wheel and drag behavior, and it uses prepareForImageGeneration plus restoreAfterImageGeneration to export a clean canvas image. The visual finish comes from my-relation-graph.scss, which overrides the map background, removes node chrome, adds a selected-node glow, animates the line stroke with stroke-dasharray, and drives the moving dot with offset-path.

Key Interactions

  • Clicking the SVG line path still enters relation-graph’s onLineClick pipeline even though the line visuals come from a custom slot.
  • When the non-SVG label branch is active, clicking the label also forwards to the same line click handler.
  • The appended Detail link calls parent logic with the current RGLine and opens an alert for that line.
  • The floating DraggableWindow can be dragged, minimized, and switched into a canvas settings panel.
  • The settings panel changes wheel behavior, changes canvas drag behavior, and downloads an exported image of the current graph.

Key Code Fragments

This block shows the layout and default line settings that the custom renderer builds on.

const graphOptions: RGOptions = {
    defaultJunctionPoint: RGJunctionPoint.lr,
    defaultLineShape: RGLineShape.StandardCurve,
    layout: {
        layoutName: 'tree',
        from: 'left',
        levelGaps: [500, 400]
    }
};

This block shows the inline route dataset and the preprocessing step that disables default end arrows before loading.

const myJsonData: RGJsonData = {
    rootId: 'base',
    nodes: [
        { id: 'base', text: '🏢 Base', },
        // ...
    ],
    lines: [
        { from: 'base', to: '1', text: 'Line X01', data: { myIcon: '🚢', myCapacity: 1.2, myWeight: 1, mySpeed: 1 } },
        // ...
    ]
};
myJsonData.lines.forEach((line) => {
    line.showEndArrow = false;
});
await graphInstance.setJsonData(myJsonData);

This block shows how the example replaces relation-graph’s default line renderer with MyLineContent.

<RelationGraph
    options={graphOptions}
    onLineClick={onLineClick}
>
    <RGSlotOnLine>
        {(lineSlotProps: RGLineSlotProps) => {
            return (
                <MyLineContent
                    {...lineSlotProps}
                    onMyLineDetailClick={onMyLineDetailClick}
                />
            );
        }}
    </RGSlotOnLine>
</RelationGraph>

This block shows the custom slot computing real path geometry once and exposing it to both relation-graph helpers and CSS animation.

const linePathInfo = useMemo<RGLinePathInfo>(() => graphInstance.generateLinePath(lineConfig), [lineConfig]);
const onLineClick = (e: React.MouseEvent | React.TouchEvent) => {
    graphInstance.onLineClick(lineConfig.line, e.nativeEvent);
};
const textStyle = graphInstance.generateLineTextStyle(lineConfig, linePathInfo);
const pathEl = document.createElementNS('http://www.w3.org/2000/svg', 'path');
pathEl.setAttribute('d', linePathInfo.pathData);
return (<g
    style={{
        '--my-line-path': `path('${linePathInfo.pathData}')`,
        '--my-line-path-length': pathEl.getTotalLength()
    }}>

This block shows the slot keeping relation-graph’s native line primitive while adding a moving dot.

<RGLinePath
    lineConfig={lineConfig}
    linePathInfo={linePathInfo}
    useTextOnPath={useSvgTextPath}
    checked={checked}
    graphInstanceId={graphInstanceId}
    onLineClick={onLineClick}
>
    <circle className="my-dot" r="5"></circle>
</RGLinePath>

This block shows the non-SVG label branch adding a separate Detail action after the computed line text.

<div
    className={`rg-line-label ${useTextOnPath ? 'rg-line-label-on-path' : ''}`}
    style={{
        ...textStyle.cssStyles
    }}
    onTouchStart={onLineClick}
    onClick={onLineClick}
>
    {textStyle.text}
    <a
        className="text-green-300 cursor-pointer hover:underline"
        onClick={() => { onMyLineDetailClick(lineConfig.line); }}
    >
        Detail
    </a>
</div>

This block shows the CSS that converts the computed geometry into pipe animation and path-following motion.

.rg-line {
    stroke: rgb(67, 102, 241);
    stroke-width: 10px;
    animation: draw-line 5s linear infinite;
    stroke-dasharray: var(--my-line-path-length);
    stroke-dashoffset: var(--my-line-path-length);
}

.my-dot {
    offset-path: var(--my-line-path);
    offset-distance: 0%;
    animation: ride-path 5s linear infinite;
}

What Makes This Example Distinct

The comparison data positions this example as a focused animated line-slot reference rather than a general line-style sample. Its clearest distinguishing trait is that it keeps RGLinePath and RGLineText in the loop, but still uses generateLinePath, generateLineTextStyle, and relation-graph’s normal onLineClick flow inside a fully replaced line renderer. That combination is already rare on its own.

Compared with adv-line-slot, this example puts more emphasis on measured pipeline animation than on endpoint annotation. It uses the actual SVG path length to drive both the stroke draw-in effect and the moving dot, while adv-line-slot is described as the more general annotated line-slot sibling. Compared with adv-line-slot-2, this is the leaner reference: it stays focused on animated line rendering and a simple detail action instead of adding custom node slots, progress-position markers, or checked-only metric panels. Compared with adv-line-slot2, it stays closer to relation-graph’s native line primitives instead of transforming each edge into ribbon-like filled geometry.

The result is a compact viewer-oriented pattern that combines a dark route-monitoring look, hybrid SVG and HTML labels, and line-level actions without turning into a broader editing demo.

Where Else This Pattern Applies

This pattern can be adapted to logistics dashboards, network traffic views, service dependency maps, manufacturing transfer flows, energy distribution routes, or security path analysis. It is especially useful when the edge needs to carry the story: animated flow, line-specific metrics, direct line actions, and exportable presentation output, while relation-graph continues to handle the underlying path geometry and event routing.