块编辑器开发文档

SlotFills 参考

💡 云策文档标注

概述

SlotFills 是 WordPress Gutenberg 编辑器中的组件,允许开发者通过插件向预定义位置注入自定义内容。使用 @wordpress/plugins API 注册插件来实现 SlotFill 功能。

关键要点

  • SlotFills 包括 Slot 和 Fill 组件,用于在 Gutenberg 管理界面中扩展功能。
  • 使用步骤:导入 registerPlugin 和所需 SlotFill,定义渲染组件,注册插件。
  • 可通过条件渲染控制 Fill 的显示,例如基于 post type 的 viewable 属性或允许列表。
  • SlotFills 通过 createSlotFill 创建,在核心代码中定义并导出为全局组件。
  • 当前可用的 SlotFills 包括 MainDashboardButton、PluginDocumentSettingPanel 等,具体用法需参考相关文档。

代码示例

import { registerPlugin } from '@wordpress/plugins';
import { PluginPostStatusInfo } from '@wordpress/editor';

const PluginPostStatusInfoTest = () => (
    <PluginPostStatusInfo>
        <p>Post Status Info SlotFill</p>
    </PluginPostStatusInfo>
);

registerPlugin( 'post-status-info-test', { render: PluginPostStatusInfoTest } );

注意事项

  • 除 MainDashboardButton 外,大多数 SlotFills 在 Post Editor 和 Site Editor 中均可用,需注意上下文渲染。
  • 条件渲染时,使用 useSelect 钩子检查 post type 属性,如 viewable,以控制 Fill 的显示。
  • SlotFills 的实现依赖于核心代码中的 Slot 和 Fill 组件,开发者应遵循现有模式进行扩展。

📄 原文内容

Slot and Fill are components that have been exposed to allow developers to inject items into some predefined places in the Gutenberg admin experience.
Please see the SlotFill component docs for more details.

In order to use them, we must leverage the @wordpress/plugins api to register a plugin that will inject our items.

Usage overview

In order to access the SlotFills, we need to do four things:

  1. Import the registerPlugin method from the @wordpress/plugins package.
  2. Import the SlotFill we want from the @wordpress/editor' package.
  3. Define a component to render our changes. Our changes/additions will be wrapped in the SlotFill component we imported.
  4. Register the plugin.

Here is an example using the PluginPostStatusInfo slotFill:

import { registerPlugin } from '@wordpress/plugins';
import { PluginPostStatusInfo } from '@wordpress/editor';

const PluginPostStatusInfoTest = () => (
    <PluginPostStatusInfo>
        <p>Post Status Info SlotFill</p>
    </PluginPostStatusInfo>
);

registerPlugin( 'post-status-info-test', { render: PluginPostStatusInfoTest } );

Conditionally rendering SlotFill content

With the exception of MainDashboardButton, every available SlotFill is exposed in both the Post Editor and Site Editor and any Fill that is registered will be rendered in both contexts. There are a number of approaches that can be implemented to conditionally render Fills.

Restricting fills to the Post Editor

A fill can be restricted to the Post Editor by checking to see if the current post type object property viewable is set to true. Any post type not set to viewable, does not have an associated edit post screen and is a good indicator that the user is not in the Post Editor. The example below will render its content on the edit post screen for any registered post type.

/**
 * WordPress dependencies
 */
import { registerPlugin } from '@wordpress/plugins';
import {
    PluginDocumentSettingPanel,
    store as editorStore,
} from '@wordpress/editor';
import { store as coreStore } from '@wordpress/core-data';
import { useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';

/**
 * The component to be rendered  as part of the plugin.
 */
const EditPostDocumentSettingPanel = () => {
    // Retrieve information about the current post type.
    const isViewable = useSelect( ( select ) => {
        const postTypeName = select( editorStore ).getCurrentPostType();
        const postTypeObject = select( coreStore ).getPostType( postTypeName );
        return postTypeObject?.viewable;
    }, [] );

    // If the post type is not viewable, then do not render the plugin.
    if ( ! isViewable ) {
        return null;
    }

    return (
        <PluginDocumentSettingPanel
            name="custom-panel"
            title={ __( 'Post Editor Example' ) }
            className="custom-panel"
        >
            <p>{ __( 'Only appears in the Edit Post screen' ) }</p>
        </PluginDocumentSettingPanel>
    );
};

registerPlugin( 'example-post-edit-only', {
    render: EditPostDocumentSettingPanel,
} );

Restricting fills to certain post types.

The following example expands on the example above by creating an allow list of post types where the fill should be rendered. In this case, the fill is only rendered when editing pages.

/**
 * WordPress dependencies
 */
import { registerPlugin } from '@wordpress/plugins';
import {
    PluginDocumentSettingPanel,
    store as editorStore,
} from '@wordpress/editor';
import { store as coreStore } from '@wordpress/core-data';
import { useSelect } from '@wordpress/data';
import { __, sprintf } from '@wordpress/i18n';

/**
 * The component to be rendered  as part of the plugin.
 */
const RestrictPostTypes = () => {
    // Retrieve information about the current post type.
    const { isViewable, postTypeName } = useSelect( ( select ) => {
        const postType = select( editorStore ).getCurrentPostType();
        const postTypeObject = select( coreStore ).getPostType( postType );
        return {
            isViewable: postTypeObject?.viewable,
            postTypeName: postType,
        };
    }, [] );

    // The list of post types that are allowed to render the plugin.
    const allowedPostTypes = [ 'page' ];

    // If the post type is not viewable or not in the allowed list, do not render the plugin.
    if ( ! isViewable || ! allowedPostTypes.includes( postTypeName ) ) {
        return null;
    }

    return (
        <PluginDocumentSettingPanel
            name="custom-panel"
            title={ __( 'Restrict Post Types Example' ) }
            className="custom-panel"
        >
            <p>
                { sprintf(
                    __(
                        'Only appears on Post Types that are in the allowed list. %s'
                    ),
                    allowedPostTypes.join( ', ' )
                ) }
            </p>
        </PluginDocumentSettingPanel>
    );
};

registerPlugin( 'example-restrict-post-types', {
    render: RestrictPostTypes,
} );

Restricting fills to the Side Editor

To restrict fills to the Site Editor, the reverse logic is true. If the post type object’s viewable property is set to true, then the fill should not be rendered. The example below will render its content on any Site Editor screen.

/**
 * WordPress dependencies
 */
import { registerPlugin } from '@wordpress/plugins';
import {
    PluginDocumentSettingPanel,
    store as editorStore,
} from '@wordpress/editor';
import { store as coreStore } from '@wordpress/core-data';
import { useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';

/**
 * The component to be rendered  as part of the plugin.
 */
const SiteEditorDocumentSettingPanel = () => {
    // Retrieve information about the current post type.
    const isViewable = useSelect( ( select ) => {
        const postTypeName = select( editorStore ).getCurrentPostType();
        const postTypeObject = select( coreStore ).getPostType( postTypeName );

        // A viewable post type is one than can be viewed in the WordPress admin. Internal ones are not set to viewable.
        return postTypeObject?.viewable;
    }, [] );

    // If the post type is viewable, do not render my fill
    if ( isViewable ) {
        return null;
    }

    return (
        <PluginDocumentSettingPanel
            name="custom-panel"
            title={ __( 'Site Editor Example' ) }
            className="custom-panel"
        >
            <p>{ __( 'Only appears in the Site Editor' ) }</p>
        </PluginDocumentSettingPanel>
    );
};

registerPlugin( 'example-site-editor', {
    render: SiteEditorDocumentSettingPanel,
} );

Restricting fills to certain screens in the Site Editor.

This example builds on the example above by providing an allow list to control which screens a fill can be rendered within the Site Editor.

/**
 * WordPress dependencies
 */
import { registerPlugin } from '@wordpress/plugins';
import {
    PluginDocumentSettingPanel,
    store as editorStore,
} from '@wordpress/editor';
import { store as coreStore } from '@wordpress/core-data';
import { useSelect } from '@wordpress/data';
import { __, sprintf } from '@wordpress/i18n';

/**
 * The component to be rendered  as part of the plugin.
 */
const SiteEditorDocumentSettingPanel = () => {
    // Allowed areas in the Site Editor.
    const allowedSiteEditorScreens = [
        'wp_template', // Templates
        'wp_block', // Patterns
        'wp_template_part', // Template Parts
    ];

    const { isViewable, postType } = useSelect( ( select ) => {
        const postTypeName = select( editorStore ).getCurrentPostType();
        const postTypeObject = select( coreStore ).getPostType( postTypeName );

        return {
            // A viewable post type is one than can be viewed in the WordPress admin. Internal ones are not set to viewable.
            isViewable: postTypeObject?.viewable,
            postType: postTypeName,
        };
    }, [] );

    // If the post type is viewable, do not render my plugin.
    if ( isViewable || ! allowedSiteEditorScreens.includes( postType ) ) {
        return null;
    }

    return (
        <PluginDocumentSettingPanel
            name="custom-panel"
            title={ __( 'Restricted to Site Editor screens' ) }
            className="custom-panel"
        >
            <p>
                { sprintf(
                    __(
                        'Only appears on Editor Screens that are in the allowed list. %s'
                    ),
                    allowedSiteEditorScreens.join( ', ' )
                ) }
            </p>
        </PluginDocumentSettingPanel>
    );
};

registerPlugin( 'example-site-editor-only', {
    render: SiteEditorDocumentSettingPanel,
} );

How do they work?

SlotFills are created using createSlotFill. This creates two components, Slot and Fill which are then used to create a new component that is exported on the wp.plugins global.

Definition of the PluginPostStatusInfo SlotFill (see core code)

/**
 * Defines as extensibility slot for the Summary panel.
 */

/**
 * WordPress dependencies
 */
import { createSlotFill, PanelRow } from '@wordpress/components';

export const { Fill, Slot } = createSlotFill( 'PluginPostStatusInfo' );

const PluginPostStatusInfo = ( { children, className } ) => (
    <Fill>
        <PanelRow className={ className }>{ children }</PanelRow>
    </Fill>
);

PluginPostStatusInfo.Slot = Slot;

export default PluginPostStatusInfo;

This new Slot is then exposed in the editor. The example below is from core and represents the Summary panel.

As we can see, the <PluginPostStatusInfo.Slot> is wrapping all of the items that will appear in the panel.
Any items that have been added via the SlotFill ( see the example above ), will be included in the fills parameter and be displayed in the end of the component.

See core code.

export default function PostSummary( { onActionPerformed } ) {
    const { isRemovedPostStatusPanel } = useSelect( ( select ) => {
        // We use isEditorPanelRemoved to hide the panel if it was programmatically removed. We do
        // not use isEditorPanelEnabled since this panel should not be disabled through the UI.
        const { isEditorPanelRemoved } = select( editorStore );
        return {
            isRemovedPostStatusPanel: isEditorPanelRemoved( PANEL_NAME ),
        };
    }, [] );

    return (
        <PostPanelSection className="editor-post-summary">
            <PluginPostStatusInfo.Slot>
                { ( fills ) => (
                    <>
                        <Stack direction="column" gap="lg">
                            <PostCardPanel
                                onActionPerformed={ onActionPerformed }
                            />
                            <PostFeaturedImagePanel withPanelBody={ false } />
                            <PostExcerptPanel />
                            <Stack direction="column" gap="xs">
                                <PostContentInformation />
                                <PostLastEditedPanel />
                            </Stack>
                            { ! isRemovedPostStatusPanel && (
                                <Stack direction="column" gap="sm">
                                    <Stack direction="column" gap="xs">
                                        <PostStatusPanel />
                                        <PostSchedulePanel />
                                        <PostURLPanel />
                                        <PostAuthorPanel />
                                        <PostTemplatePanel />
                                        <PostDiscussionPanel />
                                        <PageAttributesPanel />
                                        <PostSyncStatus />
                                        <BlogTitle />
                                        <PostsPerPage />
                                        <SiteDiscussion />
                                        <PostFormatPanel />
                                        <PostStickyPanel />
                                    </Stack>
                                    <TemplateAreas />
                                    { fills }
                                </Stack>
                            ) }
                        </Stack>
                    </>
                ) }
            </PluginPostStatusInfo.Slot>
        </PostPanelSection>
    );
}

Currently available SlotFills and examples

The following SlotFills are available in the edit-post or editor packages. Please refer to the individual items below for usage and example details: