[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:en:bidirectional-tree-line-label-tuning":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# Bidirectional Tree Line Label Tuning\n\n## What This Example Builds\n\nThis example builds a root-centered bidirectional tree where one branch grows toward the root and the other grows away from it. The graph can switch between horizontal and vertical tree presets, while keeping the same dataset and the same two-sided structure.\n\nUsers see rectangular nodes connected by orthogonal lines, with parent-side branches colored yellow and child-side branches colored green. The most important behavior is that each side of the tree can tune its line-label geometry independently, so label placement stays readable even when the orientation changes.\n\n## How the Data Is Organized\n\nThe data is a static inline `RGJsonData` object with a single `rootId`, a flat `nodes` array, and a flat `lines` array. Some lines point into the root and some point away from it, which gives the layout enough directionality to produce a bidirectional tree around the center node.\n\nThere is no preprocessing before `setJsonData`. The meaningful transformation happens after layout: the component reads `node.lot.level`, treats negative levels as the reverse-side branch, and then applies side-specific colors and line-label settings at runtime.\n\nIn a real application, the same structure could represent upstream and downstream dependencies, managers and reports, ownership inflow and outflow, or any hierarchy that needs one focal node in the middle instead of a single top-level root.\n\n## How relation-graph Is Used\n\nThe example uses `RGProvider` to provide hook context and renders a `RelationGraph` surface with runtime configuration instead of relying on a large declarative `options` prop. `RGHooks.useGraphInstance()` is the main control point: the component calls `setOptions()`, `setJsonData()`, `moveToCenter()`, and `zoomToFit()` during initialization, then uses `getNodes()`, `getLines()`, `updateNode()`, and `updateLine()` for post-layout restyling.\n\nThe layout always uses `layoutName: 'tree'`, but swaps between `from: 'left'` and `from: 'top'`. That orientation switch also changes spacing and the default junction point, using `RGJunctionPoint.lr` for horizontal mode and `RGJunctionPoint.tb` for vertical mode. The visual baseline stays stable across both presets: rectangular nodes, no node border, and `RGLineShape.SimpleOrthogonal` for the connectors.\n\nThe graph does not use custom node slots or custom line slots. Instead, it leans on built-in rendering and customizes the result in two ways: runtime line updates for geometry and SCSS overrides for label appearance. The `.rg-line-label` rule turns built-in labels into compact white chips whose border and text color inherit the current line color, which keeps the two branch groups visually aligned with their connectors.\n\nThe floating control window is a local subcomponent, not a graph feature by itself, but it matters to how the example is assembled. It hosts the orientation selector, two `MyLinesOptions` panels, and a shared canvas-settings overlay that can change wheel and drag behavior and export the current graph as an image.\n\n## Key Interactions\n\nThe orientation selector switches between horizontal and vertical tree presets. That change rebuilds the graph options, reloads the same JSON data, reapplies branch styling, and recenters the viewport.\n\nThe two `MyLinesOptions` panels update the current graph in place. Each side can change `textAnchor`, `placeText`, `textOffsetX`, `textOffsetY`, and `polyLineStartDistance` without rebuilding the dataset, so the demo behaves like a focused label-tuning playground.\n\nThe floating window itself can be dragged and minimized. Its settings overlay can switch wheel behavior between scroll and zoom, switch canvas drag behavior between selection and move, and export the graph canvas through the shared screenshot helper.\n\nNode click and line click handlers only log objects to the console, so they do not materially change the example's behavior.\n\n## Key Code Fragments\n\nThis fragment shows that orientation switching changes layout direction, spacing, junction defaults, and the graph's baseline rendering options before loading the same JSON data.\n\n```tsx\nif (activeTabName === 'h') {\n    layoutOptions = {\n        layoutName: 'tree',\n        from: 'left',\n        treeNodeGapH: 150,\n        treeNodeGapV: 20\n    };\n    defaultJunctionPoint = RGJunctionPoint.lr;\n} else {\n    layoutOptions = {\n        layoutName: 'tree',\n        from: 'top',\n        treeNodeGapH: 20,\n        treeNodeGapV: 150\n    };\n    defaultJunctionPoint = RGJunctionPoint.tb;\n}\n```\n\nThis fragment proves that branch grouping is derived from layout metadata, then used to recolor nodes and apply different label geometry to each side.\n\n```tsx\nfor (const node of graphInstance.getNodes()) {\n    if (node.lot && node.lot.level !== undefined && node.lot.level \u003C 0) {\n        graphInstance.updateNode(node, { color: '#ca8a04' });\n        leftNodes.push(node);\n    } else {\n        graphInstance.updateNode(node, { color: '#3f9802' });\n    }\n}\nconst leftNodeIds: string[] = leftNodes.map(n => n.id);\nfor (const line of graphInstance.getLines()) {\n    if (leftNodeIds.includes(line.from) || leftNodeIds.includes(line.to)) {\n        graphInstance.updateLine(line, {\n            color: '#ca8a04',\n            textAnchor: parentsLineOptions.textAnchor ? parentsLineOptions.textAnchor : (activeTabName === 'v' ? 'center' : 'start'),\n            placeText: parentsLineOptions.placeText\n        });\n    }\n}\n```\n\nThis fragment shows that line-label controls are exposed as reusable UI state rather than hardcoded per-edge values.\n\n```tsx\n\u003CSimpleUISelect\n    data={[\n        { value: '', text: 'Auto' },\n        { value: 'start', text: 'start' },\n        { value: 'middle', text: 'middle' },\n        { value: 'end', text: 'end' }\n    ]}\n    currentValue={lineOptions.textAnchor}\n    onChange={(newValue: string) => {\n        lineOptionsUpdater({\n            ...lineOptions,\n            textAnchor: newValue\n        });\n    }}\n/>\n```\n\nThis fragment shows how the built-in line labels are restyled into bordered chips instead of plain SVG text.\n\n```scss\n.rg-line-peel {\n    .rg-line-label {\n        background-color: #fff;\n        color: var(--rg-line-color);\n        border: 1px solid var(--rg-line-color);\n        font-size: 10px;\n    }\n}\n```\n\n## What Makes This Example Distinct\n\nCompared with the closely related `bothway-tree` example, this variant is much more focused on line-label geometry than on arrow direction or branch-shape variation. The comparison data consistently points to its strongest differentiator: two separate `MyLinesOptions` panels that restyle parent-side and child-side branches independently after layout.\n\nCompared with `layout-tree` and `io-tree-layout`, this example is less about general relayout mechanics and more about keeping a two-sided tree readable when built-in labels must remain visible. The combination of a bidirectional tree, orthogonal connectors, runtime orientation switching, post-layout branch detection through `node.lot.level`, and branch-specific label controls is the reusable core.\n\nIt is also a narrower reference than the `line` example. Instead of cataloging many edge styles up front, it demonstrates how to keep standard relation-graph lines and mutate them in place after layout, which is useful when the branch grouping is only known after the layout engine has run.\n\n## Where Else This Pattern Applies\n\nThis pattern transfers well to upstream and downstream dependency viewers, supply-chain trees, ownership structures, or approval flows where one focal entity needs inbound and outbound branches in the same canvas.\n\nIt is also useful when line labels carry operational meaning, such as percentages, roles, stages, or relationship types, and those labels need different offsets or anchors on opposite sides of the same hierarchy.\n\nThe post-layout grouping technique is especially practical when upstream versus downstream status should be inferred from layout results instead of being duplicated as explicit flags in the input dataset.\n",false,500,1782615383712]