diff --git a/lib/compat/wordpress-6.1/block-editor-settings.php b/lib/compat/wordpress-6.1/block-editor-settings.php index 13f0f3982f8571..3181329475df4f 100644 --- a/lib/compat/wordpress-6.1/block-editor-settings.php +++ b/lib/compat/wordpress-6.1/block-editor-settings.php @@ -154,7 +154,7 @@ function gutenberg_get_block_editor_settings( $settings ) { unset( $settings['__experimentalFeatures']['spacing']['padding'] ); } if ( isset( $settings['__experimentalFeatures']['spacing']['customSpacingSize'] ) ) { - $settings['disableCustomSpacingSize'] = ! $settings['__experimentalFeatures']['spacing']['customSpacingSize']; + $settings['disableCustomSpacingSizes'] = ! $settings['__experimentalFeatures']['spacing']['customSpacingSize']; unset( $settings['__experimentalFeatures']['spacing']['customSpacingSize'] ); } diff --git a/lib/compat/wordpress-6.1/theme.json b/lib/compat/wordpress-6.1/theme.json index d817f336536121..5d06385c0bb814 100644 --- a/lib/compat/wordpress-6.1/theme.json +++ b/lib/compat/wordpress-6.1/theme.json @@ -269,12 +269,12 @@ "blockGap": null, "margin": false, "padding": false, - "customSpacingSizes": true, + "customSpacingSize": true, "units": [ "px", "em", "rem", "vh", "vw", "%" ], "spacingScale": { "operator": "*", "increment": 1.5, - "steps": 10, + "steps": 7, "mediumStep": 1.5, "unit": "rem" } diff --git a/lib/experimental/class-wp-rest-block-editor-settings-controller.php b/lib/experimental/class-wp-rest-block-editor-settings-controller.php index 17e7536b8a2ee7..c0850a8a29f4cf 100644 --- a/lib/experimental/class-wp-rest-block-editor-settings-controller.php +++ b/lib/experimental/class-wp-rest-block-editor-settings-controller.php @@ -297,6 +297,11 @@ public function get_item_schema() { 'type' => 'array', 'context' => array( 'post-editor', 'site-editor', 'widgets-editor' ), ), + 'disableCustomSpacingSizes' => array( + 'description' => __( 'Disables custom spacing sizes.', 'gutenberg' ), + 'type' => 'boolean', + 'context' => array( 'post-editor', 'site-editor', 'widgets-editor' ), + ), ), ); diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js index 5acd5e44f05dd0..6b7a24887b3423 100644 --- a/packages/block-editor/src/components/index.js +++ b/packages/block-editor/src/components/index.js @@ -93,6 +93,7 @@ export { default as URLInputButton } from './url-input/button'; export { default as URLPopover } from './url-popover'; export { __experimentalImageURLInputUI } from './url-popover/image-url-input-ui'; export { default as withColorContext } from './color-palette/with-color-context'; +export { default as __experimentalSpacingSizesControl } from './spacing-sizes-control'; /* * Content Related Components diff --git a/packages/block-editor/src/components/spacing-sizes-control/all-input-control.js b/packages/block-editor/src/components/spacing-sizes-control/all-input-control.js new file mode 100644 index 00000000000000..d0243b90c56acb --- /dev/null +++ b/packages/block-editor/src/components/spacing-sizes-control/all-input-control.js @@ -0,0 +1,40 @@ +/** + * WordPress dependencies + */ +import { __experimentalApplyValueToSides as applyValueToSides } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import SpacingInputControl from './spacing-input-control'; +import { getAllRawValue, isValuesMixed, isValuesDefined } from './utils'; + +export default function AllInputControl( { + onChange, + values, + sides, + spacingSizes, + type, + minimumCustomValue, +} ) { + const allValue = getAllRawValue( values ); + const hasValues = isValuesDefined( values ); + const isMixed = hasValues && isValuesMixed( values ); + + const handleOnChange = ( next ) => { + const nextValues = applyValueToSides( values, next, sides ); + onChange( nextValues ); + }; + + return ( + + ); +} diff --git a/packages/block-editor/src/components/spacing-sizes-control/axial-input-controls.js b/packages/block-editor/src/components/spacing-sizes-control/axial-input-controls.js new file mode 100644 index 00000000000000..563b5950186962 --- /dev/null +++ b/packages/block-editor/src/components/spacing-sizes-control/axial-input-controls.js @@ -0,0 +1,62 @@ +/** + * Internal dependencies + */ +import SpacingInputControl from './spacing-input-control'; +import { LABELS } from './utils'; + +const groupedSides = [ 'vertical', 'horizontal' ]; + +export default function AxialInputControls( { + onChange, + values, + sides, + spacingSizes, + type, + minimumCustomValue, +} ) { + const createHandleOnChange = ( side ) => ( next ) => { + if ( ! onChange ) { + return; + } + const nextValues = { ...values }; + + if ( side === 'vertical' ) { + nextValues.top = next; + nextValues.bottom = next; + } + + if ( side === 'horizontal' ) { + nextValues.left = next; + nextValues.right = next; + } + + onChange( nextValues ); + }; + + // Filter sides if custom configuration provided, maintaining default order. + const filteredSides = sides?.length + ? groupedSides.filter( ( side ) => sides.includes( side ) ) + : groupedSides; + + return ( + <> + { filteredSides.map( ( side ) => { + const axisValue = + side === 'vertical' ? values.top : values.left; + return ( + + ); + } ) } + + ); +} diff --git a/packages/block-editor/src/components/spacing-sizes-control/index.js b/packages/block-editor/src/components/spacing-sizes-control/index.js new file mode 100644 index 00000000000000..0653375a574b9e --- /dev/null +++ b/packages/block-editor/src/components/spacing-sizes-control/index.js @@ -0,0 +1,91 @@ +/** + * WordPress dependencies + */ +import { useState } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { __experimentalText as Text } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import AllInputControl from './all-input-control'; +import InputControls from './input-controls'; +import AxialInputControls from './axial-input-controls'; +import LinkedButton from './linked-button'; +import { DEFAULT_VALUES, isValuesMixed, isValuesDefined } from './utils'; +import useSetting from '../use-setting'; + +export default function SpacingSizesControl( { + inputProps, + onChange, + label = __( 'Spacing Control' ), + values, + sides, + splitOnAxis = false, + useSelect, + minimumCustomValue = 0, +} ) { + const spacingSizes = [ + { name: 0, slug: '0', size: 0 }, + ...( useSetting( 'spacing.spacingSizes' ) || [] ), + ]; + + if ( spacingSizes.length > 8 ) { + spacingSizes.unshift( { + name: __( 'Default' ), + slug: 'default', + size: undefined, + } ); + } + + const inputValues = values || DEFAULT_VALUES; + const hasInitialValue = isValuesDefined( values ); + const hasOneSide = sides?.length === 1; + + const [ isLinked, setIsLinked ] = useState( + ! hasInitialValue || ! isValuesMixed( inputValues ) || hasOneSide + ); + + const toggleLinked = () => { + setIsLinked( ! isLinked ); + }; + + const handleOnChange = ( nextValue ) => { + const newValues = { ...values, ...nextValue }; + onChange( newValues ); + }; + + const inputControlProps = { + ...inputProps, + onChange: handleOnChange, + isLinked, + sides, + values: inputValues, + spacingSizes, + useSelect, + type: label, + minimumCustomValue, + }; + + return ( +
+ { label } + { ! hasOneSide && ( + + ) } + { isLinked && ( + + ) } + + { ! isLinked && splitOnAxis && ( + + ) } + { ! isLinked && ! splitOnAxis && ( + + ) } +
+ ); +} diff --git a/packages/block-editor/src/components/spacing-sizes-control/input-controls.js b/packages/block-editor/src/components/spacing-sizes-control/input-controls.js new file mode 100644 index 00000000000000..b8b71c22310b59 --- /dev/null +++ b/packages/block-editor/src/components/spacing-sizes-control/input-controls.js @@ -0,0 +1,46 @@ +/** + * Internal dependencies + */ +import SpacingInputControl from './spacing-input-control'; +import { ALL_SIDES, LABELS } from './utils'; + +export default function BoxInputControls( { + values, + sides, + onChange, + spacingSizes, + type, + minimumCustomValue, +} ) { + // Filter sides if custom configuration provided, maintaining default order. + const filteredSides = sides?.length + ? ALL_SIDES.filter( ( side ) => sides.includes( side ) ) + : ALL_SIDES; + + const createHandleOnChange = ( side ) => ( next ) => { + const nextValues = { ...values }; + nextValues[ side ] = next; + + onChange( nextValues ); + }; + + return ( + <> + { filteredSides.map( ( side ) => { + return ( + + ); + } ) } + + ); +} diff --git a/packages/block-editor/src/components/spacing-sizes-control/linked-button.js b/packages/block-editor/src/components/spacing-sizes-control/linked-button.js new file mode 100644 index 00000000000000..3cdb6e6c1ed1cf --- /dev/null +++ b/packages/block-editor/src/components/spacing-sizes-control/linked-button.js @@ -0,0 +1,25 @@ +/** + * WordPress dependencies + */ +import { link, linkOff } from '@wordpress/icons'; +import { __ } from '@wordpress/i18n'; +import { Button, Tooltip } from '@wordpress/components'; + +export default function LinkedButton( { isLinked, onClick } ) { + const label = isLinked ? __( 'Unlink Sides' ) : __( 'Link Sides' ); + + return ( + + +