From 323174aad891ec5d08929d2f53e15001dc9edc30 Mon Sep 17 00:00:00 2001 From: Ben Dwyer Date: Fri, 20 May 2022 23:19:14 +0100 Subject: [PATCH] Global Styles: Load block CSS conditionally (#41160) * Global Styles: Load block styles as part of the block CSS * Global Styles: Load block styles as part of the block CSS * tidy up * fix PHP linting * add comments --- .../wordpress-6.1/class-wp-theme-json-6-1.php | 128 ++++++++++++++++++ .../get-global-styles-and-settings.php | 23 ++++ lib/compat/wordpress-6.1/script-loader.php | 83 ++++++++++++ lib/load.php | 2 + 4 files changed, 236 insertions(+) create mode 100644 lib/compat/wordpress-6.1/get-global-styles-and-settings.php create mode 100644 lib/compat/wordpress-6.1/script-loader.php diff --git a/lib/compat/wordpress-6.1/class-wp-theme-json-6-1.php b/lib/compat/wordpress-6.1/class-wp-theme-json-6-1.php index cea9bd77cf17e6..f05ef80c7e2bad 100644 --- a/lib/compat/wordpress-6.1/class-wp-theme-json-6-1.php +++ b/lib/compat/wordpress-6.1/class-wp-theme-json-6-1.php @@ -87,4 +87,132 @@ protected static function get_blocks_metadata() { return static::$blocks_metadata; } + + /** + * Builds metadata for the style nodes, which returns in the form of: + * + * [ + * [ + * 'path' => [ 'path', 'to', 'some', 'node' ], + * 'selector' => 'CSS selector for some node', + * 'duotone' => 'CSS selector for duotone for some node' + * ], + * [ + * 'path' => ['path', 'to', 'other', 'node' ], + * 'selector' => 'CSS selector for other node', + * 'duotone' => null + * ], + * ] + * + * @since 5.8.0 + * + * @param array $theme_json The tree to extract style nodes from. + * @return array + */ + protected static function get_style_nodes( $theme_json ) { + $nodes = array(); + if ( ! isset( $theme_json['styles'] ) ) { + return $nodes; + } + + // Top-level. + $nodes[] = array( + 'path' => array( 'styles' ), + 'selector' => static::ROOT_BLOCK_SELECTOR, + ); + + if ( isset( $theme_json['styles']['elements'] ) ) { + foreach ( $theme_json['styles']['elements'] as $element => $node ) { + $nodes[] = array( + 'path' => array( 'styles', 'elements', $element ), + 'selector' => static::ELEMENTS[ $element ], + ); + } + } + + // Blocks. + if ( ! isset( $theme_json['styles']['blocks'] ) ) { + return $nodes; + } + + $nodes = array_merge( $nodes, static::get_block_nodes( $theme_json ) ); + + // This filter allows us to modify the output of WP_Theme_JSON so that we can do things like loading block CSS independently. + return apply_filters( 'gutenberg_get_style_nodes', $nodes ); + } + + /** + * A public helper to get the block nodes from a theme.json file. + * + * @return array The block nodes in theme.json. + */ + public function get_styles_block_nodes() { + return static::get_block_nodes( $this->theme_json ); + } + + /** + * An internal method to get the block nodes from a theme.json file. + * + * @param array $theme_json The theme.json converted to an array. + * + * @return array The block nodes in theme.json. + */ + private static function get_block_nodes( $theme_json ) { + $selectors = static::get_blocks_metadata(); + $nodes = array(); + if ( ! isset( $theme_json['styles'] ) ) { + return $nodes; + } + + // Blocks. + if ( ! isset( $theme_json['styles']['blocks'] ) ) { + return $nodes; + } + + foreach ( $theme_json['styles']['blocks'] as $name => $node ) { + $selector = null; + if ( isset( $selectors[ $name ]['selector'] ) ) { + $selector = $selectors[ $name ]['selector']; + } + + $duotone_selector = null; + if ( isset( $selectors[ $name ]['duotone'] ) ) { + $duotone_selector = $selectors[ $name ]['duotone']; + } + + $nodes[] = array( + 'name' => $name, + 'path' => array( 'styles', 'blocks', $name ), + 'selector' => $selector, + 'duotone' => $duotone_selector, + ); + + if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'] ) ) { + foreach ( $theme_json['styles']['blocks'][ $name ]['elements'] as $element => $node ) { + $nodes[] = array( + 'path' => array( 'styles', 'blocks', $name, 'elements', $element ), + 'selector' => $selectors[ $name ]['elements'][ $element ], + ); + } + } + } + + return $nodes; + } + + /** + * Gets the CSS rules for a particular block from theme.json. + * + * @param array $block_metadata Meta data about the block to get styles for. + * + * @return array Styles for the block. + */ + public function get_styles_for_block( $block_metadata ) { + $node = _wp_array_get( $this->theme_json, $block_metadata['path'], array() ); + $selector = $block_metadata['selector']; + $settings = _wp_array_get( $this->theme_json, array( 'settings' ) ); + $declarations = static::compute_style_properties( $node, $settings ); + $block_rules = static::to_ruleset( $selector, $declarations ); + return $block_rules; + } } diff --git a/lib/compat/wordpress-6.1/get-global-styles-and-settings.php b/lib/compat/wordpress-6.1/get-global-styles-and-settings.php new file mode 100644 index 00000000000000..64d6a1050791b6 --- /dev/null +++ b/lib/compat/wordpress-6.1/get-global-styles-and-settings.php @@ -0,0 +1,23 @@ +get_styles_block_nodes(); + foreach ( $block_nodes as $metadata ) { + $block_css = $tree->get_styles_for_block( $metadata ); + $block_name = str_replace( 'core/', '', $metadata['name'] ); + // These block styles are added on block_render. + // This hooks inline CSS to them so that they are loaded conditionally + // based on whether or not the block is used on the page. + wp_add_inline_style( 'wp-block-' . $block_name, $block_css ); + } +} diff --git a/lib/compat/wordpress-6.1/script-loader.php b/lib/compat/wordpress-6.1/script-loader.php new file mode 100644 index 00000000000000..d45a3a9344053b --- /dev/null +++ b/lib/compat/wordpress-6.1/script-loader.php @@ -0,0 +1,83 @@ +