Skip to content

Commit

Permalink
Use anchor positioning for image lightbox button
Browse files Browse the repository at this point in the history
  • Loading branch information
swissspidy committed Jan 28, 2025
1 parent 0a26fe6 commit 0a30dea
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 89 deletions.
14 changes: 8 additions & 6 deletions packages/block-library/src/image/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,14 +165,17 @@ function block_core_image_render_lightbox( $block_content, $block ) {
$img_height = $img_metadata['height'] ?? 'none';
}

// Create unique id and set the image metadata in the state.
$unique_image_id = uniqid();

$img_styles = "anchor-name: --wp-image-$unique_image_id;" . $img_styles;
$p->set_attribute( 'style', $img_styles );

// Figure.
$p->seek( 'figure' );
$figure_class_names = $p->get_attribute( 'class' );
$figure_styles = $p->get_attribute( 'style' );

// Create unique id and set the image metadata in the state.
$unique_image_id = uniqid();

wp_interactivity_state(
'core/image',
array(
Expand Down Expand Up @@ -209,7 +212,6 @@ function block_core_image_render_lightbox( $block_content, $block ) {
$p->next_tag( 'img' );
$p->set_attribute( 'data-wp-init', 'callbacks.setButtonStyles' );
$p->set_attribute( 'data-wp-on-async--load', 'callbacks.setButtonStyles' );
$p->set_attribute( 'data-wp-on-async-window--resize', 'callbacks.setButtonStyles' );
// Sets an event callback on the `img` because the `figure` element can also
// contain a caption, and we don't want to trigger the lightbox when the
// caption is clicked.
Expand All @@ -232,8 +234,8 @@ class="lightbox-trigger"
aria-label="' . esc_attr( $aria_label ) . '"
data-wp-init="callbacks.initTriggerButton"
data-wp-on-async--click="actions.showLightbox"
data-wp-style--right="state.imageButtonRight"
data-wp-style--top="state.imageButtonTop"
data-wp-style--bottom="state.imageButtonBottom"
data-wp-style--position-anchor="state.imageButtonAnchor"
>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
Expand Down
7 changes: 4 additions & 3 deletions packages/block-library/src/image/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@
}

button {
opacity: 0;
opacity: 1;
border: none;
background-color: rgb(90 90 90 / 25%);
backdrop-filter: blur($grid-unit-20) saturate(180%);
Expand All @@ -161,9 +161,10 @@
width: 20px;
height: 20px;
position: absolute;
/* stylelint-disable-next-line property-no-unknown -- stylelint not updated yet. */
position-area: top right;
left: -36px;
z-index: 100;
top: 16px;
right: 16px;
text-align: center;
padding: 0;
border-radius: 4px;
Expand Down
91 changes: 11 additions & 80 deletions packages/block-library/src/image/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,18 @@ const { state, actions, callbacks } = store(
`${ state.currentImage.imgStyles?.replace(
/;$/,
''
) }; object-fit:cover;`
) }; object-fit:cover; anchor-name: --wp-image-${
state.currentImageId
};`
);
},
get imageButtonRight() {
get imageButtonBottom() {
const { imageId } = getContext();
return state.metadata[ imageId ].imageButtonRight;
return state.metadata[ imageId ].imageButtonBottom;
},
get imageButtonTop() {
get imageButtonAnchor() {
const { imageId } = getContext();
return state.metadata[ imageId ].imageButtonTop;
return `--wp-image-${ imageId }`;
},
get isContentHidden() {
const ctx = getContext();
Expand Down Expand Up @@ -360,82 +362,11 @@ const { state, actions, callbacks } = store(
state.metadata[ imageId ].imageRef = ref;
state.metadata[ imageId ].currentSrc = ref.currentSrc;

const {
naturalWidth,
naturalHeight,
offsetWidth,
offsetHeight,
} = ref;

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

const figure = ref.parentElement;
const figureWidth = ref.parentElement.clientWidth;

// It needs special handling for the height because a caption will cause
// the figure to be taller than the image, which means it needs 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 );
if (
! [ 'absolute', 'fixed' ].includes(
captionComputedStyle.position
)
) {
figureHeight =
figureHeight -
caption.offsetHeight -
parseFloat( captionComputedStyle.marginTop ) -
parseFloat( captionComputedStyle.marginBottom );
}
}

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

let imageButtonTop = buttonOffsetTop + 16;
let imageButtonRight = buttonOffsetRight + 16;

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

if ( naturalRatio >= offsetRatio ) {
// If it reaches the width first, it keeps the width and compute the
// height.
const referenceHeight = offsetWidth / naturalRatio;
imageButtonTop =
( offsetHeight - referenceHeight ) / 2 +
buttonOffsetTop +
16;
imageButtonRight = 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 =
( offsetWidth - referenceWidth ) / 2 +
buttonOffsetRight +
16;
}
}
const imgHeight = ref.getAttribute( 'height' );

state.metadata[ imageId ].imageButtonTop = imageButtonTop;
state.metadata[ imageId ].imageButtonRight = imageButtonRight;
state.metadata[ imageId ].imageButtonBottom = imgHeight
? `calc((anchor-size(height) - ${ imgHeight }px) / -2 - 20px - 16px)`
: '-36px';
},
setOverlayFocus() {
if ( state.overlayEnabled ) {
Expand Down

0 comments on commit 0a30dea

Please sign in to comment.