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 && (
+
+ ) }
);
}