diff --git a/assets/js/blocks/product-collection/constants.ts b/assets/js/blocks/product-collection/constants.ts index 5647db7c99d..96ecb190aa4 100644 --- a/assets/js/blocks/product-collection/constants.ts +++ b/assets/js/blocks/product-collection/constants.ts @@ -58,6 +58,7 @@ export const DEFAULT_ATTRIBUTES: Partial< ProductCollectionAttributes > = { displayLayout: { type: LayoutOptions.GRID, columns: 3, + shrinkColumns: false, }, }; diff --git a/assets/js/blocks/product-collection/inspector-controls/columns-control.tsx b/assets/js/blocks/product-collection/inspector-controls/columns-control.tsx index 87e99ddbf79..80dc78ac4c3 100644 --- a/assets/js/blocks/product-collection/inspector-controls/columns-control.tsx +++ b/assets/js/blocks/product-collection/inspector-controls/columns-control.tsx @@ -4,6 +4,7 @@ import { __ } from '@wordpress/i18n'; import { RangeControl, + ToggleControl, // @ts-expect-error Using experimental features // eslint-disable-next-line @wordpress/no-unsafe-wp-apis __experimentalToolsPanelItem as ToolsPanelItem, @@ -12,44 +13,86 @@ import { /** * Internal dependencies */ -import { DisplayLayoutControlProps } from '../types'; +import { DisplayLayoutToolbarProps } from '../types'; import { getDefaultDisplayLayout } from '../constants'; -const ColumnsControl = ( props: DisplayLayoutControlProps ) => { - const { type, columns } = props.displayLayout; +const toggleLabel = __( + 'Shrink columns to fit', + 'woo-gutenberg-products-block' +); + +const toggleHelp = __( + 'Reduce the number of columns to better fit smaller screens and spaces.', + 'woo-gutenberg-products-block' +); + +const getColumnsLabel = ( shrinkColumns: boolean ) => + shrinkColumns + ? __( 'Max Columns', 'woo-gutenberg-products-block' ) + : __( 'Columns', 'woo-gutenberg-products-block' ); + +const ColumnsControl = ( props: DisplayLayoutToolbarProps ) => { + const { type, columns, shrinkColumns } = props.displayLayout; const showColumnsControl = type === 'flex'; const defaultLayout = getDefaultDisplayLayout(); + const onShrinkColumnsToggleChange = ( value: boolean ) => { + props.setAttributes( { + displayLayout: { + ...props.displayLayout, + shrinkColumns: value, + }, + } ); + }; + + const onPanelDeselect = () => { + props.setAttributes( { + displayLayout: defaultLayout, + } ); + }; + + const onColumnsChange = ( value: number ) => + props.setAttributes( { + displayLayout: { + ...props.displayLayout, + columns: value, + }, + } ); + return showColumnsControl ? ( - - defaultLayout?.columns !== columns || - defaultLayout?.type !== type - } - isShownByDefault - onDeselect={ () => { - props.setAttributes( { - displayLayout: defaultLayout, - } ); - } } - > - - props.setAttributes( { - displayLayout: { - ...props.displayLayout, - columns: value, - }, - } ) + <> + + defaultLayout?.shrinkColumns !== shrinkColumns + } + isShownByDefault + onDeselect={ onPanelDeselect } + > + + + + defaultLayout?.columns !== columns || + defaultLayout?.type !== type } - min={ 2 } - max={ Math.max( 6, columns ) } - /> - + isShownByDefault + onDeselect={ onPanelDeselect } + > + + + ) : null; }; diff --git a/assets/js/blocks/product-collection/types.ts b/assets/js/blocks/product-collection/types.ts index 1b27dee172f..7d20d723fee 100644 --- a/assets/js/blocks/product-collection/types.ts +++ b/assets/js/blocks/product-collection/types.ts @@ -25,6 +25,7 @@ export enum LayoutOptions { export interface ProductCollectionDisplayLayout { type: LayoutOptions; columns: number; + shrinkColumns?: boolean; } export interface ProductCollectionQuery { diff --git a/assets/js/blocks/product-template/edit.tsx b/assets/js/blocks/product-template/edit.tsx index 2009d769ebc..7bcff3bdbb1 100644 --- a/assets/js/blocks/product-template/edit.tsx +++ b/assets/js/blocks/product-template/edit.tsx @@ -87,9 +87,10 @@ const ProductTemplateEdit = ( { }, queryContext = [ { page: 1 } ], templateSlug, - displayLayout: { type: layoutType, columns } = { + displayLayout: { type: layoutType, columns, shrinkColumns } = { type: 'flex', columns: 3, + shrinkColumns: false, }, }, __unstableLayoutClassNames, @@ -203,15 +204,21 @@ const ProductTemplateEdit = ( { } ) ), [ products ] ); + const hasLayoutFlex = layoutType === 'flex' && columns > 1; + let customClassName = ''; + if ( hasLayoutFlex ) { + const dynamicGrid = `wc-block-product-template__responsive columns-${ columns }`; + const staticGrid = `is-flex-container columns-${ columns }`; + + customClassName = shrinkColumns ? dynamicGrid : staticGrid; + } + const blockProps = useBlockProps( { className: classnames( __unstableLayoutClassNames, 'wc-block-product-template', - { - 'is-flex-container': hasLayoutFlex, - [ `columns-${ columns }` ]: hasLayoutFlex, - } + customClassName ), } ); diff --git a/assets/js/blocks/product-template/style.scss b/assets/js/blocks/product-template/style.scss index 69459b35793..7cdba7a05db 100644 --- a/assets/js/blocks/product-template/style.scss +++ b/assets/js/blocks/product-template/style.scss @@ -1,5 +1,8 @@ $break-small: 600px; +$grid-gap: 1.25em; +$min-product-width: 150px; + @mixin break-small() { @media (min-width: #{ ($break-small) }) { @content; @@ -39,6 +42,25 @@ $break-small: 600px; } } } + + &__responsive { + display: grid; + grid-gap: $grid-gap; + + @for $i from 2 through 6 { + $gap-count: calc(#{ $i } - 1); + $total-gap-width: calc(#{ $gap-count } * #{ $grid-gap }); + $max-product-width: calc((100% - #{ $total-gap-width }) / #{ $i }); + + &.columns-#{ $i } { + grid-template-columns: repeat(auto-fill, minmax(max(#{ $min-product-width }, #{ $max-product-width }), 1fr)); + } + } + + > li { + margin-block-start: 0; + } + } } /** diff --git a/src/BlockTypes/ProductTemplate.php b/src/BlockTypes/ProductTemplate.php index 6ed7b0198a3..c5885077917 100644 --- a/src/BlockTypes/ProductTemplate.php +++ b/src/BlockTypes/ProductTemplate.php @@ -61,7 +61,11 @@ protected function render( $attributes, $content, $block ) { $classnames = ''; if ( isset( $block->context['displayLayout'] ) && isset( $block->context['query'] ) ) { if ( isset( $block->context['displayLayout']['type'] ) && 'flex' === $block->context['displayLayout']['type'] ) { - $classnames = "is-flex-container columns-{$block->context['displayLayout']['columns']}"; + if ( $block->context['displayLayout']['shrinkColumns'] ) { + $classnames = "wc-block-product-template__responsive columns-{$block->context['displayLayout']['columns']}"; + } else { + $classnames = "is-flex-container columns-{$block->context['displayLayout']['columns']}"; + } } } if ( isset( $attributes['style']['elements']['link']['color']['text'] ) ) {