diff --git a/lib/block-supports/behaviors.php b/lib/block-supports/behaviors.php index 6f442d7b0d2d7c..c1b3dceceee94d 100644 --- a/lib/block-supports/behaviors.php +++ b/lib/block-supports/behaviors.php @@ -84,17 +84,19 @@ function gutenberg_render_behaviors_support_lightbox( $block_content, $block ) { $aria_label = __( 'Enlarge image', 'gutenberg' ); + $processor->next_tag( 'img' ); $alt_attribute = $processor->get_attribute( 'alt' ); - if ( null !== $alt_attribute ) { + // An empty alt attribute `alt=""` is valid for decorative images. + if ( is_string( $alt_attribute ) ) { $alt_attribute = trim( $alt_attribute ); } + // It only makes sense to append the alt text to the button aria-label when the alt text is non-empty. if ( $alt_attribute ) { /* translators: %s: Image alt text. */ $aria_label = sprintf( __( 'Enlarge image: %s', 'gutenberg' ), $alt_attribute ); } - $content = $processor->get_updated_html(); // If we don't set a default, it won't work if Lightbox is set to enabled by default. $lightbox_animation = 'zoom'; @@ -102,17 +104,15 @@ function gutenberg_render_behaviors_support_lightbox( $block_content, $block ) { $lightbox_animation = $lightbox_settings['animation']; } - // 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' ); - + // Note: We want to store the `src` in the context so we + // can set it dynamically when the lightbox is opened. if ( isset( $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']; } else { - $img_uploaded_src = $z->get_attribute( 'src' ); + $img_uploaded_src = $processor->get_attribute( 'src' ); $img_width = 'none'; $img_height = 'none'; } @@ -123,7 +123,7 @@ function gutenberg_render_behaviors_support_lightbox( $block_content, $block ) { $scale_attr = false; } - $w = new WP_HTML_Tag_Processor( $content ); + $w = new WP_HTML_Tag_Processor( $block_content ); $w->next_tag( 'figure' ); $w->add_class( 'wp-lightbox-container' ); $w->set_attribute( 'data-wp-interactive', true ); @@ -163,19 +163,20 @@ function gutenberg_render_behaviors_support_lightbox( $block_content, $block ) { // Wrap the image in the body content with a button. $img = null; preg_match( '/]+>/', $body_content, $img ); - $button = - '' - . $img[0]; + + $button = + $img[0] + . ''; + $body_content = preg_replace( '/]+>/', $button, $body_content ); // We need both a responsive image and an enlarged image to animate @@ -183,7 +184,7 @@ function gutenberg_render_behaviors_support_lightbox( $block_content, $block ) { // image is a copy of the one in the body, which animates immediately // as the lightbox is opened, while the enlarged one is a full-sized // version that will likely still be loading as the animation begins. - $m = new WP_HTML_Tag_Processor( $content ); + $m = new WP_HTML_Tag_Processor( $block_content ); $m->next_tag( 'figure' ); $m->add_class( 'responsive-image' ); $m->next_tag( 'img' ); @@ -199,7 +200,7 @@ function gutenberg_render_behaviors_support_lightbox( $block_content, $block ) { $m->set_attribute( 'data-wp-style--object-fit', 'selectors.core.image.lightboxObjectFit' ); $initial_image_content = $m->get_updated_html(); - $q = new WP_HTML_Tag_Processor( $content ); + $q = new WP_HTML_Tag_Processor( $block_content ); $q->next_tag( 'figure' ); $q->add_class( 'enlarged-image' ); $q->next_tag( 'img' ); @@ -219,7 +220,7 @@ function gutenberg_render_behaviors_support_lightbox( $block_content, $block ) { $close_button_icon = ''; $close_button_color = esc_attr( wp_get_global_styles( array( 'color', 'text' ) ) ); - $dialog_label = $alt_attribute ? esc_attr( $alt_attribute ) : esc_attr__( 'Image', 'gutenberg' ); + $dialog_label = esc_attr__( 'Enlarged image', 'gutenberg' ); $close_button_label = esc_attr__( 'Close', 'gutenberg' ); $lightbox_html = << array( $this, 'install_fonts' ), 'permission_callback' => array( $this, 'update_font_library_permissions_check' ), 'args' => array( - 'fontFamilies' => array( + 'font_families' => array( 'required' => true, 'type' => 'string', 'validate_callback' => array( $this, 'validate_install_font_families' ), @@ -147,13 +147,13 @@ private function get_validation_errors( $font_families, $files ) { $error_messages = array(); if ( ! is_array( $font_families ) ) { - $error_messages[] = __( 'fontFamilies should be an array of font families.', 'gutenberg' ); + $error_messages[] = __( 'font_families should be an array of font families.', 'gutenberg' ); return $error_messages; } // Checks if there is at least one font family. if ( count( $font_families ) < 1 ) { - $error_messages[] = __( 'fontFamilies should have at least one font family definition.', 'gutenberg' ); + $error_messages[] = __( 'font_families should have at least one font family definition.', 'gutenberg' ); return $error_messages; } @@ -260,7 +260,7 @@ public function validate_install_font_families( $param, $request ) { */ public function uninstall_schema() { return array( - 'fontFamilies' => array( + 'font_families' => array( 'type' => 'array', 'description' => __( 'The font families to install.', 'gutenberg' ), 'required' => true, @@ -289,7 +289,7 @@ public function uninstall_schema() { * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ public function uninstall_fonts( $request ) { - $fonts_to_uninstall = $request->get_param( 'fontFamilies' ); + $fonts_to_uninstall = $request->get_param( 'font_families' ); $errors = array(); $successes = array(); @@ -397,7 +397,7 @@ private function needs_write_permission( $font_families ) { */ public function install_fonts( $request ) { // Get new fonts to install. - $fonts_param = $request->get_param( 'fontFamilies' ); + $fonts_param = $request->get_param( 'font_families' ); /* * As this is receiving form data, the font families are encoded as a string. diff --git a/lib/load.php b/lib/load.php index 77232efcfae1a9..72ec9e62a8d74e 100644 --- a/lib/load.php +++ b/lib/load.php @@ -244,3 +244,4 @@ function () { require __DIR__ . '/block-supports/duotone.php'; require __DIR__ . '/block-supports/shadow.php'; require __DIR__ . '/block-supports/background.php'; +require __DIR__ . '/block-supports/behaviors.php'; diff --git a/packages/base-styles/_z-index.scss b/packages/base-styles/_z-index.scss index 12443a30a96656..cbe495d3787cd9 100644 --- a/packages/base-styles/_z-index.scss +++ b/packages/base-styles/_z-index.scss @@ -128,6 +128,7 @@ $z-layers: ( ".block-editor-block-rename-modal": 1000001, ".edit-site-list__rename-modal": 1000001, ".edit-site-swap-template-modal": 1000001, + ".edit-site-template-panel__replace-template-modal": 1000001, // Note: The ConfirmDialog component's z-index is being set to 1000001 in packages/components/src/confirm-dialog/styles.ts // because it uses emotion and not sass. We need it to render on top its parent popover. diff --git a/packages/block-editor/src/components/block-styles/index.js b/packages/block-editor/src/components/block-styles/index.js index b998614e4b0892..f598b35f890f15 100644 --- a/packages/block-editor/src/components/block-styles/index.js +++ b/packages/block-editor/src/components/block-styles/index.js @@ -14,7 +14,6 @@ import { Popover, } from '@wordpress/components'; import deprecated from '@wordpress/deprecated'; -import { __ } from '@wordpress/i18n'; /** * Internal dependencies @@ -65,9 +64,7 @@ function BlockStyles( { clientId, onSwitch = noop, onHoverClassName = noop } ) {
{ stylesToRender.map( ( style ) => { - const buttonText = style.isDefault - ? __( 'Default' ) - : style.label || style.name; + const buttonText = style.label || style.name; return ( ' - . $img[0]; + + $button = + $img[0] + . ''; + $body_content = preg_replace( '/]+>/', $button, $body_content ); // We need both a responsive image and an enlarged image to animate @@ -220,7 +226,7 @@ function block_core_image_render_lightbox( $block_content, $block ) { // image is a copy of the one in the body, which animates immediately // as the lightbox is opened, while the enlarged one is a full-sized // version that will likely still be loading as the animation begins. - $m = new WP_HTML_Tag_Processor( $content ); + $m = new WP_HTML_Tag_Processor( $block_content ); $m->next_tag( 'figure' ); $m->add_class( 'responsive-image' ); $m->next_tag( 'img' ); @@ -236,7 +242,7 @@ function block_core_image_render_lightbox( $block_content, $block ) { $m->set_attribute( 'data-wp-style--object-fit', 'selectors.core.image.lightboxObjectFit' ); $initial_image_content = $m->get_updated_html(); - $q = new WP_HTML_Tag_Processor( $content ); + $q = new WP_HTML_Tag_Processor( $block_content ); $q->next_tag( 'figure' ); $q->add_class( 'enlarged-image' ); $q->next_tag( 'img' ); @@ -252,24 +258,32 @@ function block_core_image_render_lightbox( $block_content, $block ) { $q->set_attribute( 'data-wp-style--object-fit', 'selectors.core.image.lightboxObjectFit' ); $enlarged_image_content = $q->get_updated_html(); - $background_color = esc_attr( wp_get_global_styles( array( 'color', 'background' ) ) ); + // If the current theme does NOT have a `theme.json`, or the colors are not defined, + // we need to set the background color & close button color to some default values + // because we can't get them from the Global Styles. + $background_color = '#fff'; + $close_button_color = '#000'; + if ( wp_theme_has_theme_json() ) { + $global_styles_color = wp_get_global_styles( array( 'color' ) ); + if ( ! empty( $global_styles_color['background'] ) ) { + $background_color = esc_attr( $global_styles_color['background'] ); + } + if ( ! empty( $global_styles_color['text'] ) ) { + $close_button_color = esc_attr( $global_styles_color['text'] ); + } + } $close_button_icon = ''; - $close_button_color = esc_attr( wp_get_global_styles( array( 'color', 'text' ) ) ); - $dialog_label = $alt_attribute ? esc_attr( $alt_attribute ) : esc_attr__( 'Image' ); $close_button_label = esc_attr__( 'Close' ); $lightbox_html = << -
+
HTML; @@ -290,11 +304,13 @@ function block_core_image_render_lightbox( $block_content, $block ) { } /** - * Ensure that the view script has the `wp-interactivity` dependency. + * Ensures that the view script has the `wp-interactivity` dependency. * * @since 6.4.0 * * @global WP_Scripts $wp_scripts + * + * @return void */ function block_core_image_ensure_interactivity_dependency() { global $wp_scripts; @@ -310,6 +326,8 @@ function block_core_image_ensure_interactivity_dependency() { /** * Registers the `core/image` block on server. + * + * @return void */ function register_block_core_image() { register_block_type_from_metadata( diff --git a/packages/block-library/src/image/style.scss b/packages/block-library/src/image/style.scss index 5c3552fd80c2ee..2ef602982e57b5 100644 --- a/packages/block-library/src/image/style.scss +++ b/packages/block-library/src/image/style.scss @@ -154,6 +154,8 @@ .wp-lightbox-container { position: relative; + display: flex; + flex-direction: column; button { border: none; @@ -169,6 +171,13 @@ outline: 5px auto -webkit-focus-ring-color; outline-offset: 5px; } + + &:hover, + &:focus, + &:not(:hover):not(:active):not(.has-background) { + background: none; + border: none; + } } } @@ -186,11 +195,23 @@ .close-button { position: absolute; - top: calc(env(safe-area-inset-top) + 20px); - right: calc(env(safe-area-inset-right) + 20px); + 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 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 + display: flex; + align-items: center; + justify-content: center; + + &:hover, + &:focus, + &:not(:hover):not(:active):not(.has-background) { + background: none; + border: none; + } } .lightbox-image-container { diff --git a/packages/block-library/src/image/view.js b/packages/block-library/src/image/view.js index 13f20c9cd7cb68..3eb47dcc7cab4b 100644 --- a/packages/block-library/src/image/view.js +++ b/packages/block-library/src/image/view.js @@ -227,7 +227,17 @@ store( roleAttribute: ( { context } ) => { return context.core.image.lightboxEnabled ? 'dialog' - : ''; + : null; + }, + ariaModal: ( { context } ) => { + return context.core.image.lightboxEnabled + ? 'true' + : null; + }, + dialogLabel: ( { context } ) => { + return context.core.image.lightboxEnabled + ? context.core.image.dialogLabel + : null; }, lightboxObjectFit: ( { context } ) => { if ( context.core.image.initialized ) { @@ -237,7 +247,7 @@ store( enlargedImgSrc: ( { context } ) => { return context.core.image.initialized ? context.core.image.imageUploadedSrc - : ''; + : 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='; }, }, }, @@ -360,9 +370,9 @@ function setStyles( context, event ) { naturalHeight, offsetWidth: originalWidth, offsetHeight: originalHeight, - } = event.target.nextElementSibling; + } = event.target.previousElementSibling; let { x: screenPosX, y: screenPosY } = - event.target.nextElementSibling.getBoundingClientRect(); + event.target.previousElementSibling.getBoundingClientRect(); // Natural ratio of the image clicked to open the lightbox. const naturalRatio = naturalWidth / naturalHeight; diff --git a/packages/block-library/src/latest-posts/edit.js b/packages/block-library/src/latest-posts/edit.js index 7aaf1b3ecf0eda..586ecc59432730 100644 --- a/packages/block-library/src/latest-posts/edit.js +++ b/packages/block-library/src/latest-posts/edit.js @@ -483,12 +483,17 @@ export default function LatestPostsEdit( { attributes, setAttributes } ) { .split( ' ', excerptLength ) .join( ' ' ) } { createInterpolateElement( - /* translators: excerpt truncation character, default … */ - __( ' … Read more' ), + sprintf( + /* translators: 1: The static string "Read more", 2: The post title only visible to screen readers. */ + __( '… %1$s: %2$s' ), + __( 'Read more' ), + titleTrimmed || __( '(no title)' ) + ), { a: ( // eslint-disable-next-line jsx-a11y/anchor-has-content ), + span: ( + + ), } ) } diff --git a/packages/block-library/src/latest-posts/index.php b/packages/block-library/src/latest-posts/index.php index 356ba5032689f7..d5f759c0c0e259 100644 --- a/packages/block-library/src/latest-posts/index.php +++ b/packages/block-library/src/latest-posts/index.php @@ -48,14 +48,6 @@ function render_block_core_latest_posts( $attributes ) { $block_core_latest_posts_excerpt_length = $attributes['excerptLength']; add_filter( 'excerpt_length', 'block_core_latest_posts_get_excerpt_length', 20 ); - $filter_latest_posts_excerpt_more = static function ( $more ) use ( $attributes ) { - $use_excerpt = 'excerpt' === $attributes['displayPostContentRadio']; - /* translators: %1$s is a URL to a post, excerpt truncation character, default … */ - return $use_excerpt ? sprintf( __( ' … Read more' ), esc_url( get_permalink() ) ) : $more; - }; - - add_filter( 'excerpt_more', $filter_latest_posts_excerpt_more ); - if ( ! empty( $attributes['categories'] ) ) { $args['category__in'] = array_column( $attributes['categories'], 'id' ); } @@ -151,6 +143,24 @@ function render_block_core_latest_posts( $attributes ) { $trimmed_excerpt = get_the_excerpt( $post ); + /* + * Adds a "Read more" link with screen reader text. + * […] is the default excerpt ending from wp_trim_excerpt() in Core. + */ + if ( str_ends_with( $trimmed_excerpt, ' […]' ) ) { + $excerpt_length = (int) apply_filters( 'excerpt_length', $block_core_latest_posts_excerpt_length ); + if ( $excerpt_length <= $block_core_latest_posts_excerpt_length ) { + $trimmed_excerpt = substr( $trimmed_excerpt, 0, -11 ); + $trimmed_excerpt .= sprintf( + /* translators: 1: A URL to a post, 2: The static string "Read more", 3: The post title only visible to screen readers. */ + __( '… %2$s: %3$s' ), + esc_url( $post_link ), + __( 'Read more' ), + esc_html( $title ) + ); + } + } + if ( post_password_required( $post ) ) { $trimmed_excerpt = __( 'This content is password protected.' ); } diff --git a/packages/block-library/src/media-text/edit.js b/packages/block-library/src/media-text/edit.js index 30181c9044c34d..f3baeb8c1756ba 100644 --- a/packages/block-library/src/media-text/edit.js +++ b/packages/block-library/src/media-text/edit.js @@ -244,7 +244,7 @@ function MediaTextEdit( { attributes, isSelected, setAttributes } ) { setAttributes( { imageFill: ! imageFill, diff --git a/packages/block-library/src/navigation/edit/deleted-navigation-warning.js b/packages/block-library/src/navigation/edit/deleted-navigation-warning.js index c787b90b76682f..6386cee71431e0 100644 --- a/packages/block-library/src/navigation/edit/deleted-navigation-warning.js +++ b/packages/block-library/src/navigation/edit/deleted-navigation-warning.js @@ -4,14 +4,19 @@ import { Warning } from '@wordpress/block-editor'; import { Button } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; +import { createInterpolateElement } from '@wordpress/element'; function DeletedNavigationWarning( { onCreateNew } ) { return ( - { __( 'Navigation menu has been deleted or is unavailable. ' ) } - + { createInterpolateElement( + __( + 'Navigation menu has been deleted or is unavailable. ' + ), + { + button: