Industry Chain Group Layout Editor
This example builds an editable industry value chain board where HTML stage cards anchor several relation-graph tree groups on one fixed canvas. Users can select a whole group, adjust node, line, and tree-layout settings, insert a new random tree at a chosen row position, and trigger dependency-aware relayout as the structure changes.
Editable Tree Groups on an Industry Value Chain Board
What This Example Builds
This example builds a three-stage industry value chain board where each row in the visible cards anchors a separate relation-graph tree. Users see an upstream, midstream, and downstream business canvas, while the actual graph nodes expand outward from those card rows instead of replacing them.
The experience is part viewer and part lightweight editor. A user can click any visible graph node to select its whole group, drag that selected group with alignment guides, adjust group-level node, line, and tree-layout options in a floating panel, delete the selected group, or insert a newly generated tree before or after a specific stage-card row. The most important idea is that ordinary HTML business cards remain the visible backbone of the page while relation-graph renders editable tree clusters around them.
How the Data Is Organized
The source model starts as three columns of nested JsonNode trees in my-data.ts. getMyAllColumnsData() returns the shipped three-column scene with c1, c2, and c3, each carrying display metadata such as title, subtitle, icon, and theme colors alongside the tree roots for that column.
Before any layout runs, the example preprocesses those nested trees into group records. buildMyGroup() and flatNodeData() in MyMixLayout.ts flatten each tree into graph nodes and lines, assign a shared myGroupId, annotate each node with hasChildren and deep, and turn the real root into a tiny hidden anchor node. That preprocessing matters because later interactions operate on whole groups rather than on a single clicked node.
The same tree shape is reused for insertion. When a user chooses an insertion point, the floating panel generates another nested JsonNode tree with generateRandomTreeData() and splices it into the chosen column before the graph is rebuilt. In a real application, those trees could come from product capability maps, process families, organization clusters, or staged dependency models instead of from the bundled UAV industry sample.
How relation-graph Is Used
The outer RelationGraph runs in fixed layout mode, and the example treats it as a host canvas for several independent tree layouts rather than as one automatically arranged whole graph. In MyGraph.tsx, the graph options set layoutName: 'fixed', rectangular nodes, simple orthogonal lines, and a default left-right junction strategy. The actual tree placement is delegated to MyMixLayout, which creates a separate tree layout per group at runtime with graphInstance.createLayout(...) and locks the root position with fixedRootNode = true.
Slots do most of the composition work. RGSlotOnCanvas renders the large HTML stage board from MyCanvasContent4Cols.tsx. Each stage-card row exposes an RGConnectTarget, and connectElementToNode() then adds fake orthogonal bridge lines from those DOM anchor points to the hidden group roots. RGSlotOnNode reduces graph nodes to compact text chips, while RGSlotOnView adds RGMiniView, RGEditingNodeController, and RGEditingReferenceLine as workspace overlays.
The graph instance API is used heavily. The example clears and reloads graph data with clearGraph, addNodes, and addLines; bridges DOM anchors with addFakeLines; selects whole groups with setEditingNodes; updates style changes with updateNode and updateLine; computes dependency-aware stacking with getConnectTargetById and getNodesRectBox; inserts new groups with generateNewUUID; and exports the canvas through prepareForImageGeneration and restoreAfterImageGeneration inside the draggable settings window.
Styling is layered in two places. Group defaults are stored per group and re-applied through applyGroupStyles(), while my-relation-graph.scss provides project-level overrides for graph node text and column-themed node variants. The floating editor itself is a reusable tabbed component imported from the neighboring mix-layout-8 example, but here it is deliberately restricted to tree-oriented layout tuning with treeLayoutOnly={true}.
Key Interactions
Clicking any visible node selects the entire group that shares the same myGroupId. That selection is then handed to RGEditingNodeController, so the group can be moved as a unit and can expose a delete action above the selection box.
Blank-canvas clicks clear the editing state. This is important because the page uses selection as the entry point to all editing, and clearing selection also closes the group-scoped editing context.
Expanding or collapsing a branch triggers dependency-aware relayout. The changed group is laid out again, then any groups whose placement depends on that group’s bounds are recursively laid out afterward. This is what keeps the business board coherent even when one cluster becomes taller or shorter.
Hovering a stage-card row reveals before-or-after insertion hotspots. Choosing one of those positions switches the floating panel into add-group mode, where users can tune random tree depth, child count, and child-probability settings before inserting the new group into that exact column position.
The settings button on the draggable window opens canvas-level controls rather than group-level controls. From there, users can switch wheel and drag behavior and export a screenshot of the current composed board.
Key Code Fragments
This fragment shows that the example rebuilds the graph from column data, waits for the canvas DOM, then reconnects HTML anchors before running group layouts.
const applyAllColsData = async (newCols: MyColumnData[]) => {
if (newCols.length === 0) return;
await myLayout.current.loadData(newCols);
await graphInstance.sleep(300);
myLayout.current.connectElementToNode();
myLayout.current.applyAllGroupLayout();
graphInstance.zoomToFit();
};
This fragment shows how the HTML stage cards expose RGConnectTarget anchor points for bridge lines.
<RGConnectTarget
targetId={'col-item-ct-' + item.id}
junctionPoint={RGJunctionPoint.lr}
disableDrag={true}
disableDrop={true}
>
<div className={`w-1.5 h-1.5 rounded-full ${themeColor.dot}`} />
</RGConnectTarget>
This fragment shows how the layout manager converts those anchor points into fake orthogonal lines that visually connect each card row to its hidden root node.
const fakeLine: JsonLine = {
id: 'ct-line-' + group.rootNodeId,
fromType: RGInnerConnectTargetType.CanvasPoint,
from: connectTargetId,
toType: RGInnerConnectTargetType.Node,
to: group.rootNodeId,
lineShape: RGLineShape.SimpleOrthogonal,
color: group.groupStyles.lineStyles.color || 'rgba(0,0,0,0.1)',
showEndArrow: false
};
This fragment shows the dependency-aware vertical placement logic that stacks one group relative to another group’s visible bounds.
if (group.refs.length > 0) {
const refedGroup = this.getGroupById(group.refs[0]);
if (!group.layouted) this.layoutGroup(refedGroup, deep + 1);
const refedGroupView = this.getGroupViewInfo(refedGroup.groupId);
if (refedGroup.groupStyles.layoutOptions.layoutExpansionDirection === 'start') {
groupRootNodeXy.y = refedGroupView.minY - 60;
}
if (refedGroup.groupStyles.layoutOptions.layoutExpansionDirection === 'end') {
groupRootNodeXy.y = refedGroupView.maxY + 45;
}
}
This fragment shows that clicking one node escalates immediately into whole-group editing rather than single-node inspection.
const onNodeClick = (node: RGNode) => {
myLayout.current.selectGroupNodes(node.data?.myGroupId);
if (editingGroupStyles.myGroupId !== node.data?.myGroupId) {
openEditGroupStylesPanel();
}
};
What Makes This Example Distinct
Compared with nearby mixed-layout viewers such as mix-layout-2, this example turns the same card-anchored composition pattern into an authoring workspace. It does not stop at expansion-depth changes or passive relayout; it lets users select a whole anchored cluster, tune node, line, and tree settings for that group, delete the group, or insert a new random tree at an explicit row position.
Compared with grouped-layout presentation examples such as organizational-chart and mix-layout-tree, the visible anchors here are ordinary HTML stage cards rendered in RGSlotOnCanvas, not only graph-rendered node cards or shared-root hierarchy clusters. That makes the example a stronger starting point when a graph must live inside an existing dashboard or business-board shell.
Compared with focused interaction demos like gee-node-alignment-guides, the alignment overlay is only one part of a larger workflow. Here it sits inside a dependency-aware board editor that also handles DOM-anchor composition, batch style tuning, runtime group lifecycle changes, and recursive relayout when group size changes.
The rare combination is the real lesson: RGSlotOnCanvas stage cards, RGConnectTarget anchor dots, hidden root nodes, fake bridge lines, per-group tree layout creation, refs-driven dependency placement, whole-group editing, insertion hotspots, and editor overlays all work together in one fixed canvas. That combination is why this example reads as a business-board graph workbench instead of a static mixed-layout showcase.
Where Else This Pattern Applies
This pattern transfers well to any interface where a graph should extend from visible business cards instead of replacing them. Product pipeline boards, platform capability maps, supplier networks, staged risk controls, and service-delivery chains all benefit from the same separation between HTML anchor cards and graph-rendered dependency detail.
It is also a good fit for hybrid editing tools where users should manipulate clusters rather than author arbitrary nodes and edges across the whole canvas. Team structure groups, modular system bundles, or portfolio workstreams can reuse the same group-level selection, tabbed restyling, and dependency-aware relayout model.
Finally, the insertion flow is useful whenever new tree-shaped substructures must be added into a precise stage position. That includes adding a new supplier tier, inserting a new process family, attaching a new solution package, or testing alternative dependency branches without rebuilding the whole scene by hand.