Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SizeControl component that allows negative values & customizable range #61180

Open
wants to merge 2 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
165 changes: 2 additions & 163 deletions packages/block-editor/src/components/height-control/index.js
@@ -1,54 +1,8 @@
/**
* WordPress dependencies
*/
import { useMemo } from '@wordpress/element';
import {
BaseControl,
RangeControl,
Flex,
FlexItem,
__experimentalSpacer as Spacer,
__experimentalUseCustomUnits as useCustomUnits,
__experimentalUnitControl as UnitControl,
__experimentalParseQuantityAndUnitFromRawValue as parseQuantityAndUnitFromRawValue,
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import { useSettings } from '../use-settings';

const RANGE_CONTROL_CUSTOM_SETTINGS = {
px: { max: 1000, step: 1 },
'%': { max: 100, step: 1 },
vw: { max: 100, step: 1 },
vh: { max: 100, step: 1 },
em: { max: 50, step: 0.1 },
rem: { max: 50, step: 0.1 },
svw: { max: 100, step: 1 },
lvw: { max: 100, step: 1 },
dvw: { max: 100, step: 1 },
svh: { max: 100, step: 1 },
lvh: { max: 100, step: 1 },
dvh: { max: 100, step: 1 },
vi: { max: 100, step: 1 },
svi: { max: 100, step: 1 },
lvi: { max: 100, step: 1 },
dvi: { max: 100, step: 1 },
vb: { max: 100, step: 1 },
svb: { max: 100, step: 1 },
lvb: { max: 100, step: 1 },
dvb: { max: 100, step: 1 },
vmin: { max: 100, step: 1 },
svmin: { max: 100, step: 1 },
lvmin: { max: 100, step: 1 },
dvmin: { max: 100, step: 1 },
vmax: { max: 100, step: 1 },
svmax: { max: 100, step: 1 },
lvmax: { max: 100, step: 1 },
dvmax: { max: 100, step: 1 },
};
import SizeControl from '../size-control';

/**
* HeightControl renders a linked unit control and range control for adjusting the height of a block.
Expand All @@ -67,122 +21,7 @@ export default function HeightControl( {
onChange,
value,
} ) {
const customRangeValue = parseFloat( value );

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

const selectedUnit =
useMemo(
() => parseQuantityAndUnitFromRawValue( value ),
[ value ]
)[ 1 ] ||
units[ 0 ]?.value ||
'px';

const handleSliderChange = ( next ) => {
onChange( [ next, selectedUnit ].join( '' ) );
};

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 );
}
};

return (
<fieldset className="block-editor-height-control">
<BaseControl.VisualLabel as="legend">
{ label }
</BaseControl.VisualLabel>
<Flex>
<FlexItem isBlock>
<UnitControl
value={ value }
units={ units }
onChange={ onChange }
onUnitChange={ handleUnitChange }
min={ 0 }
size={ '__unstable-large' }
label={ label }
hideLabelFromVision
/>
</FlexItem>
<FlexItem isBlock>
<Spacer marginX={ 2 } marginBottom={ 0 }>
<RangeControl
value={ customRangeValue }
min={ 0 }
max={
RANGE_CONTROL_CUSTOM_SETTINGS[ selectedUnit ]
?.max ?? 100
}
step={
RANGE_CONTROL_CUSTOM_SETTINGS[ selectedUnit ]
?.step ?? 0.1
}
withInputField={ false }
onChange={ handleSliderChange }
__nextHasNoMarginBottom
label={ label }
hideLabelFromVision
/>
</Spacer>
</FlexItem>
</Flex>
</fieldset>
<SizeControl label={label} onChange={onChange} value={value} />
);
}
64 changes: 64 additions & 0 deletions packages/block-editor/src/components/size-control/README.md
@@ -0,0 +1,64 @@
# Size Control

The `SizeControl` component adds a linked unit control and slider component for controlling the size of something within the block editor. It supports passing a label & rangeConfig, and is used for controlling the minimum size dimensions of Group blocks.

_Note:_ It is worth noting that the minimum size option is an opt-in feature. Themes need to declare support for it before it'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.

## Development guidelines

### Usage

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

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

const MyLineSizeControl = () => (
const [ value, setValue ] = useState();
<SizeControl
label={ 'My Size Control' }
onChange={ setValue }
value={ value }
/>
);
```

### Props

#### `value`

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

The value of the current size.

#### `onChange`

- **Type:** `Function`

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

#### `label`

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

A label for the size control. This is useful when using the size control for a feature that is controlled in the same way as size, but requires a different label. For example, "Minimum size".

#### `rangeConfig`

- **Type:** `Object`
- **Default:** `{}`

An object of the shape `{[key: units]:{min?: number; step?: number; max?: number}}` for overriding the min, max & step values.

#### `adaptiveRange`

- **Type:** `boolean`
- **Default:** `false`

This is to set the behavior when the value is out of it's specified range. When true, the min/max will adapt to the value.

## 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.