JavaScript is required

GraphInstance Expand and Collapse API

这个示例展示如何用 `RGHooks.useGraphInstance()` 获取 provider 作用域 relation-graph 实例,加载小型内联树图并在挂载后规范化视口。随后演示一个聚焦控制模式:外部按钮按 id 定位节点 `b`,并通过 `expandNode()` / `collapseNode()` 切换该分支。

使用 GraphInstance 展开或折叠目标节点

这个示例构建了什么

这个示例构建了一个紧凑的自左向右树形 demo,它的真正目的不是讲述数据故事,而是展示 API 控制。用户会看到一个全高页面,其中包含一个说明性页头、一个绿色提示区、一个操作按钮,以及下方一个样式极简的图谱画布。

关键交互在于页头按钮。它会通过 provider 作用域内的 graph instance 查找节点 b,并在展开与折叠状态之间切换该分支。节点点击事件也已接入,但它只会记录被点击节点的标签,不会改变图谱。

这里最值得关注的是完整的实例工作流:从 RGHooks.useGraphInstance() 获取当前 graph instance,加载数据,规范化视口,然后通过普通的外围 UI 驱动某个特定分支。

数据是如何组织的

数据在 initializeGraph() 中以内联方式声明为一个 RGJsonData 对象,包含 rootId: 'a'、一个扁平的 nodes 数组和一个扁平的 lines 数组。该结构描述了一个包含 16 个节点和 15 条连接的小层级结构,因此这个示例可以把重点放在实例方法的使用上,而不是远程加载或数据转换。

在图谱加载之前,代码会先对连线列表做一步预处理:如果某条连线还没有 id,就为它赋值 line-${index}。完成这一步规范化后,数据集会被直接传给 setJsonData()

在真实应用中,同样这种扁平的 nodes-and-lines 结构可以表示组织分支、依赖树、课程大纲、审批链,或任何其他需要通过外部控件按 id 显示或隐藏已知分支的层级结构。

relation-graph 是如何使用的

index.tsxRGProvider 包裹整个示例,而 MyGraph.tsx 则通过 RGHooks.useGraphInstance() 获取当前 graph instance。一个只在挂载时执行的 useEffect() 会调用 initializeGraph(),该函数构建内联数据集,补齐缺失的连线 id,然后依次执行 setJsonData()moveToCenter()zoomToFit()

图谱使用 layoutName: 'tree',并设置 from: 'left'treeNodeGapH: 120treeNodeGapV: 10,因此它呈现为一棵横向树。节点默认是矩形 100x30 区块,连线使用 RGLineShape.StandardCurve,连线锚点使用 RGJunctionPoint.lr,内置展开控制器放在右侧边缘,并启用了 reLayoutWhenExpandedOrCollapsed,因此分支切换后树会自动重新排布。

这里没有自定义节点、连线、画布或视口插槽,也没有编辑工作流。本地 SCSS 文件只包含空的选择器骨架,因此这个示例基本保持 relation-graph 默认行为。可见 UI 中唯一使用到的共享本地依赖是来自通用 DraggableWindow 模块的 SimpleUIButton;该共享代码中定义的可拖拽面板和截图辅助功能在这里并未挂载。

关键交互

  • 点击页头按钮会查找节点 b,并通过 collapseNode(...)expandNode(...) 对它进行切换。
  • 因为启用了 reLayoutWhenExpandedOrCollapsed,这个程序化分支变化发生后,树会重新流式排布,而不是保留过时的间距。
  • 点击节点会触发 onNodeClick,但处理函数只会记录 nodeItem.text,因此节点检查只是次要内容,API 演示才是重点。

关键代码片段

这个包装器证明,demo 依赖 provider 上下文,然后才会通过 hooks 读取 graph instance。

const Demo = () => {
    return (
        <RGProvider>
            <MyGraph />
        </RGProvider>
    );
};

这段代码展示了 provider 作用域内的实例获取方式,以及横向树布局配置。

const MyGraph = () => {
    const graphInstance = RGHooks.useGraphInstance();

    const graphOptions: RGOptions = {
        layout: {
            layoutName: 'tree',
            from: 'left',
            treeNodeGapH: 120,
            treeNodeGapV: 10
        },

这段 options 配置说明,该示例针对一个自左向右的矩形树进行了调优,并会在展开状态变化后重新布局。

        defaultNodeShape: RGNodeShape.rect,
        defaultNodeWidth: 100,
        defaultNodeHeight: 30,
        defaultLineShape: RGLineShape.StandardCurve,
        defaultNodeBorderWidth: 1,
        defaultJunctionPoint: RGJunctionPoint.lr,
        reLayoutWhenExpandedOrCollapsed: true,
        defaultExpandHolderPosition: 'right',
        debug: false

这段代码表明,图谱数据在加载前以内联方式组装为一棵扁平树。

const myJsonData: RGJsonData = {
    rootId: 'a',
    nodes: [
        { id: 'a', text: 'a' },
        { id: 'b', text: 'b' },
        { id: 'b1', text: 'b1' },
        // ...
        { id: 'c3', text: 'c3' }
    ],
    lines: [

这一步预处理会在执行 setJsonData() 之前补齐缺失的连线 id。

myJsonData.lines.forEach((line, index) => {
    if (!line.id) {
        line.id = `line-${index}`;
    }
});

这段初始化顺序代码就是挂载时完整的图谱设置过程。

await graphInstance.setJsonData(myJsonData);
graphInstance.moveToCenter();
graphInstance.zoomToFit();

这个处理函数就是 API 演示的核心:外围 UI 解析一个已知节点 id,并直接切换该分支。

const callApi = () => {
    const targetNodeId = 'b';
    const targetNode = graphInstance.getNodeById(targetNodeId);
    if (targetNode) {
        if (targetNode.expanded === true) {
            graphInstance.collapseNode(targetNode);
        } else {
            graphInstance.expandNode(targetNode);
        }
    }
};

这个页头片段展示了,外部按钮如何作为主要教学界面呈现在图谱上方。

<div className="px-4 py-2 bg-green-200 rounded mt-2 mb-2 flex gap-2">
    <Info size={16} /> The following examples demonstrate the effect of expanding/collapsing a specific node by calling the API interface:
</div>
<SimpleUIButton onClick={callApi}>
    Expanding / Collapsing Node "b"
</SimpleUIButton>

这个示例的独特之处

对比数据将这个示例放在 customize-fullscreen-actionexpand-graduallyexpand-graph-step-by-stepadv-hide-2-show 附近,但它的关注点比这些示例都更窄。它使用类似的 provider-and-instance 结构和同样通用的树形骨架,然后把几乎全部教学价值集中到一个可重复模式上:外围 UI 可以解析一个已知节点 id,并通过 graph instance 控制该分支。

customize-fullscreen-actionadv-hide-2-show 相比,这个示例保持了简单的包装结构,并把加载后的实例控制作为主要教学点,而不是全屏目标控制或延迟首次可见。与 expand-gradually 相比,它不讲解启动时折叠加点击展开的披露方式。与 expand-graph-step-by-step 相比,它避免了递归播放和动画编排,而是专注于最小、确定性的控制案例。

这使得它在真实需求不是“渲染一棵树”,而是“把一个普通页面控件连接到图中的一个已知分支”时,更适合作为起点。它的独特组合在于:基于 hook 的实例访问、内联扁平树数据、防御性的连线 id 规范化、立即执行的居中与适配初始化,以及一个会反复切换节点 b 的外部按钮,整体构成一棵朴素的自左向右树。

这种模式还适用于哪里

这种模式很适合迁移到以下场景:仪表盘中需要由筛选面板或摘要卡片展开某个已知子树;引导流程中需要帮助面板打开概念图的某个分支;以及组织结构或依赖关系查看器中,需要外围控件在不重建数据集的情况下,把注意力聚焦到某个预定义区域。

它同样适用于教程页面和调试工具,这些场景需要单独演示一个具体的图谱操作。这个可复用思路很简单:把 graph instance 保持在 provider 作用域内,按 id 解析目标节点,然后让普通页面 UI 去触发实例方法,而不是强迫每个动作都从画布内部发起。