选中连线自定义工具栏
这个示例展示如何把自定义悬浮工具栏附着到当前选中的 relation-graph 连线上。工具栏会跟随连线实时几何位置,可修改线色、线形、线宽或删除连线;外围辅助窗还提供画布设置与图片导出。
自定义选中连线工具栏
这个示例构建了什么
这个示例构建了一个小型的 relation-graph 编辑工作区:当用户点击一条已有连线时,会在该连线上方打开一个浮动工具栏。用户可以通过这个工具栏修改当前选中连线的颜色、形状和宽度,也可以直接删除这条连线。
整体视觉效果刻意更偏向编辑器,而不是某个特定业务场景。点状画布背景、辅助窗口以及附着在图谱上的覆盖层,共同让这个示例更像一个聚焦于自定义连线维护的参考实现。这里的关键点在于,连线工具栏并不是固定在屏幕上的 UI;当图谱移动或缩放时,它会跟随当前选中连线的实时几何位置一起移动。
数据是如何组织的
图数据以内联方式组织成一个 RGJsonData 对象,其中包含 rootId、一个较小的 nodes 数组以及一个 lines 数组。大多数连线都继承图谱的默认配置,而连线 l4 则预先设置为正交形状,并带有显式的连接点,因此画布初始状态同时展示了默认的曲线样式,以及一个预配置好的例外连线。
在调用 setJsonData() 之前,没有额外的预处理流程。唯一有意义的准备工作,就是内联定义这份示例数据集,以及对 l4 做逐条连线覆盖。在生产环境图谱中,同样的数据结构可以用来表示工作流分支、依赖关系、审批路径,或任何以边为核心、且用户工作流中包含连线样式调整或删除操作的数据集。
relation-graph 是如何使用的
该示例在入口层使用 RGProvider,这样嵌套组件就可以消费 relation-graph 的 hooks。在 MyGraph 内部,RGHooks.useGraphInstance() 驱动完整生命周期:通过 setJsonData() 加载内联 JSON,使用 zoomToFit() 适配视图,借助 updateLine() 更新连线属性,调用 removeLine() 删除连线,并通过 setEditingNodes() 和 setEditingLine() 协调当前编辑目标。
图谱配置很少,但都是有意为之。示例将 defaultLineShape 设置为 RGLineShape.StandardCurve,将 defaultJunctionPoint 设置为 RGJunctionPoint.ltrb,并将 defaultLineColor 设置为 #00a63e,从而建立起绿色曲线这一基线样式,之后工具栏再按需覆盖这些属性。
这里最重要的 relation-graph 技术点是自定义覆盖层模式。RGSlotOnView 在图谱表面挂载了两个组件:RGEditingConnectController 和自定义的 MyLineToolbar。MyLineToolbar 通过 RGHooks.useEditingLine() 读取当前选中的连线及其实时起止点,再基于中点计算平移变换,这样工具栏就能附着在该连线上,而不是固定在浏览器视口上。
该示例还自定义了周边工作区。SCSS 文件添加了一个会随图谱变换变量同步缩放的点状编辑背景,共享的 DraggableWindow 则通过 RGHooks.useGraphStore() 读取图谱配置,使用 setOptions() 在运行时暴露滚轮与拖拽行为的调整能力,并通过 prepareForImageGeneration() 和 restoreAfterImageGeneration() 将图谱导出为图片。
关键交互
点击一条连线后,这条连线会成为当前激活的编辑目标,随后自定义工具栏会显示在它的上方。从这一刻起,这个工具栏就成为连线维护的主要操作界面。
点击颜色色块、形状选项或宽度选项后,会对当前激活的连线应用一次有针对性的 updateLine() 更新。这让编辑模型保持得很简单:只处理一条选中连线、一个浮动操作面板,以及立即可见的视觉反馈。
点击删除按钮会移除当前激活的连线,显示一条警告风格的全局消息,清除当前编辑中的连线,并重置选中状态。因此,删除流程同时处理了图谱数据变更和相关 UI 清理。
节点点击和画布手势负责管理编辑目标的切换。普通节点点击会替换当前编辑节点集合,带修饰键的节点点击会切换节点是否属于集合,而点击空白画布则会同时清除节点和连线的编辑状态。当画布处于框选模式时,框选结束会用选区视图内的节点替换当前编辑节点集合。
关键代码片段
下面这个片段展示了内联图数据,以及为 l4 预设的逐条连线例外配置。
const myJsonData: RGJsonData = {
rootId: 'a',
nodes: [
{ id: 'a', text: 'Border color' },
{ id: 'a1', text: 'No border' },
{ id: 'a2', text: 'Plain' },
{ id: 'a1-1', text: 'Text Node' },
{ id: 'a1-4', text: 'XXX' },
{ id: 'b', text: 'Font color' },
// ...
],
lines: [
{ id: 'l1', text: 'Line1 Text', from: 'a', to: 'b' },
{ id: 'l4', text: 'Line4 Text', from: 'a', to: 'a2', lineShape: RGLineShape.StandardOrthogonal, fromJunctionPoint: RGJunctionPoint.left, toJunctionPoint: RGJunctionPoint.right },
// ...
]
};
下面这个片段展示了点击与选择事件如何在节点、连线和空白画布之间交接编辑状态。
const onNodeClick = (nodeObject: RGNode, $event: RGUserEvent) => {
if ($event.shiftKey || $event.ctrlKey || ($event.metaKey && !$event.altKey)) {
graphInstance.toggleEditingNode(nodeObject);
} else {
graphInstance.setEditingNodes([nodeObject]);
}
graphInstance.setEditingLine(null);
};
const onLineClick = (lineObject: RGLine, linkObject: RGLink) => {
graphInstance.setEditingLine(lineObject);
};
下面这个片段展示了自定义覆盖层如何直接挂载到图谱视图中,而不是放在单独的侧边栏里。
<RelationGraph
options={graphOptions}
onCanvasSelectionEnd={onCanvasSelectionEnd}
onCanvasClick={onCanvasClick}
onNodeClick={onNodeClick}
onLineClick={onLineClick}
>
<RGSlotOnView>
<RGEditingConnectController />
<MyLineToolbar onLinePropChange={onLinePropChange} onRemoveLine={onRemoveLine} />
</RGSlotOnView>
</RelationGraph>
下面这个片段展示了为什么工具栏能够跟随选中连线的几何位置,并直接应用属性修改。
const editingLine = RGHooks.useEditingLine();
const toolbarXyOnCanvas = {
x: (editingLine.startPoint.x + editingLine.endPoint.x) / 2,
y: Math.min(editingLine.startPoint.y, editingLine.endPoint.y),
};
return (
editingLine.line ? <div
style={{
transform: `translate(-50%, -100%) translate(${toolbarXyOnCanvas.x}px, ${toolbarXyOnCanvas.y - 20}px)`
}}
>
下面这个片段展示了连线操作界面本身:属性选项通过 onLinePropChange(...) 调用,删除操作则通过 onRemoveLine(...) 触发。
{[
{ value: RGLineShape.StandardStraight, text: 'Straight' },
{ value: RGLineShape.StandardOrthogonal, text: 'Orthogonal' },
{ value: RGLineShape.StandardCurve, text: 'Bezier' }
].map((shape) => (
<div
key={shape.value}
onClick={() => onLinePropChange(editingLine.line!, 'lineShape', shape.value)}
>
{shape.text}
</div>
))}
这个示例的独特之处
与附近的 change-line-path、change-line-vertices 和 change-line-text 等示例相比,这个示例改变的是“选中连线的控制界面”,而不是“选中连线的编辑目标”。那些示例会保留内置的 RGEditingLineController,用于直接操作路径、端点或标签。而这个示例没有在渲染后的图谱中显示该控制器,而是通过 RGSlotOnView 配合 RGHooks.useEditingLine(),把一个自定义浮动操作卡片附着到当前激活的连线上。
它还组合了几个在示例集中相对少见的特性:由选择驱动的编辑目标管理、通过 updateLine() 与 removeLine() 实现运行时连线变更、基于实时连线几何信息的中点覆盖层定位,以及一个暴露画布设置与导出操作的共享辅助窗口。点状画布和辅助窗口本身并不独特,但“自定义选中连线工具栏”这一模式才是这里真正的核心。
与 editor-button-on-line 相比,这个示例更窄、更聚焦。它整体承担的编辑器工作更少,但在选中连线属性编辑上走得更深:通过一个专用覆盖层同时提供颜色、形状、宽度和删除操作。与 line-vertex-on-node 相比,它关注的是在连线被选中之后如何维护一条已有连线,而不是从一个选中节点创建新的连接。
这个模式还适用于哪里
这种模式非常适合那些边本身承载工作流含义,并且需要轻量级内联操作的图谱工具。例如审批路径编辑器、依赖关系图、API 调用图、网络拓扑维护工具,以及那些用户需要在不打开模态框的情况下,直接重设样式、停用或移除某条连接的流程图。
同样的做法也适用于需要品牌化或业务定制边控件的产品。团队可以继续使用 relation-graph 提供布局、视口行为和编辑状态管理,但将默认的选中连线 UI 替换为业务专属操作,例如修改优先级、分配负责人、切换路由策略,或打开一个自定义的边详情面板。