From 678a25ffa1c16b916ae2814281293c50afa8dacd Mon Sep 17 00:00:00 2001 From: Luigi Date: Tue, 3 Oct 2023 14:56:04 +0200 Subject: [PATCH 01/12] Add slide animation --- .../product-gallery-large-image/frontend.tsx | 37 +++++++++++++++---- assets/js/blocks/product-gallery/style.scss | 34 ++++++++++++++--- src/BlockTypes/ProductGalleryLargeImage.php | 16 ++++---- 3 files changed, 67 insertions(+), 20 deletions(-) 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..416af95c9ac 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,6 +3,11 @@ */ import { store as interactivityStore } from '@woocommerce/interactivity'; +/** + * Internal dependencies + */ +import { ProductGallerySelectors } from '../../frontend'; + type Context = { woocommerce: { styles: { @@ -17,19 +22,24 @@ type Context = { type Store = { context: Context; - selectors: typeof productButtonSelectors; + selectors: typeof productButtonSelectors & ProductGallerySelectors; ref: HTMLElement; }; const productButtonSelectors = { 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; + }, + '' + ); + }, }, }, }; @@ -78,5 +88,18 @@ interactivityStore( }, }, }, + effects: { + woocommerce: { + scrollInto: ( store: Store ) => { + if ( store.selectors?.woocommerce?.isSelected( store ) ) { + store.ref.scrollIntoView( { + behavior: 'smooth', + block: 'nearest', + inline: 'center', + } ); + } + }, + }, + }, } ); diff --git a/assets/js/blocks/product-gallery/style.scss b/assets/js/blocks/product-gallery/style.scss index 126b943eb1e..fb27dbbba69 100644 --- a/assets/js/blocks/product-gallery/style.scss +++ b/assets/js/blocks/product-gallery/style.scss @@ -5,6 +5,8 @@ $next-previous: "#{$large-image}-next-previous"; $next-previous-left-off: "#{$next-previous}-left--off"; $next-previous-right-off: "#{$next-previous}-right--off"; $gallery-next-previous-outside-image: "#{$gallery}[data-next-previous-buttons-position='outsideTheImage']:not(.is-single-product-gallery-image)"; +$gallery-next-previous-inside-image: "#{$gallery}:not([data-next-previous-buttons-position='outsideTheImage']:not(.is-single-product-gallery-image))"; + $outside-image-offset: 30px; $outside-image-max-width: calc(100% - (2 * $outside-image-offset)); @@ -57,8 +59,28 @@ $outside-image-max-width: calc(100% - (2 * $outside-image-offset)); // 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 { overflow: hidden; - max-width: $outside-image-max-width; margin: 0 auto; + max-width: $outside-image-max-width; + margin-left: 30px; + } + + .wc-block-woocommerce-product-gallery-large-image__container { + flex-shrink: 0; + max-width: 100%; + overflow: hidden; + } + + .wc-block-product-gallery-large-image__container { + display: flex; + overflow-x: hidden; + scroll-snap-type: x mandatory; + scroll-behavior: smooth; + } + + #{$gallery-next-previous-inside-image} & .wc-block-woocommerce-product-gallery-large-image__container { + max-width: 100%; + overflow: hidden; + margin: unset; } img { @@ -76,10 +98,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 { @@ -95,6 +113,12 @@ $outside-image-max-width: calc(100% - (2 * $outside-image-offset)); margin-left: $outside-image-offset; margin-right: $outside-image-offset; } + + // Reset the margin of the inner blocks when the Next/Previous buttons are outside the image. + #{$gallery-next-previous-inside-image} & > * { + margin-left: unset; + margin-right: unset; + } } } diff --git a/src/BlockTypes/ProductGalleryLargeImage.php b/src/BlockTypes/ProductGalleryLargeImage.php index 31062e91c90..5536541bb0d 100644 --- a/src/BlockTypes/ProductGalleryLargeImage.php +++ b/src/BlockTypes/ProductGalleryLargeImage.php @@ -87,11 +87,11 @@ protected function render( $attributes, $content, $block ) { return strtr( ' diff --git a/assets/js/atomic/blocks/product-elements/image/types.ts b/assets/js/atomic/blocks/product-elements/image/types.ts index 9b56e165ed9..ccd88e86f43 100644 --- a/assets/js/atomic/blocks/product-elements/image/types.ts +++ b/assets/js/atomic/blocks/product-elements/image/types.ts @@ -24,4 +24,6 @@ export interface BlockAttributes { width?: string; // Image scaling method. scale: 'cover' | 'contain' | 'fill'; + // Aspect ratio of the image. + aspectRatio: string; } diff --git a/patterns/product-collection-3-columns.php b/patterns/product-collection-3-columns.php index f0a438a8fd2..d50e3bcc7b6 100644 --- a/patterns/product-collection-3-columns.php +++ b/patterns/product-collection-3-columns.php @@ -18,7 +18,7 @@
- + @@ -26,20 +26,6 @@ - - - - - - - - - - - -

- -
diff --git a/patterns/product-collection-4-columns.php b/patterns/product-collection-4-columns.php index 46179d523b2..78a6be333cd 100644 --- a/patterns/product-collection-4-columns.php +++ b/patterns/product-collection-4-columns.php @@ -18,7 +18,7 @@
- + @@ -26,20 +26,6 @@ - - - - - - - - - - - -

- -
diff --git a/patterns/product-collection-5-columns.php b/patterns/product-collection-5-columns.php index 3b3dd2abdf7..04173211c3f 100644 --- a/patterns/product-collection-5-columns.php +++ b/patterns/product-collection-5-columns.php @@ -18,7 +18,7 @@
- +
@@ -36,20 +36,6 @@
- - - - - - - - - - - -

- -
diff --git a/patterns/product-query-product-gallery.php b/patterns/product-query-product-gallery.php index a5c692cd404..f0fcda2cebc 100644 --- a/patterns/product-query-product-gallery.php +++ b/patterns/product-query-product-gallery.php @@ -19,7 +19,7 @@
- + @@ -29,20 +29,6 @@ - - - - - - - - - - - -

- -
diff --git a/src/BlockTypes/ProductImage.php b/src/BlockTypes/ProductImage.php index cf28af2cf4e..445111ee16d 100644 --- a/src/BlockTypes/ProductImage.php +++ b/src/BlockTypes/ProductImage.php @@ -158,6 +158,9 @@ private function render_image( $product, $attributes ) { if ( ! empty( $attributes['scale'] ) ) { $image_style .= sprintf( 'object-fit:%s;', $attributes['scale'] ); } + if ( ! empty( $attributes['aspectRatio'] ) ) { + $image_style .= sprintf( 'aspect-ratio:%s;', $attributes['aspectRatio'] ); + } if ( ! $product->get_image_id() ) { // The alt text is left empty on purpose, as it's considered a decorative image. From ea0b1f59c7c454715616681e9ad098398669e361 Mon Sep 17 00:00:00 2001 From: Luigi Date: Fri, 6 Oct 2023 13:57:08 +0200 Subject: [PATCH 03/12] improve animation --- .../product-gallery-large-image/frontend.tsx | 45 ++++++++++++++++++- assets/js/blocks/product-gallery/style.scss | 2 +- 2 files changed, 45 insertions(+), 2 deletions(-) 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 416af95c9ac..0a6c91ee6b4 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 @@ -44,6 +44,8 @@ const productButtonSelectors = { }, }; +let isDialogStatusChanged = false; + interactivityStore( // @ts-expect-error: Store function isn't typed. { @@ -91,13 +93,54 @@ interactivityStore( effects: { woocommerce: { scrollInto: ( store: Store ) => { - if ( store.selectors?.woocommerce?.isSelected( store ) ) { + // Scroll to the selected image with a smooth animation. + if ( + store.selectors?.woocommerce?.isSelected( store ) && + 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.selectors?.woocommerce?.isSelected( store ) && + store.ref.tagName === 'IMG' && + 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.selectors?.woocommerce?.isSelected( store ) && + store.ref.tagName === 'IMG' + ) { + 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 fb27dbbba69..fd39ed2fc77 100644 --- a/assets/js/blocks/product-gallery/style.scss +++ b/assets/js/blocks/product-gallery/style.scss @@ -74,7 +74,7 @@ $outside-image-max-width: calc(100% - (2 * $outside-image-offset)); display: flex; overflow-x: hidden; scroll-snap-type: x mandatory; - scroll-behavior: smooth; + scroll-behavior: auto; } #{$gallery-next-previous-inside-image} & .wc-block-woocommerce-product-gallery-large-image__container { From c7fb03288acab070a3767480b196d6fda510ae09 Mon Sep 17 00:00:00 2001 From: Luigi Date: Fri, 6 Oct 2023 14:03:20 +0200 Subject: [PATCH 04/12] improve naming --- assets/js/blocks/product-gallery/frontend.tsx | 2 +- .../inner-blocks/product-gallery-large-image/frontend.tsx | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/assets/js/blocks/product-gallery/frontend.tsx b/assets/js/blocks/product-gallery/frontend.tsx index 93af417a031..9229af9c841 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; 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 0a6c91ee6b4..55c4519a13b 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 @@ -22,11 +22,12 @@ type Context = { type Store = { context: Context; - selectors: typeof productButtonSelectors & ProductGallerySelectors; + selectors: typeof productGalleryLargeImageSelectors & + ProductGallerySelectors; ref: HTMLElement; }; -const productButtonSelectors = { +const productGalleryLargeImageSelectors = { woocommerce: { productGalleryLargeImage: { styles: ( { context }: Store ) => { From aace907540079aef8d9899b2c537963e8fda48e5 Mon Sep 17 00:00:00 2001 From: Luigi Date: Fri, 6 Oct 2023 17:42:45 +0200 Subject: [PATCH 05/12] fix regression --- assets/js/blocks/product-gallery/frontend.tsx | 2 +- .../inner-blocks/product-gallery-large-image/frontend.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/js/blocks/product-gallery/frontend.tsx b/assets/js/blocks/product-gallery/frontend.tsx index 9229af9c841..11f95696ed1 100644 --- a/assets/js/blocks/product-gallery/frontend.tsx +++ b/assets/js/blocks/product-gallery/frontend.tsx @@ -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 55c4519a13b..9e8f02ba674 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 @@ -50,7 +50,7 @@ let isDialogStatusChanged = false; interactivityStore( // @ts-expect-error: Store function isn't typed. { - selectors: productButtonSelectors, + selectors: productGalleryLargeImageSelectors, actions: { woocommerce: { handleMouseMove: ( { From 69daeaeb81981e091170f87eb1fa014357c952e1 Mon Sep 17 00:00:00 2001 From: Luigi Date: Wed, 11 Oct 2023 15:12:56 +0200 Subject: [PATCH 06/12] fix css --- assets/js/blocks/product-gallery/style.scss | 11 ++++++----- src/BlockTypes/ProductGalleryLargeImage.php | 17 +++++++++-------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/assets/js/blocks/product-gallery/style.scss b/assets/js/blocks/product-gallery/style.scss index a5e754194d1..459eaeb4170 100644 --- a/assets/js/blocks/product-gallery/style.scss +++ b/assets/js/blocks/product-gallery/style.scss @@ -57,17 +57,17 @@ $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; margin: 0 auto; max-width: $outside-image-max-width; - margin-left: 30px; } - .wc-block-woocommerce-product-gallery-large-image__container { + .wc-block-product-gallery-large-image__wrapper { flex-shrink: 0; max-width: 100%; overflow: hidden; + width: 100%; } .wc-block-product-gallery-large-image__container { @@ -77,13 +77,13 @@ $outside-image-max-width: calc(100% - (2 * $outside-image-offset)); scroll-behavior: auto; } - #{$gallery-next-previous-inside-image} & .wc-block-woocommerce-product-gallery-large-image__container { + #{$gallery-next-previous-inside-image} & .wc-block-product-gallery-large-image__image-element { max-width: 100%; overflow: hidden; margin: unset; } - #{$gallery-next-previous-inside-image} & .wc-block-woocommerce-product-gallery-large-image__container { + #{$gallery-next-previous-inside-image} & .wc-block-product-gallery-large-image__image-element { overflow: unset; max-width: unset; margin: unset; @@ -95,6 +95,7 @@ $outside-image-max-width: calc(100% - (2 * $outside-image-offset)); margin: 0 auto; z-index: 1; transition: all 0.1s linear; + width: 100%; // 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 { diff --git a/src/BlockTypes/ProductGalleryLargeImage.php b/src/BlockTypes/ProductGalleryLargeImage.php index 5536541bb0d..8b126b83e1e 100644 --- a/src/BlockTypes/ProductGalleryLargeImage.php +++ b/src/BlockTypes/ProductGalleryLargeImage.php @@ -88,7 +88,6 @@ protected function render( $attributes, $content, $block ) { return strtr( '