You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义Gutenberg块中为不同RichText组件配置专属ToolbarButton

问题描述

我有一个内部包含多个RichText组件的自定义Gutenberg块,需要为不同的RichText组件显示特定的ToolbarButton,实现类似原生allowedFormats={['core/italic']}的用户体验——也就是只为指定RichText元素显示对应控件。

我不想用registerFormatType,因为这些控件需要修改块属性而非富文本格式。我尝试过通过焦点状态控制工具栏显示,伪代码如下:

edit : () => {

    const [isRichText1Focus,setIsRichText1Focus] = useState(false)
    const [isRichText2Focus,setIsRichText2Focus] = useState(false)

    return (
        
        <>
        
            {isRichText1Focus && (

                <BlockControls>
                    <ToolbarGroup>
                        <ToolbarButton
                            icon="test"
                            title="Title"
                            onClick={ () => {
                                // 修改块属性
                            } }
                        />
                    </ToolbarGroup>
                </BlockControls>

            )}

            {isRichText2Focus && (

                <BlockControls>
                    <ToolbarGroup>
                        <ToolbarButton
                            icon="test 2"
                            title="Title 2"
                            onClick={ () => {
                                // 修改另一个块属性
                            } }
                        />
                    </ToolbarGroup>
                </BlockControls>

            )}

            <div className='blockName'>

                <RichText
                    onBlur={() => {
                        setIsRichText1Focus(false)
                    }}
                    // 其他参数
                    unstableOnFocus={() => {
                        setIsRichText1Focus(true)
                    }}
                />

                <RichText
                    onBlur={() => {
                        setIsRichText2Focus(false)
                    }}
                    // 其他参数
                    unstableOnFocus={() => {
                        setIsRichText2Focus(true)
                    }}
                />

            </div>
        </>

    ) 

}

但这个方法有问题:RichText的onBlur无法可靠覆盖点击工具栏本身的情况,导致焦点状态判断失效。

我想知道:能不能复刻Gutenberg原生registerFormatType和formatTypes API的焦点/失焦逻辑?或者有没有更简单的方案?要求必须使用原生Toolbar,不能自定义UI。

解决方案

方法1:利用useSelect监听当前激活的RichText标识符

Gutenberg编辑器内部会跟踪当前激活的RichText组件(通过identifier属性),你可以通过useSelect获取这个状态,从而精准控制工具栏显示:

  1. 给每个RichText组件添加唯一的identifier属性
  2. 使用useSelect监听editor/rich-text/current-identifier状态
  3. 根据当前激活的标识符渲染对应工具栏

示例代码:

import { useSelect } from '@wordpress/data';
import { BlockControls, ToolbarGroup, ToolbarButton, RichText } from '@wordpress/block-editor';
import { useState } from '@wordpress/element';

const MyCustomBlock = ({ attributes, setAttributes }) => {
    // 获取当前激活的RichText标识符
    const currentRichTextId = useSelect( ( select ) => {
        return select( 'core/editor' ).getCurrentRichTextIdentifier();
    }, [] );

    return (
        <>
            {/* 根据当前激活的RichText显示对应工具栏 */}
            { currentRichTextId === 'rich-text-1' && (
                <BlockControls>
                    <ToolbarGroup>
                        <ToolbarButton
                            icon="edit"
                            title="修改标题属性"
                            onClick={ () => setAttributes({ titleAttr: '新标题' }) }
                        />
                    </ToolbarGroup>
                </BlockControls>
            ) }

            { currentRichTextId === 'rich-text-2' && (
                <BlockControls>
                    <ToolbarGroup>
                        <ToolbarButton
                            icon="text"
                            title="修改内容属性"
                            onClick={ () => setAttributes({ contentAttr: '新内容' }) }
                        />
                    </ToolbarGroup>
                </BlockControls>
            ) }

            <div className="custom-block">
                <RichText
                    identifier="rich-text-1" // 唯一标识符
                    tagName="h2"
                    value={ attributes.title }
                    onChange={ ( val ) => setAttributes({ title: val }) }
                    placeholder="输入标题"
                />

                <RichText
                    identifier="rich-text-2" // 唯一标识符
                    tagName="p"
                    value={ attributes.content }
                    onChange={ ( val ) => setAttributes({ content: val }) }
                    placeholder="输入内容"
                />
            </div>
        </>
    );
};

export default MyCustomBlock;

方法2:结合useRef和文档焦点事件优化原方案

如果不想依赖编辑器内部状态,可以通过useRef跟踪RichText的DOM元素,结合全局焦点事件判断当前焦点是否在目标RichText或工具栏内:

import { BlockControls, ToolbarGroup, ToolbarButton, RichText } from '@wordpress/block-editor';
import { useState, useRef, useEffect } from '@wordpress/element';

const MyCustomBlock = ({ attributes, setAttributes }) => {
    const [activeRichText, setActiveRichText] = useState(null);
    const rt1Ref = useRef(null);
    const rt2Ref = useRef(null);

    useEffect(() => {
        const handleFocusChange = (e) => {
            // 检查焦点是否在RichText1或当前激活它时的工具栏内
            const isRT1Active = rt1Ref.current?.contains(e.target) || 
                               document.querySelector('.block-editor-block-controls').contains(e.target) && activeRichText === 'rt1';
            // 检查焦点是否在RichText2或当前激活它时的工具栏内
            const isRT2Active = rt2Ref.current?.contains(e.target) || 
                               document.querySelector('.block-editor-block-controls').contains(e.target) && activeRichText === 'rt2';

            if (isRT1Active) {
                setActiveRichText('rt1');
            } else if (isRT2Active) {
                setActiveRichText('rt2');
            } else {
                setActiveRichText(null);
            }
        };

        document.addEventListener('focus', handleFocusChange, true);
        return () => document.removeEventListener('focus', handleFocusChange, true);
    }, [activeRichText]);

    return (
        <>
            { activeRichText === 'rt1' && (
                <BlockControls>
                    <ToolbarGroup>
                        <ToolbarButton
                            icon="test"
                            title="标题控件"
                            onClick={ () => setAttributes({ titleAttr: '更新' }) }
                        />
                    </ToolbarGroup>
                </BlockControls>
            ) }

            { activeRichText === 'rt2' && (
                <BlockControls>
                    <ToolbarGroup>
                        <ToolbarButton
                            icon="test2"
                            title="内容控件"
                            onClick={ () => setAttributes({ contentAttr: '更新' }) }
                        />
                    </ToolbarGroup>
                </BlockControls>
            ) }

            <div className="custom-block">
                <RichText
                    ref={rt1Ref}
                    tagName="h2"
                    value={ attributes.title }
                    onChange={ (val) => setAttributes({ title: val }) }
                    onFocus={ () => setActiveRichText('rt1') }
                />

                <RichText
                    ref={rt2Ref}
                    tagName="p"
                    value={ attributes.content }
                    onChange={ (val) => setAttributes({ content: val }) }
                    onFocus={ () => setActiveRichText('rt2') }
                />
            </div>
        </>
    );
};

export default MyCustomBlock;

方案对比

  • 方法1更简洁,完全复用Gutenberg原生状态管理,是推荐方案,无需处理复杂的焦点判断逻辑。
  • 方法2适合需要更自定义控制的场景,但需要额外处理焦点事件,兼容性稍差。

内容的提问来源于stack exchange,提问作者Michael Watson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 23:42:06