From 1b685cc500305a8c62bad6ec7b8798d31060d051 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Mon, 29 Mar 2021 17:58:11 +1000 Subject: [PATCH 1/2] Update spacing block support - Adds margin block support - Updates padding block support to allow customisable sides - Does not update global styles sidebar yet --- lib/class-wp-theme-json.php | 11 ++ .../components/spacing-panel-control/index.js | 5 +- packages/block-editor/src/hooks/margin.js | 101 ++++++++++++++++++ packages/block-editor/src/hooks/padding.js | 30 +++++- packages/block-editor/src/hooks/spacing.js | 88 +++++++++++++++ packages/block-editor/src/hooks/style.js | 18 +--- packages/block-editor/src/hooks/test/style.js | 6 ++ packages/blocks/src/api/constants.js | 5 + 8 files changed, 243 insertions(+), 21 deletions(-) create mode 100644 packages/block-editor/src/hooks/margin.js create mode 100644 packages/block-editor/src/hooks/spacing.js diff --git a/lib/class-wp-theme-json.php b/lib/class-wp-theme-json.php index 107ef3d3f6a757..0949b5d91f4c7b 100644 --- a/lib/class-wp-theme-json.php +++ b/lib/class-wp-theme-json.php @@ -119,6 +119,12 @@ class WP_Theme_JSON { 'text' => null, ), 'spacing' => array( + 'margin' => array( + 'top' => null, + 'right' => null, + 'bottom' => null, + 'left' => null, + ), 'padding' => array( 'top' => null, 'right' => null, @@ -309,6 +315,11 @@ class WP_Theme_JSON { 'value' => array( 'typography', 'lineHeight' ), 'support' => array( 'lineHeight' ), ), + 'margin' => array( + 'value' => array( 'spacing', 'margin' ), + 'support' => array( 'spacing', 'margin' ), + 'properties' => array( 'top', 'right', 'bottom', 'left' ), + ), 'padding' => array( 'value' => array( 'spacing', 'padding' ), 'support' => array( 'spacing', 'padding' ), diff --git a/packages/block-editor/src/components/spacing-panel-control/index.js b/packages/block-editor/src/components/spacing-panel-control/index.js index aafdbb94151d27..ae31709619d8c5 100644 --- a/packages/block-editor/src/components/spacing-panel-control/index.js +++ b/packages/block-editor/src/components/spacing-panel-control/index.js @@ -12,8 +12,11 @@ import useEditorFeature from '../use-editor-feature'; export default function SpacingPanelControl( { children, ...props } ) { const isSpacingEnabled = useEditorFeature( 'spacing.customPadding' ); + const isMarginEnabled = useEditorFeature( 'spacing.customMargin' ); - if ( ! isSpacingEnabled ) return null; + if ( ! isSpacingEnabled && ! isMarginEnabled ) { + return null; + } return ( diff --git a/packages/block-editor/src/hooks/margin.js b/packages/block-editor/src/hooks/margin.js new file mode 100644 index 00000000000000..8d43b0622d3034 --- /dev/null +++ b/packages/block-editor/src/hooks/margin.js @@ -0,0 +1,101 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { Platform } from '@wordpress/element'; +import { getBlockSupport } from '@wordpress/blocks'; +import { __experimentalBoxControl as BoxControl } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import useEditorFeature from '../components/use-editor-feature'; +import { SPACING_SUPPORT_KEY, useCustomSides } from './spacing'; +import { cleanEmptyObject } from './utils'; +import { useCustomUnits } from '../components/unit-control'; + +/** + * Determines if there is margin support. + * + * @param {string|Object} blockType Block name or Block Type object. + * @return {boolean} Whether there is support. + */ +const hasMarginSupport = ( blockType ) => { + const support = getBlockSupport( blockType, SPACING_SUPPORT_KEY ); + return !! ( true === support || support?.margin ); +}; + +/** + * Custom hook that checks if margin settings have been disabled. + * + * @param {string} name The name of the block. + * @return {boolean} Whether margin setting is disabled. + */ +export function useIsMarginDisabled( { name: blockName } = {} ) { + const isDisabled = ! useEditorFeature( 'spacing.customMargin' ); + return ! hasMarginSupport( blockName ) || isDisabled; +} + +/** + * Inspector control panel containing the margin related configuration + * + * @param {Object} props Block props. + * @return {WPElement} Margin edit element. + */ +export function MarginEdit( props ) { + const { + name: blockName, + attributes: { style }, + setAttributes, + } = props; + + const units = useCustomUnits(); + const sides = useCustomSides( blockName, 'margin' ); + + if ( ! hasMarginSupport( blockName ) ) { + return null; + } + + const onChange = ( next ) => { + const newStyle = { + ...style, + spacing: { + ...style?.spacing, + margin: next, + }, + }; + + setAttributes( { + style: cleanEmptyObject( newStyle ), + } ); + }; + + const onChangeShowVisualizer = ( next ) => { + const newStyle = { + ...style, + visualizers: { + margin: next, + }, + }; + + setAttributes( { + style: cleanEmptyObject( newStyle ), + } ); + }; + + return Platform.select( { + web: ( + <> + + + ), + native: null, + } ); +} diff --git a/packages/block-editor/src/hooks/padding.js b/packages/block-editor/src/hooks/padding.js index 7253ee0629b66d..f476a03ce3bfb2 100644 --- a/packages/block-editor/src/hooks/padding.js +++ b/packages/block-editor/src/hooks/padding.js @@ -9,16 +9,33 @@ import { __experimentalBoxControl as BoxControl } from '@wordpress/components'; /** * Internal dependencies */ +import useEditorFeature from '../components/use-editor-feature'; +import { SPACING_SUPPORT_KEY, useCustomSides } from './spacing'; import { cleanEmptyObject } from './utils'; import { useCustomUnits } from '../components/unit-control'; -export const SPACING_SUPPORT_KEY = 'spacing'; - -const hasPaddingSupport = ( blockName ) => { - const spacingSupport = getBlockSupport( blockName, SPACING_SUPPORT_KEY ); - return spacingSupport && spacingSupport.padding !== false; +/** + * Determines if there is padding support. + * + * @param {string|Object} blockType Block name or Block Type object. + * @return {boolean} Whether there is support. + */ +const hasPaddingSupport = ( blockType ) => { + const support = getBlockSupport( blockType, SPACING_SUPPORT_KEY ); + return !! ( true === support || support?.padding ); }; +/** + * Custom hook that checks if padding settings have been disabled. + * + * @param {string} name The name of the block. + * @return {boolean} Whether padding setting is disabled. + */ +export function useIsPaddingDisabled( { name: blockName } = {} ) { + const isDisabled = ! useEditorFeature( 'spacing.customPadding' ); + return ! hasPaddingSupport( blockName ) || isDisabled; +} + /** * Inspector control panel containing the padding related configuration * @@ -34,6 +51,7 @@ export function PaddingEdit( props ) { } = props; const units = useCustomUnits(); + const sides = useCustomSides( blockName, 'padding' ); if ( ! hasPaddingSupport( blockName ) ) { return null; @@ -43,6 +61,7 @@ export function PaddingEdit( props ) { const newStyle = { ...style, spacing: { + ...style?.spacing, padding: next, }, }; @@ -73,6 +92,7 @@ export function PaddingEdit( props ) { onChange={ onChange } onChangeShowVisualizer={ onChangeShowVisualizer } label={ __( 'Padding' ) } + sides={ sides } units={ units } /> diff --git a/packages/block-editor/src/hooks/spacing.js b/packages/block-editor/src/hooks/spacing.js new file mode 100644 index 00000000000000..ddab0143e1fcad --- /dev/null +++ b/packages/block-editor/src/hooks/spacing.js @@ -0,0 +1,88 @@ +/** + * WordPress dependencies + */ +import { getBlockSupport } from '@wordpress/blocks'; +import { Platform } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { PaddingEdit, useIsPaddingDisabled } from './padding'; +import { MarginEdit, useIsMarginDisabled } from './margin'; +import SpacingPanelControl from '../components/spacing-panel-control'; + +export const SPACING_SUPPORT_KEY = 'spacing'; + +/** + * Inspector controls for spacing support. + * + * @param {Object} props Block props. + * @return {WPElement} Inspector controls for spacing support features. + */ +export function SpacingPanel( props ) { + const isDisabled = useIsSpacingDisabled( props ); + const isSupported = hasSpacingSupport( props.name ); + + if ( isDisabled || ! isSupported ) { + return null; + } + + return ( + + + + + ); +} + +/** + * Determine whether there is block support for padding or margins. + * + * @param {string} blockName Block name. + * @return {boolean} Whether there is support. + */ +export function hasSpacingSupport( blockName ) { + if ( Platform.OS !== 'web' ) { + return false; + } + + const support = getBlockSupport( blockName, SPACING_SUPPORT_KEY ); + + return !! ( true === support || support?.padding || support?.margin ); +} + +/** + * Determines whether spacing support has been disabled. + * + * @param {Object} props Block properties. + * @return {boolean} If spacing support is completely disabled. + */ +const useIsSpacingDisabled = ( props = {} ) => { + const paddingDisabled = useIsPaddingDisabled( props ); + const marginDisabled = useIsMarginDisabled( props ); + + return paddingDisabled && marginDisabled; +}; + +/** + * Custom hook to retrieve which padding/margin is supported + * e.g. top, right, bottom or left. + * + * Sides are opted into by default. It is only if a specific side is set to + * false that it is omitted. + * + * @param {string} blockName Block name. + * @param {string} feature The feature custom sides relate to e.g. padding or margins. + * @return {Object} Sides supporting custom margin. + */ +export function useCustomSides( blockName, feature ) { + const support = getBlockSupport( blockName, SPACING_SUPPORT_KEY ); + + // Return empty config when setting is boolean as theme isn't setting + // arbitrary sides. + if ( typeof support[ feature ] === 'boolean' ) { + return {}; + } + + return support[ feature ]; +} diff --git a/packages/block-editor/src/hooks/style.js b/packages/block-editor/src/hooks/style.js index 85b8247743b385..649b9d4851fe2d 100644 --- a/packages/block-editor/src/hooks/style.js +++ b/packages/block-editor/src/hooks/style.js @@ -18,9 +18,8 @@ import { createHigherOrderComponent } from '@wordpress/compose'; */ import { BORDER_SUPPORT_KEY, BorderPanel } from './border'; import { COLOR_SUPPORT_KEY, ColorEdit } from './color'; +import { SPACING_SUPPORT_KEY, SpacingPanel } from './spacing'; import { TypographyPanel, TYPOGRAPHY_SUPPORT_KEYS } from './typography'; -import { SPACING_SUPPORT_KEY, PaddingEdit } from './padding'; -import SpacingPanelControl from '../components/spacing-panel-control'; const styleSupportKeys = [ ...TYPOGRAPHY_SUPPORT_KEYS, @@ -119,7 +118,7 @@ export function addSaveProps( props, blockType, attributes ) { } /** - * Filters registered block settings to extand the block edit wrapper + * Filters registered block settings to extend the block edit wrapper * to apply the desired styles and classnames properly. * * @param {Object} settings Original block settings @@ -152,23 +151,12 @@ export function addEditProps( settings ) { */ export const withBlockControls = createHigherOrderComponent( ( BlockEdit ) => ( props ) => { - const { name: blockName } = props; - - const hasSpacingSupport = hasBlockSupport( - blockName, - SPACING_SUPPORT_KEY - ); - return [ , , , , - hasSpacingSupport && ( - - - - ), + , ]; }, 'withToolbarControls' diff --git a/packages/block-editor/src/hooks/test/style.js b/packages/block-editor/src/hooks/test/style.js index 62c5a97b6e211e..33db413dace0a5 100644 --- a/packages/block-editor/src/hooks/test/style.js +++ b/packages/block-editor/src/hooks/test/style.js @@ -18,6 +18,10 @@ describe( 'getInlineStyles', () => { color: { text: 'red', background: 'black' }, typography: { lineHeight: 1.5, fontSize: 10 }, border: { radius: 10 }, + spacing: { + padding: { top: '10px' }, + margin: { bottom: '15px' }, + }, } ) ).toEqual( { backgroundColor: 'black', @@ -25,6 +29,8 @@ describe( 'getInlineStyles', () => { color: 'red', lineHeight: 1.5, fontSize: 10, + marginBottom: '15px', + paddingTop: '10px', } ); } ); } ); diff --git a/packages/blocks/src/api/constants.js b/packages/blocks/src/api/constants.js index eb95932528aee4..22fc6c2537d2f8 100644 --- a/packages/blocks/src/api/constants.js +++ b/packages/blocks/src/api/constants.js @@ -53,6 +53,11 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = { value: [ 'typography', 'lineHeight' ], support: [ 'lineHeight' ], }, + margin: { + value: [ 'spacing', 'margin' ], + support: [ 'spacing', 'margin' ], + properties: [ 'top', 'right', 'bottom', 'left' ], + }, padding: { value: [ 'spacing', 'padding' ], support: [ 'spacing', 'padding' ], From 3730244dc0973200520a715312f16cf8ffd076a2 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Wed, 31 Mar 2021 14:06:49 +1000 Subject: [PATCH 2/2] Update global styles spacing panel - Fixes bug where BoxControl cannot be reset in Global Styles - Updates the spacing panel to include margins and custom sides --- lib/class-wp-theme-json.php | 5 +- packages/components/src/box-control/index.js | 16 ++- .../src/components/sidebar/spacing-panel.js | 100 ++++++++++++++---- 3 files changed, 95 insertions(+), 26 deletions(-) diff --git a/lib/class-wp-theme-json.php b/lib/class-wp-theme-json.php index 0949b5d91f4c7b..89e70f47b9b5dd 100644 --- a/lib/class-wp-theme-json.php +++ b/lib/class-wp-theme-json.php @@ -119,7 +119,7 @@ class WP_Theme_JSON { 'text' => null, ), 'spacing' => array( - 'margin' => array( + 'margin' => array( 'top' => null, 'right' => null, 'bottom' => null, @@ -158,6 +158,7 @@ class WP_Theme_JSON { ), 'spacing' => array( 'customPadding' => null, + 'customMargin' => null, 'units' => null, ), 'typography' => array( @@ -315,7 +316,7 @@ class WP_Theme_JSON { 'value' => array( 'typography', 'lineHeight' ), 'support' => array( 'lineHeight' ), ), - 'margin' => array( + 'margin' => array( 'value' => array( 'spacing', 'margin' ), 'support' => array( 'spacing', 'margin' ), 'properties' => array( 'top', 'right', 'bottom', 'left' ), diff --git a/packages/components/src/box-control/index.js b/packages/components/src/box-control/index.js index 6e1f8be2f81576..767756833faede 100644 --- a/packages/components/src/box-control/index.js +++ b/packages/components/src/box-control/index.js @@ -7,7 +7,7 @@ import { noop } from 'lodash'; * WordPress dependencies */ import { useInstanceId } from '@wordpress/compose'; -import { useState } from '@wordpress/element'; +import { useRef, useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; /** @@ -52,6 +52,7 @@ export default function BoxControl( { values: valuesProp, units, sides, + resetToInitialValues = false, } ) { const [ values, setValues ] = useControlledState( valuesProp, { fallback: DEFAULT_VALUES, @@ -59,6 +60,13 @@ export default function BoxControl( { const inputValues = values || DEFAULT_VALUES; const hasInitialValue = isValuesDefined( valuesProp ); + // Determine which values the reset button should apply. + // Global styles generally prefer to reset to the initial value likely + // coming from a theme. + const resetValues = useRef( + resetToInitialValues && valuesProp ? valuesProp : DEFAULT_VALUES + ); + const [ isDirty, setIsDirty ] = useState( hasInitialValue ); const [ isLinked, setIsLinked ] = useState( ! hasInitialValue || ! isValuesMixed( inputValues ) @@ -93,10 +101,8 @@ export default function BoxControl( { }; const handleOnReset = () => { - const initialValues = DEFAULT_VALUES; - - onChange( initialValues ); - setValues( initialValues ); + onChange( resetValues.current ); + setValues( resetValues.current ); setIsDirty( false ); }; diff --git a/packages/edit-site/src/components/sidebar/spacing-panel.js b/packages/edit-site/src/components/sidebar/spacing-panel.js index f2609b49c4696f..7baf8277423c31 100644 --- a/packages/edit-site/src/components/sidebar/spacing-panel.js +++ b/packages/edit-site/src/components/sidebar/spacing-panel.js @@ -6,19 +6,33 @@ import { __experimentalBoxControl as BoxControl, PanelBody, } from '@wordpress/components'; +import { getBlockSupport } from '@wordpress/blocks'; /** * Internal dependencies */ import { useEditorFeature } from '../editor/utils'; -export function useHasSpacingPanel( { supports, name } ) { +export function useHasSpacingPanel( context ) { + const hasPadding = useHasPadding( context ); + const hasMargin = useHasMargin( context ); + + return hasPadding || hasMargin; +} + +function useHasPadding( { name, supports } ) { return ( useEditorFeature( 'spacing.customPadding', name ) && supports.includes( 'padding' ) ); } +function useHasMargin( { name, supports } ) { + const settings = useEditorFeature( 'spacing.customMargin', name ); + + return settings && supports.includes( 'margin' ); +} + function filterUnitsWithSettings( settings = [], units = [] ) { return units.filter( ( unit ) => { return settings.includes( unit.value ); @@ -35,29 +49,77 @@ function useCustomUnits( { units, contextName } ) { return usedUnits.length === 0 ? false : usedUnits; } -export default function SpacingPanel( { - context: { name }, - getStyle, - setStyle, -} ) { +function useCustomSides( blockName, feature ) { + const support = getBlockSupport( blockName, 'spacing' ); + + // Return empty config when setting is boolean as theme isn't setting + // arbitrary sides. + if ( typeof support[ feature ] === 'boolean' ) { + return {}; + } + + return support[ feature ]; +} + +function filterValuesBySides( values, sides ) { + if ( Object.entries( sides ).length === 0 ) { + // If no custom side configuration all sides are opted into by default. + return values; + } + + // Only include sides opted into within filtered values. + return Object.keys( sides ) + .filter( ( side ) => sides[ side ] ) + .reduce( + ( filtered, side ) => ( { ...filtered, [ side ]: values[ side ] } ), + {} + ); +} + +export default function SpacingPanel( { context, getStyle, setStyle } ) { + const { name } = context; + const showPaddingControl = useHasPadding( context ); + const showMarginControl = useHasMargin( context ); const units = useCustomUnits( { contextName: name } ); + const paddingValues = getStyle( name, 'padding' ); - const setPaddingValues = ( { top, right, bottom, left } ) => { - setStyle( name, 'padding', { - top: top || paddingValues?.top, - right: right || paddingValues?.right, - bottom: bottom || paddingValues?.bottom, - left: left || paddingValues?.left, - } ); + const paddingSides = useCustomSides( name, 'padding' ); + + const setPaddingValues = ( newPaddingValues ) => { + const padding = filterValuesBySides( newPaddingValues, paddingSides ); + setStyle( name, 'padding', padding ); }; + + const marginValues = getStyle( name, 'margin' ); + const marginSides = useCustomSides( name, 'margin' ); + + const setMarginValues = ( newMarginValues ) => { + const margin = filterValuesBySides( newMarginValues, marginSides ); + setStyle( name, 'margin', margin ); + }; + return ( - + { showPaddingControl && ( + + ) } + { showMarginControl && ( + + ) } ); }