[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:en:trade-partner-relationship-explorer":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# Trade Partner Relationship Explorer with Lazy Branch Expansion\n\n## What This Example Builds\n\nThis example builds a trade-oriented relationship viewer around one focal company. The center card sits between inbound categories such as Purchased Products, Supplier, and Country of Origin, and outbound categories such as Supply Products, Buyer, and Country of Destination.\n\nUsers can zoom and drag the canvas, expand category nodes to load partner companies on demand, hide either the upper or lower half of the graph from the center node, and inspect a loaded company through an anchored partner card. The main value is not generic tree rendering. It is a compact business viewer that combines branch growth, branch filtering, and node-level inspection inside one graph.\n\n## How the Data Is Organized\n\nThe initial graph is seeded inline inside `initializeGraph()` as two arrays: seven nodes and six lines. One center node stores UI state in `data.expandedUpSide` and `data.expandedDownSide`, while the six category nodes declare `type`, fixed widths, and `expandHolderPosition` so the graph starts as a split inbound-versus-outbound tree.\n\nLazy-loaded data comes from `fetchMockData(componyId)`, which returns an `RGJsonData` fragment with `nodes` and `lines`. Each fetched child is a `company` node with the `c-company` class name, and the expand handler copies the parent node's `x` and `y` position into every new child before insertion so the next layout pass can animate outward from the expanded branch.\n\nIn a real business system, the same structure could be backed by supplier lists, buyer relationships, country-level trade dimensions, product catalogs, or customs records. The demo mock API keeps the payload small, but the seed-plus-fragment pattern already matches incremental loading from a real backend.\n\n## How relation-graph Is Used\n\nThe page mounts inside `RGProvider`, and `RGHooks.useGraphInstance()` drives all graph mutations. The graph uses the `tree` layout with `from: 'top'`, `treeNodeGapH: 10`, `treeNodeGapV: 120`, orthogonal connectors, and top-bottom junction points. That configuration produces a centered vertical structure with inbound categories above the root and outbound categories below it.\n\n`RelationGraph` binds `onNodeExpand` so category nodes can append new graph fragments through `addNodes(...)`, `addLines(...)`, and `doLayout()`. The same graph instance API also bootstraps the first render with `addNodes(...)`, `addLines(...)`, `doLayout()`, `moveToCenter()`, and `zoomToFit()`, then manages lazy-load feedback with `loading('Loading Data...')`, `sleep(400)`, and `clearLoading()`.\n\nThe main customization point is `RGSlotOnNode`. It replaces the default node body so the center node can render two custom visibility toggles, category nodes can render as styled business cards, and lazily inserted company nodes can render through `MyComponyDetail`. The local SCSS file then recolors default expand buttons to blue and reduces text size for `.c-company` nodes.\n\n## Key Interactions\n\n- Expanding one of the seeded category nodes triggers a one-time async load. A loading overlay appears, five company nodes are generated, and the graph relayouts after insertion.\n- The center node has separate top and bottom toggle buttons. They do not remove data; they hide related nodes by filtering `getNodeRelatedNodes(...)` on negative or positive `lot.level` values.\n- Wheel input zooms the canvas and drag input moves it, so the viewer behaves like an exploratory dashboard rather than a static diagram.\n- When a company node enters the slot's `checked` state, `PartnerCard` appears below that node with fallback partner metrics and product tags.\n\n## Key Code Fragments\n\nThis fragment shows the layout and canvas options that shape the inbound-versus-outbound tree.\n\n```tsx\nconst graphOptions: RGOptions = {\n  layout: {\n    layoutName: 'tree',\n    from: 'top',\n    treeNodeGapH: 10,\n    treeNodeGapV: 120,\n  },\n  defaultLineShape: RGLineShape.StandardOrthogonal,\n  defaultJunctionPoint: RGJunctionPoint.tb,\n  wheelEventAction: 'zoom',\n  dragEventAction: 'move',\n};\n```\n\nThis fragment shows how the demo seeds one center company and separate upper and lower category nodes before the first layout pass.\n\n```tsx\nconst nodes = [\n  {\n    id: 'center', text: 'HONGKONG SLKH CASTING CO LTD',\n    color: '#1a73e8', fontColor: '#ffffff', borderColor: '#1a73e8',\n    width: 300, height: 50,\n    data: { expandedUpSide: true, expandedDownSide: true }\n  },\n  { id: 'in1', text: 'Purchased Products', width: 200, expandHolderPosition: 'top', expanded: false, borderColor: '#f59e0b', color: '#ffffff', fontColor: '#334155', type: 'input' },\n  { id: 'in2', text: 'Supplier', width: 200, expandHolderPosition: 'top', expanded: false, borderColor: '#3b82f6', color: '#ffffff', fontColor: '#334155', type: 'input' },\n  { id: 'out1', text: 'Supply Products', width: 200, expandHolderPosition: 'bottom', expanded: false, borderColor: '#f59e0b', color: '#ffffff', fontColor: '#334155', type: 'output' },\n  { id: 'out2', text: 'Buyer', width: 200, expandHolderPosition: 'bottom', expanded: false, borderColor: '#3b82f6', color: '#ffffff', fontColor: '#334155', type: 'output' },\n];\n```\n\nThis fragment shows the one-time lazy expansion path, including loading feedback, position seeding, incremental insertion, and relayout.\n\n```tsx\nconst onNodeExpand = async (node: RGNode) => {\n  if (!node.data.dataLoaded) {\n    graphInstance.loading('Loading Data...');\n    node.data.dataLoaded = true;\n    const newNodeAndLines = await fetchMockData(node.id);\n    newNodeAndLines.nodes.forEach((n: JsonNode) => {\n      n.x = node.x;\n      n.y = node.y;\n    });\n    graphInstance.addNodes(newNodeAndLines.nodes);\n    graphInstance.addLines(newNodeAndLines.lines);\n    await graphInstance.sleep(400);\n    graphInstance.clearLoading();\n    await graphInstance.doLayout();\n  }\n}\n```\n\nThis fragment shows how the center node hides one side of the graph by updating related nodes instead of rebuilding the dataset.\n\n```tsx\nconst toggleRootUp = (node: RGNode) => {\n  const newExpanded = !node.data.expandedUpSide;\n  graphInstance.updateNodeData(node, {\n    expandedUpSide: newExpanded\n  });\n  const relatedNodes = graphInstance.getNodeRelatedNodes(node);\n  const leftNodes = relatedNodes.filter(n => n.lot.level \u003C 0);\n  leftNodes.forEach(node => {\n    graphInstance.updateNode(node, {\n      hidden: newExpanded === false\n    });\n  });\n}\n```\n\nThis fragment shows how the custom node slot turns a company node into both a compact chip and a trigger for the floating partner overlay.\n\n```tsx\nconst MyComponyDetail: React.FC\u003C{ node: RGNode, checked?: boolean }> = ({ node, checked }) => {\n  const partnerInfo = node.data.info || {\n    name: \"JINAN MEIDE CASTING CO LTD\",\n    country: \"China\",\n    yearsActive: 8,\n  };\n  return (\n    \u003Cdiv className=\"flex items-center justify-center px-4 py-2 text-sm font-medium transition-all gap-2\">\n      \u003CHotelIcon className=\"text-blue-500 shrink-0\" size={16} />\n      \u003Cdiv className=\"rg-node-text\">{node.text}\u003C/div>\n      \u003Cdiv className=\"absolute top-[60px]\">\n        {checked && \u003CPartnerCard partner={partnerInfo} />}\n      \u003C/div>\n    \u003C/div>\n  )\n}\n```\n\n## What Makes This Example Distinct\n\nThe comparison data does not support treating any single mechanism here as unique. Other examples also demonstrate lazy expansion, custom node rendering, or root-side branch controls. What stands out is the combination.\n\nCompared with `expand-button`, this example embeds lazy expansion inside a trade-specific dashboard scene instead of presenting the mechanic in isolation. Compared with `multiple-expand-buttons`, it adds async graph growth and a checked-node inspection card on top of the root-level branch toggles. Compared with `investment` and `investment-penetration`, the emphasis shifts away from ownership or navigation flows toward trade-dimension exploration and partner inspection.\n\nThat makes this example a strong starting point when the requirement is a focal company in the middle, domain categories around it, deferred branch loading, and a richer in-canvas detail view for selected partners.\n\n## Where Else This Pattern Applies\n\nThis pattern transfers well to supply-chain exploration, procurement review tools, export and import investigation screens, and B2B partner due-diligence dashboards. The same split root can represent upstream versus downstream relationships, domestic versus overseas markets, or inbound versus outbound logistics categories.\n\nIt is also a useful template when the full relationship graph is too large to preload. In those cases, the current structure can be reused with real APIs so each branch loads only when the analyst expands a category, while the checked-node overlay becomes a compact place for partner metrics, compliance notes, or product summaries.\n",false,500,1782615396900]