diff --git a/docs/how-to-guides/themes/theme-json.md b/docs/how-to-guides/themes/theme-json.md index 63866002ea593e..795c9a8367ccb1 100644 --- a/docs/how-to-guides/themes/theme-json.md +++ b/docs/how-to-guides/themes/theme-json.md @@ -182,6 +182,7 @@ The settings section has the following structure and default values: "wideSize": "1000px", }, "spacing": { + "customMargin": false, "customPadding": false, /* Supersedes add_theme_support('custom-spacing') */ "units": [ "px", "em", "rem", "vh", "vw" ], /* filter values, as in add_theme_support('custom-units', ... ) */ }, @@ -464,6 +465,12 @@ Each block declares which style properties it exposes via the [block supports me "text": "value" }, "spacing": { + "margin": { + "top": "value", + "right": "value", + "bottom": "value", + "left": "value" + }, "padding": { "top": "value", "right": "value", diff --git a/docs/reference-guides/block-api/block-supports.md b/docs/reference-guides/block-api/block-supports.md index 1f5ab06e7b7b16..5b920ab3b5adf5 100644 --- a/docs/reference-guides/block-api/block-supports.md +++ b/docs/reference-guides/block-api/block-supports.md @@ -497,6 +497,7 @@ supports: { - Type: `Object` - Default value: null - Subproperties: + - `margin`: type `boolean` or `array`, 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). @@ -504,6 +505,7 @@ This value signals that a block supports some of the CSS style properties relate ```js supports: { spacing: { + margin: true, // Enable margin UI control. padding: true, // Enable padding UI control. } } @@ -511,12 +513,13 @@ supports: { 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. +- `style`: attribute of `object` type with no default assigned. This is added when `margin` or `padding` support is declared. It stores the custom values set by the user. ```js supports: { spacing: { - padding: [ 'top', 'bottom' ], // Enable padding for arbitrary sides. + margin: [ 'top', 'bottom' ], // Enable margin for arbitrary sides. + padding: true, // Enable padding for all sides. } } ``` diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php index f13539fd6aad0f..07ec3c9956dd8a 100644 --- a/lib/block-supports/layout.php +++ b/lib/block-supports/layout.php @@ -65,8 +65,8 @@ function gutenberg_render_layout_support_flag( $block_content, $block ) { ?> > * { ; - margin-left: auto; - margin-right: auto; + margin-left: auto !important; + margin-right: auto !important; } > .alignwide { diff --git a/lib/class-wp-theme-json.php b/lib/class-wp-theme-json.php index f6112584a1295b..f2e6c4571c274e 100644 --- a/lib/class-wp-theme-json.php +++ b/lib/class-wp-theme-json.php @@ -55,6 +55,12 @@ class WP_Theme_JSON { 'text' => null, ), 'spacing' => array( + 'margin' => array( + 'top' => null, + 'right' => null, + 'bottom' => null, + 'left' => null, + ), 'padding' => array( 'bottom' => null, 'left' => null, @@ -91,6 +97,7 @@ class WP_Theme_JSON { 'custom' => null, 'layout' => null, 'spacing' => array( + 'customMargin' => null, 'customPadding' => null, 'units' => null, ), @@ -237,6 +244,10 @@ class WP_Theme_JSON { 'line-height' => array( 'value' => array( 'typography', 'lineHeight' ), ), + 'margin' => array( + 'value' => array( 'spacing', 'margin' ), + 'properties' => array( 'top', 'right', 'bottom', 'left' ), + ), 'padding' => array( 'value' => array( 'spacing', 'padding' ), 'properties' => array( 'top', 'right', 'bottom', 'left' ), diff --git a/lib/experimental-default-theme.json b/lib/experimental-default-theme.json index 7c0cf2e2401efa..ab27fd425784b5 100644 --- a/lib/experimental-default-theme.json +++ b/lib/experimental-default-theme.json @@ -209,6 +209,7 @@ ] }, "spacing": { + "customMargin": false, "customPadding": false, "units": [ "px", "em", "rem", "vh", "vw" ] }, diff --git a/packages/block-editor/src/components/block-list/layout.js b/packages/block-editor/src/components/block-list/layout.js index 3f898ae12c02fe..e29fb8ab05ccaa 100644 --- a/packages/block-editor/src/components/block-list/layout.js +++ b/packages/block-editor/src/components/block-list/layout.js @@ -43,8 +43,8 @@ export function LayoutStyle( { selector, layout = {} } ) { ? ` ${ appendSelectors( selector, '> *' ) } { max-width: ${ contentSize ?? wideSize }; - margin-left: auto; - margin-right: auto; + margin-left: auto !important; + margin-right: auto !important; } ${ appendSelectors( selector, '> [data-align="wide"]' ) } { diff --git a/packages/block-editor/src/hooks/margin.js b/packages/block-editor/src/hooks/margin.js new file mode 100644 index 00000000000000..a400a1eccb31a4 --- /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. + */ +export function 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/spacing.js b/packages/block-editor/src/hooks/spacing.js index 1566f3015f39c0..a911727dd82f8f 100644 --- a/packages/block-editor/src/hooks/spacing.js +++ b/packages/block-editor/src/hooks/spacing.js @@ -10,6 +10,7 @@ import { getBlockSupport } from '@wordpress/blocks'; * Internal dependencies */ import InspectorControls from '../components/inspector-controls'; +import { MarginEdit, hasMarginSupport, useIsMarginDisabled } from './margin'; import { PaddingEdit, hasPaddingSupport, @@ -36,13 +37,14 @@ export function SpacingPanel( props ) { + ); } /** - * Determine whether there is block support for padding. + * Determine whether there is block support for padding or margins. * * @param {string} blockName Block name. * @return {boolean} Whether there is support. @@ -52,7 +54,7 @@ export function hasSpacingSupport( blockName ) { return false; } - return hasPaddingSupport( blockName ); + return hasPaddingSupport( blockName ) || hasMarginSupport( blockName ); } /** @@ -63,8 +65,9 @@ export function hasSpacingSupport( blockName ) { */ const useIsSpacingDisabled = ( props = {} ) => { const paddingDisabled = useIsPaddingDisabled( props ); + const marginDisabled = useIsMarginDisabled( props ); - return paddingDisabled; + return paddingDisabled && marginDisabled; }; /** diff --git a/packages/block-editor/src/hooks/test/style.js b/packages/block-editor/src/hooks/test/style.js index b6bd98eb56da6b..3067d104674491 100644 --- a/packages/block-editor/src/hooks/test/style.js +++ b/packages/block-editor/src/hooks/test/style.js @@ -25,6 +25,7 @@ describe( 'getInlineStyles', () => { }, spacing: { padding: { top: '10px' }, + margin: { bottom: '15px' }, }, } ) ).toEqual( { @@ -36,6 +37,7 @@ 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 e276484dca72d9..badbc94320f001 100644 --- a/packages/blocks/src/api/constants.js +++ b/packages/blocks/src/api/constants.js @@ -66,6 +66,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' ], diff --git a/packages/edit-site/src/components/sidebar/spacing-panel.js b/packages/edit-site/src/components/sidebar/spacing-panel.js index f8c4e2053b5dd5..8672ae5d5be26c 100644 --- a/packages/edit-site/src/components/sidebar/spacing-panel.js +++ b/packages/edit-site/src/components/sidebar/spacing-panel.js @@ -45,15 +45,21 @@ const CSS_UNITS = [ export function useHasSpacingPanel( context ) { const hasPadding = useHasPadding( context ); + const hasMargin = useHasMargin( context ); - return hasPadding; + return hasPadding || hasMargin; } -export function useHasPadding( { name, supports } ) { - return ( - useEditorFeature( 'spacing.customPadding', name ) && - supports.includes( 'padding' ) - ); +function useHasPadding( { name, supports } ) { + const settings = useEditorFeature( 'spacing.customPadding', name ); + + return settings && supports.includes( 'padding' ); +} + +function useHasMargin( { name, supports } ) { + const settings = useEditorFeature( 'spacing.customMargin', name ); + + return settings && supports.includes( 'margin' ); } function filterUnitsWithSettings( settings = [], units = [] ) { @@ -88,6 +94,7 @@ function filterValuesBySides( values, sides ) { export default function SpacingPanel( { context, getStyle, setStyle } ) { const { name } = context; const showPaddingControl = useHasPadding( context ); + const showMarginControl = useHasMargin( context ); const units = useCustomUnits( { contextName: name, units: CSS_UNITS } ); const paddingValues = getStyle( name, 'padding' ); @@ -98,6 +105,14 @@ export default function SpacingPanel( { context, getStyle, setStyle } ) { 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 && ( @@ -109,6 +124,15 @@ export default function SpacingPanel( { context, getStyle, setStyle } ) { units={ units } /> ) } + { showMarginControl && ( + + ) } ); }