diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md
index 33d791e5c77df7..ae187eab57b26d 100644
--- a/docs/reference-guides/core-blocks.md
+++ b/docs/reference-guides/core-blocks.md
@@ -390,7 +390,7 @@ Display multiple images in a rich gallery. ([Source](https://github.com/WordPres
- **Category:** media
- **Allowed Blocks:** core/image
- **Supports:** align, anchor, color (background, gradients, ~~text~~), interactivity (clientNavigation), layout (default, ~~allowEditing~~, ~~allowInheriting~~, ~~allowSwitching~~), spacing (blockGap, margin, padding), units (em, px, rem, vh, vw), ~~html~~
-- **Attributes:** allowResize, aspectRatio, caption, columns, fixedHeight, ids, imageCrop, images, linkTarget, linkTo, randomOrder, shortCodeTransforms, sizeSlug
+- **Attributes:** allowResize, aspectRatio, caption, columns, fixedHeight, ids, imageCrop, images, linkTarget, linkTo, navigationButtonType, randomOrder, shortCodeTransforms, sizeSlug
## Group
diff --git a/packages/block-library/src/gallery/block.json b/packages/block-library/src/gallery/block.json
index 02dbb05cb78b48..0b13bd51fe0aaa 100644
--- a/packages/block-library/src/gallery/block.json
+++ b/packages/block-library/src/gallery/block.json
@@ -4,6 +4,7 @@
"name": "core/gallery",
"title": "Gallery",
"category": "media",
+ "usesContext": [ "galleryId" ],
"allowedBlocks": [ "core/image" ],
"description": "Display multiple images in a rich gallery.",
"keywords": [ "images", "photos" ],
@@ -60,6 +61,11 @@
},
"default": []
},
+ "navigationButtonType": {
+ "type": "string",
+ "default": "icon",
+ "enum": [ "icon", "text", "both" ]
+ },
"shortCodeTransforms": {
"type": "array",
"items": {
@@ -112,7 +118,8 @@
"providesContext": {
"allowResize": "allowResize",
"imageCrop": "imageCrop",
- "fixedHeight": "fixedHeight"
+ "fixedHeight": "fixedHeight",
+ "navigationButtonType": "navigationButtonType"
},
"supports": {
"anchor": true,
diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js
index 16fc9d497fa94f..5488705a24ec96 100644
--- a/packages/block-library/src/gallery/edit.js
+++ b/packages/block-library/src/gallery/edit.js
@@ -14,6 +14,8 @@ import {
MenuItem,
__experimentalToolsPanel as ToolsPanel,
__experimentalToolsPanelItem as ToolsPanelItem,
+ __experimentalToggleGroupControl as ToggleGroupControl,
+ __experimentalToggleGroupControlOption as ToggleGroupControlOption,
ToolbarDropdownMenu,
PanelBody,
} from '@wordpress/components';
@@ -94,6 +96,20 @@ const LINK_OPTIONS = [
noticeText: __( 'None' ),
},
];
+const NAVIGATION_BUTTON_TYPE_OPTIONS = [
+ {
+ label: __( 'Icon' ),
+ value: 'icon',
+ },
+ {
+ label: __( 'Text' ),
+ value: 'text',
+ },
+ {
+ label: __( 'Both' ),
+ value: 'both',
+ },
+];
const ALLOWED_MEDIA_TYPES = [ 'image' ];
const PLACEHOLDER_TEXT = Platform.isNative
@@ -134,6 +150,7 @@ export default function GalleryEdit( props ) {
: LINK_OPTIONS;
const {
+ navigationButtonType,
columns,
imageCrop,
randomOrder,
@@ -205,6 +222,16 @@ export default function GalleryEdit( props ) {
const newImages = useGetNewImages( images, imageData );
+ // Check if there is at least one image with lightbox enabled
+ const hasLightboxImages = lightboxSetting?.enabled
+ ? images.filter(
+ ( image ) =>
+ image.attributes?.lightbox?.enabled === undefined ||
+ image.attributes?.lightbox?.enabled === true
+ ).length > 0
+ : images.filter( ( image ) => image.attributes.lightbox?.enabled )
+ .length > 0;
+
const themeOptions = themeRatios?.map( ( { name, ratio } ) => ( {
label: name,
value: ratio,
@@ -644,6 +671,7 @@ export default function GalleryEdit( props ) {
label={ __( 'Settings' ) }
resetAll={ () => {
setAttributes( {
+ navigationButtonType: 'icon',
columns: undefined,
imageCrop: true,
randomOrder: false,
@@ -779,6 +807,43 @@ export default function GalleryEdit( props ) {
/>
) }
+ navigationButtonType !== 'icon' }
+ onDeselect={ () =>
+ setAttributes( {
+ navigationButtonType: 'icon',
+ } )
+ }
+ >
+ { hasLightboxImages && (
+
+ setAttributes( {
+ navigationButtonType: value,
+ } )
+ }
+ isBlock
+ __next40pxDefaultSize
+ help={ __(
+ 'Adjust the appearance of buttons in the lightbox.'
+ ) }
+ >
+ { NAVIGATION_BUTTON_TYPE_OPTIONS.map(
+ ( option ) => (
+
+ )
+ ) }
+
+ ) }
+
) }
{ Platform.isNative && (
diff --git a/packages/block-library/src/gallery/index.php b/packages/block-library/src/gallery/index.php
index 5281fec126e42c..6e9755b1090336 100644
--- a/packages/block-library/src/gallery/index.php
+++ b/packages/block-library/src/gallery/index.php
@@ -34,16 +34,35 @@ function block_core_gallery_data_id_backcompatibility( $parsed_block ) {
add_filter( 'render_block_data', 'block_core_gallery_data_id_backcompatibility' );
+/**
+ * Adds a unique ID to the gallery block context.
+ *
+ * @since 7.0.0
+ *
+ * @param array $context Default context.
+ * @param array $parsed_block Block being rendered, filtered by render_block_data.
+ * @return array Filtered context.
+ */
+function block_core_gallery_render_context( $context, $parsed_block ) {
+ if ( 'core/gallery' === $parsed_block['blockName'] ) {
+ $context['galleryId'] = uniqid();
+ }
+ return $context;
+}
+
+add_filter( 'render_block_context', 'block_core_gallery_render_context', 10, 2 );
+
/**
* Renders the `core/gallery` block on the server.
*
* @since 6.0.0
*
* @param array $attributes Attributes of the block being rendered.
- * @param string $content Content of the block being rendered.
+ * @param string $content Content of the block being rendered.
+ * @param array $block The block instance being rendered.
* @return string The content of the block being rendered.
*/
-function block_core_gallery_render( $attributes, $content ) {
+function block_core_gallery_render( $attributes, $content, $block ) {
// Adds a style tag for the --wp--style--unstable-gallery-gap var.
// The Gallery block needs to recalculate Image block width based on
// the current gap setting in order to maintain the number of flex columns
@@ -116,11 +135,63 @@ function block_core_gallery_render( $attributes, $content ) {
wp_style_engine_get_stylesheet_from_css_rules(
$gallery_styles,
- array(
- 'context' => 'block-supports',
+ array( 'context' => 'block-supports' )
+ );
+
+ // Gets all image IDs from the state that match this gallery's ID.
+ $state = wp_interactivity_state( 'core/image' );
+ $gallery_id = $block->context['galleryId'] ?? null;
+ $image_ids = array();
+ if ( isset( $gallery_id ) && isset( $state['metadata'] ) ) {
+ foreach ( $state['metadata'] as $image_id => $metadata ) {
+ if ( isset( $metadata['galleryId'] ) && $metadata['galleryId'] === $gallery_id ) {
+ $image_ids[] = $image_id;
+ }
+ }
+ }
+
+ // Randomize image IDs when `randomOrder` setting is enabled
+ if ( ! empty( $attributes['randomOrder'] ) && ! empty( $image_ids ) ) {
+ shuffle( $image_ids );
+ }
+
+ $processed_content->set_attribute( 'data-wp-interactive', 'core/gallery' );
+ $processed_content->set_attribute(
+ 'data-wp-context',
+ wp_json_encode(
+ array( 'galleryId' => $gallery_id ),
+ JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
)
);
+ // Populates the aria label for each image in the gallery.
+ if ( ! empty( $image_ids ) ) {
+ if ( 1 <= count( $image_ids ) ) {
+ for ( $i = 0; $i < count( $image_ids ); $i++ ) {
+ $image_id = $image_ids[ $i ];
+ $alt = $state['metadata'][ $image_id ]['alt'];
+ wp_interactivity_state(
+ 'core/image',
+ array(
+ 'metadata' => array(
+ $image_id => array(
+ 'customAriaLabel' => empty( $alt )
+ /* translators: %1$s: current image index, %2$s: total number of images */
+ ? sprintf( __( 'Enlarged image %1$s of %2$s' ), $i + 1, count( $image_ids ) )
+ /* translators: %1$s: current image index, %2$s: total number of images, %3$s: Image alt text */
+ : sprintf( __( 'Enlarged image %1$s of %2$s: %3$s' ), $i + 1, count( $image_ids ), $alt ),
+ /* translators: %1$s: current image index, %2$s: total number of images */
+ 'triggerButtonAriaLabel' => sprintf( __( 'Enlarge %1$s of %2$s' ), $i + 1, count( $image_ids ) ),
+ // This metadata is used to store the order of the images in the gallery.
+ 'order' => $i,
+ ),
+ ),
+ )
+ );
+ }
+ }
+ }
+
// The WP_HTML_Tag_Processor class calls get_updated_html() internally
// when the instance is treated as a string, but here we explicitly
// convert it to a string.
@@ -151,13 +222,22 @@ function block_core_gallery_render( $attributes, $content ) {
}
$image_blocks = $matches[0];
- // Randomize the order of Image blocks.
- shuffle( $image_blocks );
+ // Reorder image blocks according to the randomized `$image_ids` order
+ $reordered_blocks = array();
+ foreach ( $image_ids as $image_id ) {
+ foreach ( $image_blocks as $block ) {
+ if ( strpos( $block, $image_id ) !== false ) {
+ $reordered_blocks[] = $block;
+ break;
+ }
+ }
+ }
+
$i = 0;
$content = preg_replace_callback(
$pattern,
- static function () use ( $image_blocks, &$i ) {
- $new_image_block = $image_blocks[ $i ];
+ static function () use ( $reordered_blocks, &$i ) {
+ $new_image_block = $reordered_blocks[ $i ];
++$i;
return $new_image_block;
},
@@ -166,6 +246,7 @@ static function () use ( $image_blocks, &$i ) {
return $content;
}
+
/**
* Registers the `core/gallery` block on server.
*
diff --git a/packages/block-library/src/image/block.json b/packages/block-library/src/image/block.json
index 54e33f3264780b..66a4fac4a30236 100644
--- a/packages/block-library/src/image/block.json
+++ b/packages/block-library/src/image/block.json
@@ -8,9 +8,11 @@
"allowResize",
"imageCrop",
"fixedHeight",
+ "navigationButtonType",
"postId",
"postType",
- "queryId"
+ "queryId",
+ "galleryId"
],
"description": "Insert an image to make a visual statement.",
"keywords": [ "img", "photo", "picture" ],
diff --git a/packages/block-library/src/image/index.php b/packages/block-library/src/image/index.php
index bc312167f53295..7e37ca0f021cc1 100644
--- a/packages/block-library/src/image/index.php
+++ b/packages/block-library/src/image/index.php
@@ -117,7 +117,7 @@ public function block_core_image_extract_empty_figcaption_element() {
* if the way the blocks are rendered changes, or if a new kind of filter is
* introduced.
*/
- add_filter( 'render_block_core/image', 'block_core_image_render_lightbox', 15, 2 );
+ add_filter( 'render_block_core/image', 'block_core_image_render_lightbox', 15, 3 );
} else {
/*
* Remove the filter if previously added by other Image blocks.
@@ -171,18 +171,20 @@ function block_core_image_get_lightbox_settings( $block ) {
*
* @since 6.4.0
*
- * @param string $block_content Rendered block content.
- * @param array $block Block object.
+ * @param string $block_content Rendered block content.
+ * @param array $block Block object.
+ * @param array $block_instance Block instance.
*
* @return string Filtered block content.
*/
-function block_core_image_render_lightbox( $block_content, $block ) {
+function block_core_image_render_lightbox( $block_content, $block, $block_instance ) {
/*
* If there's no IMG tag in the block then return the given block content
* as-is. There's nothing that this code can knowingly modify to add the
* lightbox behavior.
*/
$processor = new WP_HTML_Tag_Processor( $block_content );
+
if ( $processor->next_tag( 'figure' ) ) {
$processor->set_bookmark( 'figure' );
}
@@ -190,15 +192,28 @@ function block_core_image_render_lightbox( $block_content, $block ) {
return $block_content;
}
- $alt = $processor->get_attribute( 'alt' );
- $img_uploaded_src = $processor->get_attribute( 'src' );
- $img_class_names = $processor->get_attribute( 'class' );
- $img_styles = $processor->get_attribute( 'style' );
- $img_width = 'none';
- $img_height = 'none';
- $img_srcset = false;
- $aria_label = __( 'Enlarge' );
- $dialog_aria_label = __( 'Enlarged image' );
+ $alt = $processor->get_attribute( 'alt' );
+ $img_uploaded_src = $processor->get_attribute( 'src' );
+ $img_class_names = $processor->get_attribute( 'class' );
+ $img_styles = $processor->get_attribute( 'style' );
+ $img_width = 'none';
+ $img_height = 'none';
+ $img_srcset = false;
+
+ wp_interactivity_config(
+ 'core/image',
+ array(
+ 'defaultAriaLabel' => __( 'Enlarged image' ),
+ 'closeButtonText' => esc_html__( 'Close' ),
+ 'prevButtonText' => esc_html__( 'Previous' ),
+ 'nextButtonText' => esc_html__( 'Next' ),
+ )
+ );
+
+ if ( $alt ) {
+ /* translators: %s: Image alt text. */
+ $custom_aria_label = sprintf( __( 'Enlarged image: %s' ), $alt );
+ }
if ( isset( $block['attrs']['id'] ) ) {
$img_uploaded_src = wp_get_attachment_url( $block['attrs']['id'] );
@@ -215,23 +230,25 @@ function block_core_image_render_lightbox( $block_content, $block ) {
// Create unique id and set the image metadata in the state.
$unique_image_id = uniqid();
-
wp_interactivity_state(
'core/image',
array(
'metadata' => array(
$unique_image_id => array(
- 'uploadedSrc' => $img_uploaded_src,
- 'lightboxSrcset' => $img_srcset,
- 'figureClassNames' => $figure_class_names,
- 'figureStyles' => $figure_styles,
- 'imgClassNames' => $img_class_names,
- 'imgStyles' => $img_styles,
- 'targetWidth' => $img_width,
- 'targetHeight' => $img_height,
- 'scaleAttr' => $block['attrs']['scale'] ?? false,
- 'ariaLabel' => $dialog_aria_label,
- 'alt' => $alt,
+ 'uploadedSrc' => $img_uploaded_src,
+ 'lightboxSrcset' => $img_srcset,
+ 'figureClassNames' => $figure_class_names,
+ 'figureStyles' => $figure_styles,
+ 'imgClassNames' => $img_class_names,
+ 'imgStyles' => $img_styles,
+ 'targetWidth' => $img_width,
+ 'targetHeight' => $img_height,
+ 'scaleAttr' => $block['attrs']['scale'] ?? false,
+ 'alt' => $alt,
+ 'galleryId' => $block_instance->context['galleryId'] ?? null,
+ 'customAriaLabel' => $custom_aria_label ?? null,
+ 'navigationButtonType' => $block_instance->context['navigationButtonType'] ?? 'icon',
+ 'triggerButtonAriaLabel' => null,
),
),
)
@@ -242,9 +259,7 @@ function block_core_image_render_lightbox( $block_content, $block ) {
$processor->set_attribute(
'data-wp-context',
wp_json_encode(
- array(
- 'imageId' => $unique_image_id,
- ),
+ array( 'imageId' => $unique_image_id ),
JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
)
);
@@ -282,11 +297,11 @@ function block_core_image_render_lightbox( $block_content, $block ) {
class="lightbox-trigger"
type="button"
aria-haspopup="dialog"
- aria-label="' . esc_attr( $aria_label ) . '"
+ data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
data-wp-init="callbacks.initTriggerButton"
data-wp-on--click="actions.showLightbox"
- data-wp-style--right="state.imageButtonRight"
- data-wp-style--top="state.imageButtonTop"
+ data-wp-style--right="state.thisImage.buttonRight"
+ data-wp-style--top="state.thisImage.buttonTop"
>