[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:en:custom-line-slot-rendering":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# Custom Line Slot Rendering with Endpoint Labels and Detail Actions\n\n## What This Example Builds\n\nThis example builds a left-to-right tree graph and replaces the default line renderer with a custom slot component. The visible result is a set of curved blue lines with a moving white dot, a label for each connection, and extra `Start Text` and `End Text` markers anchored directly to the computed path. The nodes themselves are kept visually light, with transparent boxes and a selection glow instead of heavy chrome.\n\nUsers can click lines, trigger a line-specific `Detail` action, open the floating settings window, change canvas interaction modes, and export the current graph as an image. The main point of the demo is that the entire line presentation is custom, but relation-graph still supplies the path geometry, label positioning, and normal line-click behavior.\n\n## How the Data Is Organized\n\nThe graph data is assembled inline as `RGJsonData` inside `initializeGraph`. It declares a single root node named `base`, several first-level incoming and outgoing links, and a few second-level child links under nodes `1` and `4`. Each connection is still represented in the standard relation-graph form: `from`, `to`, and `text`.\n\nThere is only one preprocessing step before `setJsonData`: every line has `showEndArrow` set to `false`. That keeps the custom pipe-like styling and endpoint labels from competing with default arrowheads. After loading, the graph is centered and fitted to the viewport, but no additional data transformation is applied.\n\nIn a real application, the same structure could represent network routes, system dependencies, equipment connections, warehouse paths, or organization-level handoff relationships. The example keeps the payload simple so the documentation focus stays on line rendering rather than on domain-specific metadata.\n\n## How relation-graph Is Used\n\n`RGProvider` wraps the demo so relation-graph hooks can resolve the active graph instance. Inside `MyGraph`, `RelationGraph` is configured with a tree layout that grows from the left, `levelGaps` of `[500, 400]`, `RGJunctionPoint.lr`, and `RGLineShape.StandardCurve`. Those settings make the paths predictable enough to decorate consistently.\n\nThe main extension point is `RGSlotOnLine`. Instead of accepting relation-graph's built-in edge output, the demo passes each line's slot props into `MyLineContent`, along with a parent callback for the separate detail action. Inside that slot component, `RGHooks.useGraphInstance()` is used to call `generateLinePath` and `generateLineTextStyle`, so the custom renderer still uses relation-graph's own geometry and text-position logic.\n\n`MyLineContent` then keeps relation-graph primitives in the loop. `RGLinePath` renders the actual SVG edge and handles forwarded line clicks. The slot injects a moving `\u003Ccircle>` inside the path renderer and adds start and end text groups that follow the same computed path through CSS `offset-path`. When the line is using the div-based text branch, `RGLineText` renders the HTML label container and appends a `Detail` link after the computed line text.\n\nOutside the slot, the graph instance API is used for setup and viewer utilities. `setJsonData`, `moveToCenter`, and `zoomToFit` initialize the graph on mount. The floating `DraggableWindow` contains a settings panel that reads the current graph options through `RGHooks.useGraphStore()`, updates wheel and drag behavior with `setOptions`, and exports the canvas with `prepareForImageGeneration` and `restoreAfterImageGeneration`. The screenshot helper uses `modern-screenshot` to turn the prepared graph DOM into a downloadable image.\n\nThe visual customization is finalized in `my-relation-graph.scss`. That stylesheet removes the default node box styling, adds a checked-node glow, thickens the line background and foreground strokes, colors the label text, and animates the dot and endpoint labels along the SVG path by reading the custom `--my-line-path` variable from the slot.\n\n## Key Interactions\n\n- Clicking the custom SVG line still enters relation-graph's normal `onLineClick` pipeline because the slot forwards the event through `graphInstance.onLineClick(...)`.\n- The div-based label branch is also clickable and forwards the same line event, so replacing the line renderer does not remove standard line interaction.\n- The appended `Detail` action calls back into `MyGraph` and opens an alert for the current line text.\n- The floating `DraggableWindow` can be dragged, minimized, and switched into a settings panel.\n- The settings panel changes wheel behavior, changes drag behavior, and downloads an image of the current graph canvas.\n\n## Key Code Fragments\n\nThis block shows the layout and default line settings that the custom renderer is built on.\n\n```tsx\nconst graphOptions: RGOptions = {\n    defaultJunctionPoint: RGJunctionPoint.lr,\n    defaultLineShape: RGLineShape.StandardCurve,\n    layout: {\n        layoutName: 'tree',\n        from: 'left',\n        levelGaps: [500, 400]\n    }\n};\n```\n\nThis block shows the inline dataset and the preprocessing step that disables default end arrows before loading.\n\n```tsx\nconst myJsonData: RGJsonData = {\n    rootId: 'base',\n    nodes: [\n        { id: 'base', text: '🏢 Base', },\n        // ...\n    ],\n    lines: [\n        { from: 'base', to: '1', text: 'Line X01' },\n        // ...\n    ]\n};\nmyJsonData.lines.forEach((line) => {\n    line.showEndArrow = false;\n});\n```\n\nThis block shows the graph instance loading the data and immediately framing it for presentation.\n\n```tsx\nawait graphInstance.setJsonData(myJsonData);\ngraphInstance.moveToCenter();\ngraphInstance.zoomToFit();\n```\n\nThis block shows how the example replaces the default line renderer with a custom slot component.\n\n```tsx\n\u003CRelationGraph\n    options={graphOptions}\n    onLineClick={onLineClick}\n>\n    \u003CRGSlotOnLine>\n        {(lineSlotProps: RGLineSlotProps) => {\n            return (\n                \u003CMyLineContent\n                    {...lineSlotProps}\n                    onMyLineDetailClick={onMyLineDetailClick}\n                />\n            );\n        }}\n    \u003C/RGSlotOnLine>\n\u003C/RelationGraph>\n```\n\nThis block shows the custom slot deriving path geometry, deciding how to render text, and forwarding clicks back into relation-graph.\n\n```tsx\nconst linePathInfo = useMemo\u003CRGLinePathInfo>(() => graphInstance.generateLinePath(lineConfig), [lineConfig]);\nconst onLineClick = (e: React.MouseEvent | React.TouchEvent) => {\n    graphInstance.onLineClick(lineConfig.line, e.nativeEvent);\n};\nconst useTextOnPath = (lineConfig.line.useTextOnPath || defaultLineTextOnPath);\nconst useSvgTextPath = useTextOnPath && (lineConfig.line.lineShape !== RGLineShape.StandardStraight);\nconst useDivLineText = !useSvgTextPath && lineConfig.line.text;\nconst textStyle = graphInstance.generateLineTextStyle(lineConfig, linePathInfo);\n```\n\nThis block shows the slot keeping `RGLinePath` but layering extra path-bound visuals on top of it.\n\n```tsx\n\u003CRGLinePath\n    lineConfig={lineConfig}\n    linePathInfo={linePathInfo}\n    useTextOnPath={useSvgTextPath}\n    checked={checked}\n    graphInstanceId={graphInstanceId}\n    onLineClick={onLineClick}\n>\n    \u003Ccircle className=\"my-dot\" r=\"5\">\u003C/circle>\n    \u003Cg className=\"my-text-start\" style={{ transform: 'translate(0px, -3px)' }}>\n        \u003Ctext fontSize={10} textAnchor={'start'}>Start Text\u003C/text>\n    \u003C/g>\n    \u003Cg className=\"my-text-end\" style={{ transform: 'translate(0px, -3px)' }}>\n        \u003Ctext fontSize={10} textAnchor={'end'}>End Text\u003C/text>\n    \u003C/g>\n\u003C/RGLinePath>\n```\n\nThis block shows the line label branch adding a separate detail affordance after the computed text.\n\n```tsx\n\u003Cdiv\n    className={`rg-line-label ${useTextOnPath ? 'rg-line-label-on-path' : ''}`}\n    style={{\n        ...textStyle.cssStyles\n    }}\n    onTouchStart={onLineClick}\n    onClick={onLineClick}\n>\n    {textStyle.text}\n    \u003Ca\n        className=\"text-green-300 cursor-pointer hover:underline\"\n        onClick={() => { onMyLineDetailClick(lineConfig.line); }}\n    >\n        Detail\n    \u003C/a>\n\u003C/div>\n```\n\nThis block shows the shared utility panel exporting the prepared graph canvas as an image.\n\n```tsx\nconst downloadImage = async () => {\n    const canvasDom = await graphInstance.prepareForImageGeneration();\n    let graphBackgroundColor = graphInstance.getOptions().backgroundColor;\n    if (!graphBackgroundColor || graphBackgroundColor === 'transparent') {\n        graphBackgroundColor = '#ffffff';\n    }\n    const imageBlob = await domToImageByModernScreenshot(canvasDom, {\n        backgroundColor: graphBackgroundColor\n    });\n    if (imageBlob) {\n        downloadBlob(imageBlob, 'my-image-name');\n    }\n    await graphInstance.restoreAfterImageGeneration();\n};\n```\n\n## What Makes This Example Distinct\n\nThe comparison data positions this example as a focused reference for fully replacing relation-graph's line renderer while still keeping relation-graph primitives and events in play. Its strongest distinguishing point is the combination of `RGSlotOnLine`, `RGLinePath`, `RGLineText`, `generateLinePath`, and `generateLineTextStyle` in one renderer, rather than stopping at stroke color changes or ribbon-like restyling. The animated dot plus explicit start and end text markers is also called out as a rare visual combination.\n\nCompared with `adv-line-slot-1`, this example emphasizes endpoint annotation and simpler slot wiring rather than path-length-aware animation. Compared with `adv-line-slot-2`, it stays narrower in scope: it does not add route metrics or a custom node slot, and it keeps the focus on line rendering itself. Compared with `div-on-line`, it redraws every edge through `RGSlotOnLine` instead of leaving the native renderer intact and attaching a separate overlay to one selected line. Compared with `adv-line-slot2` and `customer-line1`, it stays closer to relation-graph's native line primitives instead of converting connections into filled ribbon areas or theme-driven bands.\n\nThat makes it a strong starting point when the requirement is not just to restyle a line, but to replace the line layer with a geometry-aware renderer that still behaves like a normal relation-graph line.\n\n## Where Else This Pattern Applies\n\nThis pattern can be reused in route monitoring, infrastructure maps, equipment wiring diagrams, dependency visualization, transport handoff views, or any directional network where the line needs more meaning than a plain stroke. It is especially useful when each connection needs animated flow cues, path-bound endpoint labels, a separate line-level action, and exportable viewer tooling without turning the graph into a full editor.\n",false,500,1782615378622]