diff --git a/lib/class-wp-theme-json.php b/lib/class-wp-theme-json.php index 3b81119b4c5e9..b1056222a722d 100644 --- a/lib/class-wp-theme-json.php +++ b/lib/class-wp-theme-json.php @@ -77,6 +77,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, @@ -110,6 +116,7 @@ class WP_Theme_JSON { 'palette' => null, ), 'spacing' => array( + 'customMargin' => null, 'customPadding' => null, 'units' => null, ), @@ -258,6 +265,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 d5c071164d361..24255272c6f18 100644 --- a/lib/experimental-default-theme.json +++ b/lib/experimental-default-theme.json @@ -167,6 +167,7 @@ ] }, "spacing": { + "customMargin": false, "customPadding": false, "units": [ "px", "em", "rem", "vh", "vw" ] }, 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 aafdbb94151d2..ae31709619d8c 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 0000000000000..a400a1eccb31a --- /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 c90b756eb87ea..29b81cd0a859a 100644 --- a/packages/block-editor/src/hooks/spacing.js +++ b/packages/block-editor/src/hooks/spacing.js @@ -7,6 +7,7 @@ import { Platform } from '@wordpress/element'; /** * Internal dependencies */ +import { MarginEdit, hasMarginSupport, useIsMarginDisabled } from './margin'; import { PaddingEdit, hasPaddingSupport, @@ -33,12 +34,13 @@ export function SpacingPanel( props ) { return ( + ); } /** - * 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. @@ -48,7 +50,7 @@ export function hasSpacingSupport( blockName ) { return false; } - return hasPaddingSupport( blockName ); + return hasPaddingSupport( blockName ) || hasMarginSupport( blockName ); } /** @@ -59,8 +61,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 b6bd98eb56da6..3067d10467449 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 4215022da6659..c5e4761bfa382 100644 --- a/packages/blocks/src/api/constants.js +++ b/packages/blocks/src/api/constants.js @@ -65,6 +65,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 3a3756f7fa38a..e4ecc593be197 100644 --- a/packages/edit-site/src/components/sidebar/spacing-panel.js +++ b/packages/edit-site/src/components/sidebar/spacing-panel.js @@ -17,8 +17,9 @@ import { store as editSiteStore } from '../../store'; export function useHasSpacingPanel( context ) { const hasPadding = useHasPadding( context ); + const hasMargin = useHasMargin( context ); - return hasPadding; + return hasPadding || hasMargin; } export function useHasPadding( { name, supports } ) { @@ -28,6 +29,12 @@ export function useHasPadding( { name, supports } ) { ); } +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 ); @@ -82,6 +89,7 @@ function useThemeValues( name, feature ) { 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' ); @@ -93,6 +101,15 @@ export default function SpacingPanel( { context, getStyle, setStyle } ) { setStyle( name, 'padding', padding ); }; + const marginValues = getStyle( name, 'margin' ); + const themeMarginValues = useThemeValues( name, 'margin' ); + const marginSides = useCustomSides( name, 'margin' ); + + const setMarginValues = ( newMarginValues ) => { + const margin = filterValuesBySides( newMarginValues, marginSides ); + setStyle( name, 'margin', margin ); + }; + return ( { showPaddingControl && ( @@ -105,6 +122,16 @@ export default function SpacingPanel( { context, getStyle, setStyle } ) { resetValues={ themePaddingValues } /> ) } + { showMarginControl && ( + + ) } ); }