Skip to content

Commit

Permalink
Image lightbox: move image data from context to state (WordPress#63348)
Browse files Browse the repository at this point in the history
* move the image context data to state

* move image and button refs from context to state

* Use hash instead of counter for the unique id

* Don't use immutability

* Fix reading prop from old context

* Only store the current image ID in the state

* Replace hash based on url with PHP's uniqid()

* fix lightbox trigger image position

---------

Co-authored-by: madhusudhand <madhudollu@git.wordpress.org>
Co-authored-by: luisherranz <luisherranz@git.wordpress.org>
Co-authored-by: mikachan <mikachan@git.wordpress.org>
Co-authored-by: SantosGuillamot <santosguillamot@git.wordpress.org>
  • Loading branch information
5 people authored and carstingaxion committed Jul 18, 2024
1 parent 513aaec commit 06ba49e
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 39 deletions.
39 changes: 27 additions & 12 deletions packages/block-library/src/image/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -185,22 +185,37 @@ function block_core_image_render_lightbox( $block_content, $block ) {
$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(
'metadata' => array(
$unique_image_id => array(
'uploadedSrc' => $img_uploaded_src,
'figureClassNames' => $figure_class_names,
'figureStyles' => $figure_styles,
'imgClassNames' => $img_class_names,
'imgStyles' => $img_styles,
'targetWidth' => $img_width,
'targetHeight' => $img_height,
'scaleAttr' => $block['attrs']['scale'] ?? false,
'ariaLabel' => $aria_label,
'alt' => $alt,
),
),
)
);

$p->add_class( 'wp-lightbox-container' );
$p->set_attribute( 'data-wp-interactive', 'core/image' );
$p->set_attribute(
'data-wp-context',
wp_json_encode(
array(
'uploadedSrc' => $img_uploaded_src,
'figureClassNames' => $figure_class_names,
'figureStyles' => $figure_styles,
'imgClassNames' => $img_class_names,
'imgStyles' => $img_styles,
'targetWidth' => $img_width,
'targetHeight' => $img_height,
'scaleAttr' => $block['attrs']['scale'] ?? false,
'ariaLabel' => $aria_label,
'alt' => $alt,
'imageId' => $unique_image_id,
),
JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
)
Expand Down Expand Up @@ -231,8 +246,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="context.imageButtonRight"
data-wp-style--top="context.imageButtonTop"
data-wp-style--right="state.imageButtonRight"
data-wp-style--top="state.imageButtonTop"
>
<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
70 changes: 43 additions & 27 deletions packages/block-library/src/image/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@ const { state, actions, callbacks } = store(
'core/image',
{
state: {
currentImage: {},
currentImageId: null,
get currentImage() {
return state.metadata[ state.currentImageId ];
},
get overlayOpened() {
return state.currentImage.currentSrc;
return state.currentImageId !== null;
},
get roleAttribute() {
return state.overlayOpened ? 'dialog' : null;
Expand All @@ -48,31 +51,43 @@ const { state, actions, callbacks } = store(
) }; object-fit:cover;`
);
},
get imageButtonRight() {
const { imageId } = getContext();
return state.metadata[ imageId ].imageButtonRight;
},
get imageButtonTop() {
const { imageId } = getContext();
return state.metadata[ imageId ].imageButtonTop;
},
},
actions: {
showLightbox() {
const ctx = getContext();
const { imageId } = getContext();

// Bails out if the image has not loaded yet.
if ( ! ctx.imageRef?.complete ) {
if ( ! state.metadata[ imageId ].imageRef?.complete ) {
return;
}

// Stores the positons of the scroll to fix it until the overlay is
// Stores the positions of the scroll to fix it until the overlay is
// closed.
state.scrollTopReset = document.documentElement.scrollTop;
state.scrollLeftReset = document.documentElement.scrollLeft;

// Moves the information of the expaned image to the state.
ctx.currentSrc = ctx.imageRef.currentSrc;
state.currentImage = ctx;
// Sets the current expanded image in the state and enables the overlay.
state.currentImageId = imageId;
state.overlayEnabled = true;

// Computes the styles of the overlay for the animation.
callbacks.setOverlayStyles();
},
hideLightbox() {
if ( state.overlayEnabled ) {
// Starts the overlay closing animation. The showClosingAnimation
// class is used to avoid showing it on page load.
state.showClosingAnimation = true;
state.overlayEnabled = false;

// Waits until the close animation has completed before allowing a
// user to scroll again. The duration of this animation is defined in
// the `styles.scss` file, but in any case we should wait a few
Expand All @@ -86,14 +101,9 @@ const { state, actions, callbacks } = store(
preventScroll: true,
} );

// Resets the current image to mark the overlay as closed.
state.currentImage = {};
// Resets the current image id to mark the overlay as closed.
state.currentImageId = null;
}, 450 );

// Starts the overlay closing animation. The showClosingAnimation
// class is used to avoid showing it on page load.
state.showClosingAnimation = true;
state.overlayEnabled = false;
}
},
handleKeydown( event ) {
Expand Down Expand Up @@ -213,6 +223,7 @@ const { state, actions, callbacks } = store(
let containerMaxHeight = imgMaxHeight;
let containerWidth = imgMaxWidth;
let containerHeight = imgMaxHeight;

// Checks if the target image has a different ratio than the original
// one (thumbnail). Recalculates the width and height.
if ( naturalRatio.toFixed( 2 ) !== imgRatio.toFixed( 2 ) ) {
Expand Down Expand Up @@ -323,9 +334,11 @@ const { state, actions, callbacks } = store(
`;
},
setButtonStyles() {
const ctx = getContext();
const { imageId } = getContext();
const { ref } = getElement();
ctx.imageRef = ref;

state.metadata[ imageId ].imageRef = ref;
state.metadata[ imageId ].currentSrc = ref.currentSrc;

const {
naturalWidth,
Expand Down Expand Up @@ -368,10 +381,13 @@ const { state, actions, callbacks } = store(
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 ( ctx.scaleAttr === 'contain' ) {
if ( state.metadata[ imageId ].scaleAttr === 'contain' ) {
// Natural ratio of the image.
const naturalRatio = naturalWidth / naturalHeight;
// Offset ratio of the image.
Expand All @@ -381,25 +397,25 @@ const { state, actions, callbacks } = store(
// If it reaches the width first, it keeps the width and compute the
// height.
const referenceHeight = offsetWidth / naturalRatio;
ctx.imageButtonTop =
imageButtonTop =
( offsetHeight - referenceHeight ) / 2 +
buttonOffsetTop +
16;
ctx.imageButtonRight = buttonOffsetRight + 16;
imageButtonRight = buttonOffsetRight + 16;
} else {
// If it reaches the height first, it keeps the height and compute
// the width.
const referenceWidth = offsetHeight * naturalRatio;
ctx.imageButtonTop = buttonOffsetTop + 16;
ctx.imageButtonRight =
imageButtonTop = buttonOffsetTop + 16;
imageButtonRight =
( offsetWidth - referenceWidth ) / 2 +
buttonOffsetRight +
16;
}
} else {
ctx.imageButtonTop = buttonOffsetTop + 16;
ctx.imageButtonRight = buttonOffsetRight + 16;
}

state.metadata[ imageId ].imageButtonTop = imageButtonTop;
state.metadata[ imageId ].imageButtonRight = imageButtonRight;
},
setOverlayFocus() {
if ( state.overlayEnabled ) {
Expand All @@ -409,9 +425,9 @@ const { state, actions, callbacks } = store(
}
},
initTriggerButton() {
const ctx = getContext();
const { imageId } = getContext();
const { ref } = getElement();
ctx.buttonRef = ref;
state.metadata[ imageId ].buttonRef = ref;
},
},
},
Expand Down

0 comments on commit 06ba49e

Please sign in to comment.