双击编辑节点文字
这个示例展示固定布局 relation-graph,用自定义内联编辑器替换默认节点主体。它演示了模式控制的双击改名、键盘与失焦提交/取消行为、`graphInstance.updateNode()` 持久化,以及用于画布设置和图片导出的共享悬浮面板。
固定布局图中的节点文本内联编辑
这个示例构建了什么
这个示例构建了一个固定位置的关系图,节点标签可以在原位直接编辑。界面上可以看到带点状背景的画布、蓝色直线连线和一个浮动辅助窗口,而节点本身则展示了不同的边框、颜色、尺寸、形状以及固定位置设置。
主要的用户操作是维护标签。启用编辑后,双击节点会将普通标签切换为黄色的内联输入框,修改后的文本会直接写回图中,而无需重新加载整份数据集。
关键设计选择在于通过自定义节点插槽替换节点主体。这样一来,示例就可以在组件层级自行控制焦点处理、键盘行为、提交与取消规则,以及全局编辑开关。
数据如何组织
图数据在 initializeGraph() 中以内联方式声明为一个 RGJsonData 对象,其中包含 rootId、扁平的 nodes 数组以及 lines 数组。每条节点记录都带有固定的 x 和 y 坐标,且部分节点还包含可视化覆盖项,例如 borderWidth、borderColor、fontColor、color、width、height、fixed、className,或节点级别的 nodeShape。
在调用 setJsonData() 之前有两个很小的预处理步骤。代码会用 line-${index} 补齐缺失的连线 id,并将数值型的 nodeShape 转换为当前 API 所需的 RGNodeShape 枚举。完成这些规范化之后,整份数据只会加载一次,后续所有标签变更都通过定向的节点更新完成。
在真实产品中,同样的数据结构可以表示流程节点、设备标签、组织单元、平面图标注,或需要快速维护文本而不必打开独立表单的知识图谱实体。
relation-graph 的使用方式
入口组件用 RGProvider 包裹整个 demo,MyGraph 使用 RGHooks.useGraphInstance() 来初始化和变更图。主要选项设置了 fixed 布局、默认矩形节点、直线连线、边框连接点、蓝色连线颜色,以及线宽 2。由于布局是固定的,内联节点坐标决定了初始位置,而不是依赖运行时的树布局或力导布局。
自定义渲染路径以 RGSlotOnNode 为中心。每个渲染出来的节点都会被替换为 MyEditableNode,它接收运行时的 node、全局 isEditingEnabled 标志,以及一个通过 graphInstance.updateNode(...) 持久化文本变更的回调。当前源码没有使用连线插槽或活动视图插槽;这里值得关注的扩展点是节点插槽。
图实例 API 分两层使用。MyGraph 在挂载期间调用 setJsonData()、moveToCenter() 和 zoomToFit(),然后在确认标签编辑时调用 updateNode()。DraggableWindow 内部共享的 CanvasSettingsPanel 使用 RGHooks.useGraphStore() 读取当前滚轮和拖拽行为,使用 setOptions() 在运行时切换这些行为,并通过 prepareForImageGeneration()、getOptions() 和 restoreAfterImageGeneration() 执行导出流程。
样式分布在图选项和 CSS 两部分。SCSS 文件通过画布偏移与缩放变量以及径向渐变的点状背景来自定义 .relation-graph,而可编辑节点组件则提供了黄色输入框,以及在可编辑时显示的 cursor-text 光标提示。
关键交互
双击是主要入口。只有当全局复选框启用了该模式时,节点才会进入内联编辑,并且处理函数会阻止事件冒泡,这样图就不会把同一次手势解释为普通的节点点击。
开始编辑后,输入框会自动获得焦点并选中文本。按 Enter 或使输入框失焦会提交变更,按 Escape 则会恢复之前的节点文本并退出而不保存。
编辑开关是全局的,而不是按节点单独控制。浮动窗口中的复选框会更新 MyGraph 中的 isEditingEnabled,这个布尔值随后会传递给每一个通过插槽渲染的节点。
浮动窗口还增加了次级控制。它可以被拖动、最小化、展开为设置面板,可用来在滚动、缩放和无之间切换滚轮行为,可用来在选择、移动和无之间切换画布拖拽行为,也可以将当前图导出为图片。
关键代码片段
这段代码展示了固定布局图的配置,以及自定义节点渲染器所依赖的默认可视规则。
const graphOptions: RGOptions = {
defaultLineColor: '#43a2f1',
defaultLineWidth: 2,
layout: {
layoutName: 'fixed'
},
defaultNodeShape: RGNodeShape.rect,
defaultLineShape: RGLineShape.StandardStraight,
defaultJunctionPoint: RGJunctionPoint.border
};
这段代码说明示例会在加载数据前先对输入数据做规范化处理,然后再将固定布局图居中显示。
myJsonData.lines.forEach((line, index) => {
if (!line.id) {
line.id = `line-${index}`;
}
});
myJsonData.nodes.forEach(node => {
if (node.nodeShape === 0) node.nodeShape = RGNodeShape.circle;
if (node.nodeShape === 1) node.nodeShape = RGNodeShape.rect;
});
await graphInstance.setJsonData(myJsonData);
graphInstance.moveToCenter();
graphInstance.zoomToFit();
这段代码证明,relation-graph 的节点插槽正是将默认节点主体替换为可编辑渲染器的机制。
<RelationGraph options={graphOptions}>
<RGSlotOnNode>
{({ node, checked, dragging }: RGNodeSlotProps) => (
<MyEditableNode
node={node}
enableEditingMode={isEditingEnabled}
onNodeTextChange={onNodeTextChange}
/>
)}
</RGSlotOnNode>
</RelationGraph>
这段代码展示了自定义节点如何进入编辑模式,并立即为文本替换准备输入框。
useEffect(() => {
if (isEditing && inputRef.current) {
inputRef.current.focus();
inputRef.current.select();
}
}, [isEditing]);
const handleDoubleClick = (e: React.MouseEvent) => {
if (enableEditingMode) {
e.stopPropagation();
setIsEditing(true);
}
};
这段代码展示了提交与取消流程,包括最终通过 graphInstance.updateNode(...) 持久化文本的回调。
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
finishEditing();
} else if (e.key === 'Escape') {
setEditingText(node.text || '');
setIsEditing(false);
}
};
const finishEditing = () => {
if (editingText !== node.text) {
onNodeTextChange(node, editingText);
}
setIsEditing(false);
};
这段代码展示了共享设置面板如何通过当前图实例实时修改图行为。
<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 }); }}
/>
这个示例的独特之处
根据已准备的文档上下文,这个示例的独特之处并不在于共享的浮动窗口或 provider 与 hook 的组合方式,因为附近的 deep-each、investment 和 selections 等示例也复用了这套脚手架。更少见的组合是自定义 MyEditableNode 渲染器,加上受模式控制的双击编辑、自动聚焦与文本全选、Enter 或失焦提交、Escape 取消,以及在固定布局图上通过 updateNode() 持久化修改。
稀有度记录还表明,黄色内联编辑器状态、带点状背景的编辑画布,以及固定位置的样式示例节点,在这组示例中都较为少见。这使得该 demo 相比那些强调选择、遍历、尺寸调整或更广泛编辑器行为的示例,更适合作为节点标签维护的直接起点。
它也比完整的图编辑器更聚焦。代码不会添加节点、重新连接连线,也不会管理多步骤编辑状态。它的价值在于隔离出一个具体能力:用内联编辑器替换默认节点内容,并让持久化路径保持简洁。
这种模式还适用于哪里
这种模式很适合用于结构已经明确,但标签需要在上下文中快速修正的图表。示例包括工作流命名工具、流程图、组织架构图、设备图、拓扑标签,以及知识图谱整理界面。
生产版本可以保留相同的基于插槽的编辑策略,同时将内联示例数据替换为服务器数据,在调用 updateNode() 之前加入校验规则、保存审计历史,或为节点文本之外的字段打开更丰富的侧边面板。核心思想仍然不变:让图始终保持已加载状态,只编辑那个发生变化的标签。