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

Cherry-picked commits for WordPress 6.4 RC1 v3 #55405

Merged
merged 4 commits into from
Oct 17, 2023
Merged
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
22 changes: 16 additions & 6 deletions packages/block-library/src/image/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -218,13 +218,17 @@ function block_core_image_render_lightbox( $block_content, $block ) {
)
);
$w->next_tag( 'img' );
$w->set_attribute( 'data-wp-init', 'effects.core.image.setCurrentSrc' );
$w->set_attribute( 'data-wp-init', 'effects.core.image.initOriginImage' );
$w->set_attribute( 'data-wp-on--load', 'actions.core.image.handleLoad' );
$w->set_attribute( 'data-wp-effect', 'effects.core.image.setButtonStyles' );
// We need to set an event callback on the `img` specifically
// because the `figure` element can also contain a caption, and
// we don't want to trigger the lightbox when the caption is clicked.
$w->set_attribute( 'data-wp-on--click', 'actions.core.image.showLightbox' );
$w->set_attribute( 'data-wp-effect--setStylesOnResize', 'effects.core.image.setStylesOnResize' );
$body_content = $w->get_updated_html();

// Wrap the image in the body content with a button.
// Add a button alongside image in the body content.
$img = null;
preg_match( '/<img[^>]+>/', $body_content, $img );

Expand All @@ -235,11 +239,17 @@ function block_core_image_render_lightbox( $block_content, $block ) {
aria-haspopup="dialog"
aria-label="' . esc_attr( $aria_label ) . '"
data-wp-on--click="actions.core.image.showLightbox"
data-wp-style--width="context.core.image.imageButtonWidth"
data-wp-style--height="context.core.image.imageButtonHeight"
data-wp-style--left="context.core.image.imageButtonLeft"
data-wp-style--right="context.core.image.imageButtonRight"
data-wp-style--top="context.core.image.imageButtonTop"
></button>';
style="background: #000"
>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M9 5H5V9" stroke="#FFFFFF" stroke-width="1.5"/>
<path d="M15 19L19 19L19 15" stroke="#FFFFFF" stroke-width="1.5"/>
<path d="M15 5H19V9" stroke="#FFFFFF" stroke-width="1.5"/>
<path d="M9 19L5 19L5 15" stroke="#FFFFFF" stroke-width="1.5"/>
</svg>
</button>';

$body_content = preg_replace( '/<img[^>]+>/', $button, $body_content );

Expand Down
31 changes: 27 additions & 4 deletions packages/block-library/src/image/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -157,25 +157,48 @@
display: flex;
flex-direction: column;

img {
cursor: zoom-in;
}

img:hover + button {
opacity: 1;
}

button {
opacity: 0;
border: none;
background: none;
background: #000;
cursor: zoom-in;
width: 100%;
height: 100%;
width: 24px;
height: 24px;
position: absolute;
z-index: 100;
top: 10px;
right: 10px;
text-align: center;
padding: 0;
border-radius: 10%;

&:focus-visible {
outline: 5px auto #212121;
outline: 5px auto -webkit-focus-ring-color;
outline-offset: 5px;
}

&:hover {
cursor: pointer;
opacity: 1;
}

&:focus {
opacity: 1;
}

&:hover,
&:focus,
&:not(:hover):not(:active):not(.has-background) {
background: none;
background: #000;
border: none;
}
}
Expand Down
125 changes: 76 additions & 49 deletions packages/block-library/src/image/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,10 @@ store(
context.core.image.lastFocusedElement =
window.document.activeElement;
context.core.image.scrollDelta = 0;
context.core.image.pointerType = event.pointerType;

context.core.image.lightboxEnabled = true;
setStyles(
context,
event.target.previousElementSibling
);
setStyles( context, context.core.image.imageRef );

context.core.image.scrollTopReset =
window.pageYOffset ||
Expand Down Expand Up @@ -137,7 +135,7 @@ store(
false
);
},
hideLightbox: async ( { context } ) => {
hideLightbox: async ( { context, event } ) => {
context.core.image.hideAnimationEnabled = true;
if ( context.core.image.lightboxEnabled ) {
// We want to wait until the close animation is completed
Expand All @@ -154,9 +152,16 @@ store(
}, 450 );

context.core.image.lightboxEnabled = false;
context.core.image.lastFocusedElement.focus( {
preventScroll: true,
} );

// We want to avoid drawing attention to the button
// after the lightbox closes for mouse and touch users.
// Note that the `event.pointerType` property returns
// as an empty string if a keyboard fired the event.
if ( event.pointerType === '' ) {
context.core.image.lastFocusedElement.focus( {
preventScroll: true,
} );
}
}
},
handleKeydown: ( { context, actions, event } ) => {
Expand Down Expand Up @@ -191,11 +196,12 @@ store(
}
}
},
handleLoad: ( { state, context, effects, ref } ) => {
// This is fired just by lazily loaded
// images on the page, not all images.
handleLoad: ( { context, effects, ref } ) => {
context.core.image.imageLoaded = true;
context.core.image.imageCurrentSrc = ref.currentSrc;
effects.core.image.setButtonStyles( {
state,
context,
ref,
} );
Expand Down Expand Up @@ -258,17 +264,14 @@ store(
effects: {
core: {
image: {
setCurrentSrc: ( { context, ref } ) => {
initOriginImage: ( { context, ref } ) => {
context.core.image.imageRef = ref;
if ( ref.complete ) {
context.core.image.imageLoaded = true;
context.core.image.imageCurrentSrc = ref.currentSrc;
}
},
initLightbox: async ( { context, ref } ) => {
context.core.image.figureRef =
ref.querySelector( 'figure' );
context.core.image.imageRef =
ref.querySelector( 'img' );
if ( context.core.image.lightboxEnabled ) {
const focusableElements =
ref.querySelectorAll( focusableSelectors );
Expand All @@ -279,10 +282,17 @@ store(
focusableElements.length - 1
];

ref.querySelector( '.close-button' ).focus();
// We want to avoid drawing unnecessary attention to the close
// button for mouse and touch users. Note that even if opening
// the lightbox via keyboard, the event fired is of type
// `pointerEvent`, so we need to rely on the `event.pointerType`
// property, which returns an empty string for keyboard events.
if ( context.core.image.pointerType === '' ) {
ref.querySelector( '.close-button' ).focus();
}
}
},
setButtonStyles: ( { state, context, ref } ) => {
setButtonStyles: ( { context, ref } ) => {
const {
naturalWidth,
naturalHeight,
Expand All @@ -291,54 +301,71 @@ store(
} = ref;

// If the image isn't loaded yet, we can't
// calculate how big the button should be.
// calculate where the button should be.
if ( naturalWidth === 0 || naturalHeight === 0 ) {
return;
}

// Subscribe to the window dimensions so we can
// recalculate the styles if the window is resized.
if (
( state.core.image.windowWidth ||
state.core.image.windowHeight ) &&
context.core.image.scaleAttr === 'contain'
) {
// In the case of an image with object-fit: contain, the
// size of the img element can be larger than the image itself,
// so we need to calculate the size of the button to match.
const figure = ref.parentElement;
const figureWidth = ref.parentElement.clientWidth;

// We need special handling for the height because
// a caption will cause the figure to be taller than
// the image, which means we need to account for that
// when calculating the placement of the button in the
// top right corner of the image.
let figureHeight = ref.parentElement.clientHeight;
const caption = figure.querySelector( 'figcaption' );
if ( caption ) {
const captionComputedStyle =
window.getComputedStyle( caption );
figureHeight =
figureHeight -
caption.offsetHeight -
parseFloat( captionComputedStyle.marginTop ) -
parseFloat( captionComputedStyle.marginBottom );
}

const buttonOffsetTop = figureHeight - offsetHeight;
const buttonOffsetRight = figureWidth - offsetWidth;

// In the case of an image with object-fit: contain, the
// size of the <img> element can be larger than the image itself,
// so we need to calculate where to place the button.
if ( context.core.image.scaleAttr === 'contain' ) {
// Natural ratio of the image.
const naturalRatio = naturalWidth / naturalHeight;
// Offset ratio of the image.
const offsetRatio = offsetWidth / offsetHeight;

if ( naturalRatio > offsetRatio ) {
if ( naturalRatio >= offsetRatio ) {
// If it reaches the width first, keep
// the width and recalculate the height.
context.core.image.imageButtonWidth =
offsetWidth;
const buttonHeight = offsetWidth / naturalRatio;
context.core.image.imageButtonHeight =
buttonHeight;
// the width and compute the height.
const referenceHeight =
offsetWidth / naturalRatio;
context.core.image.imageButtonTop =
( offsetHeight - buttonHeight ) / 2;
( offsetHeight - referenceHeight ) / 2 +
buttonOffsetTop +
10;
context.core.image.imageButtonRight =
buttonOffsetRight + 10;
} else {
// If it reaches the height first, keep
// the height and recalculate the width.
context.core.image.imageButtonHeight =
offsetHeight;
const buttonWidth = offsetHeight * naturalRatio;
context.core.image.imageButtonWidth =
buttonWidth;
context.core.image.imageButtonLeft =
( offsetWidth - buttonWidth ) / 2;
// the height and compute the width.
const referenceWidth =
offsetHeight * naturalRatio;
context.core.image.imageButtonTop =
buttonOffsetTop + 10;
context.core.image.imageButtonRight =
( offsetWidth - referenceWidth ) / 2 +
buttonOffsetRight +
10;
}
} else {
// In all other cases, we can trust that the size of
// the image is the right size for the button as well.

context.core.image.imageButtonWidth = offsetWidth;
context.core.image.imageButtonHeight = offsetHeight;
context.core.image.imageButtonTop =
buttonOffsetTop + 10;
context.core.image.imageButtonRight =
buttonOffsetRight + 10;
}
},
setStylesOnResize: ( { state, context, ref } ) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/edit-widgets/src/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ body.js.widgets-php {

// Don't display admin notices on the Widgets screen.
// Needs !important because plugins may add inline styles to notices.
.widgets-php .notice {
.js .widgets-php .notice {
display: none !important;
}

Expand Down