Gutenberg 区块开发:基于 Inspector Controls 实现高级自定义设置面板
大家好!今天我们要深入探讨 Gutenberg 区块开发中一个非常重要的方面:如何利用 Inspector Controls
组件构建高级的自定义设置面板。Inspector Controls
是 Gutenberg 编辑器侧边栏中用于控制区块属性的关键工具,一个精心设计的 Inspector Controls
面板能够极大地提升用户体验,让他们可以轻松地定制区块的行为和外观。
我们将从基础概念入手,逐步讲解如何使用 Inspector Controls
及其相关的组件,实现各种高级的自定义设置,并提供大量的代码示例,确保你能够将其运用到实际的区块开发中。
1. Inspector Controls
基础
Inspector Controls
组件是 @wordpress/block-editor
包提供的一个 React 组件,它负责将设置控件渲染到 Gutenberg 编辑器的侧边栏中。任何放置在 Inspector Controls
组件中的控件都会自动出现在区块的设置面板里。
最基本的用法如下:
import { InspectorControls } from '@wordpress/block-editor';
import { PanelBody, TextControl } from '@wordpress/components';
function Edit( { attributes, setAttributes } ) {
const { myCustomText } = attributes;
return (
<>
<InspectorControls>
<PanelBody title="自定义设置">
<TextControl
label="自定义文本"
value={ myCustomText }
onChange={ ( newText ) => setAttributes( { myCustomText: newText } ) }
/>
</PanelBody>
</InspectorControls>
<div>
{ myCustomText }
</div>
</>
);
}
export default Edit;
这段代码展示了一个简单的 Inspector Controls
用例。它包含一个 PanelBody
组件,用于将设置分组,以及一个 TextControl
组件,允许用户输入文本。当用户在 TextControl
中输入文本时,setAttributes
函数会更新区块的 myCustomText
属性,从而改变区块的显示内容。
2. 常用组件和控件
Inspector Controls
并非孤立存在,它通常与一系列的 @wordpress/components
包提供的组件和控件一起使用,以构建丰富的用户界面。以下是一些常用的组件:
PanelBody
: 用于将设置控件分组,提供折叠/展开功能,使设置面板更清晰易用。TextControl
: 用于输入单行文本。TextareaControl
: 用于输入多行文本。SelectControl
: 提供下拉选择框。RadioControl
: 提供单选按钮组。CheckboxControl
: 提供复选框。RangeControl
: 提供滑块控件,用于选择数值范围。ColorPicker
: 提供颜色选择器。ToggleControl
: 提供开关按钮。Button
: 提供按钮,用于触发特定操作。
这些组件都具有丰富的属性,可以定制它们的外观和行为。例如,RangeControl
可以设置最小值、最大值、步长等。
3. 高级自定义设置技巧
接下来,我们将介绍一些高级的自定义设置技巧,帮助你构建更强大的 Inspector Controls
面板。
3.1. 条件渲染控件
有时候,我们需要根据其他属性的值来显示或隐藏某些控件。这可以通过简单的条件判断来实现。
import { InspectorControls } from '@wordpress/block-editor';
import { PanelBody, ToggleControl, TextControl } from '@wordpress/components';
function Edit( { attributes, setAttributes } ) {
const { showAdvancedOptions, advancedText } = attributes;
return (
<>
<InspectorControls>
<PanelBody title="高级设置">
<ToggleControl
label="显示高级选项"
checked={ showAdvancedOptions }
onChange={ ( newShowAdvancedOptions ) => setAttributes( { showAdvancedOptions: newShowAdvancedOptions } ) }
/>
{ showAdvancedOptions && (
<TextControl
label="高级文本"
value={ advancedText }
onChange={ ( newText ) => setAttributes( { advancedText: newText } ) }
/>
) }
</PanelBody>
</InspectorControls>
<div>
{ advancedText }
</div>
</>
);
}
export default Edit;
在这个例子中,TextControl
只会在 showAdvancedOptions
属性为 true
时才显示。这使得用户可以根据需要选择是否显示高级设置。
3.2. 使用 useState
管理局部状态
有时候,我们需要管理一些与区块属性无关的局部状态。例如,我们可能需要记录一个临时值,用于在 Inspector Controls
面板中进行计算。在这种情况下,我们可以使用 React 的 useState
Hook。
import { InspectorControls } from '@wordpress/block-editor';
import { PanelBody, TextControl, Button } from '@wordpress/components';
import { useState } from '@wordpress/element';
function Edit( { attributes, setAttributes } ) {
const { myNumber } = attributes;
const [ tempNumber, setTempNumber ] = useState( myNumber || 0 ); // 初始化 tempNumber
const handleUpdateNumber = () => {
setAttributes( { myNumber: parseInt(tempNumber) } );
};
return (
<>
<InspectorControls>
<PanelBody title="数字设置">
<TextControl
label="数字"
type="number"
value={ tempNumber }
onChange={ ( newNumber ) => setTempNumber( newNumber ) }
/>
<Button onClick={ handleUpdateNumber } isPrimary>
更新数字
</Button>
</PanelBody>
</InspectorControls>
<div>
当前数字:{ myNumber }
</div>
</>
);
}
export default Edit;
在这个例子中,tempNumber
是一个局部状态,用于临时存储用户输入的数字。当用户点击 "更新数字" 按钮时,handleUpdateNumber
函数会将 tempNumber
的值更新到区块的 myNumber
属性中。注意,tempNumber
被强制转换成数字类型 parseInt(tempNumber)
,防止出现类型错误。
3.3. 动态生成控件
有时候,我们需要根据数据动态生成控件。例如,我们可能需要根据一个数组来生成一组 CheckboxControl
。
import { InspectorControls } from '@wordpress/block-editor';
import { PanelBody, CheckboxControl } from '@wordpress/components';
function Edit( { attributes, setAttributes } ) {
const { selectedOptions } = attributes;
const options = [ 'Option 1', 'Option 2', 'Option 3' ];
const handleOptionChange = ( option ) => {
const newSelectedOptions = selectedOptions.includes( option )
? selectedOptions.filter( ( item ) => item !== option )
: [ ...selectedOptions, option ];
setAttributes( { selectedOptions: newSelectedOptions } );
};
return (
<>
<InspectorControls>
<PanelBody title="选项设置">
{ options.map( ( option ) => (
<CheckboxControl
key={ option }
label={ option }
checked={ selectedOptions.includes( option ) }
onChange={ () => handleOptionChange( option ) }
/>
) ) }
</PanelBody>
</InspectorControls>
<div>
选择的选项:{ selectedOptions.join( ', ' ) }
</div>
</>
);
}
export default Edit;
在这个例子中,我们使用 map
函数遍历 options
数组,为每个选项生成一个 CheckboxControl
。handleOptionChange
函数负责更新 selectedOptions
属性,添加或删除选中的选项。
3.4. 使用 withState
高阶组件 (不推荐,已废弃,作为参考)
注意:withState
是一个已废弃的 API,在新的 Gutenberg 版本中不再推荐使用。这里仅作为历史参考,建议使用 useState
Hook 代替。
在旧版本的 Gutenberg 中,withState
高阶组件可以用于管理 Inspector Controls
中的局部状态。它可以将状态注入到组件的 props 中,并提供更新状态的函数。
import { InspectorControls } from '@wordpress/block-editor';
import { PanelBody, TextControl, Button } from '@wordpress/components';
import { withState } from '@wordpress/compose';
const MyComponent = withState( { tempNumber: 0 } )( ( { attributes, setAttributes, tempNumber, setState } ) => {
const { myNumber } = attributes;
const handleUpdateNumber = () => {
setAttributes( { myNumber: parseInt(tempNumber) } );
};
return (
<>
<InspectorControls>
<PanelBody title="数字设置">
<TextControl
label="数字"
type="number"
value={ tempNumber }
onChange={ ( newNumber ) => setState( { tempNumber: newNumber } ) }
/>
<Button onClick={ handleUpdateNumber } isPrimary>
更新数字
</Button>
</PanelBody>
</InspectorControls>
<div>
当前数字:{ myNumber }
</div>
</>
);
} );
function Edit( props ) {
return <MyComponent { ...props } />;
}
export default Edit;
在这个例子中,withState( { tempNumber: 0 } )
将 tempNumber
状态注入到 MyComponent
中。setState
函数可以用于更新 tempNumber
的值。
3.5. 使用 useInstanceId
获取唯一 ID
在某些情况下,我们需要为控件生成唯一的 ID。例如,当我们需要将 label
与 input
元素关联时,我们需要确保每个 input
元素都有唯一的 ID。可以使用 useInstanceId
Hook 来生成唯一的 ID。
import { InspectorControls } from '@wordpress/block-editor';
import { PanelBody, TextControl } from '@wordpress/components';
import { useInstanceId } from '@wordpress/compose';
function Edit( { attributes, setAttributes } ) {
const { myText } = attributes;
const instanceId = useInstanceId( Edit );
const textControlId = `my-text-control-${ instanceId }`;
return (
<>
<InspectorControls>
<PanelBody title="文本设置">
<TextControl
label="文本"
id={ textControlId }
value={ myText }
onChange={ ( newText ) => setAttributes( { myText: newText } ) }
/>
</PanelBody>
</InspectorControls>
<div htmlFor={ textControlId }>
{ myText }
</div>
</>
);
}
export default Edit;
在这个例子中,useInstanceId( Edit )
会为每个 Edit
组件实例生成一个唯一的 ID。我们将这个 ID 用于构建 TextControl
的 ID。
3.6. 使用 __experimentalInputControl
实现更精细的控制
__experimentalInputControl
是一个实验性的组件,它提供对输入控件更精细的控制。它允许你自定义输入框的类型、前缀、后缀等。
import { InspectorControls } from '@wordpress/block-editor';
import { PanelBody, __experimentalInputControl as InputControl } from '@wordpress/components';
function Edit( { attributes, setAttributes } ) {
const { myNumber } = attributes;
return (
<>
<InspectorControls>
<PanelBody title="数字设置">
<InputControl
label="数字"
type="number"
value={ myNumber }
onChange={ ( newNumber ) => setAttributes( { myNumber: parseFloat(newNumber) || 0 } ) }
prefix="¥"
suffix=" 元"
/>
</PanelBody>
</InspectorControls>
<div>
{ myNumber }
</div>
</>
);
}
export default Edit;
在这个例子中,我们使用 InputControl
组件,并设置了 prefix
和 suffix
属性,分别在输入框的前面和后面添加了 "¥" 和 " 元" 符号。注意这里将输入的字符串转化为浮点数parseFloat(newNumber) || 0
,如果转化失败,默认值为0。
4. 实例:构建一个高级图像选择器
让我们结合上述技巧,构建一个高级的图像选择器,它允许用户选择图片,并设置图片的对齐方式、尺寸和链接。
import { InspectorControls, MediaPlaceholder } from '@wordpress/block-editor';
import { PanelBody, SelectControl, RangeControl, ToggleControl } from '@wordpress/components';
import { useState } from '@wordpress/element';
function Edit( { attributes, setAttributes } ) {
const { imageUrl, imageAlt, alignment, imageWidth, linkToMedia } = attributes;
const [ isEditingImage, setIsEditingImage ] = useState( false );
const handleImageSelect = ( media ) => {
setAttributes( {
imageUrl: media.url,
imageAlt: media.alt,
} );
setIsEditingImage( false );
};
const handleImageRemove = () => {
setAttributes( {
imageUrl: null,
imageAlt: null,
} );
};
return (
<>
<InspectorControls>
<PanelBody title="图像设置">
{ imageUrl ? (
<>
<img src={ imageUrl } alt={ imageAlt } style={ { maxWidth: '100%' } } />
<button onClick={ handleImageRemove }>移除图片</button>
<SelectControl
label="对齐方式"
value={ alignment }
options={ [
{ label: '无', value: '' },
{ label: '左对齐', value: 'left' },
{ label: '居中', value: 'center' },
{ label: '右对齐', value: 'right' },
] }
onChange={ ( newAlignment ) => setAttributes( { alignment: newAlignment } ) }
/>
<RangeControl
label="图片宽度"
value={ imageWidth }
min={ 50 }
max={ 500 }
step={ 10 }
onChange={ ( newWidth ) => setAttributes( { imageWidth: newWidth } ) }
/>
<ToggleControl
label="链接到媒体文件"
checked={ linkToMedia }
onChange={ ( newLinkToMedia ) => setAttributes( { linkToMedia: newLinkToMedia } ) }
/>
</>
) : (
<MediaPlaceholder
onSelect={ handleImageSelect }
allowedTypes={ [ 'image' ] }
multiple={ false }
labels={ {
title: '选择图片',
instructions: '上传或选择媒体库中的图片',
} }
onError={ ( error ) => console.error( error ) }
/>
) }
</PanelBody>
</InspectorControls>
<div style={ { textAlign: alignment } }>
{ imageUrl && (
<a href={ linkToMedia ? imageUrl : '#' }>
<img
src={ imageUrl }
alt={ imageAlt }
style={ { width: `${ imageWidth }px` } }
/>
</a>
) }
</div>
</>
);
}
export default Edit;
这个例子展示了如何使用 MediaPlaceholder
组件来选择图片,并使用 SelectControl
、RangeControl
和 ToggleControl
组件来设置图片的对齐方式、尺寸和链接。它还使用 useState
Hook 来管理编辑图片的状态。
5. 表格总结常用控件属性
组件名称 | 常用属性 | 说明 |
---|---|---|
PanelBody |
title , initialOpen , onToggle |
title : 面板标题;initialOpen : 初始状态是否展开;onToggle : 切换展开/折叠状态时的回调函数。 |
TextControl |
label , value , onChange , type , placeholder , help |
label : 标签;value : 当前值;onChange : 值改变时的回调函数;type : 输入框类型(text, email, number 等);placeholder : 占位符;help : 帮助文本。 |
SelectControl |
label , value , options , onChange , help |
label : 标签;value : 当前值;options : 选项数组([{ label: ‘选项1’, value: ‘value1’ }, …]);onChange : 值改变时的回调函数;help : 帮助文本。 |
CheckboxControl |
label , checked , onChange , help |
label : 标签;checked : 是否选中;onChange : 值改变时的回调函数;help : 帮助文本。 |
RangeControl |
label , value , onChange , min , max , step , allowReset , withInputField |
label : 标签;value : 当前值;onChange : 值改变时的回调函数;min : 最小值;max : 最大值;step : 步长;allowReset : 是否显示重置按钮;withInputField : 是否显示输入框。 |
ColorPicker |
color , onChange , enableAlpha |
color : 当前颜色;onChange : 颜色改变时的回调函数;enableAlpha : 是否启用透明度。 |
ToggleControl |
label , checked , onChange , help |
label : 标签;checked : 是否选中;onChange : 值改变时的回调函数;help : 帮助文本。 |
Button |
isPrimary , isSecondary , isSmall , isLarge , onClick , children |
isPrimary : 是否是主要按钮;isSecondary : 是否是次要按钮;isSmall : 是否是小按钮;isLarge : 是否是大按钮;onClick : 点击事件回调函数;children : 按钮文本。 |
__experimentalInputControl |
label , value , onChange , type , prefix , suffix , min , max , step |
label : 标签;value : 当前值;onChange : 值改变时的回调函数;type : 输入框类型(text, email, number 等);prefix : 前缀;suffix : 后缀;min : 最小值;max : 最大值;step : 步长。 |
总结:构建更强大的区块设置面板
通过学习 Inspector Controls
的基础知识,以及各种高级技巧和组件,我们能够构建出更强大、更灵活的 Gutenberg 区块设置面板。掌握这些技术,将极大地提升你的区块开发的效率和用户体验。
希望今天的讲解能够帮助你更好地理解和使用 Inspector Controls
,并在实际的区块开发中发挥它的强大功能。记住,不断实践和探索是掌握任何技术的关键。祝你在 Gutenberg 区块开发的道路上越走越远!