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 fdb68ce2277c40..bf00cd7372cd72 100644 --- a/packages/block-editor/src/components/border-radius-control/index.js +++ b/packages/block-editor/src/components/border-radius-control/index.js @@ -16,12 +16,14 @@ import { __ } from '@wordpress/i18n'; */ import LinkedButton from './linked-button'; import { useSettings } from '../use-settings'; -import { hasDefinedValues, hasMixedValues } from './utils'; -import SingleInputControl from './single-input-control'; +import { hasDefinedValues, hasMixedValues, getAllValue } from './utils'; +import PresetInputControl from '../preset-input-control'; import { - DEFAULT_VALUES, RANGE_CONTROL_MAX_SIZE, EMPTY_ARRAY, + CORNERS, + ICONS, + MIN_BORDER_RADIUS_VALUE, } from './constants'; function useBorderRadiusSizes( presets ) { @@ -50,6 +52,110 @@ function useBorderRadiusSizes( presets ) { }, [ customSizes, themeSizes, defaultSizes ] ); } +/** + * Gets the value for a specific corner from the values object. + * + * @param {Object|string} values Border radius values. + * @param {string} corner Corner name ('all', 'topLeft', etc.). + * + * @return {string|undefined} The corner value. + */ +function getCornerValue( values, corner ) { + if ( corner === 'all' ) { + return getAllValue( values ); + } + + // Handle string values (shorthand) + if ( typeof values === 'string' ) { + return values; + } + + // Handle object values (longhand) + return values?.[ corner ]; +} + +/** + * Gets the selected unit for a specific corner. + * + * @param {Object} selectedUnits Units object. + * @param {string} corner Corner name. + * + * @return {string} The selected unit. + */ +function getCornerUnit( selectedUnits, corner ) { + if ( corner === 'all' ) { + return selectedUnits.flat; + } + return selectedUnits[ corner ]; +} + +/** + * Creates an onChange handler for a specific corner. + * + * @param {string} corner Corner name. + * @param {Object} values Current values. + * @param {Function} onChange Original onChange callback. + * + * @return {Function} Corner-specific onChange handler. + */ +function createCornerChangeHandler( corner, values, onChange ) { + return ( newValue ) => { + if ( corner === 'all' ) { + onChange( { + topLeft: newValue, + topRight: newValue, + bottomLeft: newValue, + bottomRight: newValue, + } ); + } else { + // For shorthand style & backwards compatibility, handle flat string value. + const currentValues = + typeof values !== 'string' + ? values || {} + : { + topLeft: values, + topRight: values, + bottomLeft: values, + bottomRight: values, + }; + + onChange( { + ...currentValues, + [ corner ]: newValue, + } ); + } + }; +} + +/** + * Creates a unit change handler for a specific corner. + * + * @param {string} corner Corner name. + * @param {Object} selectedUnits Current selected units. + * @param {Function} setSelectedUnits Unit setter function. + * + * @return {Function} Corner-specific unit change handler. + */ +function createCornerUnitChangeHandler( + corner, + selectedUnits, + setSelectedUnits +) { + return ( newUnit ) => { + const newUnits = { ...selectedUnits }; + if ( corner === 'all' ) { + newUnits.flat = newUnit; + newUnits.topLeft = newUnit; + newUnits.topRight = newUnit; + newUnits.bottomLeft = newUnit; + newUnits.bottomRight = newUnit; + } else { + newUnits[ corner ] = newUnit; + } + setSelectedUnits( newUnits ); + }; +} + /** * Control to display border radius options. * @@ -97,17 +203,28 @@ export default function BorderRadiusControl( { onChange, values, presets } ) { { isLinked ? ( - <> - - + ) : ( { [ @@ -116,15 +233,31 @@ export default function BorderRadiusControl( { onChange, values, presets } ) { 'bottomLeft', 'bottomRight', ].map( ( corner ) => ( - ) ) } diff --git a/packages/block-editor/src/components/border-radius-control/single-input-control.js b/packages/block-editor/src/components/border-radius-control/single-input-control.js deleted file mode 100644 index fdca7a14521c69..00000000000000 --- a/packages/block-editor/src/components/border-radius-control/single-input-control.js +++ /dev/null @@ -1,278 +0,0 @@ -/** - * WordPress dependencies - */ -import { - __experimentalParseQuantityAndUnitFromRawValue as parseQuantityAndUnitFromRawValue, - __experimentalUnitControl as UnitControl, - __experimentalHStack as HStack, - Icon, - Tooltip, - RangeControl, - Button, - CustomSelectControl, -} from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; -import { useState } from '@wordpress/element'; -import { settings } from '@wordpress/icons'; - -/** - * Internal dependencies - */ -import { - getAllValue, - getCustomValueFromPreset, - getPresetValueFromControlValue, - getPresetValueFromCustomValue, - getSliderValueFromPreset, - isValuePreset, - convertPresetsToCustomValues, -} from './utils'; -import { - CORNERS, - ICONS, - MIN_BORDER_RADIUS_VALUE, - MAX_BORDER_RADIUS_VALUES, - RANGE_CONTROL_MAX_SIZE, -} from './constants'; - -export default function SingleInputControl( { - corner, - onChange, - selectedUnits, - setSelectedUnits, - values: valuesProp, - units, - presets, -} ) { - const changeCornerValue = ( validatedValue ) => { - if ( corner === 'all' ) { - onChange( { - topLeft: validatedValue, - topRight: validatedValue, - bottomLeft: validatedValue, - bottomRight: validatedValue, - } ); - } else { - onChange( { - ...values, - [ corner ]: validatedValue, - } ); - } - }; - - const onChangeValue = ( next ) => { - if ( ! onChange ) { - return; - } - - // Filter out CSS-unit-only values to prevent invalid styles. - const isNumeric = ! isNaN( parseFloat( next ) ); - const nextValue = isNumeric ? next : undefined; - changeCornerValue( nextValue ); - }; - - const onChangeUnit = ( next ) => { - const newUnits = { ...selectedUnits }; - if ( corner === 'all' ) { - newUnits.flat = next; - newUnits.topLeft = next; - newUnits.topRight = next; - newUnits.bottomLeft = next; - newUnits.bottomRight = next; - } else { - newUnits[ corner ] = 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, - }; - - // For 'all' corner, convert presets to custom values before calling getAllValue - // For individual corners, check if the value should be converted to a preset - let value; - if ( corner === 'all' ) { - const convertedValues = convertPresetsToCustomValues( values, presets ); - const customValue = getAllValue( convertedValues ); - value = getPresetValueFromCustomValue( customValue, presets ); - } else { - value = getPresetValueFromCustomValue( values[ corner ], presets ); - } - const resolvedPresetValue = isValuePreset( value ) - ? getCustomValueFromPreset( value, presets ) - : value; - const [ parsedQuantity, parsedUnit ] = - parseQuantityAndUnitFromRawValue( resolvedPresetValue ); - const computedUnit = value - ? parsedUnit - : selectedUnits[ corner ] || selectedUnits.flat || 'px'; - const unitConfig = - units && units.find( ( item ) => item.value === computedUnit ); - const step = unitConfig?.step || 1; - const [ showCustomValueControl, setShowCustomValueControl ] = useState( - value !== undefined && ! isValuePreset( value ) - ); - const showRangeControl = presets.length <= RANGE_CONTROL_MAX_SIZE; - const presetIndex = getSliderValueFromPreset( value, presets ); - const rangeTooltip = ( newValue ) => - value === undefined ? undefined : presets[ newValue ]?.name; - const marks = presets - .slice( 1, presets.length - 1 ) - .map( ( _newValue, index ) => ( { - value: index + 1, - label: undefined, - } ) ); - const hasPresets = marks.length > 0; - let options = []; - if ( ! showRangeControl ) { - options = [ - ...presets, - { - name: __( 'Custom' ), - slug: 'custom', - size: resolvedPresetValue, - }, - ].map( ( size, index ) => ( { - key: index, - name: size.name, - } ) ); - } - const icon = ICONS[ corner ]; - - const handleSliderChange = ( next ) => { - const val = - next !== undefined ? `${ next }${ computedUnit }` : undefined; - changeCornerValue( val ); - }; - - // 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 ( - - { icon && ( - - ) } - { ( ! hasPresets || showCustomValueControl ) && ( -
- -
- -
-
- -
- ) } - { hasPresets && showRangeControl && ! showCustomValueControl && ( - { - changeCornerValue( - getPresetValueFromControlValue( - newSize, - 'range', - presets - ) - ); - } } - withInputField={ false } - aria-valuenow={ presetIndex } - aria-valuetext={ presets[ presetIndex ]?.name } - renderTooltipContent={ rangeTooltip } - min={ 0 } - max={ presets.length - 1 } - marks={ marks } - label={ CORNERS[ corner ] } - hideLabelFromVision - __nextHasNoMarginBottom - /> - ) } - - { ! showRangeControl && ! showCustomValueControl && ( - option.key === presetIndex - ) || options[ options.length - 1 ] - } - onChange={ ( selection ) => { - if ( - selection.selectedItem.key === - options.length - 1 - ) { - setShowCustomValueControl( true ); - } else { - changeCornerValue( - getPresetValueFromControlValue( - selection.selectedItem.key, - 'selectList', - presets - ) - ); - } - } } - options={ options } - label={ CORNERS[ corner ] } - hideLabelFromVision - size="__unstable-large" - /> - ) } - { hasPresets && ( -