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

WIP Experiment: combine gradient and background image into background-image #60739

Draft
wants to merge 3 commits into
base: trunk
Choose a base branch
from
Draft
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
115 changes: 85 additions & 30 deletions lib/block-supports/background.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,52 +40,107 @@ function gutenberg_register_background_support( $block_type ) {
* @return string Filtered block content.
*/
function gutenberg_render_background_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_background_image_support = block_has_support( $block_type, array( 'background', 'backgroundImage' ), false );
$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_background_image_support = block_has_support( $block_type, array( 'background', 'backgroundImage' ), false );
$has_background_gradient_support = block_has_support( $block_type, array( 'color', 'gradients' ), false );
$global_styles = gutenberg_get_global_styles();
$global_block_styles = $global_styles['blocks'][ $block['blockName'] ] ?? null;

// Background styles.
$background_styles = array();
if ( $has_background_image_support &&
! wp_should_skip_block_supports_serialization( $block_type, 'background', 'backgroundImage' ) &&
! empty( $block_attributes['style']['background'] )
) {
$background_styles['backgroundImage'] = $block_attributes['style']['background']['backgroundImage'] ?? null;
$background_styles['backgroundSize'] = $block_attributes['style']['background']['backgroundSize'] ?? null;
$background_styles['backgroundPosition'] = $block_attributes['style']['background']['backgroundPosition'] ?? null;
$background_styles['backgroundRepeat'] = $block_attributes['style']['background']['backgroundRepeat'] ?? null;
$background_styles['backgroundAttachment'] = $block_attributes['style']['background']['backgroundAttachment'] ?? null;

if ( ! empty( $background_styles['backgroundImage'] ) ) {
$background_styles['backgroundSize'] = $background_styles['backgroundSize'] ?? 'cover';
// If the background size is set to `contain` and no position is set, set the position to `center`.
if ( 'contain' === $background_styles['backgroundSize'] && ! $background_styles['backgroundPosition'] ) {
$background_styles['backgroundPosition'] = 'center';
}
}
}

// Gradient styles.
$background_gradient_styles = array();
if ( $has_background_gradient_support && ! wp_should_skip_block_supports_serialization( $block_type, 'color', 'gradients' ) ) {
$preset_gradient_color = array_key_exists( 'gradient', $block_attributes ) ? "var:preset|gradient|{$block_attributes['gradient']}" : null;
$custom_gradient_color = $block_attributes['style']['color']['gradient'] ?? null;
$background_gradient_styles['gradient'] = $preset_gradient_color ? $preset_gradient_color : $custom_gradient_color;
}

// @TODO: This is a temporary solution to handle the inheritance of background image and gradient.
// This should be handled in the style engine.??
// if inherited gradient and style background image, remove the gradient class, merge the gradient style with the style attribute
// if inherited background image and style gradient.
if ( ! isset( $background_styles['backgroundImage'] ) && ! empty( $global_block_styles['background']['backgroundImage'] ) && isset( $background_gradient_styles['gradient'] ) ) {
$background_styles['backgroundImage'] = $global_block_styles['background']['backgroundImage'];
}

if ( ! isset( $background_gradient_styles['gradient'] ) && ! empty( $global_block_styles['color']['gradient'] ) && isset( $background_styles['backgroundImage'] ) ) {
$background_gradient_styles['gradient'] = $global_block_styles['color']['gradient'];
}

if (
! $has_background_image_support ||
wp_should_skip_block_supports_serialization( $block_type, 'background', 'backgroundImage' ) ||
! isset( $block_attributes['style']['background'] )
empty( $background_styles ) &&
empty( $background_gradient_styles )
) {
return $block_content;
}

$background_styles = array();
$background_styles['backgroundImage'] = $block_attributes['style']['background']['backgroundImage'] ?? null;
$background_styles['backgroundSize'] = $block_attributes['style']['background']['backgroundSize'] ?? null;
$background_styles['backgroundPosition'] = $block_attributes['style']['background']['backgroundPosition'] ?? null;
$background_styles['backgroundRepeat'] = $block_attributes['style']['background']['backgroundRepeat'] ?? null;
$background_styles['backgroundAttachment'] = $block_attributes['style']['background']['backgroundAttachment'] ?? null;
if ( ! empty( $background_styles['backgroundImage'] ) ) {
$background_styles['backgroundSize'] = $background_styles['backgroundSize'] ?? 'cover';
// If the background size is set to `contain` and no position is set, set the position to `center`.
if ( 'contain' === $background_styles['backgroundSize'] && ! $background_styles['backgroundPosition'] ) {
$background_styles['backgroundPosition'] = 'center';
}
}

$styles = gutenberg_style_engine_get_styles( array( 'background' => $background_styles ) );
$styles = gutenberg_style_engine_get_styles(
array(
'background' => $background_styles,
'color' => $background_gradient_styles,
),
array( 'convert_vars_to_classnames' => ! empty( $preset_gradient_color ) && empty( $background_styles ) )
);

if ( ! empty( $styles['css'] ) ) {
if ( ! empty( $styles['css'] ) || $preset_gradient_color ) {
// Inject background 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 .= ';';
// Remove any existing background color if a background image and gradient is set.
if ( $existing_style && ! empty( $background_gradient_styles['gradient'] ) && ! empty( $background_styles ) ) {
$existing_style = preg_replace( '/background\s*:\s*' . preg_quote( $background_gradient_styles['gradient'], '/' ) . '\s*;?/', '', $existing_style, 1 );
}

if ( ! empty( $styles['css'] ) ) {
$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 );
// $tags->add_class( 'has-background' );
}
}

$updated_style .= $styles['css'];
$tags->set_attribute( 'style', $updated_style );
$tags->add_class( 'has-background' );
// This is just testing. The style engine should be smart enough to remove the classname for a preset, where
// it's used in the CSS string.
// And this is done in the site editor when editing the block.
if ( ! empty( $styles['classnames'] ) && $preset_gradient_color ) {
foreach ( explode( ' ', $styles['classnames'] ) as $class_name ) {
if ( 'has-background' !== $class_name ) {
$tags->remove_class( $class_name );
}
}
}

return $tags->get_updated_html();
Expand Down
4 changes: 2 additions & 2 deletions lib/block-supports/colors.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
$has_background_colors_support = true === $color_support ||
( isset( $color_support['background'] ) && $color_support['background'] ) ||
( is_array( $color_support ) && ! isset( $color_support['background'] ) );
$has_gradients_support = $color_support['gradients'] ?? false;

Check warning on line 90 in lib/block-supports/colors.php

View workflow job for this annotation

GitHub Actions / PHP coding standards

Unused variable $has_gradients_support.
$color_block_styles = array();

// Text colors.
Expand All @@ -105,13 +105,13 @@
$color_block_styles['background'] = $preset_background_color ? $preset_background_color : $custom_background_color;
}

// Gradients.
/* // Gradients.

Check failure on line 108 in lib/block-supports/colors.php

View workflow job for this annotation

GitHub Actions / PHP coding standards

Line indented incorrectly; expected at least 1 tabs, found 0

Check failure on line 108 in lib/block-supports/colors.php

View workflow job for this annotation

GitHub Actions / PHP coding standards

Spaces must be used for mid-line alignment; tabs are not allowed

if ( $has_gradients_support && ! wp_should_skip_block_supports_serialization( $block_type, 'color', 'gradients' ) ) {
$preset_gradient_color = array_key_exists( 'gradient', $block_attributes ) ? "var:preset|gradient|{$block_attributes['gradient']}" : null;
$custom_gradient_color = $block_attributes['style']['color']['gradient'] ?? null;
$color_block_styles['gradient'] = $preset_gradient_color ? $preset_gradient_color : $custom_gradient_color;
}
}*/

$attributes = array();
$styles = gutenberg_style_engine_get_styles( array( 'color' => $color_block_styles ), array( 'convert_vars_to_classnames' => true ) );
Expand Down
30 changes: 22 additions & 8 deletions lib/class-wp-theme-json-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -2335,9 +2335,9 @@
if ( null === $properties ) {
$properties = static::PROPERTIES_METADATA;
}
$declarations = array();
$root_variable_duplicates = array();
$root_style_length = strlen( '--wp--style--root--' );
$declarations = array();
$properties_to_remove = array();

Check failure on line 2339 in lib/class-wp-theme-json-gutenberg.php

View workflow job for this annotation

GitHub Actions / PHP coding standards

Tabs must be used to indent lines; spaces are not allowed
$root_style_length = strlen( '--wp--style--root--' );

foreach ( $properties as $css_property => $value_path ) {
if ( ! is_array( $value_path ) ) {
Expand All @@ -2358,13 +2358,17 @@
}

if ( $is_root_style && $use_root_padding ) {
$root_variable_duplicates[] = substr( $css_property, $root_style_length );
$properties_to_remove[] = substr( $css_property, $root_style_length );
}

// Processes background styles.
// Processes background styles and merges gradient with `background-image`.
if ( 'background' === $value_path[0] && isset( $styles['background'] ) ) {
$background_styles = gutenberg_style_engine_get_styles( array( 'background' => $styles['background'] ) );
$value = $background_styles['declarations'][ $css_property ] ?? $value;
$background_styles = gutenberg_style_engine_get_styles( $styles );
$background_image_styles = ! empty( $background_styles['declarations'][ $css_property ] ) ? $background_styles['declarations'][ $css_property ] : null;
if ( $background_image_styles ) {
$value = $background_image_styles;
$properties_to_remove[] = 'background';
}
}

// Skip if empty and not "0" or value represents array of longhand values.
Expand All @@ -2383,6 +2387,16 @@
continue;
}

// Look up protected properties, keyed by value path.
// Skip protected properties that are explicitly set to `null`.
$path_string = implode( '.', $value_path );
if (
isset( static::PROTECTED_PROPERTIES[ $path_string ] ) &&
_wp_array_get( $settings, static::PROTECTED_PROPERTIES[ $path_string ], null ) === null
) {
continue;
}

// Calculates fluid typography rules where available.
if ( 'font-size' === $css_property ) {
/*
Expand Down Expand Up @@ -2412,7 +2426,7 @@
}

// If a variable value is added to the root, the corresponding property should be removed.
foreach ( $root_variable_duplicates as $duplicate ) {
foreach ( $properties_to_remove as $duplicate ) {
$discard = array_search( $duplicate, array_column( $declarations, 'name' ), true );
if ( is_numeric( $discard ) ) {
array_splice( $declarations, $discard, 1 );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,16 @@ export function getStylesDeclarations(
// The goal is to move everything to server side generated engine styles
// This is temporary as we absorb more and more styles into the engine.
const extraRules = getCSSRules( blockStyles );


/*
@TODO how to do this in the editor. Probably should match frontend.
Quick way might be to check extraRules['background-image] and extraRules['background']
and then combine.
Style engine should do it? How?
Some post-processing function that combines the two?
*/

extraRules.forEach( ( rule ) => {
// Don't output padding properties if padding variables are set or if we're not editing a full template.
if (
Expand Down
108 changes: 96 additions & 12 deletions packages/block-editor/src/hooks/color.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { addFilter } from '@wordpress/hooks';
import { getBlockSupport } from '@wordpress/blocks';
import { useMemo, Platform, useCallback } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
import { getCSSRules } from '@wordpress/style-engine';

/**
* Internal dependencies
Expand All @@ -33,6 +34,7 @@ import {
} from '../components/global-styles/color-panel';
import BlockColorContrastChecker from './contrast-checker';
import { store as blockEditorStore } from '../store';
import { globalStylesDataKey } from '../store/private-keys';

export const COLOR_SUPPORT_KEY = 'color';

Expand Down Expand Up @@ -130,9 +132,15 @@ function addAttributes( settings ) {
* @param {Object|string} blockNameOrType Block type.
* @param {Object} attributes Block attributes.
*
* @param inheritedValue
* @return {Object} Filtered props applied to save element.
*/
export function addSaveProps( props, blockNameOrType, attributes ) {
export function addSaveProps(
props,
blockNameOrType,
attributes,
inheritedValue
) {
if (
! hasColorSupport( blockNameOrType ) ||
shouldSkipSerialization( blockNameOrType, COLOR_SUPPORT_KEY )
Expand All @@ -159,10 +167,21 @@ export function addSaveProps( props, blockNameOrType, attributes ) {
? getColorClassName( 'color', textColor )
: undefined;

const gradientClass = shouldSerialize( 'gradients' )
? __experimentalGetGradientClass( gradient )
: undefined;

// Do not add gradient class if there is a background image, because the values are merged into `background-image`.
const hasBackgroundImage =
typeof style?.background?.backgroundImage === 'string' ||
typeof style?.background?.backgroundImage?.url === 'string' ||
typeof inheritedValue?.background?.backgroundImage === 'string' ||
typeof inheritedValue?.background?.backgroundImage?.url === 'string';

const gradientClass =
! hasBackgroundImage && shouldSerialize( 'gradients' )
? __experimentalGetGradientClass( gradient )
: undefined;
/*
@TODO- if there's an inherited background image and an applied attribute present,
we need to rebuild the background image value to include both the inherited and applied values.
*/
const backgroundClass = shouldSerialize( 'background' )
? getColorClassName( 'background-color', backgroundColor )
: undefined;
Expand Down Expand Up @@ -201,15 +220,24 @@ function styleToAttributes( style ) {
? backgroundColorValue.substring( 'var:preset|color|'.length )
: undefined;
const gradientValue = style?.color?.gradient;

// Do not add gradient class if there is a background image, because the values are merged into `background-image`.
const hasBackgroundImage =
typeof style?.background?.backgroundImage === 'string' ||
typeof style?.background?.backgroundImage?.url === 'string';
const gradientSlug = gradientValue?.startsWith( 'var:preset|gradient|' )
? gradientValue.substring( 'var:preset|gradient|'.length )
: undefined;
const updatedStyle = { ...style };

updatedStyle.color = {
...updatedStyle.color,
text: textColorSlug ? undefined : textColorValue,
background: backgroundColorSlug ? undefined : backgroundColorValue,
gradient: gradientSlug ? undefined : gradientValue,
// @TODO this is not quite right. We don't want to add a background style value if there is a gradient.
// But we let the preset var pass to the style engine.
gradient:
! hasBackgroundImage && gradientSlug ? undefined : gradientValue,
};
return {
style: cleanEmptyObject( updatedStyle ),
Expand Down Expand Up @@ -341,6 +369,25 @@ function useBlockProps( {
'color.palette.theme',
'color.palette.default'
);
// HACK alert.
// Could this be passed to useBlockProps as middleware somewhere?
const { inheritedValue } = useSelect(
( select ) => {
const { getSettings } = select( blockEditorStore );
const _settings = getSettings();
return {
/*
* @TODO 1. Pass inherited value down to all block style controls,
* See: packages/block-editor/src/hooks/style.js
* @TODO 2. Add support for block style variations,
* See implementation: packages/block-editor/src/hooks/block-style-variation.js
*/
inheritedValue:
_settings[ globalStylesDataKey ]?.blocks?.[ name ],
};
},
[ name ]
);

const colors = useMemo(
() => [
Expand Down Expand Up @@ -377,12 +424,49 @@ function useBlockProps( {
)?.color;
}

const saveProps = addSaveProps( { style: extraStyles }, name, {
textColor,
backgroundColor,
gradient,
style,
} );
const hasBackgroundImage =
typeof style?.background?.backgroundImage === 'string' ||
typeof style?.background?.backgroundImage?.url === 'string' ||
typeof inheritedValue?.background?.backgroundImage === 'string' ||
typeof inheritedValue?.background?.backgroundImage?.url === 'string';

const hasInheritedGradient = typeof inheritedValue?.color?.gradient === 'string';

// Builds a custom style if there's an inherited image,
// and gradient present applied in editor.
// @TODO - same has to be done where's there's an inherited gradient, and image applied in editor.
if ( hasBackgroundImage || hasInheritedGradient ) {
const backgroundStyles = {
color: {
gradient: gradient
? `var:preset|gradient|${ gradient }`
: inheritedValue?.color?.gradient,
},
background: {
backgroundImage:
style?.background?.backgroundImage ||
inheritedValue?.background?.backgroundImage,
},
};
const css = getCSSRules( backgroundStyles );
const rule = css.find(
( { key } ) => key === 'backgroundImage'
)?.value;

extraStyles.backgroundImage = rule;
}

const saveProps = addSaveProps(
{ style: extraStyles },
name,
{
textColor,
backgroundColor,
gradient,
style,
},
inheritedValue
);

const hasBackgroundValue =
backgroundColor ||
Expand Down
Loading
Loading