diff --git a/assets/js/blocks/product-gallery/frontend.tsx b/assets/js/blocks/product-gallery/frontend.tsx index 93af417a031..11f95696ed1 100644 --- a/assets/js/blocks/product-gallery/frontend.tsx +++ b/assets/js/blocks/product-gallery/frontend.tsx @@ -16,7 +16,7 @@ interface Context { }; } -interface Selectors { +export interface ProductGallerySelectors { woocommerce: { isSelected: ( store: unknown ) => boolean; pagerDotFillOpacity: ( store: SelectorsStore ) => number; @@ -36,7 +36,7 @@ interface Actions { interface Store { state: State; context: Context; - selectors: Selectors; + selectors: ProductGallerySelectors; actions: Actions; ref?: HTMLElement; } diff --git a/assets/js/blocks/product-gallery/inner-blocks/product-gallery-large-image/frontend.tsx b/assets/js/blocks/product-gallery/inner-blocks/product-gallery-large-image/frontend.tsx index b5d193b8207..c1d0b9140b0 100644 --- a/assets/js/blocks/product-gallery/inner-blocks/product-gallery-large-image/frontend.tsx +++ b/assets/js/blocks/product-gallery/inner-blocks/product-gallery-large-image/frontend.tsx @@ -3,41 +3,56 @@ */ import { store as interactivityStore } from '@woocommerce/interactivity'; +/** + * Internal dependencies + */ +import { ProductGallerySelectors } from '../../frontend'; + type Context = { woocommerce: { - styles: { - // eslint-disable-next-line @typescript-eslint/naming-convention - 'transform-origin': string; - transform: string; - transition: string; - }; + styles: + | { + // eslint-disable-next-line @typescript-eslint/naming-convention + 'transform-origin': string; + transform: string; + transition: string; + } + | undefined; isDialogOpen: boolean; }; }; type Store = { context: Context; - selectors: typeof productButtonSelectors; + selectors: typeof productGalleryLargeImageSelectors & + ProductGallerySelectors; ref: HTMLElement; }; -const productButtonSelectors = { +const productGalleryLargeImageSelectors = { woocommerce: { - styles: ( { context }: Store ) => { - const { styles } = context.woocommerce; + productGalleryLargeImage: { + styles: ( { context }: Store ) => { + const { styles } = context.woocommerce; - return Object.entries( styles ).reduce( ( acc, [ key, value ] ) => { - const style = `${ key }:${ value };`; - return acc.length > 0 ? `${ acc } ${ style }` : style; - }, '' ); + return Object.entries( styles ?? [] ).reduce( + ( acc, [ key, value ] ) => { + const style = `${ key }:${ value };`; + return acc.length > 0 ? `${ acc } ${ style }` : style; + }, + '' + ); + }, }, }, }; +let isDialogStatusChanged = false; + interactivityStore( // @ts-expect-error: Store function isn't typed. { - selectors: productButtonSelectors, + selectors: productGalleryLargeImageSelectors, actions: { woocommerce: { handleMouseMove: ( { @@ -78,5 +93,58 @@ interactivityStore( }, }, }, + effects: { + woocommerce: { + scrollInto: ( store: Store ) => { + if ( ! store.selectors.woocommerce.isSelected( store ) ) { + return; + } + + // Scroll to the selected image with a smooth animation. + if ( + store.context.woocommerce.isDialogOpen === + isDialogStatusChanged + ) { + store.ref.scrollIntoView( { + behavior: 'smooth', + block: 'nearest', + inline: 'center', + } ); + } + + // Scroll to the selected image when the dialog is being opened without an animation. + if ( + store.context.woocommerce.isDialogOpen && + store.context.woocommerce.isDialogOpen !== + isDialogStatusChanged && + store.ref.closest( 'dialog' ) + ) { + store.ref.scrollIntoView( { + behavior: 'instant', + block: 'nearest', + inline: 'center', + } ); + + isDialogStatusChanged = + store.context.woocommerce.isDialogOpen; + } + + // Scroll to the selected image when the dialog is being closed without an animation. + if ( + ! store.context.woocommerce.isDialogOpen && + store.context.woocommerce.isDialogOpen !== + isDialogStatusChanged + ) { + store.ref.scrollIntoView( { + behavior: 'instant', + block: 'nearest', + inline: 'center', + } ); + isDialogStatusChanged = + store.context.woocommerce.isDialogOpen; + } + }, + }, + }, } ); diff --git a/assets/js/blocks/product-gallery/style.scss b/assets/js/blocks/product-gallery/style.scss index 660cfed5c9a..3b485211f7b 100644 --- a/assets/js/blocks/product-gallery/style.scss +++ b/assets/js/blocks/product-gallery/style.scss @@ -57,24 +57,43 @@ $outside-image-max-width: calc(100% - (2 * $outside-image-offset)); overflow: hidden; // Restrict the width of the Large Image when the Next/Previous buttons are outside the image. - #{$gallery-next-previous-outside-image} & .wc-block-woocommerce-product-gallery-large-image__container { + #{$gallery-next-previous-outside-image} & .wc-block-product-gallery-large-image__image-element { overflow: hidden; - max-width: $outside-image-max-width; margin: 0 auto; + max-width: $outside-image-max-width; + } + + .wc-block-product-gallery-large-image__wrapper { + flex-shrink: 0; + max-width: 100%; + overflow: hidden; + width: 100%; } - #{$gallery-next-previous-inside-image} & .wc-block-woocommerce-product-gallery-large-image__container { - overflow: unset; - max-width: unset; - margin: unset; + .wc-block-product-gallery-large-image__container { + display: flex; + overflow-x: hidden; + scroll-snap-type: x mandatory; + width: fit-content; + height: fit-content; + scroll-behavior: auto; + align-items: center; } + #{$gallery-next-previous-inside-image} & .wc-block-product-gallery-large-image__image-element { + width: fit-content; + overflow: hidden; + margin: 0 auto; + } + + img { display: block; position: relative; margin: 0 auto; z-index: 1; transition: all 0.1s linear; + width: auto; // Keep the order in this way. The hoverZoom class should override the full-screen-on-click class when both are applied. &.wc-block-woocommerce-product-gallery-large-image__image--full-screen-on-click { @@ -84,10 +103,6 @@ $outside-image-max-width: calc(100% - (2 * $outside-image-offset)); &.wc-block-woocommerce-product-gallery-large-image__image--hoverZoom { cursor: zoom-in; } - - &[hidden] { - display: none; - } } .wc-block-product-gallery-large-image__inner-blocks { diff --git a/src/BlockTypes/ProductGalleryLargeImage.php b/src/BlockTypes/ProductGalleryLargeImage.php index 31062e91c90..8b126b83e1e 100644 --- a/src/BlockTypes/ProductGalleryLargeImage.php +++ b/src/BlockTypes/ProductGalleryLargeImage.php @@ -87,11 +87,10 @@ protected function render( $attributes, $content, $block ) { return strtr( '