Skip to content

Commit

Permalink
Dimensions: Add Aspect Ratio block support (#56897)
Browse files Browse the repository at this point in the history
* Try: Dimensions Aspect Ratio block support

* Try server-rendering output

* Try outputting has-aspect-ratio classname

* Ensure block support is checked properly

* Try adding global styles support

* Update docs, add appropriate checks

* Try unsetting minHeight

* Try unsetting min-height when applying aspect ratio and vice versa

* Allow Cover block to expand when the content extends beyond the boundaries of the aspect-ratio

* Hide cover block resize handle when aspect ratio is set, clear aspect ratio when updating cover block min height

* Clear aspect-ratio if minHeight is set on the site frontend

* Attempt to get unset rules playing nicely in the editor with global styles

* Hide aspectRatio control from global styles for the group block

* Skip output of has-aspect-ratio if value isn't set
  • Loading branch information
andrewserong authored and cbravobernal committed Jan 25, 2024
1 parent 45de2cb commit d3ac16c
Show file tree
Hide file tree
Showing 27 changed files with 340 additions and 26 deletions.
2 changes: 2 additions & 0 deletions docs/how-to-guides/themes/global-settings-and-styles.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ The settings section has the following structure:
},
"custom": {},
"dimensions": {
"aspectRatio": false,
"minHeight": false,
},
"layout": {
Expand Down Expand Up @@ -773,6 +774,7 @@ Each block declares which style properties it exposes via the [block supports me
"text": "value"
},
"dimensions": {
"aspectRatio": "value",
"minHeight": "value"
},
"filter": {
Expand Down
2 changes: 1 addition & 1 deletion docs/how-to-guides/themes/theme-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ Use this setting to enable the following Global Styles settings:
- color: link
- spacing: blockGap, margin, padding
- typography: lineHeight
- dimensions: minHeight
- dimensions: aspectRatio, minHeight
- position: sticky

```php
Expand Down
4 changes: 3 additions & 1 deletion docs/reference-guides/block-api/block-supports.md
Original file line number Diff line number Diff line change
Expand Up @@ -442,19 +442,21 @@ This value signals that a block supports some of the CSS style properties relate
```js
supports: {
dimensions: {
aspectRatio: true // Enable aspect ratio control.
minHeight: true // Enable min height control.
}
}
```

When a block declares support for a specific dimensions property, its attributes definition is extended to include the `style` attribute.

- `style`: attribute of `object` type with no default assigned. This is added when `minHeight` support is declared. It stores the custom values set by the user, e.g.:
- `style`: attribute of `object` type with no default assigned. This is added when `aspectRatio` or `minHeight` support is declared. It stores the custom values set by the user, e.g.:

```js
attributes: {
style: {
dimensions: {
aspectRatio: "16/9",
minHeight: "50vh"
}
}
Expand Down
4 changes: 2 additions & 2 deletions docs/reference-guides/core-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ Add an image or video with a text overlay. ([Source](https://github.com/WordPres

- **Name:** core/cover
- **Category:** media
- **Supports:** align, anchor, color (heading, text, ~~background~~, ~~enableContrastChecker~~), layout (~~allowJustification~~), spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~
- **Supports:** align, anchor, color (heading, text, ~~background~~, ~~enableContrastChecker~~), dimensions (aspectRatio), layout (~~allowJustification~~), spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~
- **Attributes:** allowedBlocks, alt, backgroundType, contentPosition, customGradient, customOverlayColor, dimRatio, focalPoint, gradient, hasParallax, id, isDark, isRepeated, isUserOverlayColor, minHeight, minHeightUnit, overlayColor, tagName, templateLock, url, useFeaturedImage

## Details
Expand Down Expand Up @@ -341,7 +341,7 @@ Gather blocks in a layout container. ([Source](https://github.com/WordPress/gute

- **Name:** core/group
- **Category:** design
- **Supports:** align (full, wide), anchor, ariaLabel, background (backgroundImage, backgroundSize), color (background, button, gradients, heading, link, text), dimensions (minHeight), layout (allowSizingOnChildren), position (sticky), spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~
- **Supports:** align (full, wide), anchor, ariaLabel, background (backgroundImage, backgroundSize), color (background, button, gradients, heading, link, text), dimensions (aspectRatio, minHeight), layout (allowSizingOnChildren), position (sticky), spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~
- **Attributes:** allowedBlocks, tagName, templateLock

## Heading
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Setting that enables the following UI tools:
- background: backgroundImage
- border: color, radius, style, width
- color: link
- dimensions: minHeight
- dimensions: aspectRatio, minHeight
- position: sticky
- spacing: blockGap, margin, padding
- typography: lineHeight
Expand Down Expand Up @@ -116,6 +116,7 @@ Settings related to dimensions.

| Property | Type | Default | Props |
| --- | --- | --- |--- |
| aspectRatio | boolean | false | |
| minHeight | boolean | false | |

---
Expand Down Expand Up @@ -237,6 +238,7 @@ Dimensions styles

| Property | Type | Props |
| --- | --- |--- |
| aspectRatio | string, object | |
| minHeight | string, object | |

---
Expand Down
77 changes: 77 additions & 0 deletions lib/block-supports/dimensions.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,83 @@ function gutenberg_apply_dimensions_support( $block_type, $block_attributes ) {
return $attributes;
}

/**
* Renders server-side dimensions styles to the block wrapper.
* This block support uses the `render_block` hook to ensure that
* it is also applied to non-server-rendered blocks.
*
* @param string $block_content Rendered block content.
* @param array $block Block object.
* @return string Filtered block content.
*/
function gutenberg_render_dimensions_support( $block_content, $block ) {
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
$block_attributes = ( isset( $block['attrs'] ) && is_array( $block['attrs'] ) ) ? $block['attrs'] : array();
$has_aspect_ratio_support = block_has_support( $block_type, array( 'dimensions', 'aspectRatio' ), false );

if (
! $has_aspect_ratio_support ||
wp_should_skip_block_supports_serialization( $block_type, 'dimensions', 'aspectRatio' )
) {
return $block_content;
}

$dimensions_block_styles = array();
$dimensions_block_styles['aspectRatio'] = $block_attributes['style']['dimensions']['aspectRatio'] ?? null;

// To ensure the aspect ratio does not get overridden by `minHeight` unset any existing rule.
if (
isset( $dimensions_block_styles['aspectRatio'] )
) {
$dimensions_block_styles['minHeight'] = 'unset';
} elseif (
isset( $block_attributes['style']['dimensions']['minHeight'] ) ||
isset( $block_attributes['minHeight'] )
) {
$dimensions_block_styles['aspectRatio'] = 'unset';
}

$styles = gutenberg_style_engine_get_styles( array( 'dimensions' => $dimensions_block_styles ) );

if ( ! empty( $styles['css'] ) ) {
// Inject dimensions styles to the first element, presuming it's the wrapper, if it exists.
$tags = new WP_HTML_Tag_Processor( $block_content );

if ( $tags->next_tag() ) {
$existing_style = $tags->get_attribute( 'style' );
$updated_style = '';

if ( ! empty( $existing_style ) ) {
$updated_style = $existing_style;
if ( ! str_ends_with( $existing_style, ';' ) ) {
$updated_style .= ';';
}
}

$updated_style .= $styles['css'];
$tags->set_attribute( 'style', $updated_style );

if ( ! empty( $styles['classnames'] ) ) {
foreach ( explode( ' ', $styles['classnames'] ) as $class_name ) {
if (
str_contains( $class_name, 'aspect-ratio' ) &&
! isset( $block_attributes['style']['dimensions']['aspectRatio'] )
) {
continue;
}
$tags->add_class( $class_name );
}
}
}

return $tags->get_updated_html();
}

return $block_content;
}

add_filter( 'render_block', 'gutenberg_render_dimensions_support', 10, 2 );

// Register the block support.
WP_Block_Supports::get_instance()->register(
'dimensions',
Expand Down
17 changes: 15 additions & 2 deletions lib/class-wp-theme-json-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ class WP_Theme_JSON_Gutenberg {
* @var array
*/
const PROPERTIES_METADATA = array(
'aspect-ratio' => array( 'dimensions', 'aspectRatio' ),
'background' => array( 'color', 'gradient' ),
'background-color' => array( 'color', 'background' ),
'border-radius' => array( 'border', 'radius' ),
Expand Down Expand Up @@ -381,7 +382,8 @@ class WP_Theme_JSON_Gutenberg {
),
'custom' => null,
'dimensions' => array(
'minHeight' => null,
'aspectRatio' => null,
'minHeight' => null,
),
'layout' => array(
'contentSize' => null,
Expand Down Expand Up @@ -486,7 +488,8 @@ class WP_Theme_JSON_Gutenberg {
'text' => null,
),
'dimensions' => array(
'minHeight' => null,
'aspectRatio' => null,
'minHeight' => null,
),
'filter' => array(
'duotone' => null,
Expand Down Expand Up @@ -661,6 +664,7 @@ public static function get_element_class_name( $element ) {
array( 'color', 'heading' ),
array( 'color', 'button' ),
array( 'color', 'caption' ),
array( 'dimensions', 'aspectRatio' ),
array( 'dimensions', 'minHeight' ),
// BEGIN EXPERIMENTAL.
// Allow `position.fixed` to be opted-in by default.
Expand Down Expand Up @@ -2093,6 +2097,15 @@ protected static function compute_style_properties( $styles, $settings = array()
$value = gutenberg_get_typography_font_size_value( array( 'size' => $value ) );
}

if ( 'aspect-ratio' === $css_property ) {
// For aspect ratio to work, other dimensions rules must be unset.
// This ensures that a fixed height does not override the aspect ratio.
$declarations[] = array(
'name' => 'min-height',
'value' => 'unset',
);
}

$declarations[] = array(
'name' => $css_property,
'value' => $value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,17 @@ export default function AspectRatioTool( {
onChange = () => {},
options = DEFAULT_ASPECT_RATIO_OPTIONS,
defaultValue = DEFAULT_ASPECT_RATIO_OPTIONS[ 0 ].value,
hasValue,
isShownByDefault = true,
} ) {
// Match the CSS default so if the value is used directly in CSS it will look correct in the control.
const displayValue = value ?? 'auto';

return (
<ToolsPanelItem
hasValue={ () => displayValue !== defaultValue }
hasValue={
hasValue ? hasValue : () => displayValue !== defaultValue
}
label={ __( 'Aspect ratio' ) }
onDeselect={ () => onChange( undefined ) }
isShownByDefault={ isShownByDefault }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { getValueFromVariable, TOOLSPANEL_DROPDOWNMENU_PROPS } from './utils';
import SpacingSizesControl from '../spacing-sizes-control';
import HeightControl from '../height-control';
import ChildLayoutControl from '../child-layout-control';
import AspectRatioTool from '../dimensions-tool/aspect-ratio-tool';
import { cleanEmptyObject } from '../../hooks/utils';
import { setImmutably } from '../../utils/object';

Expand All @@ -39,6 +40,7 @@ export function useHasDimensionsPanel( settings ) {
const hasMargin = useHasMargin( settings );
const hasGap = useHasGap( settings );
const hasMinHeight = useHasMinHeight( settings );
const hasAspectRatio = useHasAspectRatio( settings );
const hasChildLayout = useHasChildLayout( settings );

return (
Expand All @@ -49,6 +51,7 @@ export function useHasDimensionsPanel( settings ) {
hasMargin ||
hasGap ||
hasMinHeight ||
hasAspectRatio ||
hasChildLayout )
);
}
Expand Down Expand Up @@ -77,6 +80,10 @@ function useHasMinHeight( settings ) {
return settings?.dimensions?.minHeight;
}

function useHasAspectRatio( settings ) {
return settings?.dimensions?.aspectRatio;
}

function useHasChildLayout( settings ) {
const {
type: parentLayoutType = 'default',
Expand Down Expand Up @@ -192,6 +199,7 @@ const DEFAULT_CONTROLS = {
margin: true,
blockGap: true,
minHeight: true,
aspectRatio: true,
childLayout: true,
};

Expand Down Expand Up @@ -346,15 +354,43 @@ export default function DimensionsPanel( {
const showMinHeightControl = useHasMinHeight( settings );
const minHeightValue = decodeValue( inheritedValue?.dimensions?.minHeight );
const setMinHeightValue = ( newValue ) => {
const tempValue = setImmutably(
value,
[ 'dimensions', 'minHeight' ],
newValue
);
// Apply min-height, while removing any applied aspect ratio.
onChange(
setImmutably( value, [ 'dimensions', 'minHeight' ], newValue )
setImmutably(
tempValue,
[ 'dimensions', 'aspectRatio' ],
undefined
)
);
};
const resetMinHeightValue = () => {
setMinHeightValue( undefined );
};
const hasMinHeightValue = () => !! value?.dimensions?.minHeight;

// Aspect Ratio
const showAspectRatioControl = useHasAspectRatio( settings );
const aspectRatioValue = decodeValue(
inheritedValue?.dimensions?.aspectRatio
);
const setAspectRatioValue = ( newValue ) => {
const tempValue = setImmutably(
value,
[ 'dimensions', 'aspectRatio' ],
newValue
);
// Apply aspect-ratio, while removing any applied min-height.
onChange(
setImmutably( tempValue, [ 'dimensions', 'minHeight' ], undefined )
);
};
const hasAspectRatioValue = () => !! value?.dimensions?.aspectRatio;

// Child Layout
const showChildLayoutControl = useHasChildLayout( settings );
const childLayout = inheritedValue?.layout;
Expand Down Expand Up @@ -397,6 +433,7 @@ export default function DimensionsPanel( {
dimensions: {
...previousValue?.dimensions,
minHeight: undefined,
aspectRatio: undefined,
},
};
}, [] );
Expand Down Expand Up @@ -617,6 +654,18 @@ export default function DimensionsPanel( {
/>
</ToolsPanelItem>
) }
{ showAspectRatioControl && (
<AspectRatioTool
hasValue={ hasAspectRatioValue }
value={ aspectRatioValue }
onChange={ setAspectRatioValue }
panelId={ panelId }
isShownByDefault={
defaultControls.aspectRatio ??
DEFAULT_CONTROLS.aspectRatio
}
/>
) }
{ showChildLayoutControl && (
<VStack
as={ ToolsPanelItem }
Expand Down
15 changes: 9 additions & 6 deletions packages/block-editor/src/components/global-styles/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const VALID_SETTINGS = [
'color.palette',
'color.text',
'custom',
'dimensions.aspectRatio',
'dimensions.minHeight',
'layout.contentSize',
'layout.definitions',
Expand Down Expand Up @@ -344,12 +345,14 @@ export function useSettingsForBlockElement(
}
} );

if ( ! supportedStyles.includes( 'minHeight' ) ) {
updatedSettings.dimensions = {
...updatedSettings.dimensions,
minHeight: false,
};
}
[ 'aspectRatio', 'minHeight' ].forEach( ( key ) => {
if ( ! supportedStyles.includes( key ) ) {
updatedSettings.dimensions = {
...updatedSettings.dimensions,
[ key ]: false,
};
}
} );

[ 'radius', 'color', 'style', 'width' ].forEach( ( key ) => {
if (
Expand Down
Loading

0 comments on commit d3ac16c

Please sign in to comment.