根节点左右分支显隐切换
这个示例渲染以根为中心的双向树,根节点提供左右两侧独立切换按钮。每个按钮通过筛选 `lot.level` 相关节点来隐藏或显示一侧层级,并复用共享示例工具提供画布设置和图片导出。
以根节点为中心并可分别切换左右分支的树形图
这个示例构建了什么
这个示例构建了一个以单个根节点为中心的双向树。用户可以在左侧看到传入分支,在右侧看到传出分支,并且在根节点外侧放置了两个粉色操作按钮,因此每一侧都可以独立显示或隐藏。
这个示例的重点并不是通用的展开或折叠行为,而是一个使用 RGSlotOnNode 构建的自定义根节点控制界面,其中一个按钮作用于层级结构的左侧,另一个按钮作用于右侧。
数据是如何组织的
图数据以内联方式声明为一个 RGJsonData 对象,其中包含 rootId: 'a'、13 个节点和 12 条连线。组件在布局之前不会对数据集做预处理。相反,它会把 nodes 和 lines 数组添加到图实例中,执行 doLayout(),然后再把两个运行时标记 leftExpanded 和 rightExpanded 写到根节点上。
这种结构很适合真实场景中的数据,即一个焦点实体需要同时展示两个方向上的邻域,例如上游和下游血缘、父子关系、供应商与客户树,或者围绕某个事件的原因与结果分支。
relation-graph 的使用方式
这个示例包裹在 RGProvider 中,并通过 RGHooks.useGraphInstance() 驱动初始化以及后续更新。图使用树形布局,配置了 treeNodeGapH: 150、RGLineShape.StandardCurve、RGJunctionPoint.lr、灰色默认连线颜色以及沿路径跟随的连线标签。内置工具栏保持启用,并水平放置在右下角。
最重要的自定义点是 RGSlotOnNode。只有当 node.lot.level === 0 时,它才会替换默认节点主体,这使根节点成为一个特殊的交互目标,而非根节点则继续保持为简单的文本块。随后,示例依赖实例 API,例如 addNodes、addLines、doLayout、getRootNode、getNodeRelatedNodes、updateNodeData、updateNode、moveToCenter 和 zoomToFit。
浮动说明面板和设置覆盖层来自共享的 DraggableWindow 辅助组件,而不是示例专属的图逻辑。该辅助组件使用 RGHooks.useGraphStore() 和 setOptions(...) 来切换滚轮与拖拽行为,并且通过 prepareForImageGeneration() 和 restoreAfterImageGeneration() 支持导出图片。本地 SCSS 文件实际上更像是占位符,因此可见样式主要由自定义节点插槽标记和共享窗口组件提供。
关键交互
- 点击左侧根节点按钮,会切换那些计算后
lot.level为负值的关联节点的可见性。 - 点击右侧根节点按钮,会切换那些计算后
lot.level为正值的关联节点的可见性。 - 根节点按钮会根据根节点上保存的
leftExpanded和rightExpanded标记,在加号与减号图标之间切换。 - 只有根节点会获得这些额外交互控件;其他所有节点都保持为只读文本。
- 浮动辅助窗口可以被拖动、最小化、重新打开,并切换为设置覆盖层。
- 设置覆盖层可以修改滚轮行为、修改画布拖拽行为,并将当前图下载为图片。
关键代码片段
这个片段表明,该示例从静态内联树数据开始,而不是按需加载或重建数据。
const treeJsonData: RGJsonData = {
rootId: 'a',
nodes: [
{ id: 'a', text: 'Root Node a', width: 120, height: 80 },
{ id: 'R-b', text: 'R-b' },
{ id: 'R-c', text: 'R-c' },
{ id: 'R-c-1', text: 'R-c-1' },
{ id: 'R-c-2', text: 'R-c-2' },
{ id: 'R-d', text: 'R-d' },
{ id: 'b', text: 'b' },
这个片段展示了初始化流程:加载节点和连线、执行布局、添加根节点元数据,然后让视口适配图内容。
const initializeGraph = async () => {
graphInstance.addNodes(treeJsonData.nodes);
graphInstance.addLines(treeJsonData.lines);
await graphInstance.doLayout();
const rootNode = graphInstance.getRootNode();
graphInstance.updateNodeData(rootNode, {
leftExpanded: true,
rightExpanded: true,
});
graphInstance.moveToCenter();
graphInstance.zoomToFit();
};
这个片段证明,左侧可见性并不是由内置展开控制器来控制的。它是根据关联节点以及 lot.level 中的布局侧别值计算出来的。
const toggleRootNodeLeft = async () => {
const rootNode = graphInstance.getRootNode();
if (rootNode) {
graphInstance.updateNodeData(rootNode, {
leftExpanded: !rootNode.data.leftExpanded
});
const relatedNodes = graphInstance.getNodeRelatedNodes(rootNode);
const leftNodes = relatedNodes.filter(n => n.lot.level < 0);
leftNodes.forEach(node => {
graphInstance.updateNode(node, {
hidden: !rootNode.data.leftExpanded
});
});
}
};
这个片段展示了根节点如何获得一个自定义交互界面,而所有其他节点都回退为纯文本渲染。
<RGSlotOnNode>
{({ node }) => {
return node.lot.level === 0 ? (
<div className="px-6 py-1 w-full h-full flex place-items-center justify-center text-xs">
<div className="px-3 py-0.5 bg-gray-100 bg-opacity-30 rounded text-black text-sm">
{node.text}
</div>
{/* left and right buttons omitted */}
</div>
) : (
<div className="px-6 py-1 w-full h-full flex place-items-center justify-center text-xs">
{node.text}
</div>
);
}}
</RGSlotOnNode>
这个片段表明,画布设置和导出属于共享的演示工具,而不是这个示例最核心的专属图行为。
<SettingRow
label="Wheel Event:"
options={[
{ label: 'Scroll', value: 'scroll' },
{ label: 'Zoom', value: 'zoom' },
{ label: 'None', value: 'none' },
]}
value={wheelMode}
onChange={(newValue: string) => { graphInstance.setOptions({ wheelEventAction: newValue }); }}
/>
这个示例的独特之处
根据对比数据,这个示例在与其他与展开相关的演示一起阅读时最具区别性。与 expand-animation 和 expand-gradually 相比,它并不关注 relation-graph 内置的展开控制器、初始折叠状态,或展开后的重新布局策略。它的核心启发是一个自定义根节点 UI,把同一层级结构相反两侧拆分成两个独立操作。
与 open-all-close-all 相比,它的控制范围要窄得多,也更有针对性。后者会编排整个图的递归展开与折叠,而这个示例保持数据集静态,只对与根节点相关的节点立即应用可见性变化。
与其他 RGSlotOnNode 示例(例如 element-connect-to-node)相比,这里的自定义插槽较少被用作通用视觉组合技巧,而更多被用作只服务于根节点的行为界面。真正突出的组合在于:一个居中的双向树、根节点元数据标记、通过 getNodeRelatedNodes(...) 加上 lot.level 的方向过滤,以及对现有节点直接执行 hidden 更新。
这种模式还能用在哪里
这种模式非常适合用于以一个中心实体为核心、并且需要分别控制相反关系方向的查看器。示例包括上游与下游数据血缘、组织视图中的管理者与下属分支、供应商与客户依赖关系,以及事故分析中的原因与结果图。
当产品团队希望使用一套在外观和行为上都不同于 relation-graph 默认展开控件的自定义控制时,这种方式也很有价值。相同的方法还可以扩展到根节点范围过滤、方向强调,或一键分支摘要,而无需改变底层图数据集格式。