JavaScript is required

Node Hover Detail Tooltip

This example shows how to open a non-interactive node detail tooltip from a custom `RGSlotOnNode` renderer and render the card inside `RGSlotOnView`. It uses wrapper-relative pointer coordinates, hover enter and leave handlers, and inline icon-node data to keep the graph read-only while still exposing node metadata.

Building a Hover-Only Node Detail Tooltip

What This Example Builds

This example builds a read-only relation graph where hovering a custom node opens a small detail card near the pointer. The visible scene is intentionally compact: green circular icon nodes, short labels rendered below the nodes, and a floating white tooltip that stays inside the graph view.

Users can inspect node details by moving the pointer onto a node and can dismiss the card by leaving that node. Node and line clicks are present, but they only log the selected object, so the main lesson is passive inspection rather than editing or command execution.

The most useful idea is the split responsibility between the node slot and the view slot: the node slot owns the hover trigger, and the view slot owns the overlay UI.

How the Data Is Organized

The graph data is created inline as one RGJsonData object inside initializeGraph(). It defines rootId: '2', a nodes array, and a lines array with explicit line ids and labels. Each node also carries data.myicon, which the custom node renderer uses to choose a Lucide icon.

There is no preprocessing step before setJsonData(). The component builds the final payload directly in code and loads it as-is, then recenters and fits the viewport. In a business application, the same shape could represent products, services, devices, departments, or dependency items, while data.myicon could be replaced by type, status, or category metadata that selects a custom visual.

How relation-graph Is Used

index.tsx wraps the demo with RGProvider, and MyGraph.tsx calls RGHooks.useGraphInstance() to get the live graph instance. A useEffect() hook runs initializeGraph() after the instance becomes available, which loads the inline data through setJsonData() and then calls moveToCenter() and zoomToFit().

The graph options are minimal and visual. defaultNodeColor provides the green fill that the custom slot reads through node.color, defaultNodeShape is set to RGNodeShape.circle, and defaultJunctionPoint is set to RGJunctionPoint.border. The reviewed source does not set an explicit layout option, so the node arrangement depends on relation-graph defaults for this dataset.

The main customization happens through two slots. RGSlotOnNode replaces the built-in node body with a circular icon renderer and binds onMouseEnter and onMouseLeave directly on the node DOM. RGSlotOnView renders the tooltip card as overlay HTML inside the graph scene. The tooltip position is computed from the pointer coordinates relative to a wrapper element, not from a graph event callback.

The graph-level click events are intentionally secondary. onNodeClick and onLineClick are bound on RelationGraph, but both handlers only log objects to the console. This keeps the demo in viewer mode and makes the hover overlay the only meaningful graph interaction.

Local styling is light and focused. The SCSS file defines the circular node size and centering through .c-my-rg-node, while the tooltip rows use .c-node-menu-item for spacing and typography. The overlay itself also sets pointerEvents: 'none', which preserves hover flow on the slotted node instead of turning the card into an interactive menu.

Key Interactions

  • Hovering a node opens a detail tooltip near the current pointer location.
  • Moving the pointer off the node closes the tooltip immediately.
  • The tooltip stays non-interactive because its overlay container disables pointer events.
  • Clicking a node logs the node object but does not change graph state.
  • Clicking a line logs the line object but does not open any additional UI.

Key Code Fragments

This options block shows that the example uses relation-graph mostly as a viewer and customizes only the basic node shape, color, and line junction mode.

const graphOptions: RGOptions = {
    defaultNodeColor: 'rgba(66,187,66,1)',
    defaultNodeShape: RGNodeShape.circle,
    defaultJunctionPoint: RGJunctionPoint.border,
};

This inline payload shows that icon selection is driven by per-node metadata and that the graph content is assembled directly in component code.

const myJsonData: RGJsonData = {
    rootId: '2',
    nodes: [
        { id: '1', text: 'Node-1', data: { myicon: 'star' } },
        { id: '2', text: 'Node-2', data: { myicon: 'settings' } },
        // ... more icon-tagged nodes ...
        { id: '5', text: 'Node-5', data: { myicon: 'gift' } }
    ],
    lines: [
        { id: 'l1', from: '7', to: '71', text: 'Investment' },
        // ... more labeled lines ...
    ]
};

This initialization sequence proves that the graph instance is loaded through the standard hook-based startup flow.

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

This hover handler is the key implementation detail for positioning the overlay from wrapper-relative pointer coordinates.

const showNodeTips = (nodeObject: RGNode, $event: React.MouseEvent) => {
    setCurrentNode(nodeObject);
    if (myPage.current) {
        const _base_position = myPage.current.getBoundingClientRect();
        setIsShowNodeTipsPanel(true);
        setNodeMenuPanelPosition({
            x: $event.clientX - _base_position.x + 10,
            y: $event.clientY - _base_position.y + 10
        });
    }
};

This node slot fragment shows that the hover trigger lives on the custom node DOM rather than on a graph-wide mouse-move listener.

<RGSlotOnNode>
    {({ node }: RGNodeSlotProps) => (
        <div
            className="h-full"
            onMouseEnter={(e) => showNodeTips(node, e)}
            onMouseLeave={() => hideNodeTips()}
        >
            <div className="c-my-rg-node" style={{ backgroundColor: node.color }}>
                <GetLucideIcon name={node.data?.myicon} />
            </div>
        </div>
    )}
</RGSlotOnNode>

This view slot fragment proves that the tooltip is rendered inside the graph scene and deliberately avoids intercepting pointer input.

<RGSlotOnView>
    {isShowNodeTipsPanel && currentNode && (
        <div
            className="p-3 bg-white border border-gray-300 shadow-xl absolute rounded-lg"
            style={{
                left: `${nodeMenuPanelPosition.x}px`,
                top: `${nodeMenuPanelPosition.y}px`,
                zIndex: 1000,
                pointerEvents: 'none'
            }}
        >

What Makes This Example Distinct

The comparison data places this example near node-menu-2, node-menu, and node-line-tips-contentmenu, but it uses the shared slot-overlay building blocks for a narrower purpose. Against node-menu-2 and node-menu, the important difference is that this demo is passive and hover-driven: it does not open actionable menus, it does not branch across node, line, and canvas targets, and it does not need click-away logic for an interactive panel.

Compared with node-line-tips-contentmenu, this is the smaller node-only recipe. It does not add line hit-testing, right-click menus, helper windows, or mixed overlay types. The comparison records also emphasize a rare combination here: green circular icon nodes, under-node badge labels, wrapper-relative pointer math, hover enter and leave handlers, and a floating white card with pointerEvents: 'none' in an otherwise read-only viewer.

That makes this example a stronger starting point when the requirement is lightweight node inspection inside the graph scene without introducing menu state, editing flows, or graph mutation. It should not be described as the only overlay demo, but it is one of the clearest references for a non-interactive node-hover detail pattern.

Where Else This Pattern Applies

This pattern transfers well to dependency maps, service topology viewers, product relationship explorers, device networks, and organization browsing screens where users need quick read-only details without leaving the graph. The tooltip content can be replaced with health summaries, ownership fields, status metadata, risk labels, or compact metrics while keeping the same hover-trigger and view-overlay structure.

It is also a practical extension point for richer inspection UI. A team could start with this passive card and later add viewport edge clamping, delayed hover timing, pinned detail panels, or touch-friendly alternatives, while still keeping the base idea of slotted node triggers plus in-scene overlay rendering.