diff --git a/assets/js/blocks/product-query/editor.scss b/assets/js/blocks/product-query/editor.scss deleted file mode 100644 index 91571d4e3cf..00000000000 --- a/assets/js/blocks/product-query/editor.scss +++ /dev/null @@ -1,4 +0,0 @@ -.woo-inherit-query-toggle { - grid-column-start: 1; - grid-column-end: 3; -} diff --git a/assets/js/blocks/product-query/inspector-controls.tsx b/assets/js/blocks/product-query/inspector-controls.tsx index 692bd60fb64..a91e480002b 100644 --- a/assets/js/blocks/product-query/inspector-controls.tsx +++ b/assets/js/blocks/product-query/inspector-controls.tsx @@ -1,9 +1,10 @@ /** * External dependencies */ -import { ElementType } from 'react'; +import React, { ElementType } from 'react'; +import { createPortal, useContext } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; -import { InspectorControls } from '@wordpress/block-editor'; +import { BlockList, InspectorControls } from '@wordpress/block-editor'; import { useSelect } from '@wordpress/data'; import { addFilter } from '@wordpress/hooks'; import { ProductQueryFeedbackPrompt } from '@woocommerce/editor-components/feedback-prompt'; @@ -38,8 +39,7 @@ import { } from './constants'; import { PopularPresets } from './inspector-controls/popular-presets'; import { AttributesFilter } from './inspector-controls/attributes-filter'; - -import './editor.scss'; +import { LayoutControls } from './inspector-controls/layout-controls'; const NAMESPACED_CONTROLS = ALL_PRODUCT_QUERY_CONTROLS.map( ( id ) => @@ -222,10 +222,29 @@ const ProductQueryControls = ( props: ProductQueryBlock ) => { export const withProductQueryControls = < T extends EditorBlock< T > >( BlockEdit: ElementType ) => ( props: ProductQueryBlock ) => { + const $rootContainer: HTMLElement = useContext( + BlockList.__unstableElementContext + ); + + const gap = props.attributes?.style?.spacing?.blockGap; + + const Styles = ( + + ); + const NewStyles = () => + $rootContainer ? createPortal( Styles, $rootContainer ) : null; return isWooQueryBlockVariation( props ) ? ( <> + + ) : ( diff --git a/assets/js/blocks/product-query/inspector-controls/layout-controls.tsx b/assets/js/blocks/product-query/inspector-controls/layout-controls.tsx new file mode 100644 index 00000000000..2676771e1e1 --- /dev/null +++ b/assets/js/blocks/product-query/inspector-controls/layout-controls.tsx @@ -0,0 +1,119 @@ +/* eslint-disable @wordpress/no-unsafe-wp-apis */ +/** + * External dependencies + */ +import { __ } from '@wordpress/i18n'; +import { + InspectorControls, + __experimentalSpacingSizesControl as SpacingSizesControl, +} from '@wordpress/block-editor'; +import { + RangeControl, + __experimentalUseCustomUnits as useCustomUnits, +} from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { ProductQueryBlock } from '../types'; +import { setQueryAttribute } from '../utils'; + +function parseCSSFromGapValue( + blockGap: Record< 'top' | 'right' | 'bottom' | 'left', string | undefined > +) { + return blockGap + ? Object.fromEntries( + Object.entries( blockGap ).map( ( [ key, value ] ) => { + const slug = value?.match( /var:preset\|spacing\|(.+)/ ); + const style = slug + ? `var(--wp--preset--spacing--${ slug[ 1 ] })` + : value; + + return [ key, style ]; + } ) + ) + : blockGap; +} + +function parseGapValueFromCSS( + blockGap: Record< 'top' | 'right' | 'bottom' | 'left', string | undefined > +) { + const res = blockGap + ? Object.fromEntries( + Object.entries( blockGap ).map( ( [ key, value ] ) => { + const slug = value?.match( + /var\(--wp--preset--spacing--(.+)\)/ + ); + + const style = slug + ? `var:preset|spacing|${ slug[ 1 ] }` + : value; + + return [ key, style ]; + } ) + ) + : blockGap; + + return res; +} + +export const LayoutControls = ( props: ProductQueryBlock ) => { + const gap = props.attributes?.style?.spacing?.blockGap; + const units = useCustomUnits( { + availableUnits: [ 'px', 'em', 'rem', 'vw' ], + } ); + + return ( + + { + if ( + ! perPage || + isNaN( perPage ) || + perPage < 1 || + perPage > 100 + ) { + return; + } + setQueryAttribute( props, { perPage } ); + } } + step="1" + value={ props.attributes.query.perPage } + isDragEnabled={ false } + /> +
+ + props.setAttributes( { + style: { + ...props.attributes?.style, + spacing: { + ...props.attributes?.style?.spacing, + blockGap: parseCSSFromGapValue( value ), + }, + }, + } ) + } + label={ __( + 'Block spacing', + 'woo-gutenberg-products-block' + ) } + units={ units } + allowReset={ true } + sides={ [ 'horizontal', 'vertical' ] } + splitOnAxis={ true } + /> +
+
+ ); +}; diff --git a/assets/js/blocks/product-query/style.scss b/assets/js/blocks/product-query/style.scss index 0c345569197..a5f545f9ccf 100644 --- a/assets/js/blocks/product-query/style.scss +++ b/assets/js/blocks/product-query/style.scss @@ -3,3 +3,19 @@ grid-column: 1 / -1; } } + +.wc-block-controls-container { + align-items: center; + display: grid; + grid-template-columns: auto 1fr auto; + grid-template-rows: 16px auto; + + .components-base-control { + margin-bottom: 0; + } +} + +.woo-inherit-query-toggle { + grid-column-start: 1; + grid-column-end: 3; +} diff --git a/assets/js/blocks/product-query/types.ts b/assets/js/blocks/product-query/types.ts index e8bc6128918..8c934fe5eeb 100644 --- a/assets/js/blocks/product-query/types.ts +++ b/assets/js/blocks/product-query/types.ts @@ -97,7 +97,7 @@ export interface QueryBlockQuery { orderBy: 'date' | 'relevance' | 'title'; pages?: number; parents?: number[]; - perPage?: number; + perPage: number; postType: string; search?: string; sticky?: string;