[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:en:system-architecture-diagram-editor":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# System Architecture Diagram Editor with Group Repacking\n\n## What This Example Builds\n\nThis example builds a layered system architecture board that users can adjust directly on the canvas. The visible result is a stack of nested section containers with left-rail titles, depth-based background colors, and white leaf cards, while the structural parent-child lines stay hidden.\n\nUsers can switch visible depth presets, toggle the minimap, show or hide expand buttons, change the level color palette, open a shared canvas settings panel, export an image, select one node, select an entire subtree, rename nodes inline, resize selected groups, refit group boxes, relayout child items, and append a new child item.\n\nThe main implementation highlight is that the example does not rely on a stock automatic layout alone. It loads fixed-position hierarchy data into relation-graph, waits for hierarchy metadata such as `node.lot.level` and `node.lot.childs`, then runs a custom packing and refit pass so the graph reads like an architecture workbench instead of a normal node-link diagram.\n\n## How the Data Is Organized\n\nThe shipped data source is a hardcoded nested tree named `aiOutputJson` in `ai-json-data.ts`. Each item carries `text`, `x`, `y`, and optional `children`, so the source structure is already hierarchical and already contains approximate placement hints.\n\n`parseJsonDataByAI()` converts that tree into `RGJsonData` by recursively flattening every item into a node, generating an id, preserving the original coordinates, storing `hasChildren` and `deep` in `node.data`, and creating one parent-child line for each non-root relationship. The graph still loads those lines because the hierarchy logic depends on them, but the example hides them before and after load so the presentation stays box-oriented.\n\nThere are also two important preprocessing consequences after `setJsonData()`. First, relation-graph generates the `node.lot` hierarchy metadata that the rest of the example depends on. Second, the custom actions layer can reopen the graph to a chosen depth, repack each group's children, and resize ancestor containers around descendant bounds.\n\nIn a real product, the same data shape could represent a platform architecture, capability map, layered product modules, enterprise system landscape, service taxonomy, or any other prepared hierarchy where grouped sections matter more than visible connectors.\n\n## How relation-graph Is Used\n\nThe entry component wraps the demo with `RGProvider`, and `MyGraph` uses `RGHooks.useGraphInstance()` as the main imperative control surface. The initial `RGOptions` configure `layout.layoutName = 'fixed'`, rectangular nodes, zero default node border, curved lines, border junction points, left-side expand holders, relayout on expand or collapse, wheel scrolling, canvas dragging, and debug mode.\n\nInitialization uses relation-graph as a staged loader. The example calls `loading()`, `setJsonData()`, `moveToCenter()`, `zoomToFit()`, and `clearLoading()`, while `MyGraphActions` calls `doLayout()`, `updateNodesVisibleProperty()`, `getNodesRectBox()`, `updateNodeData()`, `updateNode()`, `addNodes()`, `addLines()`, `generateNewNodeId()`, `getDescendantNodes()`, and `updateEditingControllerView()` to keep the editable board coherent.\n\nSlots do most of the visual adaptation. `RGSlotOnNode` replaces the default graph node with either a leaf card or a grouped container, both rendered through `MyEditableNode`. `RGSlotOnView` mounts `RGMiniView`, `RGEditingReferenceLine`, `RGEditingNodeController`, `RGEditingResize`, and the custom `MyNodeToolbar`, so editing affordances live in the graph overlay layer instead of in a side form.\n\nThe shared `DraggableWindow` component adds the presentation controls around the graph. It exposes depth presets, minimap and expand-button toggles, color palette changes, wheel-mode and drag-mode switches through `setOptions(...)`, and image export through `prepareForImageGeneration()` and `restoreAfterImageGeneration()`.\n\nStyling is also part of the relation-graph usage story. The SCSS restyles expand buttons with `var(--rg-node-color)`, gives leaf nodes a bordered card appearance, makes grouped nodes look like padded containers, and moves the top-level and level-1 labels into a left-rail title treatment, including a large vertical root label.\n\n## Key Interactions\n\n- Single-clicking a node makes it the active editing selection through `setEditingNodes([node])`.\n- Double-clicking a leaf wrapper or group wrapper selects that node together with all descendants through `getDescendantNodes(...)`.\n- Double-clicking the text inside `MyEditableNode` enters inline editing; blur or Enter saves with `updateNode(...)`, and Escape cancels.\n- Clicking the canvas clears both checked state and the current editing selection.\n- The depth preset selector reruns `openByLevel(...)`, the custom layout pass, and `zoomToFit()` so the same board can be inspected at different hierarchy depths.\n- The floating controls can show or hide `RGMiniView`, switch expand-holder visibility, and recolor the board by changing the level palette.\n- The shared settings panel can switch wheel mode, switch canvas drag mode, and download the current graph as an image.\n- When one selected node finishes resizing, `onResizeEnd` triggers a child relayout for that node.\n- When a selected node has children, the overlay toolbar exposes three direct actions: relayout child items, fit the container around descendants, and append a new child item.\n\n## Key Code Fragments\n\nThis recursive helper proves that the shipped source starts from a nested tree and converts it into flat graph nodes and lines before loading relation-graph.\n\n```ts\nconst nodeJson = {\n    id: 'n-' + nodes_collect.length,\n    text: item.text,\n    x: item.x,\n    y: item.y,\n    data: { hasChildren, deep }\n};\nnodes_collect.push(nodeJson);\nif (parentNode) {\n    links_collect.push({ id: `${parentNode.id}-to-${nodeJson.id}`, from: parentNode.id, to: nodeJson.id });\n}\n```\n\nThis initialization flow hides structural connectors, loads the prepared graph, then applies depth expansion, custom layout, and viewport fitting.\n\n```ts\nconst initializeGraph = async () => {\n    const myJsonData: RGJsonData = await parseJsonDataByAI();\n    myJsonData.lines.forEach(line => {\n        line.hidden = true;\n    });\n    graphInstance.loading();\n    await graphInstance.setJsonData(myJsonData);\n    updateGraphStyles();\n    await myGraphActions.current.openByLevel(8);\n    await myGraphActions.current.doMyLayout();\n    graphInstance.moveToCenter();\n    graphInstance.zoomToFit();\n};\n```\n\nThis custom layout pass repacks each non-leaf group and then normalizes the width of the level-1 sections.\n\n```ts\nfor (const node of allNodes) {\n    if (node.rgChildrenSize > 0) {\n        const childrenNodes = node.lot.childs;\n        const nodesWithNewXy = adjustNodeLayout({\n            nodes: childrenNodes,\n            width: node.lot.level \u003C 2 ? 1600 : 200,\n            gap: 20\n        });\n        nodesWithNewXy.forEach(newXy => {\n            const cnode = childrenNodes.find(n => n.id === newXy.nodeId);\n            if (cnode) this.moveNodeTo(cnode, newXy.newX, newXy.newY);\n        });\n        this.updateNodeSizeByChildrenSize(node);\n    }\n}\n```\n\nThis refit step shows how a group's bounding box is recomputed from descendant bounds and then propagated upward to ancestor containers.\n\n```ts\nconst childrenNodesSize = graphInstance.getNodesRectBox(childrenNodes);\nconst padding = 10;\nconst leftTitleWidth = node.lot.level \u003C= 1 ? 250 : 0;\nconst titleHeight = leftTitleWidth > 0 ? 0 : 30;\ngraphInstance.updateNode(node, {\n    x: childrenNodesSize.minX - padding - leftTitleWidth,\n    y: childrenNodesSize.minY - padding - titleHeight,\n    width: Math.max(groupMinWidth, childrenNodesSize.width + padding * 2) + leftTitleWidth,\n    height: childrenNodesSize.height + padding * 2 + titleHeight\n});\n```\n\nThis edit handler shows that inline renaming is a real implemented behavior, not just a visual input mockup.\n\n```tsx\nconst handleKeyDown = (e: React.KeyboardEvent) => {\n    if (e.key === 'Enter') {\n        finishEditing();\n    } else if (e.key === 'Escape') {\n        setEditingText(node.text || '');\n        setIsEditing(false);\n    }\n};\nconst finishEditing = () => {\n    if (editingText !== node.text) {\n        onNodeTextChange(node, editingText);\n    }\n    setIsEditing(false);\n};\n```\n\nThis view slot is where the example attaches the minimap, snap reference lines, resize handles, and the group-action toolbar.\n\n```tsx\n\u003CRGSlotOnView>\n    {showMiniView && \u003CRGMiniView />}\n    \u003CRGEditingReferenceLine adsorption={true} />\n    \u003CRGEditingNodeController>\n        \u003CMyNodeToolbar\n            onLayoutItemsButtonClick={onLayoutItemsButtonClick}\n            onFitContentButtonClick={onFitContentButtonClick}\n            onAddChildrenButtonClick={onAddChildrenButtonClick}\n        />\n        \u003CRGEditingResize />\n    \u003C/RGEditingNodeController>\n\u003C/RGSlotOnView>\n```\n\n## What Makes This Example Distinct\n\nAccording to the comparison data, the closest viewer-style neighbor is `system-architecture-diagram`. Both examples share the same hidden-link architecture-board foundation, fixed-layout hierarchy loading, depth-based coloring, left-rail titles, and post-layout container packing. The difference is that this example adds the editing overlay layer: inline renaming, resize handles, snap reference lines, subtree selection, fit-content refit, and add-child actions on top of the same grouped visual language.\n\nCompared with editor-oriented neighbors such as `undo-redo-example` and `freely-draw-lines-on-canvas`, this example is narrower and more scene-specific. It does not broaden into history management, generic line authoring, or freehand creation. Instead, it keeps one prepared layered hierarchy and focuses on architecture-aware mutations such as repacking descendants, refitting ancestor boxes, and extending a selected section with one more child item.\n\nCompared with `industry-chain`, the reusable lesson is also different. Both examples support depth-based inspection and a floating workbench shell, but `industry-chain` is primarily a read-only hierarchy viewer. This example goes further by combining depth presets with direct manipulation on grouped containers while still keeping the screen visually organized through hidden connectors.\n\nThe distinctive combination is therefore not any one feature in isolation. It is the combination of fixed-position hierarchy flattening, hidden-link grouped presentation, custom post-layout repacking, recursive group refit, inline text editing, subtree multi-selection, resize-driven relayout, and lightweight runtime controls on the same canvas.\n\n## Where Else This Pattern Applies\n\nThis pattern transfers well to semi-editable capability maps, platform architecture boards, layered product inventories, enterprise application landscapes, and operating model diagrams where the hierarchy is known in advance but teams still need limited in-canvas maintenance.\n\nIt also fits internal planning tools for AI pipelines, service domains, module ownership maps, compliance control stacks, and technical workbench screens where grouped sections should remain readable without constant visible connectors. The main reusable idea is to keep real structural graph data for logic and editing, while presenting the result as nested architecture panels rather than as a conventional edge-heavy graph.\n",false,500,1782616038041]