Skip to content

Commit

Permalink
BoxControl: Add support for presets (WordPress#67688)
Browse files Browse the repository at this point in the history
Co-authored-by: youknowriad <[email protected]>
Co-authored-by: ntsekouras <[email protected]>
Co-authored-by: ramonjd <[email protected]>
Co-authored-by: jorgefilipecosta <[email protected]>
Co-authored-by: mirka <[email protected]>
Co-authored-by: jameskoster <[email protected]>
  • Loading branch information
7 people authored Dec 12, 2024
1 parent 3de8dfb commit 4263e85
Show file tree
Hide file tree
Showing 7 changed files with 256 additions and 42 deletions.
6 changes: 5 additions & 1 deletion packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Enhancements

- `BoxControl`: Add presets support ([#67688](https://github.com/WordPress/gutenberg/pull/67688)).

### Deprecations

- `SelectControl`: Deprecate 36px default size ([#66898](https://github.com/WordPress/gutenberg/pull/66898)).
Expand All @@ -24,7 +28,7 @@
- `Menu`: Replace hardcoded white color with theme-ready variable ([#67649](https://github.com/WordPress/gutenberg/pull/67649)).
- `Navigation` (deprecated): Replace hardcoded white color with theme-ready variable ([#67649](https://github.com/WordPress/gutenberg/pull/67649)).
- `ToggleGroupControl`: Replace hardcoded white color with theme-ready variable ([#67649](https://github.com/WordPress/gutenberg/pull/67649)).
- `RangeControl`: Update the design of the range control marks ([#67611](https://github.com/WordPress/gutenberg/pull/67611))
- `RangeControl`: Update the design of the range control marks ([#67611](https://github.com/WordPress/gutenberg/pull/67611)).
- `BorderBoxControl`: Reduce gap value when unlinked ([#67049](https://github.com/WordPress/gutenberg/pull/67049)).
- `DropdownMenu`: Increase option height to 40px ([#67435](https://github.com/WordPress/gutenberg/pull/67435)).
- `MenuItem`: Increase height to 40px ([#67435](https://github.com/WordPress/gutenberg/pull/67435)).
Expand Down
16 changes: 16 additions & 0 deletions packages/components/src/box-control/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,22 @@ A callback function when an input value changes.
- Required: No
- Default: `() => {}`

### `presets`

Available 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

### `resetValues`

The `top`, `right`, `bottom`, and `left` box dimension values to use when the control is reset.
Expand Down
4 changes: 4 additions & 0 deletions packages/components/src/box-control/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ function BoxControl( {
splitOnAxis = false,
allowReset = true,
resetValues = DEFAULT_VALUES,
presets,
presetKey,
onMouseOver,
onMouseOut,
}: BoxControlProps ) {
Expand Down Expand Up @@ -153,6 +155,8 @@ function BoxControl( {
sides,
values: inputValues,
__next40pxDefaultSize,
presets,
presetKey,
};

maybeWarnDeprecated36pxSize( {
Expand Down
182 changes: 141 additions & 41 deletions packages/components/src/box-control/input-control.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -11,10 +13,13 @@ import Tooltip from '../tooltip';
import { parseQuantityAndUnitFromRawValue } from '../unit-control/utils';
import {
CUSTOM_VALUE_SETTINGS,
getAllowedSides,
getMergedValue,
isValueMixed,
getAllowedSides,
getPresetIndexFromValue,
getPresetValueFromIndex,
isValuePreset,
isValuesDefined,
isValueMixed,
LABELS,
} from './utils';
import {
Expand All @@ -24,6 +29,7 @@ import {
StyledUnitControl,
} from './styles/box-control-styles';
import type { BoxControlInputControlProps, BoxControlValue } from './types';
import Button from '../button';

const noop = () => {};

Expand Down Expand Up @@ -79,6 +85,8 @@ export default function BoxInputControl( {
sides,
side,
min = 0,
presets,
presetKey,
...props
}: BoxControlInputControlProps ) {
const defaultValuesToModify = getSidesToModify( side, sides );
Expand All @@ -91,6 +99,15 @@ export default function BoxInputControl( {
onChange( nextValues );
};

const handleRawOnValueChange = ( next?: string ) => {
const nextValues = { ...values };
defaultValuesToModify.forEach( ( modifiedSide ) => {
nextValues[ modifiedSide ] = next;
} );

handleOnChange( nextValues );
};

const handleOnValueChange = (
next?: string,
extra?: { event: React.SyntheticEvent< Element, Event > }
Expand Down Expand Up @@ -148,52 +165,135 @@ export default function BoxInputControl( {
const usedValue =
mergedValue === undefined && computedUnit ? computedUnit : mergedValue;
const mixedPlaceholder = isMixed || isMixedUnit ? __( 'Mixed' ) : undefined;
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 presetIndex = hasPresetValue
? getPresetIndexFromValue( mergedValue, presetKey, presets )
: undefined;
const marks = hasPresets
? [ { value: 0, label: '', tooltip: __( 'None' ) } ].concat(
presets.map( ( preset, index ) => ( {
value: index + 1,
label: '',
tooltip: preset.name ?? preset.slug,
} ) )
)
: [];

return (
<InputWrapper key={ `box-control-${ side }` } expanded>
<FlexedBoxControlIcon side={ side } sides={ sides } />
<Tooltip placement="top-end" text={ LABELS[ side ] }>
<StyledUnitControl
{ ...props }
min={ min }
__shouldNotWarnDeprecated36pxSize
__next40pxDefaultSize={ __next40pxDefaultSize }
className="component-box-control__unit-control"
id={ inputId }
isPressEnterToChange
disableUnits={ isMixed || isMixedUnit }
value={ usedValue }
onChange={ handleOnValueChange }
onUnitChange={ handleOnUnitChange }
onFocus={ handleOnFocus }
{ showCustomValueControl && (
<>
<Tooltip placement="top-end" text={ LABELS[ side ] }>
<StyledUnitControl
{ ...props }
min={ min }
__shouldNotWarnDeprecated36pxSize
__next40pxDefaultSize={ __next40pxDefaultSize }
className="component-box-control__unit-control"
id={ inputId }
isPressEnterToChange
disableUnits={ isMixed || isMixedUnit }
value={ usedValue }
onChange={ handleOnValueChange }
onUnitChange={ handleOnUnitChange }
onFocus={ handleOnFocus }
label={ LABELS[ side ] }
placeholder={ mixedPlaceholder }
hideLabelFromVision
/>
</Tooltip>

<FlexedRangeControl
__nextHasNoMarginBottom
__next40pxDefaultSize={ __next40pxDefaultSize }
__shouldNotWarnDeprecated36pxSize
aria-controls={ inputId }
label={ LABELS[ side ] }
hideLabelFromVision
onChange={ ( newValue ) => {
handleOnValueChange(
newValue !== undefined
? [ newValue, computedUnit ].join( '' )
: undefined
);
} }
min={ isFinite( min ) ? 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 && (
<FlexedRangeControl
__next40pxDefaultSize
className="spacing-sizes-control__range-control"
value={ presetIndex !== undefined ? presetIndex + 1 : 0 }
onChange={ ( newIndex ) => {
const newValue =
newIndex === 0 || newIndex === undefined
? undefined
: getPresetValueFromIndex(
newIndex - 1,
presetKey,
presets
);
handleRawOnValueChange( newValue );
} }
withInputField={ false }
aria-valuenow={
presetIndex !== undefined ? presetIndex + 1 : 0
}
aria-valuetext={
marks[ presetIndex !== undefined ? presetIndex + 1 : 0 ]
.label
}
renderTooltipContent={ ( index ) =>
marks[ ! index ? 0 : index ].tooltip
}
min={ 0 }
max={ marks.length - 1 }
marks={ marks }
label={ LABELS[ side ] }
placeholder={ mixedPlaceholder }
hideLabelFromVision
__nextHasNoMarginBottom
/>
) }

{ hasPresets && (
<Button
label={
showCustomValueControl
? __( 'Use size preset' )
: __( 'Set custom size' )
}
icon={ settings }
onClick={ () => {
setShowCustomValueControl( ! showCustomValueControl );
} }
isPressed={ showCustomValueControl }
size="small"
iconSize={ 24 }
/>
</Tooltip>

<FlexedRangeControl
__nextHasNoMarginBottom
__next40pxDefaultSize={ __next40pxDefaultSize }
__shouldNotWarnDeprecated36pxSize
aria-controls={ inputId }
label={ LABELS[ side ] }
hideLabelFromVision
onChange={ ( newValue ) => {
handleOnValueChange(
newValue !== undefined
? [ newValue, computedUnit ].join( '' )
: undefined
);
} }
min={ isFinite( min ) ? min : 0 }
max={ CUSTOM_VALUE_SETTINGS[ computedUnit ?? 'px' ]?.max ?? 10 }
step={
CUSTOM_VALUE_SETTINGS[ computedUnit ?? 'px' ]?.step ?? 0.1
}
value={ parsedQuantity ?? 0 }
withInputField={ false }
/>
) }
</InputWrapper>
);
}
12 changes: 12 additions & 0 deletions packages/components/src/box-control/stories/index.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,15 @@ AxialControlsWithSingleSide.args = {
sides: [ 'horizontal' ],
splitOnAxis: true,
};

export const ControlWithPresets = TemplateControlled.bind( {} );
ControlWithPresets.args = {
...Default.args,
presets: [
{ name: 'Small', slug: 'small', value: '4px' },
{ name: 'Medium', slug: 'medium', value: '8px' },
{ name: 'Large', slug: 'large', value: '12px' },
{ name: 'Extra Large', slug: 'extra-large', value: '16px' },
],
presetKey: 'padding',
};
18 changes: 18 additions & 0 deletions packages/components/src/box-control/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ export type CustomValueUnits = {
[ key: string ]: { max: number; step: number };
};

export interface Preset {
name: string;
slug: string;
value?: string;
}

type UnitControlPassthroughProps = Omit<
UnitControlProps,
'label' | 'onChange' | 'onFocus' | 'units'
Expand Down Expand Up @@ -94,6 +100,16 @@ export type BoxControlProps = Pick< UnitControlProps, 'units' > &
* @default false
*/
__next40pxDefaultSize?: boolean;
/**
* Available presets to pick from.
*/
presets?: Preset[];
/**
* 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 }`
*/
presetKey?: string;
};

export type BoxControlInputControlProps = UnitControlPassthroughProps & {
Expand All @@ -120,6 +136,8 @@ export type BoxControlInputControlProps = UnitControlPassthroughProps & {
* It can be a concrete side like: left, right, top, bottom or a combined one like: horizontal, vertical.
*/
side: keyof typeof LABELS;
presets?: Preset[];
presetKey?: string;
};

export type BoxControlIconProps = {
Expand Down
Loading

0 comments on commit 4263e85

Please sign in to comment.