JavaScript is required

Canvas Multi-Node Selection

This example shows a fixed relation-graph canvas where users can marquee-select multiple existing nodes, clear selection with a canvas click, and switch canvas dragging between selection and move modes. It also surfaces live selection geometry and a selected-node count in a floating helper window, while reusing shared canvas settings and image export tooling.

Canvas Multi-Node Selection with Live Telemetry

What This Example Builds

This example builds a full-height graph viewer for selecting multiple existing nodes with a drag rectangle. Users see a centered icon-based network, a floating helper window, a mode toggle that switches canvas dragging between selection and move, and a live counter of how many nodes are currently selected. The main point is not graph editing, but turning relation-graph marquee selection into an explicit multi-node picking workflow with surrounding UI feedback.

How the Data Is Organized

The graph is defined as an inline RGJsonData object with a fixed rootId, a flat nodes array, and a lines array that links parent and child ids. There is no domain-specific transformation before loading; the data is passed directly to setJsonData, then the graph is centered and fitted into the viewport. In a real product, the same structure can represent service dependencies, resource inventories, topology maps, or any prepared graph where operators need to select existing items in bulk.

How relation-graph Is Used

The entry file wraps the example in RGProvider, then MyGraph.tsx uses RGHooks.useGraphInstance() and RGHooks.useSelection() to connect React state to relation-graph runtime state. The graph options are intentionally narrow: layout.layoutName = 'center', defaultJunctionPoint = RGJunctionPoint.border, and dragEventAction is driven by React state so the canvas can switch between marquee selection and panning without rebuilding the graph.

The example-specific behavior is attached through onCanvasSelectionEnd and onCanvasClick. When selection ends, the code resolves the nodes inside the selection rectangle with getNodesInSelectionView(...), then writes each node’s selected flag back through updateNode(...) and derives a React-side selected-node list. When the user clicks blank canvas space, it clears every node’s selected state and resets the count.

The graph body is simplified through RGSlotOnNode, which replaces the normal node content with a centered cloud icon. That keeps the visual focus on the selection workflow instead of labels or node templates. The floating window comes from a shared DraggableWindow helper. In this example it hosts instructions, the selection-mode button, the selected-node count, and the live marquee geometry. The same shared helper also exposes a settings panel for runtime wheel or drag changes and image export. The local SCSS mainly styles the helper text and toggle button; it includes checked-node selectors, but they are empty, so the reviewed local files do not define a custom selected-node appearance.

Key Interactions

  • Dragging on the canvas in selection mode creates a marquee, and finishing that gesture selects only the nodes returned by getNodesInSelectionView(...).
  • Clicking blank canvas space clears all node selection state and resets the visible selected-node count.
  • The button in the floating window toggles canvas dragging between selection and move.
  • While the marquee is active, the helper window shows whether selection is in progress and displays the rectangle’s start position and size from RGHooks.useSelection().
  • The shared settings overlay can change wheel and drag behavior and download an image of the current graph view, but those controls come from reused helper code rather than the core selection logic.

Key Code Fragments

This options block shows that the example binds relation-graph drag behavior directly to React state.

const graphOptions: RGOptions = {
    debug: false,
    defaultJunctionPoint: RGJunctionPoint.border,
    dragEventAction: enableSelectionMode ? 'selection' : 'move',
    layout: {
        layoutName: 'center'
    }
};

This initialization block proves that the graph data is inline and only receives viewport fitting after it is loaded.

const myJsonData: RGJsonData = {
    rootId: '2',
    nodes: [
        { id: '1', text: 'Node-1' },
        { id: '2', text: 'Node-2' },
        // ... more prepared nodes
    ],
    lines: [
        { id: 'l1', from: '7', to: '71' },
        // ... more prepared lines
    ]
};
await graphInstance.setJsonData(myJsonData);
graphInstance.moveToCenter();
graphInstance.zoomToFit();

This handler is the core selection workflow: resolve nodes inside the marquee, write selected back to each node, then recalculate the selected list.

const onCanvasSelectionEnd = (selectionView: RGSelectionView, $event: RGUserEvent) => {
    const nodesInSelection = graphInstance.getNodesInSelectionView(selectionView);

    graphInstance.getNodes().forEach((node: RGNode) => {
        const isSelected = nodesInSelection.some(n => n.id === node.id);
        graphInstance.updateNode(node.id, { selected: isSelected });
    });

    updateSelectedNodes();
};

This JSX ties the mode toggle and live marquee telemetry to the floating helper window.

<button
    className={`c-my-button ${enableSelectionMode ? 'c-my-button-checked' : ''}`}
    style={{ width: '200px' }}
    onClick={() => { setEnableSelectionMode(!enableSelectionMode); }}
>
    {enableSelectionMode ? 'Disable Selection Mode' : 'Enable Selection Mode'}
</button>
<div className="c-option-name">Selected Nodes Count: {selectedNodes.length}</div>
{selectionView.show && <div className="rounded p-3 bg-green-100">
    Start: {selectionView.x}, {selectionView.y}
    Size: {selectionView.width}, {selectionView.height}
</div>}

This shared helper fragment shows how the floating window opens runtime canvas settings and image export.

<SettingRow
    label="Canvas Drag Event:"
    options={[
        { label: 'Selection', value: 'selection' },
        { label: 'Move', value: 'move' },
        { label: 'none', value: 'none' },
    ]}
    value={dragMode}
    onChange={(newValue: string) => { graphInstance.setOptions({ dragEventAction: newValue }); }}
/>
<SimpleUIButton onClick={downloadImage}>
    Download Image
</SimpleUIButton>

What Makes This Example Distinct

Compared with nearby selection demos, this example keeps the drag box focused on inspection rather than authoring. The comparison data specifically distinguishes it from canvas-selection, where the dragged region can hand off into node creation. Here, the result of the gesture is selection bookkeeping on existing nodes: resolve enclosed nodes, set per-node selected flags, and show the selected count.

It is also more specific than generic canvas-interaction playgrounds. Compared with drag-and-wheel-event, the runtime drag-mode switch is not the end goal by itself; it exists to support one concrete workflow, and that workflow is reinforced by RGHooks.useSelection(), onCanvasSelectionEnd, and the live count plus geometry readout. Compared with canvas-event, it spends less time on drag lifecycle feedback and more time on the post-gesture result: which nodes ended up inside the box.

The comparison and rarity data support one narrow combination as the main distinguishing point: pure drag-box multi-node picking, click-to-clear reset behavior, runtime selection or move switching, icon-only nodes, and a floating helper window that surfaces both live marquee geometry and the resolved selection count in the same viewer-style demo.

Where Else This Pattern Applies

This pattern transfers well to inspection-oriented tools where users need to collect existing graph items without editing structure. Examples include network incident triage, dependency analysis, asset or device selection before batch operations, and knowledge graph review tools where operators mark a temporary working set.

It also applies to mixed interfaces where the graph is only one panel in a larger workflow. The live selection rectangle and selected-node count can drive side panels, comparison drawers, export actions, or bulk tagging flows while keeping the underlying graph read-only.