From 508c70e25d74c26b9a3bf2c0ce922670841c0e70 Mon Sep 17 00:00:00 2001 From: madhusudhand Date: Mon, 15 Jul 2024 16:21:35 +0530 Subject: [PATCH 01/58] add lightbox support to gallery block --- packages/block-library/src/gallery/index.php | 58 ++++++++++++++- packages/block-library/src/gallery/view.js | 16 +++++ packages/block-library/src/image/index.php | 11 ++- packages/block-library/src/image/style.scss | 34 +++++++++ packages/block-library/src/image/view.js | 74 ++++++++++++++++++-- tools/webpack/interactivity.js | 1 + 6 files changed, 187 insertions(+), 7 deletions(-) create mode 100644 packages/block-library/src/gallery/view.js diff --git a/packages/block-library/src/gallery/index.php b/packages/block-library/src/gallery/index.php index 5af6be16d39a2b..d0ac13d40391a7 100644 --- a/packages/block-library/src/gallery/index.php +++ b/packages/block-library/src/gallery/index.php @@ -43,7 +43,7 @@ function block_core_gallery_data_id_backcompatibility( $parsed_block ) { * @param string $content Content of the block 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 @@ -121,6 +121,35 @@ function block_core_gallery_render( $attributes, $content ) { ) ); + $lightbox_settings = block_core_image_get_lightbox_settings( $block->parsed_block ); + + if ( + isset( $lightbox_settings ) && + // 'none' === $link_destination && + isset( $lightbox_settings['enabled'] ) && + true === $lightbox_settings['enabled'] + ) { + $processed_content->set_attribute( 'data-wp-interactive', 'core/gallery' ); + $processed_content->set_attribute( 'data-wp-context', '{"lightbox": true, "images": []}' ); + $processed_content->set_attribute( 'data-wp-init', 'callbacks.init' ); + $processed_content->set_attribute( 'data-wp-on-async--load', 'callbacks.init' ); + + $suffix = wp_scripts_get_suffix(); + if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) { + $module_url = gutenberg_url( '/build/interactivity/gallery.min.js' ); + } + + wp_register_script_module( + '@wordpress/block-library/gallery', + isset( $module_url ) ? $module_url : includes_url( "blocks/gallery/view{$suffix}.js" ), + array( '@wordpress/interactivity' ), + defined( 'GUTENBERG_VERSION' ) ? GUTENBERG_VERSION : get_bloginfo( 'version' ) + ); + + wp_enqueue_script_module( '@wordpress/block-library/gallery' ); + + } + // 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. @@ -166,6 +195,33 @@ static function () use ( $image_blocks, &$i ) { return $content; } + +// NOTE: this setting isn't required for the gallery block. +// since lightbox implementation is done in the image block, it can totally be removed. +function block_core_gallery_get_lightbox_settings( $block ) { + // Gets the lightbox setting from the block attributes. + if ( isset( $block['attrs']['lightbox'] ) ) { + $lightbox_settings = $block['attrs']['lightbox']; + } + + if ( ! isset( $lightbox_settings ) ) { + // TODO: change it to gallery block name. + $lightbox_settings = wp_get_global_settings( array( 'lightbox' ), array( 'block_name' => 'core/image' ) ); + + // If not present in global settings, check the top-level global settings. + // + // NOTE: If no block-level settings are found, the previous call to + // `wp_get_global_settings` will return the whole `theme.json` structure in + // which case we can check if the "lightbox" key is present at the top-level + // of the global settings and use its value. + if ( isset( $lightbox_settings['lightbox'] ) ) { + $lightbox_settings = wp_get_global_settings( array( 'lightbox' ) ); + } + } + + return $lightbox_settings ?? null; +} + /** * Registers the `core/gallery` block on server. * diff --git a/packages/block-library/src/gallery/view.js b/packages/block-library/src/gallery/view.js new file mode 100644 index 00000000000000..0dc914fcf699eb --- /dev/null +++ b/packages/block-library/src/gallery/view.js @@ -0,0 +1,16 @@ +/** + * WordPress dependencies + */ +import { store } from '@wordpress/interactivity'; + +store( + 'core/gallery', + { + callbacks: { + init() {}, + }, + }, + { + lock: false, + } +); diff --git a/packages/block-library/src/image/index.php b/packages/block-library/src/image/index.php index 75f0d404e4820c..7bd7bf7c067f2d 100644 --- a/packages/block-library/src/image/index.php +++ b/packages/block-library/src/image/index.php @@ -220,6 +220,7 @@ function block_core_image_render_lightbox( $block_content, $block ) { JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP ) ); + $p->set_attribute( 'data-wp-init', 'callbacks.initImage' ); // Image. $p->next_tag( 'img' ); @@ -268,6 +269,8 @@ class="lightbox-trigger" */ function block_core_image_print_lightbox_overlay() { $close_button_label = esc_attr__( 'Close' ); + $prev_button_label = esc_attr__( 'Previous' ); + $next_button_label = esc_attr__( 'Next' ); // If the current theme does NOT have a `theme.json`, or the colors are not // defined, it needs to set the background color & close button color to some @@ -288,7 +291,7 @@ function block_core_image_print_lightbox_overlay() { HTML; diff --git a/packages/block-library/src/image/view.js b/packages/block-library/src/image/view.js index 5039e6dbb22c05..baff5e42a08f28 100644 --- a/packages/block-library/src/image/view.js +++ b/packages/block-library/src/image/view.js @@ -1,8 +1,12 @@ /** * WordPress dependencies */ -import { store, getContext, getElement } from '@wordpress/interactivity'; -// import { __, sprintf } from '@wordpress/i18n'; +import { + store, + getContext, + getElement, + getConfig, +} from '@wordpress/interactivity'; /** * Tracks whether user is touching screen; used to differentiate behavior for @@ -20,39 +24,47 @@ let isTouching = false; */ let lastTouchTime = 0; -/** - * Holds all elements that are made inert when the lightbox is open; used to - * remove inert attribute of only those elements explicitly made inert. - * - * @type {Array} - */ -let inertElements = []; - const { state, actions, callbacks } = store( 'core/image', { state: { - images: [], - currentImageIndex: -1, - get currentImageId() { - return state.currentImageIndex > -1 && state.images.length > 0 - ? state.images[ state.currentImageIndex ] - : null; + selectedImageId: null, + selectedGalleryId: null, + get galleryImages() { + return state.selectedGalleryId + ? Object.entries( state.metadata ) + .filter( + ( [ , value ] ) => + value.galleryId === state.selectedGalleryId + ) + .map( ( [ key ] ) => key ) + : [ state.selectedImageId ]; + }, + get selectedImageIndex() { + return state.galleryImages.findIndex( + ( id ) => id === state.selectedImageId + ); + }, + get selectedImage() { + return state.metadata[ state.selectedImageId ]; }, - get currentImage() { - return state.metadata[ state.currentImageId ]; + get thisImage() { + const { imageId } = getContext(); + return state.metadata[ imageId ]; }, get hasNavigation() { - return state.images.length > 1; + return state.galleryImages.length > 1; }, get hasNextImage() { - return state.currentImageIndex + 1 < state.images.length; + return ( + state.selectedImageIndex + 1 < state.galleryImages.length + ); }, get hasPreviousImage() { - return state.currentImageIndex - 1 >= 0; + return state.selectedImageIndex - 1 >= 0; }, get overlayOpened() { - return state.currentImageId !== null; + return state.selectedImageId !== null; }, get roleAttribute() { return state.overlayOpened ? 'dialog' : null; @@ -60,16 +72,22 @@ const { state, actions, callbacks } = store( get ariaModal() { return state.overlayOpened ? 'true' : null; }, + get ariaLabel() { + return ( + state.selectedImage.customAriaLabel || + getConfig().defaultAriaLabel + ); + }, get enlargedSrc() { return ( - state.currentImage.uploadedSrc || + state.selectedImage.uploadedSrc || 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=' ); }, get figureStyles() { return ( state.overlayOpened && - `${ state.currentImage.figureStyles?.replace( + `${ state.selectedImage.figureStyles?.replace( /margin[^;]*;?/g, '' ) };` @@ -78,31 +96,24 @@ const { state, actions, callbacks } = store( get imgStyles() { return ( state.overlayOpened && - `${ state.currentImage.imgStyles?.replace( + `${ state.selectedImage.imgStyles?.replace( /;$/, '' ) }; object-fit:cover;` ); }, - get imageButtonRight() { - const { imageId } = getContext(); - return state.metadata[ imageId ].imageButtonRight; - }, - get imageButtonTop() { - const { imageId } = getContext(); - return state.metadata[ imageId ].imageButtonTop; - }, get isContentHidden() { const ctx = getContext(); return ( - state.overlayEnabled && state.currentImageId === ctx.imageId + state.overlayEnabled && + state.selectedImageId === ctx.imageId ); }, get isContentVisible() { const ctx = getContext(); return ( ! state.overlayEnabled && - state.currentImageId === ctx.imageId + state.selectedImageId === ctx.imageId ); }, }, @@ -120,32 +131,14 @@ const { state, actions, callbacks } = store( state.scrollTopReset = document.documentElement.scrollTop; state.scrollLeftReset = document.documentElement.scrollLeft; - const { state: galleryState } = store( 'core/gallery' ); - const { lightbox, galleryId } = - getContext( 'core/gallery' ) || {}; - state.images = lightbox - ? galleryState.images[ galleryId ] || [] - : [ imageId ]; - - // Sets the current image index to the one that was clicked. - callbacks.setCurrentImageIndex( imageId ); - - // Sets the current expanded image in the state and enables the overlay. + // Sets the selected image and gallery and enables the overlay. + state.selectedImageId = imageId; + const { galleryId } = getContext( 'core/gallery' ) || {}; + state.selectedGalleryId = galleryId || null; state.overlayEnabled = true; // Computes the styles of the overlay for the animation. callbacks.setOverlayStyles(); - - // make all children of the document inert exempt .wp-lightbox-overlay - inertElements = []; - document - .querySelectorAll( 'body > :not(.wp-lightbox-overlay)' ) - .forEach( ( el ) => { - if ( ! el.hasAttribute( 'inert' ) ) { - el.setAttribute( 'inert', '' ); - inertElements.push( el ); - } - } ); }, hideLightbox() { if ( state.overlayEnabled ) { @@ -163,54 +156,38 @@ const { state, actions, callbacks } = store( // Delays before changing the focus. Otherwise the focus ring will // appear on Firefox before the image has finished animating, which // looks broken. - state.currentImage.buttonRef.focus( { + state.selectedImage.buttonRef.focus( { preventScroll: true, } ); - // Resets the current image index to mark the overlay as closed. - state.currentImageIndex = -1; - state.images = []; + // Resets the selected image and gallery ids. + state.selectedImageId = null; + state.selectedGalleryId = null; }, 450 ); - - // remove inert attribute from all children of the document - inertElements.forEach( ( el ) => { - el.removeAttribute( 'inert' ); - } ); - inertElements = []; } }, showPreviousImage( e ) { - if ( ! state.hasNavigation ) { - return; + if ( state.hasPreviousImage ) { + e.stopPropagation(); + state.selectedImageId = + state.galleryImages[ state.selectedImageIndex - 1 ]; + callbacks.setOverlayStyles(); } - - e.stopPropagation(); - if ( state.currentImageIndex - 1 < 0 ) { - return; - } - state.currentImageIndex = state.currentImageIndex - 1; - callbacks.setOverlayStyles(); }, showNextImage( e ) { - if ( ! state.hasNavigation ) { - return; - } + if ( state.hasNextImage ) { + e.stopPropagation(); - e.stopPropagation(); - if ( state.currentImageIndex + 1 >= state.images.length ) { - return; + state.selectedImageId = + state.galleryImages[ state.selectedImageIndex + 1 ]; + callbacks.setOverlayStyles(); } - state.currentImageIndex = state.currentImageIndex + 1; - callbacks.setOverlayStyles(); }, handleKeydown( event ) { if ( state.overlayEnabled ) { - // Closes the lightbox when the user presses the escape key. if ( event.key === 'Escape' ) { actions.hideLightbox(); - } - - if ( event.key === 'ArrowLeft' ) { + } else if ( event.key === 'ArrowLeft' ) { actions.showPreviousImage( event ); } else if ( event.key === 'ArrowRight' ) { actions.showNextImage( event ); @@ -262,12 +239,6 @@ const { state, actions, callbacks } = store( }, }, callbacks: { - setCurrentImageIndex( imageId ) { - const currentIndex = state.images.findIndex( - ( id ) => id === imageId - ); - state.currentImageIndex = currentIndex; - }, setOverlayStyles() { if ( ! state.overlayEnabled ) { return; @@ -278,9 +249,9 @@ const { state, actions, callbacks } = store( naturalHeight, offsetWidth: originalWidth, offsetHeight: originalHeight, - } = state.currentImage.imageRef; + } = state.selectedImage.imageRef; let { x: screenPosX, y: screenPosY } = - state.currentImage.imageRef.getBoundingClientRect(); + state.selectedImage.imageRef.getBoundingClientRect(); // Natural ratio of the image clicked to open the lightbox. const naturalRatio = naturalWidth / naturalHeight; @@ -289,7 +260,7 @@ const { state, actions, callbacks } = store( // If it has object-fit: contain, recalculates the original sizes // and the screen position without the blank spaces. - if ( state.currentImage.scaleAttr === 'contain' ) { + if ( state.selectedImage.scaleAttr === 'contain' ) { if ( naturalRatio > originalRatio ) { const heightWithoutSpace = originalWidth / naturalRatio; // Recalculates screen position without the top space. @@ -310,15 +281,15 @@ const { state, actions, callbacks } = store( // size), the image's dimensions in the lightbox are the same // as those of the image in the content. let imgMaxWidth = parseFloat( - state.currentImage.targetWidth && - state.currentImage.targetWidth !== 'none' - ? state.currentImage.targetWidth + state.selectedImage.targetWidth && + state.selectedImage.targetWidth !== 'none' + ? state.selectedImage.targetWidth : naturalWidth ); let imgMaxHeight = parseFloat( - state.currentImage.targetHeight && - state.currentImage.targetHeight !== 'none' - ? state.currentImage.targetHeight + state.selectedImage.targetHeight && + state.selectedImage.targetHeight !== 'none' + ? state.selectedImage.targetHeight : naturalHeight ); @@ -436,14 +407,6 @@ const { state, actions, callbacks } = store( }px; `; }, - setScreenReaderText() { - const { ref } = getElement(); - if ( ! state.overlayEnabled ) { - ref.textContent = ''; - } else { - ref.textContent = state.currentImage.screenReaderText; - } - }, setButtonStyles() { const { imageId } = getContext(); const { ref } = getElement(); @@ -492,8 +455,8 @@ const { state, actions, callbacks } = store( const buttonOffsetTop = figureHeight - offsetHeight; const buttonOffsetRight = figureWidth - offsetWidth; - let imageButtonTop = buttonOffsetTop + 16; - let imageButtonRight = buttonOffsetRight + 16; + let buttonTop = buttonOffsetTop + 16; + let buttonRight = buttonOffsetRight + 16; // In the case of an image with object-fit: contain, the size of the // element can be larger than the image itself, so it needs to @@ -508,25 +471,25 @@ const { state, actions, callbacks } = store( // If it reaches the width first, it keeps the width and compute the // height. const referenceHeight = offsetWidth / naturalRatio; - imageButtonTop = + buttonTop = ( offsetHeight - referenceHeight ) / 2 + buttonOffsetTop + 16; - imageButtonRight = buttonOffsetRight + 16; + buttonRight = buttonOffsetRight + 16; } else { // If it reaches the height first, it keeps the height and compute // the width. const referenceWidth = offsetHeight * naturalRatio; - imageButtonTop = buttonOffsetTop + 16; - imageButtonRight = + buttonTop = buttonOffsetTop + 16; + buttonRight = ( offsetWidth - referenceWidth ) / 2 + buttonOffsetRight + 16; } } - state.metadata[ imageId ].imageButtonTop = imageButtonTop; - state.metadata[ imageId ].imageButtonRight = imageButtonRight; + state.metadata[ imageId ].buttonTop = buttonTop; + state.metadata[ imageId ].buttonRight = buttonRight; }, setOverlayFocus() { if ( state.overlayEnabled ) { @@ -535,6 +498,18 @@ const { state, actions, callbacks } = store( ref.focus(); } }, + setInertElements() { + // Makes all children of the document inert exempt .wp-lightbox-overlay. + document + .querySelectorAll( 'body > :not(.wp-lightbox-overlay)' ) + .forEach( ( el ) => { + if ( state.overlayEnabled ) { + el.setAttribute( 'inert', '' ); + } else { + el.removeAttribute( 'inert' ); + } + } ); + }, initTriggerButton() { const { imageId } = getContext(); const { ref } = getElement(); From d229233c4331e49cb448736dce6270e747cd033f Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Fri, 21 Feb 2025 20:52:56 +0900 Subject: [PATCH 16/58] Fix Screen reader double reading --- packages/block-library/src/image/index.php | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/block-library/src/image/index.php b/packages/block-library/src/image/index.php index 45cca7aba44dfc..a9da36c633158f 100644 --- a/packages/block-library/src/image/index.php +++ b/packages/block-library/src/image/index.php @@ -321,7 +321,6 @@ class="wp-lightbox-overlay zoom" - HTML; From e68f3a28fe455ed77fe27c41f36c05add422fbe0 Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Fri, 21 Feb 2025 21:06:55 +0900 Subject: [PATCH 17/58] Don't hide lightbox when hitting Enter on prev/next buttons --- packages/block-library/src/image/view.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/block-library/src/image/view.js b/packages/block-library/src/image/view.js index a7a93ef9929ca4..06bc37ca184eaf 100644 --- a/packages/block-library/src/image/view.js +++ b/packages/block-library/src/image/view.js @@ -167,23 +167,22 @@ const { state, actions, callbacks } = store( }, 450 ); } }, - showPreviousImage( e ) { + showPreviousImage: withSyncEvent( ( event ) => { + event.stopPropagation(); if ( state.hasPreviousImage ) { - e.stopPropagation(); state.selectedImageId = state.galleryImages[ state.selectedImageIndex - 1 ]; callbacks.setOverlayStyles(); } - }, - showNextImage( e ) { + } ), + showNextImage: withSyncEvent( ( event ) => { + event.stopPropagation(); if ( state.hasNextImage ) { - e.stopPropagation(); - state.selectedImageId = state.galleryImages[ state.selectedImageIndex + 1 ]; callbacks.setOverlayStyles(); } - }, + } ), handleKeydown: withSyncEvent( ( event ) => { if ( state.overlayEnabled ) { if ( event.key === 'Escape' ) { From 4e0b31a994d852a83866318d4b01cd142caaed08 Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Fri, 21 Feb 2025 21:08:12 +0900 Subject: [PATCH 18/58] Match visual order with DOM order --- packages/block-library/src/image/index.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/block-library/src/image/index.php b/packages/block-library/src/image/index.php index a9da36c633158f..bc7c2895c261ca 100644 --- a/packages/block-library/src/image/index.php +++ b/packages/block-library/src/image/index.php @@ -308,9 +308,6 @@ class="wp-lightbox-overlay zoom" - + HTML; From 24524c65eb59278306f2dd29bdc7d162a438b8ee Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Fri, 21 Feb 2025 21:10:38 +0900 Subject: [PATCH 19/58] Do not apply opacity to focus styles on disabled prev/next buttons --- packages/block-library/src/image/style.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/image/style.scss b/packages/block-library/src/image/style.scss index 7ea1567da81eb3..e4d3ba544a8f76 100644 --- a/packages/block-library/src/image/style.scss +++ b/packages/block-library/src/image/style.scss @@ -246,7 +246,7 @@ display: none; } - &[aria-disabled="true"] { + &[aria-disabled="true"] svg { opacity: 0.3; } From c7608c31ceb83f6780ec720b4e7b2ec5b384dd4c Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Fri, 21 Feb 2025 21:42:26 +0900 Subject: [PATCH 20/58] Add a tooltip to navigation button --- packages/block-library/src/image/index.php | 20 ++++--- packages/block-library/src/image/style.scss | 62 +++++++++++++++------ 2 files changed, 59 insertions(+), 23 deletions(-) diff --git a/packages/block-library/src/image/index.php b/packages/block-library/src/image/index.php index bc7c2895c261ca..1e5ae84cb921a2 100644 --- a/packages/block-library/src/image/index.php +++ b/packages/block-library/src/image/index.php @@ -302,12 +302,15 @@ class="wp-lightbox-overlay zoom" data-wp-bind--style="state.overlayStyles" tabindex="-1" > - - + - + HTML; diff --git a/packages/block-library/src/image/style.scss b/packages/block-library/src/image/style.scss index e4d3ba544a8f76..b14144b8a1d111 100644 --- a/packages/block-library/src/image/style.scss +++ b/packages/block-library/src/image/style.scss @@ -207,7 +207,7 @@ visibility: hidden; cursor: zoom-out; - .close-button { + .wp-lightbox-close-button { position: absolute; top: calc(env(safe-area-inset-top) + 16px); // equivalent to $grid-unit-20 right: calc(env(safe-area-inset-right) + 16px); // equivalent to $grid-unit-20 @@ -228,8 +228,8 @@ } } - .prev-button, - .next-button { + .wp-lightbox-navigation-container-prev, + .wp-lightbox-navigation-container-next { display: none; position: absolute; top: 50%; @@ -237,37 +237,67 @@ padding: 0; cursor: pointer; z-index: 5000000; - min-width: 40px; // equivalent to $button-size-next-default-40px - min-height: 40px; // equivalent to $button-size-next-default-40px; - align-items: center; - justify-content: center; &[hidden] { display: none; } + @include break-mobile() { + display: block; + } + } + + .wp-lightbox-navigation-button { + min-width: 40px; // equivalent to $button-size-next-default-40px + min-height: 40px; // equivalent to $button-size-next-default-40px; + display: flex; + align-items: center; + justify-content: center; + &[aria-disabled="true"] svg { opacity: 0.3; } &:hover, - &:focus, - &:not(:hover):not(:active):not(.has-background) { + &:focus { background: none; border: none; - } - @include break-mobile() { - display: flex; + + .wp-lightbox-navigation-tooltip { + display: block; + } } } - .prev-button { - left: calc(env(safe-area-inset-left) + 16px); // equivalent to $grid-unit-20 + .wp-lightbox-navigation-tooltip { + display: none; + background: #000; + border-radius: 2px; + color: #fff; + text-align: center; + line-height: 1.4; + font-size: 12px; + padding: 4px 8px; + z-index: 5000000; + margin: 0; + position: absolute; + top: calc(100% + 8px); + } + + .wp-lightbox-navigation-container-prev { + left: calc(env(safe-area-inset-left) + 16px); + + .wp-lightbox-navigation-tooltip { + left: 0; + } } - .next-button { - right: calc(env(safe-area-inset-right) + 16px); // equivalent to $grid-unit-20 + .wp-lightbox-navigation-container-next { + right: calc(env(safe-area-inset-right) + 16px); + + .wp-lightbox-navigation-tooltip { + right: 0; + } } .lightbox-image-container { From 230ab348e4d2ad57d7e1348d5f5d3d8e5b2c75dd Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Sun, 23 Feb 2025 11:21:08 +0900 Subject: [PATCH 21/58] Add `cursor:pointer` to navigation buttons --- packages/block-library/src/image/style.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/block-library/src/image/style.scss b/packages/block-library/src/image/style.scss index b14144b8a1d111..c1be626224a2ae 100644 --- a/packages/block-library/src/image/style.scss +++ b/packages/block-library/src/image/style.scss @@ -251,6 +251,7 @@ min-width: 40px; // equivalent to $button-size-next-default-40px min-height: 40px; // equivalent to $button-size-next-default-40px; display: flex; + cursor: pointer; align-items: center; justify-content: center; From a9aa62b3f9584e43e7e70568cd744a2547e0f184 Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Sun, 23 Feb 2025 11:32:35 +0900 Subject: [PATCH 22/58] Add focus trap --- packages/block-library/src/image/view.js | 26 ++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/block-library/src/image/view.js b/packages/block-library/src/image/view.js index 06bc37ca184eaf..fb2eeedfc3d058 100644 --- a/packages/block-library/src/image/view.js +++ b/packages/block-library/src/image/view.js @@ -25,6 +25,11 @@ let isTouching = false; */ let lastTouchTime = 0; +const focusableSelectors = [ + '.wp-lightbox-close-button', + '.wp-lightbox-navigation-button', +]; + const { state, actions, callbacks } = store( 'core/image', { @@ -191,6 +196,27 @@ const { state, actions, callbacks } = store( actions.showPreviousImage( event ); } else if ( event.key === 'ArrowRight' ) { actions.showNextImage( event ); + } else if ( event.key === 'Tab' ) { + // Traps focus within the overlay. + const focusableElements = Array.from( + document.querySelectorAll( focusableSelectors ) + ); + const firstFocusableElement = focusableElements[ 0 ]; + const lastFocusableElement = + focusableElements[ focusableElements.length - 1 ]; + if ( + event.shiftKey && + event.target === firstFocusableElement + ) { + event.preventDefault(); + lastFocusableElement.focus(); + } else if ( + ! event.shiftKey && + event.target === lastFocusableElement + ) { + event.preventDefault(); + firstFocusableElement.focus(); + } } } } ), From 7ede46abeadcf5d2093c9dd045d25b8f0c36cca7 Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Sun, 23 Feb 2025 12:15:53 +0900 Subject: [PATCH 23/58] Add "Lightbox navigation" option --- docs/reference-guides/core-blocks.md | 2 +- packages/block-library/src/gallery/block.json | 4 + packages/block-library/src/gallery/edit.js | 26 ++++++- packages/block-library/src/gallery/index.php | 76 ++++++++++--------- 4 files changed, 68 insertions(+), 40 deletions(-) diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index 6a8f3255297c46..f3b647e14c11cf 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -342,7 +342,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, caption, columns, fixedHeight, ids, imageCrop, images, linkTarget, linkTo, randomOrder, shortCodeTransforms, sizeSlug +- **Attributes:** allowResize, caption, columns, fixedHeight, ids, imageCrop, images, lightBoxNavigation, linkTarget, linkTo, randomOrder, shortCodeTransforms, sizeSlug ## Group diff --git a/packages/block-library/src/gallery/block.json b/packages/block-library/src/gallery/block.json index 874adae1ca3150..f411b61c706cf3 100644 --- a/packages/block-library/src/gallery/block.json +++ b/packages/block-library/src/gallery/block.json @@ -103,6 +103,10 @@ "allowResize": { "type": "boolean", "default": false + }, + "lightBoxNavigation": { + "type": "boolean", + "default": true } }, "providesContext": { diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js index 1936c02c468189..e253947dfb7946 100644 --- a/packages/block-library/src/gallery/edit.js +++ b/packages/block-library/src/gallery/edit.js @@ -125,8 +125,15 @@ export default function GalleryEdit( props ) { ) : LINK_OPTIONS; - const { columns, imageCrop, randomOrder, linkTarget, linkTo, sizeSlug } = - attributes; + const { + columns, + imageCrop, + randomOrder, + linkTarget, + linkTo, + sizeSlug, + lightBoxNavigation, + } = attributes; const { __unstableMarkNextChangeAsNotPersistent, @@ -442,6 +449,12 @@ export default function GalleryEdit( props ) { } ); } + function toggleLightboxNavigation() { + setAttributes( { + lightBoxNavigation: ! attributes.lightBoxNavigation, + } ); + } + function updateImagesSize( newSizeSlug ) { setAttributes( { sizeSlug: newSizeSlug } ); const changedAttributes = {}; @@ -623,6 +636,15 @@ export default function GalleryEdit( props ) { onChange={ toggleOpenInNewTab } /> ) } + { Platform.isWeb && ! imageSizeOptions && hasImageIds && ( '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; + if ( $attributes['lightBoxNavigation']) { + // 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; + } } } - } - - $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 ), + $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 ), + ), ), - ), - ) - ); + ) + ); + } } } } From deeaf4030073df6002499e6d1c28ce0f0090bc96 Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Sun, 23 Feb 2025 12:22:27 +0900 Subject: [PATCH 24/58] Restore aria-live/aria-atomic --- packages/block-library/src/image/index.php | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/block-library/src/image/index.php b/packages/block-library/src/image/index.php index 1e5ae84cb921a2..2c8b4ef7012c92 100644 --- a/packages/block-library/src/image/index.php +++ b/packages/block-library/src/image/index.php @@ -327,6 +327,7 @@ class="wp-lightbox-overlay zoom" +
HTML; From 4f1cde2e25ff348f12e4187fa6efe14e728a6f52 Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Sun, 23 Feb 2025 12:37:25 +0900 Subject: [PATCH 25/58] Ajust z-index values --- packages/block-library/src/image/style.scss | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/block-library/src/image/style.scss b/packages/block-library/src/image/style.scss index c1be626224a2ae..eb0d67a1113f55 100644 --- a/packages/block-library/src/image/style.scss +++ b/packages/block-library/src/image/style.scss @@ -236,7 +236,7 @@ transform: translateY(-50%); padding: 0; cursor: pointer; - z-index: 5000000; + z-index: 2000002; &[hidden] { display: none; @@ -279,7 +279,6 @@ line-height: 1.4; font-size: 12px; padding: 4px 8px; - z-index: 5000000; margin: 0; position: absolute; top: calc(100% + 8px); @@ -310,7 +309,7 @@ transform: translate(-50%, -50%); width: var(--wp--lightbox-container-width); height: var(--wp--lightbox-container-height); - z-index: 9999999999; + z-index: 2000001; } .wp-block-image { From 1da03379debdaaa9b84dc345f78a01e19a02845b Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Sun, 23 Feb 2025 13:09:22 +0900 Subject: [PATCH 26/58] Regenerate fixtures --- .../fixtures/blocks/core__gallery-with-caption.json | 1 + test/integration/fixtures/blocks/core__gallery.json | 1 + test/integration/fixtures/blocks/core__gallery__columns.json | 3 ++- .../fixtures/blocks/core__gallery__deprecated-7.json | 3 ++- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/test/integration/fixtures/blocks/core__gallery-with-caption.json b/test/integration/fixtures/blocks/core__gallery-with-caption.json index 1106c8ccb64b2a..041475047de51d 100644 --- a/test/integration/fixtures/blocks/core__gallery-with-caption.json +++ b/test/integration/fixtures/blocks/core__gallery-with-caption.json @@ -13,6 +13,7 @@ "linkTo": "none", "sizeSlug": "large", "allowResize": false, + "lightBoxNavigation": true, "className": "columns-2" }, "innerBlocks": [ diff --git a/test/integration/fixtures/blocks/core__gallery.json b/test/integration/fixtures/blocks/core__gallery.json index 27fc4c75dc8dc0..53765394f5ce8e 100644 --- a/test/integration/fixtures/blocks/core__gallery.json +++ b/test/integration/fixtures/blocks/core__gallery.json @@ -13,6 +13,7 @@ "linkTo": "none", "sizeSlug": "large", "allowResize": false, + "lightBoxNavigation": true, "className": "columns-2" }, "innerBlocks": [ diff --git a/test/integration/fixtures/blocks/core__gallery__columns.json b/test/integration/fixtures/blocks/core__gallery__columns.json index beb97c3ac50802..6f08ccad7a355b 100644 --- a/test/integration/fixtures/blocks/core__gallery__columns.json +++ b/test/integration/fixtures/blocks/core__gallery__columns.json @@ -13,7 +13,8 @@ "fixedHeight": true, "linkTo": "none", "sizeSlug": "large", - "allowResize": false + "allowResize": false, + "lightBoxNavigation": true }, "innerBlocks": [ { diff --git a/test/integration/fixtures/blocks/core__gallery__deprecated-7.json b/test/integration/fixtures/blocks/core__gallery__deprecated-7.json index 05a630370f9b09..82b4dbf74976dc 100644 --- a/test/integration/fixtures/blocks/core__gallery__deprecated-7.json +++ b/test/integration/fixtures/blocks/core__gallery__deprecated-7.json @@ -12,7 +12,8 @@ "fixedHeight": true, "linkTo": "media", "sizeSlug": "large", - "allowResize": false + "allowResize": false, + "lightBoxNavigation": true }, "innerBlocks": [ { From 0ed7d4c244fd278e75a260a4258344f0172954dc Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Sun, 23 Feb 2025 13:28:21 +0900 Subject: [PATCH 27/58] Fix PHP lint error --- packages/block-library/src/gallery/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/gallery/index.php b/packages/block-library/src/gallery/index.php index 37b617fca4c0d6..fa4d61b7df7687 100644 --- a/packages/block-library/src/gallery/index.php +++ b/packages/block-library/src/gallery/index.php @@ -138,7 +138,7 @@ function block_core_gallery_render( $attributes, $content, $block ) { array( 'context' => 'block-supports' ) ); - if ( $attributes['lightBoxNavigation']) { + if ( $attributes['lightBoxNavigation'] ) { // 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; From 206bae2c2511eb88f82b80af4c57d67e3d1110ae Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Mon, 25 Aug 2025 22:51:47 +0900 Subject: [PATCH 28/58] Show toggle only when lightbox.allowEditing is enabled --- packages/block-library/src/gallery/edit.js | 56 ++++++++++++---------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js index 022f0478d0971e..0d2013d35cbf99 100644 --- a/packages/block-library/src/gallery/edit.js +++ b/packages/block-library/src/gallery/edit.js @@ -706,24 +706,26 @@ export default function GalleryEdit( props ) { /> ) } - !! linkTarget } - onDeselect={ () => - toggleLightboxNavigation( true ) - } - > - - + hasValue={ () => !! linkTarget } + onDeselect={ () => + toggleLightboxNavigation( true ) + } + > + + + ) } ) } { Platform.isNative && ( @@ -788,15 +790,17 @@ export default function GalleryEdit( props ) { onChange={ toggleOpenInNewTab } /> ) } - + { lightboxSetting?.allowEditing && ( + + ) } ) } From c005efb42d532b8689071c9fd902faddf5bf0dcf Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Mon, 25 Aug 2025 23:03:20 +0900 Subject: [PATCH 29/58] Fix: add data-wp-watch--inert --- packages/block-library/src/image/index.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/block-library/src/image/index.php b/packages/block-library/src/image/index.php index d80101bcf7fe40..488f52087551a5 100644 --- a/packages/block-library/src/image/index.php +++ b/packages/block-library/src/image/index.php @@ -293,7 +293,8 @@ class="wp-lightbox-overlay zoom" data-wp-bind--aria-modal="state.ariaModal" data-wp-class--active="state.overlayEnabled" data-wp-class--show-closing-animation="state.overlayOpened" - data-wp-watch="callbacks.setOverlayFocus" + data-wp-watch--focus="callbacks.setOverlayFocus" + data-wp-watch--inert="callbacks.setInertElements" data-wp-on--keydown="actions.handleKeydown" data-wp-on-async--touchstart="actions.handleTouchStart" data-wp-on--touchmove="actions.handleTouchMove" From 1a6f7debee8e771777d04a12e0384f76b308cbc7 Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Mon, 25 Aug 2025 23:16:21 +0900 Subject: [PATCH 30/58] Remove tooltip from previous and next buttons --- packages/block-library/src/image/index.php | 6 ++--- packages/block-library/src/image/style.scss | 26 --------------------- 2 files changed, 2 insertions(+), 30 deletions(-) diff --git a/packages/block-library/src/image/index.php b/packages/block-library/src/image/index.php index 488f52087551a5..8d88e34767a522 100644 --- a/packages/block-library/src/image/index.php +++ b/packages/block-library/src/image/index.php @@ -309,10 +309,9 @@ class="wp-lightbox-overlay zoom"
diff --git a/packages/block-library/src/image/style.scss b/packages/block-library/src/image/style.scss index eb0d67a1113f55..b7fb6a17d7c32c 100644 --- a/packages/block-library/src/image/style.scss +++ b/packages/block-library/src/image/style.scss @@ -263,41 +263,15 @@ &:focus { background: none; border: none; - - + .wp-lightbox-navigation-tooltip { - display: block; - } } } - .wp-lightbox-navigation-tooltip { - display: none; - background: #000; - border-radius: 2px; - color: #fff; - text-align: center; - line-height: 1.4; - font-size: 12px; - padding: 4px 8px; - margin: 0; - position: absolute; - top: calc(100% + 8px); - } - .wp-lightbox-navigation-container-prev { left: calc(env(safe-area-inset-left) + 16px); - - .wp-lightbox-navigation-tooltip { - left: 0; - } } .wp-lightbox-navigation-container-next { right: calc(env(safe-area-inset-right) + 16px); - - .wp-lightbox-navigation-tooltip { - right: 0; - } } .lightbox-image-container { From fa14a03baf71a3803627a949b21c02102ed5ec42 Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Mon, 25 Aug 2025 23:25:22 +0900 Subject: [PATCH 31/58] Enable Lightbox by default --- docs/reference-guides/core-blocks.md | 2 +- packages/block-library/src/gallery/block.json | 4 - packages/block-library/src/gallery/edit.js | 49 +----------- packages/block-library/src/gallery/index.php | 76 +++++++++---------- .../blocks/core__gallery-with-caption.json | 1 - .../fixtures/blocks/core__gallery.json | 1 - .../blocks/core__gallery__columns.json | 3 +- .../blocks/core__gallery__deprecated-7.json | 3 +- 8 files changed, 42 insertions(+), 97 deletions(-) diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index 2c28f4147d1100..e55430688e4431 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -387,7 +387,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, caption, columns, fixedHeight, ids, imageCrop, images, lightBoxNavigation, linkTarget, linkTo, randomOrder, shortCodeTransforms, sizeSlug +- **Attributes:** allowResize, caption, columns, fixedHeight, ids, imageCrop, images, linkTarget, linkTo, randomOrder, shortCodeTransforms, sizeSlug ## Group diff --git a/packages/block-library/src/gallery/block.json b/packages/block-library/src/gallery/block.json index f411b61c706cf3..874adae1ca3150 100644 --- a/packages/block-library/src/gallery/block.json +++ b/packages/block-library/src/gallery/block.json @@ -103,10 +103,6 @@ "allowResize": { "type": "boolean", "default": false - }, - "lightBoxNavigation": { - "type": "boolean", - "default": true } }, "providesContext": { diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js index 0d2013d35cbf99..ec605185675c96 100644 --- a/packages/block-library/src/gallery/edit.js +++ b/packages/block-library/src/gallery/edit.js @@ -127,15 +127,8 @@ export default function GalleryEdit( props ) { ) : LINK_OPTIONS; - const { - columns, - imageCrop, - randomOrder, - linkTarget, - linkTo, - sizeSlug, - lightBoxNavigation, - } = attributes; + const { columns, imageCrop, randomOrder, linkTarget, linkTo, sizeSlug } = + attributes; const { __unstableMarkNextChangeAsNotPersistent, @@ -455,12 +448,6 @@ export default function GalleryEdit( props ) { } ); } - function toggleLightboxNavigation() { - setAttributes( { - lightBoxNavigation: ! attributes.lightBoxNavigation, - } ); - } - function updateImagesSize( newSizeSlug ) { setAttributes( { sizeSlug: newSizeSlug } ); const changedAttributes = {}; @@ -591,7 +578,6 @@ export default function GalleryEdit( props ) { columns: undefined, imageCrop: true, randomOrder: false, - lightBoxNavigation: true, } ); if ( sizeSlug !== DEFAULT_MEDIA_SIZE_SLUG ) { @@ -706,26 +692,6 @@ export default function GalleryEdit( props ) { /> ) } - { lightboxSetting?.allowEditing && ( - !! linkTarget } - onDeselect={ () => - toggleLightboxNavigation( true ) - } - > - - - ) } ) } { Platform.isNative && ( @@ -790,17 +756,6 @@ export default function GalleryEdit( props ) { onChange={ toggleOpenInNewTab } /> ) } - { lightboxSetting?.allowEditing && ( - - ) } ) } diff --git a/packages/block-library/src/gallery/index.php b/packages/block-library/src/gallery/index.php index fa4d61b7df7687..0a014b2da1f07f 100644 --- a/packages/block-library/src/gallery/index.php +++ b/packages/block-library/src/gallery/index.php @@ -138,49 +138,47 @@ function block_core_gallery_render( $attributes, $content, $block ) { array( 'context' => 'block-supports' ) ); - if ( $attributes['lightBoxNavigation'] ) { - // 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; - } + // 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; } } + } + + $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 + ) + ); - $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 ), - ), + // 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 ), ), - ) - ); - } + ), + ) + ); } } } diff --git a/test/integration/fixtures/blocks/core__gallery-with-caption.json b/test/integration/fixtures/blocks/core__gallery-with-caption.json index 041475047de51d..1106c8ccb64b2a 100644 --- a/test/integration/fixtures/blocks/core__gallery-with-caption.json +++ b/test/integration/fixtures/blocks/core__gallery-with-caption.json @@ -13,7 +13,6 @@ "linkTo": "none", "sizeSlug": "large", "allowResize": false, - "lightBoxNavigation": true, "className": "columns-2" }, "innerBlocks": [ diff --git a/test/integration/fixtures/blocks/core__gallery.json b/test/integration/fixtures/blocks/core__gallery.json index 53765394f5ce8e..27fc4c75dc8dc0 100644 --- a/test/integration/fixtures/blocks/core__gallery.json +++ b/test/integration/fixtures/blocks/core__gallery.json @@ -13,7 +13,6 @@ "linkTo": "none", "sizeSlug": "large", "allowResize": false, - "lightBoxNavigation": true, "className": "columns-2" }, "innerBlocks": [ diff --git a/test/integration/fixtures/blocks/core__gallery__columns.json b/test/integration/fixtures/blocks/core__gallery__columns.json index 6f08ccad7a355b..beb97c3ac50802 100644 --- a/test/integration/fixtures/blocks/core__gallery__columns.json +++ b/test/integration/fixtures/blocks/core__gallery__columns.json @@ -13,8 +13,7 @@ "fixedHeight": true, "linkTo": "none", "sizeSlug": "large", - "allowResize": false, - "lightBoxNavigation": true + "allowResize": false }, "innerBlocks": [ { diff --git a/test/integration/fixtures/blocks/core__gallery__deprecated-7.json b/test/integration/fixtures/blocks/core__gallery__deprecated-7.json index 82b4dbf74976dc..05a630370f9b09 100644 --- a/test/integration/fixtures/blocks/core__gallery__deprecated-7.json +++ b/test/integration/fixtures/blocks/core__gallery__deprecated-7.json @@ -12,8 +12,7 @@ "fixedHeight": true, "linkTo": "media", "sizeSlug": "large", - "allowResize": false, - "lightBoxNavigation": true + "allowResize": false }, "innerBlocks": [ { From 1997e518715a5ac8ce1a5558480ce3c432cf0413 Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Tue, 26 Aug 2025 16:34:26 +0900 Subject: [PATCH 32/58] Add some reset CSS to the navigation buttons so they aren't affected by theme styles --- packages/block-library/src/image/style.scss | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/block-library/src/image/style.scss b/packages/block-library/src/image/style.scss index b7fb6a17d7c32c..430e06cd0bdb92 100644 --- a/packages/block-library/src/image/style.scss +++ b/packages/block-library/src/image/style.scss @@ -260,9 +260,11 @@ } &:hover, - &:focus { + &:focus, + &:not(:hover):not(:active):not(.has-background) { background: none; border: none; + padding: 0; } } From dac54a3fbec1ba6c6329779ee1dcc25b46213640 Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Tue, 26 Aug 2025 16:34:56 +0900 Subject: [PATCH 33/58] Fix since tag --- packages/block-library/src/gallery/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/gallery/index.php b/packages/block-library/src/gallery/index.php index 0a014b2da1f07f..5579126d5d4759 100644 --- a/packages/block-library/src/gallery/index.php +++ b/packages/block-library/src/gallery/index.php @@ -37,7 +37,7 @@ function block_core_gallery_data_id_backcompatibility( $parsed_block ) { /** * Adds a unique ID to the gallery block context. * - * @since 6.8.0 + * @since 6.9.0 * * @param array $context Default context. * @param array $parsed_block Block being rendered, filtered by render_block_data. From f8495974dfdf72b0f535c41b89beb08ab790ee1b Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Thu, 4 Sep 2025 19:17:34 +0900 Subject: [PATCH 34/58] Add control to show visible text fo three buttons --- docs/reference-guides/core-blocks.md | 2 +- packages/block-library/src/gallery/block.json | 7 +++- packages/block-library/src/gallery/edit.js | 32 +++++++++++++++++-- packages/block-library/src/image/block.json | 1 + packages/block-library/src/image/index.php | 27 ++++++++++------ packages/block-library/src/image/style.scss | 4 +-- 6 files changed, 58 insertions(+), 15 deletions(-) diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index e55430688e4431..105ab01d2cf615 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -387,7 +387,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, caption, columns, fixedHeight, ids, imageCrop, images, linkTarget, linkTo, randomOrder, shortCodeTransforms, sizeSlug +- **Attributes:** allowResize, caption, columns, fixedHeight, hasIcon, ids, imageCrop, images, linkTarget, linkTo, randomOrder, shortCodeTransforms, sizeSlug ## Group diff --git a/packages/block-library/src/gallery/block.json b/packages/block-library/src/gallery/block.json index 874adae1ca3150..a3db188c27879b 100644 --- a/packages/block-library/src/gallery/block.json +++ b/packages/block-library/src/gallery/block.json @@ -61,6 +61,10 @@ }, "default": [] }, + "hasIcon": { + "type": "boolean", + "default": true + }, "shortCodeTransforms": { "type": "array", "items": { @@ -108,7 +112,8 @@ "providesContext": { "allowResize": "allowResize", "imageCrop": "imageCrop", - "fixedHeight": "fixedHeight" + "fixedHeight": "fixedHeight", + "hasIcon": "hasIcon" }, "supports": { "anchor": true, diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js index ec605185675c96..c425024a007e9a 100644 --- a/packages/block-library/src/gallery/edit.js +++ b/packages/block-library/src/gallery/edit.js @@ -127,8 +127,15 @@ export default function GalleryEdit( props ) { ) : LINK_OPTIONS; - const { columns, imageCrop, randomOrder, linkTarget, linkTo, sizeSlug } = - attributes; + const { + hasIcon, + columns, + imageCrop, + randomOrder, + linkTarget, + linkTo, + sizeSlug, + } = attributes; const { __unstableMarkNextChangeAsNotPersistent, @@ -575,6 +582,7 @@ export default function GalleryEdit( props ) { label={ __( 'Settings' ) } resetAll={ () => { setAttributes( { + hasIcon: true, columns: undefined, imageCrop: true, randomOrder: false, @@ -692,6 +700,26 @@ export default function GalleryEdit( props ) { /> ) } + ! hasIcon } + onDeselect={ () => + setAttributes( { hasIcon: true } ) + } + > + + setAttributes( { hasIcon: value } ) + } + checked={ hasIcon } + /> + ) } { Platform.isNative && ( diff --git a/packages/block-library/src/image/block.json b/packages/block-library/src/image/block.json index b5d12b048f0112..16114d38140451 100644 --- a/packages/block-library/src/image/block.json +++ b/packages/block-library/src/image/block.json @@ -8,6 +8,7 @@ "allowResize", "imageCrop", "fixedHeight", + "hasIcon", "postId", "postType", "queryId", diff --git a/packages/block-library/src/image/index.php b/packages/block-library/src/image/index.php index 8d88e34767a522..530a70a05cdc6a 100644 --- a/packages/block-library/src/image/index.php +++ b/packages/block-library/src/image/index.php @@ -251,7 +251,12 @@ class="lightbox-trigger" $body_content = preg_replace( '/]+>/', $button, $body_content ); - add_action( 'wp_footer', 'block_core_image_print_lightbox_overlay' ); + $block_context = $block_instance->context; + + $overlay_callback = function () use ( $block_context ) { + block_core_image_print_lightbox_overlay( $block_context ); + }; + add_action( 'wp_footer', $overlay_callback ); return $body_content; } @@ -259,11 +264,15 @@ class="lightbox-trigger" /** * @since 6.5.0 */ -function block_core_image_print_lightbox_overlay() { - $close_button_label = esc_attr__( 'Close' ); - $dialog_label = esc_attr__( 'Enlarged images' ); - $prev_button_label = esc_attr__( 'Previous' ); - $next_button_label = esc_attr__( 'Next' ); +function block_core_image_print_lightbox_overlay( $block_context ) { + $dialog_label = esc_attr__( 'Enlarged images' ); + $has_icon = $block_context['hasIcon'] ?? true; + $close_button_label = $has_icon ? esc_attr__( 'Close' ) : ''; + $close_button_content = $has_icon ? '' : esc_attr__( 'Close' ); + $prev_button_label = $has_icon ? esc_attr__( 'Previous' ) : ''; + $prev_button_content = $has_icon ? '' : esc_attr__( 'Previous' ); + $next_button_label = $has_icon ? esc_attr__( 'Next' ) : ''; + $next_button_content = $has_icon ? '' : esc_attr__( 'Next' ); // If the current theme does NOT have a `theme.json`, or the colors are not // defined, it needs to set the background color & close button color to some @@ -306,11 +315,11 @@ class="wp-lightbox-overlay zoom" tabindex="-1" >
diff --git a/packages/block-library/src/image/style.scss b/packages/block-library/src/image/style.scss index 430e06cd0bdb92..0a636987b23c7b 100644 --- a/packages/block-library/src/image/style.scss +++ b/packages/block-library/src/image/style.scss @@ -234,7 +234,7 @@ position: absolute; top: 50%; transform: translateY(-50%); - padding: 0; + padding: 0 8px; cursor: pointer; z-index: 2000002; @@ -264,7 +264,7 @@ &:not(:hover):not(:active):not(.has-background) { background: none; border: none; - padding: 0; + padding: 0 8px; } } From a082fbf7229b1bb60c3c36adc73b01b93e8fc07b Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Thu, 4 Sep 2025 19:54:37 +0900 Subject: [PATCH 35/58] Enhance text of Enlarge button --- packages/block-library/src/gallery/index.php | 4 +++- packages/block-library/src/image/index.php | 25 ++++++++++---------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/packages/block-library/src/gallery/index.php b/packages/block-library/src/gallery/index.php index 5579126d5d4759..b8b42d2a677a58 100644 --- a/packages/block-library/src/gallery/index.php +++ b/packages/block-library/src/gallery/index.php @@ -170,11 +170,13 @@ function block_core_gallery_render( $attributes, $content, $block ) { array( 'metadata' => array( $image_id => array( - 'customAriaLabel' => empty( $alt ) + '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 ) ), ), ), ) diff --git a/packages/block-library/src/image/index.php b/packages/block-library/src/image/index.php index 530a70a05cdc6a..e3b18a2d969d3d 100644 --- a/packages/block-library/src/image/index.php +++ b/packages/block-library/src/image/index.php @@ -187,17 +187,18 @@ function block_core_image_render_lightbox( $block_content, $block, $block_instan array( 'metadata' => array( $unique_image_id => array( - 'uploadedSrc' => $img_uploaded_src, - '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, + 'uploadedSrc' => $img_uploaded_src, + '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, + 'triggerButtonAriaLabel' => null, ), ), ) @@ -238,7 +239,7 @@ function block_core_image_render_lightbox( $block_content, $block, $block_instan class="lightbox-trigger" type="button" aria-haspopup="dialog" - aria-label="' . esc_attr( __( 'Enlarge' ) ) . '" + data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel" data-wp-init="callbacks.initTriggerButton" data-wp-on-async--click="actions.showLightbox" data-wp-style--right="state.thisImage.buttonRight" From f96f5eaf2d2588aea2b08f3e61bccdfb80e4d8f6 Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Thu, 4 Sep 2025 19:59:46 +0900 Subject: [PATCH 36/58] Regenerate fixtures --- test/integration/fixtures/blocks/core__gallery-with-caption.json | 1 + test/integration/fixtures/blocks/core__gallery.json | 1 + test/integration/fixtures/blocks/core__gallery__columns.json | 1 + .../integration/fixtures/blocks/core__gallery__deprecated-7.json | 1 + 4 files changed, 4 insertions(+) diff --git a/test/integration/fixtures/blocks/core__gallery-with-caption.json b/test/integration/fixtures/blocks/core__gallery-with-caption.json index 1106c8ccb64b2a..51458d65dffc74 100644 --- a/test/integration/fixtures/blocks/core__gallery-with-caption.json +++ b/test/integration/fixtures/blocks/core__gallery-with-caption.json @@ -5,6 +5,7 @@ "attributes": { "images": [], "ids": [], + "hasIcon": true, "shortCodeTransforms": [], "caption": "Gallery Caption", "imageCrop": true, diff --git a/test/integration/fixtures/blocks/core__gallery.json b/test/integration/fixtures/blocks/core__gallery.json index 27fc4c75dc8dc0..cceaab68e9dca7 100644 --- a/test/integration/fixtures/blocks/core__gallery.json +++ b/test/integration/fixtures/blocks/core__gallery.json @@ -5,6 +5,7 @@ "attributes": { "images": [], "ids": [], + "hasIcon": true, "shortCodeTransforms": [], "caption": "", "imageCrop": true, diff --git a/test/integration/fixtures/blocks/core__gallery__columns.json b/test/integration/fixtures/blocks/core__gallery__columns.json index beb97c3ac50802..3dc0525fe7349c 100644 --- a/test/integration/fixtures/blocks/core__gallery__columns.json +++ b/test/integration/fixtures/blocks/core__gallery__columns.json @@ -5,6 +5,7 @@ "attributes": { "images": [], "ids": [], + "hasIcon": true, "shortCodeTransforms": [], "columns": 1, "caption": "", diff --git a/test/integration/fixtures/blocks/core__gallery__deprecated-7.json b/test/integration/fixtures/blocks/core__gallery__deprecated-7.json index 05a630370f9b09..f888d6ded424a8 100644 --- a/test/integration/fixtures/blocks/core__gallery__deprecated-7.json +++ b/test/integration/fixtures/blocks/core__gallery__deprecated-7.json @@ -5,6 +5,7 @@ "attributes": { "images": [], "ids": [], + "hasIcon": true, "shortCodeTransforms": [], "caption": "", "imageCrop": true, From 38b5ab6a02e5329cf33cdf9569da51c9afe06fad Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Fri, 5 Sep 2025 10:11:39 +0900 Subject: [PATCH 37/58] Make navigation button appearance more configurable --- docs/reference-guides/core-blocks.md | 2 +- packages/block-library/src/gallery/block.json | 9 ++-- packages/block-library/src/gallery/edit.js | 44 ++++++++++++++----- packages/block-library/src/image/block.json | 2 +- packages/block-library/src/image/index.php | 38 ++++++++++++---- packages/block-library/src/image/style.scss | 1 + 6 files changed, 70 insertions(+), 26 deletions(-) diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index 105ab01d2cf615..fefaac913abe1e 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -387,7 +387,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, caption, columns, fixedHeight, hasIcon, ids, imageCrop, images, linkTarget, linkTo, randomOrder, shortCodeTransforms, sizeSlug +- **Attributes:** allowResize, 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 a3db188c27879b..0c1d741113a07e 100644 --- a/packages/block-library/src/gallery/block.json +++ b/packages/block-library/src/gallery/block.json @@ -61,9 +61,10 @@ }, "default": [] }, - "hasIcon": { - "type": "boolean", - "default": true + "navigationButtonType": { + "type": "string", + "default": "icon", + "enum": [ "icon", "text", "icon-and-text" ] }, "shortCodeTransforms": { "type": "array", @@ -113,7 +114,7 @@ "allowResize": "allowResize", "imageCrop": "imageCrop", "fixedHeight": "fixedHeight", - "hasIcon": "hasIcon" + "navigationButtonType": "navigationButtonType" }, "supports": { "anchor": true, diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js index c425024a007e9a..fa5aa497b2fb8d 100644 --- a/packages/block-library/src/gallery/edit.js +++ b/packages/block-library/src/gallery/edit.js @@ -94,6 +94,20 @@ const LINK_OPTIONS = [ noticeText: __( 'None' ), }, ]; +const NAVIGATION_BUTTON_TYPE_OPTIONS = [ + { + label: __( 'Icon only' ), + value: 'icon', + }, + { + label: __( 'Text only' ), + value: 'text', + }, + { + label: __( 'Icon and Text' ), + value: 'icon-and-text', + }, +]; const ALLOWED_MEDIA_TYPES = [ 'image' ]; const PLACEHOLDER_TEXT = Platform.isNative @@ -128,7 +142,7 @@ export default function GalleryEdit( props ) { : LINK_OPTIONS; const { - hasIcon, + navigationButtonType, columns, imageCrop, randomOrder, @@ -582,7 +596,7 @@ export default function GalleryEdit( props ) { label={ __( 'Settings' ) } resetAll={ () => { setAttributes( { - hasIcon: true, + navigationButtonType: 'icon', columns: undefined, imageCrop: true, randomOrder: false, @@ -701,23 +715,29 @@ export default function GalleryEdit( props ) { ) } ! hasIcon } + hasValue={ () => navigationButtonType !== 'icon' } onDeselect={ () => - setAttributes( { hasIcon: true } ) + setAttributes( { + navigationButtonType: 'icon', + } ) } > - - setAttributes( { hasIcon: value } ) + setAttributes( { + navigationButtonType: value, + } ) } - checked={ hasIcon } + options={ NAVIGATION_BUTTON_TYPE_OPTIONS } + size="__unstable-large" + help={ __( + 'Configure the visual appearance of the buttons in the lightbox.' + ) } /> diff --git a/packages/block-library/src/image/block.json b/packages/block-library/src/image/block.json index 16114d38140451..1c2678a9c97d27 100644 --- a/packages/block-library/src/image/block.json +++ b/packages/block-library/src/image/block.json @@ -8,7 +8,7 @@ "allowResize", "imageCrop", "fixedHeight", - "hasIcon", + "navigationButtonType", "postId", "postType", "queryId", diff --git a/packages/block-library/src/image/index.php b/packages/block-library/src/image/index.php index e3b18a2d969d3d..67df9d0492e731 100644 --- a/packages/block-library/src/image/index.php +++ b/packages/block-library/src/image/index.php @@ -266,14 +266,36 @@ class="lightbox-trigger" * @since 6.5.0 */ function block_core_image_print_lightbox_overlay( $block_context ) { - $dialog_label = esc_attr__( 'Enlarged images' ); - $has_icon = $block_context['hasIcon'] ?? true; - $close_button_label = $has_icon ? esc_attr__( 'Close' ) : ''; - $close_button_content = $has_icon ? '' : esc_attr__( 'Close' ); - $prev_button_label = $has_icon ? esc_attr__( 'Previous' ) : ''; - $prev_button_content = $has_icon ? '' : esc_attr__( 'Previous' ); - $next_button_label = $has_icon ? esc_attr__( 'Next' ) : ''; - $next_button_content = $has_icon ? '' : esc_attr__( 'Next' ); + $dialog_label = esc_attr__( 'Enlarged images' ); + $navigation_button_type = $block_context['navigationButtonType'] ?? 'icon'; + $close_button_text = esc_attr__( 'Close' ); + $prev_button_text = esc_attr__( 'Previous' ); + $next_button_text = esc_attr__( 'Next' ); + $close_button_icon = ''; + $prev_button_icon = ''; + $next_button_icon = ''; + + switch ( $navigation_button_type ) { + case 'text': + $close_button_content = $close_button_text; + $prev_button_content = $prev_button_text; + $next_button_content = $next_button_text; + break; + case 'icon-and-text': + $close_button_content = $close_button_icon . $close_button_text; + $prev_button_content = $prev_button_icon . $prev_button_text; + $next_button_content = $next_button_text . $next_button_icon; + break; + default: + $close_button_content = $close_button_icon; + $prev_button_content = $prev_button_icon; + $next_button_content = $next_button_icon; + break; + } + + $close_button_label = 'icon' === $navigation_button_type ? $close_button_text : ''; + $prev_button_label = 'icon' === $navigation_button_type ? $prev_button_text : ''; + $next_button_label = 'icon' === $navigation_button_type ? $next_button_text : ''; // If the current theme does NOT have a `theme.json`, or the colors are not // defined, it needs to set the background color & close button color to some diff --git a/packages/block-library/src/image/style.scss b/packages/block-library/src/image/style.scss index 0a636987b23c7b..063af7973c6ce4 100644 --- a/packages/block-library/src/image/style.scss +++ b/packages/block-library/src/image/style.scss @@ -250,6 +250,7 @@ .wp-lightbox-navigation-button { min-width: 40px; // equivalent to $button-size-next-default-40px min-height: 40px; // equivalent to $button-size-next-default-40px; + gap: 8px; display: flex; cursor: pointer; align-items: center; From 3d29bb4e2d131bedc0e8c068235ebf0a884ce811 Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Fri, 5 Sep 2025 10:43:35 +0900 Subject: [PATCH 38/58] Update fixtures --- .../integration/fixtures/blocks/core__gallery-with-caption.json | 2 +- test/integration/fixtures/blocks/core__gallery.json | 2 +- test/integration/fixtures/blocks/core__gallery__columns.json | 2 +- .../fixtures/blocks/core__gallery__deprecated-7.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/integration/fixtures/blocks/core__gallery-with-caption.json b/test/integration/fixtures/blocks/core__gallery-with-caption.json index 51458d65dffc74..540a12ed96a509 100644 --- a/test/integration/fixtures/blocks/core__gallery-with-caption.json +++ b/test/integration/fixtures/blocks/core__gallery-with-caption.json @@ -5,7 +5,7 @@ "attributes": { "images": [], "ids": [], - "hasIcon": true, + "navigationButtonType": "icon", "shortCodeTransforms": [], "caption": "Gallery Caption", "imageCrop": true, diff --git a/test/integration/fixtures/blocks/core__gallery.json b/test/integration/fixtures/blocks/core__gallery.json index cceaab68e9dca7..59fcced5158755 100644 --- a/test/integration/fixtures/blocks/core__gallery.json +++ b/test/integration/fixtures/blocks/core__gallery.json @@ -5,7 +5,7 @@ "attributes": { "images": [], "ids": [], - "hasIcon": true, + "navigationButtonType": "icon", "shortCodeTransforms": [], "caption": "", "imageCrop": true, diff --git a/test/integration/fixtures/blocks/core__gallery__columns.json b/test/integration/fixtures/blocks/core__gallery__columns.json index 3dc0525fe7349c..1767284cc4168a 100644 --- a/test/integration/fixtures/blocks/core__gallery__columns.json +++ b/test/integration/fixtures/blocks/core__gallery__columns.json @@ -5,7 +5,7 @@ "attributes": { "images": [], "ids": [], - "hasIcon": true, + "navigationButtonType": "icon", "shortCodeTransforms": [], "columns": 1, "caption": "", diff --git a/test/integration/fixtures/blocks/core__gallery__deprecated-7.json b/test/integration/fixtures/blocks/core__gallery__deprecated-7.json index f888d6ded424a8..09d621e7821b47 100644 --- a/test/integration/fixtures/blocks/core__gallery__deprecated-7.json +++ b/test/integration/fixtures/blocks/core__gallery__deprecated-7.json @@ -5,7 +5,7 @@ "attributes": { "images": [], "ids": [], - "hasIcon": true, + "navigationButtonType": "icon", "shortCodeTransforms": [], "caption": "", "imageCrop": true, From 0b16ca9c46639df20d42be2102f2605b47850fb8 Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Fri, 5 Sep 2025 15:15:46 +0900 Subject: [PATCH 39/58] Remove aria-label attirbute if value is empty --- packages/block-library/src/image/index.php | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/block-library/src/image/index.php b/packages/block-library/src/image/index.php index 67df9d0492e731..f9e71577a754f1 100644 --- a/packages/block-library/src/image/index.php +++ b/packages/block-library/src/image/index.php @@ -297,6 +297,11 @@ function block_core_image_print_lightbox_overlay( $block_context ) { $prev_button_label = 'icon' === $navigation_button_type ? $prev_button_text : ''; $next_button_label = 'icon' === $navigation_button_type ? $next_button_text : ''; + // Create aria-label attributes only when labels are not empty + $close_button_label_attr = ! empty( $close_button_label ) ? ' aria-label="' . esc_attr( $close_button_label ) . '"' : ''; + $prev_button_label_attr = ! empty( $prev_button_label ) ? ' aria-label="' . esc_attr( $prev_button_label ) . '"' : ''; + $next_button_label_attr = ! empty( $next_button_label ) ? ' aria-label="' . esc_attr( $next_button_label ) . '"' : ''; + // If the current theme does NOT have a `theme.json`, or the colors are not // defined, it needs to set the background color & close button color to some // default values because it can't get them from the Global Styles. @@ -315,7 +320,7 @@ function block_core_image_print_lightbox_overlay( $block_context ) { echo << -
- + HTML; } From fd265f0eadf6b3e2229386e5aa2cb0ad37bb4b0f Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Fri, 5 Sep 2025 15:16:36 +0900 Subject: [PATCH 40/58] Update help text --- packages/block-library/src/gallery/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js index fa5aa497b2fb8d..282506bec1ec5a 100644 --- a/packages/block-library/src/gallery/edit.js +++ b/packages/block-library/src/gallery/edit.js @@ -736,7 +736,7 @@ export default function GalleryEdit( props ) { options={ NAVIGATION_BUTTON_TYPE_OPTIONS } size="__unstable-large" help={ __( - 'Configure the visual appearance of the buttons in the lightbox.' + 'Adjust the appearance of buttons in the lightbox.' ) } /> From 3fadb5cf6edcd84b11e04090cb4979bbc21feed6 Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Fri, 5 Sep 2025 15:20:43 +0900 Subject: [PATCH 41/58] Simplify logic --- packages/block-library/src/image/index.php | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/block-library/src/image/index.php b/packages/block-library/src/image/index.php index f9e71577a754f1..35d86e1d968551 100644 --- a/packages/block-library/src/image/index.php +++ b/packages/block-library/src/image/index.php @@ -293,14 +293,9 @@ function block_core_image_print_lightbox_overlay( $block_context ) { break; } - $close_button_label = 'icon' === $navigation_button_type ? $close_button_text : ''; - $prev_button_label = 'icon' === $navigation_button_type ? $prev_button_text : ''; - $next_button_label = 'icon' === $navigation_button_type ? $next_button_text : ''; - - // Create aria-label attributes only when labels are not empty - $close_button_label_attr = ! empty( $close_button_label ) ? ' aria-label="' . esc_attr( $close_button_label ) . '"' : ''; - $prev_button_label_attr = ! empty( $prev_button_label ) ? ' aria-label="' . esc_attr( $prev_button_label ) . '"' : ''; - $next_button_label_attr = ! empty( $next_button_label ) ? ' aria-label="' . esc_attr( $next_button_label ) . '"' : ''; + $close_button_label_attr = 'icon' === $navigation_button_type ? ' aria-label="' . esc_attr( $close_button_text ) . '"' : ''; + $prev_button_label_attr = 'icon' === $navigation_button_type ? ' aria-label="' . esc_attr( $prev_button_text ) . '"' : ''; + $next_button_label_attr = 'icon' === $navigation_button_type ? ' aria-label="' . esc_attr( $next_button_text ) . '"' : ''; // If the current theme does NOT have a `theme.json`, or the colors are not // defined, it needs to set the background color & close button color to some From 7b4faa9f9df849ca8d6147501a502dce09356e6d Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Wed, 24 Sep 2025 20:09:51 +0900 Subject: [PATCH 42/58] Fix: navigationButtonType doesn't work properly when there are multiple galleries --- packages/block-library/src/image/index.php | 61 ++++++++-------------- packages/block-library/src/image/view.js | 14 +++++ 2 files changed, 37 insertions(+), 38 deletions(-) diff --git a/packages/block-library/src/image/index.php b/packages/block-library/src/image/index.php index 35d86e1d968551..a1ec670906246f 100644 --- a/packages/block-library/src/image/index.php +++ b/packages/block-library/src/image/index.php @@ -198,6 +198,7 @@ function block_core_image_render_lightbox( $block_content, $block, $block_instan 'alt' => $alt, 'galleryId' => $block_instance->context['galleryId'] ?? null, 'customAriaLabel' => $custom_aria_label ?? null, + 'navigationButtonType' => $block_instance->context['navigationButtonType'] ?? 'icon', 'triggerButtonAriaLabel' => null, ), ), @@ -252,10 +253,8 @@ class="lightbox-trigger" $body_content = preg_replace( '/]+>/', $button, $body_content ); - $block_context = $block_instance->context; - - $overlay_callback = function () use ( $block_context ) { - block_core_image_print_lightbox_overlay( $block_context ); + $overlay_callback = function () { + block_core_image_print_lightbox_overlay(); }; add_action( 'wp_footer', $overlay_callback ); @@ -265,37 +264,18 @@ class="lightbox-trigger" /** * @since 6.5.0 */ -function block_core_image_print_lightbox_overlay( $block_context ) { - $dialog_label = esc_attr__( 'Enlarged images' ); - $navigation_button_type = $block_context['navigationButtonType'] ?? 'icon'; - $close_button_text = esc_attr__( 'Close' ); - $prev_button_text = esc_attr__( 'Previous' ); - $next_button_text = esc_attr__( 'Next' ); - $close_button_icon = ''; - $prev_button_icon = ''; - $next_button_icon = ''; - - switch ( $navigation_button_type ) { - case 'text': - $close_button_content = $close_button_text; - $prev_button_content = $prev_button_text; - $next_button_content = $next_button_text; - break; - case 'icon-and-text': - $close_button_content = $close_button_icon . $close_button_text; - $prev_button_content = $prev_button_icon . $prev_button_text; - $next_button_content = $next_button_text . $next_button_icon; - break; - default: - $close_button_content = $close_button_icon; - $prev_button_content = $prev_button_icon; - $next_button_content = $next_button_icon; - break; - } - - $close_button_label_attr = 'icon' === $navigation_button_type ? ' aria-label="' . esc_attr( $close_button_text ) . '"' : ''; - $prev_button_label_attr = 'icon' === $navigation_button_type ? ' aria-label="' . esc_attr( $prev_button_text ) . '"' : ''; - $next_button_label_attr = 'icon' === $navigation_button_type ? ' aria-label="' . esc_attr( $next_button_text ) . '"' : ''; +function block_core_image_print_lightbox_overlay() { + $dialog_label = esc_attr__( 'Enlarged images' ); + $close_button_text = esc_attr__( 'Close' ); + $prev_button_text = esc_attr__( 'Previous' ); + $next_button_text = esc_attr__( 'Next' ); + $close_button_icon = ''; + $prev_button_icon = ''; + $next_button_icon = ''; + + $close_button_label_attr = ' aria-label="' . esc_attr( $close_button_text ) . '"'; + $prev_button_label_attr = ' aria-label="' . esc_attr( $prev_button_text ) . '"'; + $next_button_label_attr = ' aria-label="' . esc_attr( $next_button_text ) . '"'; // If the current theme does NOT have a `theme.json`, or the colors are not // defined, it needs to set the background color & close button color to some @@ -338,10 +318,14 @@ class="wp-lightbox-overlay zoom" tabindex="-1" >
diff --git a/packages/block-library/src/image/view.js b/packages/block-library/src/image/view.js index 6855d95dbc5bd8..6a725cd3817749 100644 --- a/packages/block-library/src/image/view.js +++ b/packages/block-library/src/image/view.js @@ -54,6 +54,20 @@ const { state, actions, callbacks } = store( get selectedImage() { return state.metadata[ state.selectedImageId ]; }, + get hasNavigationIcon() { + const { navigationButtonType } = state.selectedImage; + return ( + navigationButtonType === 'icon' || + navigationButtonType === 'icon-and-text' + ); + }, + get hasNavigationText() { + const { navigationButtonType } = state.selectedImage; + return ( + navigationButtonType === 'text' || + navigationButtonType === 'icon-and-text' + ); + }, get thisImage() { const { imageId } = getContext(); return state.metadata[ imageId ]; From a994601bd5ebdd72657586d4566b7c6630273fbf Mon Sep 17 00:00:00 2001 From: Aki Hamano Date: Wed, 24 Sep 2025 20:18:33 +0900 Subject: [PATCH 43/58] Output aria-label only when there is no visible text --- packages/block-library/src/image/index.php | 17 +++++++++-------- packages/block-library/src/image/view.js | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/packages/block-library/src/image/index.php b/packages/block-library/src/image/index.php index a1ec670906246f..6c34b7af2655f0 100644 --- a/packages/block-library/src/image/index.php +++ b/packages/block-library/src/image/index.php @@ -160,7 +160,12 @@ function block_core_image_render_lightbox( $block_content, $block, $block_instan wp_interactivity_config( 'core/image', - array( 'defaultAriaLabel' => __( 'Enlarged image' ) ) + array( + 'defaultAriaLabel' => __( 'Enlarged image' ), + 'closeButtonText' => esc_html__( 'Close' ), + 'prevButtonText' => esc_html__( 'Previous' ), + 'nextButtonText' => esc_html__( 'Next' ), + ) ); if ( $alt ) { @@ -273,10 +278,6 @@ function block_core_image_print_lightbox_overlay() { $prev_button_icon = ''; $next_button_icon = ''; - $close_button_label_attr = ' aria-label="' . esc_attr( $close_button_text ) . '"'; - $prev_button_label_attr = ' aria-label="' . esc_attr( $prev_button_text ) . '"'; - $next_button_label_attr = ' aria-label="' . esc_attr( $next_button_text ) . '"'; - // If the current theme does NOT have a `theme.json`, or the colors are not // defined, it needs to set the background color & close button color to some // default values because it can't get them from the Global Styles. @@ -317,12 +318,12 @@ class="wp-lightbox-overlay zoom" data-wp-bind--style="state.overlayStyles" tabindex="-1" > -