From b93775c32ee8889529a1479ef28bd3a6a0f10990 Mon Sep 17 00:00:00 2001 From: scruffian Date: Thu, 23 Feb 2023 17:49:39 +0000 Subject: [PATCH 01/13] Duotone: Output presets in block supports --- lib/block-supports/duotone.php | 53 ++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/lib/block-supports/duotone.php b/lib/block-supports/duotone.php index e0dea9e97cc2bb..76a70971f091de 100644 --- a/lib/block-supports/duotone.php +++ b/lib/block-supports/duotone.php @@ -418,6 +418,16 @@ function gutenberg_register_duotone_support( $block_type ) { } } +class WP_Duotone { + /** + * An array of Duotone presets. + * + * @since 6.3.0 + * @var array + */ + static $duotone_presets = array(); +} + /** * Render out the duotone stylesheet and SVG. * @@ -538,6 +548,8 @@ static function () use ( $filter_svg, $selector ) { } } ); + } else { + WP_Duotone::$duotone_presets[] = $filter_preset['slug']; } // Like the layout hook, this assumes the hook only applies to blocks with a single wrapper. @@ -560,3 +572,44 @@ static function () use ( $filter_svg, $selector ) { // 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 ); + +add_action( + 'wp_footer', + static function () { + // Get the presets from the theme.json. + $tree = WP_Theme_JSON_Resolver::get_merged_data(); + $settings = $tree->get_settings(); + $presets_by_origin = _wp_array_get( $settings, array( 'color', 'duotone' ), array() ); + $flat_presets = []; + // Flatten the array + foreach( $presets_by_origin as $presets ) { + foreach( $presets as $preset ) { + $flat_presets[ _wp_to_kebab_case( $preset['slug'] ) ] = $preset; + } + } + + foreach( WP_Duotone::$duotone_presets as $preset_slug ) { + // Convert $preset from a slug to an array of colors. + $duotone_preset = $flat_presets[ $preset_slug ]; + $filter_svg = gutenberg_get_duotone_filter_svg( $duotone_preset ); + echo $filter_svg; + } + + /* + * Safari renders elements incorrectly on first paint when the + * SVG filter comes after the content that it is filtering, so + * we force a repaint with a WebKit hack which solves the issue. + */ + global $is_safari; + if ( $is_safari ) { + /* + * Simply accessing el.offsetHeight flushes layout and style + * changes in WebKit without having to wait for setTimeout. + */ + printf( + '', + wp_json_encode( $selector ) + ); + } + } +); \ No newline at end of file From 5d16d5cf8fb5fc0729ccc5fe987765d51f6b75a3 Mon Sep 17 00:00:00 2001 From: scruffian Date: Thu, 23 Feb 2023 17:51:18 +0000 Subject: [PATCH 02/13] remove filters --- lib/compat/wordpress-6.2/script-loader.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/compat/wordpress-6.2/script-loader.php b/lib/compat/wordpress-6.2/script-loader.php index 149a6a18e14507..fb6a3d454f2174 100644 --- a/lib/compat/wordpress-6.2/script-loader.php +++ b/lib/compat/wordpress-6.2/script-loader.php @@ -191,3 +191,6 @@ function gutenberg_enqueue_global_styles_custom_css() { } } add_action( 'wp_enqueue_scripts', 'gutenberg_enqueue_global_styles_custom_css' ); + +remove_action( 'wp_body_open', 'wp_global_styles_render_svg_filters' ); +remove_action( 'in_admin_header', 'wp_global_styles_render_svg_filters' ); \ No newline at end of file From 8517e27268bde0d70b34054021a53012addaaa1f Mon Sep 17 00:00:00 2001 From: Jerry Jones Date: Thu, 23 Feb 2023 15:52:32 -0600 Subject: [PATCH 03/13] Check if duotone is unset before adding to duotone presets global 'unset' is a valid attribute to pass, but we don't want to output any svg definition for it. Checking for unset makes sure we're dealing with a valid slug that should output a corresponding svg definition. --- lib/block-supports/duotone.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/block-supports/duotone.php b/lib/block-supports/duotone.php index 76a70971f091de..39ff81bb0e31a5 100644 --- a/lib/block-supports/duotone.php +++ b/lib/block-supports/duotone.php @@ -548,7 +548,7 @@ static function () use ( $filter_svg, $selector ) { } } ); - } else { + } else if ( ! $is_duotone_unset ) { WP_Duotone::$duotone_presets[] = $filter_preset['slug']; } From 9c29e6a71d074e6d35627aa536771274ae21f56f Mon Sep 17 00:00:00 2001 From: scruffian Date: Fri, 24 Feb 2023 11:11:51 +0000 Subject: [PATCH 04/13] save the presets that are used in theme.json for blocks that are actually rendered --- lib/block-supports/duotone.php | 39 +++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/lib/block-supports/duotone.php b/lib/block-supports/duotone.php index 39ff81bb0e31a5..10b3b91f2bffef 100644 --- a/lib/block-supports/duotone.php +++ b/lib/block-supports/duotone.php @@ -549,7 +549,7 @@ static function () use ( $filter_svg, $selector ) { } ); } else if ( ! $is_duotone_unset ) { - WP_Duotone::$duotone_presets[] = $filter_preset['slug']; + WP_Duotone::$duotone_presets[ $filter_preset['slug'] ] = true; } // Like the layout hook, this assumes the hook only applies to blocks with a single wrapper. @@ -588,7 +588,7 @@ static function () { } } - foreach( WP_Duotone::$duotone_presets as $preset_slug ) { + foreach( WP_Duotone::$duotone_presets as $preset_slug => $value ) { // Convert $preset from a slug to an array of colors. $duotone_preset = $flat_presets[ $preset_slug ]; $filter_svg = gutenberg_get_duotone_filter_svg( $duotone_preset ); @@ -612,4 +612,37 @@ static function () { ); } } -); \ No newline at end of file +); + +function gutenberg_render_duotone_svgs( $block_content, $block ) { + // Get the per block settings from the theme.json. + $tree = WP_Theme_JSON_Resolver::get_merged_data(); + $block_nodes = $tree->get_styles_block_nodes(); + + // For each of the block settings, if there's a preset then save it + $block_metadata = null; + foreach( $block_nodes as $node ) { + // If the block doesn't support Duotone then skip it. + if ( empty( $node['duotone'] ) ) { + continue; + } + if( ! empty( $node['name'] ) && $node['name'] === $block['blockName'] ) { + $block_metadata = $node; + break; + } + }; + if ( $block_metadata ) { + $theme_json = $tree->get_raw_data(); + $styles = _wp_array_get( $theme_json, $block_metadata['path'], array() ); + $duotone_filter = _wp_array_get( $styles, array( 'filter', 'duotone' ), array() ); + if ( $duotone_filter ) { + preg_match('/var\(--wp--preset--duotone--(.*)\)/', $duotone_filter, $matches ); + // Save the preset slug to be used later. + WP_Duotone::$duotone_presets[ $matches[1] ] = true; + } + } + + // Don't change the block content. + return $block_content; +} +add_action( 'render_block', 'gutenberg_render_duotone_svgs', 10, 2 ); \ No newline at end of file From 859ee9bc79a2e9abc54a86f0fb6bf23cf283d666 Mon Sep 17 00:00:00 2001 From: scruffian Date: Fri, 24 Feb 2023 11:15:09 +0000 Subject: [PATCH 05/13] rename function --- lib/block-supports/duotone.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/block-supports/duotone.php b/lib/block-supports/duotone.php index 10b3b91f2bffef..b474d56cb7fb03 100644 --- a/lib/block-supports/duotone.php +++ b/lib/block-supports/duotone.php @@ -614,7 +614,7 @@ static function () { } ); -function gutenberg_render_duotone_svgs( $block_content, $block ) { +function gutenberg_save_duotone_preset_svgs( $block_content, $block ) { // Get the per block settings from the theme.json. $tree = WP_Theme_JSON_Resolver::get_merged_data(); $block_nodes = $tree->get_styles_block_nodes(); @@ -645,4 +645,4 @@ function gutenberg_render_duotone_svgs( $block_content, $block ) { // Don't change the block content. return $block_content; } -add_action( 'render_block', 'gutenberg_render_duotone_svgs', 10, 2 ); \ No newline at end of file +add_action( 'render_block', 'gutenberg_save_duotone_preset_svgs', 10, 2 ); \ No newline at end of file From f42f7696bf2adbbe924654ac6eae5b10b3058252 Mon Sep 17 00:00:00 2001 From: scruffian Date: Fri, 24 Feb 2023 11:23:55 +0000 Subject: [PATCH 06/13] reability --- lib/block-supports/duotone.php | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/lib/block-supports/duotone.php b/lib/block-supports/duotone.php index b474d56cb7fb03..107d3491c23c66 100644 --- a/lib/block-supports/duotone.php +++ b/lib/block-supports/duotone.php @@ -633,16 +633,27 @@ function gutenberg_save_duotone_preset_svgs( $block_content, $block ) { }; if ( $block_metadata ) { $theme_json = $tree->get_raw_data(); - $styles = _wp_array_get( $theme_json, $block_metadata['path'], array() ); - $duotone_filter = _wp_array_get( $styles, array( 'filter', 'duotone' ), array() ); - if ( $duotone_filter ) { - preg_match('/var\(--wp--preset--duotone--(.*)\)/', $duotone_filter, $matches ); + // Define the path to the duotone filter. + $duotone_filter_path = array_merge( $block_metadata['path'], array( 'filter', 'duotone' ) ); + $duotone_filter = _wp_array_get( $theme_json, $duotone_filter_path, array() ); + $duotone_slug = gutenberg_get_duotone_slug_from_preset_css_variable( $duotone_filter ); + if ( $duotone_slug ) { // Save the preset slug to be used later. - WP_Duotone::$duotone_presets[ $matches[1] ] = true; + WP_Duotone::$duotone_presets[ $duotone_slug ] = true; } } // Don't change the block content. return $block_content; } + +function gutenberg_get_duotone_slug_from_preset_css_variable( $css_variable ) { + if ( ! empty( $css_variable ) ) { + // Get the preset slug from the filter. + preg_match('/var\(--wp--preset--duotone--(.*)\)/', $css_variable, $matches ); + if ( $matches[1] ) { + return $matches[1]; + } + } +} add_action( 'render_block', 'gutenberg_save_duotone_preset_svgs', 10, 2 ); \ No newline at end of file From f2d9165fb247f135827aa95f10211a34ef9b4adb Mon Sep 17 00:00:00 2001 From: scruffian Date: Fri, 24 Feb 2023 14:37:41 +0000 Subject: [PATCH 07/13] only output the duotone presets that are used --- lib/block-supports/duotone.php | 7 ++- lib/class-wp-theme-json-gutenberg.php | 8 ++-- .../get-global-styles-and-settings.php | 43 ++++++++++--------- 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/lib/block-supports/duotone.php b/lib/block-supports/duotone.php index 107d3491c23c66..f7b376a358ae61 100644 --- a/lib/block-supports/duotone.php +++ b/lib/block-supports/duotone.php @@ -311,8 +311,10 @@ function gutenberg_get_duotone_filter_id( $preset ) { * @return string Duotone CSS filter property url value. */ function gutenberg_get_duotone_filter_property( $preset ) { - $filter_id = gutenberg_get_duotone_filter_id( $preset ); - return "url('#" . $filter_id . "')"; + if( array_key_exists( $preset['slug'], WP_Duotone::$duotone_presets ) ) { + $filter_id = gutenberg_get_duotone_filter_id( $preset ); + return "url('#" . $filter_id . "')"; + } } /** @@ -626,6 +628,7 @@ function gutenberg_save_duotone_preset_svgs( $block_content, $block ) { if ( empty( $node['duotone'] ) ) { continue; } + // TODO: Why do some nodes not have a name? if( ! empty( $node['name'] ) && $node['name'] === $block['blockName'] ) { $block_metadata = $node; break; diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 7334cb6e97cde5..0281c387952fcf 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -1634,12 +1634,12 @@ protected static function get_settings_values_by_slug( $settings, $preset_metada ) { $value_func = $preset_metadata['value_func']; $value = call_user_func( $value_func, $preset ); - } else { - // If we don't have a value, then don't add it to the result. - continue; } - $result[ $slug ] = $value; + // If we don't have a value, then don't add it to the result. + if ( ! empty( $value) ) { + $result[ $slug ] = $value; + } } } return $result; diff --git a/lib/compat/wordpress-6.2/get-global-styles-and-settings.php b/lib/compat/wordpress-6.2/get-global-styles-and-settings.php index e02a0466a0b98f..d96e1adb6c4579 100644 --- a/lib/compat/wordpress-6.2/get-global-styles-and-settings.php +++ b/lib/compat/wordpress-6.2/get-global-styles-and-settings.php @@ -121,46 +121,49 @@ function gutenberg_get_global_stylesheet( $types = array() ) { } /* - * If variables are part of the stylesheet, - * we add them. + * For the remaining types (presets, styles), we do consider origins: * - * This is so themes without a theme.json still work as before 5.9: - * they can override the default presets. - * See https://core.trac.wordpress.org/ticket/54782 + * - themes without theme.json: only the classes for the presets defined by core + * - themes with theme.json: the presets and styles classes, both from core and the theme */ - $styles_variables = ''; - if ( in_array( 'variables', $types, true ) ) { + + // We need to do this first for duotone. + $styles_rest = ''; + if ( ! empty( $types ) ) { /* * We only use the default, theme, and custom origins. * This is because styles for blocks origin are added * at a later phase (render cycle) so we only render the ones in use. * @see wp_add_global_styles_for_blocks */ - $origins = array( 'default', 'theme', 'custom' ); - $styles_variables = $tree->get_stylesheet( array( 'variables' ), $origins ); - $types = array_diff( $types, array( 'variables' ) ); + $origins = array( 'default', 'theme', 'custom' ); + if ( ! $supports_theme_json ) { + $origins = array( 'default' ); + } + $styles_rest = $tree->get_stylesheet( $types, $origins ); } /* - * For the remaining types (presets, styles), we do consider origins: + * If variables are part of the stylesheet, + * we add them. * - * - themes without theme.json: only the classes for the presets defined by core - * - themes with theme.json: the presets and styles classes, both from core and the theme + * This is so themes without a theme.json still work as before 5.9: + * they can override the default presets. + * See https://core.trac.wordpress.org/ticket/54782 */ - $styles_rest = ''; - if ( ! empty( $types ) ) { + $styles_variables = ''; + if ( in_array( 'variables', $types, true ) ) { /* * We only use the default, theme, and custom origins. * This is because styles for blocks origin are added * at a later phase (render cycle) so we only render the ones in use. * @see wp_add_global_styles_for_blocks */ - $origins = array( 'default', 'theme', 'custom' ); - if ( ! $supports_theme_json ) { - $origins = array( 'default' ); - } - $styles_rest = $tree->get_stylesheet( $types, $origins ); + $origins = array( 'default', 'theme', 'custom' ); + $styles_variables = $tree->get_stylesheet( array( 'variables' ), $origins ); + $types = array_diff( $types, array( 'variables' ) ); } + $stylesheet = $styles_variables . $styles_rest; if ( $can_use_cached ) { wp_cache_set( $cache_key, $stylesheet, $cache_group ); From 05f1fd41cd25f57a9deae22678e9514a000b9408 Mon Sep 17 00:00:00 2001 From: scruffian Date: Fri, 24 Feb 2023 14:40:47 +0000 Subject: [PATCH 08/13] remove unused code --- lib/block-supports/duotone.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/block-supports/duotone.php b/lib/block-supports/duotone.php index f7b376a358ae61..9addb8925ee844 100644 --- a/lib/block-supports/duotone.php +++ b/lib/block-supports/duotone.php @@ -628,8 +628,8 @@ function gutenberg_save_duotone_preset_svgs( $block_content, $block ) { if ( empty( $node['duotone'] ) ) { continue; } - // TODO: Why do some nodes not have a name? - if( ! empty( $node['name'] ) && $node['name'] === $block['blockName'] ) { + + if( $node['name'] === $block['blockName'] ) { $block_metadata = $node; break; } From 3b0511b93d9c7b6bc56b6f6fb4a43bf9d5757833 Mon Sep 17 00:00:00 2001 From: scruffian Date: Fri, 24 Feb 2023 14:52:35 +0000 Subject: [PATCH 09/13] output custom duotones still --- lib/block-supports/duotone.php | 12 ++++++++---- lib/class-wp-theme-json-gutenberg.php | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/block-supports/duotone.php b/lib/block-supports/duotone.php index 9addb8925ee844..b19600b027cb06 100644 --- a/lib/block-supports/duotone.php +++ b/lib/block-supports/duotone.php @@ -304,6 +304,12 @@ function gutenberg_get_duotone_filter_id( $preset ) { return 'wp-duotone-' . $preset['slug']; } +function gutenberg_get_duotone_preset_value( $preset ) { + if( array_key_exists( $preset['slug'], WP_Duotone::$duotone_presets ) ) { + return gutenberg_get_duotone_filter_property( $preset ); + } +} + /** * Returns the CSS filter property url to reference the rendered SVG. * @@ -311,10 +317,8 @@ function gutenberg_get_duotone_filter_id( $preset ) { * @return string Duotone CSS filter property url value. */ function gutenberg_get_duotone_filter_property( $preset ) { - if( array_key_exists( $preset['slug'], WP_Duotone::$duotone_presets ) ) { - $filter_id = gutenberg_get_duotone_filter_id( $preset ); - return "url('#" . $filter_id . "')"; - } + $filter_id = gutenberg_get_duotone_filter_id( $preset ); + return "url('#" . $filter_id . "')"; } /** diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 0281c387952fcf..b68dd37e51ffba 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -152,7 +152,7 @@ class WP_Theme_JSON_Gutenberg { 'path' => array( 'color', 'duotone' ), 'prevent_override' => array( 'color', 'defaultDuotone' ), 'use_default_names' => false, - 'value_func' => 'gutenberg_get_duotone_filter_property', + 'value_func' => 'gutenberg_get_duotone_preset_value', 'css_vars' => '--wp--preset--duotone--$slug', 'classes' => array(), 'properties' => array( 'filter' ), From 5bb17b81dca51a10863028616e09ec691ae71b86 Mon Sep 17 00:00:00 2001 From: scruffian Date: Fri, 24 Feb 2023 15:26:22 +0000 Subject: [PATCH 10/13] reset changes to theme json class --- .../get-global-styles-and-settings.php | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/lib/compat/wordpress-6.2/get-global-styles-and-settings.php b/lib/compat/wordpress-6.2/get-global-styles-and-settings.php index d96e1adb6c4579..58ec275563c4a8 100644 --- a/lib/compat/wordpress-6.2/get-global-styles-and-settings.php +++ b/lib/compat/wordpress-6.2/get-global-styles-and-settings.php @@ -121,49 +121,48 @@ function gutenberg_get_global_stylesheet( $types = array() ) { } /* - * For the remaining types (presets, styles), we do consider origins: + * If variables are part of the stylesheet, + * we add them. * - * - themes without theme.json: only the classes for the presets defined by core - * - themes with theme.json: the presets and styles classes, both from core and the theme + * This is so themes without a theme.json still work as before 5.9: + * they can override the default presets. + * See https://core.trac.wordpress.org/ticket/54782 */ - - // We need to do this first for duotone. - $styles_rest = ''; - if ( ! empty( $types ) ) { + $styles_variables = ''; + if ( in_array( 'variables', $types, true ) ) { /* * We only use the default, theme, and custom origins. * This is because styles for blocks origin are added * at a later phase (render cycle) so we only render the ones in use. * @see wp_add_global_styles_for_blocks */ - $origins = array( 'default', 'theme', 'custom' ); - if ( ! $supports_theme_json ) { - $origins = array( 'default' ); - } - $styles_rest = $tree->get_stylesheet( $types, $origins ); + $origins = array( 'default', 'theme', 'custom' ); + $styles_variables = $tree->get_stylesheet( array( 'variables' ), $origins ); + $types = array_diff( $types, array( 'variables' ) ); } /* - * If variables are part of the stylesheet, - * we add them. + * For the remaining types (presets, styles), we do consider origins: * - * This is so themes without a theme.json still work as before 5.9: - * they can override the default presets. - * See https://core.trac.wordpress.org/ticket/54782 + * - themes without theme.json: only the classes for the presets defined by core + * - themes with theme.json: the presets and styles classes, both from core and the theme */ - $styles_variables = ''; - if ( in_array( 'variables', $types, true ) ) { + $styles_rest = ''; + if ( ! empty( $types ) ) { /* * We only use the default, theme, and custom origins. * This is because styles for blocks origin are added * at a later phase (render cycle) so we only render the ones in use. * @see wp_add_global_styles_for_blocks */ - $origins = array( 'default', 'theme', 'custom' ); - $styles_variables = $tree->get_stylesheet( array( 'variables' ), $origins ); - $types = array_diff( $types, array( 'variables' ) ); + $origins = array( 'default', 'theme', 'custom' ); + if ( ! $supports_theme_json ) { + $origins = array( 'default' ); + } + $styles_rest = $tree->get_stylesheet( $types, $origins ); } + $stylesheet = $styles_variables . $styles_rest; if ( $can_use_cached ) { wp_cache_set( $cache_key, $stylesheet, $cache_group ); From 8262c8228157f08b081d18af27f8eb85939332ee Mon Sep 17 00:00:00 2001 From: scruffian Date: Fri, 24 Feb 2023 15:29:12 +0000 Subject: [PATCH 11/13] rename action to filter --- lib/block-supports/duotone.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/block-supports/duotone.php b/lib/block-supports/duotone.php index b19600b027cb06..83f149b0771bd4 100644 --- a/lib/block-supports/duotone.php +++ b/lib/block-supports/duotone.php @@ -663,4 +663,4 @@ function gutenberg_get_duotone_slug_from_preset_css_variable( $css_variable ) { } } } -add_action( 'render_block', 'gutenberg_save_duotone_preset_svgs', 10, 2 ); \ No newline at end of file +add_filter( 'render_block', 'gutenberg_save_duotone_preset_svgs', 10, 2 ); \ No newline at end of file From 4b10ea2146d4e609b9f2781e4c8d1721082f5e9f Mon Sep 17 00:00:00 2001 From: scruffian Date: Fri, 24 Feb 2023 16:03:02 +0000 Subject: [PATCH 12/13] fixes for classic themes --- lib/block-supports/duotone.php | 96 +++++++++++++++------------ lib/class-wp-theme-json-gutenberg.php | 35 ++++++++++ 2 files changed, 87 insertions(+), 44 deletions(-) diff --git a/lib/block-supports/duotone.php b/lib/block-supports/duotone.php index 83f149b0771bd4..6061721a4b91b0 100644 --- a/lib/block-supports/duotone.php +++ b/lib/block-supports/duotone.php @@ -432,6 +432,50 @@ class WP_Duotone { * @var array */ static $duotone_presets = array(); + + static function gutenberg_save_duotone_preset_svgs( $block_content, $block ) { + // Get the per block settings from the theme.json. + $tree = WP_Theme_JSON_Resolver::get_merged_data(); + $block_nodes = $tree->get_styles_block_nodes(); + + // For each of the block settings, if there's a preset then save it + $block_metadata = null; + foreach( $block_nodes as $node ) { + // If the block doesn't support Duotone then skip it. + if ( empty( $node['duotone'] ) ) { + continue; + } + + if( $node['name'] === $block['blockName'] ) { + $block_metadata = $node; + break; + } + }; + if ( $block_metadata ) { + $theme_json = $tree->get_raw_data(); + // Define the path to the duotone filter. + $duotone_filter_path = array_merge( $block_metadata['path'], array( 'filter', 'duotone' ) ); + $duotone_filter = _wp_array_get( $theme_json, $duotone_filter_path, array() ); + $duotone_slug = static::gutenberg_get_duotone_slug_from_preset_css_variable( $duotone_filter ); + if ( $duotone_slug ) { + // Save the preset slug to be used later. + WP_Duotone::$duotone_presets[ $duotone_slug ] = true; + } + } + + // Don't change the block content. + return $block_content; + } + + static function gutenberg_get_duotone_slug_from_preset_css_variable( $css_variable ) { + if ( ! empty( $css_variable ) ) { + // Get the preset slug from the filter. + preg_match('/var\(--wp--preset--duotone--(.*)\)/', $css_variable, $matches ); + if ( $matches[1] ) { + return $matches[1]; + } + } + } } /** @@ -598,7 +642,14 @@ static function () { // Convert $preset from a slug to an array of colors. $duotone_preset = $flat_presets[ $preset_slug ]; $filter_svg = gutenberg_get_duotone_filter_svg( $duotone_preset ); + $filter_css = gutenberg_get_duotone_filter_property( $duotone_preset ); + echo $filter_svg; + + // This is for classic themes - in block themes, the CSS is added in the head via the value_func. + $duotone_preset_css_var = WP_Theme_JSON_Gutenberg::get_preset_css_var( array( 'color', 'duotone' ), $preset_slug ); + wp_add_inline_style( 'core-block-supports', 'body{' . $duotone_preset_css_var . ' :' . $filter_css . ';}' ); + } /* @@ -620,47 +671,4 @@ static function () { } ); -function gutenberg_save_duotone_preset_svgs( $block_content, $block ) { - // Get the per block settings from the theme.json. - $tree = WP_Theme_JSON_Resolver::get_merged_data(); - $block_nodes = $tree->get_styles_block_nodes(); - - // For each of the block settings, if there's a preset then save it - $block_metadata = null; - foreach( $block_nodes as $node ) { - // If the block doesn't support Duotone then skip it. - if ( empty( $node['duotone'] ) ) { - continue; - } - - if( $node['name'] === $block['blockName'] ) { - $block_metadata = $node; - break; - } - }; - if ( $block_metadata ) { - $theme_json = $tree->get_raw_data(); - // Define the path to the duotone filter. - $duotone_filter_path = array_merge( $block_metadata['path'], array( 'filter', 'duotone' ) ); - $duotone_filter = _wp_array_get( $theme_json, $duotone_filter_path, array() ); - $duotone_slug = gutenberg_get_duotone_slug_from_preset_css_variable( $duotone_filter ); - if ( $duotone_slug ) { - // Save the preset slug to be used later. - WP_Duotone::$duotone_presets[ $duotone_slug ] = true; - } - } - - // Don't change the block content. - return $block_content; -} - -function gutenberg_get_duotone_slug_from_preset_css_variable( $css_variable ) { - if ( ! empty( $css_variable ) ) { - // Get the preset slug from the filter. - preg_match('/var\(--wp--preset--duotone--(.*)\)/', $css_variable, $matches ); - if ( $matches[1] ) { - return $matches[1]; - } - } -} -add_filter( 'render_block', 'gutenberg_save_duotone_preset_svgs', 10, 2 ); \ No newline at end of file +add_filter( 'render_block', array( 'WP_Duotone', 'gutenberg_save_duotone_preset_svgs' ), 10, 2 ); \ No newline at end of file diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index b68dd37e51ffba..3504319173df2a 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -3460,4 +3460,39 @@ public function set_spacing_sizes() { _wp_array_set( $this->theme_json, array( 'settings', 'spacing', 'spacingSizes', 'default' ), $spacing_sizes ); } + + /** + * Returns the CSS variable for a preset. + * + * @since 6.3.0 + * + * @param array $path Path to the preset. + * @param string $slug Slug of the preset. + * @return string CSS variable. + */ + public static function get_preset_css_var( $path, $slug ) { + $duotone_preset_metadata = static::get_preset_metadata_from_path( $path ); + return static::replace_slug_in_string( $duotone_preset_metadata['css_vars'], $slug ); + } + + /** + * Returns the metadata for a preset. + * + * @since 6.3.0 + * + * @param array $path Path to the preset. + * @return array Preset metadata. + */ + static function get_preset_metadata_from_path( $path ) { + $preset_metadata = array_filter( + static::PRESETS_METADATA, + function( $preset ) use ( &$path ) { + if ( $preset['path'] === $path ) { + return $preset; + } + } + ); + + return reset( $preset_metadata ); + } } From 00d3dac78d49e2e1f2b2404afb47c28c2697c5b2 Mon Sep 17 00:00:00 2001 From: Jerry Jones Date: Fri, 24 Feb 2023 16:15:44 -0600 Subject: [PATCH 13/13] Huge refactor of outputting only duotone css and svg needed for rendered blocks I'm sorry this is in one giant commit. The general idea is: - On wp_loaded - save all duotone presets defined in merged global styles - save all block names and their corresponding duotone slug info - on block render, check if the block: - is one of the blocks using duotone via global styles - has any defined duotone styles - if so, add it to the duotone_output array to have its duotone info output --- lib/block-supports/duotone.php | 230 +++++++++++++++++++++------------ 1 file changed, 144 insertions(+), 86 deletions(-) diff --git a/lib/block-supports/duotone.php b/lib/block-supports/duotone.php index 6061721a4b91b0..f6d155ff8fa5e8 100644 --- a/lib/block-supports/duotone.php +++ b/lib/block-supports/duotone.php @@ -305,7 +305,7 @@ function gutenberg_get_duotone_filter_id( $preset ) { } function gutenberg_get_duotone_preset_value( $preset ) { - if( array_key_exists( $preset['slug'], WP_Duotone::$duotone_presets ) ) { + if( array_key_exists( $preset['slug'], WP_Duotone::$duotone_output ) ) { return gutenberg_get_duotone_filter_property( $preset ); } } @@ -426,56 +426,135 @@ function gutenberg_register_duotone_support( $block_type ) { class WP_Duotone { /** - * An array of Duotone presets. - * + * An array of Duotone presets from global, theme, and custom styles. + * + * Example: + * [ + * 'blue-orange' => + * [ + * 'slug' => 'blue-orange', + * 'colors' => [ '#0000ff', '#ffcc00' ], + * ] + * ], + * … + * ] + * * @since 6.3.0 * @var array */ static $duotone_presets = array(); - static function gutenberg_save_duotone_preset_svgs( $block_content, $block ) { + /** + * An array of block names from global, theme, and custom styles that have duotone presets. We'll use this to quickly + * check if a block being rendered needs to have duotone applied, and which duotone preset to use. + * + * Example: + * [ + * 'core/featured-image' => 'blue-orange', + * … + * ] + * + */ + static $duotone_block_names = array(); + + /** + * An array of Duotone SVG and CSS ouput needed for the frontend duotone rendering based on what is + * being ouptput on the page. Organized by a slug of the preset/color group and the information needed + * to generate the SVG and CSS at render. + * + * Example: + * [ + * 'blue-orange' => [ + * 'slug' => 'blue-orange', + * 'colors' => [ '#0000ff', '#ffcc00' ], + * ], + * 'wp-duotone-000000-ffffff-2' => [ + * 'slug' => 'wp-duotone-000000-ffffff-2', + * 'colors' => [ '#000000', '#ffffff' ], + * ], + * ] + * + * @since 6.3.0 + * @var array + */ + static $duotone_output = array(); + + + + /** + * Get all possible duotone presets from global and theme styles and store as slug => [ colors array ] + * We only want to process this one time. On block render we'll access and output only the needed presets for that page. + * + */ + static function gutenberg_save_duotone_presets() { + // Get the per block settings from the theme.json. + $tree = WP_Theme_JSON_Resolver::get_merged_data(); + $settings = $tree->get_settings(); + $presets_by_origin = _wp_array_get( $settings, array( 'color', 'duotone' ), array() ); + $flat_presets = []; + // Flatten the array + foreach( $presets_by_origin as $presets ) { + foreach( $presets as $preset ) { + // $flat_presets[ _wp_to_kebab_case( $preset['slug'] ) ] = $preset; + self::$duotone_presets[ _wp_to_kebab_case( $preset['slug'] ) ] = [ + 'slug' => $preset[ 'slug' ], + 'colors' => $preset[ 'colors' ], + ]; + } + } + } + + static function gutenberg_save_duotone_block_names() { // Get the per block settings from the theme.json. $tree = WP_Theme_JSON_Resolver::get_merged_data(); $block_nodes = $tree->get_styles_block_nodes(); + $theme_json = $tree->get_raw_data(); + - // For each of the block settings, if there's a preset then save it - $block_metadata = null; - foreach( $block_nodes as $node ) { - // If the block doesn't support Duotone then skip it. - if ( empty( $node['duotone'] ) ) { + foreach( $block_nodes as $block_node ) { + // This block definition doesn't include any duotone settings. Skip it. + if ( empty( $block_node['duotone'] ) ) { continue; } - if( $node['name'] === $block['blockName'] ) { - $block_metadata = $node; - break; - } - }; - if ( $block_metadata ) { - $theme_json = $tree->get_raw_data(); - // Define the path to the duotone filter. - $duotone_filter_path = array_merge( $block_metadata['path'], array( 'filter', 'duotone' ) ); + // Value looks like this: 'var(--wp--preset--duotone--blue-orange)' + $duotone_filter_path = array_merge( $block_node['path'], array( 'filter', 'duotone' ) ); $duotone_filter = _wp_array_get( $theme_json, $duotone_filter_path, array() ); - $duotone_slug = static::gutenberg_get_duotone_slug_from_preset_css_variable( $duotone_filter ); - if ( $duotone_slug ) { - // Save the preset slug to be used later. - WP_Duotone::$duotone_presets[ $duotone_slug ] = true; + + if( empty( $duotone_filter ) ) { + continue; } - } - // Don't change the block content. - return $block_content; + // If it has a duotone preset, save the block name and the preset slug. + self::$duotone_block_names[ $block_node[ 'name' ] ] = self::gutenberg_get_duotone_slug_from_preset_css_variable( $duotone_filter ); + } } static function gutenberg_get_duotone_slug_from_preset_css_variable( $css_variable ) { if ( ! empty( $css_variable ) ) { // Get the preset slug from the filter. + // TODO: Support var:preset|duotone|slug syntax. preg_match('/var\(--wp--preset--duotone--(.*)\)/', $css_variable, $matches ); if ( $matches[1] ) { return $matches[1]; } } } + + /** + * Get all possible duotone presets from global and theme styles. We only want to process this one time. On block render we'll access and output only the needed presets for that page. + * + */ + static function gutenberg_identify_used_duotone_blocks( $block_content, $block ) { + // If the block name exists in our pre-defined list of block selectors that use duotone in theme.json (or related), add it to our list of duotone to output + if( array_key_exists( $block['blockName'], self::$duotone_block_names ) ) { + $preset_slug = self::$duotone_block_names[ $block['blockName'] ]; + + self::$duotone_output[ $preset_slug ] = self::$duotone_presets[ $preset_slug ]; + } + + return $block_content; + } } /** @@ -573,33 +652,13 @@ function gutenberg_render_duotone_support( $block_content, $block ) { // For *non*-presets then generate an SVG for the filter. // Note: duotone presets are already pre-generated so no need to do this again. if ( $is_duotone_colors_array ) { - $filter_svg = gutenberg_get_duotone_filter_svg( $filter_preset ); - - add_action( - 'wp_footer', - static function () use ( $filter_svg, $selector ) { - echo $filter_svg; - - /* - * Safari renders elements incorrectly on first paint when the - * SVG filter comes after the content that it is filtering, so - * we force a repaint with a WebKit hack which solves the issue. - */ - global $is_safari; - if ( $is_safari ) { - /* - * Simply accessing el.offsetHeight flushes layout and style - * changes in WebKit without having to wait for setTimeout. - */ - printf( - '', - wp_json_encode( $selector ) - ); - } - } - ); + WP_Duotone::$duotone_output[ $filter_preset[ 'slug' ] ] = $filter_preset; } else if ( ! $is_duotone_unset ) { - WP_Duotone::$duotone_presets[ $filter_preset['slug'] ] = true; + WP_Duotone::$duotone_output[ $filter_preset['slug'] ] = WP_Duotone::$duotone_presets[ $filter_preset['slug'] ]; + } + + if( ! $is_duotone_unset ) { + duotone_safari_rerender_hack( $selector ); } // Like the layout hook, this assumes the hook only applies to blocks with a single wrapper. @@ -626,49 +685,48 @@ static function () use ( $filter_svg, $selector ) { add_action( 'wp_footer', static function () { - // Get the presets from the theme.json. - $tree = WP_Theme_JSON_Resolver::get_merged_data(); - $settings = $tree->get_settings(); - $presets_by_origin = _wp_array_get( $settings, array( 'color', 'duotone' ), array() ); - $flat_presets = []; - // Flatten the array - foreach( $presets_by_origin as $presets ) { - foreach( $presets as $preset ) { - $flat_presets[ _wp_to_kebab_case( $preset['slug'] ) ] = $preset; - } - } - - foreach( WP_Duotone::$duotone_presets as $preset_slug => $value ) { + foreach( WP_Duotone::$duotone_output as $duotone_data ) { // Convert $preset from a slug to an array of colors. - $duotone_preset = $flat_presets[ $preset_slug ]; - $filter_svg = gutenberg_get_duotone_filter_svg( $duotone_preset ); - $filter_css = gutenberg_get_duotone_filter_property( $duotone_preset ); + $filter_svg = gutenberg_get_duotone_filter_svg( $duotone_data ); + $filter_css = gutenberg_get_duotone_filter_property( $duotone_data ); echo $filter_svg; // This is for classic themes - in block themes, the CSS is added in the head via the value_func. - $duotone_preset_css_var = WP_Theme_JSON_Gutenberg::get_preset_css_var( array( 'color', 'duotone' ), $preset_slug ); + $duotone_preset_css_var = WP_Theme_JSON_Gutenberg::get_preset_css_var( array( 'color', 'duotone' ), $duotone_data[ 'slug'] ); wp_add_inline_style( 'core-block-supports', 'body{' . $duotone_preset_css_var . ' :' . $filter_css . ';}' ); } - - /* - * Safari renders elements incorrectly on first paint when the - * SVG filter comes after the content that it is filtering, so - * we force a repaint with a WebKit hack which solves the issue. - */ - global $is_safari; - if ( $is_safari ) { - /* - * Simply accessing el.offsetHeight flushes layout and style - * changes in WebKit without having to wait for setTimeout. - */ - printf( - '', - wp_json_encode( $selector ) - ); - } } ); -add_filter( 'render_block', array( 'WP_Duotone', 'gutenberg_save_duotone_preset_svgs' ), 10, 2 ); \ No newline at end of file +/** + * Safari renders elements incorrectly on first paint when the SVG filter comes after the content that it is filtering, + * so we force a repaint with a WebKit hack which solves the issue. + * + * @param string $selector The selector to apply the hack for. + */ +function duotone_safari_rerender_hack( $selector ) { + add_action( + 'wp_footer', + static function () use ( $selector ) { + global $is_safari; + + // TODO: Move is_safari check to before the add_action, so we don't add the action unless we're on safari + if ( $is_safari ) { + /* + * Simply accessing el.offsetHeight flushes layout and style + * changes in WebKit without having to wait for setTimeout. + */ + printf( + '', + wp_json_encode( $selector ) + ); + } + } + ); +} + +add_action( 'wp_loaded', array( 'WP_Duotone', 'gutenberg_save_duotone_presets' ), 10 ); +add_action( 'wp_loaded', array( 'WP_Duotone', 'gutenberg_save_duotone_block_names' ), 10 ); +add_filter( 'block_render', array( 'WP_Duotone', 'gutenberg_identify_used_duotone_blocks' ), 10, 2 ); \ No newline at end of file