# 关于传统小工具区块

传统小工具区块允许用户添加、编辑和预览由插件注册的第三方小工具，以及使用经典小工具编辑器添加的小工具。

可通过区块插入器添加传统小工具区块，并从该区块的下拉菜单中选择小工具来添加第三方小工具。

也可通过在区块插入器中搜索小工具名称并选择该小工具来添加第三方小工具。系统将插入一个传统小工具区块的变体。

## 与传统小工具区块的兼容性

### `widget-added` 事件

传统小工具区块将以类似于定制器的方式显示小工具的表单，因此与大多数第三方小工具兼容。

如果小工具在其表单中使用 JavaScript，则必须在 `document` 上触发 `'widget-added'` jQuery 事件后，将事件添加到 DOM 中。

例如，当“更改密码”复选框被勾选时，小工具可能希望显示一个“密码”字段。

```js
( function ( $ ) {
	$( document ).on( 'widget-added', function ( $event, $control ) {
		$control.find( '.change-password' ).on( 'change', function () {
			var isChecked = $( this ).prop( 'checked' );
			$control.find( '.password' ).toggleClass( 'hidden', ! isChecked );
		} );
	} );
} )( jQuery );
```

请注意，所有小工具的事件处理程序都在 `widget-added` 回调中添加。

### 显示“无预览可用”

当未选择传统小工具区块时，该区块将显示小工具的预览。

当小工具的 `widget()` 函数未呈现任何内容或仅呈现空的 HTML 元素时，传统小工具区块会自动显示“无预览可用”的消息。

小工具可以通过在不应显示预览时从 `widget()` 提前返回来利用这一点。

```php
class ExampleWidget extends WP_Widget {
	...
	public function widget( $instance ) {
		if ( ! isset( $instance['name'] ) ) {
			// 名称为必填项，如果没有则什么都不显示。
			return;
		}
		?>
		<h3>名称：<?php echo esc_html( $instance['name'] ); ?></h3>
		...
		<?php
	}
	...
}
```

### 允许迁移到区块

您可以允许用户轻松将包含特定小工具的传统小工具区块迁移到一个或多个区块。这使得插件作者可以逐步淘汰他们的小工具，转而使用更直观且可以在更多地方使用的区块。

以下步骤展示了如何实现这一点。

#### 1) 在 REST API 中显示小工具的实例

首先，我们需要告诉 WordPress 允许在 REST API 中显示您的小工具的实例数组。

如果满足以下条件，则可以安全地执行此操作：

-   您知道小工具在 `$instance` 中存储的所有值都可以用 JSON 表示；并且
-   您知道小工具没有在 `$instance` 中存储任何应对具有站点定制权限的用户隐藏的私有数据。

如果这样做是安全的，则在注册小工具时包含一个名为 `show_instance_in_rest` 的小工具选项，并将其值设置为 `true`。

```php
class ExampleWidget extends WP_Widget {
	...
	/**
	 * 设置小工具
	 */
	public function __construct() {
		$widget_ops = array(
			// ...其他选项
			'show_instance_in_rest' => true,
			// ...其他选项
		);
		parent::__construct( 'example_widget', 'ExampleWidget', $widget_ops );
	}
	...
}
```

这允许区块编辑器和其他 REST API 客户端通过访问 REST API 响应中的 `instance.raw` 来查看您的小工具的实例数组。

请注意，[WordPress 5.8.0 之前的版本允许您通过在扩展 `WP_Widget` 的类中将 `$show_instance_in_rest` 设置为 `true`](https://core.trac.wordpress.org/ticket/53332) 来启用此功能。

```php
class ExampleWidget extends WP_Widget {
	...
	public $show_instance_in_rest = true;
	...
}
```

现在已不推荐使用此方法，建议改用小工具选项方法。

#### 2) 添加区块转换

现在，我们可以定义一个[区块转换](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-transforms/)，告诉区块编辑器用什么替换包含您小工具的传统小工具区块。

这是通过向区块定义中添加 JavaScript 代码来实现的。在此示例中，我们定义了一个转换，将 ID 为 `'example_widget'` 的小工具转换为名称为 `'example/block'` 的区块。

```js
transforms: {
	from: [
		{
			type: 'block',
			blocks: [ 'core/legacy-widget' ],
			isMatch: ( { idBase, instance } ) => {
				if ( ! instance?.raw ) {
					// 如果原始实例未在 REST API 中显示，则无法转换。
					return false;
				}
				return idBase === 'example_widget';
			},
			transform: ( { instance } ) => {
				return createBlock( 'example/block', {
					name: instance.raw.name,
				} );
			},
		},
	]
},
```

#### 3) 在传统小工具区块中隐藏小工具

最后，我们可以告诉传统小工具区块在“选择小工具”下拉列表和区块插入器中隐藏您的小工具。这鼓励用户使用替换您小工具的区块。

可以使用 `widget_types_to_hide_from_legacy_widget_block` 过滤器来实现这一点。

```php
function hide_example_widget( $widget_types ) {
	$widget_types[] = 'example_widget';
	return $widget_types;
}
add_filter( 'widget_types_to_hide_from_legacy_widget_block', 'hide_example_widget' );
```

## 在其他区块编辑器中使用传统小工具区块（高级）

您可以选择允许在其他区块编辑器（例如 WordPress 文章编辑器）中使用传统小工具区块。默认情况下，此功能未启用。

首先，确保页面上加载了传统小工具所需的任何样式和脚本。一种便捷的方法是手动执行用户浏览小工具 WP 管理屏幕时通常运行的所有钩子。

```php
add_action( 'admin_print_styles', function() {
	if ( get_current_screen()->is_block_editor() ) {
		do_action( 'admin_print_styles-widgets.php' );
	}
} );
add_action( 'admin_print_scripts', function() {
	if ( get_current_screen()->is_block_editor() ) {
		do_action( 'load-widgets.php' );
		do_action( 'widgets.php' );
		do_action( 'sidebar_admin_setup' );
		do_action( 'admin_print_scripts-widgets.php' );
	}
} );
add_action( 'admin_print_footer_scripts', function() {
	if ( get_current_screen()->is_block_editor() ) {
		do_action( 'admin_print_footer_scripts-widgets.php' );
	}
} );
add_action( 'admin_footer', function() {
	if ( get_current_screen()->is_block_editor() ) {
		do_action( 'admin_footer-widgets.php' );
	}
} );
```

然后，使用 `@wordpress/widgets` 包中定义的 `registerLegacyWidgetBlock` 注册传统小工具区块。

```php
add_action( 'enqueue_block_editor_assets', function() {
	wp_enqueue_script( 'wp-widgets' );
	wp_add_inline_script( 'wp-widgets', 'wp.widgets.registerLegacyWidgetBlock()' );
} );
```