diff --git a/docs/reference-guides/theme-json-reference/theme-json-living.md b/docs/reference-guides/theme-json-reference/theme-json-living.md index 95f2e047dd0e31..138e32f76505ee 100644 --- a/docs/reference-guides/theme-json-reference/theme-json-living.md +++ b/docs/reference-guides/theme-json-reference/theme-json-living.md @@ -74,6 +74,7 @@ Settings related to borders. | radius | Allow users to set custom border radius. | `boolean` | `false` | | style | Allow users to set custom border styles. | `boolean` | `false` | | width | Allow users to set custom border widths. | `boolean` | `false` | +| radiusSizes | Border radius size presets for the border radius selector. | `[ { name, slug, size } ]` | | --- diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 083ce3516b71af..69e33e9459c7e5 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -204,6 +204,15 @@ class WP_Theme_JSON_Gutenberg { 'classes' => array(), 'properties' => array( 'box-shadow' ), ), + array( + 'path' => array( 'border', 'radiusSizes' ), + 'prevent_override' => false, + 'use_default_names' => false, + 'value_key' => 'size', + 'css_vars' => '--wp--preset--border-radius--$slug', + 'classes' => array(), + 'properties' => array( 'border-radius' ), + ), ); /** @@ -386,10 +395,11 @@ class WP_Theme_JSON_Gutenberg { 'backgroundSize' => null, ), 'border' => array( - 'color' => null, - 'radius' => null, - 'style' => null, - 'width' => null, + 'color' => null, + 'radius' => null, + 'style' => null, + 'width' => null, + 'radiusSizes' => null, ), 'color' => array( 'background' => null, diff --git a/lib/theme-i18n.json b/lib/theme-i18n.json index e4d14502132cbe..1da001f7eb842b 100644 --- a/lib/theme-i18n.json +++ b/lib/theme-i18n.json @@ -45,6 +45,13 @@ } ] }, + "border": { + "radiusSizes": [ + { + "name": "Border radius size name" + } + ] + }, "blocks": { "*": { "typography": { @@ -77,6 +84,13 @@ "name": "Space size name" } ] + }, + "border": { + "radiusSizes": [ + { + "name": "Border radius size name" + } + ] } } } diff --git a/packages/block-editor/src/components/border-radius-control/all-input-control.js b/packages/block-editor/src/components/border-radius-control/all-input-control.js deleted file mode 100644 index 14abf3c6c2bc94..00000000000000 --- a/packages/block-editor/src/components/border-radius-control/all-input-control.js +++ /dev/null @@ -1,67 +0,0 @@ -/** - * WordPress dependencies - */ -import { __experimentalUnitControl as UnitControl } from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; - -/** - * Internal dependencies - */ -import { - getAllValue, - getAllUnit, - hasMixedValues, - hasDefinedValues, -} from './utils'; - -export default function AllInputControl( { - onChange, - selectedUnits, - setSelectedUnits, - values, - ...props -} ) { - let allValue = getAllValue( values ); - - if ( allValue === undefined ) { - // If we don't have any value set the unit to any current selection - // or the most common unit from the individual radii values. - allValue = getAllUnit( selectedUnits ); - } - - const hasValues = hasDefinedValues( values ); - const isMixed = hasValues && hasMixedValues( values ); - const allPlaceholder = isMixed ? __( 'Mixed' ) : null; - - // Filter out CSS-unit-only values to prevent invalid styles. - const handleOnChange = ( next ) => { - const isNumeric = ! isNaN( parseFloat( next ) ); - const nextValue = isNumeric ? next : undefined; - onChange( nextValue ); - }; - - // Store current unit selection for use as fallback for individual - // radii controls. - const handleOnUnitChange = ( unit ) => { - setSelectedUnits( { - topLeft: unit, - topRight: unit, - bottomLeft: unit, - bottomRight: unit, - } ); - }; - - return ( - - ); -} diff --git a/packages/block-editor/src/components/border-radius-control/index.js b/packages/block-editor/src/components/border-radius-control/index.js index cab9b87b3b29c0..a9a7c44d586e21 100644 --- a/packages/block-editor/src/components/border-radius-control/index.js +++ b/packages/block-editor/src/components/border-radius-control/index.js @@ -3,26 +3,21 @@ */ import { BaseControl, - RangeControl, __experimentalParseQuantityAndUnitFromRawValue as parseQuantityAndUnitFromRawValue, __experimentalUseCustomUnits as useCustomUnits, + __experimentalVStack as VStack, + __experimentalHStack as HStack, } from '@wordpress/components'; -import { useState } from '@wordpress/element'; +import { useState, useMemo } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import AllInputControl from './all-input-control'; -import InputControls from './input-controls'; import LinkedButton from './linked-button'; import { useSettings } from '../use-settings'; -import { - getAllValue, - getAllUnit, - hasDefinedValues, - hasMixedValues, -} from './utils'; +import { hasDefinedValues, hasMixedValues } from './utils'; +import SingleInputControl from './single-input-control'; const DEFAULT_VALUES = { topLeft: undefined, @@ -30,12 +25,33 @@ const DEFAULT_VALUES = { bottomLeft: undefined, bottomRight: undefined, }; -const MIN_BORDER_RADIUS_VALUE = 0; -const MAX_BORDER_RADIUS_VALUES = { - px: 100, - em: 20, - rem: 20, -}; +const RANGE_CONTROL_MAX_SIZE = 8; +const EMPTY_ARRAY = []; +function useBorderRadiusSizes( presets ) { + const defaultSizes = presets?.default ?? EMPTY_ARRAY; + const customSizes = presets?.custom ?? EMPTY_ARRAY; + const themeSizes = presets?.theme ?? EMPTY_ARRAY; + + return useMemo( () => { + const sizes = [ + { name: __( 'None' ), slug: '0', size: 0 }, + ...customSizes, + ...themeSizes, + ...defaultSizes, + ]; + + return sizes.length > RANGE_CONTROL_MAX_SIZE + ? [ + { + name: __( 'Default' ), + slug: 'default', + size: undefined, + }, + ...sizes, + ] + : sizes; + }, [ customSizes, themeSizes, defaultSizes ] ); +} /** * Control to display border radius options. @@ -43,14 +59,15 @@ const MAX_BORDER_RADIUS_VALUES = { * @param {Object} props Component props. * @param {Function} props.onChange Callback to handle onChange. * @param {Object} props.values Border radius values. + * @param {Object} props.presets Border radius presets. * * @return {Element} Custom border radius control. */ -export default function BorderRadiusControl( { onChange, values } ) { +export default function BorderRadiusControl( { onChange, values, presets } ) { const [ isLinked, setIsLinked ] = useState( ! hasDefinedValues( values ) || ! hasMixedValues( values ) ); - + const options = useBorderRadiusSizes( presets ); // Tracking selected units via internal state allows filtering of CSS unit // only values from being saved while maintaining preexisting unit selection // behaviour. Filtering CSS unit only values prevents invalid style values. @@ -72,64 +89,49 @@ export default function BorderRadiusControl( { onChange, values } ) { availableUnits: availableUnits || [ 'px', 'em', 'rem' ], } ); - const unit = getAllUnit( selectedUnits ); - const unitConfig = units && units.find( ( item ) => item.value === unit ); - const step = unitConfig?.step || 1; - - const [ allValue ] = parseQuantityAndUnitFromRawValue( - getAllValue( values ) - ); - const toggleLinked = () => setIsLinked( ! isLinked ); - const handleSliderChange = ( next ) => { - onChange( next !== undefined ? `${ next }${ unit }` : undefined ); - }; - return (
- - { __( 'Radius' ) } - -
- { isLinked ? ( - <> - - - - ) : ( - + + { __( 'Radius' ) } + + + + { isLinked ? ( + <> + - ) } - -
+ + ) : ( + + { [ + 'topLeft', + 'topRight', + 'bottomLeft', + 'bottomRight', + ].map( ( corner ) => ( + + ) ) } + + ) }
); } diff --git a/packages/block-editor/src/components/border-radius-control/input-controls.js b/packages/block-editor/src/components/border-radius-control/input-controls.js deleted file mode 100644 index 4529c00b997ac7..00000000000000 --- a/packages/block-editor/src/components/border-radius-control/input-controls.js +++ /dev/null @@ -1,91 +0,0 @@ -/** - * WordPress dependencies - */ -import { - __experimentalParseQuantityAndUnitFromRawValue as parseQuantityAndUnitFromRawValue, - __experimentalUnitControl as UnitControl, - Tooltip, -} from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; - -const CORNERS = { - topLeft: __( 'Top left' ), - topRight: __( 'Top right' ), - bottomLeft: __( 'Bottom left' ), - bottomRight: __( 'Bottom right' ), -}; - -export default function BoxInputControls( { - onChange, - selectedUnits, - setSelectedUnits, - values: valuesProp, - ...props -} ) { - const createHandleOnChange = ( corner ) => ( next ) => { - if ( ! onChange ) { - return; - } - - // Filter out CSS-unit-only values to prevent invalid styles. - const isNumeric = ! isNaN( parseFloat( next ) ); - const nextValue = isNumeric ? next : undefined; - - onChange( { - ...values, - [ corner ]: nextValue, - } ); - }; - - const createHandleOnUnitChange = ( side ) => ( next ) => { - const newUnits = { ...selectedUnits }; - newUnits[ side ] = next; - setSelectedUnits( newUnits ); - }; - - // For shorthand style & backwards compatibility, handle flat string value. - const values = - typeof valuesProp !== 'string' - ? valuesProp - : { - topLeft: valuesProp, - topRight: valuesProp, - bottomLeft: valuesProp, - bottomRight: valuesProp, - }; - - // Controls are wrapped in tooltips as visible labels aren't desired here. - // Tooltip rendering also requires the UnitControl to be wrapped. See: - // https://github.com/WordPress/gutenberg/pull/24966#issuecomment-685875026 - return ( -
- { Object.entries( CORNERS ).map( ( [ corner, label ] ) => { - const [ parsedQuantity, parsedUnit ] = - parseQuantityAndUnitFromRawValue( values[ corner ] ); - - const computedUnit = values[ corner ] - ? parsedUnit - : selectedUnits[ corner ] || selectedUnits.flat; - - return ( - -
- -
-
- ); - } ) } -
- ); -} diff --git a/packages/block-editor/src/components/border-radius-control/linked-button.js b/packages/block-editor/src/components/border-radius-control/linked-button.js index 58afe350b72126..e8a967e7ee5677 100644 --- a/packages/block-editor/src/components/border-radius-control/linked-button.js +++ b/packages/block-editor/src/components/border-radius-control/linked-button.js @@ -11,7 +11,7 @@ export default function LinkedButton( { isLinked, ...props } ) { return (