JavaScript is required

Connect Lines to Node Content Targets

This example renders four fixed-position table panels and connects them with curved fake lines that terminate on specific headers or cells inside the node content. It uses RGSlotOnNode, RGConnectTarget, and mount-time graph initialization to demonstrate precise endpoint placement inside custom node bodies.

Connect Curved Lines to Exact Targets Inside Table Nodes

What This Example Builds

This example builds a small fixed-position relationship view made of four rectangular panels. Each panel is rendered as custom table markup instead of a default graph node, and the visible connectors land on specific headers or cells inside those tables rather than on the outer node border.

What the user sees is a full-height canvas with gray table cards, dark title bars, bright pink anchor chips, and blue or pink curved lines crossing between the cards. The user can click nodes or lines to inspect the bound graph objects in the console, but the main point of the demo is visual: it shows how to expose precise connection targets inside node content.

How the Data Is Organized

The graph data is assembled inline inside initializeGraph(). It defines rootId: 'a', four rectangle nodes with explicit x and y coordinates, an empty lines array, and ten fakeLines. Each fake line uses from and to ids such as a-r2-c2 or b-r1-c1, and those ids are matched to RGConnectTarget components rendered inside the node slot content.

There is no separate preprocessing pass before setJsonData(), but there is a deliberate naming convention: the fake-line endpoints and the slot-rendered target ids must stay in sync. In a real application, the same pattern could represent schema columns, form fields, approval checkpoints, data lineage fields, account mappings, or any structured panel where connections need to land on exact internal rows or headers.

How relation-graph Is Used

RGProvider wraps the example so MyGraph can read the live graph instance with RGHooks.useGraphInstance(). The graph runs in fixed layout mode, so relation-graph does not calculate placement; the four nodes already carry their coordinates in the inline RGJsonData. The options stay minimal: debug is disabled, and defaultJunctionPoint is set to RGJunctionPoint.lr.

The main integration point is RGSlotOnNode. Instead of accepting the default node body, the example branches on node.id and renders four different table panels. Inside those panels, selected headers and cells are wrapped in RGConnectTarget, which registers named HTML targets that the fakeLines can attach to. That is the core technique: the graph still manages line routing and viewport behavior, but the actual endpoints are embedded inside custom node DOM.

The graph instance API handles startup. On mount, initializeGraph() builds the inline payload, calls setJsonData(...), then recenters and fits the view with moveToCenter() and zoomToFit(). There are no editor tools, drag handles, overlay slots, or runtime data mutations. Styling is lightweight and local: .c-data-table adds white table backgrounds, dark borders, and rounded pink chips so the connectable fragments are visually obvious.

Key Interactions

  • Clicking a node triggers onNodeClick, which logs the selected RGNode object for inspection.
  • Clicking a curve triggers onLineClick, which logs the selected RGLine object without changing graph state.
  • The main interaction pattern is visual inspection: users trace which specific header or cell chip is connected to which target in another panel.

Key Code Fragments

This block shows that the graph is intentionally configured as a fixed-position viewer with left-right junction defaults.

const graphOptions: RGOptions = {
    debug: false,
    defaultJunctionPoint: RGJunctionPoint.lr,
    layout: {
        layoutName: 'fixed'
    }
};

This block shows the inline dataset using four positioned nodes, no normal lines, and fake lines instead.

const myJsonData: RGJsonData = {
    rootId: 'a',
    nodes: [
        { id: 'a', text: 'A区内配矿', nodeShape: RGNodeShape.rect, x: -500, y: -200 },
        { id: 'b', text: 'B区内洗选贸易企业', nodeShape: RGNodeShape.rect, x: 0, y: -400 },
        { id: 'c', text: 'C区内电场焦化企业', nodeShape: RGNodeShape.rect, x: 500, y: -200 },
        { id: 'd', text: 'D区外企业', nodeShape: RGNodeShape.rect, x: 0, y: 0 }
    ],
    lines: [],
    fakeLines: [

This block shows the endpoint-mapping pattern: fake lines reference the same ids that will later be registered inside node content.

fakeLines: [
    { id: 'fl-1', from: 'a-r2-c2', to: 'b-r1-c1', text: '', lineShape: RGLineShape.StandardCurve, color: 'rgba(29,169,245,0.76)', lineWidth: 3 },
    { id: 'fl-2', from: 'a-r2-c2', to: 'b-r2-c1', text: '', lineShape: RGLineShape.StandardCurve, color: 'rgba(29,169,245,0.76)', lineWidth: 3 },
    { id: 'fl-3', from: 'a-r3-c2', to: 'b-r3-c1', text: '', lineShape: RGLineShape.StandardCurve, color: '#e85f84', lineWidth: 3 },
    { id: 'fl-4', from: 'a-r3-c2', to: 'd-r2-c1', text: '', lineShape: RGLineShape.StandardCurve, color: '#e85f84', lineWidth: 3 },
    { id: 'fl-5', from: 'a-r3-c2', to: 'c-r4-c1', text: '', lineShape: RGLineShape.Curve5, color: '#e85f84', lineWidth: 3 },
    // ...
]

This block shows the mount-time initialization flow through the graph instance API.

await graphInstance.setJsonData(myJsonData);
graphInstance.moveToCenter();
graphInstance.zoomToFit();

useEffect(() => {
    initializeGraph();
}, []);

This block shows relation-graph receiving the click handlers and the custom node slot.

<RelationGraph
    options={graphOptions}
    onNodeClick={onNodeClick}
    onLineClick={onLineClick}
>
    <RGSlotOnNode>
        {({ node }: RGNodeSlotProps) => (

This block shows a real node-content anchor: the visible chip is wrapped in RGConnectTarget, so a fake line can terminate on that exact table cell.

<td>
    <RGConnectTarget targetId="a-r2-c2">
        <div>a-r2-c2</div>
    </RGConnectTarget>
</td>

This block shows the local styling that makes the target chips look like explicit connection points.

.c-data-table {
    width: 100%;
    background-color: #ffffff;
    td, th {
        border: 1px solid #4a5568;
        color: #000000;
        padding: 5px;
    }
    td div, th div {
        background-color: #e85f84;
        padding: 2px 10px;
        border-radius: 5px;
        color: #fff;
    }
}

What Makes This Example Distinct

The comparison data identifies this example as a compact technique reference for node-internal anchor wiring. Its distinctive feature is not just that it customizes node content, but that it spreads named RGConnectTarget endpoints across both table headers and body cells, then connects them with curved fakeLines inside one small fixed-layout viewer.

Compared with table-relationship, this example is lighter and more generic. It keeps only four hand-authored panel templates, avoids extra utility windows, and mixes different target positions instead of repeating one schema-card pattern. Compared with advanced-line-usage, the lesson is not about line metadata or arrow variations on default nodes; it is about attaching curves to exact DOM positions inside slot-rendered node bodies. Compared with node-menu-2 and node-tips, the slot DOM is structural rather than overlay-oriented: it exists so lines can land on internal targets, not so menus or tips can appear near the pointer.

That combination of fixed layout, per-node table JSX, mixed header-and-cell targets, and color-coded curved fake lines makes this a better starting point than nearby examples when the real problem is precise endpoint placement inside complex node content.

Where Else This Pattern Applies

This pattern applies to schema browsers, field-mapping tools, policy coverage matrices, process handoff boards, bill-of-material tables, and data lineage views where a connection should land on a specific row, column, or label inside a structured card. It is also useful when migrating an existing HTML panel design into relation-graph without flattening every relationship down to one connection point per node.

The same approach can be extended beyond tables. Any custom node body that can render stable internal fragments with predictable ids can expose those fragments as RGConnectTargets and let relation-graph route visible curves to them.