diff --git a/lib/block-supports/behaviors.php b/lib/block-supports/behaviors.php index 58c8ca7200102..b435d769ef340 100644 --- a/lib/block-supports/behaviors.php +++ b/lib/block-supports/behaviors.php @@ -87,10 +87,19 @@ function gutenberg_render_behaviors_support_lightbox( $block_content, $block ) { // We want to store the src in the context so we can set it dynamically when the lightbox is opened. $z = new WP_HTML_Tag_Processor( $content ); $z->next_tag( 'img' ); + if ( isset( $block['attrs']['id'] ) ) { - $img_src = wp_get_attachment_url( $block['attrs']['id'] ); + $img_uploaded_src = wp_get_attachment_url( $block['attrs']['id'] ); + $img_metadata = wp_get_attachment_metadata( $block['attrs']['id'] ); + $img_width = $img_metadata['width']; + $img_height = $img_metadata['height']; + $img_uploaded_srcset = wp_get_attachment_image_srcset( $block['attrs']['id'] ); } else { - $img_src = $z->get_attribute( 'src' ); + $img_uploaded_src = $z->get_attribute( 'src' ); + $img_dimensions = wp_getimagesize( $img_uploaded_src ); + $img_width = $img_dimensions[0]; + $img_height = $img_dimensions[1]; + $img_uploaded_srcset = ''; } $w = new WP_HTML_Tag_Processor( $content ); @@ -99,24 +108,59 @@ function gutenberg_render_behaviors_support_lightbox( $block_content, $block ) { $w->set_attribute( 'data-wp-interactive', true ); $w->set_attribute( 'data-wp-context', - sprintf( '{ "core":{ "image": { "initialized": false, "imageSrc": "%s", "lightboxEnabled": false, "lightboxAnimation": "%s", "hideAnimationEnabled": false } } }', $img_src, $lightbox_animation ) + sprintf( + '{ "core": + { "image": + { "imageLoaded": false, + "initialized": false, + "lightboxEnabled": false, + "hideAnimationEnabled": false, + "preloadInitialized": false, + "lightboxAnimation": "%s", + "imageUploadedSrc": "%s", + "imageCurrentSrc": "", + "imageSrcSet": "%s", + "targetWidth": "%s", + "targetHeight": "%s" + } + } + }', + $lightbox_animation, + $img_uploaded_src, + $img_uploaded_srcset, + $img_width, + $img_height + ) ); + $w->next_tag( 'img' ); + $w->set_attribute( 'data-wp-effect', 'effects.core.image.setCurrentSrc' ); $body_content = $w->get_updated_html(); // Wrap the image in the body content with a button. $img = null; - preg_match( '/]+>/', $content, $img ); + preg_match( '/]+>/', $body_content, $img ); $button = '
- ' + ' . $img[0] . '
'; $body_content = preg_replace( '/]+>/', $button, $body_content ); // Add src to the modal image. $m = new WP_HTML_Tag_Processor( $content ); + $m->next_tag( 'figure' ); + $m->add_class( 'responsive-image' ); $m->next_tag( 'img' ); - $m->set_attribute( 'data-wp-bind--src', 'selectors.core.image.imageSrc' ); - $modal_content = $m->get_updated_html(); + $m->set_attribute( 'src', '' ); + $m->set_attribute( 'data-wp-bind--src', 'selectors.core.image.responsiveImgSrc' ); + $initial_image_content = $m->get_updated_html(); + + $q = new WP_HTML_Tag_Processor( $content ); + $q->next_tag( 'figure' ); + $q->add_class( 'enlarged-image' ); + $q->next_tag( 'img' ); + $q->set_attribute( 'src', '' ); + $q->set_attribute( 'data-wp-bind--src', 'selectors.core.image.enlargedImgSrc' ); + $enlarged_image_content = $q->get_updated_html(); $background_color = esc_attr( wp_get_global_styles( array( 'color', 'background' ) ) ); @@ -142,7 +186,8 @@ function gutenberg_render_behaviors_support_lightbox( $block_content, $block ) { - $modal_content + $initial_image_content + $enlarged_image_content
HTML; diff --git a/packages/block-library/src/image/interactivity.js b/packages/block-library/src/image/interactivity.js index 2ef370496a894..6560cef9e0243 100644 --- a/packages/block-library/src/image/interactivity.js +++ b/packages/block-library/src/image/interactivity.js @@ -22,32 +22,43 @@ store( { core: { image: { showLightbox: ( { context, event } ) => { + // We can't initialize the lightbox until the reference + // image is loaded, otherwise the UX is broken. + if ( ! context.core.image.imageLoaded ) { + return; + } context.core.image.initialized = true; context.core.image.lastFocusedElement = window.document.activeElement; context.core.image.scrollDelta = 0; + context.core.image.lightboxEnabled = true; + if ( context.core.image.lightboxAnimation === 'zoom' ) { + setZoomStyles( + event.target.nextElementSibling, + context, + event + ); + } + // Hide overflow only when the animation is in progress, + // otherwise the removal of the scrollbars will draw attention + // to itself and look like an error + document.documentElement.classList.add( + 'has-lightbox-open' + ); + // Since the img is hidden and its src not loaded until // the lightbox is opened, let's create an img element on the fly // so we can get the dimensions we need to calculate the styles + context.core.image.preloadInitialized = true; const imgDom = document.createElement( 'img' ); - imgDom.onload = function () { - // Enable the lightbox only after the image - // is loaded to prevent flashing of unstyled content - context.core.image.lightboxEnabled = true; - if ( context.core.image.lightboxAnimation === 'zoom' ) { - setZoomStyles( imgDom, context, event ); - } - - // Hide overflow only when the animation is in progress, - // otherwise the removal of the scrollbars will draw attention - // to itself and look like an error - document.documentElement.classList.add( - 'has-lightbox-open' - ); + context.core.image.activateLargeImage = true; }; - imgDom.setAttribute( 'src', context.core.image.imageSrc ); + imgDom.setAttribute( + 'src', + context.core.image.imageUploadedSrc + ); }, hideLightbox: async ( { context, event } ) => { context.core.image.hideAnimationEnabled = true; @@ -131,9 +142,14 @@ store( { roleAttribute: ( { context } ) => { return context.core.image.lightboxEnabled ? 'dialog' : ''; }, - imageSrc: ( { context } ) => { + responsiveImgSrc: ( { context } ) => { + return context.core.image.activateLargeImage + ? '' + : context.core.image.imageCurrentSrc; + }, + enlargedImgSrc: ( { context } ) => { return context.core.image.initialized - ? context.core.image.imageSrc + ? context.core.image.imageUploadedSrc : ''; }, }, @@ -142,6 +158,30 @@ store( { effects: { core: { image: { + setCurrentSrc: ( { context, ref } ) => { + if ( ref.complete ) { + context.core.image.imageLoaded = true; + context.core.image.imageCurrentSrc = ref.currentSrc; + } else { + ref.addEventListener( 'load', function () { + context.core.image.imageLoaded = true; + context.core.image.imageCurrentSrc = + this.currentSrc; + } ); + } + }, + preloadLightboxImage: ( { context, ref } ) => { + ref.addEventListener( 'mouseover', () => { + if ( ! context.core.image.preloadInitialized ) { + context.core.image.preloadInitialized = true; + const imgDom = document.createElement( 'img' ); + imgDom.setAttribute( + 'src', + context.core.image.imageUploadedSrc + ); + } + } ); + }, initLightbox: async ( { context, ref } ) => { context.core.image.figureRef = ref.querySelector( 'figure' ); @@ -163,8 +203,8 @@ store( { } ); function setZoomStyles( imgDom, context, event ) { - let targetWidth = imgDom.naturalWidth; - let targetHeight = imgDom.naturalHeight; + let targetWidth = context.core.image.targetWidth; + let targetHeight = context.core.image.targetHeight; const verticalPadding = 40; diff --git a/test/e2e/specs/editor/blocks/image.spec.js b/test/e2e/specs/editor/blocks/image.spec.js index 5f802d0e85063..0735b9a7573e6 100644 --- a/test/e2e/specs/editor/blocks/image.spec.js +++ b/test/e2e/specs/editor/blocks/image.spec.js @@ -987,13 +987,19 @@ test.describe( 'Image - interactivity', () => { const lightbox = page.locator( '.wp-lightbox-overlay' ); await expect( lightbox ).toBeHidden(); - const image = lightbox.locator( 'img' ); + const responsiveImage = lightbox.locator( '.responsive-image img' ); + const enlargedImage = lightbox.locator( '.enlarged-image img' ); - await expect( image ).toHaveAttribute( 'src', '' ); + await expect( responsiveImage ).toHaveAttribute( + 'src', + new RegExp( filename ) + ); + await expect( enlargedImage ).toHaveAttribute( 'src', '' ); await page.getByRole( 'button', { name: 'Enlarge image' } ).click(); - await expect( image ).toHaveAttribute( + await expect( responsiveImage ).toHaveAttribute( 'src', '' ); + await expect( enlargedImage ).toHaveAttribute( 'src', new RegExp( filename ) ); @@ -1176,12 +1182,19 @@ test.describe( 'Image - interactivity', () => { await page.goto( `/?p=${ postId }` ); const lightbox = page.locator( '.wp-lightbox-overlay' ); - const imageDom = lightbox.locator( 'img' ); - await expect( imageDom ).toHaveAttribute( 'src', '' ); + const responsiveImage = lightbox.locator( '.responsive-image img' ); + const enlargedImage = lightbox.locator( '.enlarged-image img' ); + + await expect( responsiveImage ).toHaveAttribute( + 'src', + new RegExp( imgUrl ) + ); + await expect( enlargedImage ).toHaveAttribute( 'src', '' ); await page.getByRole( 'button', { name: 'Enlarge image' } ).click(); - await expect( imageDom ).toHaveAttribute( 'src', imgUrl ); + await expect( responsiveImage ).toHaveAttribute( 'src', '' ); + await expect( enlargedImage ).toHaveAttribute( 'src', imgUrl ); } ); } );