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

Duotone: Make it possible to define duotone settings in theme.json #34073

Closed
wants to merge 16 commits into from
Closed
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
187 changes: 130 additions & 57 deletions lib/block-supports/duotone.php
Original file line number Diff line number Diff line change
Expand Up @@ -251,64 +251,36 @@ function gutenberg_register_duotone_support( $block_type ) {
}

/**
* Render out the duotone stylesheet and SVG.
* Returns the markup for duotone style tag.
*
* @param string $block_content Rendered block content.
* @param array $block Block object.
* @return string Filtered block content.
* @param string $selectors_group The selector to target.
* @param string $duotone_id The ID of the SVG filter.
* @return string The markup for the <style> tag.
*/
function gutenberg_render_duotone_support( $block_content, $block ) {
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );

$duotone_support = false;
if ( $block_type && property_exists( $block_type, 'supports' ) ) {
$duotone_support = _wp_array_get( $block_type->supports, array( 'color', '__experimentalDuotone' ), false );
}

$has_duotone_attribute = isset( $block['attrs']['style']['color']['duotone'] );

if (
! $duotone_support ||
! $has_duotone_attribute
) {
return $block_content;
}

$duotone_colors = $block['attrs']['style']['color']['duotone'];

$duotone_values = array(
'r' => array(),
'g' => array(),
'b' => array(),
);
foreach ( $duotone_colors as $color_str ) {
$color = gutenberg_tinycolor_string_to_rgb( $color_str );

$duotone_values['r'][] = $color['r'] / 255;
$duotone_values['g'][] = $color['g'] / 255;
$duotone_values['b'][] = $color['b'] / 255;
}

$duotone_id = 'wp-duotone-filter-' . uniqid();

$selectors = explode( ',', $duotone_support );
$selectors_scoped = array_map(
function ( $selector ) use ( $duotone_id ) {
return '.' . $duotone_id . ' ' . trim( $selector );
},
$selectors
);
$selectors_group = implode( ', ', $selectors_scoped );

function gutenberg_get_duotone_style( $selectors_group, $duotone_id ) {
ob_start();

?>

<style>
<?php echo $selectors_group; ?> {
filter: url( <?php echo esc_url( '#' . $duotone_id ); ?> );
}
</style>
<?php
$duotone_style = ob_get_clean();
return $duotone_style;
}

/**
* Returns the markup for duotone filters.
*
* @param string $duotone_id
* @param array $duotone_values
* @return string The markup for the <svg> tag.
*/
function gutenberg_get_duotone_svg_filters( $duotone_id, $duotone_values ) {
ob_start();
?>

<svg
xmlns:xlink="http://www.w3.org/1999/xlink"
Expand Down Expand Up @@ -340,8 +312,96 @@ function ( $selector ) use ( $duotone_id ) {
</svg>

<?php
$duotone_svg = ob_get_clean();
return $duotone_svg;
}

/**
* Get Duotone colors from array.
*
* @param array $duotone_colors The array of colors that define the duotone style.
* @return array The array of values mapped to rgb.
*/
function get_duotone_color_values( $duotone_colors ) {
$duotone_values = array(
'r' => array(),
'g' => array(),
'b' => array(),
);
foreach ( $duotone_colors as $color_str ) {
$color = gutenberg_tinycolor_string_to_rgb( $color_str );

$duotone_values['r'][] = $color['r'] / 255;
$duotone_values['g'][] = $color['g'] / 255;
$duotone_values['b'][] = $color['b'] / 255;
}

return $duotone_values;
}

/**
* Output Duotone markup in the footer.
*
* @param $duotone_markup The markup for the SVG filer, and if necessary the style tag.
*/
function gutenberg_output_duotone_markup( $duotone_markup ) {
add_action(
// Ideally we should use wp_head, but SVG defs can't be put in there.
is_admin() ? 'in_admin_footer' : 'wp_footer',
function () use ( $duotone_markup ) {
echo $duotone_markup;
}
);
}

/**
* Render out the duotone stylesheet and SVG.
*
* @param string $block_content Rendered block content.
* @param array $block Block object.
* @return string Filtered block content.
*/
function gutenberg_render_duotone_support( $block_content, $block ) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Github is confused. None of this code is new, it's just broken into smaller functions.

$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );

$duotone_support = false;
if ( $block_type && property_exists( $block_type, 'supports' ) ) {
$duotone_support = _wp_array_get( $block_type->supports, array( 'color', '__experimentalDuotone' ), false );
}

$has_duotone_attribute = isset( $block['attrs']['style']['color']['duotone'] );

if (
! $duotone_support ||
! $has_duotone_attribute
) {
return $block_content;
}

$duotone_colors = $block['attrs']['style']['color']['duotone'];

$duotone_values = get_duotone_color_values( $duotone_colors );
$duotone_id = gutenberg_get_duotone_filter_id( uniqid() );

$selectors = explode( ',', $duotone_support );
$selectors_scoped = array_map(
function ( $selector ) use ( $duotone_id ) {
return '.' . $duotone_id . ' ' . trim( $selector );
},
$selectors
);
$selectors_group = implode( ', ', $selectors_scoped );

if ( 'unset' === $duotone_colors ) {
$duotone_markup = '<style>' . $selectors_group . '{ filter: unset; }</style>';
} else {
$duotone_markup = gutenberg_get_duotone_style( $selectors_group, $duotone_id );
$duotone_markup .= gutenberg_get_duotone_svg_filters( $duotone_id, $duotone_values );
}

$duotone = ob_get_clean();
if ( ! is_admin() ) {
gutenberg_output_duotone_markup( $duotone_markup );
}

// Like the layout hook, this assumes the hook only applies to blocks with a single wrapper.
$content = preg_replace(
Expand All @@ -351,14 +411,6 @@ function ( $selector ) use ( $duotone_id ) {
1
);

add_action(
// Ideally we should use wp_head, but SVG defs can't be put in there.
'wp_footer',
function () use ( $duotone ) {
echo $duotone;
}
);

return $content;
}

Expand All @@ -373,3 +425,24 @@ function () use ( $duotone ) {
// Remove WordPress core filter to avoid rendering duplicate support elements.
remove_filter( 'render_block', 'wp_render_duotone_support', 10, 2 );
add_filter( 'render_block', 'gutenberg_render_duotone_support', 10, 2 );

/**
* Returns the ID for the duotone filter
*
* @param string $id The ID of the filter.
*/
function gutenberg_get_duotone_filter_id( $id ) {
return 'wp-duotone-' . $id;
}

/**
* Generates duotone markup from a settings array
*
* @param array $duotone_setting Block object.
*/
function gutenberg_generate_duotone_filters_settings( $duotone_setting) {
$duotone_values = get_duotone_color_values( $duotone_setting['colors'] );
$duotone_id = gutenberg_get_duotone_filter_id( $duotone_setting['slug'] );
$duotone_markup = gutenberg_get_duotone_svg_filters( $duotone_id, $duotone_values );
gutenberg_output_duotone_markup( $duotone_markup );
}
9 changes: 9 additions & 0 deletions lib/class-wp-theme-json-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class WP_Theme_JSON_Gutenberg {
),
'color' => array(
'background' => null,
'duotone' => null,
'gradient' => null,
'text' => null,
),
Expand Down Expand Up @@ -218,6 +219,7 @@ class WP_Theme_JSON_Gutenberg {
'border-width' => array( 'border', 'width' ),
'border-style' => array( 'border', 'style' ),
'color' => array( 'color', 'text' ),
'filter' => array( 'color', 'duotone' ),
'font-family' => array( 'typography', 'fontFamily' ),
'font-size' => array( 'typography', 'fontSize' ),
'font-style' => array( 'typography', 'fontStyle' ),
Expand Down Expand Up @@ -855,6 +857,13 @@ private function get_block_styles( $style_nodes, $setting_nodes ) {
$preset_rules .= self::compute_preset_classes( $node, $selector );
}

$theme_json_settings = $this->get_settings();
if( ! empty( $theme_json_settings['color'] ) && ! empty( $theme_json_settings['color']['duotone'] ) && ! empty( $theme_json_settings['color']['duotone'] ) ) {
foreach( $theme_json_settings['color']['duotone'] as $duotone_setting ) {
gutenberg_generate_duotone_filters_settings( $duotone_setting );
}
}

return $block_rules . $preset_rules;
}

Expand Down
4 changes: 2 additions & 2 deletions lib/global-styles.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@
*/
function gutenberg_experimental_global_styles_get_stylesheet( $tree, $type = 'all' ) {
// Check if we can use cached.
$can_use_cached = (
$can_use_cached = false; /*(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is just for testing

( 'all' === $type ) &&
( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) &&
( ! defined( 'SCRIPT_DEBUG' ) || ! SCRIPT_DEBUG ) &&
( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) &&
! is_admin()
);
);*/

if ( $can_use_cached ) {
// Check if we have the styles already cached.
Expand Down
22 changes: 18 additions & 4 deletions packages/block-editor/src/hooks/duotone.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ const EMPTY_ARRAY = [];
* @return {Object} R, G, and B values.
*/
export function getValuesFromColors( colors = [] ) {
if ( typeof colors === 'string' ) {
return colors;
}

const values = { r: [], g: [], b: [] };

colors.forEach( ( color ) => {
Expand Down Expand Up @@ -66,11 +70,21 @@ export function getValuesFromColors( colors = [] ) {
* @return {WPElement} Duotone element.
*/
function DuotoneFilter( { selector, id, values } ) {
if ( values === 'unset' ) {
const unsetStylesheet = `
${ selector } {
filter: unset !important; /* We need !important to overide rules that come from theme.json.*/
}
`;
return (
<style dangerouslySetInnerHTML={ { __html: unsetStylesheet } } />
);
}

const stylesheet = `
${ selector } {
filter: url( #${ id } );
}
`;
${ selector } {
filter: url( #${ id } ) !important; /* We need !important to overide rules that come from theme.json.*/
}`;

return (
<>
Expand Down
3 changes: 2 additions & 1 deletion packages/block-library/src/image/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@
},
"__experimentalBorder": {
"radius": true
}
},
"__experimentalSelector": ".wp-block-image img"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure if this is a good idea?

Copy link
Member

@oandregal oandregal Aug 17, 2021

Choose a reason for hiding this comment

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

Do the other block supports still work? If they don't, we'd need to implement "selectors per property" first, which we talked about at #28913

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think the only one that would be affected would be border radius, which seems to still work fine.

Copy link
Member

Choose a reason for hiding this comment

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

This is problematic.

These are the testing steps:

  • Using the empty theme, set the border-radius for the image block to 0px in the theme.json
  • Create a post and add an image. Set its border-radius to any value.

I expected the image to change the border but it didn't. The theme style rule has higher specificity than the block's.

The border-radius rules were added in https://github.com/WordPress/gutenberg/pull/27667/files#r699061648 I'm going to pull @aaronrobertshaw thoughts on this. I'd think the fix for this would be to serialize the border-radius directly to the img element, using __experimentalSkipSerialization. Once we have fixed that in a separate PR, we could continue with this one.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for the ping.

__experimentalSkipSerialization didn't exist yet when border-radius support was originally added to the image block. Now that it does, there's no reason we can't move the application of the border styles to the inner image element.

In fact, there is already an open PR that does this as part of adopting border color and width for the image block.

},
"styles": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@ import {
const PLACEHOLDER_VALUES = [ '#333', '#CCC' ];

export default function CustomDuotoneBar( { value, onChange } ) {
if ( typeof value === 'string' ) {
return null;
}

const hasGradient = !! value;
const values = hasGradient ? value : PLACEHOLDER_VALUES;
const background = getGradientFromCSSColors( values );
const controlPoints = getColorStopsFromColors( values );

return (
<CustomGradientBar
disableInserter
Expand Down
Loading