[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:en:google-income-statement-ribbon-infographic":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# Google Income Statement Ribbon Infographic\n\n## What This Example Builds\n\nThis example builds a standalone financial infographic viewer for a Google income statement. The screen shows a left-to-right breakdown where revenue categories feed into a shared root and expense categories branch away from it, so the graph reads like a compact statement summary instead of a generic tree.\n\nThe most visible parts are data-scaled node columns, wide ribbon-like connections, branded callouts for selected Google business lines, and a layered gradient background. Users can inspect the flow bands through large clickable ribbon areas, and they can clear checked state by clicking empty canvas space.\n\nIts main value is packaging. Compared with the related tutorial-style example, this version exposes only the finished viewer, so it is easier to study as a production-oriented reference.\n\n## How the Data Is Organized\n\nThe source data starts as one nested object with two top-level branches: `revenues` and `expenses`. Each item carries business fields such as `name`, `amount`, `color`, `year_over_year_change`, `percentage_of_revenue`, `margin`, and optional `desc`, while nested `details` arrays describe deeper breakdown levels.\n\nBefore `setJsonData` runs, `getMyJsonData()` converts that nested structure into `RGJsonData` with a synthetic root node, a flat `nodes` array, and a flat `lines` array. The recursive `findNodesFromOrignData()` helper also strips `details` out of each node payload so the remaining metrics can be rendered directly inside the node slot.\n\nThe preprocessing does more than flatten the tree. Revenue items are connected `child -> parent`, expense items are connected `parent -> child`, and every line stores percentage-based offset and height metadata in `line.data`. That extra geometry data is what lets the custom line slot render proportional ribbons instead of uniform strokes.\n\nIn a real application, the same structure could represent a profit and loss statement, a budget allocation tree, a category contribution chart, or any breakdown where both amount and flow proportion matter.\n\n## How relation-graph Is Used\n\nThe entry file keeps the composition minimal: `RGProvider` wraps `StepFinalVersion`, and the actual viewer component reads the graph instance through `RGHooks.useGraphInstance()`. That hook is central to the example because it is used both during graph initialization and inside the custom line slot.\n\nThe graph itself is configured as a left-to-right tree with large horizontal and vertical spacing. The options keep the structural defaults simple: rectangular base nodes, standard curved lines, left-right junction points, a 3px default line width, and a 1px default border width. Those defaults matter mostly as graph scaffolding because the visible presentation is overridden by slots and CSS.\n\n`RelationGraph` binds two graph events: `onLineClick` and `onCanvasClick`. The local line handler only logs, so this example stays in viewer mode rather than opening editors or panels. The canvas handler is functional, though, because it calls `clearChecked()` to reset checked state.\n\nThe most important customization points are `RGSlotOnNode` and `RGSlotOnLine`. The node slot renders an HTML block whose height depends on `node.data.amount`, then stacks the node label, dollar amount, and optional percentage or year-over-year text above it. The line slot resolves the live link with `getLinkByLine()`, rebuilds an SVG area path with `createAreaLinePathWithOffset()`, and forwards clicks back into relation-graph through `graphInstance.onLineClick(...)`.\n\nThe stylesheet completes the effect. It replaces the plain canvas background with fixed radial gradients, removes visible node borders, adds a halo for checked nodes, and makes the custom ribbon paths react to hover and checked state by increasing opacity. Together, those overrides turn a standard tree layout into a presentation-focused infographic.\n\n## Key Interactions\n\nThe graph initializes on mount. `initializeGraph()` loads transformed JSON data, centers the graph, fits it to the viewport, and then zooms out slightly so the full composition has more breathing room.\n\nLine inspection is implemented through the custom ribbon body rather than through thin default strokes. Each filled SVG area captures clicks on its whole surface and forwards that event into relation-graph's built-in line-click handling, which makes wide financial flows easier to inspect.\n\nCanvas clicks act as a reset. Clicking empty space clears checked state, which removes the active visual emphasis from graph items.\n\nThe interaction model remains intentionally light. The example does not add editing, drill-down, or side-panel inspection. Even the application-level `onLineClick` handler only logs the clicked line, so the focus stays on graph presentation and checked-state feedback.\n\n## Key Code Fragments\n\nThis fragment shows that the standalone example is only a provider wrapper around the finished implementation.\n\n```tsx\nconst Example: React.FC = () => {\n\n    return (\n        \u003CRGProvider>\u003CStepFinalVersion />\u003C/RGProvider>\n    );\n};\n```\n\nThis fragment shows the left-to-right tree baseline and the graph defaults that support the custom rendering layer.\n\n```tsx\nconst graphOptions: RGOptions = {\n    debug: false,\n    layout: {\n        layoutName: 'tree',\n        from: 'left',\n        treeNodeGapH: 300,\n        treeNodeGapV: 100\n    },\n    defaultNodeShape: RGNodeShape.rect,\n    defaultLineShape: RGLineShape.StandardCurve,\n    defaultLineWidth: 3,\n    defaultJunctionPoint: RGJunctionPoint.lr,\n    defaultNodeBorderWidth: 1\n};\n```\n\nThis fragment shows the mount-time data load and viewport adjustment.\n\n```tsx\nconst initializeGraph = async () => {\n    const myJsonData = await getMyJsonData();\n    await graphInstance.setJsonData(myJsonData);\n    graphInstance.moveToCenter();\n    graphInstance.zoomToFit();\n    graphInstance.zoom(-10); // Zoom out by 10% based on current zoom level\n};\n```\n\nThis fragment proves that revenue lines are stored as child-to-parent ribbons with percentage-based offsets.\n\n```ts\nif (income) {\n    allJsonLines.push({\n        from: cJsonNode.id,\n        to: parentJsonNode.id,\n        color: cJsonNode.color,\n        data: {\n            fromOffsetYPercent: 0,\n            fromHeightPercent: 1,\n            toOffsetYPercent: sumAmount / parentJsonNode.data.amount,\n            toHeightPercent: cJsonNode.data.amount / parentJsonNode.data.amount,\n        }\n    });\n}\n```\n\nThis fragment proves that expense lines reverse the direction while keeping the same proportional geometry idea.\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 the amount-driven node markup and the stacked financial labels.\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: '#0c63ff',\n        width: '200px',\n        whiteSpace: 'nowrap',\n        textAlign: 'left'\n    }}>\n        \u003Cdiv style={{transform: 'translateY(-110%)'}}>\n            \u003Cdiv style={{fontSize: '24px'}}>{node.text}\u003C/div>\n            \u003Cdiv style={{fontSize: '22px'}}>$ {node.data.amount} B\u003C/div>\n```\n\nThis fragment shows how the custom line slot rebuilds ribbon geometry from live link positions and keeps the line clickable.\n\n```tsx\nconst graphInstance = RGHooks.useGraphInstance();\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 presentation also depends on CSS, not only on graph options.\n\n```scss\n.rg-node-peel.rg-node-checked {\n    .rg-node {\n        border: none;\n        box-shadow: 0 0 0 10px var(--rg-node-color);\n    }\n}\n\n.my-rg-line {\n    opacity: 0.5;\n    pointer-events: fill;\n    cursor: pointer;\n```\n\n## What Makes This Example Distinct\n\nThe comparison data places this example closest to `demo-for-google-income-statement`, but the difference is important: this file set removes the six-step tutorial shell and keeps only the finished viewer. That makes it a better starting point when the goal is to reuse the completed infographic pattern rather than teach the build-up process.\n\nIts rare implementation pattern is the bidirectional preprocessing around one synthetic root. Revenue branches point inward, expense branches point outward, and both sides keep percentage metadata that the line slot later turns into proportional ribbons. That is a more specialized pattern than the ordinary left-to-right tree scaffolding shared with many other examples.\n\nCompared with `node-content-lines`, the distinctive lesson is not connector targeting inside rich node content. Here the stronger idea is custom edge rendering: the example stores geometric proportions during preprocessing and recomputes filled flow bands from live node positions at render time.\n\nCompared with `canvas-event` and `customize-fullscreen-action`, this example uses a similar hook-initialized viewer baseline for a different purpose. Those examples emphasize event instrumentation or surrounding page behavior, while this one concentrates on a polished financial narrative with amount-scaled columns, ribbon flows, branded callouts, checked halos, and a gradient canvas.\n\n## Where Else This Pattern Applies\n\nThis pattern transfers well to other statement-style views where one dataset needs both hierarchy and proportional flow. Examples include operating-cost breakdowns, departmental budget allocation, business-unit contribution charts, and sustainability or resource-flow summaries.\n\nIt also fits dashboards that need a polished narrative graphic rather than an editing surface. Teams can keep the same recursive preprocessing and line-geometry approach while replacing the Google-specific labels, colors, logos, and explanatory text with their own business domains.\n",false,500,1782615394646]