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

Dimensions: Add Aspect Ratio block support #56897

Merged
merged 14 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 );
Copy link
Member

@ramonjd ramonjd Dec 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mainly interested... so $tags->add_class( 'a-class b-class' ) should work too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the moment I'm calling explode so that we add them one-by-one... I looked up the add_class method and it does look like we should be able to call it with a full classname string that contains multiple classes, I just wasn't sure if it's guaranteed to 🤔. Happy to switch it over to the simpler add_class with the full single string, though 🙂

Thanks for giving this PR an early look! 🙇

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I gave it a try and it works 😄 No biggie, I was just wondering.
Screenshot 2023-12-12 at 4 43 30 pm

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice one! I can switch it over 😀

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah if it's not explicitly indicated in the docs it might not be officially supported even though it currently works. We were using similar logic to fetch a bunch of classes from an element in one go in the layout support, but this change broke that ability. The refactor of layout in #54075 to fix that has more info.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree - might be a nice feature though to have an add_classes method that accepts an array or something.

Copy link
Contributor Author

@andrewserong andrewserong Dec 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks folks, good points! Related to what you've linked to in #54075, I just did a quick test of what happens if the class string includes multiple classes, and it seems it gets treated as a single classname so the de-duping logic in add_class gets skipped, resulting in potential duplicates:

image

I'll stick with the foreach for now 🙂

}
}
}

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',
);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like min-height is already getting removed when aspect-ratio is set in global styles, so do we still need this here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes — we need the min-height: unset in global styles output (which is this part of the code) so that we unset the Cover block (or any other block's) default min-height value that might be in use.

$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
Loading