Double click to edit node text
This example shows a fixed-layout relation-graph that replaces the default node body with a custom inline editor. It demonstrates mode-gated double-click label editing, keyboard and blur commit or cancel behavior, `graphInstance.updateNode()` persistence, and a shared floating panel for canvas settings and image export.
Inline Node Text Editing in a Fixed Layout Graph
What This Example Builds
This example builds a fixed-position relation graph where node labels can be edited in place. The screen shows a dotted canvas, blue straight links, and a floating helper window, while the nodes themselves demonstrate different border, color, size, shape, and fixed-position settings.
The primary user action is label maintenance. When editing is enabled, a double-click on a node swaps the normal label for a yellow inline input, and the changed text is written back to the graph without reloading the whole dataset.
The key design choice is that the node body is replaced through a custom node slot. That lets the example own focus handling, keyboard behavior, commit and cancel rules, and the global editing toggle at the component level.
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. Each node record carries fixed x and y coordinates, and several nodes also include visual overrides such as borderWidth, borderColor, fontColor, color, width, height, fixed, className, or a node-specific nodeShape.
There are two small preprocessing steps before setJsonData(). The code fills in missing line ids with line-${index}, and it converts numeric nodeShape values into the RGNodeShape enum expected by the current API. After that normalization, the full dataset is loaded once and all later label changes happen through targeted node updates.
In a real product, the same structure can stand for process nodes, equipment labels, organization units, floor-plan annotations, or knowledge-graph entities whose text needs quick maintenance without opening a separate form.
How relation-graph Is Used
The entry component wraps the demo in RGProvider, and MyGraph uses RGHooks.useGraphInstance() to initialize and mutate the graph. The main options set a fixed layout, rectangular nodes by default, straight lines, border junction points, blue line color, and line width 2. Because the layout is fixed, the inline node coordinates determine the initial placement instead of a runtime tree or force layout.
The custom rendering path is centered on RGSlotOnNode. Each rendered node is replaced with MyEditableNode, which receives the runtime node, the global isEditingEnabled flag, and a callback that persists text changes through graphInstance.updateNode(...). The current source does not use a line slot or an active view slot; the notable extension point here is the node slot.
The graph instance API is used in two layers. MyGraph calls setJsonData(), moveToCenter(), and zoomToFit() during mount, then uses updateNode() when a label edit is confirmed. The shared CanvasSettingsPanel inside DraggableWindow uses RGHooks.useGraphStore() to read current wheel and drag behavior, uses setOptions() to switch those behaviors at runtime, and runs the export pipeline with prepareForImageGeneration(), getOptions(), and restoreAfterImageGeneration().
Styling is split between graph options and CSS. The SCSS file customizes .relation-graph with canvas offset and scale variables plus a radial-gradient dot background, while the editable node component contributes the yellow input field and the cursor-text affordance when editing is available.
Key Interactions
Double-click is the main entry point. A node only enters inline editing when the global checkbox has enabled that mode, and the handler stops propagation so the graph does not interpret the same gesture as a normal node click.
Once editing starts, the input is focused automatically and its text is selected. Pressing Enter or blurring the input commits the change, while pressing Escape restores the previous node text and exits without saving.
The editing toggle is global rather than per-node. The checkbox in the floating window updates isEditingEnabled in MyGraph, and that boolean is passed into every slotted node renderer.
The floating window adds secondary controls. It can be dragged, minimized, expanded into a settings panel, used to switch wheel behavior between scroll, zoom, and none, used to switch canvas dragging between selection, move, and none, and used to export the current graph as an image.
Key Code Fragments
This fragment shows the fixed-layout graph configuration and the default visual rules that the custom node renderer builds on.
const graphOptions: RGOptions = {
defaultLineColor: '#43a2f1',
defaultLineWidth: 2,
layout: {
layoutName: 'fixed'
},
defaultNodeShape: RGNodeShape.rect,
defaultLineShape: RGLineShape.StandardStraight,
defaultJunctionPoint: RGJunctionPoint.border
};
This fragment shows that the example normalizes incoming data before loading it and then centers the loaded fixed-layout graph.
myJsonData.lines.forEach((line, index) => {
if (!line.id) {
line.id = `line-${index}`;
}
});
myJsonData.nodes.forEach(node => {
if (node.nodeShape === 0) node.nodeShape = RGNodeShape.circle;
if (node.nodeShape === 1) node.nodeShape = RGNodeShape.rect;
});
await graphInstance.setJsonData(myJsonData);
graphInstance.moveToCenter();
graphInstance.zoomToFit();
This fragment proves that relation-graph’s node slot is the mechanism that swaps the default node body for the editable renderer.
<RelationGraph options={graphOptions}>
<RGSlotOnNode>
{({ node, checked, dragging }: RGNodeSlotProps) => (
<MyEditableNode
node={node}
enableEditingMode={isEditingEnabled}
onNodeTextChange={onNodeTextChange}
/>
)}
</RGSlotOnNode>
</RelationGraph>
This fragment shows how the custom node enters edit mode and immediately prepares the input for text replacement.
useEffect(() => {
if (isEditing && inputRef.current) {
inputRef.current.focus();
inputRef.current.select();
}
}, [isEditing]);
const handleDoubleClick = (e: React.MouseEvent) => {
if (enableEditingMode) {
e.stopPropagation();
setIsEditing(true);
}
};
This fragment shows the commit and cancel flow, including the callback that eventually persists the text through graphInstance.updateNode(...).
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
finishEditing();
} else if (e.key === 'Escape') {
setEditingText(node.text || '');
setIsEditing(false);
}
};
const finishEditing = () => {
if (editingText !== node.text) {
onNodeTextChange(node, editingText);
}
setIsEditing(false);
};
This fragment shows the shared settings panel mutating live graph behavior through the active graph instance.
<SettingRow
label="Wheel Event:"
options={[
{ label: 'Scroll', value: 'scroll' },
{ label: 'Zoom', value: 'zoom' },
{ label: 'None', value: 'none' },
]}
value={wheelMode}
onChange={(newValue: string) => { graphInstance.setOptions({ wheelEventAction: newValue }); }}
/>
What Makes This Example Distinct
Based on the prepared doc-context, the distinctive part is not the shared floating window or the provider-and-hook setup, because nearby examples such as deep-each, investment, and selections also reuse that scaffolding. The rarer combination is the custom MyEditableNode renderer plus mode-gated double-click editing, automatic focus and text selection, Enter-or-blur commit, Escape cancel, and persistence through updateNode() on a fixed-layout graph.
The rarity records also mark the yellow inline editor state, the dotted editing canvas, and the fixed-position style-sample nodes as unusual in this example set. That makes this demo a more direct starting point for node-label maintenance than examples that emphasize selection, traversal, resizing, or broader editor behaviors.
It is also narrower than a full graph editor. The code does not add nodes, reconnect lines, or manage multi-step editing state. Its value is that it isolates one concrete capability: replace default node content with an inline editor and keep the persistence path small.
Where Else This Pattern Applies
This pattern transfers well to diagrams where the structure is already known but labels need quick correction in context. Examples include workflow naming tools, process maps, organization charts, equipment maps, topology labels, and knowledge-graph curation screens.
A production version could keep the same slot-based editing strategy while replacing the inline sample data with server data, adding validation rules before updateNode(), storing audit history, or opening richer side panels for fields beyond the node text. The core idea still holds: let the graph stay loaded and edit only the label that changed.