JavaScript is required

Node Resize Controls and Styles

This example turns a small relation graph into a focused selected-node resizing workspace. It demonstrates how `setEditingNodes([node])`, `RGEditingNodeController`, and `RGEditingResize` work together, and it also shows how the built-in resize overlay can be switched between the default look and two custom SCSS skins.

Click-to-Resize Nodes with Switchable Resize Handle Styles

What This Example Builds

This example builds a small node-resizing workspace on top of a relation graph. The screen shows a full-height dotted canvas, a mixed sample graph with different node shapes and sizes, and a floating helper window that explains the interaction and exposes a style switcher.

The main user flow is direct size editing. Clicking a node makes it the current editing target, which causes relation-graph’s built-in resize overlay to appear around that node. The user can then drag the resize handles and switch the overlay between the default appearance and two custom skins.

The most useful part of the example is its narrow scope. It does not try to become a full graph editor. Instead, it isolates the minimum setup for selected-node resizing and shows how to restyle the built-in resize controls with CSS.

How the Data Is Organized

The graph data is declared inline inside initializeGraph() as one RGJsonData object with rootId, a flat nodes array, and a lines array. The nodes are intentionally varied: some change border width or color, some change fill or font color, one is circular, two are rectangular, one has explicit width and height, and one uses fixed coordinates.

There is only one preprocessing step before setJsonData(): every line is mapped to add a generated id value. After that, the dataset is loaded once, centered, and zoomed to fit. The example does not do server synchronization, resize persistence, or incremental graph assembly in its own code.

In a real application, the same structure could represent dashboard cards, equipment blocks, organization units, topology devices, or workflow steps where operators need to adjust node dimensions in context without building a larger authoring tool.

How relation-graph Is Used

The entry file wraps the demo in RGProvider, and MyGraph uses RGHooks.useGraphInstance() to control the live graph. On mount, it loads the inline dataset with setJsonData(), then calls moveToCenter() and zoomToFit() so the sample graph is immediately usable.

The graph options stay minimal. The only explicit option is dragEventAction: 'move', which turns canvas dragging into viewport panning. The editing flow is driven by two graph events: onNodeClick calls setEditingNodes([nodeObject]), while onCanvasClick calls setEditingNodes([]). That means the built-in editing-node state is the single source of truth for whether resize handles should be shown.

The resize UI is mounted through RGSlotOnView, not through custom node rendering. Inside that slot, RGEditingNodeController hosts RGEditingResize, which lets relation-graph draw and manage the resize overlay for the current editing node. The demo keeps the default node body and changes only the editing overlay.

Styling is split between a wrapper class and SCSS overrides. The root <div> uses myGraphStyle in its class list, SimpleUISelect changes that value at runtime, and the SCSS file uses .my-graph-style-01 and .my-graph-style-02 to restyle .rg-editing-ctrl and .rg-resize-ctl. The same SCSS file also gives the canvas its scale-aware dotted background.

The floating DraggableWindow is shared helper infrastructure rather than the main lesson, but it still matters for runtime behavior. Its settings panel reads graph options with RGHooks.useGraphStore(), changes wheel and drag behavior with graphInstance.setOptions(...), and exports the graph image through prepareForImageGeneration() and restoreAfterImageGeneration().

Key Interactions

Clicking a node enters resize editing mode. The code puts that node into the editing-node set, and RGEditingResize renders handles for that selected target.

Dragging the built-in handles changes the current node’s dimensions directly on the canvas. The example does not add custom resize math; it relies on relation-graph’s built-in controller.

Clicking empty canvas space clears the editing-node set. That immediately removes the resize overlay and returns the canvas to its non-editing state.

The floating selector switches resize skins without changing resize behavior. It only changes the wrapper class, so the same built-in controller is shown with different handle shapes, sizes, offsets, and accent colors.

The helper window itself is interactive. It can be dragged, minimized, expanded into a settings panel, used to switch wheel and drag behavior, and used to export the current graph as an image, but those controls are secondary to the resize workflow.

Key Code Fragments

This fragment shows that the graph data is seeded inline and that line ids are generated before the dataset is loaded.

const myJsonData: RGJsonData = {
    rootId: 'a',
    nodes: [
        { id: 'a', text: 'Border color', borderColor: '#333333' },
        { id: 'a1-4', text: 'XXX', nodeShape: RGNodeShape.circle },
        { id: 'd', text: 'Node Size', width: 150, height: 150, color: '#ff8c00', borderWidth: 5, borderColor: '#ffd700', fontColor: '#ffffff' },
    ],
    lines: [
        { from: 'a', to: 'b' },
        { from: 'a', to: 'a1' }
    ].map((line, index) => ({ ...line, id: `line_${index}` }))
};

This fragment shows the mount-time graph initialization path.

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

This fragment proves that the resize target is controlled entirely through relation-graph’s editing-node state.

const onNodeClick = (nodeObject: RGNode, $event: RGUserEvent) => {
    console.log('onNodeClick:', nodeObject);
    graphInstance.setEditingNodes([nodeObject]);
};

const onCanvasClick = () => {
    graphInstance.setEditingNodes([]);
};

This fragment shows that the UI switches resize-controller skins by changing only the wrapper class.

<div className={`my-graph ${myGraphStyle}`} style={{ height: '100vh' }}>
    <DraggableWindow>
        <SimpleUISelect
            data={[
                {value: '', text: 'Default'},
                {value: 'my-graph-style-01', text: 'Customize Style 1'},
                {value: 'my-graph-style-02', text: 'Customize Style 2'},
            ]}
            currentValue={myGraphStyle}
            onChange={setMyGraphStyle}
        />
    </DraggableWindow>

This fragment shows where the built-in resize overlay is mounted in the view layer.

<RelationGraph
    options={graphOptions}
    onCanvasClick={onCanvasClick}
    onNodeClick={onNodeClick}
>
    <RGSlotOnView>
        <RGEditingNodeController>
            <RGEditingResize />
        </RGEditingNodeController>
    </RGSlotOnView>
</RelationGraph>

This fragment shows how the second custom skin changes controller color and handle geometry through SCSS alone.

.my-graph-style-02 {
    .relation-graph {
        .rg-editing-ctrl {
            --editor-main-color: #f616df;
            box-shadow: 0 0 0 2px var(--editor-main-color);
            --my-size-big: 20px;
            --my-size-small: 8px;
            .rg-resize-ctl {
                --resize-handler-size: 12px;
                --resize-handler-offset: -5px;
            }
        }
    }
}

What Makes This Example Distinct

The comparison data shows that this example is not distinctive merely because it uses RGEditingNodeController, RGEditingResize, editing-node state, or the shared floating helper window. Nearby examples such as batch-operations-on-nodes, edit-node-text, gee-node-alignment-guides, and freely-draw-lines-on-canvas reuse parts of that editing scaffold.

What stands out is the narrower retrieval target. This example concentrates on one selected-node workflow: click a node, expose the built-in resize overlay, and optionally swap that overlay between the default look and two custom SCSS skins. Compared with batch-operations-on-nodes and gee-node-alignment-guides, it spends its interaction budget on resize-controller theming rather than multi-select tools, snapping, or drag-time alignment aids.

It is also a more direct geometry-editing reference than edit-node-text or freely-draw-lines-on-canvas. Those examples lean on custom node slots, inline text inputs, drawing layers, or node creation flows. This one keeps the stock node rendering and demonstrates that built-in resize controls can be restyled through CSS while the resize behavior itself stays unchanged.

The prepared rarity notes also make the live skin switch and mixed node geometry sampler important. The example deliberately mixes circle and rectangle nodes with different sizes so the resize overlay can be tested against different shapes in one compact canvas.

Where Else This Pattern Applies

This pattern transfers well to tools where users need to resize existing graph items but do not need a full authoring environment. Examples include dashboard-layout editors, organization-chart maintenance tools, equipment or floor-plan annotations, topology diagram cleanup tools, and whiteboard templates with resizable cards.

A production version could keep the same editing-node workflow while replacing the inline sample data with API data, persisting width and height after resize, adding permission checks, or exposing more controller skins for different editing modes. The reusable idea is to keep resize behavior inside relation-graph’s built-in controller and customize its appearance with wrapper-level CSS.