Skip to content

Commit

Permalink
Initial test of an experiment to combinecolor.gradient and `backgro…
Browse files Browse the repository at this point in the history
…und.backgroundImage` into one CSS property `background-image`

Merge for block supports

Moving logic to style engine

Background for gradients will exist if there's no background image

Ensuring gradient classnames aren't shown

Need to strip classnames

Add test deprecation for Group block. Need to do Quote, Pullquote, Post content and Verse
PHP linting
  • Loading branch information
ramonjd committed Jul 4, 2024
1 parent 6d52495 commit e9b71ef
Show file tree
Hide file tree
Showing 11 changed files with 282 additions and 46 deletions.
79 changes: 56 additions & 23 deletions lib/block-supports/background.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,41 +40,75 @@ 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 );

if (
! $has_background_image_support ||
wp_should_skip_block_supports_serialization( $block_type, 'background', 'backgroundImage' ) ||
! isset( $block_attributes['style']['background'] )
$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 );

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

$background_styles = array();
$background_styles['backgroundImage'] = isset( $block_attributes['style']['background']['backgroundImage'] ) ? $block_attributes['style']['background']['backgroundImage'] : array();
$background_styles['backgroundSize'] = isset( $block_attributes['style']['background']['backgroundSize'] ) ? $block_attributes['style']['background']['backgroundSize'] : 'cover';
$background_styles['backgroundImage'] = isset( $block_attributes['style']['background']['backgroundImage'] ) ? $block_attributes['style']['background']['backgroundImage'] : array();

if ( ! empty( $background_styles['backgroundImage'] ) ) {
$background_styles['backgroundSize'] = isset( $block_attributes['style']['background']['backgroundSize'] ) ? $block_attributes['style']['background']['backgroundSize'] : 'cover';
$background_styles['backgroundPosition'] = isset( $block_attributes['style']['background']['backgroundPosition'] ) ? $block_attributes['style']['background']['backgroundPosition'] : null;
$background_styles['backgroundRepeat'] = isset( $block_attributes['style']['background']['backgroundRepeat'] ) ? $block_attributes['style']['background']['backgroundRepeat'] : null;
if ( isset( $background_styles['backgroundImage']['source'] ) && 'file' === $background_styles['backgroundImage']['source'] && isset( $background_styles['backgroundImage']['url'] ) ) {
$background_styles['backgroundPosition'] = isset( $block_attributes['style']['background']['backgroundPosition'] ) ? $block_attributes['style']['background']['backgroundPosition'] : null;
$background_styles['backgroundRepeat'] = isset( $block_attributes['style']['background']['backgroundRepeat'] ) ? $block_attributes['style']['background']['backgroundRepeat'] : null;

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

if (
empty( $background_styles ) &&
empty( $background_gradient_styles )
) {
return $block_content;
}

$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'] ) ) {
// 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 = '';

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

$updated_style = '';

if ( ! empty( $existing_style ) ) {
$updated_style = $existing_style;
Expand All @@ -85,7 +119,6 @@ function gutenberg_render_background_support( $block_content, $block ) {

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

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 @@ -105,13 +105,13 @@ function gutenberg_apply_colors_support( $block_type, $block_attributes ) {
$color_block_styles['background'] = $preset_background_color ? $preset_background_color : $custom_background_color;
}

// Gradients.
/* // Gradients.
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
16 changes: 10 additions & 6 deletions lib/class-wp-theme-json-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -2272,7 +2272,7 @@ protected static function compute_style_properties( $styles, $settings = array()
return $declarations;
}

$root_variable_duplicates = array();
$properties_to_remove = array();

foreach ( $properties as $css_property => $value_path ) {
$value = static::get_property_value( $styles, $value_path, $theme_json );
Expand All @@ -2287,7 +2287,7 @@ protected static function compute_style_properties( $styles, $settings = array()
}

if ( str_starts_with( $css_property, '--wp--style--root--' ) && $use_root_padding ) {
$root_variable_duplicates[] = substr( $css_property, strlen( '--wp--style--root--' ) );
$properties_to_remove[] = substr( $css_property, strlen( '--wp--style--root--' ) );
}

// Look up protected properties, keyed by value path.
Expand All @@ -2302,10 +2302,14 @@ protected static function compute_style_properties( $styles, $settings = array()
}
}

// 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 Down Expand Up @@ -2343,7 +2347,7 @@ protected static function compute_style_properties( $styles, $settings = array()
}

// 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 @@ -390,6 +390,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
22 changes: 18 additions & 4 deletions packages/block-editor/src/hooks/color.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,14 @@ 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';
const gradientClass =
! hasBackgroundImage && shouldSerialize( 'gradients' )
? __experimentalGetGradientClass( gradient )
: undefined;

const backgroundClass = shouldSerialize( 'background' )
? getColorClassName( 'background-color', backgroundColor )
Expand Down Expand Up @@ -201,15 +206,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
9 changes: 8 additions & 1 deletion packages/block-editor/src/hooks/use-color-props.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,14 @@ export function getColorClassesAndStyles( attributes ) {
);
const textClass = getColorClassName( 'color', textColor );

const gradientClass = __experimentalGetGradientClass( 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 gradientClass = ! hasBackgroundImage
? __experimentalGetGradientClass( gradient )
: undefined;

const hasGradient = gradientClass || style?.color?.gradient;

// Determine color CSS class name list.
Expand Down
133 changes: 133 additions & 0 deletions packages/block-library/src/group/deprecated.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,139 @@ const migrateAttributes = ( attributes ) => {
};

const deprecated = [
// Version with preset gradient color and background image.
// If there is a background image and gradient preset, remove the gradient classname.
{
attributes: {
tagName: {
type: 'string',
default: 'div',
},
templateLock: {
type: [ 'string', 'boolean' ],
enum: [ 'all', 'insert', 'contentOnly', false ],
},
allowedBlocks: {
type: 'array',
},
},
supports: {
__experimentalOnEnter: true,
__experimentalOnMerge: true,
__experimentalSettings: true,
align: [ 'wide', 'full' ],
anchor: true,
ariaLabel: true,
html: false,
background: {
backgroundImage: true,
backgroundSize: true,
__experimentalDefaultControls: {
backgroundImage: true,
},
},
color: {
gradients: true,
heading: true,
button: true,
link: true,
__experimentalDefaultControls: {
background: true,
text: true,
},
},
spacing: {
margin: [ 'top', 'bottom' ],
padding: true,
blockGap: true,
__experimentalDefaultControls: {
padding: true,
blockGap: true,
},
},
dimensions: {
minHeight: true,
},
__experimentalBorder: {
color: true,
radius: true,
style: true,
width: true,
__experimentalDefaultControls: {
color: true,
radius: true,
style: true,
width: true,
},
},
position: {
sticky: true,
},
typography: {
fontSize: true,
lineHeight: true,
__experimentalFontFamily: true,
__experimentalFontWeight: true,
__experimentalFontStyle: true,
__experimentalTextTransform: true,
__experimentalTextDecoration: true,
__experimentalLetterSpacing: true,
__experimentalDefaultControls: {
fontSize: true,
},
},
layout: {
allowSizingOnChildren: true,
},
interactivity: {
clientNavigation: true,
},
},
save( { attributes: { tagName: Tag } } ) {
return (
<Tag { ...useInnerBlocksProps.save( useBlockProps.save() ) } />
);
},
isEligible( { gradient, style } ) {
return (
gradient &&
( typeof style?.background?.backgroundImage === 'string' ||
typeof style?.background?.backgroundImage?.url ===
'string' )
);
},
migrate( attributes ) {
const { style = null, gradient } = attributes;

const hasBackgroundImage =
typeof style?.background?.backgroundImage === 'string' ||
typeof style?.background?.backgroundImage?.url === 'string';

if ( hasBackgroundImage && gradient ) {
let newClassName = attributes?.className;
if ( newClassName ) {
const regex = new RegExp(
`has-${ gradient }-gradient-background[\\s]?`,
'g'
);
newClassName = newClassName.replace( regex, '' ).trim();
}
return {
...attributes,
className: newClassName ? newClassName : undefined,
style: {
...style,
color: {
...style.color,
gradient: `var(--wp--preset--gradient--${ gradient })`,
},
},
gradient: null,
};
}
return attributes;
},
},
// Version with default layout.
{
attributes: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,9 @@ protected static function filter_declaration( $property, $value, $spacer = '' )
$filtered_value = wp_strip_all_tags( $value, true );

if ( '' !== $filtered_value ) {
return safecss_filter_attr( "{$property}:{$spacer}{$filtered_value}" );
// @TODO safecss_filter_attr can't handle background-image with multiple values.
return "{$property}:{$spacer}{$filtered_value}";
//return safecss_filter_attr( "{$property}:{$spacer}{$filtered_value}" );
}
return '';
}
Expand Down
Loading

0 comments on commit e9b71ef

Please sign in to comment.