JavaScript is required

节点展开按钮插槽自定义

这个示例展示如何用附着在节点上的自定义插槽徽章,替换 relation-graph 默认展开/收起控件。它加载本地层级数据,在启动时先折叠一个分支,并通过插槽提供的切换回调同时支持点击与触摸交互。

使用插槽渲染徽章的自定义节点展开控件

本示例构建的内容

本示例构建了一个全高层级查看器,其主要教学重点并不是数据集本身,而是替换 relation-graph 默认的展开/折叠控件。图谱初始时以居中的矩形树形结构展示类似子系统的节点,使用锚定到边框的直线连线,并在每个可展开节点的右侧渲染一个粉色胶囊形控件。

用户可以查看层级结构,并通过这个自定义控件切换分支。其中一个分支会在初始化后立即折叠,因此自定义控件在折叠和展开两种状态下都会可见,而不是只在手动交互之后才出现。

数据如何组织

数据来自一个本地异步辅助函数,它返回一个包含 rootIdnodeslines 的根 JSON 对象。节点记录仅包含简单的 idtext 字段,而连线记录则使用 fromtotext 描述父子关系,这使得该结构可以很容易替换为设备树、产品分解结构、子系统图或其他层级依赖数据。

在图谱加载之前,代码会执行一个很小的预处理步骤:遍历 lines,当某条关系记录尚未包含 id 时为其补上兜底 id。完成这一步标准化后,示例会把整理好的对象传给 setJsonData

relation-graph 的使用方式

本示例先用 RGProvider 包裹图谱,然后通过 RGHooks.useGraphInstance() 读取实例,这样初始化逻辑就可以保留在 React 组件内部,而不必使用外部 ref。组件挂载时,它会加载本地 JSON,用 setJsonData 注入数据,将画布移动到中心、适配视口,并折叠节点 6,从而创建一个有意渐进展示的初始视图。

图谱配置经过调整,以在突出自定义控件的同时保持视觉上下文尽可能简单。layout.layoutName 被设置为 center,启用了 reLayoutWhenExpandedOrCollapsed,并将默认展开控件的位置移动到了右侧。节点使用 RGNodeShape.rect,连线使用 RGLineShape.StandardStraight,并通过 RGJunctionPoint.border 将连线连接点锚定在节点边框上。

最关键的定制是 RelationGraph 上的 nodeExpandButtonSlot={NodeExpandHolder}。这个插槽会用一个 React 组件替换内置的分支控件,该组件会接收当前 node 以及框架提供的 expandOrCollapseNode 回调。示例引入的样式表目前并没有提供有意义的覆盖,因此这个控件的大部分外观都直接来自插槽渲染出来的标记和工具类。

关键交互

核心交互是通过这个自定义控件来切换分支,而不是使用 relation-graph 默认的展开按钮。该控件将同一个回调同时绑定到 onClickonTouchEnd,因此这个演示在桌面端和触摸设备上都可以使用,而无需单独实现移动端版本。

控件标签也会反映分支状态。当 node.expanded === false 时,控件显示 Open;否则显示 Close。再结合节点 6 的初始折叠效果,开发者就能清楚看到如何构建具备状态感知能力的替代控件。

关键代码片段

这个片段展示了图谱数据会在加载前先做标准化处理,随后移动到中心、适配视口,并在初始化时折叠一个分支。

const initializeGraph = async () => {
    const myJsonData: RGJsonData = await loadMockJsonData();
    if (myJsonData.lines) {
        myJsonData.lines = myJsonData.lines.map((line, index) => ({
            ...line,
            id: line.id || `line-${index}`
        }));
    }
    await graphInstance.setJsonData(myJsonData);
    graphInstance.moveToCenter();
    graphInstance.zoomToFit();
    graphInstance.collapseNode(graphInstance.getNodeById('6'));
};

这个片段展示了图谱配置如何在保持层级结构可读性的同时,让展开或折叠触发重新布局。

const graphOptions: RGOptions = {
    debug: false,
    reLayoutWhenExpandedOrCollapsed: true,
    defaultExpandHolderPosition: 'right',
    defaultNodeShape: RGNodeShape.rect,
    defaultLineShape: RGLineShape.StandardStraight,
    defaultJunctionPoint: RGJunctionPoint.border,
    layout: {
        layoutName: 'center'
    }
};

这个片段证明该示例用自定义插槽渲染器替换了内置的展开控件。

<RelationGraph
    options={graphOptions}
    nodeExpandButtonSlot={NodeExpandHolder}
/>

这个片段展示了实际的插槽约定:自定义控件会读取 node.expanded,并对点击和触摸输入都使用提供的切换回调。

const NodeExpandHolder: React.FC<RGNodeExpandHolderProps> = ({
    node,
    expandOrCollapseNode
}) => {
    return (
        <div className="absolute left-[100%] top-[50%] transform translate-y-[-50%] translate-x-[5px]">
            <div
                className="pointer-events-auto cursor-pointer rounded-lg border px-2 h-8 bg-pink-400 hover:bg-pink-800 hover:text-white flex place-items-center justify-center"
                onClick={expandOrCollapseNode}
                onTouchEnd={expandOrCollapseNode}
            >
                {node.expanded === false ? "Open" : "Close"}
            </div>
        </div>
    );
};

本示例的独特之处

与附近的示例相比,这个示例异常聚焦于 nodeExpandButtonSlot 这一约定本身。对比数据表明,graph-instance-api 也展示了通过实例 API 控制分支,但那个示例讲的是外部命令式控制,而本示例讲的是如何在图谱表面内部替换节点本地控件。

它也比 built-in-slots 更聚焦。后者会概览多个覆盖层插槽,而本示例专注于一个会立即带来行为变化的插槽:它读取 node.expanded,渲染与状态对应的文本,并直接调用插槽提供的 expandOrCollapseNode 回调。

另一个区别性的细节是右侧偏移的自定义徽章、onClickonTouchEnd 的双绑定,以及在加载后立即折叠节点 6 的组合。这意味着图谱一加载,自定义控件就会以真实的折叠状态出现,因此与完全展开的演示相比,它更适合作为自定义分支控件开发的起点。

这种模式还适用于哪些场景

这种模式很适合迁移到产品结构查看器、设备或子系统图、组织层级图以及知识树等场景中,尤其是在默认展开标记太小、过于抽象,或与整个应用的视觉风格不一致时。

当项目需要带有明确文案、适合触摸点击区域或自定义品牌样式的分支控件时,它也很有用。同样的插槽方案还可以扩展为图标按钮、状态感知徽章、带权限控制的展开控件,或者在仍然把实际分支状态切换委托给 relation-graph 提供回调的同时触发埋点分析的控件。