[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:en:folder-layout-line-offset":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# Folder Layout with Runtime Connector Offset Control\n\n## What This Example Builds\n\nThis example builds a left-to-right file-hierarchy viewer on top of relation-graph's built-in `folder` layout. The rendered result looks like a compact folder tree: parent nodes are shown as folder-style rows, connectors use muted orthogonal routing, and expand holders appear on the right side of each branch.\n\nUsers can explore the hierarchy by expanding and collapsing branches, and they can adjust one routing parameter at runtime from a floating control window. The most instructive part of the example is that the slider does not rebuild the graph data; instead, it updates the already rendered lines so every connector leaves its source node with a different horizontal offset.\n\n## How the Data Is Organized\n\nThe source data starts as a nested tree in `data.ts`, where each item has an `id`, `text`, and optional `children`. Before anything is passed into relation-graph, the example converts that tree into explicit `RGJsonData` arrays with `nodes` and `lines`.\n\nThat preprocessing step matters because the example needs direct control over each connector. Once the data is flattened, every parent-child relationship becomes its own line record, which makes it possible to assign line ids, set explicit junction points, and later update the live connectors in one batch. In a real application, the same structure could represent a filesystem, a document outline, a category hierarchy, a menu tree, or any nested ownership structure that benefits from folder-style presentation.\n\n## How relation-graph Is Used\n\nThe graph instance is provided through `RGProvider`, and `MyGraph` uses `RGHooks.useGraphInstance()` to load data, center the viewport, fit the result, and update live lines after mount. The graph options focus on the `folder` layout: `layoutName: 'folder'`, `from: 'left'`, `treeNodeGapH: 20`, and `treeNodeGapV: 10` define a compact horizontal hierarchy.\n\nThe visual defaults are tuned to reinforce the file-tree reading. Nodes are rectangular at `100x30`, lines use `RGLineShape.StandardOrthogonal`, corners are rounded with `defaultPolyLineRadius: 4`, the default line color is `#aaaaaa`, and expand holders are placed on the right. The example also enables `reLayoutWhenExpandedOrCollapsed`, so branch expansion stays aligned with the folder layout instead of leaving stale spacing behind.\n\n`RGSlotOnNode` is the main rendering customization. The slot checks `node.rgChildrenSize` and gives parent nodes a folder icon plus a light gray rounded background, while leaves only reserve empty icon space. That keeps the graph visually narrow and avoids turning the example into a card-style hierarchy demo.\n\nThe floating `DraggableWindow` is shared demo scaffolding, but it still shows practical relation-graph integration. Its settings panel reads current graph options from the graph store, updates `wheelEventAction` and `dragEventAction` through `setOptions(...)`, and exports the current canvas through `prepareForImageGeneration()`, `getOptions()`, and `restoreAfterImageGeneration()`.\n\n## Key Interactions\n\nThe primary interaction is the range slider in the floating window. It controls `fromOffsetX`, and a dependent effect iterates over the current lines with `getLines()` and reapplies `fromJunctionPointOffsetX` through `updateLine(...)`. That makes the routing change visible immediately across the whole graph.\n\nBranch exploration is the second important interaction. Because the graph uses the `folder` layout, places expand holders on the right, and enables relayout after expand or collapse, users can inspect deeper levels without losing the hierarchical shape.\n\nThe floating window itself is also interactive: it can be dragged, minimized, switched into a settings panel, and used to export the graph image. Those controls are inherited from shared demo utilities rather than unique to this example, but they do affect how the example is explored.\n\n## Key Code Fragments\n\nThis fragment shows that the example deliberately converts nested tree data into flat `nodes` and `lines` before loading the graph.\n\n```ts\nconst myJsonData: RGJsonData = {\n    rootId: 'a',\n    nodes: [],\n    lines: []\n};\n\nflattenTreeData(rootNodeJson, null, myJsonData.nodes, myJsonData.lines);\nreturn myJsonData;\n```\n\nThis recursive helper is the proof that every parent-child edge becomes an explicit line record.\n\n```ts\ntreeNodes.forEach((nodeData) => {\n    const node: JsonNode = {\n        id: nodeData.id,\n        text: nodeData.text\n    };\n    nodes.push(node);\n    if (parent) {\n        const line: JsonLine = {\n            from: parent.id,\n            to: nodeData.id\n        };\n        lines.push(line);\n    }\n```\n\nThis options block shows the example is built around relation-graph's `folder` layout rather than a generic tree preset.\n\n```ts\nconst graphOptions: RGOptions = {\n    debug: false,\n    layout: {\n        layoutName: 'folder',\n        from: 'left',\n        treeNodeGapH: 20,\n        treeNodeGapV: 10\n    },\n    defaultNodeShape: RGNodeShape.rect,\n    defaultNodeWidth: 100,\n    defaultNodeHeight: 30,\n```\n\nThis initialization step proves that line routing is normalized before `setJsonData(...)` is called.\n\n```ts\nmyJsonData.lines.forEach((line, index) => {\n    if (!line.id) {\n        line.id = `l${index + 1}`;\n    }\n    line.fromJunctionPoint = RGJunctionPoint.bottom;\n    line.fromJunctionPointOffsetX = fromOffsetX;\n    line.toJunctionPoint = RGJunctionPoint.left;\n});\nawait graphInstance.setJsonData(myJsonData);\n```\n\nThis update path is the core runtime technique: it changes already rendered connectors instead of rebuilding the dataset.\n\n```ts\nconst updateMyData = () => {\n    graphInstance.getLines().forEach(line => {\n        graphInstance.updateLine(line, {\n            fromJunctionPointOffsetX: fromOffsetX\n        });\n    });\n};\n```\n\nThis node slot is what makes expandable parents read like folders instead of plain rectangles.\n\n```tsx\n\u003CRGSlotOnNode>\n    {({ node }: RGNodeSlotProps) => {\n        return (\n            \u003Cdiv className={`w-full h-full flex place-items-center gap-2 p-2 ${node.rgChildrenSize > 0 ? 'bg-gray-100 rounded' : ''}`}>\n                {\n                    node.rgChildrenSize > 0\n                        ? \u003CFolderOpen className=\"w-4 h-4 text-blue-700\" />\n                        : \u003Cdiv className=\"w-4 h-4 \">\u003C/div>\n                }\n```\n\n## What Makes This Example Distinct\n\nAccording to the comparison data, this example is not simply another hierarchy viewer. Its distinctive combination is a folder-specific layout, recursive tree-to-`RGJsonData` preprocessing, pre-load junction normalization, slot-based folder rendering for expandable parents, and one graph-wide slider that updates connector start offsets after mount.\n\nThat makes it different from nearby examples in specific ways. Compared with `layout-folder2`, this example is more focused on file-structure semantics and one reusable routing control than on richer card-like node presentation or spacing relayout controls. Compared with `tree-data`, it goes further by flattening the source tree into explicit lines, which is what enables both pre-load routing setup and later batch updates on live connectors. Compared with `io-tree-layout` or `layout-tree`, the emphasis is not on switching presets or orientations; it is on keeping one stable folder-viewer configuration while tuning how all connectors leave their source nodes.\n\nThe comparison data also warns against overstating its uniqueness. It is not the only example that uses the `folder` layout, and the floating utility window, canvas settings, and image export come from shared demo scaffolding. What stands out here is the focused combination of folder-style presentation and connector-offset control inside a viewer-oriented example.\n\n## Where Else This Pattern Applies\n\nThis pattern transfers well to product areas that need a readable nested structure without opening a full graph editor. Typical candidates include cloud storage browsers, permission inheritance trees, product category trees, document or page outlines, build artifact folders, and code ownership hierarchies.\n\nThe preprocessing pattern is also reusable when backend data arrives as nested objects but the UI needs line-level routing control after render. Flattening the tree into explicit nodes and lines gives later features a clean extension point for connector geometry, highlighting, or conditional line updates without changing the original domain shape.\n",false,500,1782615388054]