diff --git a/lib/block-supports/dimensions.php b/lib/block-supports/dimensions.php index 2692b316d4db17..a93457347274b2 100644 --- a/lib/block-supports/dimensions.php +++ b/lib/block-supports/dimensions.php @@ -26,7 +26,6 @@ function gutenberg_register_dimensions_support( $block_type ) { } $has_dimensions_support = gutenberg_block_has_support( $block_type, array( '__experimentalDimensions' ), false ); - // Future block supports such as height & width will be added here. if ( $has_dimensions_support ) { $block_type->attributes['style'] = array( @@ -44,14 +43,35 @@ function gutenberg_register_dimensions_support( $block_type ) { * * @return array Block dimensions CSS classes and inline styles. */ -function gutenberg_apply_dimensions_support( $block_type, $block_attributes ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable +function gutenberg_apply_dimensions_support( $block_type, $block_attributes ) { if ( gutenberg_skip_dimensions_serialization( $block_type ) ) { return array(); } $styles = array(); - // Height support to be added in near future. + // Height. + $has_height_support = gutenberg_block_has_support( $block_type, array( '__experimentalDimensions', 'height' ), false ); + + if ( $has_height_support ) { + $height_value = _wp_array_get( $block_attributes, array( 'style', 'dimensions', 'height' ), null ); + + if ( null !== $height_value ) { + $styles[] = sprintf( 'height: %s;', $height_value ); + } + } + + // Minimum height. + $has_min_height_support = gutenberg_block_has_support( $block_type, array( '__experimentalDimensions', 'minHeight' ), false ); + + if ( $has_min_height_support ) { + $min_height_value = _wp_array_get( $block_attributes, array( 'style', 'dimensions', 'minHeight' ), null ); + + if ( null !== $min_height_value ) { + $styles[] = sprintf( 'min-height: %s;', $min_height_value ); + } + } + // Width support to be added in near future. return empty( $styles ) ? array() : array( 'style' => implode( ' ', $styles ) ); @@ -63,10 +83,11 @@ function gutenberg_apply_dimensions_support( $block_type, $block_attributes ) { * * @param WP_Block_type $block_type Block type. * - * @return boolean Whether to serialize spacing support styles & classes. + * @return boolean Whether to serialize dimensions support styles & classes. */ function gutenberg_skip_dimensions_serialization( $block_type ) { $dimensions_support = _wp_array_get( $block_type->supports, array( '__experimentalDimensions' ), false ); + return is_array( $dimensions_support ) && array_key_exists( '__experimentalSkipSerialization', $dimensions_support ) && $dimensions_support['__experimentalSkipSerialization']; diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index cf713529b4f70d..285a5133095d6d 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -60,6 +60,10 @@ class WP_Theme_JSON_Gutenberg { 'gradient' => null, 'text' => null, ), + 'dimensions' => array( + 'height' => null, + 'minHeight' => null, + ), 'spacing' => array( 'margin' => null, 'padding' => null, @@ -94,6 +98,10 @@ class WP_Theme_JSON_Gutenberg { 'palette' => null, ), 'custom' => null, + 'dimensions' => array( + 'height' => null, + 'minHeight' => null, + ), 'layout' => array( 'contentSize' => null, 'wideSize' => null, @@ -222,6 +230,7 @@ class WP_Theme_JSON_Gutenberg { 'font-size' => array( 'typography', 'fontSize' ), 'font-style' => array( 'typography', 'fontStyle' ), 'font-weight' => array( 'typography', 'fontWeight' ), + 'height' => array( 'dimensions', 'height' ), 'letter-spacing' => array( 'typography', 'letterSpacing' ), 'line-height' => array( 'typography', 'lineHeight' ), 'margin' => array( 'spacing', 'margin' ), @@ -229,6 +238,7 @@ class WP_Theme_JSON_Gutenberg { 'margin-right' => array( 'spacing', 'margin', 'right' ), 'margin-bottom' => array( 'spacing', 'margin', 'bottom' ), 'margin-left' => array( 'spacing', 'margin', 'left' ), + 'min-height' => array( 'dimensions', 'minHeight' ), 'padding' => array( 'spacing', 'padding' ), 'padding-top' => array( 'spacing', 'padding', 'top' ), 'padding-right' => array( 'spacing', 'padding', 'right' ), diff --git a/lib/theme.json b/lib/theme.json index 6b52969009beed..66c0f238d0ee2f 100644 --- a/lib/theme.json +++ b/lib/theme.json @@ -210,6 +210,10 @@ } ] }, + "dimensions": { + "height": false, + "minHeight": false + }, "spacing": { "customMargin": false, "customPadding": false, diff --git a/package-lock.json b/package-lock.json index 77a1fa1c1c7115..5e6609560bfb52 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16487,9 +16487,9 @@ } }, "@types/hammerjs": { - "version": "2.0.39", - "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.39.tgz", - "integrity": "sha512-lYR2Y/tV2ujpk/WyUc7S0VLI0a9hrtVIN9EwnrNo5oSEJI2cK2/XrgwOQmXLL3eTulOESvh9qP6si9+DWM9cOA==" + "version": "2.0.40", + "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.40.tgz", + "integrity": "sha512-VbjwR1fhsn2h2KXAY4oy1fm7dCxaKy0D+deTb8Ilc3Eo3rc5+5eA4rfYmZaHgNJKxVyI0f6WIXzO2zLkVmQPHA==" }, "@types/hast": { "version": "2.3.1", @@ -18930,7 +18930,7 @@ "jsdom-jscore-rn": "git+https://github.com/iamcco/jsdom-jscore-rn.git#a562f3d57c27c13e5bfc8cf82d496e69a3ba2800", "node-fetch": "^2.6.0", "react-native": "0.64.0", - "react-native-gesture-handler": "git+https://github.com/wordpress-mobile/react-native-gesture-handler.git#1.10.1-wp", + "react-native-gesture-handler": "git+https://github.com/wordpress-mobile/react-native-gesture-handler.git#1.10.1-wp-2", "react-native-get-random-values": "git+https://github.com/wordpress-mobile/react-native-get-random-values.git#v1.4.0-wp", "react-native-hr": "git+https://github.com/Riglerr/react-native-hr.git#2d01a5cf77212d100e8b99e0310cce5234f977b3", "react-native-hsv-color-picker": "git+https://github.com/wordpress-mobile/react-native-hsv-color-picker.git#v1.0.1-wp", @@ -30662,12 +30662,27 @@ } }, "color": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz", - "integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", "requires": { - "color-convert": "^1.9.1", - "color-string": "^1.5.4" + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + }, + "dependencies": { + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + } } }, "color-convert": { @@ -30684,9 +30699,9 @@ "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=" }, "color-string": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz", - "integrity": "sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.6.0.tgz", + "integrity": "sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA==", "requires": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" @@ -32278,7 +32293,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", - "dev": true, "requires": { "mdn-data": "2.0.14", "source-map": "^0.6.1" @@ -32287,8 +32301,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" } } }, @@ -46283,8 +46296,7 @@ "mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", - "dev": true + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" }, "mdurl": { "version": "1.0.1", @@ -47938,9 +47950,9 @@ "optional": true }, "nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==" + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", + "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==" }, "nanomatch": { "version": "1.2.13", @@ -53383,8 +53395,8 @@ } }, "react-native-gesture-handler": { - "version": "git+https://github.com/wordpress-mobile/react-native-gesture-handler.git#5282a8f3b5f4ef8439c9c60f90c99629658c4cc3", - "from": "git+https://github.com/wordpress-mobile/react-native-gesture-handler.git#1.10.1-wp", + "version": "git+https://github.com/wordpress-mobile/react-native-gesture-handler.git#0b284c3d26c2af4b99f9b631b9888df7e11502d0", + "from": "git+https://github.com/wordpress-mobile/react-native-gesture-handler.git#1.10.1-wp-2", "requires": { "@egjs/hammerjs": "^2.0.17", "fbjs": "^3.0.0", @@ -53523,15 +53535,6 @@ "nth-check": "^1.0.2" } }, - "css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", - "requires": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - } - }, "css-what": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", @@ -53546,11 +53549,6 @@ "domelementtype": "1" } }, - "mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" - }, "nth-check": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", @@ -53558,11 +53556,6 @@ "requires": { "boolbase": "~1.0.0" } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" } } }, diff --git a/packages/block-editor/src/hooks/dimensions.js b/packages/block-editor/src/hooks/dimensions.js index 697955670b497e..10639e65c3397a 100644 --- a/packages/block-editor/src/hooks/dimensions.js +++ b/packages/block-editor/src/hooks/dimensions.js @@ -13,6 +13,20 @@ import { getBlockSupport } from '@wordpress/blocks'; * Internal dependencies */ import InspectorControls from '../components/inspector-controls'; +import { + HeightEdit, + hasHeightSupport, + hasHeightValue, + resetHeight, + useIsHeightDisabled, +} from './height'; +import { + MinHeightEdit, + hasMinHeightSupport, + hasMinHeightValue, + resetMinHeight, + useIsMinHeightDisabled, +} from './min-height'; import { MarginEdit, hasMarginSupport, @@ -29,6 +43,7 @@ import { } from './padding'; import { cleanEmptyObject } from './utils'; +export const DIMENSIONS_SUPPORT_KEY = '__experimentalDimensions'; export const SPACING_SUPPORT_KEY = 'spacing'; export const ALL_SIDES = [ 'top', 'right', 'bottom', 'left' ]; export const AXIAL_SIDES = [ 'vertical', 'horizontal' ]; @@ -37,12 +52,13 @@ export const AXIAL_SIDES = [ 'vertical', 'horizontal' ]; * Inspector controls for dimensions support. * * @param {Object} props Block props. - * - * @return {WPElement} Inspector controls for spacing support features. + * @return {WPElement} Inspector controls for dimensions support features. */ export function DimensionsPanel( props ) { const isPaddingDisabled = useIsPaddingDisabled( props ); const isMarginDisabled = useIsMarginDisabled( props ); + const isHeightDisabled = useIsHeightDisabled( props ); + const isMinHeightDisabled = useIsMinHeightDisabled( props ); const isDisabled = useIsDimensionsDisabled( props ); const isSupported = hasDimensionsSupport( props.name ); @@ -50,6 +66,11 @@ export function DimensionsPanel( props ) { return null; } + const defaultDimensionsControls = getBlockSupport( props.name, [ + DIMENSIONS_SUPPORT_KEY, + '__experimentalDefaultControls', + ] ); + const defaultSpacingControls = getBlockSupport( props.name, [ SPACING_SUPPORT_KEY, '__experimentalDefaultControls', @@ -62,6 +83,11 @@ export function DimensionsPanel( props ) { props.setAttributes( { style: cleanEmptyObject( { ...style, + dimensions: { + ...style?.dimensions, + height: undefined, + minHeight: undefined, + }, spacing: { ...style?.spacing, margin: undefined, @@ -78,6 +104,30 @@ export function DimensionsPanel( props ) { header={ __( 'Dimensions' ) } resetAll={ resetAll } > + { ! isHeightDisabled && ( + hasHeightValue( props ) } + label={ __( 'Height' ) } + onDeselect={ () => resetHeight( props ) } + isShownByDefault={ defaultDimensionsControls?.height } + > + + + ) } + { ! isMinHeightDisabled && ( + hasMinHeightValue( props ) } + label={ __( 'Minimum height' ) } + onDeselect={ () => resetMinHeight( props ) } + isShownByDefault={ + defaultDimensionsControls?.minHeight + } + > + + + ) } { ! isPaddingDisabled && ( hasPaddingValue( props ) } @@ -115,21 +165,29 @@ export function hasDimensionsSupport( blockName ) { return false; } - return hasPaddingSupport( blockName ) || hasMarginSupport( blockName ); + return ( + hasHeightSupport( blockName ) || + hasMinHeightSupport( blockName ) || + hasPaddingSupport( blockName ) || + hasMarginSupport( blockName ) + ); } /** * Determines whether dimensions support has been disabled. * * @param {Object} props Block properties. - * - * @return {boolean} If spacing support is completely disabled. + * @return {boolean} If dimensions support is completely disabled. */ const useIsDimensionsDisabled = ( props = {} ) => { + const heightDisabled = useIsHeightDisabled( props ); + const minHeightDisabled = useIsMinHeightDisabled( props ); const paddingDisabled = useIsPaddingDisabled( props ); const marginDisabled = useIsMarginDisabled( props ); - return paddingDisabled && marginDisabled; + return ( + heightDisabled && minHeightDisabled && paddingDisabled && marginDisabled + ); }; /** diff --git a/packages/block-editor/src/hooks/height.js b/packages/block-editor/src/hooks/height.js new file mode 100644 index 00000000000000..a2c80bc4eea9b9 --- /dev/null +++ b/packages/block-editor/src/hooks/height.js @@ -0,0 +1,121 @@ +/** + * WordPress dependencies + */ +import { getBlockSupport } from '@wordpress/blocks'; +import { + __experimentalUseCustomUnits as useCustomUnits, + __experimentalUnitControl as UnitControl, +} from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import useSetting from '../components/use-setting'; +import { DIMENSIONS_SUPPORT_KEY } from './dimensions'; +import { cleanEmptyObject } from './utils'; + +/** + * Determines if there is height support. + * + * @param {string|Object} blockType Block name or Block Type object. + * @return {boolean} Whether there is support. + */ +export function hasHeightSupport( blockType ) { + const support = getBlockSupport( blockType, DIMENSIONS_SUPPORT_KEY ); + return !! ( true === support || support?.height ); +} + +/** + * Checks if there is a current value in the height block support attributes. + * + * @param {Object} props Block props. + * @return {boolean} Whether or not the block has a height value set. + */ +export function hasHeightValue( props ) { + return props.attributes.style?.dimensions?.height !== undefined; +} + +/** + * Resets the height block support attributes. This can be used when + * disabling the height support controls for a block via a progressive + * discovery panel. + * + * @param {Object} props Block props. + * @param {Object} props.attributes Block's attributes. + * @param {Object} props.setAttributes Function to set block's attributes. + */ +export function resetHeight( { attributes = {}, setAttributes } ) { + const { style } = attributes; + + setAttributes( { + style: cleanEmptyObject( { + ...style, + dimensions: { + ...style?.dimensions, + height: undefined, + }, + } ), + } ); +} + +/** + * Custom hook that checks if height controls have been disabled. + * + * @param {string} name The name of the block. + * @return {boolean} Whether height control is disabled. + */ +export function useIsHeightDisabled( { name: blockName } = {} ) { + const isDisabled = ! useSetting( 'dimensions.height' ); + return ! hasHeightSupport( blockName ) || isDisabled; +} + +/** + * Inspector control panel containing the height related configuration. + * + * @param {Object} props Block props. + * @return {WPElement} Edit component for height. + */ +export function HeightEdit( props ) { + const { + attributes: { style }, + setAttributes, + } = props; + + const units = useCustomUnits( { + availableUnits: useSetting( 'dimensions.units' ) || [ + '%', + 'px', + 'em', + 'rem', + 'vh', + 'vw', + ], + } ); + + if ( useIsHeightDisabled( props ) ) { + return null; + } + + const onChange = ( next ) => { + const newStyle = { + ...style, + dimensions: { + ...style?.dimensions, + height: next, + }, + }; + + setAttributes( { style: cleanEmptyObject( newStyle ) } ); + }; + + return ( + + ); +} diff --git a/packages/block-editor/src/hooks/min-height.js b/packages/block-editor/src/hooks/min-height.js new file mode 100644 index 00000000000000..6e57e23eb94d90 --- /dev/null +++ b/packages/block-editor/src/hooks/min-height.js @@ -0,0 +1,122 @@ +/** + * WordPress dependencies + */ +import { getBlockSupport } from '@wordpress/blocks'; +import { + __experimentalUseCustomUnits as useCustomUnits, + __experimentalUnitControl as UnitControl, +} from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import useSetting from '../components/use-setting'; +import { DIMENSIONS_SUPPORT_KEY } from './dimensions'; +import { cleanEmptyObject } from './utils'; + +/** + * Determines if there is min-height support. + * + * @param {string|Object} blockType Block name or Block Type object. + * @return {boolean} Whether there is support. + */ +export function hasMinHeightSupport( blockType ) { + const support = getBlockSupport( blockType, DIMENSIONS_SUPPORT_KEY ); + return !! ( true === support || support?.minHeight ); +} + +/** + * Checks if there is a current value in the min-height block support attributes. + * + * @param {Object} props Block props. + * @return {boolean} Whether or not the block has a min-height value set. + */ +export function hasMinHeightValue( props ) { + return props.attributes.style?.dimensions?.minHeight !== undefined; +} + +/** + * Resets the min-height block support attributes. This can be used when + * disabling the min-height support controls for a block via a progressive + * discovery panel. + * + * @param {Object} props Block props. + * @param {Object} props.attributes Block's attributes. + * @param {Object} props.setAttributes Function to set block's attributes. + */ +export function resetMinHeight( { attributes = {}, setAttributes } ) { + const { style } = attributes; + + setAttributes( { + style: cleanEmptyObject( { + ...style, + dimensions: { + ...style?.dimensions, + minHeight: undefined, + }, + } ), + } ); +} + +/** + * Custom hook that checks if min-height controls have been disabled. + * + * @param {Object} props Block props. + * @param {string} props.name The name of the block. + * @return {boolean} Whether min-height control is disabled. + */ +export function useIsMinHeightDisabled( { name: blockName } = {} ) { + const isDisabled = ! useSetting( 'dimensions.minHeight' ); + return ! hasMinHeightSupport( blockName ) || isDisabled; +} + +/** + * Inspector control panel containing the min-height related configuration. + * + * @param {Object} props Block props. + * @return {WPElement} Edit component for min-height. + */ +export function MinHeightEdit( props ) { + const { + attributes: { style }, + setAttributes, + } = props; + + const units = useCustomUnits( { + availableUnits: useSetting( 'dimensions.units' ) || [ + '%', + 'px', + 'em', + 'rem', + 'vh', + 'vw', + ], + } ); + + if ( useIsMinHeightDisabled( props ) ) { + return null; + } + + const onChange = ( next ) => { + const newStyle = { + ...style, + dimensions: { + ...style?.dimensions, + minHeight: next, + }, + }; + + setAttributes( { style: cleanEmptyObject( newStyle ) } ); + }; + + return ( + + ); +} diff --git a/packages/block-editor/src/hooks/style.js b/packages/block-editor/src/hooks/style.js index 87bf4685d50d42..2c3fff303ec402 100644 --- a/packages/block-editor/src/hooks/style.js +++ b/packages/block-editor/src/hooks/style.js @@ -37,13 +37,18 @@ import { TYPOGRAPHY_SUPPORT_KEY, TYPOGRAPHY_SUPPORT_KEYS, } from './typography'; -import { SPACING_SUPPORT_KEY, DimensionsPanel } from './dimensions'; +import { + DIMENSIONS_SUPPORT_KEY, + SPACING_SUPPORT_KEY, + DimensionsPanel, +} from './dimensions'; import useDisplayBlockControls from '../components/use-display-block-controls'; const styleSupportKeys = [ ...TYPOGRAPHY_SUPPORT_KEYS, BORDER_SUPPORT_KEY, COLOR_SUPPORT_KEY, + DIMENSIONS_SUPPORT_KEY, SPACING_SUPPORT_KEY, ]; @@ -152,8 +157,11 @@ const skipSerializationPaths = { [ `${ TYPOGRAPHY_SUPPORT_KEY }.__experimentalSkipSerialization` ]: [ TYPOGRAPHY_SUPPORT_KEY, ], + [ `${ DIMENSIONS_SUPPORT_KEY }.__experimentalSkipSerialization` ]: [ + DIMENSIONS_SUPPORT_KEY, + ], [ `${ SPACING_SUPPORT_KEY }.__experimentalSkipSerialization` ]: [ - 'spacing', + SPACING_SUPPORT_KEY, ], }; diff --git a/packages/block-editor/src/hooks/test/style.js b/packages/block-editor/src/hooks/test/style.js index 706d9a93ed0ba4..70f6e0c271e5b0 100644 --- a/packages/block-editor/src/hooks/test/style.js +++ b/packages/block-editor/src/hooks/test/style.js @@ -23,6 +23,10 @@ describe( 'getInlineStyles', () => { style: 'dotted', color: '#21759b', }, + dimensions: { + height: '500px', + minHeight: '200px', + }, spacing: { padding: { top: '10px' }, margin: { bottom: '15px' }, @@ -37,6 +41,8 @@ describe( 'getInlineStyles', () => { color: 'red', lineHeight: 1.5, fontSize: 10, + height: '500px', + minHeight: '200px', marginBottom: '15px', paddingTop: '10px', } ); diff --git a/packages/blocks/src/api/constants.js b/packages/blocks/src/api/constants.js index 18342aa9162565..5459558e804955 100644 --- a/packages/blocks/src/api/constants.js +++ b/packages/blocks/src/api/constants.js @@ -72,6 +72,10 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = { value: [ 'typography', 'fontWeight' ], support: [ 'typography', '__experimentalFontWeight' ], }, + height: { + value: [ 'dimensions', 'height' ], + support: [ '__experimentalDimensions', 'height' ], + }, lineHeight: { value: [ 'typography', 'lineHeight' ], support: [ 'typography', 'lineHeight' ], @@ -86,6 +90,10 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = { marginLeft: 'left', }, }, + minHeight: { + value: [ 'dimensions', 'minHeight' ], + support: [ '__experimentalDimensions', 'minHeight' ], + }, padding: { value: [ 'spacing', 'padding' ], support: [ 'spacing', 'padding' ], diff --git a/packages/edit-site/src/components/editor/global-styles-renderer.js b/packages/edit-site/src/components/editor/global-styles-renderer.js index fdec33c3fc0230..1c68b413955dfe 100644 --- a/packages/edit-site/src/components/editor/global-styles-renderer.js +++ b/packages/edit-site/src/components/editor/global-styles-renderer.js @@ -192,7 +192,13 @@ export const getNodesWithStyles = ( tree, blockSelectors ) => { const pickStyleKeys = ( treeToPickFrom ) => pickBy( treeToPickFrom, ( value, key ) => - [ 'border', 'color', 'spacing', 'typography' ].includes( key ) + [ + 'border', + 'color', + 'dimensions', + 'spacing', + 'typography', + ].includes( key ) ); // Top-level. diff --git a/packages/edit-site/src/components/sidebar/dimensions-panel.js b/packages/edit-site/src/components/sidebar/dimensions-panel.js index 05b4aadd2d5bf0..387cc379b6690a 100644 --- a/packages/edit-site/src/components/sidebar/dimensions-panel.js +++ b/packages/edit-site/src/components/sidebar/dimensions-panel.js @@ -7,6 +7,7 @@ import { __experimentalToolsPanelItem as ToolsPanelItem, __experimentalBoxControl as BoxControl, __experimentalUseCustomUnits as useCustomUnits, + __experimentalUnitControl as UnitControl, } from '@wordpress/components'; import { __experimentalUseCustomSides as useCustomSides } from '@wordpress/block-editor'; @@ -18,10 +19,24 @@ import { useSetting } from '../editor/utils'; const AXIAL_SIDES = [ 'horizontal', 'vertical' ]; export function useHasDimensionsPanel( context ) { + const hasHeight = useHasHeight( context ); + const hasMinHeight = useHasMinHeight( context ); const hasPadding = useHasPadding( context ); const hasMargin = useHasMargin( context ); - return hasPadding || hasMargin; + return hasHeight || hasMinHeight || hasPadding || hasMargin; +} + +function useHasHeight( { name, supports } ) { + const settings = useSetting( 'dimensions.height', name ); + + return settings && supports.includes( 'height' ); +} + +function useHasMinHeight( { name, supports } ) { + const settings = useSetting( 'dimensions.minHeight', name ); + + return settings && supports.includes( 'minHeight' ); } function useHasPadding( { name, supports } ) { @@ -76,6 +91,8 @@ function splitStyleValue( value ) { export default function DimensionsPanel( { context, getStyle, setStyle } ) { const { name } = context; + const showHeightControl = useHasHeight( context ); + const showMinHeightControl = useHasMinHeight( context ); const showPaddingControl = useHasPadding( context ); const showMarginControl = useHasMargin( context ); const units = useCustomUnits( { @@ -88,6 +105,19 @@ export default function DimensionsPanel( { context, getStyle, setStyle } ) { ], } ); + // Height. + const heightValue = getStyle( name, 'height' ); + const setHeightValue = ( next ) => setStyle( name, 'height', next ); + const resetHeightValue = () => setHeightValue( undefined ); + const hasHeightValue = () => !! heightValue; + + // Min height. + const minHeightValue = getStyle( name, 'minHeight' ); + const setMinHeightValue = ( next ) => setStyle( name, 'minHeight', next ); + const resetMinHeightValue = () => setMinHeightValue( undefined ); + const hasMinHeightValue = () => !! minHeightValue; + + // Padding. const paddingValues = splitStyleValue( getStyle( name, 'padding' ) ); const paddingSides = useCustomSides( name, 'padding' ); const isAxialPadding = @@ -102,6 +132,7 @@ export default function DimensionsPanel( { context, getStyle, setStyle } ) { const hasPaddingValue = () => paddingValues && Object.keys( paddingValues ).length; + // Margin. const marginValues = splitStyleValue( getStyle( name, 'margin' ) ); const marginSides = useCustomSides( name, 'margin' ); const isAxialMargin = @@ -117,6 +148,8 @@ export default function DimensionsPanel( { context, getStyle, setStyle } ) { marginValues && Object.keys( marginValues ).length; const resetAll = () => { + resetHeightValue(); + resetMinHeightValue(); resetPaddingValue(); resetMarginValue(); }; @@ -127,6 +160,40 @@ export default function DimensionsPanel( { context, getStyle, setStyle } ) { header={ __( 'Dimensions' ) } resetAll={ resetAll } > + { showHeightControl && ( + + + + ) } + { showMinHeightControl && ( + + + + ) } { showPaddingControl && ( array( 'core/group' => array( - 'border' => array( + 'border' => array( 'radius' => '10px', ), - 'elements' => array( + 'elements' => array( 'link' => array( 'color' => array( 'text' => '#111', ), ), ), - 'spacing' => array( + 'dimensions' => array( + 'height' => '200px', + 'minHeight' => '100px', + ), + 'spacing' => array( 'padding' => '24px', ), ), @@ -352,11 +356,11 @@ function test_get_stylesheet() { ); $this->assertEquals( - 'body{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}body{color: var(--wp--preset--color--grey);}.wp-site-blocks > * + * { margin-top: var( --wp--style--block-gap ); margin-bottom: 0; }a{background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}', + 'body{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}body{color: var(--wp--preset--color--grey);}.wp-site-blocks > * + * { margin-top: var( --wp--style--block-gap ); margin-bottom: 0; }a{background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;height: 200px;min-height: 100px;padding: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}', $theme_json->get_stylesheet() ); $this->assertEquals( - 'body{color: var(--wp--preset--color--grey);}.wp-site-blocks > * + * { margin-top: var( --wp--style--block-gap ); margin-bottom: 0; }a{background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;padding: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}', + 'body{color: var(--wp--preset--color--grey);}.wp-site-blocks > * + * { margin-top: var( --wp--style--block-gap ); margin-bottom: 0; }a{background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;height: 200px;min-height: 100px;padding: 24px;}.wp-block-group a{color: #111;}h1,h2,h3,h4,h5,h6{color: #123456;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a{background-color: #777;color: #555;}.wp-block-image{border-top-left-radius: 10px;border-bottom-right-radius: 1em;margin-bottom: 30px;}.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}', $theme_json->get_stylesheet( 'block_styles' ) ); $this->assertEquals(