恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
OpenPencil SDK 指南:使用 useToolbar 读取 ToolbarRoot 无头工具栏上下文
首页
资讯中心
/
OpenPencil SDK 指南:使用 useToolbar 读取 ToolbarRoot 无头工具栏上下文
OpenPencil SDK 指南:使用 useToolbar 读取 ToolbarRoot 无头工具栏上下文
发布时间:2026/10/9 12:38:45
前端桌面应用AI 应用MCP 服务【免费下载链接】open-pencilAI-native design editor. Open-source Figma alternative.项目地址https://gitcode.com/gh_mirrors/op/open-pencil点击查看免费下载导读在 OpenPencil 的 Vue SDKopen-pencil/vue中ToolbarRoot是负责提供工具栏上下文的无头headless结构原语而useToolbar()则是让任何自定义后代组件读取这份上下文的官方入口。本文将围绕useToolbar()展开先剖析它返回的ToolbarContext上下文形状再结合ToolbarRoot、ToolbarItem的源码说明上下文的提供与消费机制最后给出可运行的自定义编辑器外壳示例帮助你用完全自有的按钮、样式与布局搭建工具栏同时复用 OpenPencil 编辑器核心的工具状态管理能力。useToolbar 是什么依据官方 API 文档packages/docs/it/programmable/sdk/api/advanced/use-toolbar.md的定义useToolbar()restituisce il contesto delToolbarRootpiù vicino.useToolbar()返回最近的ToolbarRoot提供的上下文。一句话概括useToolbar()读取由最近的ToolbarRoot提供的局部工具栏上下文。它面向的场景非常明确——当你在ToolbarRoot之下编写自定义后代组件且这些组件需要访问当前编辑器的工具列表tools当前激活的工具状态activeTool选择/切换工具的动作setTool以及展开/关闭工具 flyout子工具弹出面板的动作toggleFlyout/closeFlyout。时才需要调用它。源码位置与实现useToolbar()的实现位于 packages/vue/src/primitives/Toolbar/context.ts核心逻辑只有数行export const TOOLBAR_KEY: InjectionKeyToolbarContext Symbol(toolbar) export function provideToolbar(ctx: ToolbarContext) { provide(TOOLBAR_KEY, ctx) } export function useToolbar(): ToolbarContext { const ctx inject(TOOLBAR_KEY) if (!ctx) throw new Error([open-pencil] useToolbar() called outside ToolbarRoot) return ctx }可以看到它的底层机制是 Vue 的provide/injectTOOLBAR_KEY是一个类型为InjectionKeyToolbarContext、值为Symbol(toolbar)的注入键用于隔离命名空间、避免与外部应用的注入键冲突provideToolbar(ctx)由ToolbarRoot在挂载时调用把上下文对象注入到组件树中useToolbar()调用inject(TOOLBAR_KEY)读取上下文如果注入键不存在即调用点不在任何ToolbarRoot之内会立即抛出错误[open-pencil] useToolbar() called outside ToolbarRoot。这是一个刻意的快速失败设计——它保证上下文访问不可能是隐式的undefined从而让错误的组件结构在运行时被立刻发现。useToolbar与ToolbarContext类型均由open-pencil/vue的包入口统一导出见 packages/vue/src/index.tsexport { ToolbarRoot, ToolbarItem, useToolbar } from #vue/primitives/Toolbar export type { ToolbarContext } from #vue/primitives/ToolbarToolbarContextuseToolbar 返回的上下文形状调用useToolbar()返回的是一个ToolbarContext对象其完整字段定义同样在 context.ts 中export interface ToolbarContext { editor: Editor tools: EditorToolDef[] activeTool: ComputedRefTool flyoutSelections: ReadonlyMapTool, Tool expandedFlyout: RefTool | null setTool: (tool: Tool) void toggleFlyout: (tool: Tool) void closeFlyout: () void }逐个字段说明其语义字段类型说明editorEditor当前编辑器实例来自open-pencil/core/editor可访问画布、场景图与全部编辑器 APItoolsEditorToolDef[]工具栏渲染所用的工具定义列表默认取EDITOR_TOOLSactiveToolComputedRefTool当前激活的工具响应式只读直接绑定editor.state.activeToolflyoutSelectionsReadonlyMapTool, Tool各工具当前在 flyout 中记忆的选中子工具例如在 FRAME 组里曾选过 SECTION会记在这里expandedFlyoutRefTool \| null当前展开的 flyout 所属工具键null表示全部收起setTool(tool: Tool) void选择工具写入编辑器状态并关闭 flyouttoggleFlyout(tool: Tool) void展开/收起指定工具的 flyoutcloseFlyout() void收起所有 flyout其中Tool是编辑器的工具类型如SELECT、FRAME、RECTANGLE等EditorToolDef是工具定义结构均来自open-pencil/core/editor。上下文的提供方ToolbarRoot 做了什么useToolbar()能读到什么完全取决于ToolbarRoot注入了什么。ToolbarRoot是无头原语它不渲染任何按钮、图标或样式只负责收集编辑器状态、组装上下文并provide给后代然后把渲染完全交给传入的默认插槽slot。见 packages/vue/src/primitives/Toolbar/ToolbarRoot.vuescript setup langts import { computed, reactive, ref, watch } from vue import { EDITOR_TOOLS } from open-pencil/core/editor import type { EditorToolDef, Tool } from open-pencil/core/editor import { useEditor } from #vue/editor/context import { provideToolbar } from #vue/primitives/Toolbar/context const { tools EDITOR_TOOLS } defineProps{ tools?: EditorToolDef[] }() const editor useEditor() const activeTool computed(() editor.state.activeTool) const expandedFlyout refTool | null(null) const flyoutSelections reactive(new MapTool, Tool()) watch( activeTool, (currentTool) { for (const tool of tools) { if (tool.flyout?.includes(currentTool)) { flyoutSelections.set(tool.key, currentTool) } } }, { immediate: true } ) function setTool(tool: Tool) { editor.setTool(tool) expandedFlyout.value null } function toggleFlyout(tool: Tool) { expandedFlyout.value expandedFlyout.value tool ? null : tool } function closeFlyout() { expandedFlyout.value null } /script关键机制逐条拆解可选的toolspropToolbarRoot接收可选的tools?: EditorToolDef[]不传时默认使用核心包导出的EDITOR_TOOLS见下文工具定义一节传入时则完全接管工具列表的渲染来源——这正是自定义工具栏的切入点。响应式激活状态activeTool是computed(() editor.state.activeTool)因此useToolbar().activeTool永远是编辑器实时状态的响应式投影任何setTool调用都会自动触发依赖它的 UI 更新。flyout 记忆逻辑watch监听activeTool变化一旦当前工具命中某个工具定义的flyout列表例如RECTANGLE的 flyout 包含[RECTANGLE, LINE, ELLIPSE, POLYGON, STAR]就把这个子工具记入flyoutSelections实现上次在这个组里选了什么的 UI 记忆expandedFlyout则单独管理展开状态。动作与注入setTool委托给editor.setTool(tool)并关闭 flyout最后通过provideToolbar({ editor, tools, activeTool, flyoutSelections, expandedFlyout, setTool, toggleFlyout, closeFlyout })注入整棵子树。ToolbarRoot的模板把全部上下文以插槽属性暴露出来ToolbarRoot.vuetemplate slot :toolstools :active-toolactiveTool :flyout-selectionsflyoutSelections :expanded-flyoutexpandedFlyout :actionsactions / /template这意味着插槽属性和useToolbar()只是同一份上下文的两种消费方式简单场景可以直接用v-slot解构深度嵌套的自定义组件则更适合用useToolbar()就地读取。消费方示例ToolbarItem 内部正是靠 useToolbar 工作ToolbarItem是官方提供的最小消费组件它本身就在内部调用useToolbar()是研究该钩子用法的第一手范例packages/vue/src/primitives/Toolbar/ToolbarItem.vuescript setup langts import { computed } from vue import type { Tool } from open-pencil/core/editor import { useToolbar } from #vue/primitives/Toolbar/context const { tool } defineProps{ tool: Tool }() const { activeTool, setTool } useToolbar() const isActive computed(() activeTool.value tool) const actions { select: () setTool(tool) } /script template slot :activeisActive :actionsactions :tooltool / /templateToolbarItem展示的正是官方文档所说useToolbar()的三大用途读取工具通过setTool(tool)选择当前按钮对应的工具读取激活状态activeTool.value tool计算出isActive响应式布尔值暴露选择动作把select动作通过插槽属性暴露给真正渲染按钮的自定义内容。由于useToolbar()读取的是最近的ToolbarRoot上下文ToolbarItem可以放心嵌套在任意深度的自定义布局中而不需要逐层透传 props。实战用 ToolbarRoot useToolbar 搭建自定义编辑器外壳把useToolbar()用于你自己的组件才能体现自定义外壳Custom Editor Shell的价值。SDK 文档给出了一套推荐的分层结构见 custom-editor-shell.mdopen-pencil/core负责创建编辑器createEditoropen-pencil/vue将其适配为 Vue 组合式 API 与无头原语provideEditor、CanvasRoot、ToolbarRoot等你的应用负责外壳、样式与产品级交互——这正是 SDK 存在的主要原因内建的 OpenPencil 应用只是众多可能外壳中的一种你完全可以为嵌入式编辑器、内部素材工具、模板编辑器、标注界面或 AI 辅助编辑面搭建完全不同的外壳。自定义工具栏组件首先定义一个完全自有的工具栏按钮组件用useToolbar()取代ToolbarItem的默认行为!-- MyToolButton.vue -- script setup langts import { computed } from vue import { useToolbar } from open-pencil/vue import type { Tool } from open-pencil/core/editor const props defineProps{ tool: Tool }() const { activeTool, setTool } useToolbar() const isActive computed(() activeTool.value props.tool) /script template button :data-activeisActive clicksetTool(props.tool) slot{{ tool }}/slot /button /template这个组件必须被放置在某棵ToolbarRoot子树之内否则useToolbar()会在运行时抛出useToolbar() called outside ToolbarRoot错误。组装外壳官方指南中的外壳示例custom-editor-shell.md展示了ToolbarRoot的插槽式用法open-pencil/vue自带的示例应用packages/vue/example/src/App.vue则给出了同时使用ToolbarRoot与ToolbarItem的完整参考script setup langts import { createEditor, type Tool } from open-pencil/core/editor import { OpenPencilProvider, CanvasRoot, CanvasSurface, LayerTree, ToolbarRoot, ToolbarItem, NodeProperties } from open-pencil/vue const editor createEditor() // 示例画布内容 editor.createShape(FRAME, 100, 100, 400, 300) editor.createShape(RECTANGLE, 150, 150, 120, 80) editor.createShape(ELLIPSE, 350, 200, 100, 100) editor.zoomToFit() const TOOL_LIST: Tool[] [ SELECT, FRAME, RECTANGLE, ELLIPSE, LINE, POLYGON, STAR, TEXT, PEN, HAND ] /script template OpenPencilProvider :editoreditor ToolbarRoot div classtoolbar ToolbarItem v-fortool in TOOL_LIST :keytool v-slot{ active, select } :tooltool button :class{ active } clickselect{{ tool }}/button /ToolbarItem /div /ToolbarRoot CanvasRoot CanvasSurface classcanvas-area / /CanvasRoot /OpenPencilProvider /template结合前文的自定义按钮组件等价写法是把MyToolButton直接放进ToolbarRootToolbarRoot div classtoolbar MyToolButton v-fortool in TOOL_LIST :keytool :tooltool / /div /ToolbarRoot两者的区别只在于ToolbarItem通过插槽把active/select暴露给你而自定义组件直接用useToolbar()自行读取——结果一致后者更适合需要深度嵌套或复用多个上下文字段的复杂组件。此外ToolbarRoot支持传入自定义tools因此你可以用EditorToolDef[]完全控制工具列表的构成顺序、标签、flyout 组合而不是局限于默认的EDITOR_TOOLS。配套 APIuseToolbarState 与相关文档useToolbar()聚焦读取上下文而演示/表现层状态则由同目录下的useToolbarState()负责use-toolbar-state.md 明确其为面向展示层的工具栏状态助手用于移动端分类分页。两者的分工在 packages/vue/src/primitives/Toolbar/useToolbarState.ts 的注释里写得很清楚This composable is presentation-oriented and complementsuseToolbarwhen building toolbar shells.useToolbarState()返回移动端分类分页状态与goPrev()/goNext()等导航助手并附带两个纯函数供工具栏 UI 使用isToolbarToolActive(tool, activeTool)判断某工具定义是否处于激活态——tool.key activeTool或activeTool属于该工具的flyout列表useToolbarState.tsgetToolbarToolSelection(tool, activeTool, flyoutSelections)计算某工具当前应显示为哪个工具键——若激活工具在 flyout 内则返回激活工具本身否则回退到记忆的flyoutSelections或工具自身的keyuseToolbarState.ts。这两个纯函数与useToolbar()配合可以覆盖高亮当前激活工具 高亮其所在组的完整工具栏交互。官方文档中的关联 APIToolbarRoot无头工具栏结构原语useToolbar()上下文的提供方ToolbarItem官方最小消费组件内部直接调用useToolbar()useToolbarState面向移动端分页的展示层状态助手Custom Editor Shell包含ToolbarRoot完整组装示例的自定义外壳指南。底层类型EditorToolDef 与 EDITOR_TOOLSuseToolbar()返回的tools: EditorToolDef[]中的类型与默认值定义在核心包 packages/core/src/editor/tool-registry.tsexport interface EditorToolDef { key: Tool label: string shortcut: string flyout?: Tool[] } export const EDITOR_TOOLS: EditorToolDef[] [ { key: SELECT, label: Move, shortcut: V }, { key: FRAME, label: Frame, shortcut: F, flyout: [FRAME, SECTION] }, { key: RECTANGLE, label: Rectangle, shortcut: R, flyout: [RECTANGLE, LINE, ELLIPSE, POLYGON, STAR] }, { key: PEN, label: Pen, shortcut: P }, { key: TEXT, label: Text, shortcut: T }, { key: HAND, label: Hand, shortcut: H } ]字段说明字段含义默认值参考EDITOR_TOOLSkey工具唯一标识Tool类型SELECT、FRAME、RECTANGLE、PEN、TEXT、HANDlabel显示名称Move、Frame、Rectangle、Pen、Text、Handshortcut快捷键提示V、F、R、P、T、Hflyout可选的子工具组FRAME组含SECTIONRECTANGLE组含LINE/ELLIPSE/POLYGON/STAR同文件的TOOL_SHORTCUTStool-registry.ts还维护了键盘事件键码到工具的映射KeyV → SELECT、KeyF → FRAME、KeyS → SECTION等这正是ToolbarRoot中activeTool能被快捷键驱动的底层来源。理解这层类型结构后你就知道useToolbar()返回的tools既可以用于循环渲染按钮也可以作为自定义 flyout 分组的依据。小结与使用建议useToolbar()是 OpenPencil Vue SDK 工具栏体系中的上下文读取钩子与ToolbarRoot提供方、ToolbarItem官方消费方、useToolbarState展示层助手共同构成一套完整的无头工具栏方案。使用时记住三条准则必须在ToolbarRoot子树内调用否则抛出useToolbar() called outside ToolbarRoot错误优先用它读取tools/activeTool/setTool让自定义工具栏保持无头——样式与布局完全归你工具状态归 SDK展示层需求交给useToolbarState()分页、滑动方向、flyout 记忆等 UI 状态不必自己再造轮子。查看入口 - 实现源码packages/vue/src/primitives/Toolbar/context.ts - 提供方组件packages/vue/src/primitives/Toolbar/ToolbarRoot.vue - 官方消费方packages/vue/src/primitives/Toolbar/ToolbarItem.vue - 展示层助手packages/vue/src/primitives/Toolbar/useToolbarState.ts - 工具类型定义packages/core/src/editor/tool-registry.ts - 可运行示例packages/vue/example/src/App.vue - 外壳组装指南packages/docs/programmable/sdk/guides/custom-editor-shell.md赞分享前端桌面应用AI 应用MCP 服务【免费下载链接】open-pencilAI-native design editor. Open-source Figma alternative.项目地址https://gitcode.com/gh_mirrors/op/open-pencil点击查看免费下载相关推荐Open-Pencil SDK 中的 useToolbar读取编辑器工具栏上下文的无头原语指南Open Pencil SDK 中的 useToolbar读取编辑器工具栏上下文的无头原语指南 useToolbar 是 Open Pencil Vue SD前端桌面应用AI 应用MCP 服务OpenPencil SDK 无样式工具栏原语 ToolbarRoot 深入指南从上下文到自绘工具栏OpenPencil SDK 无样式工具栏原语 ToolbarRoot 深入指南从上下文到自绘工具栏 ToolbarRoot 是 OpenPencil 可编程前端桌面应用AI 应用MCP 服务open-pencil 工具栏编程指南useToolbar 上下文与 ToolbarRoot 组合式 API 实战open pencil 工具栏编程指南useToolbar 上下文与 ToolbarRoot 组合式 API 实战 useToolbar 是 open pen前端桌面应用AI 应用MCP 服务上一篇Terraform AWS Provider 数据源 aws_key_pair 完全指南查询与复用 EC2 密钥对信息下一篇SeaTunnel SNMP Sink 连接器完全指南基于 SNMPv2c SET 协议的网络设备配置下发实战创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考