Skip to content

Commit

Permalink
Add width block support
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronrobertshaw committed Aug 27, 2021
1 parent 36aceb3 commit 193263d
Show file tree
Hide file tree
Showing 12 changed files with 383 additions and 5 deletions.
14 changes: 13 additions & 1 deletion lib/block-supports/dimensions.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,19 @@ function gutenberg_apply_dimensions_support( $block_type, $block_attributes ) {
}
}

// Width support to be added in near future.
// Width.

// Width support flag can be true|false|"segmented" cannot use
// `gutenberg_block_has_support` which checked for boolean true or array.
$has_width_support = _wp_array_get( $block_type->supports, array( '__experimentalDimensions', 'width' ), false );

if ( $has_width_support ) {
$width_value = _wp_array_get( $block_attributes, array( 'style', 'dimensions', 'width' ), null );

if ( null !== $width_value ) {
$styles[] = sprintf( 'width: %s;', $width_value );
}
}

return empty( $styles ) ? array() : array( 'style' => implode( ' ', $styles ) );
}
Expand Down
3 changes: 3 additions & 0 deletions lib/class-wp-theme-json-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class WP_Theme_JSON_Gutenberg {
),
'dimensions' => array(
'height' => null,
'width' => null,
),
'spacing' => array(
'margin' => null,
Expand Down Expand Up @@ -99,6 +100,7 @@ class WP_Theme_JSON_Gutenberg {
'custom' => null,
'dimensions' => array(
'customHeight' => null,
'customWidth' => null,
),
'layout' => array(
'contentSize' => null,
Expand Down Expand Up @@ -244,6 +246,7 @@ class WP_Theme_JSON_Gutenberg {
'--wp--style--block-gap' => array( 'spacing', 'blockGap' ),
'text-decoration' => array( 'typography', 'textDecoration' ),
'text-transform' => array( 'typography', 'textTransform' ),
'width' => array( 'dimensions', 'width' ),
);

const ELEMENTS = array(
Expand Down
3 changes: 2 additions & 1 deletion lib/theme.json
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,8 @@
]
},
"dimensions": {
"customHeight": false
"customHeight": false,
"customWidth": false
},
"spacing": {
"customMargin": false,
Expand Down
130 changes: 130 additions & 0 deletions packages/block-editor/src/components/width-control/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/**
* WordPress dependencies
*/
import {
Button,
ButtonGroup,
__experimentalUnitControl as UnitControl,
} from '@wordpress/components';
import { useEffect, useRef, useState } from '@wordpress/element';
import { edit } from '@wordpress/icons';
import { __ } from '@wordpress/i18n';

const DEFAULT_WIDTHS = [ '25%', '50%', '75%', '100%' ];
const DEFAULT_UNIT = '%';
const MIN_WIDTH = 0;

/**
* Determines the CSS unit within the supplied width value.
*
* @param {string} value Value including CSS unit.
* @param {Array} units Available CSS units to validate against.
*
* @return {string} CSS unit extracted from supplied value.
*/
const parseUnit = ( value, units ) => {
let unit = String( value )
.trim()
.match( /[\d.\-\+]*\s*(.*)/ )[ 1 ];

if ( ! unit ) {
return DEFAULT_UNIT;
}

unit = unit.toLowerCase();
unit = units.find( ( item ) => item.value === unit );

return unit?.value || DEFAULT_UNIT;
};

/**
* Width control that will display as either a simple `UnitControl` or a
* segmented control containing preset percentage widths. The segmented version
* contains a toggle to switch to a UnitControl and Slider for explicit control.
*
* @param {Object} props Component props.
* @return {WPElement} Width control.
*/
export default function WidthControl( props ) {
const {
label = __( 'Width' ),
onChange,
units,
value,
isSegmentedControl = false,
min = MIN_WIDTH,
presetWidths = DEFAULT_WIDTHS,
} = props;

const ref = useRef();
const hasCustomValue = value && ! presetWidths.includes( value );
const [ customView, setCustomView ] = useState( hasCustomValue );
const currentUnit = parseUnit( value, units );

// When switching to the custom view, move focus to the UnitControl.
useEffect( () => {
if ( customView && ref.current ) {
ref.current.focus();
}
}, [ customView ] );

// Unless segmented control is desired return a normal UnitControl.
if ( ! isSegmentedControl ) {
return (
<UnitControl
label={ label }
min={ min }
unit={ currentUnit }
{ ...props }
/>
);
}

const toggleCustomView = () => {
setCustomView( ! customView );
};

const handlePresetChange = ( selectedValue ) => {
const newWidth = selectedValue === value ? undefined : selectedValue;
onChange( newWidth );
};

const renderCustomView = () => (
<UnitControl
ref={ ref }
min={ min }
unit={ currentUnit }
{ ...props }
/>
);

const renderPresetView = () => (
<ButtonGroup aria-label={ __( 'Button width' ) }>
{ presetWidths.map( ( width ) => (
<Button
key={ width }
isSmall
variant={ value === width ? 'primary' : undefined }
onClick={ () => handlePresetChange( width ) }
>
{ width }
</Button>
) ) }
</ButtonGroup>
);

return (
<fieldset className="components-width-control is-segmented">
<legend>{ label }</legend>
<div className="components-width-control__wrapper">
{ customView ? renderCustomView() : renderPresetView() }
<Button
icon={ edit }
isSmall
isPressed={ customView }
onClick={ toggleCustomView }
/>
</div>
</fieldset>
);
}
34 changes: 34 additions & 0 deletions packages/block-editor/src/components/width-control/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
.components-width-control.is-segmented {
legend {
margin-bottom: $grid-unit-10;
}

.components-width-control__wrapper {
display: flex;
align-items: center;
justify-content: space-between;
}

.components-unit-control-wrapper {
flex: 1;
margin-right: $grid-unit-10;
max-width: 80px;
}

.components-range-control {
flex: 1;
margin-bottom: 0;

.components-base-control__field {
margin-bottom: 0;
height: 30px;
}
}

.components-button.is-small.has-icon:not(.has-text) {
margin-left: $grid-unit-20;
min-width: 30px;
height: 30px;
padding: 0 4px;
}
}
23 changes: 22 additions & 1 deletion packages/block-editor/src/hooks/dimensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ import {
useIsPaddingDisabled,
} from './padding';
import { cleanEmptyObject } from './utils';
import {
WidthEdit,
hasWidthSupport,
hasWidthValue,
resetWidth,
useIsWidthDisabled,
} from './width';

export const DIMENSIONS_SUPPORT_KEY = '__experimentalDimensions';
export const SPACING_SUPPORT_KEY = 'spacing';
Expand All @@ -51,6 +58,7 @@ export function DimensionsPanel( props ) {
const isPaddingDisabled = useIsPaddingDisabled( props );
const isMarginDisabled = useIsMarginDisabled( props );
const isHeightDisabled = useIsHeightDisabled( props );
const isWidthDisabled = useIsWidthDisabled( props );
const isDisabled = useIsDimensionsDisabled( props );
const isSupported = hasDimensionsSupport( props.name );

Expand Down Expand Up @@ -78,6 +86,7 @@ export function DimensionsPanel( props ) {
dimensions: {
...style?.dimensions,
height: undefined,
width: undefined,
},
spacing: {
...style?.spacing,
Expand Down Expand Up @@ -106,6 +115,16 @@ export function DimensionsPanel( props ) {
<HeightEdit { ...props } />
</ToolsPanelItem>
) }
{ ! isWidthDisabled && (
<ToolsPanelItem
hasValue={ () => hasWidthValue( props ) }
label={ __( 'Width' ) }
onDeselect={ () => resetWidth( props ) }
isShownByDefault={ defaultDimensionsControls?.width }
>
<WidthEdit { ...props } />
</ToolsPanelItem>
) }
{ ! isPaddingDisabled && (
<ToolsPanelItem
hasValue={ () => hasPaddingValue( props ) }
Expand Down Expand Up @@ -145,6 +164,7 @@ export function hasDimensionsSupport( blockName ) {

return (
hasHeightSupport( blockName ) ||
hasWidthSupport( blockName ) ||
hasPaddingSupport( blockName ) ||
hasMarginSupport( blockName )
);
Expand All @@ -158,10 +178,11 @@ export function hasDimensionsSupport( blockName ) {
*/
const useIsDimensionsDisabled = ( props = {} ) => {
const heightDisabled = useIsHeightDisabled( props );
const widthDisabled = useIsWidthDisabled( props );
const paddingDisabled = useIsPaddingDisabled( props );
const marginDisabled = useIsMarginDisabled( props );

return heightDisabled && paddingDisabled && marginDisabled;
return heightDisabled && widthDisabled && paddingDisabled && marginDisabled;
};

/**
Expand Down
2 changes: 2 additions & 0 deletions packages/block-editor/src/hooks/test/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ describe( 'getInlineStyles', () => {
},
dimensions: {
height: '500px',
width: '100%',
},
spacing: {
padding: { top: '10px' },
Expand All @@ -43,6 +44,7 @@ describe( 'getInlineStyles', () => {
height: '500px',
marginBottom: '15px',
paddingTop: '10px',
width: '100%',
} );
} );

Expand Down
Loading

0 comments on commit 193263d

Please sign in to comment.