diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md
index df16ea29219d54..5199ce7c7b8cc9 100644
--- a/docs/reference-guides/core-blocks.md
+++ b/docs/reference-guides/core-blocks.md
@@ -330,8 +330,8 @@ Insert an image to make a visual statement. ([Source](https://github.com/WordPre
- **Name:** core/image
- **Category:** media
-- **Supports:** anchor, color (~~background~~, ~~text~~), filter (duotone)
-- **Attributes:** align, alt, behaviors, caption, height, href, id, linkClass, linkDestination, linkTarget, rel, sizeSlug, title, url, width
+- **Supports:** anchor, behaviors (lightbox), color (~~background~~, ~~text~~), filter (duotone)
+- **Attributes:** align, alt, caption, height, href, id, linkClass, linkDestination, linkTarget, rel, sizeSlug, title, url, width
## Latest Comments
diff --git a/lib/block-supports/behaviors.php b/lib/block-supports/behaviors.php
new file mode 100644
index 00000000000000..ea753b2e6f2612
--- /dev/null
+++ b/lib/block-supports/behaviors.php
@@ -0,0 +1,138 @@
+attributes ) {
+ $block_type->attributes = array();
+ }
+
+ $block_type->attributes['behaviors'] = array(
+ 'type' => 'object',
+ );
+
+ // If it supports the lightbox behavior, add the hook to that block.
+ // In the future, this should be a loop with all the behaviors.
+ $has_lightbox_support = block_has_support( $block_type, array( 'behaviors', 'lightbox' ), false );
+ if ( $has_lightbox_support ) {
+ // Use priority 15 to run this hook after other hooks/plugins.
+ // They could use the `render_block_{$this->name}` filter to modify the markup.
+ add_filter( 'render_block_' . $block_type->name, 'gutenberg_render_behaviors_support_lightbox', 15, 2 );
+ }
+}
+
+/**
+ * Add the directives and layout needed for the lightbox behavior.
+ * This functions shouldn't be in this file. It should be moved to a package (or somewhere else), where all the behaviors logic is defined.
+ *
+ * @param string $block_content Rendered block content.
+ * @param array $block Block object.
+ * @return string Filtered block content.
+ */
+function gutenberg_render_behaviors_support_lightbox( $block_content, $block ) {
+ $experiments = get_option( 'gutenberg-experiments' );
+ $link_destination = isset( $block['attrs']['linkDestination'] ) ? $block['attrs']['linkDestination'] : 'none';
+ // Get the lightbox setting from the block attributes.
+ if ( isset( $block['attrs']['behaviors']['lightbox'] ) ) {
+ $lightbox = $block['attrs']['behaviors']['lightbox'];
+ // If the lightbox setting is not set in the block attributes, get it from the theme.json file.
+ } else {
+ $theme_data = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data()->get_data();
+ if ( isset( $theme_data['behaviors']['blocks'][ $block['blockName'] ]['lightbox'] ) ) {
+ $lightbox = $theme_data['behaviors']['blocks'][ $block['blockName'] ]['lightbox'];
+ } else {
+ $lightbox = false;
+ }
+ }
+
+ if ( ! $lightbox || 'none' !== $link_destination || empty( $experiments['gutenberg-interactivity-api-core-blocks'] ) ) {
+ return $block_content;
+ }
+
+ $processor = new WP_HTML_Tag_Processor( $block_content );
+
+ $aria_label = __( 'Enlarge image', 'gutenberg' );
+
+ $alt_attribute = trim( $processor->get_attribute( 'alt' ) );
+
+ if ( $alt_attribute ) {
+ /* translators: %s: Image alt text. */
+ $aria_label = sprintf( __( 'Enlarge image: %s', 'gutenberg' ), $alt_attribute );
+ }
+ $content = $processor->get_updated_html();
+
+ $w = new WP_HTML_Tag_Processor( $content );
+ $w->next_tag( 'figure' );
+ $w->add_class( 'wp-lightbox-container' );
+ $w->set_attribute( 'data-wp-interactive', true );
+ $w->set_attribute( 'data-wp-context', '{ "core": { "image": { "initialized": false, "lightboxEnabled": false } } }' );
+ $body_content = $w->get_updated_html();
+
+ // Wrap the image in the body content with a button.
+ $img = null;
+ preg_match( '/]+>/', $content, $img );
+ $button = '
+ '
+ . $img[0] .
+ '
';
+ $body_content = preg_replace( '/]+>/', $button, $body_content );
+
+ // Add directive to expand modal image if appropriate.
+ $m = new WP_HTML_Tag_Processor( $content );
+ $m->next_tag( 'img' );
+ $m->set_attribute( 'data-wp-context', '{ "core": { "image": { "imageSrc": "' . wp_get_attachment_url( $block['attrs']['id'] ) . '"} } }' );
+ $m->set_attribute( 'data-wp-bind--src', 'selectors.core.image.imageSrc' );
+ $modal_content = $m->get_updated_html();
+
+ $background_color = esc_attr( wp_get_global_styles( array( 'color', 'background' ) ) );
+
+ $close_button_icon = '';
+ $close_button_color = esc_attr( wp_get_global_styles( array( 'color', 'text' ) ) );
+ $dialog_label = $alt_attribute ? esc_attr( $alt_attribute ) : esc_attr__( 'Image', 'gutenberg' );
+ $close_button_label = esc_attr__( 'Close', 'gutenberg' );
+
+ $lightbox_html = <<
+
+ $modal_content
+
+
+HTML;
+
+ return str_replace( '', $lightbox_html . '', $body_content );
+}
+
+// Register the block support.
+WP_Block_Supports::get_instance()->register(
+ 'behaviors',
+ array(
+ 'register_attribute' => 'gutenberg_register_behaviors_support',
+ )
+);
diff --git a/lib/load.php b/lib/load.php
index 4f947263a572b1..b0ed333185ef6a 100644
--- a/lib/load.php
+++ b/lib/load.php
@@ -166,3 +166,4 @@ function gutenberg_is_experiment_enabled( $name ) {
require __DIR__ . '/block-supports/duotone.php';
require __DIR__ . '/block-supports/anchor.php';
require __DIR__ . '/block-supports/shadow.php';
+require __DIR__ . '/block-supports/behaviors.php';
diff --git a/packages/block-editor/src/hooks/behaviors.js b/packages/block-editor/src/hooks/behaviors.js
index 05c2792bb0419c..6676b51442579b 100644
--- a/packages/block-editor/src/hooks/behaviors.js
+++ b/packages/block-editor/src/hooks/behaviors.js
@@ -8,6 +8,7 @@ import {
__experimentalHStack as HStack,
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';
+import { hasBlockSupport } from '@wordpress/blocks';
import { createHigherOrderComponent } from '@wordpress/compose';
import { useSelect } from '@wordpress/data';
@@ -42,24 +43,17 @@ function BehaviorsControl( {
[ blockName ]
);
- if (
- ! settings ||
- // If every behavior is disabled, do not show the behaviors inspector control.
- Object.entries( settings ).every( ( [ , value ] ) => ! value )
- ) {
- return null;
- }
-
- // Block behaviors take precedence over theme behaviors.
- const behaviors = merge( themeBehaviors, blockBehaviors || {} );
-
const noBehaviorsOption = {
value: '',
label: __( 'No behaviors' ),
};
const behaviorsOptions = Object.entries( settings )
- .filter( ( [ , behaviorValue ] ) => behaviorValue ) // Filter out behaviors that are disabled.
+ .filter(
+ ( [ behaviorName, behaviorValue ] ) =>
+ hasBlockSupport( blockName, 'behaviors.' + behaviorName ) &&
+ behaviorValue
+ ) // Filter out behaviors that are disabled.
.map( ( [ behaviorName ] ) => ( {
value: behaviorName,
label:
@@ -68,8 +62,14 @@ function BehaviorsControl( {
behaviorName.slice( 1 ).toLowerCase(),
} ) );
+ // If every behavior is disabled, do not show the behaviors inspector control.
+ if ( behaviorsOptions.length === 0 ) return null;
+
const options = [ noBehaviorsOption, ...behaviorsOptions ];
+ // Block behaviors take precedence over theme behaviors.
+ const behaviors = merge( themeBehaviors, blockBehaviors || {} );
+
const helpText = disabled
? __( 'The lightbox behavior is disabled for linked images.' )
: __( 'Add behaviors.' );
@@ -116,8 +116,8 @@ function BehaviorsControl( {
export const withBehaviors = createHigherOrderComponent( ( BlockEdit ) => {
return ( props ) => {
const blockEdit = ;
- // Only add behaviors to the core/image block.
- if ( props.name !== 'core/image' ) {
+ // Only add behaviors to blocks with support.
+ if ( ! hasBlockSupport( props.name, 'behaviors' ) ) {
return blockEdit;
}
const blockHasLink =
diff --git a/packages/block-library/src/image/block.json b/packages/block-library/src/image/block.json
index d51d70dd711f0e..436331e37c3321 100644
--- a/packages/block-library/src/image/block.json
+++ b/packages/block-library/src/image/block.json
@@ -80,13 +80,13 @@
"source": "attribute",
"selector": "figure > a",
"attribute": "target"
- },
- "behaviors": {
- "type": "object"
}
},
"supports": {
"anchor": true,
+ "behaviors": {
+ "lightbox": true
+ },
"color": {
"text": false,
"background": false
diff --git a/packages/block-library/src/image/index.php b/packages/block-library/src/image/index.php
index 70a40f4b59db9c..1e437d860aa097 100644
--- a/packages/block-library/src/image/index.php
+++ b/packages/block-library/src/image/index.php
@@ -30,90 +30,6 @@ function render_block_core_image( $attributes, $content ) {
$processor->set_attribute( 'data-id', $attributes['data-id'] );
}
- $link_destination = isset( $attributes['linkDestination'] ) ? $attributes['linkDestination'] : 'none';
-
- // Get the lightbox setting from the block attributes.
- if ( isset( $attributes['behaviors']['lightbox'] ) ) {
- $lightbox = $attributes['behaviors']['lightbox'];
- // If the lightbox setting is not set in the block attributes, get it from the theme.json file.
- } else {
- $theme_data = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data()->get_data();
- if ( isset( $theme_data['behaviors']['blocks']['core/image']['lightbox'] ) ) {
- $lightbox = $theme_data['behaviors']['blocks']['core/image']['lightbox'];
- } else {
- $lightbox = false;
- }
- }
-
- $experiments = get_option( 'gutenberg-experiments' );
-
- if ( ! empty( $experiments['gutenberg-interactivity-api-core-blocks'] ) && 'none' === $link_destination && $lightbox ) {
-
- $aria_label = __( 'Enlarge image' );
-
- $alt_attribute = trim( $processor->get_attribute( 'alt' ) );
-
- if ( $alt_attribute ) {
- /* translators: %s: Image alt text. */
- $aria_label = sprintf( __( 'Enlarge image: %s' ), $alt_attribute );
- }
- $content = $processor->get_updated_html();
-
- $w = new WP_HTML_Tag_Processor( $content );
- $w->next_tag( 'figure' );
- $w->add_class( 'wp-lightbox-container' );
- $w->set_attribute( 'data-wp-interactive', true );
- $w->set_attribute( 'data-wp-context', '{ "core": { "image": { "initialized": false, "lightboxEnabled": false } } }' );
- $body_content = $w->get_updated_html();
-
- // Wrap the image in the body content with a button.
- $img = null;
- preg_match( '/]+>/', $content, $img );
- $button = '
- '
- . $img[0] .
- '
';
- $body_content = preg_replace( '/]+>/', $button, $body_content );
-
- // Add directive to expand modal image if appropriate.
- $m = new WP_HTML_Tag_Processor( $content );
- $m->next_tag( 'img' );
- $m->set_attribute( 'data-wp-context', '{ "core": { "image": { "imageSrc": "' . wp_get_attachment_url( $attributes['id'] ) . '"} } }' );
- $m->set_attribute( 'data-wp-bind--src', 'selectors.core.image.imageSrc' );
- $modal_content = $m->get_updated_html();
-
- $background_color = esc_attr( wp_get_global_styles( array( 'color', 'background' ) ) );
-
- $close_button_icon = '';
- $close_button_color = esc_attr( wp_get_global_styles( array( 'color', 'text' ) ) );
-
- $dialog_label = $alt_attribute ? esc_attr( $alt_attribute ) : esc_attr__( 'Image' );
- $close_button_label = esc_attr__( 'Close' );
-
- $lightbox_html = <<
-
- $modal_content
-
-
-HTML;
-
- return str_replace( '', $lightbox_html . '', $body_content );
- }
-
return $processor->get_updated_html();
}