Selected Node Directional Line Creation
This example shows a focused graph authoring pattern where selecting one node reveals directional chips for creating new outgoing connections. It uses relation-graph's editing state, around-node overlay rendering, and interactive line creation flow to save new edges without building a full diagram editor shell.
Directional Line Creation from a Selected Node
What This Example Builds
This example builds a lightweight graph editing workspace where one selected node becomes the source of new outgoing connections. The page starts with a small seeded graph, hides the built-in toolbar, and adds its own floating helper window plus an around-node toolbar.
Users can click a node, use modifier keys to adjust the editing selection, drag a selection box, and then launch a new connection from small chips placed above, below, left, or right of the selected node. The main point of the example is not generic node actions, but a focused pattern for starting relation-graph’s interactive line-creation flow from contextual controls attached to exactly one node.
How the Data Is Organized
The graph data is declared inline as a single RGJsonData object with a rootId, a flat nodes array, and a flat lines array. Each line only needs id, from, and to, which keeps the seed dataset small and easy to replace with business entities such as people, services, workflow steps, or document dependencies.
There is no preprocessing pipeline before setJsonData(...). Initialization happens in a mount-time effect: the example creates the JSON object, loads it into the graph instance, and then calls zoomToFit(). New connections are not stored in separate editor state either; they are appended directly into the graph through addLines(...) after the interactive drag completes and the target exposes an id.
How relation-graph Is Used
The entry component wraps the page in RGProvider, and MyGraph reads the provider-scoped instance through RGHooks.useGraphInstance(). That instance is responsible for initial data loading, viewport fitting, editing-state changes, launching the connection gesture, generating ids, appending new lines, clearing checked state, and updating runtime options from the floating settings panel.
The example does not define a custom layout object. Instead, it relies on the runtime’s default handling of the seeded graph data and immediately fits the result to the viewport after loading.
RelationGraph is configured with showToolBar: false, so all interaction scaffolding is custom. The component registers handlers for node clicks, line clicks, canvas clicks, and selection-box completion. Those handlers keep editing state synchronized across different user gestures.
The overlay pattern is the important relation-graph technique here. RGSlotOnView mounts RGEditingNodeController, and inside that controller the custom MyNodeToolbar reads RGHooks.useEditingNodes(). Because the toolbar checks for exactly one editing node, the quick-create chips only appear when there is a single valid source node.
The toolbar actions do not draw lines manually. Each chip passes a preset JsonLineLike template into startCreatingLinePlot(...), including color, width, line shape, and fromJunctionPoint. That lets relation-graph manage the guided drag-to-connect interaction while the example controls where the line starts and how it looks.
The floating DraggableWindow is secondary to the authoring flow, but it still shows another useful integration point. Its settings panel reads useGraphStore() to reflect current wheel and drag modes, updates them through setOptions(...), and uses prepareForImageGeneration() plus restoreAfterImageGeneration() to export the graph as an image.
Key Interactions
- Clicking a node without modifiers replaces the editing-node set with that node and clears the active editing line.
- Clicking a node with
Shift,Ctrl, orMetatoggles that node in the editing selection instead of replacing the whole set. - Dragging a selection box ends with
getNodesInSelectionView(...), and the resolved nodes become the new editing selection. - Clicking empty canvas clears editing nodes, clears the active editing line, and removes checked state.
- Clicking a line marks that line as the current editing line, which makes the line-selection styling meaningful even though this example does not expose line-path editing controls.
- When exactly one node is selected, the around-node chips appear and launch
startCreatingLinePlot(...)with directional presets. - A new edge is only persisted when the drag completes on a target with an
id; the callback then generates a new line id and appends the line throughaddLines(...). - The floating helper window can switch wheel and drag behavior at runtime and can export the current graph view as an image.
Key Code Fragments
This fragment shows that the example uses an inline seed dataset and immediately fits the graph after loading it.
const initializeGraph = async () => {
const myJsonData: RGJsonData = {
rootId: 'a',
nodes: [
{ id: 'a', text: 'Border color' },
// ...
],
lines: [
{ id: 'l1', from: 'a', to: 'b' },
// ...
]
};
await graphInstance.setJsonData(myJsonData);
graphInstance.zoomToFit();
};
This fragment shows how node clicks, selection-box completion, and canvas clicks all feed the same editing-state model.
const onNodeClick = (nodeObject: RGNode, $event: RGUserEvent) => {
if ($event.shiftKey || $event.ctrlKey || ($event.metaKey && !$event.altKey)) {
graphInstance.toggleEditingNode(nodeObject);
} else {
graphInstance.setEditingNodes([nodeObject]);
}
graphInstance.setEditingLine(null);
};
const onCanvasSelectionEnd = (selectionView: RGSelectionView) => {
const willSelectedNodes = graphInstance.getNodesInSelectionView(selectionView) || [];
graphInstance.setEditingNodes(willSelectedNodes);
};
This fragment shows that the toolbar starts relation-graph’s built-in line creation flow and only saves the line when the drop target is a real node.
graphInstance.startCreatingLinePlot(e.nativeEvent, {
template: lineTemplate,
fromNode: fromNode,
onCreateLine: (fromNode, toNode, newLineJson) => {
if (toNode.id) {
const newLineId = graphInstance.generateNewUUID(8);
const newLineJsonData = Object.assign({}, newLineJson, {
from: fromNode.id,
to: toNode.id,
text: 'New Line ' + newLineId
});
graphInstance.addLines([newLineJsonData]);
}
}
});
This fragment shows how the around-node toolbar is gated to a single editing node and injects source-side junction-point presets.
const editingNodes = RGHooks.useEditingNodes();
const onlyOneNodeBeSelected = editingNodes.nodes.length === 1;
return onlyOneNodeBeSelected ? (
<div
style={{ backgroundColor: '#e85f84' }}
onClick={(e) => {
onStartCreateLine(editingNodes.nodes[0], {
lineWidth: 3,
color: '#e85f84',
fromJunctionPoint: RGJunctionPoint.top,
lineShape: RGLineShape.StandardCurve,
text: 'New Line'
}, e);
}}
>T1</div>
) : null;
This fragment shows the visual customization that turns the graph into an editor-like canvas and makes checked line labels inherit the line color.
.relation-graph {
--rg-canvas-scale: 1;
--rg-canvas-offset-x: 0px;
--rg-canvas-offset-y: 0px;
background-position: var(--rg-canvas-offset-x) var(--rg-canvas-offset-y);
background-size: calc(var(--rg-canvas-scale) * 15px) calc(var(--rg-canvas-scale) * 15px);
background-image: radial-gradient(circle, rgb(197, 197, 197) calc(var(--rg-canvas-scale) * 1px), transparent 0);
.rg-line-peel.rg-line-checked .rg-line-label {
background-color: var(--rg-line-color);
color: #fff;
}
}
What Makes This Example Distinct
Its main distinction is how narrowly it isolates contextual edge authoring. Compared with line-vertex-on-node, this example stays on the source side: it gives the user directional presets for the selected node, but it does not add extra UI for choosing the target attachment point. That makes it a smaller reference when the requirement is simply “select one node and create an outgoing connection from a predictable side.”
Compared with custom-node-quick-actions, the around-node controls are not placeholders or generic shortcuts. Each chip launches a real graph mutation workflow and persists the completed connection with generateNewUUID(...) plus addLines(...). The toolbar is also stricter than many overlay demos because it disappears unless exactly one node is selected.
Compared with broader editor examples such as editor-button-on-line, the reusable value here is restraint. It combines a dotted editing canvas, a floating utility window, synchronized editing state across node, line, canvas, and box-selection events, and a single-node line launcher without expanding into a full diagram editor with node creation, resize handles, custom line slots, or inline deletion tools.
Where Else This Pattern Applies
This pattern transfers well to workflow builders where each step should expose approved outgoing transitions from specific sides of the node.
It also fits dependency mapping tools, service-topology editors, and knowledge-graph curation screens where users need a fast “connect from selected item” action without opening a separate creation dialog.
In internal admin tools, the same approach can be used for organization charts, approval routing, or data lineage views when connection styling should be constrained by a small set of presets rather than left fully free-form.