ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Gutenberg ToolsPanel 组件完全指南:用渐进式发现构建可折叠的块控制面板

Gutenberg ToolsPanel 组件完全指南:用渐进式发现构建可折叠的块控制面板 Gutenberg ToolsPanel 组件完全指南用渐进式发现构建可折叠的块控制面板【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg本文以 packages/components/src/tools-panel/tools-panel/README.md 为主体结合wordpress/components中ToolsPanel、ToolsPanelItem、ToolsPanelHeader的源码实现与测试用例展开。读者将掌握 ToolsPanel 的设计动机渐进式发现、两列网格布局规则、完整的DimensionPanel实战用法以及全部 Props 的语义与底层工作机制。一、ToolsPanel 是什么为块支持控件提供“渐进式发现”ToolsPanel是 GutenbergWordPress 块编辑器组件库wordpress/components中的实验性组件。文档开篇明确标注该特性仍处于早期实现阶段可能发生剧烈甚至破坏性的变更“Experimental” means this is an early implementation subject to drastic and breaking changes。它的核心定位是为面板的子控件提供渐进式发现progressive discovery能力。典型场景是块编辑器侧边栏中的块支持block supports控件——例如颜色、间距、版式等设置。一个块可能拥有大量可选设置如果全部同时展示侧边栏会显得臃肿ToolsPanel 的做法是面板头部带有一个自动生成的下拉菜单菜单项由面板内匹配ToolsPanelItem组件类型的子组件自动推导而来每个菜单项可以切换对应子控件的显示/隐藏控件切换时触发onSelect/onDeselect回调例如在关闭某个块支持控件时重置对应的块属性。从源码看ToolsPanel的渲染结构由三部分拼装而成见 component.tsx一个两列的Grid容器columns{ 2 }templateColumnsrepeat( 2, minmax(0, 1fr) )ToolsPanelContext.Provider向下传递面板的注册、注销、菜单状态等上下文ToolsPanelHeader负责渲染标题与下拉菜单。Grid columns{ 2 } gap{ 4 } columnGap{ TOOLS_PANEL_GAP } rowGap{ TOOLS_PANEL_GAP } templateColumnsrepeat( 2, minmax(0, 1fr) ) ToolsPanelContext.Provider value{ panelContext } ToolsPanelHeader label{ label } resetAll{ resetAllItems } toggleItem{ toggleItem } headingLevel{ headingLevel } dropdownMenuProps{ dropdownMenuProps } / { children } /ToolsPanelContext.Provider /Grid也就是说ToolsPanel自身不直接渲染菜单内容而是把“菜单状态”放入ToolsPanelContext由各ToolsPanelItem在注册时把自身信息上报给面板再由ToolsPanelHeader依据菜单状态生成下拉菜单——这是一个典型的“子组件自注册、父组件聚合”的架构。二、核心机制ToolsPanelItem 的注册与菜单生成2.1 菜单如何生成ToolsPanel创建了一个带头部含下拉菜单的容器菜单由面板内匹配ToolsPanelItem组件类型的子组件自动生成。每个菜单项控制对应子控件的显示与关闭当控件被切换时会触发该控件的onSelect与onDeselect回调从而让开发者有机会在切换时执行额外逻辑例如重置块属性。源码层面useToolsPanel见 hook.ts通过useReducer维护三份状态panelItems所有已注册的ToolsPanelItem按label去重重复注册会被替换而非追加见panelItemsReducer的REGISTER_PANEL分支menuItemOrder菜单顺序append-only注销后重新注册仍保留原位置menuItemValues菜单项当前选中状态。菜单在渲染期间通过useMemo派生menuItems保证面板永远不可能渲染出一个“半成品”菜单。每个菜单项按其isShownByDefault归属到default默认控件组或optional可选控件组其选中状态由menuItemValues[label] ?? getSeedValue(item)决定。2.2 初始显示状态hasValue、isShownByDefault 与 defaultShown一个子控件初始是否显示取决于两件事是否已有值——通过子组件 props 中传入的hasValue函数判定是否被标记为默认显示——通过isShownByDefaultprop。源码中getSeedValuehook.ts精确描述了这一规则const getSeedValue ( item ) item.hasValue() || ( ! item.isShownByDefault !! item.defaultShown );即只要控件有值就一定显示没有值时仅当它是非默认项且设置了defaultShown时才默认显示。isShownByDefault标记的控件则始终显示与是否有值、菜单是否勾选无关。2.3 非 ToolsPanelItem 子组件始终渲染但不进菜单未被ToolsPanelItem包裹的组件仍然会被渲染但它们不会出现在ToolsPanel菜单中也不受菜单控制。文档给出的典型场景是在面板中显示介绍性或帮助性文本help text。对应的ToolsPanelItem组件的实现见 tools-panel-item/component.tsx在isShown为 false 时返回null除非设置了 placeholder 模式从而把非菜单控制的元素与受控子项区分开。2.4 注册的生命周期与 panelId 隔离ToolsPanelItem通过useLayoutEffect在渲染前完成注册避免闪烁并让同一面板的一整批注册在同一个 commit 内被 React 批量处理见 tools-panel-item/hook.ts。注销时会把注册对象一并回传若已有替换者占用了同一 label面板会忽略这次清理。当设置了panelId时它会通过ToolsPanelContext传递用于限制面板项只有panelId显式为null或与面板panelId完全一致的项才能注册自身。这允许控件从共享来源例如 Slot/Fill注入到指定面板。hasMatchingPanel的判断逻辑tools-panel-item/hook.tsconst hasMatchingPanel currentPanelId panelId || currentPanelId null;三、ToolsPanel 布局两列网格与跨列控制ToolsPanel采用两列网格布局默认情况下面板内的ToolsPanelItem被样式化为跨两列grid-column: 1 / -1见 style.module.scss这符合绝大多数使用场景大多数非控件元素如帮助文本会作为相关控件ToolsPanelItem的子节点渲染无需额外样式若某个元素与多个控件相关例如对比度检查器 contrast checker或与面板整体相关例如面板描述它会被直接渲染进面板而不包裹ToolsPanelItem。此时它默认只占一列若不希望如此需要一点样式微调例如grid-column: 1 / -1;。文档中的使用示例演示了三种典型情况一个非ToolsPanelItem的描述段落、应单行显示的控件grid-column: span 1、以及跨两列的控件。当hasInnerWrapper为 true 时面板会额外应用tools-panel-with-inner-wrapper样式让内部包装层也呈现两列网格若所有可选控件都被隐藏则应用tools-panel-hidden-inner-wrapper隐藏内层style.module.scss。四、完整使用示例DimensionPanel以下是文档给出的完整用法以DimensionPanel为例组合了ToolsPanel、ToolsPanelItem、UnitControl与BoxControlimport styled from emotion/styled; import { BoxControl, __experimentalToolsPanel as ToolsPanel, __experimentalToolsPanelItem as ToolsPanelItem, __experimentalUnitControl as UnitControl, } from wordpress/components; import { __ } from wordpress/i18n; const PanelDescription styled.div grid-column: span 2; ; const SingleColumnItem styled( ToolsPanelItem ) grid-column: span 1; ; export function DimensionPanel() { const [ height, setHeight ] useState(); const [ width, setWidth ] useState(); const [ padding, setPadding ] useState(); const [ margin, setMargin ] useState(); const resetAll () { setHeight( undefined ); setWidth( undefined ); setPadding( undefined ); setMargin( undefined ); }; return ( ToolsPanel label{ __( Dimensions ) } resetAll{ resetAll } PanelDescription Select dimensions or spacing related settings from the menu for additional controls. /PanelDescription SingleColumnItem hasValue{ () !! height } label{ __( Height ) } onDeselect{ () setHeight( undefined ) } isShownByDefault UnitControl __next40pxDefaultSize label{ __( Height ) } onChange{ setHeight } value{ height } / /SingleColumnItem SingleColumnItem hasValue{ () !! width } label{ __( Width ) } onDeselect{ () setWidth( undefined ) } isShownByDefault UnitControl __next40pxDefaultSize label{ __( Width ) } onChange{ setWidth } value{ width } / /SingleColumnItem ToolsPanelItem hasValue{ () !! padding } label{ __( Padding ) } onDeselect{ () setPadding( undefined ) } BoxControl label{ __( Padding ) } onChange{ setPadding } values{ padding } allowReset{ false } / /ToolsPanelItem ToolsPanelItem hasValue{ () !! margin } label{ __( Margin ) } onDeselect{ () setMargin( undefined ) } BoxControl label{ __( Margin ) } onChange{ setMargin } values{ margin } allowReset{ false } / /ToolsPanelItem /ToolsPanel ); }示例要点拆解面板描述PanelDescription不是ToolsPanelItem所以不会被菜单控制始终渲染通过grid-column: span 2让它跨两列展示单列控件SingleColumnItem通过styled( ToolsPanelItem )覆写为grid-column: span 1让 Height / Width 两个单值控件并排显示在同一行默认显示Height / Width 设置了isShownByDefault因此即使没有值也会在首次渲染时出现可选控件Padding / Margin 未设置默认显示初始隐藏在菜单中用户勾选后才出现重置语义每个控件在onDeselect中把自己的状态重置为undefinedresetAll统一把所有维度重置——这正是文档所述“关闭块支持控件时重置块属性”的落地写法可访问性label同时用作面板标题文本与下拉菜单的aria-labelBoxControl的allowReset{ false }避免了控件内部重置按钮与面板菜单重置行为重复。五、ToolsPanel Props 全解析以下为ToolsPanel的全部公开 Props类型定义见 types.ts默认值与解析逻辑见 hook.tslabel:string必填显示在面板头部Header的文本同时用作面板下拉菜单的aria-label。从tools-panel-header/component.tsx的实现看菜单的按钮文案由它派生例如sprintf( _x( %s options, ... ), labelText )。若label为空ToolsPanelHeader直接返回null不渲染头部。resetAll:( filters?: ResetAllFilter[] ) void必填当用户点击菜单中“Reset all”选项时调用的函数。其参数是所有已注册ToolsPanelItem的resetAllFilter回调数组。源码中resetAllFiltershook.ts由两部分组成各面板项在注册时上报的resetAllFilter加上直接向 context 注册的“外部过滤器”供非面板项消费者使用。resetAllItems会先调用外部传入的resetAll此时标记isResetting让子项能区分“重置”与“用户手动关闭”再派发RESET_ALLaction 隐藏所有可选控件见 hook.ts。注意RESET_ALLreducer 的细节可选项被直接置为 false 立即隐藏默认项则被保留——因为onDeselect和resetAllFilter都是可选的resetAll不必覆盖每个属性重置后各子项会通过hasValue再次上报真实值。hasInnerWrapper:boolean可选默认false标记面板内的项将被包含在一个内部包装元素中以便面板据此布局。为 true 时应用tools-panel-with-inner-wrapper样式内层转为两列网格grid-column: 1 / -1。测试用例中专门用GroupedItems模拟了这种“子项被包装组件包裹”的注册场景见 test/index.jsdom.test.tsx。dropdownMenuProps:DropdownMenuProps可选用于配置面板DropdownMenu的 Popover props类型为OmitReact.ComponentPropstypeof DropdownMenu, label——即除label外均可覆盖label固定由面板的labelprop 派生。headingLevel:1 | 2 | 3 | 4 | 5 | 6 | 1 | 2 | 3 | 4 | 5 | 6可选默认2面板标题的标题级别heading level最终传递给Heading组件用于保证文档大纲与可访问性结构的正确性。panelId:string | null可选若设置了panelId它会通过ToolsPanelContext传递用于限制面板项面板项只有在其panelId显式为null或与面板的panelId完全一致时才能注册自身。这使控件可以从共享来源例如 SlotFill注入到指定面板而不会误注册到其它面板。shouldRenderPlaceholderItems:boolean可选默认false告知ToolsPanel所有ToolsPanelItem子组件在被关闭隐藏时应渲染占位内容而非返回null。这在配合ItemGroup样式、需要保持面板子项数量稳定时很有用。两个使用注意点占位项不会应用正常情况下通过classNameprop 施加的样式因为tools-panel-item-placeholder样式为display: none见 style.module.scss面板会通过firstDisplayedItem/lastDisplayedItem配合实验性 props__experimentalFirstVisibleItemClass/__experimentalLastVisibleItemClass让ItemGroup在占位项混入时仍能正确地为可见项的首尾应用圆角等样式见 hook.ts。实验性附加 Props类型定义中还有两个实验性 props__experimentalFirstVisibleItemClass与__experimentalLastVisibleItemClass分别用于为面板内第一个/最后一个可见的ToolsPanelItem追加自定义 CSS 类。六、ToolsPanelItem 配套 Props组合使用ToolsPanel的菜单行为完全建立在ToolsPanelItem的自我上报之上二者是配套组件。完整的ToolsPanelItem文档见 tools-panel-item/README.md关键 Props 如下Prop类型必填默认说明labelstring是—双重用途下拉菜单的可读文案 在菜单 context 中定位对应项的 key同一面板内必须唯一hasValue() boolean是—构建菜单时用于决定该项的初始勾选状态isShownByDefaultboolean否false标记该项为默认显示控件无论是否有值、菜单是否勾选都显示defaultShownboolean否false仅对可选项生效首次渲染且hasValue()为 false 时是否仍显示仅决定无值项的初始状态有值项始终显示onSelect() void否—该项在菜单中被选中时调用onDeselect() void否—该项在菜单中被取消选中时调用通常用于重置控件值onShownChange( isShown: boolean ) void否—用户通过菜单显式显示/隐藏该项时调用传入true/false与onDeselect不同它无论项是否有值都会触发且只对明确的菜单动作响应不受“因获得值而显示”“因 Reset all 而隐藏”等情况触发isShownByDefault项不会触发它panelIdstring \| null否—与面板的panelId匹配或为null才能注册用于从共享来源注入控件resetAllFilter( attributes?: any ) any否() {}面板收集所有项的resetAllFilter以数组形式传给resetAll可迭代执行附加的清理任务关于 onShownChange / onSelect / onDeselect 的选择ToolsPanelItem文档给出了一张行为对照表帮助开发者在三个回调之间做正确取舍用户动作触发的回调显示一个没有值的可选控件onShownChange( true )、onSelect隐藏一个没有值的可选控件onShownChange( false )隐藏一个有值的可选控件onShownChange( false )、onDeselect结论用onShownChange来追踪/持久化用户希望某项是否可见用onDeselect来重置控件值——两者回答的是不同问题不要把同一个处理器同时接到两个回调上否则一次菜单动作会被处理两次。七、Header 与菜单的底层实现ToolsPanelHeader见 tools-panel-header/component.tsx负责渲染标题与下拉菜单实现细节值得借鉴默认控件组DefaultControlsGroup有值的默认控件显示为带 “Reset” 后缀的菜单项sprintf( __( Reset %s ), label )点击后调用toggleItem并播报无障碍提示speak( ... )无值的默认控件显示为带对勾、aria-disabled的勾选状态项可选控件组OptionalControlsGroup按选中状态显示“Show %s”/“Hide and reset %s”点击后切换重置入口菜单底部是独立的MenuGroup与 “Reset all” 菜单项仅当存在已选中项canResetAll时才可点击图标切换当所有可选控件都隐藏时菜单按钮图标显示为plus提示用户可添加控件否则为moreVertical且当所有选项隐藏时给按钮加上 “All options are currently hidden” 描述areAllOptionalControlsHidden由 hook.ts 计算默认组为空、可选组非空且全部未选中无障碍播报菜单打开、控件显示/隐藏、全部重置等动作都配合wordpress/a11y的speak()输出 assertive 级别的提示保证屏幕阅读器用户获得反馈。八、测试验证与可参考实现仓库在 packages/components/src/tools-panel/test/index.jsdom.test.tsx共 1900 行与index.browser.test.tsx中提供了丰富的测试覆盖可作为理解行为边界的权威参考注册/注销验证ToolsPanelItem的注册与注销、重复 label 的替换、通过 SlotFill 注入的项在面板 remount 后不会残留对应tool-panel-item/hook.ts中的清理逻辑重置流程验证resetAll收到所有项的resetAllFilter数组、重置后可选控件隐藏而默认控件保留对应RESET_ALLreducer回调语义验证onShownChange只在用户显式菜单动作时触发而onSelect/onDeselect的触发时机符合第六节表格面板隔离验证不同panelId面板之间控件互不注册。此外stories/index.story.tsx 提供了交互式 Storybook 演示适合在浏览器中直观验证菜单行为与布局效果。九、快速参考相关文件索引文件路径内容tools-panel/README.mdToolsPanel官方文档本文主体tools-panel/component.tsxToolsPanel渲染实现Grid Context Headertools-panel/hook.tsuseToolsPanel状态管理注册、菜单、重置tools-panel-item/README.mdToolsPanelItem官方文档tools-panel-item/hook.ts子项注册、panelId匹配、显示判定tools-panel-header/component.tsx头部与下拉菜单渲染context.tsToolsPanelContext定义与默认值types.ts全部 Props / Context 类型定义style.module.scss两列网格、占位项、菜单样式test/index.jsdom.test.tsx行为测试注册、重置、回调stories/index.story.tsxStorybook 交互演示十、小结ToolsPanel是 Gutenberg 块编辑器“渐进式发现”UI 理念的组件化落地通过ToolsPanelItem的子组件自注册机制、基于hasValue/isShownByDefault的显示判定、两列网格布局与内置的无障碍菜单交互开发者可以用极少代码构建出“默认简洁、按需展开、可整体重置”的控制面板。实践时请牢记三条核心规则label在同一面板内必须唯一用onShownChange追踪可见性、用onDeselect重置值非ToolsPanelItem元素需要自行通过grid-column控制跨列。由于该组件仍处于实验阶段集成时建议锁定wordpress/components的版本并关注后续的破坏性变更。【免费下载链接】gutenbergThe Block Editor project for WordPress and beyond. Plugin is available from the official repository.项目地址: https://gitcode.com/GitHub_Trending/gu/gutenberg创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表