[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:zh:connect-targets-across-canvas-node-and-grid":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# 跨画布组件、节点与网格卡片的混合连接目标\n\n## 本示例构建了什么\n\n本示例构建的是一个固定布局的 connect-target 练习场，而不是一个面向特定业务领域的图。这个场景在同一个 `RelationGraph` 实例中混合了两个独立的画布组件、两个已定位的图节点、节点内部端口，以及一组可连接的网格卡片。\n\n用户看到的是一个明亮的全屏沙盒，其中包含按颜色区分的弯曲演示连线、一个暴露出多个输入和输出端口的绿色节点、一个带有单个内部目标的蓝色兜底节点、两个样式不同的画布元素，以及一个可改变网格布局的白色控制面板。这里的关键行为是：同一个页面同时演示了多种端点作用域，并且还会拦截新创建的连线，但不会自动保存它们。\n\n## 数据是如何组织的\n\n数据是内联组装的，而不是通过一个大型 `setJsonData(...)` 载荷统一加载。`initializeGraph()` 创建了一个带有显式 `x` 和 `y` 坐标的小型 `nodes` 数组，然后又创建了一个 `fakeLines` 数组，其端点引用了 `canvas-el-a`、`n2`、`n1-input-1` 和 `grid-item-6` 等 id。与此同时，组件状态中还保存了 `gridCols`、`gridGap`，以及一个包含六项的 `gridItems` 数组，用于表示可连接的网格。\n\n在 `setJsonData(...)` 或 `doLayout(...)` 之前没有单独的预处理步骤，但这里存在一套严格的 id 约定。每个连线端点 id 都必须匹配图节点 id、节点内部的 `RGConnectTarget`，或画布插槽中的 `RGConnectTarget`。整节点端点通过 `RGInnerConnectTargetType.Node` 标记，而内部端口和网格卡片则保持默认的 connect-target 行为。在真实应用中，这种结构同样可以表示工作流组件、设备面板、字段映射目标、排期槽位，或需要跨混合 UI 表面进行连接的仪表盘卡片。\n\n## relation-graph 是如何使用的\n\n`RGProvider` 包裹了这个演示，因此 `RGHooks.useGraphInstance()` 可以访问当前激活的图实例。该图运行在 `fixed` 布局模式下，这意味着 relation-graph 不会根据关系自动计算位置；示例直接提供节点坐标。这些配置还移除了默认节点边框和填充色，将默认连线形状设置为 `RGLineShape.StandardCurve`，并启用了内置工具栏。\n\n这个示例同时使用了两种主要插槽类型。`RGSlotOnNode` 用自定义 JSX 替换默认节点主体：节点 `n1` 被渲染为一个带有五个内部目标的绿色卡片，而兜底节点渲染器则在蓝色节点上暴露出一个额外的内部目标。`RGSlotOnCanvas` 则渲染了两个可自由定位的画布组件，以及一个动态 CSS 网格块，其单元格同样被包裹在 `RGConnectTarget` 中。这正是这里 relation-graph 的核心技巧：连接器路径由图引擎负责处理，但实际端点存在于通过节点插槽和画布插槽渲染的普通 DOM 内部。\n\n图实例 API 驱动了初始化流程。组件挂载时，示例会调用 `addNodes(...)`，短暂等待 DOM 渲染完成，然后将视口移动到中心、让场景自适应缩放，最后通过 `addFakeLines(...)` 注入演示连线。`onLineBeCreated` 回调则是手动持久化的交接点。当用户创建一条新连线时，示例会弹出一条 toast 消息，显示源端和目标端 id，但它不会调用 `addLines(...)` 将这条线保存到图数据中。样式处理非常克制且有针对性：SCSS 文件主要让连线标签继承当前连线颜色，从而保证每种预置连接场景都易于辨识。\n\n## 关键交互\n\n- 由于 `showToolBar` 被设置为 `true`，内置工具栏和常规图视口控制都会启用。\n- 场景启动时会带有预置的 fake lines，用于演示画布到画布、画布到节点、画布到内部端口、节点到内部端口、内部到内部，以及网格到网格等连接方式。\n- 画布插槽中的两个数字输入框会更新 `gridCols` 和 `gridGap`，因此端点卡片组成的网格会在运行时重新排布，同时仍然保持可连接。\n- 当用户创建一条新连线时，`onLineBeCreated` 会显示一条成功 toast，指出源端和目标端 id，但这条新连线不会被自动持久化。\n\n## 关键代码片段\n\n以下片段展示了该图如何被配置为一个固定场景：节点外观透明、默认连线为曲线，并启用了内置工具栏。\n\n```tsx\nconst graphOptions: RGOptions = {\n    debug: false,\n    layout: {\n        layoutName: 'fixed' // Use fixed layout for easier manual positioning\n    },\n    defaultNodeBorderWidth: 0,\n    defaultNodeColor: 'transparent',\n    defaultLineShape: RGLineShape.StandardCurve,\n    showToolBar: true\n};\n```\n\n以下片段展示了混合端点类型的写法：其中一条 fake line 通过 `RGInnerConnectTargetType.Node` 连接到整个节点，另一条则连接到一个具名内部端口。\n\n```tsx\n{\n    id: 'f-2', from: 'canvas-el-a',\n    to: 'n2',\n    toType: RGInnerConnectTargetType.Node,\n    text: 'Element to Node', color: '#9c27b0'\n},\n{ id: 'f-3', from: 'canvas-el-b', fromJunctionPoint: RGJunctionPoint.ltrb, to: 'n1-input-1', text: 'To Internal Target', color: '#009688' },\n{ id: 'f-801', from: 'canvas-el-b', fromJunctionPoint: RGJunctionPoint.ltrb, to: 'n2', toType: RGInnerConnectTargetType.Node, text: 'Element to Node', color: '#9c27b0' },\n```\n\n以下片段展示了通过图实例 API 执行挂载期初始化的流程。\n\n```tsx\ngraphInstance.addNodes(nodes);\nawait graphInstance.sleep(200); // Wait for nodes to finish rendering\ngraphInstance.moveToCenter(); // Center based on elements in the canvas slot\ngraphInstance.zoomToFit(); // Zoom to a suitable scale based on elements in the canvas slot\ngraphInstance.addFakeLines(fakeLines);\n```\n\n以下片段展示了新创建的连线会被立即拦截并反馈出来，但不会被持久化。\n\n```tsx\nconst onLineBeCreated = (lineInfo: {\n        lineJson: JsonLine,\n        fromNode: RGLineTarget | RGNode,\n        toNode: RGLineTarget | RGNode\n    }) => {\n    SimpleGlobalMessage.success(`New line created! From ${lineInfo.fromNode.id} to ${lineInfo.toNode.id}`);\n    // English：Here you need to add the new line to the graph yourself through the graphInstance.addLines method.\n}\n```\n\n以下片段展示了一个真实的内部端口如何存在于自定义节点内容内部。\n\n```tsx\n\u003CRGConnectTarget\n    targetId=\"n1-input-1\"\n    junctionPoint={RGJunctionPoint.left}\n>\n    \u003Cdiv className=\"bg-orange-500 text-white text-[10px] px-2 py-1 rounded cursor-pointer hover:bg-orange-600 transition-colors\">\n        Input 1\n    \u003C/div>\n\u003C/RGConnectTarget>\n```\n\n以下片段展示了画布插槽中的网格模式：普通 HTML 卡片被映射为 `RGConnectTarget`，因此即使网格布局发生变化，它们仍然可以充当端点。\n\n```tsx\n\u003Cdiv\n    className=\"p-4 bg-gray-200\"\n    style={{\n        display: 'grid',\n        gridTemplateColumns: `repeat(${gridCols}, 1fr)`,\n        gap: `${gridGap}px`\n    }}\n>\n    {gridItems.map(item => (\n        \u003CRGConnectTarget key={item.id} targetId={item.id}>\n```\n\n以下片段展示了这段 SCSS 覆盖样式，它让连线标签继承各自连线当前的颜色。\n\n```scss\n.rg-line-label {\n    background-color: var(--rg-line-color);\n    color: #fff;\n    font-size: 10px;\n}\n```\n\n## 本示例的独特之处\n\n比较数据将该示例归类为一个混合端点参考案例，而不是某个特定场景的查看器。它最显著的特点是覆盖面广：它在同一个场景中同时展示了 `RGConnectTarget` 在独立画布组件、完整图节点、节点内部端口，以及通过普通画布插槽 HTML 渲染的网格项中的用法。与那些仅停留在 `RGSlotOnCanvas` 中，或仅局限于节点内容内部的示例相比，这种组合更少见。\n\n与 `scene-network-use-canvas-slot` 相比，这个演示并不局限于一个纯画布 DOM 面板。它增加了 `RGSlotOnNode` 端口、通过 `RGInnerConnectTargetType.Node` 实现的显式整节点类型指定，以及 `onLineBeCreated` 作为用户自建连接的交接点。与 `node-content-lines` 相比，这里的重点并不只是节点内部的精确锚定，而是节点插槽目标、整节点和画布插槽端点之间的互操作性。与 `mix-layout-2` 和 `interest-group` 相比，关注点也不是重新布局编排或基于选择的浏览体验。它更像是一个紧凑的实验室，用于在同一场景中比较多种宿主类型之间的连接行为。\n\n实时网格控制也让它在 connect-target 示例中显得比较特殊。网格可以在运行时改变列数和间距，但其中的卡片仍然是有效端点。再结合不会持久化新连线的创建回调，这使得该示例非常适合作为团队在决定如何存储用户编辑之前，用来测试混合 DOM 目标行为的起点。\n\n## 这种模式还适用于哪里\n\n这种模式很适合那些将图节点与自由形态界面组件混合在一起的应用。典型例子包括：同时包含节点端口和浮动控制面板的工作流构建器、连接机架与画布级告警组件的设备仪表盘，以及用户需要连接表单字段、卡片或网格单元，而不只是完整节点的映射工具。\n\n当团队已经拥有一些应当继续保持为普通 DOM 的 HTML 片段时，它也可以作为一种迁移模式。通过将这些片段包裹进 `RGConnectTarget`，并按需使用节点插槽或画布插槽，团队就能在不把每个对象都重构为标准图节点主体的前提下，增加可见的关系连线路由能力。\n",false,500,1782615412273]