JavaScript is required

内置工具栏位置与主题切换

这个示例构建了一个全高的关系图查看器,其核心关注点是 relation-graph 的内置工具栏,而不是图数据本身。画布加载了一个单节点,保持默认工具栏可见,并在图上方放置了一个浮动的白色控制窗口。

运行时内置工具栏位置与主题切换

本示例构建的内容

这个示例构建了一个全高的关系图查看器,其核心关注点是 relation-graph 的内置工具栏,而不是图数据本身。画布加载了一个单节点,保持默认工具栏可见,并在图上方放置了一个浮动的白色控制窗口。

用户可以在纵向和横向两种工具栏方向之间切换,把它移动到三个水平锚点和三个垂直锚点位置,并应用三种由 wrapper 驱动的颜色主题。同一个辅助窗口还可以被拖动、最小化、打开为共享的画布设置浮层,并用于导出图片。这个示例的重点是对工具栏进行就地定制,而不是用自定义 slot 内容替换它。

数据如何组织

图数据在 initializeGraph() 内以内联方式创建,为一个 RGJsonData 对象,包含 rootId: 'a'、一个节点且没有连线。这里没有外部获取,没有派生出的布局载荷,也没有除组装这个字面量对象之外的预处理阶段。

setJsonData() 执行后,图实例会通过 moveToCenter()zoomToFit() 将视图重新居中并调整到适配状态。后续所有变化都作用于工具栏选项或 wrapper 类,而不是节点或边。在真实应用中,同样的模式可以承载更大的数据集,但这个几乎为空的图能让注意力集中在工具栏行为和 CSS 换肤上。

relation-graph 的使用方式

index.tsxRGProvider 包裹这个 demo,MyGraph.tsx 则通过 RGHooks.useGraphInstance() 读取当前活动实例。这个示例将图配置保持得很收敛:debug: false,以及从 React state 派生出的 toolBarDirectiontoolBarPositionHtoolBarPositionV。这里没有引入自定义布局、节点 slot、连线 slot 或编辑 API,因为图内容本身就是有意保持最小化的。

初始化通过图实例 API 完成。组件组装了一个内联数据集,调用 setJsonData(),然后调用 moveToCenter()zoomToFit()。第二个 effect 负责监听工具栏方向和位置状态,并通过 graphInstance.updateOptions(...) 把这些值推送到正在运行的图实例中。

工具栏样式是在 options 对象之外处理的。外层 wrapper 类 my-toolbar-style-* 会随着本地状态变化,SCSS 文件则把红色、绿色和蓝色的覆盖样式作用域限定到 .rg-toolbar。浮动辅助窗口来自共享的 DraggableWindow 组件。这个辅助窗口通过 RGHooks.useGraphStore()graphInstance.setOptions() 处理滚轮与拖动画布设置,并通过 prepareForImageGeneration()restoreAfterImageGeneration() 实现图片导出。这些工具在本示例中确实参与交互,但它们属于共享脚手架,而不是本例独有的教学重点。

关键交互

  • 样式选择器可以在默认工具栏皮肤与三种由 wrapper 驱动的主题变体之间切换。
  • 水平、垂直和方向选择器会更新 React state,而依赖这些状态的 effect 会通过 updateOptions(...) 把新的工具栏位置选项推送到运行中的图实例。
  • 浮动控制窗口可以通过标题栏拖动、最小化,并打开为设置浮层。
  • 共享设置浮层可以修改滚轮行为、修改画布拖拽行为,并下载当前图的图片。

关键代码片段

这段 options 配置表明,示例自身的图配置几乎全部围绕内置工具栏展开。

const graphOptions: RGOptions = {
    debug: false,
    toolBarDirection: toolBarDirection as 'v' | 'h',
    toolBarPositionH: toolBarPositionH as 'left' | 'center' | 'right',
    toolBarPositionV: toolBarPositionV as 'top' | 'center' | 'bottom'
};

这段初始化代码说明,数据集是一个单节点的内联载荷,并且视口会在加载后立刻被归一化。

const myJsonData: RGJsonData = {
    rootId: 'a',
    nodes: [
        { id: 'a', text: 'Set Toolbar Position' },
    ],
    lines: [
    ]
};

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

这一组 effect 是核心的运行时模式:先加载一次,再在已有的图实例上更新工具栏方向和位置。

const updateGraphOptions = () => {
    graphInstance.updateOptions({
        toolBarDirection: toolBarDirection as 'v' | 'h',
        toolBarPositionH: toolBarPositionH as 'left' | 'center' | 'right',
        toolBarPositionV: toolBarPositionV as 'top' | 'center' | 'bottom'
    });
};

useEffect(() => {
    updateGraphOptions();
}, [toolBarDirection, toolBarPositionH, toolBarPositionV]);

这段渲染代码说明,控制界面位于图外部,而样式切换则是通过修改 wrapper 类后缀实现的。

<div className={`my-graph my-toolbar-style-${toolBarStyle}`} style={{ height: '100vh' }}>
    <DraggableWindow initialLeft={200} initialTop={150}>
        <div className="c-option-name">Toolbar Style:</div>
        <SimpleUISelect
            data={[
                { value: '', text: 'Default' },
                { value: '1', text: 'Style 1' },
                { value: '2', text: 'Style 2' },
                { value: '3', text: 'Style 3' }
            ]}
            currentValue={toolBarStyle}
            onChange={(newValue: string) => setToolBarStyle(newValue)}
        />

这段 SCSS 代码证明,主题变化作用于内置的 .rg-toolbar,而不是用自定义标记替换整个工具栏。

&.my-toolbar-style-1 .relation-graph {
    .rg-toolbar {
        background-color: rgba(203, 7, 7, 0.2);
        color: #d9001b;
    }
}

&.my-toolbar-style-2 .relation-graph {
    .rg-toolbar {
        background-color: rgba(61, 150, 2, 0.8);
        color: #ffffff;
    }
}

这段共享辅助代码展示了,图片导出是通过图实例 API 实现的,而不是通过自定义画布 wrapper 实现的。

const downloadImage = async () => {
    const canvasDom = await graphInstance.prepareForImageGeneration();
    let graphBackgroundColor = graphInstance.getOptions().backgroundColor;
    if (!graphBackgroundColor || graphBackgroundColor === 'transparent') {
        graphBackgroundColor = '#ffffff';
    }
    const imageBlob = await domToImageByModernScreenshot(canvasDom, {
        backgroundColor: graphBackgroundColor
    });
    if (imageBlob) {
        downloadBlob(imageBlob, 'my-image-name');
    }
    await graphInstance.restoreAfterImageGeneration();
};

这个示例的独特之处

比较记录将这个 demo 放在 layout-centergee-thumbnail-diagramdiy-line-arrowcenter-layout-optionsline-style2 附近,但它讲解的是另一层面的定制方式。与 gee-thumbnail-diagram 相比,它保留了内置工具栏的可见性,并直接重新定位这个部件,而不是隐藏工具栏再挂载一个独立的 RGMiniView 浮层。

layout-centercenter-layout-optionsdiy-line-arrow 相比,这里的运行时控制不会重新布局图,不会修改连线标记,也不会批量更新节点和边。它们只是在图已经加载完成后更新 toolBarDirectiontoolBarPositionHtoolBarPositionV。比较记录和稀有度记录还特别指出,这个几乎为空的单节点画布本身就是示例设计的一部分:它去除了图结构带来的噪音,使工具栏行为更容易被观察。

line-style2 相比,这里的 wrapper 类作用于 .rg-toolbar,而不是 checked-line 选择器。这使得本示例更适合作为如下需求的起点:项目希望保留 relation-graph 的默认工具栏,在运行时移动它,并通过带作用域的 CSS 重新换肤,而不是用自定义 slot 内容替换它。

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

  • 需要品牌化工具栏皮肤、但又不想重建工具栏的白标图嵌入场景。
  • 工具栏位置需要移动,以避免与其他浮动面板或图例重叠的仪表盘或后台工具。
  • 用于验证滚轮缩放、拖拽模式和导出行为等交互默认值的内部 playground,这类场景围绕的是标准图查看器。
  • 需要极简数据集、以便团队在没有布局噪音的情况下评估控制界面的文档页或设计系统页面。