Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ module.exports = {
'Button',
'ComboboxControl',
'CustomSelectControl',
'DimensionControl',

'FontAppearanceControl',
'FontFamilyControl',
'FontSizePicker',
Expand Down
3 changes: 3 additions & 0 deletions backport-changelog/7.0/10628.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
https://github.com/WordPress/wordpress-develop/pull/10628

* https://github.com/WordPress/gutenberg/pull/73811
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ Settings related to dimensions.
| aspectRatios | Allow users to define aspect ratios for some blocks. | `[ { name, slug, ratio } ]` | |
| minHeight | Allow users to set custom minimum height. | `boolean` | `false` |
| width | Allow users to set custom width. | `boolean` | `false` |
| dimensionSizes | Dimension size presets for dimension block supports. | `[ { name, slug, size } ]` | |

---

Expand Down
10 changes: 10 additions & 0 deletions lib/class-wp-theme-json-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,15 @@ class WP_Theme_JSON_Gutenberg {
'classes' => array(),
'properties' => array( 'border-radius' ),
),
array(
'path' => array( 'dimensions', 'dimensionSizes' ),
'prevent_override' => false,
'use_default_names' => false,
'value_key' => 'size',
'css_vars' => '--wp--preset--dimension--$slug',
'classes' => array(),
'properties' => array( 'width', 'min-height' ),
),
);

/**
Expand Down Expand Up @@ -427,6 +436,7 @@ class WP_Theme_JSON_Gutenberg {
'aspectRatio' => null,
'aspectRatios' => null,
'defaultAspectRatios' => null,
'dimensionSizes' => null,
'minHeight' => null,
'width' => null,
),
Expand Down
10 changes: 10 additions & 0 deletions lib/theme-i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@
{
"name": "Aspect ratio name"
}
],
"dimensionSizes": [
{
"name": "Dimension size name"
}
]
},
"shadow": {
Expand Down Expand Up @@ -95,6 +100,11 @@
{
"name": "Aspect ratio name"
}
],
"dimensionSizes": [
{
"name": "Dimension size name"
}
]
},
"spacing": {
Expand Down
21 changes: 21 additions & 0 deletions packages/block-editor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,25 @@ _Returns_

Undocumented declaration.

### DimensionControl

DimensionControl renders a linked unit control and range control for adjusting dimensions of a block.

_Related_

- <https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/dimension-control/README.md>

_Parameters_

- _props_ `Object`:
- _props.label_ `?string`: A label for the control.
- _props.onChange_ `( value: string ) => void`: Called when the dimension value changes.
- _props.value_ `string`: The current dimension value.

_Returns_

- `Component`: The component to be rendered.

### FontSizePicker

_Related_
Expand Down Expand Up @@ -600,6 +619,8 @@ _Returns_

### HeightControl

> **Deprecated** Use DimensionControl instead.

HeightControl renders a linked unit control and range control for adjusting the height of a block.

_Related_
Expand Down
106 changes: 106 additions & 0 deletions packages/block-editor/src/components/dimension-control/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Dimension Control

The `DimensionControl` component provides a comprehensive control for managing dimensions of a block within the block editor. It supports both preset values from theme settings and custom values with unit controls and sliders. It can be used for controlling various dimension-related block supports like height, width, min-height, etc.

_Note:_ It is worth noting that dimension options are opt-in features. Themes need to declare support for them before they'll be available, and a convenient way to do that is via opting in to the [appearanceTools](/docs/how-to-guides/themes/global-settings-and-styles.md#opt-in-into-ui-controls) UI controls.

## Features

- **Preset Support**: Automatically detects and displays dimension presets from `dimensions.dimensionSizes` theme settings
- **Custom Values**: Allows entering custom dimension values with unit selection
- **Unit Conversion**: Intelligently converts between compatible units (px ↔ em/rem, viewport units)
- **Flexible UI**: Shows either a slider (≤8 presets) or dropdown (>8 presets) for preset selection
- **Toggle Between Modes**: Users can switch between preset and custom value modes

## Development guidelines

### Usage

Renders the markup for dimension control component, to be used in the block inspector.

```jsx
import { useState } from 'react';
import { DimensionControl } from '@wordpress/block-editor';

const MyDimensionControl = () => (
const [ value, setValue ] = useState();
<DimensionControl
label={ 'My Dimension Control' }
onChange={ setValue }
value={ value }
/>
);
```

### Props

#### `value`

- **Type:** `String` or `Number` or `Undefined`

The value of the current dimension.

#### `onChange`

- **Type:** `Function`

A callback function that handles the application of the dimension value.

#### `label`

- **Type:** `String`
- **Default:** `'Dimension'`

A label for the dimension control. This is useful when using the dimension control for different dimension properties. For example, "Height", "Width", "Minimum height", etc.

## Preset Integration

The component automatically integrates with the theme's dimension size presets defined in `dimensions.dimensionSizes`. These presets are merged from:

- **Default presets**: System-defined dimension sizes
- **Theme presets**: Dimension sizes defined by the active theme
- **Custom presets**: User-defined dimension sizes

### Preset Value Format

When a preset is selected, the component returns values in the format:
```
var:preset|dimension|{slug}
```

For example: `var:preset|dimension|medium`

### Theme Configuration

To provide dimension presets, add them to your theme's `theme.json`:

```json
{
"version": 2,
"settings": {
"dimensions": {
"dimensionSizes": [
{
"name": "Small",
"slug": "small",
"size": "16px"
},
{
"name": "Medium",
"slug": "medium",
"size": "32px"
},
{
"name": "Large",
"slug": "large",
"size": "64px"
}
]
}
}
}
```

## Related components

Block Editor components are components that can be used to compose the UI of your block editor. Thus, they can only be used under a [`BlockEditorProvider`](https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/provider/README.md) in the components tree.
166 changes: 166 additions & 0 deletions packages/block-editor/src/components/dimension-control/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/**
* WordPress dependencies
*/
import { useMemo, useState } from '@wordpress/element';
import {
BaseControl,
__experimentalParseQuantityAndUnitFromRawValue as parseQuantityAndUnitFromRawValue,
__experimentalUseCustomUnits as useCustomUnits,
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import { useSettings } from '../use-settings';
import PresetInputControl from '../preset-input-control';
import { CUSTOM_VALUE_SETTINGS } from '../preset-input-control/constants';

const EMPTY_ARRAY = [];

// Dimension-specific custom value settings - override defaults for larger dimension values
const DIMENSION_CUSTOM_VALUE_SETTINGS = {
...CUSTOM_VALUE_SETTINGS,
px: { max: 1000, steps: 1 },
em: { max: 50, steps: 0.1 },
rem: { max: 50, steps: 0.1 },
};

/**
* Hook to retrieve dimension sizes from theme settings.
*
* @param {Object} presets Dimension presets object containing default, theme, and custom sizes.
* @return {Array} Array of dimension size options.
*/
function useDimensionSizes( 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;
}, [ customSizes, themeSizes, defaultSizes ] );
}

/**
* DimensionControl renders a linked unit control and range control for adjusting dimensions of a block.
*
* @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/dimension-control/README.md
*
* @param {Object} props
* @param {?string} props.label A label for the control.
* @param {( value: string ) => void } props.onChange Called when the dimension value changes.
* @param {string} props.value The current dimension value.
*
* @return {Component} The component to be rendered.
*/
export default function DimensionControl( {
label = __( 'Dimension' ),
onChange,
value,
} ) {
const [ dimensionSizes, availableUnits ] = useSettings(
'dimensions.dimensionSizes',
'spacing.units'
);

const units = useCustomUnits( {
availableUnits: availableUnits || [
'%',
'px',
'em',
'rem',
'vh',
'vw',
],
} );

const options = useDimensionSizes( dimensionSizes );

// Track selected unit for PresetInputControl
const [ selectedUnit, setSelectedUnit ] = useState( () => {
const [ , unit ] = parseQuantityAndUnitFromRawValue( value );
return unit || units[ 0 ]?.value || 'px';
} );

const handleUnitChange = ( newUnit ) => {
// Attempt to smooth over differences between currentUnit and newUnit.
// This should slightly improve the experience of switching between unit types.
const [ currentValue, currentUnit ] =
parseQuantityAndUnitFromRawValue( value );

if ( [ 'em', 'rem' ].includes( newUnit ) && currentUnit === 'px' ) {
// Convert pixel value to an approximate of the new unit, assuming a root size of 16px.
onChange( ( currentValue / 16 ).toFixed( 2 ) + newUnit );
} else if (
[ 'em', 'rem' ].includes( currentUnit ) &&
newUnit === 'px'
) {
// Convert to pixel value assuming a root size of 16px.
onChange( Math.round( currentValue * 16 ) + newUnit );
} else if (
[
'%',
'vw',
'svw',
'lvw',
'dvw',
'vh',
'svh',
'lvh',
'dvh',
'vi',
'svi',
'lvi',
'dvi',
'vb',
'svb',
'lvb',
'dvb',
'vmin',
'svmin',
'lvmin',
'dvmin',
'vmax',
'svmax',
'lvmax',
'dvmax',
].includes( newUnit ) &&
currentValue > 100
) {
// When converting to `%` or viewport-relative units, cap the new value at 100.
onChange( 100 + newUnit );
}

setSelectedUnit( newUnit );
};

return (
<fieldset className="block-editor-dimension-control">
<BaseControl.VisualLabel as="legend">
{ label }
</BaseControl.VisualLabel>
<PresetInputControl
ariaLabel={ label }
className="block-editor-dimension-control"
customValueSettings={ DIMENSION_CUSTOM_VALUE_SETTINGS }
minimumCustomValue={ 0 }
onChange={ onChange }
onUnitChange={ handleUnitChange }
presets={ options }
presetType="dimension"
selectedUnit={ selectedUnit }
showTooltip
units={ units }
value={ value }
/>
</fieldset>
);
}
Loading
Loading