[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:en:tree-layout-level-and-node-spacing":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# Compare Tree Spacing with Sliders and Per-Level Gaps\n\n## What This Example Builds\n\nThis example builds a left-to-right tree-layout playground for spacing control. The canvas shows one fixed hierarchy with rectangular nodes and orthogonal connectors, while a floating utility window lets the user switch between two spacing strategies on the same graph.\n\nIn the first mode, the user changes horizontal level distance and vertical sibling distance with range inputs. In the second mode, the user enters a comma-separated `levelGaps` array to assign different distances to different depths. The most important point is not the sample data itself, but the side-by-side comparison between uniform spacing controls and per-level spacing controls on a live relation-graph instance.\n\n## How the Data Is Organized\n\nThe graph data is declared inline as one `RGJsonData` object with `rootId`, a flat `nodes` array, and a flat `lines` array. The hierarchy is therefore expressed explicitly through edge records rather than nested `children` objects.\n\nThere is no preprocessing before the initial `setJsonData()` call. The only later transformation happens in the second control mode, where the text input is split on commas, trimmed, and converted into numbers before being reused as `layout.levelGaps`.\n\nIn a real application, the same structure could represent reporting lines, dependency trees, category hierarchies, approval chains, or any directional tree where spacing at each depth needs to be tuned separately.\n\n## How relation-graph Is Used\n\nThe entry component wraps the demo in `RGProvider`, and `MyGraph.tsx` accesses the live graph through `RGHooks.useGraphInstance()`. The base options configure the built-in `tree` layout with `from: 'left'`, `treeNodeGapH: 400`, and `treeNodeGapV: 70`, then keep the visuals intentionally plain with rectangular nodes, gray orthogonal lines, left-right junction points, right-side expand holders, and rounded polyline corners.\n\nThe instance API drives the actual lesson. On mount, the component calls `setJsonData()`, then `moveToCenter()` and `zoomToFit()`. When the active mode or spacing state changes, the component rewrites `layout` through `setOptions({ layout })`, runs `doLayout()`, and recenters plus fits the viewport again. In case 1 it updates `treeNodeGapH` and `treeNodeGapV` while clearing `levelGaps`. In case 2 it applies `levelGaps` and keeps `treeNodeGapV` for sibling spacing.\n\nThere are no custom node, line, canvas, or viewport slots, and there is no graph editing workflow. The only style override in the local SCSS is a black node-text color, which keeps the tree readable while leaving the overall appearance close to relation-graph defaults. The floating helper window comes from a shared `DraggableWindow` component, which also exposes a secondary canvas-settings panel for wheel mode, drag mode, and image export.\n\n## Key Interactions\n\nThe primary interaction is the mode switch. `SimpleUISelect` toggles between a slider-based panel and a text-input panel, and each tab change immediately triggers the corresponding relayout logic for the already-loaded tree.\n\nWithin the first mode, changing either range input reruns the tree layout immediately, so the user can compare wider or tighter level spacing against sibling spacing in real time. Within the second mode, editing the comma-separated value list updates `levelGaps` and reruns layout again, which makes uneven depth spacing visible without rebuilding the dataset.\n\nThe floating window itself can be dragged, minimized, and switched into a settings overlay. That shared overlay can change `wheelEventAction`, change `dragEventAction`, and export the current graph as an image by calling `prepareForImageGeneration()` and `restoreAfterImageGeneration()` around a screenshot capture step.\n\n## Key Code Fragments\n\nThis options block proves that the demo starts from a left-to-right tree with explicit spacing defaults and minimal styling.\n\n```tsx\nconst baseOptions: RGOptions = {\n    debug: false,\n    layout: {\n        layoutName: 'tree',\n        from: 'left',\n        treeNodeGapH: 400,\n        treeNodeGapV: 70,\n    },\n    defaultExpandHolderPosition: 'right',\n    defaultNodeShape: RGNodeShape.rect,\n```\n\nThis inline payload shows that the example uses explicit nodes and lines instead of nested tree data.\n\n```tsx\nconst myJsonData: RGJsonData = {\n    rootId: 'a',\n    nodes: [\n        { id: 'a', text: 'a' },\n        { id: 'b', text: 'b' },\n        { id: 'b1', text: 'b1' },\n        { id: 'b1-1', text: 'b1-1' },\n        // ...\n    ],\n```\n\nThis relayout flow is the first spacing strategy: update `treeNodeGapH` and `treeNodeGapV`, clear `levelGaps`, then rerun the current layout.\n\n```tsx\nconst layoutOptions: RGLayoutOptions = {\n    layoutName: 'tree',\n    from: 'left',\n    treeNodeGapH: rangeHorizontal,\n    treeNodeGapV: rangeVertical,\n    levelGaps: []\n};\ngraphInstance.setOptions({ layout: layoutOptions });\nawait graphInstance.doLayout();\n```\n\nThis second panel shows how the example turns a text field into a per-level spacing array.\n\n```tsx\n\u003Cinput className=\"w-full border border-gray-900 p-1\"\n    value={levelGaps.join(',')}\n    placeholder=\"Please enter content\"\n    onChange={(e) => {\n        setLevelGaps(e.target.value.split(',').map(item => Number(item.trim())));\n    }}\n>\u003C/input>\n```\n\nThis shared settings code proves that the floating helper also controls canvas behavior and image export through graph-instance APIs.\n\n```tsx\nconst canvasDom = await graphInstance.prepareForImageGeneration();\nlet graphBackgroundColor = graphInstance.getOptions().backgroundColor;\nif (!graphBackgroundColor || graphBackgroundColor === 'transparent') {\n    graphBackgroundColor = '#ffffff';\n}\nconst imageBlob = await domToImageByModernScreenshot(canvasDom, {\n    backgroundColor: graphBackgroundColor\n});\nawait graphInstance.restoreAfterImageGeneration();\n```\n\n## What Makes This Example Distinct\n\nComparison data makes this example distinct because it packages two tree-spacing strategies into one compact demo on the same static hierarchy. Instead of only exposing one set of layout controls, it lets the user compare slider-driven `treeNodeGapH` and `treeNodeGapV` updates against parsed per-level `levelGaps`, with each change followed by `setOptions({ layout })`, `doLayout()`, recentering, and viewport fitting.\n\nCompared with `tree-data`, this example is not about how tree data is loaded. Its stronger lesson is how an already-loaded tree can be re-spaced interactively without changing the dataset shape. Compared with `ever-changing-tree` and `layout-folder2`, it stays much narrower: it does not turn into a broad styling or direction-switching playground, and it keeps the visual treatment plain so spacing remains the main thing the user notices.\n\nIt is also close to `center-layout-options`, but it translates that two-mode spacing-comparison pattern into the directional `tree` layout. That makes the distinction between sibling spacing and per-level spacing more concrete, because the page exposes `treeNodeGapH`, `treeNodeGapV`, and `levelGaps` directly on the same left-to-right tree.\n\n## Where Else This Pattern Applies\n\n- Building an internal layout-tuning panel for organization trees, dependency trees, or approval graphs before adding domain-specific node templates.\n- Teaching teams when to use one global spacing rule versus depth-specific spacing rules in a tree-based product.\n- Creating technical demos that need immediate relayout from React state without rebuilding graph data on every control change.\n- Adding a lightweight graph workbench around a read-only hierarchy, where users adjust viewport behavior and export snapshots while testing layout parameters.\n",false,500,1782615383007]