From 55e92f5f0773dd00c3cf9584f657310e303b5052 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Tue, 22 Feb 2022 16:53:04 +1100 Subject: [PATCH 1/2] Style Engine: Try simple approach for consolidating styles into a single style tag --- lib/block-supports/layout.php | 2 +- lib/class-wp-style-engine.php | 69 +++++++++++++++++++++++++++++++++++ lib/load.php | 5 +++ 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 lib/class-wp-style-engine.php diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php index 05c1a7883057a..3afd6cd6b877a 100644 --- a/lib/block-supports/layout.php +++ b/lib/block-supports/layout.php @@ -166,7 +166,7 @@ function gutenberg_render_layout_support_flag( $block_content, $block ) { 1 ); - gutenberg_enqueue_block_support_styles( $style ); + WP_Style_Engine_Gutenberg::get_instance()->add_style( $class_name, $style ); return $content; } diff --git a/lib/class-wp-style-engine.php b/lib/class-wp-style-engine.php new file mode 100644 index 0000000000000..7eac59ead4163 --- /dev/null +++ b/lib/class-wp-style-engine.php @@ -0,0 +1,69 @@ +registered_styles[ $key ] = $value; + } + + public function output_styles() { + $style = implode( "\n", $this->registered_styles ); + echo "\n"; + } +} diff --git a/lib/load.php b/lib/load.php index 443942c98ee80..1d4e82ca01c48 100644 --- a/lib/load.php +++ b/lib/load.php @@ -112,6 +112,11 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/global-styles.php'; require __DIR__ . '/pwa.php'; +// TODO: Before this PR merges, move this to be a part of the style engine package. +// Part of the build process should be to copy the PHP file to the correct location, +// similar to the loading behaviour in `blocks.php`. +require __DIR__ . '/class-wp-style-engine.php'; + require __DIR__ . '/block-supports/elements.php'; require __DIR__ . '/block-supports/colors.php'; require __DIR__ . '/block-supports/typography.php'; From 2ba2f4d8f97fff0c059f3a63fdf1a6849eb8f0a7 Mon Sep 17 00:00:00 2001 From: ramonjd Date: Fri, 25 Feb 2022 16:12:11 +1100 Subject: [PATCH 2/2] Deduping Using array as style registry model --- lib/block-supports/layout.php | 109 +++++++++++++++++++++++----------- lib/class-wp-style-engine.php | 53 +++++++++++++++-- 2 files changed, 121 insertions(+), 41 deletions(-) diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php index 3afd6cd6b877a..9dc8c278c56ee 100644 --- a/lib/block-supports/layout.php +++ b/lib/block-supports/layout.php @@ -28,17 +28,17 @@ function gutenberg_register_layout_support( $block_type ) { /** * Generates the CSS corresponding to the provided layout. * - * @param string $selector CSS selector. - * @param array $layout Layout object. The one that is passed has already checked the existence of default block layout. + * @param string $selector CSS selector. + * @param array $layout Layout object. The one that is passed has already checked the existence of default block layout. * @param boolean $has_block_gap_support Whether the theme has support for the block gap. - * @param string $gap_value The block gap value to apply. + * @param array $gap_value The block gap value to apply. * - * @return string CSS style. + * @return string CSS style. */ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support = false, $gap_value = null ) { $layout_type = isset( $layout['type'] ) ? $layout['type'] : 'default'; - $style = ''; + $styles = array(); if ( 'default' === $layout_type ) { $content_size = isset( $layout['contentSize'] ) ? $layout['contentSize'] : ''; $wide_size = isset( $layout['wideSize'] ) ? $layout['wideSize'] : ''; @@ -51,24 +51,44 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support $all_max_width_value = wp_strip_all_tags( explode( ';', $all_max_width_value )[0] ); $wide_max_width_value = wp_strip_all_tags( explode( ';', $wide_max_width_value )[0] ); - $style = ''; if ( $content_size || $wide_size ) { - $style = "$selector > :where(:not(.alignleft):not(.alignright)) {"; - $style .= 'max-width: ' . esc_html( $all_max_width_value ) . ';'; - $style .= 'margin-left: auto !important;'; - $style .= 'margin-right: auto !important;'; - $style .= '}'; - - $style .= "$selector > .alignwide { max-width: " . esc_html( $wide_max_width_value ) . ';}'; - $style .= "$selector .alignfull { max-width: none; }"; + $styles[ "$selector > :where(:not(.alignleft):not(.alignright))" ] = array( + 'max-width' => esc_html( $all_max_width_value ), + 'margin-left' => 'auto !important', + 'margin-right' => 'auto !important', + ); + + $styles[ "$selector > .alignwide" ] = array( + 'max-width' => esc_html( $wide_max_width_value ), + ); + + $styles[ "$selector > .alignfull" ] = array( + 'max-width' => 'none', + ); } - $style .= "$selector .alignleft { float: left; margin-right: 2em; margin-left: 0; }"; - $style .= "$selector .alignright { float: right; margin-left: 2em; margin-right: 0; }"; + $styles[ "$selector .alignleft" ] = array( + 'float' => 'left', + 'margin-right' => '2em', + 'margin-left' => '0', + ); + + $styles[ "$selector .alignright" ] = array( + 'float' => 'right', + 'margin-right' => '0', + 'margin-left' => '2em', + ); + if ( $has_block_gap_support ) { - $gap_style = $gap_value ? $gap_value : 'var( --wp--style--block-gap )'; - $style .= "$selector > * { margin-top: 0; margin-bottom: 0; }"; - $style .= "$selector > * + * { margin-top: $gap_style; margin-bottom: 0; }"; + $gap_style = $gap_value ? $gap_value : 'var( --wp--style--block-gap )'; + $styles[ "$selector > *" ] = array( + 'margin-top' => '0', + 'margin-bottom' => '0', + ); + $styles[ "$selector > * + *" ] = array( + 'margin-top' => $gap_style, + 'margin-bottom' => '0', + ); } } elseif ( 'flex' === $layout_type ) { $layout_orientation = isset( $layout['orientation'] ) ? $layout['orientation'] : 'horizontal'; @@ -88,39 +108,57 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support $layout['flexWrap'] : 'wrap'; - $style = "$selector {"; - $style .= 'display: flex;'; + $styles[ "$selector" ] = array( + 'display' => 'flex', + 'flex-wrap' => $flex_wrap, + ); + if ( $has_block_gap_support ) { - $gap_style = $gap_value ? $gap_value : 'var( --wp--style--block-gap, 0.5em )'; - $style .= "gap: $gap_style;"; + $gap_style = $gap_value ? $gap_value : 'var( --wp--style--block-gap, 0.5em )'; + $styles[ "$selector" ] = array( + 'gap' => $gap_style, + ); } else { - $style .= 'gap: 0.5em;'; + $styles[ "$selector" ] = array( + 'gap' => '0.5em', + ); } - $style .= "flex-wrap: $flex_wrap;"; + if ( 'horizontal' === $layout_orientation ) { - $style .= 'align-items: center;'; + $styles[ "$selector" ] = array( + 'align-items' => 'center', + ); /** * Add this style only if is not empty for backwards compatibility, * since we intend to convert blocks that had flex layout implemented * by custom css. */ if ( ! empty( $layout['justifyContent'] ) && array_key_exists( $layout['justifyContent'], $justify_content_options ) ) { - $style .= "justify-content: {$justify_content_options[ $layout['justifyContent'] ]};"; + $styles[ "$selector" ] = array( + 'justify-content' => $justify_content_options[ $layout['justifyContent'] ], + ); } } else { - $style .= 'flex-direction: column;'; + $styles[ "$selector" ] = array( + 'flex-direction' => 'column', + ); if ( ! empty( $layout['justifyContent'] ) && array_key_exists( $layout['justifyContent'], $justify_content_options ) ) { - $style .= "align-items: {$justify_content_options[ $layout['justifyContent'] ]};"; + $styles[ "$selector" ] = array( + 'align-items' => $justify_content_options[ $layout['justifyContent'] ], + ); } else { - $style .= 'align-items: flex-start;'; + $styles[ "$selector" ] = array( + 'align-items' => 'flex-start', + ); } } - $style .= '}'; - $style .= "$selector > * { margin: 0; }"; + $styles[ "$selector > *" ] = array( + 'margin' => '0', + ); } - return $style; + return $styles; } /** @@ -156,7 +194,8 @@ function gutenberg_render_layout_support_flag( $block_content, $block ) { // Regex for CSS value borrowed from `safecss_filter_attr`, and used here // because we only want to match against the value, not the CSS attribute. $gap_value = preg_match( '%[\\\(&=}]|/\*%', $gap_value ) ? null : $gap_value; - $style = gutenberg_get_layout_style( ".$class_name", $used_layout, $has_block_gap_support, $gap_value ); + $styles = gutenberg_get_layout_style( ".$class_name", $used_layout, $has_block_gap_support, $gap_value ); + WP_Style_Engine_Gutenberg::get_instance()->add_styles( $styles ); // This assumes the hook only applies to blocks with a single wrapper. // I think this is a reasonable limitation for that particular hook. $content = preg_replace( @@ -166,8 +205,6 @@ function gutenberg_render_layout_support_flag( $block_content, $block ) { 1 ); - WP_Style_Engine_Gutenberg::get_instance()->add_style( $class_name, $style ); - return $content; } diff --git a/lib/class-wp-style-engine.php b/lib/class-wp-style-engine.php index 7eac59ead4163..e47858c4a88c8 100644 --- a/lib/class-wp-style-engine.php +++ b/lib/class-wp-style-engine.php @@ -3,7 +3,6 @@ * WP_Style_Engine class * * @package Gutenberg - */ /** @@ -58,12 +57,56 @@ public static function get_instance() { return self::$instance; } - public function add_style( $key, $value ) { - $this->registered_styles[ $key ] = $value; + public function add_styles( $styles ) { + if ( ! is_array( $styles ) ) { + return array(); + } + + foreach ( $styles as $key => $value ) { + $value = is_array( $value ) ? $value : array(); + if ( isset( $this->registered_styles[ $key ] ) ) { + $this->registered_styles[ $key ] = array_merge( $this->registered_styles[ $key ], $value ); + } else { + $this->registered_styles[ $key ] = $value; + } + } + } + + protected function deduplicate_styles() { + $result = array(); + $unique_styles = array(); + array_walk( + $this->registered_styles, + function( $value, $key ) use ( &$result, &$unique_styles ) { + $stringified_value = json_encode( $value ); + if ( array_key_exists( $stringified_value, $unique_styles ) ) { + $new_key = $unique_styles[ $stringified_value ] . ",\n" . $key; + unset( $result[ $unique_styles[ $stringified_value ] ] ); + $unique_styles[ $stringified_value ] = $new_key; + $result[ $new_key ] = $value; + } else { + $unique_styles[ $stringified_value ] = $key; + $result[ $key ] = $value; + } + } + ); + return $result; } public function output_styles() { - $style = implode( "\n", $this->registered_styles ); - echo "\n"; + $deduped_styles = $this->deduplicate_styles(); + $callback = function( $css_selector, $css_ruleset ) { + $style = "{$css_selector} {\n"; + foreach ( $css_ruleset as $css_property => $css_value ) { + $style .= " {$css_property}: {$css_value};\n"; + } + $style .= "}\n"; + return $style; + }; + + $output = array_map( $callback, array_keys( $deduped_styles ), array_values( $deduped_styles ) ); + $output = implode( "\n", $output ); + + echo "\n"; } }