[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"example-markdown-content:zh:graph-instance-expand-collapse-api":3},{"markdown":4,"isPaidExample":5,"isTruncated":5,"charLimit":6},"# 使用 GraphInstance 展开或折叠目标节点\n\n## 这个示例构建了什么\n\n这个示例构建了一个紧凑的自左向右树形 demo，它的真正目的不是讲述数据故事，而是展示 API 控制。用户会看到一个全高页面，其中包含一个说明性页头、一个绿色提示区、一个操作按钮，以及下方一个样式极简的图谱画布。\n\n关键交互在于页头按钮。它会通过 provider 作用域内的 graph instance 查找节点 `b`，并在展开与折叠状态之间切换该分支。节点点击事件也已接入，但它只会记录被点击节点的标签，不会改变图谱。\n\n这里最值得关注的是完整的实例工作流：从 `RGHooks.useGraphInstance()` 获取当前 graph instance，加载数据，规范化视口，然后通过普通的外围 UI 驱动某个特定分支。\n\n## 数据是如何组织的\n\n数据在 `initializeGraph()` 中以内联方式声明为一个 `RGJsonData` 对象，包含 `rootId: 'a'`、一个扁平的 `nodes` 数组和一个扁平的 `lines` 数组。该结构描述了一个包含 16 个节点和 15 条连接的小层级结构，因此这个示例可以把重点放在实例方法的使用上，而不是远程加载或数据转换。\n\n在图谱加载之前，代码会先对连线列表做一步预处理：如果某条连线还没有 `id`，就为它赋值 `line-${index}`。完成这一步规范化后，数据集会被直接传给 `setJsonData()`。\n\n在真实应用中，同样这种扁平的 nodes-and-lines 结构可以表示组织分支、依赖树、课程大纲、审批链，或任何其他需要通过外部控件按 id 显示或隐藏已知分支的层级结构。\n\n## relation-graph 是如何使用的\n\n`index.tsx` 用 `RGProvider` 包裹整个示例，而 `MyGraph.tsx` 则通过 `RGHooks.useGraphInstance()` 获取当前 graph instance。一个只在挂载时执行的 `useEffect()` 会调用 `initializeGraph()`，该函数构建内联数据集，补齐缺失的连线 id，然后依次执行 `setJsonData()`、`moveToCenter()` 和 `zoomToFit()`。\n\n图谱使用 `layoutName: 'tree'`，并设置 `from: 'left'`、`treeNodeGapH: 120` 和 `treeNodeGapV: 10`，因此它呈现为一棵横向树。节点默认是矩形 `100x30` 区块，连线使用 `RGLineShape.StandardCurve`，连线锚点使用 `RGJunctionPoint.lr`，内置展开控制器放在右侧边缘，并启用了 `reLayoutWhenExpandedOrCollapsed`，因此分支切换后树会自动重新排布。\n\n这里没有自定义节点、连线、画布或视口插槽，也没有编辑工作流。本地 SCSS 文件只包含空的选择器骨架，因此这个示例基本保持 relation-graph 默认行为。可见 UI 中唯一使用到的共享本地依赖是来自通用 `DraggableWindow` 模块的 `SimpleUIButton`；该共享代码中定义的可拖拽面板和截图辅助功能在这里并未挂载。\n\n## 关键交互\n\n- 点击页头按钮会查找节点 `b`，并通过 `collapseNode(...)` 或 `expandNode(...)` 对它进行切换。\n- 因为启用了 `reLayoutWhenExpandedOrCollapsed`，这个程序化分支变化发生后，树会重新流式排布，而不是保留过时的间距。\n- 点击节点会触发 `onNodeClick`，但处理函数只会记录 `nodeItem.text`，因此节点检查只是次要内容，API 演示才是重点。\n\n## 关键代码片段\n\n这个包装器证明，demo 依赖 provider 上下文，然后才会通过 hooks 读取 graph instance。\n\n```tsx\nconst Demo = () => {\n    return (\n        \u003CRGProvider>\n            \u003CMyGraph />\n        \u003C/RGProvider>\n    );\n};\n```\n\n这段代码展示了 provider 作用域内的实例获取方式，以及横向树布局配置。\n\n```tsx\nconst MyGraph = () => {\n    const graphInstance = RGHooks.useGraphInstance();\n\n    const graphOptions: RGOptions = {\n        layout: {\n            layoutName: 'tree',\n            from: 'left',\n            treeNodeGapH: 120,\n            treeNodeGapV: 10\n        },\n```\n\n这段 options 配置说明，该示例针对一个自左向右的矩形树进行了调优，并会在展开状态变化后重新布局。\n\n```tsx\n        defaultNodeShape: RGNodeShape.rect,\n        defaultNodeWidth: 100,\n        defaultNodeHeight: 30,\n        defaultLineShape: RGLineShape.StandardCurve,\n        defaultNodeBorderWidth: 1,\n        defaultJunctionPoint: RGJunctionPoint.lr,\n        reLayoutWhenExpandedOrCollapsed: true,\n        defaultExpandHolderPosition: 'right',\n        debug: false\n```\n\n这段代码表明，图谱数据在加载前以内联方式组装为一棵扁平树。\n\n```tsx\nconst myJsonData: RGJsonData = {\n    rootId: 'a',\n    nodes: [\n        { id: 'a', text: 'a' },\n        { id: 'b', text: 'b' },\n        { id: 'b1', text: 'b1' },\n        // ...\n        { id: 'c3', text: 'c3' }\n    ],\n    lines: [\n```\n\n这一步预处理会在执行 `setJsonData()` 之前补齐缺失的连线 id。\n\n```tsx\nmyJsonData.lines.forEach((line, index) => {\n    if (!line.id) {\n        line.id = `line-${index}`;\n    }\n});\n```\n\n这段初始化顺序代码就是挂载时完整的图谱设置过程。\n\n```tsx\nawait graphInstance.setJsonData(myJsonData);\ngraphInstance.moveToCenter();\ngraphInstance.zoomToFit();\n```\n\n这个处理函数就是 API 演示的核心：外围 UI 解析一个已知节点 id，并直接切换该分支。\n\n```tsx\nconst callApi = () => {\n    const targetNodeId = 'b';\n    const targetNode = graphInstance.getNodeById(targetNodeId);\n    if (targetNode) {\n        if (targetNode.expanded === true) {\n            graphInstance.collapseNode(targetNode);\n        } else {\n            graphInstance.expandNode(targetNode);\n        }\n    }\n};\n```\n\n这个页头片段展示了，外部按钮如何作为主要教学界面呈现在图谱上方。\n\n```tsx\n\u003Cdiv className=\"px-4 py-2 bg-green-200 rounded mt-2 mb-2 flex gap-2\">\n    \u003CInfo size={16} /> The following examples demonstrate the effect of expanding/collapsing a specific node by calling the API interface:\n\u003C/div>\n\u003CSimpleUIButton onClick={callApi}>\n    Expanding / Collapsing Node \"b\"\n\u003C/SimpleUIButton>\n```\n\n## 这个示例的独特之处\n\n对比数据将这个示例放在 `customize-fullscreen-action`、`expand-gradually`、`expand-graph-step-by-step` 和 `adv-hide-2-show` 附近，但它的关注点比这些示例都更窄。它使用类似的 provider-and-instance 结构和同样通用的树形骨架，然后把几乎全部教学价值集中到一个可重复模式上：外围 UI 可以解析一个已知节点 id，并通过 graph instance 控制该分支。\n\n与 `customize-fullscreen-action` 和 `adv-hide-2-show` 相比，这个示例保持了简单的包装结构，并把加载后的实例控制作为主要教学点，而不是全屏目标控制或延迟首次可见。与 `expand-gradually` 相比，它不讲解启动时折叠加点击展开的披露方式。与 `expand-graph-step-by-step` 相比，它避免了递归播放和动画编排，而是专注于最小、确定性的控制案例。\n\n这使得它在真实需求不是“渲染一棵树”，而是“把一个普通页面控件连接到图中的一个已知分支”时，更适合作为起点。它的独特组合在于：基于 hook 的实例访问、内联扁平树数据、防御性的连线 id 规范化、立即执行的居中与适配初始化，以及一个会反复切换节点 `b` 的外部按钮，整体构成一棵朴素的自左向右树。\n\n## 这种模式还适用于哪里\n\n这种模式很适合迁移到以下场景：仪表盘中需要由筛选面板或摘要卡片展开某个已知子树；引导流程中需要帮助面板打开概念图的某个分支；以及组织结构或依赖关系查看器中，需要外围控件在不重建数据集的情况下，把注意力聚焦到某个预定义区域。\n\n它同样适用于教程页面和调试工具，这些场景需要单独演示一个具体的图谱操作。这个可复用思路很简单：把 graph instance 保持在 provider 作用域内，按 id 解析目标节点，然后让普通页面 UI 去触发实例方法，而不是强迫每个动作都从画布内部发起。\n",false,500,1782615436607]