diff --git a/lib/block-supports/border.php b/lib/block-supports/border.php index e400a89fc0119d..9d8d97c690a7b8 100644 --- a/lib/block-supports/border.php +++ b/lib/block-supports/border.php @@ -56,8 +56,14 @@ function gutenberg_apply_border_support( $block_type, $block_attributes ) { gutenberg_has_border_feature_support( $block_type, 'radius' ) && isset( $block_attributes['style']['border']['radius'] ) ) { - $border_radius = (int) $block_attributes['style']['border']['radius']; - $styles[] = sprintf( 'border-radius: %dpx;', $border_radius ); + $border_radius = $block_attributes['style']['border']['radius']; + + // This check handles original unitless implementation. + if ( is_numeric( $border_radius ) ) { + $border_radius .= 'px'; + } + + $styles[] = sprintf( 'border-radius: %s;', $border_radius ); } // Border style. @@ -74,8 +80,14 @@ function gutenberg_apply_border_support( $block_type, $block_attributes ) { gutenberg_has_border_feature_support( $block_type, 'width' ) && isset( $block_attributes['style']['border']['width'] ) ) { - $border_width = intval( $block_attributes['style']['border']['width'] ); - $styles[] = sprintf( 'border-width: %dpx;', $border_width ); + $border_width = $block_attributes['style']['border']['width']; + + // This check handles original unitless implementation. + if ( is_numeric( $border_width ) ) { + $border_width .= 'px'; + } + + $styles[] = sprintf( 'border-width: %s;', $border_width ); } // Border color. diff --git a/packages/block-editor/src/hooks/border-radius.js b/packages/block-editor/src/hooks/border-radius.js index 50e9dece97eaf6..a301f0570725ad 100644 --- a/packages/block-editor/src/hooks/border-radius.js +++ b/packages/block-editor/src/hooks/border-radius.js @@ -1,16 +1,17 @@ /** * WordPress dependencies */ -import { RangeControl } from '@wordpress/components'; +import { __experimentalUnitControl as UnitControl } from '@wordpress/components'; +import { useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ +import { CSS_UNITS, parseUnit } from './border'; import { cleanEmptyObject } from './utils'; const MIN_BORDER_RADIUS_VALUE = 0; -const MAX_BORDER_RADIUS_VALUE = 50; /** * Inspector control panel containing the border radius related configuration. @@ -24,6 +25,15 @@ export function BorderRadiusEdit( props ) { setAttributes, } = props; + // Step value is maintained in state so step is appropriate for current unit + // even when current radius value is undefined. + const initialStep = parseUnit( style?.border?.radius ) === 'px' ? 1 : 0.25; + const [ step, setStep ] = useState( initialStep ); + + const onUnitChange = ( newUnit ) => { + setStep( newUnit === 'px' ? 1 : 0.25 ); + }; + const onChange = ( newRadius ) => { let newStyle = { ...style, @@ -33,7 +43,7 @@ export function BorderRadiusEdit( props ) { }, }; - if ( newRadius === undefined ) { + if ( newRadius === undefined || newRadius === '' ) { newStyle = cleanEmptyObject( newStyle ); } @@ -41,14 +51,14 @@ export function BorderRadiusEdit( props ) { }; return ( - ); } diff --git a/packages/block-editor/src/hooks/border-width.js b/packages/block-editor/src/hooks/border-width.js index 24e25f6a063850..341fca06fdc5bc 100644 --- a/packages/block-editor/src/hooks/border-width.js +++ b/packages/block-editor/src/hooks/border-width.js @@ -1,16 +1,17 @@ /** * WordPress dependencies */ -import { RangeControl } from '@wordpress/components'; +import { __experimentalUnitControl as UnitControl } from '@wordpress/components'; +import { useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ +import { CSS_UNITS, parseUnit } from './border'; import { cleanEmptyObject } from './utils'; const MIN_BORDER_WIDTH = 0; -const MAX_BORDER_WIDTH = 50; /** * Inspector control for configuring border width property. @@ -24,8 +25,17 @@ export const BorderWidthEdit = ( props ) => { setAttributes, } = props; + // Step value is maintained in state so step is appropriate for current unit + // even when current radius value is undefined. + const initialStep = parseUnit( style?.border?.width ) === 'px' ? 1 : 0.25; + const [ step, setStep ] = useState( initialStep ); + + const onUnitChange = ( newUnit ) => { + setStep( newUnit === 'px' ? 1 : 0.25 ); + }; + const onChange = ( newWidth ) => { - const newStyle = { + let newStyle = { ...style, border: { ...style?.border, @@ -33,18 +43,22 @@ export const BorderWidthEdit = ( props ) => { }, }; - setAttributes( { style: cleanEmptyObject( newStyle ) } ); + if ( newWidth === undefined || newWidth === '' ) { + newStyle = cleanEmptyObject( newStyle ); + } + + setAttributes( { style: newStyle } ); }; return ( - ); }; diff --git a/packages/block-editor/src/hooks/border.js b/packages/block-editor/src/hooks/border.js index 6749afa663482f..f7db904d666aa1 100644 --- a/packages/block-editor/src/hooks/border.js +++ b/packages/block-editor/src/hooks/border.js @@ -16,7 +16,44 @@ import { BorderRadiusEdit } from './border-radius'; import { BorderStyleEdit } from './border-style'; import { BorderWidthEdit } from './border-width'; +const isWeb = Platform.OS === 'web'; + export const BORDER_SUPPORT_KEY = '__experimentalBorder'; +export const CSS_UNITS = [ + { + value: 'px', + label: isWeb ? 'px' : __( 'Pixels (px)' ), + default: '', + a11yLabel: __( 'Pixels (px)' ), + }, + { + value: 'em', + label: isWeb ? 'em' : __( 'Relative to parent font size (em)' ), + default: '', + a11yLabel: __( 'Relative to parent font size (em)' ), + }, + { + value: 'rem', + label: isWeb ? 'rem' : __( 'Relative to root font size (rem)' ), + default: '', + a11yLabel: __( 'Relative to root font size (rem)' ), + }, +]; + +/** + * Parses a CSS unit from a border CSS value. + * + * @param {string} cssValue CSS value to parse e.g. `10px` or `1.5em`. + * @return {string} CSS unit from provided value or default 'px'. + */ +export function parseUnit( cssValue ) { + const value = String( cssValue ).trim(); + const unitMatch = value.match( /[\d.\-\+]*\s*(.*)/ )[ 1 ]; + const unit = unitMatch !== undefined ? unitMatch.toLowerCase() : ''; + const currentUnit = CSS_UNITS.find( ( item ) => item.value === unit ); + + return currentUnit?.value || 'px'; +} export function BorderPanel( props ) { const isDisabled = useIsBorderDisabled( props ); @@ -44,7 +81,11 @@ export function BorderPanel( props ) { return ( - + { isStyleSupported && } { isWidthSupported && } { isRadiusSupported && } diff --git a/packages/block-editor/src/hooks/border.scss b/packages/block-editor/src/hooks/border.scss new file mode 100644 index 00000000000000..61d75c59454635 --- /dev/null +++ b/packages/block-editor/src/hooks/border.scss @@ -0,0 +1,10 @@ +.block-editor-hooks__border-controls { + .components-unit-control-wrapper { + margin-bottom: $grid-unit-30; + + &:last-child { + margin-bottom: $grid-unit-10; + } + } +} + diff --git a/packages/block-editor/src/hooks/test/style.js b/packages/block-editor/src/hooks/test/style.js index 75a520b14ec6f5..85adb56c802e19 100644 --- a/packages/block-editor/src/hooks/test/style.js +++ b/packages/block-editor/src/hooks/test/style.js @@ -18,8 +18,8 @@ describe( 'getInlineStyles', () => { color: { text: 'red', background: 'black' }, typography: { lineHeight: 1.5, fontSize: 10 }, border: { - radius: 10, - width: 3, + radius: '10px', + width: '1em', style: 'dotted', color: '#21759b', }, @@ -31,9 +31,9 @@ describe( 'getInlineStyles', () => { ).toEqual( { backgroundColor: 'black', borderColor: '#21759b', - borderRadius: 10, + borderRadius: '10px', borderStyle: 'dotted', - borderWidth: 3, + borderWidth: '1em', color: 'red', lineHeight: 1.5, fontSize: 10, diff --git a/packages/block-editor/src/style.scss b/packages/block-editor/src/style.scss index 0d9b9b01cbde18..92fc673e65ccf5 100644 --- a/packages/block-editor/src/style.scss +++ b/packages/block-editor/src/style.scss @@ -58,6 +58,7 @@ @import "./components/warning/style.scss"; @import "./hooks/anchor.scss"; @import "./hooks/layout.scss"; +@import "./hooks/border.scss"; // This tag marks the end of the styles that apply to editing canvas contents and need to be manipulated when we resize the editor. #end-resizable-editor-section { diff --git a/packages/block-library/src/button/edit.js b/packages/block-library/src/button/edit.js index 70ce701138999b..15235369b2ee05 100644 --- a/packages/block-library/src/button/edit.js +++ b/packages/block-library/src/button/edit.js @@ -225,9 +225,7 @@ function ButtonEdit( props ) { } ) } style={ { - borderRadius: borderRadius - ? borderRadius + 'px' - : undefined, + borderRadius: borderRadius ? borderRadius : undefined, ...colorProps.style, } } onSplit={ ( value ) => diff --git a/packages/block-library/src/button/save.js b/packages/block-library/src/button/save.js index 695e27e07b5b40..af4f4e6f566f35 100644 --- a/packages/block-library/src/button/save.js +++ b/packages/block-library/src/button/save.js @@ -38,7 +38,7 @@ export default function save( { attributes, className } ) { } ); const buttonStyle = { - borderRadius: borderRadius ? borderRadius + 'px' : undefined, + borderRadius: borderRadius ? borderRadius : undefined, ...colorProps.style, }; diff --git a/packages/block-library/src/search/edit.js b/packages/block-library/src/search/edit.js index ec794a60e44aca..8301ca2c0616be 100644 --- a/packages/block-library/src/search/edit.js +++ b/packages/block-library/src/search/edit.js @@ -50,7 +50,7 @@ import { // Used to calculate border radius adjustment to avoid "fat" corners when // button is placed inside wrapper. -const DEFAULT_INNER_PADDING = 4; +const DEFAULT_INNER_PADDING = '4px'; export default function SearchEdit( { className, @@ -323,10 +323,16 @@ export default function SearchEdit( { if ( 'button-inside' === buttonPosition && style?.border?.radius ) { // We have button inside wrapper and a border radius value to apply. // Add default padding so we don't get "fat" corners. - const outerRadius = - parseInt( style?.border?.radius, 10 ) + DEFAULT_INNER_PADDING; + // + // CSS calc() is used here to support non-pixel units. The inline + // style using calc() will only apply if both values have units. + const radius = Number.isInteger( borderRadius ) + ? `${ borderRadius }px` + : borderRadius; - return { borderRadius: `${ outerRadius }px` }; + return { + borderRadius: `calc(${ radius } + ${ DEFAULT_INNER_PADDING })`, + }; } return undefined; diff --git a/packages/block-library/src/search/index.php b/packages/block-library/src/search/index.php index eba67deeecdce6..faf8741ce7e8bc 100644 --- a/packages/block-library/src/search/index.php +++ b/packages/block-library/src/search/index.php @@ -190,7 +190,8 @@ function styles_for_block_core_search( $attributes ) { if ( $has_border_radius ) { // Shared style for button and input radius values. $border_radius = $attributes['style']['border']['radius']; - $shared_styles[] = sprintf( 'border-radius: %spx;', esc_attr( $border_radius ) ); + $border_radius = is_numeric( $border_radius ) ? $border_radius . 'px' : $border_radius; + $shared_styles[] = sprintf( 'border-radius: %s;', esc_attr( $border_radius ) ); // Apply wrapper border radius if button placed inside. $button_inside = ! empty( $attributes['buttonPosition'] ) && @@ -199,10 +200,13 @@ function styles_for_block_core_search( $attributes ) { if ( $button_inside ) { // We adjust the border radius value for the outer wrapper element // to make it visually consistent with the radius applied to inner - // elements. - $default_padding = 4; - $adjusted_radius = $border_radius + $default_padding; - $wrapper_styles[] = sprintf( 'border-radius: %dpx;', esc_attr( $adjusted_radius ) ); + // elements. calc() is used to support non-pixel CSS units. + $default_padding = '4px'; + $wrapper_styles[] = sprintf( + 'border-radius: calc(%s + %s);', + esc_attr( $border_radius ), + esc_attr( $default_padding ) + ); } }