[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:en:google-income-statement-ribbon-tutorial":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# Building a Step-by-Step Google Income Statement Ribbon Graph\n\n## What This Example Builds\n\nThis example builds a guided viewer for a single income-statement dataset rather than a single fixed graph. A floating helper window lets the user switch through six stages: a plain tree, a node-slot version, an amount-scaled node version, a filled-ribbon line version, an offset-aware ribbon version, and a final branded infographic.\n\nThe canvas shows revenue branches feeding into a synthetic root and expense or profit branches flowing away from it, so the finished scene reads like a financial breakdown instead of a generic hierarchy. The main value is not only the final appearance, but the fact that the intermediate steps remain visible inside one example.\n\n## How the Data Is Organized\n\nThe source data in `my-data.ts` is a nested object with two top-level branches: `revenues` and `expenses`. `getMyJsonData()` creates one synthetic `root` node, then `findNodesFromOrignData()` recursively flattens nested `details` arrays into `nodes` and `lines` for `RGJsonData`.\n\nThe preprocessing also stores rendering metadata on each line. Every generated edge carries `fromOffsetYPercent`, `fromHeightPercent`, `toOffsetYPercent`, and `toHeightPercent` inside `line.data`, which later controls ribbon height and attachment position in the custom line renderer.\n\nThe direction of the generated links is deliberate. Revenue children connect toward the root, while the expense and profit side connects away from the root. In a real project, the same pattern can represent revenue composition, cost breakdowns, margin bridges, budget allocation trees, or any nested metric structure where edge thickness should encode contribution.\n\n## How relation-graph Is Used\n\nEach stage is mounted inside its own `RGProvider`, and every step component uses `RGHooks.useGraphInstance()` plus an initialization routine in `useEffect`. The base `RelationGraph` options stay stable across versions: tree layout, left-to-right direction, `treeNodeGapH: 300`, `treeNodeGapV: 100`, rectangular nodes, standard curved lines, 3px line width, and left-right junction points.\n\nThe example then adds customization layer by layer. Step 2 introduces `RGSlotOnNode` to replace the default node body with custom HTML content. Step 3 keeps the slot approach but makes node height proportional to `node.data.amount` and adds business metrics above the anchor box. Step 4 adds `RGSlotOnLine` and replaces thin strokes with a filled SVG area created by `createAreaLinePath()`. Step 5 upgrades that to `createAreaLinePathWithOffset()`, which reads the percentages generated during preprocessing so each ribbon occupies only its proportional vertical slice.\n\nThe later custom-line stages recover live geometry with `graphInstance.getLinkByLine(lineConfig.line)` and manually forward clicks back into relation-graph through `graphInstance.onLineClick(...)`, so the custom SVG path still participates in graph interaction. The final step keeps both slots, adds product-specific SVG components for selected nodes, binds `onLineClick` and `onCanvasClick`, and performs an extra `zoom(-10)` after `zoomToFit()` to open up the infographic composition.\n\nPresentation details are handled with local styling rather than layout changes. `Step4Version.scss` removes the visible node border, adds the multiradial background, defines ribbon opacity and hover behavior, and adds the halo styling used for checked nodes and branded callout blocks.\n\n## Key Interactions\n\n- The floating helper window switches `stepName` between six complete graph implementations and updates the explanation text at the same time.\n- The helper window can be dragged by its title bar and minimized, which lets the user inspect the graph without losing the step controls.\n- In Step 4 and later, the visible line body is a filled SVG ribbon with `pointer-events: fill`, so custom-rendered edges stay clickable after the default stroke is visually replaced.\n- In the final stage, ribbon clicks are still forwarded into relation-graph, and a canvas click clears the checked state through `graphInstance.clearChecked()`.\n\n## Key Code Fragments\n\nThis fragment shows that the wrapper swaps complete graph variants, which is why the example works as a tutorial instead of a single finished view.\n\n```tsx\n{stepName === 'v1' && \u003CRGProvider>\u003CStep1Version />\u003C/RGProvider>}\n{stepName === 'v2' && \u003CRGProvider>\u003CStep2Version />\u003C/RGProvider>}\n{stepName === 'v3' && \u003CRGProvider>\u003CStep3Version />\u003C/RGProvider>}\n{stepName === 'v4' && \u003CRGProvider>\u003CStep4Version />\u003C/RGProvider>}\n{stepName === 'v5' && \u003CRGProvider>\u003CStep5Version />\u003C/RGProvider>}\n{stepName === 'final' && \u003CRGProvider>\u003CStepFinalVersion />\u003C/RGProvider>}\n```\n\nThis fragment shows how the flattened graph keeps percentage metadata on each line for later ribbon geometry.\n\n```ts\nallJsonLines.push({\n  from: parentJsonNode.id,\n  to: cJsonNode.id,\n  color: cJsonNode.color,\n  data: {\n    fromOffsetYPercent: sumAmount / parentJsonNode.data.amount,\n    fromHeightPercent: cJsonNode.data.amount / parentJsonNode.data.amount,\n    toOffsetYPercent: 0,\n    toHeightPercent: 1,\n  }\n});\n```\n\nThis fragment shows that node height is driven directly by the business amount, not by a fixed visual preset.\n\n```tsx\n\u003Cdiv style={{width: '40px', height: `${(node.data?.amount / 100) * 200}px`, position: 'relative'}}>\n  \u003Cdiv style={{\n    position: 'absolute',\n    top: '0px',\n    left: '0px',\n    color: node.color,\n    width: '200px',\n  }}>\n```\n\nThis fragment shows how the custom line slot uses live link geometry and then forwards clicks back to relation-graph.\n\n```tsx\nconst link = graphInstance.getLinkByLine(lineConfig.line)!;\nconst path = createAreaLinePathWithOffset(\n  link.fromNode,\n  link.toNode,\n  lineConfig.line,\n  1\n);\n\nconst onClick = (e) => {\n  graphInstance.onLineClick(lineConfig.line, e);\n};\n```\n\nThis fragment shows that the final stage adds brand-specific callouts inside the node slot instead of changing the underlying graph data shape.\n\n```tsx\n{node.text === 'Google Cloud' && (\n  \u003Cdiv className=\"c-node-icon-desc\">\n    \u003CIconForGoogleCloud/>\n    \u003Cdiv style={{\n      fontSize: '20px',\n      color: '#666666',\n      width: '250px',\n    }}>{node.data.desc}\u003C/div>\n  \u003C/div>\n)}\n```\n\n## What Makes This Example Distinct\n\nAccording to the prepared comparison data, the closest neighbor is `demo-for-google-income-statement-final`, but the intent is different. The final-only neighbor is a concise reference for the finished infographic, while this example preserves the whole build-up path from plain tree rendering to branded ribbon graph.\n\nThe rare part is the combination, not any single API call by itself. This example combines recursive preprocessing around a synthetic root, opposite branch directions for revenue and expense flows, amount-scaled node slots, clickable ribbon-style line slots, and a draggable tutorial shell that switches among six implementations. That makes it a strong starting point when the goal is to teach or study progressive graph customization.\n\nThe comparison data also separates it from other custom-line viewers. Against `adv-line-slot-2`, the custom line logic here is used to express financial composition rather than route status or movement. Against examples like `node-line-tips-contentmenu` and `customize-fullscreen-action`, the surrounding window is mainly an orchestration layer for staged visual transformation, not a tooltip/menu showcase or a fullscreen wrapper lesson.\n\n## Where Else This Pattern Applies\n\nThis pattern transfers well to investor-relations visuals, CFO dashboards, and internal business reviews where one parent metric needs to be decomposed into branded subcategories with visible proportional weight.\n\nIt also fits budget allocation views, gross-to-net bridges, cloud-cost breakdowns, and product-portfolio composition charts where a tree layout is still useful, but thin edges are too weak to communicate contribution.\n\nThe tutorial-shell approach is also reusable outside finance. Teams can use the same multi-stage wrapper to demonstrate how a plain relation-graph example evolves into a more customized deliverable without forcing readers to compare several separate demos by hand.\n",false,500,1782615382092]