diff --git a/docs/reference-guides/block-api/block-supports.md b/docs/reference-guides/block-api/block-supports.md index ea746a856c1b8..1f5ab06e7b7b1 100644 --- a/docs/reference-guides/block-api/block-supports.md +++ b/docs/reference-guides/block-api/block-supports.md @@ -497,16 +497,28 @@ supports: { - Type: `Object` - Default value: null - Subproperties: - - `padding`: type `boolean`, default value `false` + - `padding`: type `boolean` or `array`, default value `false` This value signals that a block supports some of the CSS style properties related to spacing. When it does, the block editor will show UI controls for the user to set their values, if [the theme declares support](/docs/how-to-guides/themes/theme-support.md#cover-block-padding). ```js supports: { - padding: true, // Enable padding color UI control. + spacing: { + padding: true, // Enable padding UI control. + } } ``` When the block declares support for a specific spacing property, the attributes definition is extended to include the `style` attribute. - `style`: attribute of `object` type with no default assigned. This is added when `padding` support is declared. It stores the custom values set by the user. + +```js +supports: { + spacing: { + padding: [ 'top', 'bottom' ], // Enable padding for arbitrary sides. + } +} +``` + +A spacing property may define an array of allowable sides that can be configured. When arbitrary sides are defined only UI controls for those sides are displayed. \ No newline at end of file diff --git a/packages/block-editor/src/components/spacing-panel-control/index.js b/packages/block-editor/src/components/spacing-panel-control/index.js deleted file mode 100644 index aafdbb94151d2..0000000000000 --- a/packages/block-editor/src/components/spacing-panel-control/index.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * WordPress dependencies - */ -import { __ } from '@wordpress/i18n'; -import { PanelBody } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import InspectorControls from '../inspector-controls'; -import useEditorFeature from '../use-editor-feature'; - -export default function SpacingPanelControl( { children, ...props } ) { - const isSpacingEnabled = useEditorFeature( 'spacing.customPadding' ); - - if ( ! isSpacingEnabled ) return null; - - return ( - - { children } - - ); -} diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js index e72a00e59d22f..3308b98d28169 100644 --- a/packages/block-editor/src/hooks/index.js +++ b/packages/block-editor/src/hooks/index.js @@ -12,5 +12,6 @@ import './font-size'; import './border-color'; import './layout'; +export { useCustomSides } from './spacing'; export { getBorderClassesAndStyles, useBorderProps } from './use-border-props'; export { getColorClassesAndStyles, useColorProps } from './use-color-props'; diff --git a/packages/block-editor/src/hooks/padding.js b/packages/block-editor/src/hooks/padding.js index 113da0a5bfcce..e366ed4de53dc 100644 --- a/packages/block-editor/src/hooks/padding.js +++ b/packages/block-editor/src/hooks/padding.js @@ -9,11 +9,11 @@ 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 isWeb = Platform.OS === 'web'; const CSS_UNITS = [ { @@ -43,10 +43,27 @@ const CSS_UNITS = [ }, ]; -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. + */ +export function 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 @@ -63,6 +80,7 @@ export function PaddingEdit( props ) { } = props; const units = useCustomUnits( CSS_UNITS ); + const sides = useCustomSides( blockName, 'padding' ); if ( ! hasPaddingSupport( blockName ) ) { return null; @@ -72,6 +90,7 @@ export function PaddingEdit( props ) { const newStyle = { ...style, spacing: { + ...style?.spacing, padding: next, }, }; @@ -102,6 +121,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 0000000000000..1566f3015f39c --- /dev/null +++ b/packages/block-editor/src/hooks/spacing.js @@ -0,0 +1,90 @@ +/** + * WordPress dependencies + */ +import { PanelBody } from '@wordpress/components'; +import { Platform } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { getBlockSupport } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import InspectorControls from '../components/inspector-controls'; +import { + PaddingEdit, + hasPaddingSupport, + useIsPaddingDisabled, +} from './padding'; + +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. + * + * @param {string} blockName Block name. + * @return {boolean} Whether there is support. + */ +export function hasSpacingSupport( blockName ) { + if ( Platform.OS !== 'web' ) { + return false; + } + + return hasPaddingSupport( blockName ); +} + +/** + * 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 ); + + return paddingDisabled; +}; + +/** + * 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 ); + + // Skip 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 af59e918d61b9..382f62288fd6f 100644 --- a/packages/block-editor/src/hooks/style.js +++ b/packages/block-editor/src/hooks/style.js @@ -21,8 +21,7 @@ import { BORDER_SUPPORT_KEY, BorderPanel } from './border'; import { COLOR_SUPPORT_KEY, ColorEdit } from './color'; import { FONT_SIZE_SUPPORT_KEY } from './font-size'; import { TypographyPanel, TYPOGRAPHY_SUPPORT_KEYS } from './typography'; -import { SPACING_SUPPORT_KEY, PaddingEdit } from './padding'; -import SpacingPanelControl from '../components/spacing-panel-control'; +import { SPACING_SUPPORT_KEY, SpacingPanel } from './spacing'; const styleSupportKeys = [ ...TYPOGRAPHY_SUPPORT_KEYS, @@ -150,7 +149,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 @@ -183,23 +182,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 0cd3c70d9f2b6..b6bd98eb56da6 100644 --- a/packages/block-editor/src/hooks/test/style.js +++ b/packages/block-editor/src/hooks/test/style.js @@ -23,6 +23,9 @@ describe( 'getInlineStyles', () => { style: 'dotted', color: '#21759b', }, + spacing: { + padding: { top: '10px' }, + }, } ) ).toEqual( { backgroundColor: 'black', @@ -33,6 +36,7 @@ describe( 'getInlineStyles', () => { color: 'red', lineHeight: 1.5, fontSize: 10, + paddingTop: '10px', } ); } ); } ); diff --git a/packages/block-editor/src/index.js b/packages/block-editor/src/index.js index deca54bba6122..27af4c4be666b 100644 --- a/packages/block-editor/src/index.js +++ b/packages/block-editor/src/index.js @@ -12,6 +12,7 @@ export { useBorderProps as __experimentalUseBorderProps, getColorClassesAndStyles as __experimentalGetColorClassesAndStyles, useColorProps as __experimentalUseColorProps, + useCustomSides as __experimentalUseCustomSides, } from './hooks'; export * from './components'; export * from './utils'; diff --git a/packages/edit-site/src/components/sidebar/spacing-panel.js b/packages/edit-site/src/components/sidebar/spacing-panel.js index 0afcf1344f897..f8c4e2053b5dd 100644 --- a/packages/edit-site/src/components/sidebar/spacing-panel.js +++ b/packages/edit-site/src/components/sidebar/spacing-panel.js @@ -7,6 +7,7 @@ import { __experimentalBoxControl as BoxControl, PanelBody, } from '@wordpress/components'; +import { __experimentalUseCustomSides as useCustomSides } from '@wordpress/block-editor'; /** * Internal dependencies @@ -42,7 +43,13 @@ const CSS_UNITS = [ }, ]; -export function useHasSpacingPanel( { supports, name } ) { +export function useHasSpacingPanel( context ) { + const hasPadding = useHasPadding( context ); + + return hasPadding; +} + +export function useHasPadding( { name, supports } ) { return ( useEditorFeature( 'spacing.customPadding', name ) && supports.includes( 'padding' ) @@ -65,29 +72,43 @@ function useCustomUnits( { units, contextName } ) { return usedUnits.length === 0 ? false : usedUnits; } -export default function SpacingPanel( { - context: { name }, - getStyle, - setStyle, -} ) { +function filterValuesBySides( values, sides ) { + if ( ! sides ) { + // If no custom side configuration all sides are opted into by default. + return values; + } + + // Only include sides opted into within filtered values. + const filteredValues = {}; + sides.forEach( ( side ) => ( filteredValues[ side ] = values[ side ] ) ); + + return filteredValues; +} + +export default function SpacingPanel( { context, getStyle, setStyle } ) { + const { name } = context; + const showPaddingControl = useHasPadding( context ); const units = useCustomUnits( { contextName: name, units: CSS_UNITS } ); + 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 ); }; + return ( - + { showPaddingControl && ( + + ) } ); }