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 initial block support for width #26045

Closed
wants to merge 14 commits into from
114 changes: 114 additions & 0 deletions lib/block-supports/dimensions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<?php
/**
* Dimensions block support flag.
*
* @package gutenberg
*/

/**
* Ensures the style attribute required by the dimensions feature is registered,
* for block types that support the feature.
*
* @param WP_Block_Type $block_type Block Type.
*/
function gutenberg_register_dimensions_support( $block_type ) {
// Determine if width supported.
$has_width_support = gutenberg_has_dimensions_support( $block_type, 'width' );

// Setup attributes and styles within that if needed.
if ( ! $block_type->attributes ) {
$block_type->attributes = array();
}

if ( $has_width_support && ! array_key_exists( 'style', $block_type->attributes ) ) {
$block_type->attributes['style'] = array(
'type' => 'object',
);
}
}

/**
* Add CSS classes and inline styles for dimension features to the incoming attributes array.
* This will be applied to the block markup in the front-end.
*
* @param WP_Block_Type $block_type Block type.
* @param array $block_attributes Block attributes.
*
* @return array Dimensions CSS classes and inline styles.
*/
function gutenberg_apply_dimensions_support( $block_type, $block_attributes ) {
$has_width_support = gutenberg_has_dimensions_support( $block_type, 'width' );
$styles = array();

if ( $has_width_support ) {
$width_style = gutenberg_dimensions_get_css_variable_inline_style( $block_attributes, 'width', 'width' );
if ( $width_style ) {
$styles[] = $width_style;
}
}

$attributes = array();
if ( ! empty( $styles ) ) {
$attributes['style'] = implode( ' ', $styles );
}
return $attributes;
}

/**
* Generates an inline style for a dimension feature e.g. width, height.
*
* @param array $attributes Block's attributes.
* @param string $feature Key for the feature within the dimensions styles.
* @param string $css_property Slug for the CSS property the inline style sets.
*
* @return string CSS inline style.
*/
function gutenberg_dimensions_get_css_variable_inline_style( $attributes, $feature, $css_property ) {
// Retrieve current attribute value or skip if not found.
$style_value = gutenberg_experimental_get( $attributes, array( 'style', 'dimensions', $feature ), false );
if ( ! $style_value ) {
return;
}

// If we don't have a preset CSS variable, we'll assume it's a regular CSS value.
if ( strpos( $style_value, "var:preset|{$css_property}|" ) === false ) {
return sprintf( '%s: %s;', $css_property, $style_value );
}

// We have a preset CSS variable as the style.
// Get the style value from the string and return CSS style.
$index_to_splice = strrpos( $style_value, '|' ) + 1;
$slug = substr( $style_value, $index_to_splice );

// Return the actual CSS inline style e.g. `text-decoration:var(--wp--preset--text-decoration--underline);`.
return sprintf( '%s: var(--wp--preset--%s--%s);', $css_property, $css_property, $slug );
}


/**
* Checks whether the current block type supports the experimental feature
* requested.
*
* @param WP_Block_Type $block_type Block type to check for support.
* @param string $feature Name of the feature to check support for.
* @param mixed $default Fallback value for feature support, defaults to false.
*
* @return boolean Whether or not the feature is supported.
*/
function gutenberg_has_dimensions_support( $block_type, $feature, $default = false ) {
$block_support = false;
if ( property_exists( $block_type, 'supports' ) ) {
$block_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalDimensions' ), $default );
}

return true === $block_support || ( is_array( $block_support ) && gutenberg_experimental_get( $block_support, array( $feature ), false ) );
}

// Register the block support.
WP_Block_Supports::get_instance()->register(
'dimensions',
array(
'register_attribute' => 'gutenberg_register_dimensions_support',
'apply' => 'gutenberg_apply_dimensions_support',
)
);
24 changes: 24 additions & 0 deletions lib/experimental-default-theme.json
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,30 @@
"spacing": {
"customPadding": false,
"units": [ "px", "em", "rem", "vh", "vw" ]
},
"dimensions": {
"width": [
{
"name": "25%",
"slug": "25",
"value": "25%"
},
{
"name": "50%",
"slug": "50",
"value": "50%"
},
{
"name": "75%",
"slug": "75",
"value": "75%"
},
{
"name": "100%",
"slug": "100",
"value": "100%"
}
]
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions lib/global-styles.php
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ function gutenberg_experimental_global_styles_get_style_property() {
'color' => array( 'color', 'text' ),
'fontSize' => array( 'typography', 'fontSize' ),
'lineHeight' => array( 'typography', 'lineHeight' ),
'width' => array( 'dimensions', 'width' ),
);
}

Expand All @@ -423,6 +424,7 @@ function gutenberg_experimental_global_styles_get_support_keys() {
'color' => array( 'color' ),
'fontSize' => array( 'fontSize' ),
'lineHeight' => array( 'lineHeight' ),
'width' => array( '__experimentalDimensions', 'width' ),
);
}

Expand All @@ -445,6 +447,10 @@ function gutenberg_experimental_global_styles_get_presets_structure() {
'path' => array( 'typography', 'fontSizes' ),
'key' => 'size',
),
'width' => array(
'path' => array( 'dimensions', 'width' ),
'key' => 'value',
),
);
}

Expand Down Expand Up @@ -819,6 +825,7 @@ function gutenberg_experimental_global_styles_normalize_schema( $tree ) {
'custom' => array(),
'typography' => array(),
'spacing' => array(),
'dimensions' => array(),
),
);

Expand Down
1 change: 1 addition & 0 deletions lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,4 @@ function gutenberg_is_experiment_enabled( $name ) {
require dirname( __FILE__ ) . '/block-supports/align.php';
require dirname( __FILE__ ) . '/block-supports/typography.php';
require dirname( __FILE__ ) . '/block-supports/custom-classname.php';
require dirname( __FILE__ ) . '/block-supports/dimensions.php';
62 changes: 62 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,62 @@
/**
* WordPress dependencies
*/
import { Button, ButtonGroup } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

/**
* Control to facilitate width selections.
*
* @param {Object} props Component props.
* @param {string} props.value Currently selected width.
* @param {Array} props.widthOptions Width options available for selection.
* @param {Function} props.onChange Handles change in width selection.
* @return {WPElement} Width control.
*/
export default function WidthControl( {
value: selectedWidth,
widthOptions,
onChange,
} ) {
/**
* Determines the new width as a result of user interaction with
* the control. Then passes this to the supplied onChange handler.
*
* @param {string} newWidth Slug for selected width
*/
const handleChange = ( newWidth ) => {
// Check if we are toggling the width off
const width = selectedWidth === newWidth ? undefined : newWidth;

// Ensure only predefined width options are allowed
const presetWidth = widthOptions.find( ( { slug } ) => slug === width );

// Create string that will be turned into custom CSS property
const customWidthProperty = presetWidth
? `var:preset|width|${ presetWidth.slug }`
: undefined;

// Pass on to the supplied handler.
onChange( customWidthProperty );
};

return (
<>
<p>{ __( 'Width' ) }</p>
<ButtonGroup>
{ widthOptions.map( ( widthOption ) => {
return (
<Button
key={ widthOption.slug }
isSmall
isPrimary={ selectedWidth === widthOption.slug }
onClick={ () => handleChange( widthOption.slug ) }
>
{ widthOption.value }
</Button>
);
} ) }
</ButtonGroup>
</>
);
}
111 changes: 111 additions & 0 deletions packages/block-editor/src/hooks/dimensions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**
* WordPress dependencies
*/
import { hasBlockSupport } from '@wordpress/blocks';
import { PanelBody } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import { cleanEmptyObject } from './utils';
import InspectorControls from '../components/inspector-controls';
import useEditorFeature from '../components/use-editor-feature';
import WidthControl from '../components/width-control';

/**
* Key within block settings' supports array indicating support for
* dimensions including width, e.g. settings found in 'block.json'.
*/
export const DIMENSIONS_SUPPORT_KEY = '__experimentalDimensions';

export function DimensionsPanel( props ) {
const {
attributes: { style },
setAttributes,
} = props;

const widthOptions = useEditorFeature( 'dimensions.width' );
const isEnabled = useIsWidthEnabled( props );

if ( ! isEnabled ) {
return null;
}

const selectedWidth = getWidthFromAttributeValue(
widthOptions,
style?.dimensions?.width
);

function onChange( newWidth ) {
setAttributes( {
style: cleanEmptyObject( {
...style,
dimensions: {
...style?.dimensions,
width: newWidth,
},
} ),
} );
}

return (
<InspectorControls>
<PanelBody title={ __( 'Dimensions' ) }>
<WidthControl
value={ selectedWidth }
widthOptions={ widthOptions }
onChange={ onChange }
/>
</PanelBody>
</InspectorControls>
);
}

/**
* Checks if there is block support for width.
*
* @param {string|Object} blockType Block name or Block Type object.
* @return {boolean} Whether there is support.
*/
export function hasWidthSupport( blockName ) {
const support = hasBlockSupport( blockName, DIMENSIONS_SUPPORT_KEY );

// Further dimension properties to be added in future iterations.
// e.g. support && ( support.width || support.height )
return true === support || ( support && support.width );
}

/**
* Checks if width is supported and has not been disabled.
*
* @param {string|Object} blockType Block name or Block Type object.
* @return {boolean} Whether there is support.
*/
export function useIsWidthEnabled( { name: blockName } = {} ) {
const supported = hasWidthSupport( blockName );
const widthOptions = useEditorFeature( 'dimensions.width' );
const hasWidthOptions = !! widthOptions?.length;

return supported && hasWidthOptions;
}

/**
* Extracts the current width selection, if available, from the CSS variable
* set as the 'styles.width' attribute.
*
* @param {Array} widthOptions Available width options as defined in theme.json
* @param {string} value Attribute value in `styles.width`
* @return {string} Actual width value
*/
const getWidthFromAttributeValue = ( widthOptions, value ) => {
const attributeParsed = /var:preset\|width\|(.+)/.exec( value );

if ( attributeParsed && attributeParsed[ 1 ] ) {
return widthOptions.find(
( { slug } ) => slug === attributeParsed[ 1 ]
)?.slug;
}

return value;
};
3 changes: 3 additions & 0 deletions packages/block-editor/src/hooks/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ import { COLOR_SUPPORT_KEY, ColorEdit } from './color';
import { TypographyPanel, TYPOGRAPHY_SUPPORT_KEYS } from './typography';
import { SPACING_SUPPORT_KEY, PaddingEdit } from './padding';
import SpacingPanelControl from '../components/spacing-panel-control';
import { DIMENSIONS_SUPPORT_KEY, DimensionsPanel } from './dimensions';

const styleSupportKeys = [
...TYPOGRAPHY_SUPPORT_KEYS,
COLOR_SUPPORT_KEY,
SPACING_SUPPORT_KEY,
DIMENSIONS_SUPPORT_KEY,
];

const hasStyleSupport = ( blockType ) =>
Expand Down Expand Up @@ -156,6 +158,7 @@ export const withBlockControls = createHigherOrderComponent(
return [
<TypographyPanel key="typography" { ...props } />,
<ColorEdit key="colors" { ...props } />,
<DimensionsPanel key="dimensions" { ...props } />,
<BlockEdit key="edit" { ...props } />,
hasSpacingSupport && (
<SpacingPanelControl key="spacing">
Expand Down
8 changes: 7 additions & 1 deletion packages/block-library/src/button/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@
"align": true,
"alignWide": false,
"reusable": false,
"__experimentalSelector": ".wp-block-button > a"
"__experimentalSelector": ".wp-block-button > a",
"__experimentalDimensions": {
"width": true
},
"color": {
"gradients": true
}
}
}
Loading