diff --git a/packages/block-editor/src/components/height-control/index.js b/packages/block-editor/src/components/height-control/index.js new file mode 100644 index 00000000000000..cc1eea0b4bad86 --- /dev/null +++ b/packages/block-editor/src/components/height-control/index.js @@ -0,0 +1,123 @@ +/** + * 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 useSetting from '../use-setting'; + +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 }, +}; + +export default function HeightControl( { + onChange, + label = __( 'Height' ), + value, +} ) { + const customRangeValue = parseFloat( value ); + + const units = useCustomUnits( { + availableUnits: useSetting( 'spacing.units' ) || [ + '%', + '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 ( + [ 'vh', 'vw', '%' ].includes( newUnit ) && + currentValue > 100 + ) { + // When converting to `vh`, `vw`, or `%` units, cap the new value at 100. + onChange( 100 + newUnit ); + } + }; + + return ( +
+ + { label } + + + + + + + + + + + +
+ ); +} diff --git a/packages/block-editor/src/components/height-control/stories/index.js b/packages/block-editor/src/components/height-control/stories/index.js new file mode 100644 index 00000000000000..f4b586a96b0e33 --- /dev/null +++ b/packages/block-editor/src/components/height-control/stories/index.js @@ -0,0 +1,21 @@ +/** + * WordPress dependencies + */ +import { useState } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import HeightControl from '../'; + +export default { + component: HeightControl, + title: 'BlockEditor/HeightControl', +}; + +const Template = ( props ) => { + const [ value, setValue ] = useState(); + return ; +}; + +export const Default = Template.bind( {} ); diff --git a/packages/block-editor/src/components/height-control/style.scss b/packages/block-editor/src/components/height-control/style.scss new file mode 100644 index 00000000000000..add0866835f767 --- /dev/null +++ b/packages/block-editor/src/components/height-control/style.scss @@ -0,0 +1,5 @@ +.block-editor-height-control { + border: 0; + margin: 0; + padding: 0; +} diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js index 7743a98066c437..03ce8f3880ad32 100644 --- a/packages/block-editor/src/components/index.js +++ b/packages/block-editor/src/components/index.js @@ -53,6 +53,7 @@ export { default as __experimentalColorGradientControl } from './colors-gradient export { default as __experimentalColorGradientSettingsDropdown } from './colors-gradients/dropdown'; export { default as __experimentalPanelColorGradientSettings } from './colors-gradients/panel-color-gradient-settings'; export { default as __experimentalUseMultipleOriginColorsAndGradients } from './colors-gradients/use-multiple-origin-colors-and-gradients'; +export { default as __experimentalHeightControl } from './height-control'; export { default as __experimentalImageEditor, ImageEditingProvider as __experimentalImageEditingProvider, diff --git a/packages/block-editor/src/hooks/dimensions.js b/packages/block-editor/src/hooks/dimensions.js index 44a9fd83278a95..3b33df400fcfc3 100644 --- a/packages/block-editor/src/hooks/dimensions.js +++ b/packages/block-editor/src/hooks/dimensions.js @@ -182,7 +182,6 @@ export function DimensionsPanel( props ) { ) } { ! isMinHeightDisabled && ( hasMinHeightValue( props ) } label={ __( 'Min. height' ) } onDeselect={ () => resetMinHeight( props ) } diff --git a/packages/block-editor/src/hooks/min-height.js b/packages/block-editor/src/hooks/min-height.js index 3167edba8a8293..e123f0cee98b22 100644 --- a/packages/block-editor/src/hooks/min-height.js +++ b/packages/block-editor/src/hooks/min-height.js @@ -2,16 +2,13 @@ * WordPress dependencies */ import { getBlockSupport } from '@wordpress/blocks'; -import { - __experimentalUseCustomUnits as useCustomUnits, - __experimentalUnitControl as UnitControl, -} from '@wordpress/components'; import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ import useSetting from '../components/use-setting'; +import HeightControl from '../components/height-control'; import { DIMENSIONS_SUPPORT_KEY } from './dimensions'; import { cleanEmptyObject } from './utils'; @@ -81,17 +78,6 @@ export function MinHeightEdit( props ) { setAttributes, } = props; - const units = useCustomUnits( { - availableUnits: useSetting( 'dimensions.units' ) || [ - '%', - 'px', - 'em', - 'rem', - 'vh', - 'vw', - ], - } ); - if ( useIsMinHeightDisabled( props ) ) { return null; } @@ -109,13 +95,10 @@ export function MinHeightEdit( props ) { }; return ( - ); } diff --git a/packages/block-editor/src/style.scss b/packages/block-editor/src/style.scss index 853a030f045945..7d9af577f32854 100644 --- a/packages/block-editor/src/style.scss +++ b/packages/block-editor/src/style.scss @@ -32,6 +32,7 @@ @import "./components/date-format-picker/style.scss"; @import "./components/duotone-control/style.scss"; @import "./components/font-appearance-control/style.scss"; +@import "./components/height-control/style.scss"; @import "./components/image-size-control/style.scss"; @import "./components/inner-blocks/style.scss"; @import "./components/inserter-list-item/style.scss"; diff --git a/packages/edit-site/src/components/global-styles/dimensions-panel.js b/packages/edit-site/src/components/global-styles/dimensions-panel.js index d27a109e2b752a..1b88bc4074dc87 100644 --- a/packages/edit-site/src/components/global-styles/dimensions-panel.js +++ b/packages/edit-site/src/components/global-styles/dimensions-panel.js @@ -18,6 +18,7 @@ import { } from '@wordpress/components'; import { __experimentalUseCustomSides as useCustomSides, + __experimentalHeightControl as HeightControl, __experimentalSpacingSizesControl as SpacingSizesControl, } from '@wordpress/block-editor'; import { Icon, positionCenter, stretchWide } from '@wordpress/icons'; @@ -556,19 +557,15 @@ export default function DimensionsPanel( { name } ) { ) } { showMinHeightControl && ( - ) }