Skip to content

ADR-008: 使用 @vue-flow/node-toolbar 实现节点悬浮面板

Status

Accepted

Date

2026-03-27

Context

NodeShell 双模式需要在节点选中时显示悬浮面板(上方 Toolbar、下方 Prompt + ActionBar)。面板需要:

  1. 跟随节点位置,在画布缩放/平移时保持相对位置
  2. 渲染在节点 DOM 之外,不影响节点卡片高度
  3. 支持 isVisible 控制显示/隐藏

Options

A. CSS absolute 定位

在节点卡片内使用 position: absolute; bottom: 100% / top: 100% 将面板定位在节点上方/下方。

  • 优点:零依赖,实现简单
  • 缺点:面板在节点 DOM 内部,可能被 overflow: hidden 裁切;需要手动处理缩放变换下的偏移计算

B. Vue Teleport

<Teleport> 将面板渲染到画布 pane 层,手动计算位置。

  • 优点:不受节点 DOM 限制
  • 缺点:需要手动订阅节点位置变化和画布 viewport 变换,代码复杂度高

C. @vue-flow/node-toolbar(选择此方案)

Vue Flow 官方插件,提供 <NodeToolbar> 组件,内部自动处理位置计算和 viewport 变换。

  • 优点:官方维护,自动跟随缩放/平移,API 简洁(positionoffsetisVisible
  • 缺点:新增一个依赖包(~5KB gzipped),不提供 CSS(纯 JS 组件)

Decision

选择方案 C。理由:

  1. 项目已依赖 @vue-flow/core@vue-flow/background@vue-flow/minimap,node-toolbar 是同一生态的官方插件
  2. 自动处理 viewport 变换是核心需求,手写成本高且易出 bug
  3. 包体积极小,无 CSS 产物

Consequences

  • 根目录 package.json 新增 @vue-flow/node-toolbar 依赖
  • NodeShell 使用两个 <NodeToolbar> 实例(Position.TopPosition.Bottom
  • NodeToolbar 可见性通过 v-if 条件渲染控制(而非 :is-visible prop 绑定 computed),挂载时始终传 :is-visible="true"。这样未选中时 NodeToolbar 不在 DOM 中,避免了不必要的 viewport 订阅开销
  • 测试中需要 mock @vue-flow/node-toolbar 模块(vi.mock),因为它依赖 Vue Flow inject
  • 入场动画使用 opacity-only 的 panel-fade-in keyframe(fill-mode: both),NodeToolbar 不提供内置 transition 支持