From 333c7f222b69b389fc51fd6c82dc83b515f08e38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alba=20Rinc=C3=B3n?= Date: Mon, 3 Jul 2023 11:33:44 +0200 Subject: [PATCH] Add size settings to the `Product Image` block (#10034) * Add height & width setting to the image sidebar settings * Extract settings and add scale options * Add width settings * Apply settings on the frontend * Style placeholder image * Replace post featured image with product image * Allow the width to be wider than container * Fix image on top of other elements --- .../product-elements/image/attributes.ts | 10 ++ .../blocks/product-elements/image/block.tsx | 31 ++++- .../blocks/product-elements/image/edit.tsx | 20 ++- .../image/image-size-settings.tsx | 127 ++++++++++++++++++ .../blocks/product-elements/image/style.scss | 6 + .../blocks/product-elements/image/supports.ts | 1 - .../blocks/product-elements/image/types.ts | 6 + patterns/product-hero.php | 2 +- src/BlockTypes/ProductImage.php | 12 +- 9 files changed, 204 insertions(+), 11 deletions(-) create mode 100644 assets/js/atomic/blocks/product-elements/image/image-size-settings.tsx diff --git a/assets/js/atomic/blocks/product-elements/image/attributes.ts b/assets/js/atomic/blocks/product-elements/image/attributes.ts index a55d85c2610..601bd22f37e 100644 --- a/assets/js/atomic/blocks/product-elements/image/attributes.ts +++ b/assets/js/atomic/blocks/product-elements/image/attributes.ts @@ -37,6 +37,16 @@ export const blockAttributes: BlockAttributes = { type: 'boolean', default: false, }, + width: { + type: 'string', + }, + height: { + type: 'string', + }, + scale: { + type: 'string', + default: 'cover', + }, }; export default blockAttributes; diff --git a/assets/js/atomic/blocks/product-elements/image/block.tsx b/assets/js/atomic/blocks/product-elements/image/block.tsx index 280d5462f86..caf4a7a263b 100644 --- a/assets/js/atomic/blocks/product-elements/image/block.tsx +++ b/assets/js/atomic/blocks/product-elements/image/block.tsx @@ -21,9 +21,10 @@ import ProductSaleBadge from '../sale-badge/block'; import './style.scss'; import { BlockAttributes, ImageSizing } from './types'; -const ImagePlaceholder = (): JSX.Element => { +const ImagePlaceholder = ( props ): JSX.Element => { return ( { const { thumbnail, src, srcset, sizes, alt } = image || {}; const imageProps = { @@ -61,13 +68,23 @@ const Image = ( { ...( showFullSize && { src, srcSet: srcset, sizes } ), }; + const imageStyles: Record< string, string | undefined > = { + height, + width, + objectFit: scale, + }; + return ( <> { imageProps.src && ( /* eslint-disable-next-line jsx-a11y/alt-text */ - + ) } - { ! image && } + { ! image && } ); }; @@ -81,6 +98,9 @@ export const Block = ( props: Props ): JSX.Element | null => { showProductLink = true, showSaleBadge, saleBadgeAlign = 'right', + height, + width, + scale, ...restProps } = props; const styleProps = useStyleProps( props ); @@ -100,7 +120,6 @@ export const Block = ( props: Props ): JSX.Element | null => { }, styleProps.className ) } - style={ styleProps.style } > @@ -134,7 +153,6 @@ export const Block = ( props: Props ): JSX.Element | null => { }, styleProps.className ) } - style={ styleProps.style } > { !! showSaleBadge && ( @@ -148,6 +166,9 @@ export const Block = ( props: Props ): JSX.Element | null => { image={ image } loaded={ ! isLoading } showFullSize={ imageSizing !== ImageSizing.THUMBNAIL } + width={ width } + height={ height } + scale={ scale } /> diff --git a/assets/js/atomic/blocks/product-elements/image/edit.tsx b/assets/js/atomic/blocks/product-elements/image/edit.tsx index 03a5b6c76b6..e7f3122d57c 100644 --- a/assets/js/atomic/blocks/product-elements/image/edit.tsx +++ b/assets/js/atomic/blocks/product-elements/image/edit.tsx @@ -33,6 +33,7 @@ import { BLOCK_DESCRIPTION as description, } from './constants'; import { BlockAttributes, ImageSizing } from './types'; +import { ImageSizeSettings } from './image-size-settings'; type SaleBadgeAlignProps = 'left' | 'center' | 'right'; @@ -41,9 +42,16 @@ const Edit = ( { setAttributes, context, }: BlockEditProps< BlockAttributes > & { context: Context } ): JSX.Element => { - const { showProductLink, imageSizing, showSaleBadge, saleBadgeAlign } = - attributes; - const blockProps = useBlockProps(); + const { + showProductLink, + imageSizing, + showSaleBadge, + saleBadgeAlign, + width, + height, + scale, + } = attributes; + const blockProps = useBlockProps( { style: { width, height } } ); const isDescendentOfQueryLoop = Number.isFinite( context.queryId ); const isBlockThemeEnabled = getSettingWithCoercion( 'is_block_theme_enabled', @@ -59,6 +67,12 @@ const Edit = ( { return (
+ diff --git a/assets/js/atomic/blocks/product-elements/image/image-size-settings.tsx b/assets/js/atomic/blocks/product-elements/image/image-size-settings.tsx new file mode 100644 index 00000000000..22ade62c503 --- /dev/null +++ b/assets/js/atomic/blocks/product-elements/image/image-size-settings.tsx @@ -0,0 +1,127 @@ +/** + * External dependencies + */ +import { __ } from '@wordpress/i18n'; +import { BlockAttributes } from '@wordpress/blocks'; +import { + // @ts-expect-error Using experimental features + // eslint-disable-next-line @wordpress/no-unsafe-wp-apis + __experimentalToggleGroupControl as ToggleGroupControl, + // @ts-expect-error Using experimental features + // eslint-disable-next-line @wordpress/no-unsafe-wp-apis + __experimentalToggleGroupControlOption as ToggleGroupControlOption, + // @ts-expect-error Using experimental features + // eslint-disable-next-line @wordpress/no-unsafe-wp-apis + __experimentalToolsPanel as ToolsPanel, + // @ts-expect-error Using experimental features + // eslint-disable-next-line @wordpress/no-unsafe-wp-apis + __experimentalToolsPanelItem as ToolsPanelItem, + // @ts-expect-error Using experimental features + // eslint-disable-next-line @wordpress/no-unsafe-wp-apis + __experimentalUnitControl as UnitControl, +} from '@wordpress/components'; + +interface ImageSizeSettingProps { + scale: string; + width: string | undefined; + height: string | undefined; + setAttributes: ( attrs: BlockAttributes ) => void; +} + +const scaleHelp: Record< string, string > = { + cover: __( + 'Image is scaled and cropped to fill the entire space without being distorted.', + 'woo-gutenberg-products-block' + ), + contain: __( + 'Image is scaled to fill the space without clipping nor distorting.', + 'woo-gutenberg-products-block' + ), + fill: __( + 'Image will be stretched and distorted to completely fill the space.', + 'woo-gutenberg-products-block' + ), +}; + +export const ImageSizeSettings = ( { + scale, + width, + height, + setAttributes, +}: ImageSizeSettingProps ) => { + return ( + + { + setAttributes( { height: value } ); + } } + value={ height } + units={ [ + { + value: 'px', + label: 'px', + }, + ] } + /> + { + setAttributes( { width: value } ); + } } + value={ width } + units={ [ + { + value: 'px', + label: 'px', + }, + ] } + /> + { height && ( + true } + label={ __( 'Scale', 'woo-gutenberg-products-block' ) } + > + + setAttributes( { + scale: value, + } ) + } + isBlock + > + <> + + + + + + + ) } + + ); +}; diff --git a/assets/js/atomic/blocks/product-elements/image/style.scss b/assets/js/atomic/blocks/product-elements/image/style.scss index 5a6e789ccc2..6ab79d4bad2 100644 --- a/assets/js/atomic/blocks/product-elements/image/style.scss +++ b/assets/js/atomic/blocks/product-elements/image/style.scss @@ -60,4 +60,10 @@ .wc-block-components-product-image { margin: 0 0 $gap-small; + position: relative; + z-index: -1; +} + +.wc-block-product-image__tools-panel .components-input-control { + margin-bottom: 8px; } diff --git a/assets/js/atomic/blocks/product-elements/image/supports.ts b/assets/js/atomic/blocks/product-elements/image/supports.ts index c8f480973d9..27110fe30ef 100644 --- a/assets/js/atomic/blocks/product-elements/image/supports.ts +++ b/assets/js/atomic/blocks/product-elements/image/supports.ts @@ -24,7 +24,6 @@ export const supports = { spacing: { margin: true, padding: true, - __experimentalSkipSerialization: true, }, } ), __experimentalSelector: '.wc-block-components-product-image', diff --git a/assets/js/atomic/blocks/product-elements/image/types.ts b/assets/js/atomic/blocks/product-elements/image/types.ts index 38bdce15890..9b56e165ed9 100644 --- a/assets/js/atomic/blocks/product-elements/image/types.ts +++ b/assets/js/atomic/blocks/product-elements/image/types.ts @@ -18,4 +18,10 @@ export interface BlockAttributes { imageSizing: ImageSizing; // Whether or not be a children of Query Loop Block. isDescendentOfQueryLoop: boolean; + // Height of the image. + height?: string; + // Width of the image. + width?: string; + // Image scaling method. + scale: 'cover' | 'contain' | 'fill'; } diff --git a/patterns/product-hero.php b/patterns/product-hero.php index 095b9170f26..9fc61b06dd6 100644 --- a/patterns/product-hero.php +++ b/patterns/product-hero.php @@ -23,7 +23,7 @@