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 Nov 2, 2021
1 parent 5b17ec8 commit ce0236d
Show file tree
Hide file tree
Showing 13 changed files with 395 additions and 7 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
5 changes: 4 additions & 1 deletion 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,
),
'filter' => array(
'duotone' => null,
Expand Down Expand Up @@ -104,6 +105,7 @@ class WP_Theme_JSON_Gutenberg {
'custom' => null,
'dimensions' => array(
'height' => null,
'width' => null,
),
'layout' => array(
'contentSize' => null,
Expand Down Expand Up @@ -238,7 +240,6 @@ class WP_Theme_JSON_Gutenberg {
'font-size' => array( 'typography', 'fontSize' ),
'font-style' => array( 'typography', 'fontStyle' ),
'font-weight' => array( 'typography', 'fontWeight' ),
'height' => array( 'dimensions', 'height' ),
'letter-spacing' => array( 'typography', 'letterSpacing' ),
'line-height' => array( 'typography', 'lineHeight' ),
'margin' => array( 'spacing', 'margin' ),
Expand All @@ -255,6 +256,8 @@ class WP_Theme_JSON_Gutenberg {
'text-decoration' => array( 'typography', 'textDecoration' ),
'text-transform' => array( 'typography', 'textTransform' ),
'filter' => array( 'filter', 'duotone' ),
'height' => array( 'dimensions', 'height' ),
'width' => array( 'dimensions', 'width' ),
);

/**
Expand Down
3 changes: 2 additions & 1 deletion lib/theme.json
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,8 @@
]
},
"dimensions": {
"height": false
"height": false,
"width": false
},
"spacing": {
"blockGap": null,
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;
}
}
33 changes: 32 additions & 1 deletion packages/block-editor/src/hooks/dimensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ import {
resetPadding,
useIsPaddingDisabled,
} from './padding';
import {
WidthEdit,
hasWidthSupport,
hasWidthValue,
resetWidth,
useIsWidthDisabled,
} from './width';

export const DIMENSIONS_SUPPORT_KEY = '__experimentalDimensions';
export const SPACING_SUPPORT_KEY = 'spacing';
Expand All @@ -55,6 +62,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 @@ -103,6 +111,21 @@ export function DimensionsPanel( props ) {
<HeightEdit { ...props } />
</ToolsPanelItem>
) }
{ ! isWidthDisabled && (
<ToolsPanelItem
hasValue={ () => hasWidthValue( props ) }
label={ __( 'Width' ) }
onDeselect={ () => resetWidth( props ) }
resetAllFilter={ createResetAllFilter(
'width',
'dimensions'
) }
isShownByDefault={ defaultDimensionsControls?.width }
panelId={ props.clientId }
>
<WidthEdit { ...props } />
</ToolsPanelItem>
) }
{ ! isPaddingDisabled && (
<ToolsPanelItem
hasValue={ () => hasPaddingValue( props ) }
Expand Down Expand Up @@ -167,6 +190,7 @@ export function hasDimensionsSupport( blockName ) {
return (
hasGapSupport( blockName ) ||
hasHeightSupport( blockName ) ||
hasWidthSupport( blockName ) ||
hasPaddingSupport( blockName ) ||
hasMarginSupport( blockName )
);
Expand All @@ -181,10 +205,17 @@ export function hasDimensionsSupport( blockName ) {
const useIsDimensionsDisabled = ( props = {} ) => {
const gapDisabled = useIsGapDisabled( props );
const heightDisabled = useIsHeightDisabled( props );
const widthDisabled = useIsWidthDisabled( props );
const paddingDisabled = useIsPaddingDisabled( props );
const marginDisabled = useIsMarginDisabled( props );

return gapDisabled && heightDisabled && paddingDisabled && marginDisabled;
return (
gapDisabled &&
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: {
blockGap: '1em',
Expand All @@ -45,6 +46,7 @@ describe( 'getInlineStyles', () => {
height: '500px',
marginBottom: '15px',
paddingTop: '10px',
width: '100%',
} );
} );

Expand Down
Loading

0 comments on commit ce0236d

Please sign in to comment.