355 lines
15 KiB
Markdown
355 lines
15 KiB
Markdown
|
|
## `ungroup` 区块解组功能
|
|||
|
|
|
|||
|
|
通过区块配置中可选的 `transforms` 键,区块可使用 `ungroup` 子键定义用于替换当前处理区块的新区块。这些新区块通常是现有内部区块的子集,但也可包含全新区块。
|
|||
|
|
|
|||
|
|
若某区块配置了 `ungroup` 转换功能,即具备解组资格,无需作为默认分组区块即可使用。通过此 API 解组区块的用户界面与默认分组区块所用的界面相同。需满足以下条件才会显示解组按钮:必须选中单个分组区块,且该区块需包含若干内部区块。
|
|||
|
|
|
|||
|
|
**ungroup** 是一个回调函数,接收被处理区块的属性及内部区块数据,需返回区块对象数组。
|
|||
|
|
|
|||
|
|
示例:
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
export const settings = {
|
|||
|
|
title: '我的分组区块标题',
|
|||
|
|
description: '我的分组区块描述',
|
|||
|
|
/* ... */
|
|||
|
|
transforms: {
|
|||
|
|
ungroup: ( attributes, innerBlocks ) =>
|
|||
|
|
innerBlocks.flatMap( ( innerBlock ) => innerBlock.innerBlocks ),
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
当我们成功匹配此内容时,除 `data-post-id` 外所有HTML属性都将被剥离。如果给定 `div` 内存在其他HTML排列方式,则无法匹配转换器。同理,若其中出现的是 `<h3>` 而非 `<h2>`,匹配也会失败。
|
|||
|
|
|
|||
|
|
在处理包含非短语内容(如带 `<summary>` 的 `<details>`)的HTML片段时,模式定义至关重要。若未声明自定义模式,编辑器在尝试通过任何区块转换前会跳过这些特殊结构。
|
|||
|
|
|
|||
|
|
### 短代码
|
|||
|
|
|
|||
|
|
此类转换支持单向转换(从短代码创建区块),作为 `raw` 转换流程的组成部分。
|
|||
|
|
|
|||
|
|
`shortcode` 类型转换对象包含以下参数:
|
|||
|
|
|
|||
|
|
- **type** _(字符串)_:固定值 `shortcode`
|
|||
|
|
- **tag** _(字符串|数组)_:可转换的短代码标签或别名列表
|
|||
|
|
- **transform** _(函数,可选)_:接收短代码属性(首参数)与 [WPShortcodeMatch](/packages/shortcode/README.md#next)(次参数)的回调函数,应返回区块对象或区块对象数组。定义此参数时将优先于 `attributes` 参数
|
|||
|
|
- **attributes** _(对象,可选)_:根据[区块配置对象](/docs/reference-guides/block-api/block-registration.md)定义的属性结构,指定区块属性来源。若某属性包含 `shortcode` 键,则应为接收短代码属性(首参数)与 [WPShortcodeMatch](/packages/shortcode/README.md#next)(次参数)的函数,并返回将存入区块注释的属性值
|
|||
|
|
- **isMatch** _(函数,可选)_:根据 [Shortcode API](https://codex.wordpress.org/Shortcode_API) 接收短代码属性的回调函数,应返回布尔值。返回 `false` 将阻止该短代码转换为此区块
|
|||
|
|
- **priority** _(数字,可选)_:控制转换应用优先级,数值较低者优先,工作机制类似 [WordPress 钩子](https://developer.wordpress.org/reference/#Hook_to_WordPress)。如未设置,默认优先级为 `10`
|
|||
|
|
|
|||
|
|
**示例:通过 `transform` 从短代码转换为区块**
|
|||
|
|
|
|||
|
|
使用 `transform` 方法可将现有短代码转换为其对应区块:
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
transforms: {
|
|||
|
|
from: [
|
|||
|
|
{
|
|||
|
|
type: 'shortcode',
|
|||
|
|
tag: 'video',
|
|||
|
|
transform( { named: { src } } ) {
|
|||
|
|
return createBlock( 'core/video', { src } );
|
|||
|
|
},
|
|||
|
|
// 当短代码不具备正确ID时
|
|||
|
|
// 阻止其转换为此区块
|
|||
|
|
isMatch( { named: { id } } ) {
|
|||
|
|
return id === 'my-id';
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**示例:通过 `attributes` 从短代码转换为区块**
|
|||
|
|
|
|||
|
|
使用 `attributes` 参数可将现有短代码转换为其对应区块:
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
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';
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
# 转换功能
|
|||
|
|
|
|||
|
|
区块转换是一套API,允许区块与其他区块进行相互转换,同时也支持从其他实体转换为区块。与此API兼容的现有实体包括短代码、文件、正则表达式和原始DOM节点。
|
|||
|
|
|
|||
|
|
## 转换方向:`to` 与 `from`
|
|||
|
|
|
|||
|
|
区块通过配置项中可选的 `transforms` 键来声明支持的转换类型,其子键 `to` 和 `from` 分别存储不同方向的转换数组。例如:
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
export const settings = {
|
|||
|
|
title: '我的区块标题',
|
|||
|
|
description: '我的区块描述',
|
|||
|
|
/* ... */
|
|||
|
|
transforms: {
|
|||
|
|
from: [
|
|||
|
|
/* 支持的来源转换 */
|
|||
|
|
],
|
|||
|
|
to: [
|
|||
|
|
/* 支持的目标转换 */
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 转换类型
|
|||
|
|
|
|||
|
|
本节将介绍区块支持的现有转换类型:
|
|||
|
|
|
|||
|
|
- block(区块)
|
|||
|
|
- enter(回车)
|
|||
|
|
- files(文件)
|
|||
|
|
- prefix(前缀)
|
|||
|
|
- raw(原始)
|
|||
|
|
- shortcode(短代码)
|
|||
|
|
|
|||
|
|
### 区块转换
|
|||
|
|
|
|||
|
|
此类转换支持双向转换,允许区块转换为其他类型的区块。在区块工具栏中有对应的UI控件。
|
|||
|
|
|
|||
|
|
`block` 类型的转换对象包含以下参数:
|
|||
|
|
|
|||
|
|
- **type**(字符串):值为 `block`
|
|||
|
|
- **blocks**(数组):已知区块类型列表。支持通配符值(`"*"`),表示该转换适用于所有区块类型(例如:所有区块都能转换为 `core/group`)
|
|||
|
|
- **transform**(函数):接收被处理区块属性及内部区块的回调函数,应返回区块对象或区块对象数组
|
|||
|
|
- **isMatch**(函数,可选):接收区块属性(第一参数)和区块对象(第二参数)的回调函数,应返回布尔值。返回 `false` 将隐藏该转换选项
|
|||
|
|
- **isMultiBlock**(布尔值,可选):是否支持多选区块转换。为true时,`transform` 函数的首个参数为属性数组,第二参数为内部区块数组。默认为false
|
|||
|
|
- **priority**(数字,可选):控制转换优先级,数值越低优先级越高。行为类似[WordPress钩子](https://developer.wordpress.org/reference/#Hook_to_WordPress),默认优先级为 `10`
|
|||
|
|
|
|||
|
|
**示例:从段落区块转换为标题区块**
|
|||
|
|
|
|||
|
|
在标题区块配置中添加以下代码(使用 [`wp-blocks` 包](/packages/blocks/README.md#createBlock) 的 `createBlock` 函数):
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
transforms: {
|
|||
|
|
from: [
|
|||
|
|
{
|
|||
|
|
type: 'block',
|
|||
|
|
blocks: [ 'core/paragraph' ],
|
|||
|
|
transform: ( { content } ) => {
|
|||
|
|
return createBlock( 'core/heading', {
|
|||
|
|
content,
|
|||
|
|
} );
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**示例:含内部区块的转换**
|
|||
|
|
|
|||
|
|
具有InnerBlocks的区块可与其他含内部区块的区块相互转换:
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
transforms: {
|
|||
|
|
to: [
|
|||
|
|
{
|
|||
|
|
type: 'block',
|
|||
|
|
blocks: [ 'some/block-with-innerblocks' ],
|
|||
|
|
transform: ( attributes, innerBlocks ) => {
|
|||
|
|
return createBlock(
|
|||
|
|
'some/other-block-with-innerblocks',
|
|||
|
|
attributes,
|
|||
|
|
innerBlocks
|
|||
|
|
);
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 回车转换
|
|||
|
|
|
|||
|
|
此类转换仅支持来源转换,允许根据用户输入内容创建区块。当用户输入内容后按回车键时,将在新行中触发转换。
|
|||
|
|
|
|||
|
|
`enter` 类型的转换对象包含以下参数:
|
|||
|
|
|
|||
|
|
- **type**(字符串):值为 `enter`
|
|||
|
|
- **regExp**(正则表达式):用于匹配的正则表达式,匹配成功则触发转换
|
|||
|
|
- **transform**(函数):接收包含输入内容的回调函数,应返回区块对象或区块对象数组
|
|||
|
|
- **priority**(数字,可选):控制转换优先级,数值越低优先级越高。行为类似[WordPress钩子](https://developer.wordpress.org/reference/#Hook_to_WordPress),默认优先级为 `10`
|
|||
|
|
|
|||
|
|
**示例:通过 --- 创建分隔符区块**
|
|||
|
|
|
|||
|
|
当用户输入三个连字符后按回车键时创建分隔符区块:
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
transforms = {
|
|||
|
|
from: [
|
|||
|
|
{
|
|||
|
|
type: 'enter',
|
|||
|
|
regExp: /^-{3,}$/,
|
|||
|
|
transform: () => createBlock( 'core/separator' ),
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 文件类型转换
|
|||
|
|
|
|||
|
|
此类转换支持 _from_ 方向,允许通过将文件拖拽至编辑器来创建区块。
|
|||
|
|
|
|||
|
|
`files` 类型的转换是一个包含以下参数的对象:
|
|||
|
|
|
|||
|
|
- **type**(字符串):取值为 `files`
|
|||
|
|
- **transform**(函数):接收正在处理的文件数组的回调函数,应返回区块对象或区块对象数组
|
|||
|
|
- **isMatch**(函数,可选):接收正在处理的文件数组的回调函数,应返回布尔值。返回 `false` 将阻止应用此转换
|
|||
|
|
- **priority**(数字,可选):控制转换应用的优先级,数值较低的优先级高于数值较高的优先级。其行为类似于 [WordPress 钩子](https://developer.wordpress.org/reference/#Hook_to_WordPress)。与钩子类似,未设置时的默认优先级为 `10`
|
|||
|
|
|
|||
|
|
**示例:从文件创建文件区块**
|
|||
|
|
|
|||
|
|
当用户将文件拖拽至编辑器时,可通过以下代码创建文件区块:
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
transforms: {
|
|||
|
|
from: [
|
|||
|
|
{
|
|||
|
|
type: 'files',
|
|||
|
|
isMatch: ( files ) => files.length === 1,
|
|||
|
|
// 通过定义低于默认值10的优先级,
|
|||
|
|
// 使其在没有找到其他转换时作为备用方案创建文件区块
|
|||
|
|
priority: 15,
|
|||
|
|
transform: ( files ) => {
|
|||
|
|
const file = files[ 0 ];
|
|||
|
|
const blobURL = createBlobURL( file );
|
|||
|
|
// 文件将在 componentDidMount() 中上传
|
|||
|
|
return createBlock( 'core/file', {
|
|||
|
|
href: blobURL,
|
|||
|
|
fileName: file.name,
|
|||
|
|
textLinkHref: blobURL,
|
|||
|
|
} );
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 前缀类型转换
|
|||
|
|
|
|||
|
|
此类转换支持 _from_ 方向,允许根据用户输入的文本创建区块。当用户在新区块行中输入文本并添加尾随空格时触发。
|
|||
|
|
|
|||
|
|
`prefix` 类型的转换是一个包含以下参数的对象:
|
|||
|
|
|
|||
|
|
- **type**(字符串):取值为 `prefix`
|
|||
|
|
- **prefix**(字符串):匹配此转换的字符或字符序列
|
|||
|
|
- **transform**(函数):接收输入内容的回调函数,应返回区块对象或区块对象数组
|
|||
|
|
- **priority**(数字,可选):控制转换应用的优先级,数值较低的优先级高于数值较高的优先级。其行为类似于 [WordPress 钩子](https://developer.wordpress.org/reference/#Hook_to_WordPress)。与钩子类似,未设置时的默认优先级为 `10`
|
|||
|
|
|
|||
|
|
**示例:从文本创建自定义区块**
|
|||
|
|
|
|||
|
|
若要在用户输入问号时创建自定义区块,可使用以下代码:
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
transforms: {
|
|||
|
|
from: [
|
|||
|
|
{
|
|||
|
|
type: 'prefix',
|
|||
|
|
prefix: '?',
|
|||
|
|
transform( content ) {
|
|||
|
|
return createBlock( 'my-plugin/question', {
|
|||
|
|
content,
|
|||
|
|
} );
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 原始内容类型转换
|
|||
|
|
|
|||
|
|
此类转换支持 _from_ 方向,允许根据原始 HTML 节点创建区块。当用户执行区块设置界面菜单中的"转换为区块"操作,或向编辑器粘贴/拖拽内容时触发。
|
|||
|
|
|
|||
|
|
`raw` 类型的转换是一个包含以下参数的对象:
|
|||
|
|
|
|||
|
|
- **type**(字符串):取值为 `raw`
|
|||
|
|
- **transform**(函数,可选):接收正在处理节点的回调函数,应返回区块对象或区块对象数组
|
|||
|
|
- **schema**(对象|函数,可选):定义用于检测和处理粘贴内容的 [HTML 内容模型](https://html.spec.whatwg.org/multipage/dom.html#content-models),详见[下文](#内容模型与架构)
|
|||
|
|
- **selector**(字符串,可选):用于根据 [element.matches](https://developer.mozilla.org/en-US/docs/Web/API/Element/matches) 方法判断元素是否匹配的 CSS 选择器字符串。若元素不匹配则不会执行转换。这是 `isMatch` 的简写替代方案,若同时存在则优先使用 `isMatch`
|
|||
|
|
- **isMatch**(函数,可选):接收正在处理节点的回调函数,应返回布尔值。返回 `false` 将阻止应用此转换
|
|||
|
|
- **priority**(数字,可选):控制转换应用的优先级,数值较低的优先级高于数值较高的优先级。其行为类似于 [WordPress 钩子](https://developer.wordpress.org/reference/#Hook_to_WordPress)。与钩子类似,未设置时的默认优先级为 `10`
|
|||
|
|
|
|||
|
|
**示例:从 URL 创建嵌入区块**
|
|||
|
|
|
|||
|
|
若要在用户向编辑器粘贴 URL 时创建嵌入区块,可使用以下代码:
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
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(),
|
|||
|
|
} );
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
<h4 id="内容模型与架构">内容模型与架构</h4>
|
|||
|
|
|
|||
|
|
在粘贴内容时,可以定义用于验证和处理粘贴内容的[内容模型](https://html.spec.whatwg.org/multipage/dom.html#content-models)。粘贴到编辑器中的 HTML 通常包含应当转移和不应转移的混合元素。例如考虑将 `<span class="time">12:04 pm</span>` 粘贴到编辑器中的情况:我们需要复制 `12:04 pm` 并忽略 `<span>` 及其 `class` 属性,因为这些元素在复制后不再具有原有的含义和结构。
|
|||
|
|
|
|||
|
|
在编写 `raw` 转换时,可通过提供描述允许内容的 `schema` 来控制此行为,该架构将在尝试与区块匹配之前对粘贴内容进行清理。这些架构会传入 [`@wordpress/dom` 中的 `cleanNodeList`](https://github.com/wordpress/gutenberg/blob/trunk/packages/dom/src/dom/clean-node-list.js);请查阅该处获取[架构的完整描述](https://github.com/wordpress/gutenberg/blob/trunk/packages/dom/src/phrasing-content.js)。
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
schema = { span: { children: { '#text': {} } } };
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**示例:自定义内容模型**
|
|||
|
|
|
|||
|
|
假设我们需要匹配以下 HTML 片段并将其转换为某种自定义文章预览区块:
|
|||
|
|
|
|||
|
|
```html
|
|||
|
|
<div data-post-id="13">
|
|||
|
|
<h2>文章标题</h2>
|
|||
|
|
<p>一段<em>精彩</em>内容</p>
|
|||
|
|
</div>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
我们需要通过提供以下架构来告知编辑器允许内部的 `h2` 和 `p` 元素。此示例中使用函数形式,该函数接收包含 `phrasingContentSchema` 的参数(以及指示转换操作是否以粘贴文本开始的布尔值 `isPaste`)。`phrasingContentSchema` 预定义为匹配 HTML 短语元素,如 `<strong>`、`<sup>` 和 `<kbd>`。任何期望使用 `<RichText />` 组件的地方都适合允许短语内容,否则在转换过程中将丢失所有文本格式。
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
schema = ({ phrasingContentSchema }) => {
|
|||
|
|
div: {
|
|||
|
|
required: true,
|
|||
|
|
attributes: [ 'data-post-id' ],
|
|||
|
|
children: {
|
|||
|
|
h2: { children: phrasingContentSchema },
|
|||
|
|
p: { children: phrasingContentSchema }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|