diff --git a/packages/components/src/box-control/README.md b/packages/components/src/box-control/README.md index 8bcb5a5dad8fc2..78b4280ffe36e7 100644 --- a/packages/components/src/box-control/README.md +++ b/packages/components/src/box-control/README.md @@ -124,3 +124,18 @@ The current values of the control, expressed as an object of `top`, `right`, `bo - Type: `BoxControlValue` - Required: No + +### `presets` + +The list of presets to pick from. + + - Type: `Preset` + - Required: No + +### `presetKey` + +The key of the preset to apply. If you provide a list of presets, you must provide a preset key to use. The format of preset selected values is going to be `var:preset|${ presetKey }|${ presetSlug }` + + - Type: `string` + - Required: No + diff --git a/packages/components/src/box-control/index.tsx b/packages/components/src/box-control/index.tsx index 7b219a33332d90..e0d3d00c976998 100644 --- a/packages/components/src/box-control/index.tsx +++ b/packages/components/src/box-control/index.tsx @@ -21,7 +21,7 @@ import { parseQuantityAndUnitFromRawValue } from '../unit-control/utils'; import { DEFAULT_VALUES, getInitialSide, - isValuesMixed, + isValueMixed, isValuesDefined, ALL_SIDES, } from './utils'; @@ -83,6 +83,8 @@ function BoxControl( { splitOnAxis = false, allowReset = true, resetValues = DEFAULT_VALUES, + presets, + presetKey, onMouseOver, onMouseOut, }: BoxControlProps ) { @@ -95,7 +97,7 @@ function BoxControl( { const [ isDirty, setIsDirty ] = useState( hasInitialValue ); const [ isLinked, setIsLinked ] = useState( - ! hasInitialValue || ! isValuesMixed( inputValues ) || hasOneSide + ! hasInitialValue || ! isValueMixed( inputValues ) || hasOneSide ); const [ side, setSide ] = useState< BoxControlIconProps[ 'side' ] >( @@ -153,6 +155,8 @@ function BoxControl( { sides, values: inputValues, __next40pxDefaultSize, + presets, + presetKey, }; maybeWarnDeprecated36pxSize( { diff --git a/packages/components/src/box-control/input-control.tsx b/packages/components/src/box-control/input-control.tsx index e0d0685e8e089f..ea0cacfeef43ec 100644 --- a/packages/components/src/box-control/input-control.tsx +++ b/packages/components/src/box-control/input-control.tsx @@ -3,6 +3,8 @@ */ import { useInstanceId } from '@wordpress/compose'; import { __ } from '@wordpress/i18n'; +import { useState } from '@wordpress/element'; +import { settings } from '@wordpress/icons'; /** * Internal dependencies @@ -12,9 +14,12 @@ import { parseQuantityAndUnitFromRawValue } from '../unit-control/utils'; import { ALL_SIDES, CUSTOM_VALUE_SETTINGS, - getAllValue, + getMergedValue, + getPresetIndexFromValue, + getPresetValueFromIndex, + isValuePreset, isValuesDefined, - isValuesMixed, + isValueMixed, LABELS, } from './utils'; import { @@ -24,6 +29,7 @@ import { StyledUnitControl, } from './styles/box-control-styles'; import type { BoxControlInputControlProps, BoxControlValue } from './types'; +import Button from '../button'; const noop = () => {}; @@ -96,6 +102,8 @@ export default function BoxInputControl( { setSelectedUnits, sides, side, + presets, + presetKey, ...props }: BoxControlInputControlProps ) { const defaultValuesToModify = getSidesToModify( side, sides ); @@ -108,6 +116,15 @@ export default function BoxInputControl( { onChange( nextValues ); }; + const handleRewOnValueChange = ( next?: string ) => { + const nextValues = { ...values }; + defaultValuesToModify.forEach( ( modifiedSide ) => { + nextValues[ modifiedSide ] = next; + } ); + + handleOnChange( nextValues ); + }; + const handleOnValueChange = ( next?: string, extra?: { event: React.SyntheticEvent< Element, Event > } @@ -143,15 +160,9 @@ export default function BoxInputControl( { setSelectedUnits( newUnits ); }; - const mergedValue = getAllValue( - values, - selectedUnits, - defaultValuesToModify - ); + const mergedValue = getMergedValue( values, defaultValuesToModify ); const hasValues = isValuesDefined( values ); - const isMixed = - hasValues && - isValuesMixed( values, selectedUnits, defaultValuesToModify ); + const isMixed = hasValues && isValueMixed( values, defaultValuesToModify ); const mixedPlaceholder = isMixed ? __( 'Mixed' ) : undefined; const [ parsedQuantity, parsedUnit ] = parseQuantityAndUnitFromRawValue( mergedValue ); @@ -160,52 +171,136 @@ export default function BoxInputControl( { : selectedUnits[ defaultValuesToModify[ 0 ] ]; const generatedId = useInstanceId( BoxInputControl, 'box-control-input' ); const inputId = [ generatedId, side ].join( '-' ); + const hasPresets = presets && presets.length > 0 && presetKey; + const hasPresetValue = + hasPresets && + mergedValue !== undefined && + ! isMixed && + isValuePreset( mergedValue, presetKey ); + const [ showCustomValueControl, setShowCustomValueControl ] = useState( + ! hasPresets || + ( ! hasPresetValue && ! isMixed && mergedValue !== undefined ) + ); + const showRangeControl = true; + const presetIndex = hasPresetValue + ? getPresetIndexFromValue( mergedValue, presetKey, presets ) + : undefined; + const marks = hasPresets + ? [ { value: 0, label: __( 'None' ) } ].concat( + presets.map( ( preset, index ) => ( { + value: index + 1, + label: preset.name, + } ) ) + ) + : []; return ( - - - handleOnValueChange( nextValue, extra ) + { showCustomValueControl && ( + <> + + + handleOnValueChange( nextValue, extra ) + } + onUnitChange={ handleOnUnitChange } + onFocus={ handleOnFocus } + label={ LABELS[ side ] } + placeholder={ mixedPlaceholder } + hideLabelFromVision + /> + + + { + handleOnValueChange( + newValue !== undefined + ? [ newValue, computedUnit ].join( '' ) + : undefined + ); + } } + min={ 0 } + max={ + CUSTOM_VALUE_SETTINGS[ computedUnit ?? 'px' ] + ?.max ?? 10 + } + step={ + CUSTOM_VALUE_SETTINGS[ computedUnit ?? 'px' ] + ?.step ?? 0.1 + } + value={ parsedQuantity ?? 0 } + withInputField={ false } + /> + + ) } + + { hasPresets && ! showCustomValueControl && showRangeControl && ( + { + const newValue = + newIndex === 0 || newIndex === undefined + ? undefined + : getPresetValueFromIndex( + newIndex - 1, + presetKey, + presets + ); + handleRewOnValueChange( newValue ); + } } + withInputField={ false } + aria-valuenow={ + presetIndex !== undefined ? presetIndex + 1 : 0 + } + aria-valuetext={ + marks[ presetIndex !== undefined ? presetIndex + 1 : 0 ] + .label } - onUnitChange={ handleOnUnitChange } - onFocus={ handleOnFocus } + renderTooltipContent={ ( index ) => + marks[ ! index ? 0 : index ].label + } + min={ 0 } + max={ marks.length - 1 } + marks={ marks } label={ LABELS[ side ] } - placeholder={ mixedPlaceholder } hideLabelFromVision + __nextHasNoMarginBottom + /> + ) } + + { hasPresets && ( +