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

Background images: add support for theme.json ref value resolution #64128

Merged
merged 11 commits into from
Aug 15, 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
1 change: 1 addition & 0 deletions backport-changelog/6.7/7137.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
https://github.com/WordPress/wordpress-develop/pull/7137

* https://github.com/WordPress/gutenberg/pull/64128
* https://github.com/WordPress/gutenberg/pull/64192
* https://github.com/WordPress/gutenberg/pull/64328
68 changes: 49 additions & 19 deletions lib/class-wp-theme-json-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -2329,7 +2329,7 @@ protected static function flatten_tree( $tree, $prefix = '', $token = '--' ) {
* ```php
* array(
* 'name' => 'property_name',
* 'value' => 'property_value,
* 'value' => 'property_value',
ramonjd marked this conversation as resolved.
Show resolved Hide resolved
* )
* ```
*
Expand All @@ -2338,6 +2338,7 @@ protected static function flatten_tree( $tree, $prefix = '', $token = '--' ) {
* @since 6.1.0 Added `$theme_json`, `$selector`, and `$use_root_padding` parameters.
* @since 6.5.0 Output a `min-height: unset` rule when `aspect-ratio` is set.
* @since 6.6.0 Passing current theme JSON settings to wp_get_typography_font_size_value(). Using style engine to correctly fetch background CSS values.
* @since 6.7.0 Allow ref resolution of background properties.
*
* @param array $styles Styles to process.
* @param array $settings Theme settings.
Expand Down Expand Up @@ -2381,21 +2382,28 @@ protected static function compute_style_properties( $styles, $settings = array()
$root_variable_duplicates[] = substr( $css_property, $root_style_length );
}

// Processes background styles.
if ( 'background' === $value_path[0] && isset( $styles['background'] ) ) {
/*
* For user-uploaded images at the block level, assign defaults.
* Matches defaults applied in the editor and in block supports: background.php.
*/
if ( static::ROOT_BLOCK_SELECTOR !== $selector && ! empty( $styles['background']['backgroundImage']['id'] ) ) {
$styles['background']['backgroundSize'] = $styles['background']['backgroundSize'] ?? 'cover';
// If the background size is set to `contain` and no position is set, set the position to `center`.
if ( 'contain' === $styles['background']['backgroundSize'] && empty( $styles['background']['backgroundPosition'] ) ) {
$styles['background']['backgroundPosition'] = '50% 50%';
}
/*
* Processes background image styles.
* If the value is a URL, it will be converted to a CSS `url()` value.
* For an uploaded image (images with a database ID), apply size and position
* defaults equal to those applied in block supports in lib/background.php.
*/
if ( 'background-image' === $css_property && ! empty( $value ) ) {
ramonjd marked this conversation as resolved.
Show resolved Hide resolved
$background_styles = gutenberg_style_engine_get_styles(
array( 'background' => array( 'backgroundImage' => $value ) )
);

$value = $background_styles['declarations'][ $css_property ];
}
if ( empty( $value ) && static::ROOT_BLOCK_SELECTOR !== $selector && ! empty( $styles['background']['backgroundImage']['id'] ) ) {
if ( 'background-size' === $css_property ) {
$value = 'cover';
}
// If the background size is set to `contain` and no position is set, set the position to `center`.
if ( 'background-position' === $css_property ) {
$background_size = $styles['background']['backgroundSize'] ?? null;
$value = 'contain' === $background_size ? '50% 50%' : null;
}
$background_styles = gutenberg_style_engine_get_styles( array( 'background' => $styles['background'] ) );
$value = $background_styles['declarations'][ $css_property ] ?? $value;
}

// Skip if empty and not "0" or value represents array of longhand values.
Expand Down Expand Up @@ -2463,6 +2471,7 @@ protected static function compute_style_properties( $styles, $settings = array()
* @since 5.8.0
* @since 5.9.0 Added support for values of array type, which are returned as is.
* @since 6.1.0 Added the `$theme_json` parameter.
* @since 6.7.0 Added support for background image refs
*
* @param array $styles Styles subtree.
* @param array $path Which property to process.
Expand All @@ -2479,15 +2488,17 @@ protected static function get_property_value( $styles, $path, $theme_json = null
}

/*
* This converts references to a path to the value at that path
* where the values is an array with a "ref" key, pointing to a path.
* Where the current value is an array with a 'ref' key pointing
* to a path, this converts that path into the value at that path.
* For example: { "ref": "style.color.background" } => "#fff".
*/
if ( is_array( $value ) && isset( $value['ref'] ) ) {
$value_path = explode( '.', $value['ref'] );
$ref_value = _wp_array_get( $theme_json, $value_path );
$ref_value = _wp_array_get( $theme_json, $value_path, null );
// Background Image refs can refer to a string or an array containing a URL string.
$ref_value_url = $ref_value['url'] ?? null;
Copy link
Member Author

Choose a reason for hiding this comment

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

This is specifically for background images.

It's part of the reason why I think this could be a style engine util, used both internally, and externally by WP_Theme_JSON.

The style engine could subsume many style-related utils and potentially reduce the site of WP_Theme_JSON.

Copy link
Contributor

Choose a reason for hiding this comment

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

Anything that can chip away at the size of WP_Theme_JSON sounds like a win to me.

// Only use the ref value if we find anything.
if ( ! empty( $ref_value ) && is_string( $ref_value ) ) {
if ( ! empty( $ref_value ) && ( is_string( $ref_value ) || is_string( $ref_value_url ) ) ) {
$value = $ref_value;
}

Expand Down Expand Up @@ -3247,6 +3258,25 @@ public function merge( $incoming ) {
}
}
}

/*
* Style values are merged at the leaf level, however
* some values provide exceptions, namely style values that are
* objects and represent unique definitions for the style.
*/
$style_nodes = static::get_styles_block_nodes();
foreach ( $style_nodes as $style_node ) {
$path = $style_node['path'];
/*
* Background image styles should be replaced, not merged,
* as they themselves are specific object definitions for the style.
*/
$background_image_path = array_merge( $path, static::PROPERTIES_METADATA['background-image'] );
aaronrobertshaw marked this conversation as resolved.
Show resolved Hide resolved
$content = _wp_array_get( $incoming_data, $background_image_path, null );
if ( isset( $content ) ) {
_wp_array_set( $this->theme_json, $background_image_path, $content );
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
useRef,
useState,
useEffect,
useMemo,
} from '@wordpress/element';
import { useDispatch, useSelect } from '@wordpress/data';
import { focus } from '@wordpress/dom';
Expand All @@ -42,11 +43,15 @@ import { isBlobURL } from '@wordpress/blob';
/**
* Internal dependencies
*/
import { useToolsPanelDropdownMenuProps } from './utils';
import { useToolsPanelDropdownMenuProps, getResolvedValue } from './utils';
import { setImmutably } from '../../utils/object';
import MediaReplaceFlow from '../media-replace-flow';
import { store as blockEditorStore } from '../../store';
import { getResolvedThemeFilePath } from './theme-file-uri-utils';

import {
globalStylesDataKey,
globalStylesLinksDataKey,
} from '../../store/private-keys';

const IMAGE_BACKGROUND_TYPE = 'image';
const DEFAULT_CONTROLS = {
Expand Down Expand Up @@ -270,7 +275,6 @@ function BackgroundImageControls( {
onRemoveImage = noop,
onResetImage = noop,
displayInPanel,
themeFileURIs,
defaultValues,
} ) {
const mediaUpload = useSelect(
Expand Down Expand Up @@ -404,10 +408,7 @@ function BackgroundImageControls( {
name={
<InspectorImagePreviewItem
className="block-editor-global-styles-background-panel__image-preview"
imgUrl={ getResolvedThemeFilePath(
url,
themeFileURIs
) }
imgUrl={ url }
filename={ title }
label={ imgLabel }
/>
Expand Down Expand Up @@ -449,7 +450,6 @@ function BackgroundSizeControls( {
style,
inheritedValue,
defaultValues,
themeFileURIs,
} ) {
const sizeValue =
style?.background?.backgroundSize ||
Expand Down Expand Up @@ -587,7 +587,7 @@ function BackgroundSizeControls( {
<FocalPointPicker
__nextHasNoMarginBottom
label={ __( 'Focal point' ) }
url={ getResolvedThemeFilePath( imageValue, themeFileURIs ) }
url={ imageValue }
value={ backgroundPositionToCoords( backgroundPositionValue ) }
onChange={ updateBackgroundPosition }
/>
Expand Down Expand Up @@ -697,8 +697,44 @@ export default function BackgroundPanel( {
defaultControls = DEFAULT_CONTROLS,
defaultValues = {},
headerLabel = __( 'Background image' ),
themeFileURIs,
} ) {
/*
* Resolve any inherited "ref" pointers.
* Should the block editor need resolved, inherited values
* across all controls, this could be abstracted into a hook,
* e.g., useResolveGlobalStyle
*/
const { globalStyles, _links } = useSelect( ( select ) => {
const { getSettings } = select( blockEditorStore );
const _settings = getSettings();
return {
globalStyles: _settings[ globalStylesDataKey ],
_links: _settings[ globalStylesLinksDataKey ],
};
}, [] );
const resolvedInheritedValue = useMemo( () => {
const resolvedValues = {
background: {},
};

if ( ! inheritedValue?.background ) {
return inheritedValue;
}

Object.entries( inheritedValue?.background ).forEach(
( [ key, backgroundValue ] ) => {
resolvedValues.background[ key ] = getResolvedValue(
backgroundValue,
{
styles: globalStyles,
_links,
}
);
}
);
return resolvedValues;
}, [ globalStyles, _links, inheritedValue ] );

const resetAllFilter = useCallback( ( previousValue ) => {
return {
...previousValue,
Expand All @@ -710,11 +746,11 @@ export default function BackgroundPanel( {
onChange( setImmutably( value, [ 'background' ], {} ) );

const { title, url } = value?.background?.backgroundImage || {
...inheritedValue?.background?.backgroundImage,
...resolvedInheritedValue?.background?.backgroundImage,
};
const hasImageValue =
hasBackgroundImageValue( value ) ||
hasBackgroundImageValue( inheritedValue );
hasBackgroundImageValue( resolvedInheritedValue );

const imageValue =
value?.background?.backgroundImage ||
Expand Down Expand Up @@ -756,19 +792,15 @@ export default function BackgroundPanel( {
<BackgroundControlsPanel
label={ title }
filename={ title }
url={ getResolvedThemeFilePath(
url,
themeFileURIs
) }
url={ url }
onToggle={ setIsDropDownOpen }
hasImageValue={ hasImageValue }
>
<VStack spacing={ 3 } className="single-column">
<BackgroundImageControls
onChange={ onChange }
style={ value }
inheritedValue={ inheritedValue }
themeFileURIs={ themeFileURIs }
inheritedValue={ resolvedInheritedValue }
displayInPanel
onResetImage={ () => {
setIsDropDownOpen( false );
Expand All @@ -784,17 +816,15 @@ export default function BackgroundPanel( {
panelId={ panelId }
style={ value }
defaultValues={ defaultValues }
inheritedValue={ inheritedValue }
themeFileURIs={ themeFileURIs }
inheritedValue={ resolvedInheritedValue }
/>
</VStack>
</BackgroundControlsPanel>
) : (
<BackgroundImageControls
onChange={ onChange }
style={ value }
inheritedValue={ inheritedValue }
themeFileURIs={ themeFileURIs }
inheritedValue={ resolvedInheritedValue }
defaultValues={ defaultValues }
onResetImage={ () => {
setIsDropDownOpen( false );
Expand Down
5 changes: 0 additions & 5 deletions packages/block-editor/src/components/global-styles/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,11 +209,6 @@ export function useGlobalStyle(
return [ result, setStyle ];
}

export function useGlobalStyleLinks() {
const { merged: mergedConfig } = useContext( GlobalStylesContext );
return mergedConfig?._links;
}

/**
* React hook that overrides a global settings object with block and element specific settings.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ export {
useGlobalSetting,
useGlobalStyle,
useSettingsForBlockElement,
useGlobalStyleLinks,
} from './hooks';
export { getBlockCSSSelector } from './get-block-css-selector';
export {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -1008,9 +1008,23 @@ describe( 'global styles renderer', () => {
ref: 'styles.elements.h1.typography.letterSpacing',
},
},
background: {
backgroundImage: {
ref: 'styles.background.backgroundImage',
},
backgroundSize: {
ref: 'styles.background.backgroundSize',
},
},
};
const tree = {
styles: {
background: {
backgroundImage: {
url: 'http://my-image.org/image.gif',
},
backgroundSize: 'cover',
},
elements: {
h1: {
typography: {
Expand All @@ -1026,6 +1040,8 @@ describe( 'global styles renderer', () => {
).toEqual( [
'font-size: var(--wp--preset--font-size--xx-large)',
'letter-spacing: 2px',
"background-image: url( 'http://my-image.org/image.gif' )",
'background-size: cover',
] );
} );
it( 'should set default values for block background styles', () => {
Expand Down
Loading
Loading