Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gallery: Add lightbox support #62906

Open
wants to merge 15 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/block-library/src/gallery/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -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" ],
Expand Down
108 changes: 106 additions & 2 deletions packages/block-library/src/gallery/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 6.8.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
Expand Down Expand Up @@ -121,6 +140,31 @@ function block_core_gallery_render( $attributes, $content ) {
)
);

// Get 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(
'images' => $image_ids,
),
JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
)
);

add_filter( 'render_block_core/gallery', 'block_core_gallery_render_lightbox' );

// 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.
Expand Down Expand Up @@ -166,6 +210,66 @@ static function () use ( $image_blocks, &$i ) {

return $content;
}

/**
* Handles state updates needed for the lightbox behavior in a Gallery Block.
*
* Now that the Gallery Block contains inner Image Blocks,
* we add translations for the screen reader text before rendering the gallery
* so that the Image Block can pick it up in its render_callback.
*
* @since 6.7.0
*
* @param string $block_content Rendered block content.
*
* @return string Filtered block content.
*/
function block_core_gallery_render_lightbox( $block_content ) {
$state = wp_interactivity_state( 'core/gallery' );
$gallery_id = $state['galleryId'];
$images = $state['images'][ $gallery_id ] ?? array();
$translations = array();

if ( ! empty( $images ) ) {
if ( 1 === count( $images ) ) {
$image_id = $images[0];
$translations[ $image_id ] = __( 'Enlarged image', 'gutenberg' );
} else {
for ( $i = 0; $i < count( $images ); $i++ ) {
$image_id = $images[ $i ];
/* translators: %1$s: current image index, %2$s: total number of images */
$translations[ $image_id ] = sprintf( __( 'Enlarged image %1$s of %2$s', 'gutenberg' ), $i + 1, count( $images ) );
}
}

$image_state = wp_interactivity_state( 'core/image' );

foreach ( $translations as $image_id => $translation ) {
$alt = $image_state['metadata'][ $image_id ]['alt'];
wp_interactivity_state(
'core/image',
array(
'metadata' => array(
$image_id => array(
'screenReaderText' => empty( $alt ) ? $translation : "$translation: $alt",
),
),
)
);
}
}

// reset galleryId
wp_interactivity_state(
'core/gallery',
array(
'galleryId' => null,
)
);

return $block_content;
}

/**
* Registers the `core/gallery` block on server.
*
Expand Down
10 changes: 9 additions & 1 deletion packages/block-library/src/image/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@
"name": "core/image",
"title": "Image",
"category": "media",
"usesContext": [ "allowResize", "imageCrop", "fixedHeight", "postId", "postType", "queryId" ],
"usesContext": [
"allowResize",
"imageCrop",
"fixedHeight",
"postId",
"postType",
"queryId",
"galleryId"
],
"description": "Insert an image to make a visual statement.",
"keywords": [ "img", "photo", "picture" ],
"textdomain": "default",
Expand Down
45 changes: 26 additions & 19 deletions packages/block-library/src/image/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ function render_block_core_image( $attributes, $content, $block ) {
* 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.
Expand Down Expand Up @@ -130,12 +130,13 @@ 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
Expand All @@ -149,18 +150,14 @@ function block_core_image_render_lightbox( $block_content, $block ) {
return $block_content;
}

$alt = $p->get_attribute( 'alt' );
$img_uploaded_src = $p->get_attribute( 'src' );
$img_class_names = $p->get_attribute( 'class' );
$img_styles = $p->get_attribute( 'style' );
$img_width = 'none';
$img_height = 'none';
$aria_label = __( 'Enlarge image' );

if ( $alt ) {
/* translators: %s: Image alt text. */
$aria_label = sprintf( __( 'Enlarge image: %s' ), $alt );
}
$alt = $p->get_attribute( 'alt' );
$img_uploaded_src = $p->get_attribute( 'src' );
$img_class_names = $p->get_attribute( 'class' );
$img_styles = $p->get_attribute( 'style' );
$img_width = 'none';
$img_height = 'none';
$aria_label = __( 'Enlarge image' );
$screen_reader_text = __( 'Enlarged image' );

if ( isset( $block['attrs']['id'] ) ) {
$img_uploaded_src = wp_get_attachment_url( $block['attrs']['id'] );
Expand All @@ -176,7 +173,6 @@ 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(
Expand All @@ -190,8 +186,9 @@ function block_core_image_render_lightbox( $block_content, $block ) {
'targetWidth' => $img_width,
'targetHeight' => $img_height,
'scaleAttr' => $block['attrs']['scale'] ?? false,
'ariaLabel' => $aria_label,
'alt' => $alt,
'screenReaderText' => empty( $alt ) ? $screen_reader_text : "$screen_reader_text: $alt",
'galleryId' => $block_instance->context['galleryId'] ?? null,
),
),
)
Expand Down Expand Up @@ -256,6 +253,9 @@ class="lightbox-trigger"
*/
function block_core_image_print_lightbox_overlay() {
$close_button_label = esc_attr__( 'Close' );
$dialog_label = esc_attr__( 'Enlarged images' );
Copy link
Member

@gziolo gziolo Aug 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This label Enlarged images now applies to both the single image and the group of images in the gallery. Previously, the message announced was way more accurate for a single image:

Screenshot 2024-08-21 at 17 20 23

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I adjusted the screen reader reading. NVDA reads it out as follows, but how about your software?

9bca3f2b0333352215b61aa97526289c.mp4

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's how it should work. e4ebf7d is a step in the right direction 👍🏻

$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
Expand All @@ -275,10 +275,10 @@ function block_core_image_print_lightbox_overlay() {
echo <<<HTML
<div
class="wp-lightbox-overlay zoom"
aria-label="$dialog_label"
data-wp-interactive="core/image"
data-wp-context='{}'
data-wp-bind--role="state.roleAttribute"
data-wp-bind--aria-label="state.currentImage.ariaLabel"
data-wp-bind--aria-modal="state.ariaModal"
data-wp-class--active="state.overlayEnabled"
data-wp-class--show-closing-animation="state.showClosingAnimation"
Expand All @@ -295,6 +295,12 @@ class="wp-lightbox-overlay zoom"
<button type="button" aria-label="$close_button_label" style="fill: $close_button_color" class="close-button">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20" aria-hidden="true" focusable="false"><path d="m13.06 12 6.47-6.47-1.06-1.06L12 10.94 5.53 4.47 4.47 5.53 10.94 12l-6.47 6.47 1.06 1.06L12 13.06l6.47 6.47 1.06-1.06L13.06 12Z"></path></svg>
</button>
<button type="button" aria-label="$prev_button_label" style="fill: $close_button_color" class="prev-button" data-wp-bind--hidden="!state.hasNavigation" data-wp-on--click="actions.showPreviousImage" data-wp-bind--aria-disabled="!state.hasPreviousImage">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="28" height="28" aria-hidden="true" focusable="false"><path d="M14.6 7l-1.2-1L8 12l5.4 6 1.2-1-4.6-5z"></path></svg>
</button>
<button type="button" aria-label="$next_button_label" style="fill: $close_button_color" class="next-button" data-wp-bind--hidden="!state.hasNavigation" data-wp-on--click="actions.showNextImage" data-wp-bind--aria-disabled="!state.hasNextImage">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="28" height="28" aria-hidden="true" focusable="false"><path d="M10.6 6L9.4 7l4.6 5-4.6 5 1.2 1 5.4-6z"></path></svg>
</button>
<div class="lightbox-image-container">
<figure data-wp-bind--class="state.currentImage.figureClassNames" data-wp-bind--style="state.figureStyles">
<img data-wp-bind--alt="state.currentImage.alt" data-wp-bind--class="state.currentImage.imgClassNames" data-wp-bind--style="state.imgStyles" data-wp-bind--src="state.currentImage.currentSrc">
Expand All @@ -305,6 +311,7 @@ class="wp-lightbox-overlay zoom"
<img data-wp-bind--alt="state.currentImage.alt" data-wp-bind--class="state.currentImage.imgClassNames" data-wp-bind--style="state.imgStyles" data-wp-bind--src="state.enlargedSrc">
</figure>
</div>
<div data-wp-watch="callbacks.setScreenReaderText" aria-live="polite" aria-atomic="true" class="lightbox-speak screen-reader-text"></div>
<div class="scrim" style="background-color: $background_color" aria-hidden="true"></div>
<style data-wp-text="state.overlayStyles"></style>
</div>
Expand Down
42 changes: 42 additions & 0 deletions packages/block-library/src/image/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,48 @@
}
}

.prev-button,
.next-button {
display: none;
position: absolute;
top: 50%;
transform: translateY(-50%);
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;
}

&[aria-disabled="true"] {
opacity: 0.3;
}

&:hover,
&:focus,
&:not(:hover):not(:active):not(.has-background) {
background: none;
border: none;
}

@include break-mobile() {
display: flex;
}
}

.prev-button {
left: calc(env(safe-area-inset-left) + 16px); // equivalent to $grid-unit-20
}

.next-button {
right: calc(env(safe-area-inset-right) + 16px); // equivalent to $grid-unit-20
}

.lightbox-image-container {
position: absolute;
overflow: hidden;
Expand Down
Loading
Loading