[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:zh:slot-based-node-content-and-styles":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# 树形图中基于插槽的节点内容与样式\n\n## 这个示例构建了什么\n\n这个示例构建了一个从左到右布局的树形图，其节点并不共用同一种视觉模板。相反，图中通过单一节点插槽渲染多种不同的节点主体：带轮播的大型根节点卡片、视频卡片、矩形按钮节点、圆形徽章，以及半透明的胶囊标签。\n\n最终可见效果是一个全高展示画布，背景为黄绿色，连接线为半透明白色，并带有一个悬浮的白色工具窗口。用户可以像查看器一样浏览图谱，与嵌入节点中的控件交互，打开悬浮设置面板，切换画布交互模式，并将当前图谱导出为图片。\n\n最重要的点并不在于这棵树本身。这个示例展示了：一个静态的 relation-graph 数据集，如何通过 `RGSlotOnNode` 驱动异构的 React 节点内容；同时 SCSS 如何把选中态样式重定向到已渲染的插槽内容上，而不是库默认的节点主体。\n\n## 数据是如何组织的\n\n图数据以内联方式创建为一个 `RGJsonData` 对象，包含 `rootId`、`nodes` 和 `lines`。这里没有 fetch，没有异步转换流程，也没有加载后的动态图编辑。结构很直接：\n\n- `nodes` 保存图中的身份信息和展示提示。\n- `lines` 定义树的关系。\n- `type` 决定 `MyNodeContent` 应该渲染哪一种自定义节点主体。\n- `data.buttonText` 携带按钮标签这种按节点区分的负载数据。\n- 即使可见主体主要是自定义插槽内容，`nodeShape` 仍然会影响 relation-graph 的几何计算。\n\n在调用 `setJsonData()` 之前并没有单独的预处理步骤。这个示例在组装 JSON 字面量时就直接给节点打上标签，因此图谱加载后，插槽渲染器可以立即基于这些元数据进行分支。\n\n在真实应用中，同样的模式可以映射到组织角色、工作流状态、富媒体内容卡片、KPI 徽章、告警节点或操作节点等场景，在这些场景中，每种节点类型都需要不同的 HTML 结构，但仍然属于同一个图数据集。\n\n## relation-graph 是如何使用的\n\n这个图将 relation-graph 用作面向查看的树布局。`MyGraph` 配置了一个 `tree` 布局，使其从左向右生长，并采用较大的水平间距与更紧凑的垂直间距，从而让这些混合节点主体保持可读性。选项中还通过将节点填充设为透明、默认边框宽度设为 0，去掉了大部分默认节点外观，使插槽渲染出的 React 内容来定义最终视觉效果。\n\n`RelationGraph` 被包裹在 `RGProvider` 中，示例通过 `RGHooks.useGraphInstance()` 读取实时图实例。该实例用于一次性的初始化流程：通过 `setJsonData(...)` 加载数据，将视口移动到中心，然后调用 `zoomToFit()`。同一个 hook 也在共享的悬浮辅助窗口中复用，用于更新交互选项并执行图片导出流程。\n\n核心定制点是 `RGSlotOnNode`。每个节点都会经过同一个插槽函数，它会把当前节点对象传给 `MyNodeContent`。该组件根据 `node.type` 分支并渲染：\n\n- 根节点的轮播卡片，\n- 内置媒体控制的视频卡片，\n- `my-button` 的黑色操作按钮，\n- `my-circle` 的半透明圆形徽章，\n- 其余节点的玻璃风格胶囊标签。\n\nSCSS 补完了这套定制。样式表为工具栏提供半透明白色外观，去掉默认选中节点阴影，并把高亮环应用到自定义插槽包装层上。这一点很重要，因为当前可见的节点主体已经不再是 relation-graph 默认的节点框。\n\n这里没有图编辑流程。这个示例明确处于查看模式，运行时可修改的选项仅限于画布交互行为和导出支持。\n\n## 关键交互\n\n- 根节点包含一个带有 Previous 和 Next 控件的轮播组件，证明交互式 React 小部件可以放进节点主体中。\n- 视频节点暴露原生媒体控制，说明插槽内容可以包含富 HTML 媒体，而不仅仅是文本或图标。\n- 按钮节点在节点内容内部处理 DOM 点击事件，并记录当前节点 id。这个行为虽然很小，但它展示了自定义节点小部件可以拥有自己的事件处理逻辑。\n- 悬浮工具窗口可以被拖动、最小化，并切换到设置覆盖层。\n- 设置覆盖层会在实时图实例上修改 `wheelEventAction` 和 `dragEventAction`，因此用户无需重新加载数据，就能在滚动、缩放、选择、移动或无画布响应之间切换。\n- 同一个覆盖层还可以通过预处理图 DOM、使用 `modern-screenshot` 进行捕获、下载 blob，然后恢复图状态的方式，将图谱导出为图片。\n\n## 关键代码片段\n\n这段代码展示了：该示例依赖 relation-graph 的布局与选项调优，来在一个展示型画布上承载自定义节点内容。\n\n```tsx\nconst graphOptions: RGOptions = {\n    debug: true,\n    backgroundColor: 'rgb(101, 163, 13)',\n    defaultLineColor: 'rgba(255, 255, 255, 0.6)',\n    defaultNodeColor: 'transparent',\n    defaultNodeBorderWidth: 0,\n    defaultNodeShape: RGNodeShape.rect,\n    toolBarDirection: 'h',\n    toolBarPositionH: 'right',\n    toolBarPositionV: 'bottom',\n    defaultPolyLineRadius: 10,\n    defaultLineShape: RGLineShape.StandardCurve,\n    defaultJunctionPoint: RGJunctionPoint.lr,\n    layout: {\n        layoutName: 'tree',\n        from: 'left',\n        treeNodeGapH: 200,\n        treeNodeGapV: 30\n    }\n};\n```\n\n这段代码展示了：图数据本身就携带了插槽渲染器所使用的节点类型元数据。\n\n```tsx\nconst myJsonData: RGJsonData = {\n    rootId: 'a',\n    nodes: [\n        { id: 'a', text: 'a', type: 'my-root', nodeShape: RGNodeShape.rect },\n        { id: 'b', text: 'b', type: 'my-circle', nodeShape: RGNodeShape.circle },\n        // ...\n        { id: 'c', text: 'c', type: 'my-video' },\n        { id: 'c1', text: 'c1', type: 'my-button', nodeShape: RGNodeShape.rect, data: { buttonText: 'Button 1' } },\n        { id: 'c2', text: 'c2', type: 'my-button', nodeShape: RGNodeShape.rect, data: { buttonText: 'Button 2' } }\n    ],\n```\n\n这段代码展示了核心的插槽渲染技术：一个节点组件分支成多种不同的节点主体实现。\n\n```tsx\nif (node.type === 'my-root') {\n    return (\n        \u003Cdiv className=\"bg-white rounded\">\n            \u003CSimpleUICarousel contents={nodeContentTypes} width={400} height={200} />\n        \u003C/div>\n    );\n} else if (node.type === 'my-video') {\n    return (\n        \u003Cdiv className=\"relative h-56 w-72 rounded-lg overflow-hidden\">\n            \u003Cvideo playsInline className=\"h-full w-full object-cover overflow-clip-margin-content-box overflow-clip\" controls autoPlay loop muted src=\"https://relation-graph.com/images/video-dribbble.mp4\" />\n```\n\n这段代码展示了样式表如何把选中态强调效果，从库默认节点框转移到自定义插槽内容上。\n\n```scss\n.rg-node-peel.rg-node-checked {\n    .rg-node {\n        box-shadow: none;\n\n        & > div {\n            box-shadow: 0 0 0 8px var(--rg-checked-item-bg-color);\n        }\n    }\n}\n```\n\n这段代码展示了共享辅助窗口如何修改实时图的交互模式，并通过图实例 API 支持图片导出。\n\n```tsx\n\u003CSettingRow\n    label=\"Wheel Event:\"\n    options={[\n        { label: 'Scroll', value: 'scroll' },\n        { label: 'Zoom', value: 'zoom' },\n        { label: 'None', value: 'none' },\n    ]}\n    value={wheelMode}\n    onChange={(newValue: string) => { graphInstance.setOptions({ wheelEventAction: newValue }); }}\n/>\n```\n\n## 这个示例的独特之处\n\n与附近的 `node-style2`、`node-style3` 和 `node-style4` 等示例相比，这个示例在需求是“替换为异构节点主体”而不是“细化主题样式”时更有价值。对比数据在这一点上很明确：其他示例也可能使用 `RGSlotOnNode`，但它们通常是在重复同一种视觉系统，或更侧重于宽泛的样式对比。而这个示例则是在一棵紧凑的树中，同时把多种明显不同的内容类型放入节点中。\n\n最强的区分性组合是：\n\n- 从左到右的树布局，\n- 透明的默认节点主体，\n- 半透明白色曲线连接线，\n- 黄绿色全高画布，\n- 由多个 `node.type` 驱动的插槽主体，\n- 包裹自定义插槽内容的选中态样式，\n- 复用的悬浮辅助窗口，用于画布设置和导出。\n\n当内置节点渲染器不够用时，这组特性让它比 `node-style2` 更适合作为起点；而当目标不是广泛的技术对比，而是寻找一个在节点内混合 HTML 与媒体内容的实用模式时，它又比 `node` 更聚焦。\n\n悬浮辅助窗口应被视为次要部分。对比数据明确提醒不要把它描述成独有能力，因为这套外壳在附近多个示例中都会复用。这里真正有辨识度的经验，是基于插槽的节点内容系统，以及为让选中态和整体外观依然协调而必须做出的样式调整。\n\n## 这种模式还能应用到哪里\n\n这种模式非常适合那些必须在同一张图里混合多种节点展示方式、但又不改变图数据契约的场景：\n\n- 产品或内容地图，其中有些节点是标签，有些是媒体预览，有些是操作卡片，\n- 工作流仪表盘，其中审核节点、决策按钮和状态徽章需要不同的 HTML 主体，\n- 教育或叙事类图谱，其中根节点充当特性卡片，子节点充当紧凑标签或检查点，\n- 监控视图，其中告警节点、指标胶囊和下钻操作共享同一拓扑，但不共享相同的渲染需求。\n\n同样的结构也可以继续扩展，例如把内联示例数据替换为 API 数据、把 `node.type` 扩展为更丰富的渲染注册表，或者将嵌入式节点控件连接到真实业务动作，而不是仅仅做 console logging。\n",false,500,1782615371323]