Block Transforms 是一个 API,允许将块转换为其他块(从和到),以及从其他实体。现有与该 API 配合使用的实体包括短代码、文件、正则表达式和原始 DOM 节点。

转换方向:to 和 from

块通过块配置中的可选 transforms 键声明其支持的转换,其子键 to 和 from 包含每个方向的可用转换数组。示例:

export const settings = {
    title: 'My Block Title',
    description: 'My block description',
    /* ... */
    transforms: {
        from: [
            /* supported from transforms */
        ],
        to: [
            /* supported to transforms */
        ],
    },
};

转换类型

本节介绍块支持的现有转换类型:

  • block
  • enter
  • files
  • prefix
  • raw
  • shortcode

Block

这种类型的转换支持 from 和 to 两个方向,允许将块转换为不同的块。它在块工具栏中有一个相应的 UI 控件。

类型为 block 的转换是一个对象,它接受以下参数:

  • type (string):值为 block。
  • blocks (array):已知块类型的列表。它还接受通配符值 ("*"),表示该转换对所有块类型都可用(例如:所有块都可以转换为 core/group)。
  • transform (function):一个回调函数,接收正在处理的块的属性和内部块。它应返回一个块对象或一组块对象。
  • variationName (string, optional):当转换创建被转换块类型的变体时,目标块变体的名称。这允许转换 UI 显示变体标题和图标,而不是基本块类型。
  • isMatch (function, optional):一个回调函数,接收块的属性作为第一个参数,块对象作为第二个参数,并应返回布尔值。从此函数返回 false 将阻止转换可用并显示为用户的选项。
  • isMultiBlock (boolean, optional):是否可以在选择多个块时应用转换。如果为 true,transform 函数的第一个参数将是包含每个选定块的属性的数组,第二个是包含每个选定块的内部块的数组。默认为 false。
  • priority (number, optional):控制转换应用的优先级,其中较低的值优先于较高的值。这的行为很像 WordPress hook。与钩子一样,默认优先级为 10(除非另有设置)。
  • shortcuts (array, optional):将转换应用于选定块的键盘快捷键。请参见下面的 Keyboard shortcuts。

Keyboard shortcuts

类型为 block 的转换可以声明键盘快捷键,这些快捷键注册到编辑器的键盘快捷键存储中,并列出在键盘快捷键帮助模态框中。只要块完全可编辑,按下键组合即可转换选定的块。

shortcuts 的每个条目接受以下参数:

  • name (string):唯一且机器可读的快捷键名称,例如 core/block-editor/transform-heading-to-paragraph。
  • description (string):翻译后的描述,显示在键盘快捷键帮助模态框中。
  • keyCombination (object):触发快捷键的键组合,作为 character 和可选的 modifier(由 wp-keycodes package 支持的修饰符之一,例如 access 或 primary)。
  • aliases (array, optional):触发相同快捷键的替代键组合。
  • variationName (string, optional):快捷键针对的生成的块变体,默认为转换自身的 variationName。逐个声明它允许单个转换为每个变体提供一个快捷键,而不会在块切换器中出现多次。

在 to 转换中,快捷键应用于声明它们的块,并产生 blocks 的第一个条目。在 from 转换中,它们适用于 blocks 中列出的任何块,并产生声明它们的块。

示例:使用 Alt+Shift+0 将标题块转换为段落块

transforms: {
    to: [
        {
            type: 'block',
            blocks: [ 'core/paragraph' ],
            shortcuts: [
                {
                    name: 'core/block-editor/transform-heading-to-paragraph',
                    description: __(
                        'Transform the selected heading into a paragraph.'
                    ),
                    keyCombination: { modifier: 'access', character: '0' },
                },
            ],
            transform: ( { content } ) => {
                return createBlock( 'core/paragraph', { content } );
            },
        },
    ],
},

示例:将任何块转换为特定标题级别

标题块在其从段落转换的单个转换上挂起每个级别的快捷键,以便在段落上的 Alt+Shift+2 产生 H2,而块切换器仍然提供一次“Heading”:

transforms: {
    from: [
        {
            type: 'block',
            blocks: [ 'core/paragraph' ],
            shortcuts: [ 1, 2, 3, 4, 5, 6 ].map( ( level ) => ( {
                name: `core/block-editor/transform-to-heading-${ level }`,
                description: /* … */,
                keyCombination: { modifier: 'access', character: `${ level }` },
                variationName: `h${ level }`,
            } ) ),
            transform: ( { content } ) =>
                createBlock( 'core/heading', { content } ),
        },
    ],
},

键组合是全局匹配的,因此已被另一个块或编辑器本身占用的组合将不可靠地到达您的转换。在开发版本中,当两个块声明相同的组合时会记录警告。

要切换已经是目标类型的块的变体,请在 block variation 上声明快捷键。应该同时做到这两点——从另一个块类型到达变体并在该类型内切换到它——的快捷键在相同 name 下在两个地方都声明。

示例:从段落块到标题块

要声明此转换,我们将以下代码添加到标题块配置中,该配置使用来自 wp-blocks package 的 createBlock 函数。

transforms: {
    from: [
        {
            type: 'block',
            blocks: [ 'core/paragraph' ],
            transform: ( { content } ) => {
                return createBlock( 'core/heading', {
                    content,
                } );
            },
        },
    ]
},

示例:具有 InnerBlocks 的块

具有 InnerBlocks 的块也可以从另一个具有 InnerBlocks 的块转换到该块。

transforms: {
    to: [
        {
            type: 'block',
            blocks: [ 'some/block-with-innerblocks' ],
            transform: ( attributes, innerBlocks ) => {
                return createBlock(
                    'some/other-block-with-innerblocks',
                    attributes,
                    innerBlocks
                );
            },
        },
    ],
},

Enter

这种类型的转换支持 from 方向,允许从用户引入的一些内容创建块。它们在用户输入一些内容并按下 ENTER 键后在新块行中应用。

类型为 enter 的转换是一个对象,它接受以下参数:

  • type (string):值为 enter。
  • regExp (RegExp):用作匹配器的正则表达式。如果值匹配,将应用转换。
  • transform (function):一个回调函数,接收一个包含 content 字段的对象,其中包含已输入的值。它应返回一个块对象或一组块对象。
  • priority (number, optional): 控制应用转换的优先级,其中较低的值将优先于较高的值。其行为类似于一个 WordPress hook。与钩子类似,当未另行设置时,默认优先级为 10。

示例:从 — 到 Separator 块

当用户输入三个连字符然后按 ENTER 键时创建分隔符块,我们可以使用以下代码:

transforms = {
    from: [
        {
            type: 'enter',
            regExp: /^-{3,}$/,
            transform: () => createBlock( 'core/separator' ),
        },
    ],
};

Files

此类型的转换支持 from 方向,允许从拖入编辑器的文件中创建块。

类型为 files 的转换是一个对象,它接受以下参数:

  • type (string): 值为 files。
  • transform (function): 一个回调函数,接收正在处理的文件数组。它应返回一个块对象或一组块对象。
  • isMatch (function, optional): 一个回调函数,接收正在处理的文件数组并应返回布尔值。从此函数返回 false 将阻止应用转换。
  • priority (number, optional): 控制应用转换的优先级,其中较低的值将优先于较高的值。其行为类似于一个 WordPress hook。与钩子类似,当未另行设置时,默认优先级为 10。

示例:从文件到 File 块

当用户将文件拖入编辑器时创建 File 块,我们可以使用以下代码:

transforms: {
    from: [
        {
            type: 'files',
            isMatch: ( files ) => files.length === 1,
            // By defining a lower priority than the default of 10,
            // we make that the File block to be created as a fallback,
            // if no other transform is found.
            priority: 15,
            transform: ( files ) => {
                const file = files[ 0 ];
                const blobURL = createBlobURL( file );
                // File will be uploaded in componentDidMount()
                return createBlock( 'core/file', {
                    href: blobURL,
                    fileName: file.name,
                    textLinkHref: blobURL,
                } );
            },
        },
    ],
}

Prefix

此类型的转换支持 from 方向,允许从用户输入的一些文本创建块。当用户在新块行中输入一些文本并添加尾随空格时应用它们。

类型为 prefix 的转换是一个对象,它接受以下参数:

  • type (string): 值为 prefix。
  • prefix (string): 匹配此转换的字符或字符序列。
  • transform (function): 一个回调函数,接收引入的内容。它应返回一个块对象或一组块对象。
  • priority (number, optional): 控制应用转换的优先级,其中较低的值将优先于较高的值。其行为类似于一个 WordPress hook。与钩子类似,当未另行设置时,默认优先级为 10。

示例:从文本到自定义块

如果我们要在用户输入问号时创建自定义块,我们可以使用此代码:

transforms: {
    from: [
        {
            type: 'prefix',
            prefix: '?',
            transform( content ) {
                return createBlock( 'my-plugin/question', {
                    content,
                } );
            },
        },
    ],
}

Raw

此类型的转换支持 from 方向,允许从原始 HTML 节点创建块。当用户在块设置 UI 菜单中执行“转换为块”操作时应用它们,以及当某些内容被粘贴或拖入编辑器时。

类型为 raw 的转换是一个对象,它接受以下参数:

  • type (string): 值为 raw。
  • transform (function, optional): 一个回调函数,接收正在处理的节点。它应返回一个块对象或一组块对象。
  • schema (object|function, optional): 定义用于检测和粘贴内容的 HTML content model。见 下方。
  • selector (string, optional): 一个 CSS 选择器字符串,用于根据 element.matches 方法确定元素是否匹配。如果元素不匹配,则不会执行转换。这是一个简写方式,也是使用 isMatch 的替代方案,如果存在后者,它将优先。
  • isMatch (function, optional): 一个回调函数,接收正在处理的节点并应返回布尔值。从此函数返回 false 将阻止应用转换。
  • priority (number, optional): 控制应用转换的优先级,其中较低的值将优先于较高的值。其行为类似于一个 WordPress hook。与钩子类似,当未另行设置时,默认优先级为 10。

示例:从 URL 到 Embed 块

如果我们要在用户在编辑器中粘贴某些 URL 时创建 Embed 块,我们可以使用此代码:

transforms: {
    from: [
        {
            type: 'raw',
            isMatch: ( node ) =>
                node.nodeName === 'P' &&
                /^\s*(https?:\/\/\S+)\s*$/i.test( node.textContent ),
            transform: ( node ) => {
                return createBlock( 'core/embed', {
                    url: node.textContent.trim(),
                } );
            },
        },
    ],
}

Schemas and Content Models

粘贴内容时,可以定义一个 content model 用于验证和处理粘贴的内容。通常的情况是,粘贴到编辑器中的 HTML 将包含一些 应该 转移的元素以及一些 不应该 转移的元素。例如,考虑将 <span class="time">12:04 pm</span> 粘贴到编辑器中。我们希望复制 12:04 pm 并省略 <span> 及其 class 属性,因为这些不会像它们最初从复制位置那样携带相同的含义或结构。

在编写 raw 转换时,您可以通过提供描述允许内容并将应用于在尝试与您的块匹配之前清理粘贴内容的 schema 来控制这一点。Schema 被传递给 cleanNodeList from @wordpress/dom;在那里查看 schema 的完整描述。

schema = { span: { children: { '#text': {} } } };

示例:自定义内容模型

假设我们要匹配以下 HTML 片段并将其转换为某种自定义帖子预览块。

<div data-post-id="13">
    <h2>The Post Title</h2>
    <p>Some <em>great</em> content.</p>
</div>

我们要告诉编辑器允许内部的 h2 和 p 元素。我们通过提供以下 schema 来实现这一点。在本例中,我们使用函数形式,它接受一个参数来提供 phrasingContentSchema(以及一个布尔值 isPaste 指示转换操作是否以粘贴文本开始)。phrasingContentSchema 预定义为匹配 HTML phrasing 元素,例如 <strong> 和 <sup> 和 <kbd>。任何我们期望
有 <RichText /> 组件的地方都是允许 phrasing 内容的好地方,否则在转换时我们将丢失所有文本格式。

schema = ({ phrasingContentSchema }) => ({
    div: {
        required: true,
        attributes: [ 'data-post-id' ],
        children: {
            h2: { children: phrasingContentSchema },
            p: { children: phrasingContentSchema }
        }
    }
})

当我们成功匹配此内容时,每个 HTML 属性都将被剥离,除了 data-post-id。如果我们在给定的 div 内拥有其他形式的 HTML,那么它将无法匹配我们的转换器。同样地,如果我们发现的是 <h3> 而不是 <h2>,我们也无法匹配。

当想要匹配包含非短语内容的 HTML 片段时,架构(schemas)最为重要,例如带有 <summary> 的 <details>。如果不声明自定义架构,编辑器将在尝试通过任何块转换器运行它们之前跳过这些其他结构。

短代码

这种类型的转换支持 from(从)方向,允许从短代码创建块。它作为 raw 转换过程的一部分应用。

类型为 shortcode 的转换是一个对象,它采用以下参数:

  • type (string):值为 shortcode。
  • tag (string|array):此转换可以处理的短代码标签或短代码别名列表。
  • transform (function, optional):一个回调函数,接收短代码属性作为第一个参数,以及 WPShortcodeMatch 作为第二个参数。它应返回一个块对象或块对象数组。当定义此参数时,它将优先于 attributes 参数。
  • attributes (object, optional):表示根据由 块配置对象 定义的属性形状,块属性应从中获取的对象。如果某个特定属性包含一个 shortcode 键,它应该是一个函数,接收短代码属性作为第一个参数,以及 WPShortcodeMatch 作为第二个参数,并返回将在块的注释中获取的属性值。
  • isMatch (function, optional):一个回调函数,根据 短代码 API 接收短代码属性,并应返回一个布尔值。从此函数返回 false 将阻止该短代码被转换为此块。
  • priority (number, optional):控制转换应用的优先级,其中较低的值将优先于较高的值。这的行为很像 WordPress 钩子。与钩子一样,当未另外设置时,默认优先级为 10。

示例:使用 transform 从短代码到块

可以使用 transform 方法将现有的短代码转换为其对应的块。

transforms: {
    from: [
        {
            type: 'shortcode',
            tag: 'video',
            transform( { named: { src } } ) {
                return createBlock( 'core/video', { src } );
            },
            // 防止在短代码没有适当的 ID 时将其转换为该块。
            isMatch( { named: { id } } ) {
                return id === 'my-id';
            },
        },
    ],
},

示例:使用 attributes 从短代码到块

可以使用 attributes 参数将现有的短代码转换为其对应的块。

transforms: {
    from: [
        {
            type: 'shortcode',
            tag: 'youtube',
            attributes: {
                url: {
                    type: 'string',
                    source: 'attribute',
                    attribute: 'src',
                    selector: 'img',
                },
                align: {
                    type: 'string',
                    // 短代码函数将提取
                    // 短代码属性到一个值,
                    // 该值将在块的注释中获取。
                    shortcode: ( { named: { align = 'alignnone' } } ) => {
                        return align.replace( 'align', '' );
                    },
                },
            },
            // 防止在短代码没有适当的 ID 时将其转换为该块。
            isMatch( { named: { id } } ) {
                return id === 'my-id';
            },
        },
    ]
},

示例:带有包装内容的短代码到带有 InnerBlocks 的块

可以包装内部内容的短代码(例如 [example]<p>Inner.</p>[/example])可以通过在 transform 内部的 rawHandler 传递 match.shortcode.content 来转换为带有 InnerBlocks 的块。

transforms: {
    from: [
        {
            type: 'shortcode',
            tag: 'example',
            transform( attrs, match ) {
                const innerBlocks = rawHandler( {
                    HTML: match.shortcode.content || '',
                } );
                return createBlock(
                    'myplugin/example',
                    { att1: attrs.named.att1 || '' },
                    innerBlocks
                );
            },
        },
    ],
},

rawHandler 会递归进入内容,因此嵌套短代码和 HTML 与粘贴的内容一样被转换为块。

取消分组的块

通过块配置的可选 transforms 键,块可以使用 ungroup 子键来定义将替换正在处理的块的块。这些新块通常是现有内部块的子集,但也可能包括新块。

如果块具有 ungroup 转换,则它有资格进行取消分组,而无需是默认分组块。用于通过此 API 取消分组的用户界面与用于默认分组块的用户界面相同。为了显示“取消分组”按钮,我们必须选择一个单一的分组块,该块还包含一些内部块。

ungroup 是一个回调函数,它接收正在处理的块的属性和内部块。它应返回一个块对象数组。

示例:

export const settings = {
    title: 'My grouping Block Title',
    description: 'My grouping block description',
    /* ... */
    transforms: {
        ungroup: ( attributes, innerBlocks ) =>
            innerBlocks.flatMap( ( innerBlock ) => innerBlock.innerBlocks ),
    },
};