From 21d4ecf5fdbb3bf523a24224ac8bfe609805bc37 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/6] 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 59632f3e2bd169..0c94c87f7f414a 100644 --- a/lib/block-supports/layout.php +++ b/lib/block-supports/layout.php @@ -165,7 +165,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 00000000000000..7eac59ead41630 --- /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 b008f233700714..29a1116b42c8c8 100644 --- a/lib/load.php +++ b/lib/load.php @@ -120,6 +120,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 7301dee84ca8e1b4abd44ef91199e2b34e043ea1 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Fri, 25 Feb 2022 15:59:39 +1100 Subject: [PATCH 2/6] Try generating unique class names and de-duping styles for the layout support --- lib/block-supports/layout.php | 250 ++++++++++++++++++++++++++++------ lib/class-wp-style-engine.php | 38 +++++- 2 files changed, 247 insertions(+), 41 deletions(-) diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php index 0c94c87f7f414a..eba2b621517658 100644 --- a/lib/block-supports/layout.php +++ b/lib/block-supports/layout.php @@ -28,17 +28,18 @@ 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 boolean $has_block_gap_support Whether the theme has support for the block gap. * @param string $gap_value The block gap value to apply. * * @return string CSS style. */ -function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support = false, $gap_value = null ) { +function gutenberg_get_layout_style( $layout, $has_block_gap_support = false, $gap_value = null ) { $layout_type = isset( $layout['type'] ) ? $layout['type'] : 'default'; - $style = ''; + $style_engine = WP_Style_Engine_Gutenberg::get_instance(); + $class_names = array(); + if ( 'default' === $layout_type ) { $content_size = isset( $layout['contentSize'] ) ? $layout['contentSize'] : ''; $wide_size = isset( $layout['wideSize'] ) ? $layout['wideSize'] : ''; @@ -51,23 +52,118 @@ 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] ); + // Add universal styles for all default layouts. + // Add left align rule; + $class_names[] = $style_engine->add_style( + 'wp-layout-default', + array( + 'selector' => '.alignleft', + 'rules' => array( + 'float' => 'left', + 'margin-right' => '2em', + 'margin-left' => '0', + ), + ) + ); + + // Add right align rule: + $class_names[] = $style_engine->add_style( + 'wp-layout-default', + array( + 'selector' => '.alignright', + 'rules' => array( + 'float' => 'right', + 'margin-left' => '2em', + 'margin-right' => '0', + ), + ) + ); + 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; }"; + // Add value specific content size. + $class_names[] = $style_engine->add_style( + 'wp-layout-default-content-size', + array( + 'suffix' => $all_max_width_value, + 'selector' => '> :where(:not(.alignleft):not(.alignright))', + 'rules' => array( + 'max-width' => esc_html( $all_max_width_value ), + 'margin-left' => 'auto !important', + 'margin-right' => 'auto !important', + ) + ) + ); + + // Add value specific wide size. + $class_names[] = $style_engine->add_style( + 'wp-layout-default-wide-size', + array( + 'suffix' => $wide_max_width_value, + 'selector' => '> .alignwide', + 'rules' => array( + 'max-width' => esc_html( $wide_max_width_value ), + ) + ) + ); + + // Add universal full width. + $class_names[] = $style_engine->add_style( + 'wp-layout-default', + array( + 'selector' => '> .alignfull', + 'rules' => 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; }"; 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; }"; + if ( ! $gap_value ) { + $class_names[] = $style_engine->add_style( + 'wp-layout-default-global-gap', + array( + 'selector' => '> *', + 'rules' => array( + 'margin-top' => '0', + 'margin-bottom' => '0', + ), + ) + ); + $class_names[] = $style_engine->add_style( + 'wp-layout-default-global-gap', + array( + 'selector' => '> * + *', + 'rules' => array( + 'margin-top' => 'var( --wp--style--block-gap )', + 'margin-bottom' => '0', + ), + ) + ); + } else { + $class_names[] = $style_engine->add_style( + 'wp-layout-default-custom-gap', + array( + 'suffix' => $gap_value, + 'selector' => '> *', + 'rules' => array( + 'margin-top' => '0', + 'margin-bottom' => '0', + ), + ) + ); + $class_names[] = $style_engine->add_style( + 'wp-layout-default-custom-gap', + array( + 'suffix' => $gap_value, + 'selector' => '> * + *', + 'rules' => array( + 'margin-top' => $gap_value, + 'margin-bottom' => '0', + ), + ) + ); + } } } elseif ( 'flex' === $layout_type ) { $layout_orientation = isset( $layout['orientation'] ) ? $layout['orientation'] : 'horizontal'; @@ -87,39 +183,120 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support $layout['flexWrap'] : 'wrap'; - $style = "$selector {"; - $style .= 'display: flex;'; + $class_names[] = $style_engine->add_style( + 'wp-layout-flex', + array( + 'rules' => array( + 'display' => 'flex', + 'gap' => '0.5em', + ), + ) + ); + + $class_names[] = $style_engine->add_style( + 'wp-layout-flex', + array( + 'selector' => '> *', + 'rules' => array( + 'margin' => '0', + ), + ) + ); + + $class_names[] = $style_engine->add_style( + 'wp-layout-flex', + array( + 'suffix' => $flex_wrap, + 'rules' => array( + '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;"; - } else { - $style .= 'gap: 0.5em;'; + if ( ! $gap_value ) { + $class_names[] = $style_engine->add_style( + 'wp-layout-flex-global-gap', + array( + 'rules' => array( + 'gap' => 'var( --wp--style--block-gap, 0.5em )', + ), + ) + ); + } else { + $class_names[] = $style_engine->add_style( + 'wp-layout-flex-custom-gap', + array( + 'suffix' => $gap_value, + 'rules' => array( + 'gap' => $gap_value, + ), + ) + ); + } } - $style .= "flex-wrap: $flex_wrap;"; + if ( 'horizontal' === $layout_orientation ) { - $style .= 'align-items: center;'; + $class_names[] = $style_engine->add_style( + 'wp-layout-flex-orientation-horizontal', + array( + 'rules' => 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'] ]};"; + $class_names[] = $style_engine->add_style( + 'wp-layout-flex-orientation-horizontal', + array( + 'suffix' => $justify_content_options[ $layout['justifyContent'] ], + 'rules' => array( + 'justify-content' => $justify_content_options[ $layout['justifyContent'] ], + ), + ) + ); } } else { - $style .= 'flex-direction: column;'; + $class_names[] = $style_engine->add_style( + 'wp-layout-flex-orientation-vertical', + array( + 'rules' => array( + 'flex-direction' => 'column', + ), + ) + ); + if ( ! empty( $layout['justifyContent'] ) && array_key_exists( $layout['justifyContent'], $justify_content_options ) ) { - $style .= "align-items: {$justify_content_options[ $layout['justifyContent'] ]};"; + $class_names[] = $style_engine->add_style( + 'wp-layout-flex-orientation-vertical', + array( + 'suffix' => $justify_content_options[ $layout['justifyContent'] ], + 'rules' => array( + 'align-items' => $justify_content_options[ $layout['justifyContent'] ], + ), + ) + ); } else { - $style .= 'align-items: flex-start;'; + $class_names[] = $style_engine->add_style( + 'wp-layout-flex-orientation-vertical', + array( + 'suffix' => 'flex-start', + 'rules' => array( + 'align-items' => 'flex-start', + ), + ) + ); } } - $style .= '}'; - - $style .= "$selector > * { margin: 0; }"; } - return $style; + return array_unique( $class_names ); } /** @@ -149,24 +326,21 @@ function gutenberg_render_layout_support_flag( $block_content, $block ) { $used_layout = $default_layout; } - $class_name = wp_unique_id( 'wp-container-' ); $gap_value = _wp_array_get( $block, array( 'attrs', 'style', 'spacing', 'blockGap' ) ); // Skip if gap value contains unsupported characters. // 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 ); + $gap_value = preg_match( '%[\\\(&=}]|/\*%', $gap_value ) ? null : $gap_value; + $class_names = gutenberg_get_layout_style( $used_layout, $has_block_gap_support, $gap_value ); // 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( '/' . preg_quote( 'class="', '/' ) . '/', - 'class="' . esc_attr( $class_name ) . ' ', + 'class="' . esc_attr( implode( ' ', $class_names ) ) . ' ', $block_content, 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 7eac59ead41630..c516fb27dfcd35 100644 --- a/lib/class-wp-style-engine.php +++ b/lib/class-wp-style-engine.php @@ -58,12 +58,44 @@ public static function get_instance() { return self::$instance; } - public function add_style( $key, $value ) { - $this->registered_styles[ $key ] = $value; + /** + * Assemble the style rule from a list of rules, and store based on a key + * generated from the class name, the selector, and any values used as a suffix. + * + * @param string $key A class name used to construct a key. + * @param array $options An array of options, rules, and selector for constructing the rules. + * + * @return string The class name for the added style. + */ + public function add_style( $key, $options ) { + $class = ! empty( $options['suffix'] ) ? $key . '-' . sanitize_title( $options['suffix'] ) : $key; + $selector = ! empty( $options['selector'] ) ? ' ' . trim( $options['selector'] ) : ''; + $rules = ! empty( $options['rules'] ) ? $options['rules'] : array(); + $prefix = ! empty( $options['prefix'] ) ? $options['prefix'] : '.'; + + if ( ! $class ) { + return; + } + + $style = "{$prefix}{$class}{$selector} {\n"; + + if ( is_string( $rules ) ) { + $style .= ' '; + $style .= $rules; + } else { + foreach( $rules as $rule => $value ) { + $style .= " {$rule}: {$value};\n"; + } + } + $style .= "}\n"; + + $this->registered_styles[ $class . $selector ] = $style; + + return $class; } public function output_styles() { $style = implode( "\n", $this->registered_styles ); - echo "\n"; + echo "\n"; } } From 842b83ddccd62bbb363d738fc477e0a782fbe049 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Wed, 2 Mar 2022 12:21:21 +1100 Subject: [PATCH 3/6] Split out layout style generation by type --- lib/block-supports/layout.php | 435 ++++++++++++++++++---------------- 1 file changed, 235 insertions(+), 200 deletions(-) diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php index eba2b621517658..7d99a07c514759 100644 --- a/lib/block-supports/layout.php +++ b/lib/block-supports/layout.php @@ -26,276 +26,311 @@ function gutenberg_register_layout_support( $block_type ) { } /** - * Generates the CSS corresponding to the provided layout. + * Generates styles for the default flow layout type. * - * @param array $layout Layout object. The one that is passed has already checked the existence of default block layout. + * @param array $layout Layout object. * @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 string $gap_value The block gap value to apply. * - * @return string CSS style. + * @return array An array of class names corresponding to the generated layout CSS. */ -function gutenberg_get_layout_style( $layout, $has_block_gap_support = false, $gap_value = null ) { - $layout_type = isset( $layout['type'] ) ? $layout['type'] : 'default'; - +function gutenberg_generate_layout_style_flow( $layout, $has_block_gap_support = false, $gap_value = null ) { $style_engine = WP_Style_Engine_Gutenberg::get_instance(); - $class_names = array(); - - if ( 'default' === $layout_type ) { - $content_size = isset( $layout['contentSize'] ) ? $layout['contentSize'] : ''; - $wide_size = isset( $layout['wideSize'] ) ? $layout['wideSize'] : ''; + $class_names = array(); + + $content_size = isset( $layout['contentSize'] ) ? $layout['contentSize'] : ''; + $wide_size = isset( $layout['wideSize'] ) ? $layout['wideSize'] : ''; + + $all_max_width_value = $content_size ? $content_size : $wide_size; + $wide_max_width_value = $wide_size ? $wide_size : $content_size; + + // Make sure there is a single CSS rule, and all tags are stripped for security. + // TODO: Use `safecss_filter_attr` instead - once https://core.trac.wordpress.org/ticket/46197 is patched. + $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] ); + + // Add universal styles for all default layouts. + // Add left align rule; + $class_names[] = $style_engine->add_style( + 'wp-layout-default', + array( + 'selector' => '.alignleft', + 'rules' => array( + 'float' => 'left', + 'margin-right' => '2em', + 'margin-left' => '0', + ), + ) + ); - $all_max_width_value = $content_size ? $content_size : $wide_size; - $wide_max_width_value = $wide_size ? $wide_size : $content_size; + // Add right align rule: + $class_names[] = $style_engine->add_style( + 'wp-layout-default', + array( + 'selector' => '.alignright', + 'rules' => array( + 'float' => 'right', + 'margin-left' => '2em', + 'margin-right' => '0', + ), + ) + ); - // Make sure there is a single CSS rule, and all tags are stripped for security. - // TODO: Use `safecss_filter_attr` instead - once https://core.trac.wordpress.org/ticket/46197 is patched. - $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] ); + if ( $content_size || $wide_size ) { + // Add value specific content size. + $class_names[] = $style_engine->add_style( + 'wp-layout-default-content-size', + array( + 'suffix' => $all_max_width_value, + 'selector' => '> :where(:not(.alignleft):not(.alignright))', + 'rules' => array( + 'max-width' => esc_html( $all_max_width_value ), + 'margin-left' => 'auto !important', + 'margin-right' => 'auto !important', + ) + ) + ); - // Add universal styles for all default layouts. - // Add left align rule; + // Add value specific wide size. $class_names[] = $style_engine->add_style( - 'wp-layout-default', + 'wp-layout-default-wide-size', array( - 'selector' => '.alignleft', - 'rules' => array( - 'float' => 'left', - 'margin-right' => '2em', - 'margin-left' => '0', - ), + 'suffix' => $wide_max_width_value, + 'selector' => '> .alignwide', + 'rules' => array( + 'max-width' => esc_html( $wide_max_width_value ), + ) ) ); - // Add right align rule: + // Add universal full width. $class_names[] = $style_engine->add_style( 'wp-layout-default', array( - 'selector' => '.alignright', + 'selector' => '> .alignfull', 'rules' => array( - 'float' => 'right', - 'margin-left' => '2em', - 'margin-right' => '0', - ), + 'max-width' => 'none', + ) ) ); + } - if ( $content_size || $wide_size ) { - // Add value specific content size. + if ( $has_block_gap_support ) { + if ( ! $gap_value ) { $class_names[] = $style_engine->add_style( - 'wp-layout-default-content-size', + 'wp-layout-default-global-gap', array( - 'suffix' => $all_max_width_value, - 'selector' => '> :where(:not(.alignleft):not(.alignright))', - 'rules' => array( - 'max-width' => esc_html( $all_max_width_value ), - 'margin-left' => 'auto !important', - 'margin-right' => 'auto !important', - ) + 'selector' => '> *', + 'rules' => array( + 'margin-top' => '0', + 'margin-bottom' => '0', + ), ) ); - - // Add value specific wide size. $class_names[] = $style_engine->add_style( - 'wp-layout-default-wide-size', + 'wp-layout-default-global-gap', array( - 'suffix' => $wide_max_width_value, - 'selector' => '> .alignwide', - 'rules' => array( - 'max-width' => esc_html( $wide_max_width_value ), - ) + 'selector' => '> * + *', + 'rules' => array( + 'margin-top' => 'var( --wp--style--block-gap )', + 'margin-bottom' => '0', + ), + ) + ); + } else { + $class_names[] = $style_engine->add_style( + 'wp-layout-default-custom-gap', + array( + 'suffix' => $gap_value, + 'selector' => '> *', + 'rules' => array( + 'margin-top' => '0', + 'margin-bottom' => '0', + ), ) ); - - // Add universal full width. $class_names[] = $style_engine->add_style( - 'wp-layout-default', + 'wp-layout-default-custom-gap', array( - 'selector' => '> .alignfull', + 'suffix' => $gap_value, + 'selector' => '> * + *', 'rules' => array( - 'max-width' => 'none', - ) + 'margin-top' => $gap_value, + 'margin-bottom' => '0', + ), ) ); } + } - if ( $has_block_gap_support ) { - if ( ! $gap_value ) { - $class_names[] = $style_engine->add_style( - 'wp-layout-default-global-gap', - array( - 'selector' => '> *', - 'rules' => array( - 'margin-top' => '0', - 'margin-bottom' => '0', - ), - ) - ); - $class_names[] = $style_engine->add_style( - 'wp-layout-default-global-gap', - array( - 'selector' => '> * + *', - 'rules' => array( - 'margin-top' => 'var( --wp--style--block-gap )', - 'margin-bottom' => '0', - ), - ) - ); - } else { - $class_names[] = $style_engine->add_style( - 'wp-layout-default-custom-gap', - array( - 'suffix' => $gap_value, - 'selector' => '> *', - 'rules' => array( - 'margin-top' => '0', - 'margin-bottom' => '0', - ), - ) - ); - $class_names[] = $style_engine->add_style( - 'wp-layout-default-custom-gap', - array( - 'suffix' => $gap_value, - 'selector' => '> * + *', - 'rules' => array( - 'margin-top' => $gap_value, - 'margin-bottom' => '0', - ), - ) - ); - } - } - } elseif ( 'flex' === $layout_type ) { - $layout_orientation = isset( $layout['orientation'] ) ? $layout['orientation'] : 'horizontal'; + return $class_names; +} - $justify_content_options = array( - 'left' => 'flex-start', - 'right' => 'flex-end', - 'center' => 'center', - ); +/** + * Generates styles for the flex layout type. + * + * @param array $layout Layout object. + * @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. + * + * @return array An array of class names corresponding to the generated layout CSS. + */ +function gutenberg_generate_layout_style_flex( $layout, $has_block_gap_support = false, $gap_value = null ) { + $style_engine = WP_Style_Engine_Gutenberg::get_instance(); + $class_names = array(); + $layout_orientation = isset( $layout['orientation'] ) ? $layout['orientation'] : 'horizontal'; + + $justify_content_options = array( + 'left' => 'flex-start', + 'right' => 'flex-end', + 'center' => 'center', + ); - if ( 'horizontal' === $layout_orientation ) { - $justify_content_options += array( 'space-between' => 'space-between' ); - } + if ( 'horizontal' === $layout_orientation ) { + $justify_content_options += array( 'space-between' => 'space-between' ); + } - $flex_wrap_options = array( 'wrap', 'nowrap' ); - $flex_wrap = ! empty( $layout['flexWrap'] ) && in_array( $layout['flexWrap'], $flex_wrap_options, true ) ? - $layout['flexWrap'] : - 'wrap'; + $flex_wrap_options = array( 'wrap', 'nowrap' ); + $flex_wrap = ! empty( $layout['flexWrap'] ) && in_array( $layout['flexWrap'], $flex_wrap_options, true ) ? + $layout['flexWrap'] : + 'wrap'; + + $class_names[] = $style_engine->add_style( + 'wp-layout-flex', + array( + 'rules' => array( + 'display' => 'flex', + 'gap' => '0.5em', + ), + ) + ); - $class_names[] = $style_engine->add_style( - 'wp-layout-flex', - array( - 'rules' => array( - 'display' => 'flex', - 'gap' => '0.5em', - ), - ) - ); + $class_names[] = $style_engine->add_style( + 'wp-layout-flex', + array( + 'selector' => '> *', + 'rules' => array( + 'margin' => '0', + ), + ) + ); + + $class_names[] = $style_engine->add_style( + 'wp-layout-flex', + array( + 'suffix' => $flex_wrap, + 'rules' => array( + 'flex-wrap' => $flex_wrap, + ), + ) + ); + + if ( $has_block_gap_support ) { + if ( ! $gap_value ) { + $class_names[] = $style_engine->add_style( + 'wp-layout-flex-global-gap', + array( + 'rules' => array( + 'gap' => 'var( --wp--style--block-gap, 0.5em )', + ), + ) + ); + } else { + $class_names[] = $style_engine->add_style( + 'wp-layout-flex-custom-gap', + array( + 'suffix' => $gap_value, + 'rules' => array( + 'gap' => $gap_value, + ), + ) + ); + } + } + if ( 'horizontal' === $layout_orientation ) { $class_names[] = $style_engine->add_style( - 'wp-layout-flex', + 'wp-layout-flex-orientation-horizontal', array( - 'selector' => '> *', - 'rules' => array( - 'margin' => '0', + 'rules' => 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 ) ) { + $class_names[] = $style_engine->add_style( + 'wp-layout-flex-orientation-horizontal', + array( + 'suffix' => $justify_content_options[ $layout['justifyContent'] ], + 'rules' => array( + 'justify-content' => $justify_content_options[ $layout['justifyContent'] ], + ), + ) + ); + } + } else { $class_names[] = $style_engine->add_style( - 'wp-layout-flex', + 'wp-layout-flex-orientation-vertical', array( - 'suffix' => $flex_wrap, 'rules' => array( - 'flex-wrap' => $flex_wrap, + 'flex-direction' => 'column', ), ) ); - if ( $has_block_gap_support ) { - if ( ! $gap_value ) { - $class_names[] = $style_engine->add_style( - 'wp-layout-flex-global-gap', - array( - 'rules' => array( - 'gap' => 'var( --wp--style--block-gap, 0.5em )', - ), - ) - ); - } else { - $class_names[] = $style_engine->add_style( - 'wp-layout-flex-custom-gap', - array( - 'suffix' => $gap_value, - 'rules' => array( - 'gap' => $gap_value, - ), - ) - ); - } - } - - if ( 'horizontal' === $layout_orientation ) { + if ( ! empty( $layout['justifyContent'] ) && array_key_exists( $layout['justifyContent'], $justify_content_options ) ) { $class_names[] = $style_engine->add_style( - 'wp-layout-flex-orientation-horizontal', + 'wp-layout-flex-orientation-vertical', array( + 'suffix' => $justify_content_options[ $layout['justifyContent'] ], 'rules' => array( - 'align-items' => 'center', + 'align-items' => $justify_content_options[ $layout['justifyContent'] ], ), ) ); - - /** - * 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 ) ) { - $class_names[] = $style_engine->add_style( - 'wp-layout-flex-orientation-horizontal', - array( - 'suffix' => $justify_content_options[ $layout['justifyContent'] ], - 'rules' => array( - 'justify-content' => $justify_content_options[ $layout['justifyContent'] ], - ), - ) - ); - } } else { $class_names[] = $style_engine->add_style( 'wp-layout-flex-orientation-vertical', array( + 'suffix' => 'flex-start', 'rules' => array( - 'flex-direction' => 'column', + 'align-items' => 'flex-start', ), ) ); - - if ( ! empty( $layout['justifyContent'] ) && array_key_exists( $layout['justifyContent'], $justify_content_options ) ) { - $class_names[] = $style_engine->add_style( - 'wp-layout-flex-orientation-vertical', - array( - 'suffix' => $justify_content_options[ $layout['justifyContent'] ], - 'rules' => array( - 'align-items' => $justify_content_options[ $layout['justifyContent'] ], - ), - ) - ); - } else { - $class_names[] = $style_engine->add_style( - 'wp-layout-flex-orientation-vertical', - array( - 'suffix' => 'flex-start', - 'rules' => array( - 'align-items' => 'flex-start', - ), - ) - ); - } } } + return $class_names; +} + +/** + * Generates the CSS corresponding to the provided layout. + * + * @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. + * + * @return array An array of class names corresponding to the generated layout CSS. + */ +function gutenberg_get_layout_style( $layout, $has_block_gap_support = false, $gap_value = null ) { + $layout_type = isset( $layout['type'] ) ? $layout['type'] : 'default'; + + $style_engine = WP_Style_Engine_Gutenberg::get_instance(); + $class_names = array(); + + if ( 'default' === $layout_type ) { + $class_names = gutenberg_generate_layout_style_flow( $layout, $has_block_gap_support, $gap_value ); + } elseif ( 'flex' === $layout_type ) { + $class_names = gutenberg_generate_layout_style_flex( $layout, $has_block_gap_support, $gap_value ); + } + return array_unique( $class_names ); } From 66aabccede8995ffbb1a7424bdf409799f3c6c49 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Wed, 2 Mar 2022 12:29:35 +1100 Subject: [PATCH 4/6] Fix linting issues --- lib/block-supports/layout.php | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php index 7d99a07c514759..d57d4744477775 100644 --- a/lib/block-supports/layout.php +++ b/lib/block-supports/layout.php @@ -50,26 +50,26 @@ function gutenberg_generate_layout_style_flow( $layout, $has_block_gap_support = $wide_max_width_value = wp_strip_all_tags( explode( ';', $wide_max_width_value )[0] ); // Add universal styles for all default layouts. - // Add left align rule; + // Add left align rule. $class_names[] = $style_engine->add_style( 'wp-layout-default', array( 'selector' => '.alignleft', 'rules' => array( - 'float' => 'left', + 'float' => 'left', 'margin-right' => '2em', 'margin-left' => '0', ), ) ); - // Add right align rule: + // Add right align rule. $class_names[] = $style_engine->add_style( 'wp-layout-default', array( 'selector' => '.alignright', 'rules' => array( - 'float' => 'right', + 'float' => 'right', 'margin-left' => '2em', 'margin-right' => '0', ), @@ -83,11 +83,11 @@ function gutenberg_generate_layout_style_flow( $layout, $has_block_gap_support = array( 'suffix' => $all_max_width_value, 'selector' => '> :where(:not(.alignleft):not(.alignright))', - 'rules' => array( + 'rules' => array( 'max-width' => esc_html( $all_max_width_value ), 'margin-left' => 'auto !important', 'margin-right' => 'auto !important', - ) + ), ) ); @@ -97,9 +97,9 @@ function gutenberg_generate_layout_style_flow( $layout, $has_block_gap_support = array( 'suffix' => $wide_max_width_value, 'selector' => '> .alignwide', - 'rules' => array( + 'rules' => array( 'max-width' => esc_html( $wide_max_width_value ), - ) + ), ) ); @@ -110,7 +110,7 @@ function gutenberg_generate_layout_style_flow( $layout, $has_block_gap_support = 'selector' => '> .alignfull', 'rules' => array( 'max-width' => 'none', - ) + ), ) ); } @@ -230,7 +230,7 @@ function gutenberg_generate_layout_style_flex( $layout, $has_block_gap_support = $class_names[] = $style_engine->add_style( 'wp-layout-flex-global-gap', array( - 'rules' => array( + 'rules' => array( 'gap' => 'var( --wp--style--block-gap, 0.5em )', ), ) @@ -252,8 +252,8 @@ function gutenberg_generate_layout_style_flex( $layout, $has_block_gap_support = $class_names[] = $style_engine->add_style( 'wp-layout-flex-orientation-horizontal', array( - 'rules' => array( - 'align-items' => 'center', + 'rules' => array( + 'align-items' => 'center', ), ) ); @@ -278,7 +278,7 @@ function gutenberg_generate_layout_style_flex( $layout, $has_block_gap_support = $class_names[] = $style_engine->add_style( 'wp-layout-flex-orientation-vertical', array( - 'rules' => array( + 'rules' => array( 'flex-direction' => 'column', ), ) @@ -321,8 +321,6 @@ function gutenberg_generate_layout_style_flex( $layout, $has_block_gap_support = */ function gutenberg_get_layout_style( $layout, $has_block_gap_support = false, $gap_value = null ) { $layout_type = isset( $layout['type'] ) ? $layout['type'] : 'default'; - - $style_engine = WP_Style_Engine_Gutenberg::get_instance(); $class_names = array(); if ( 'default' === $layout_type ) { @@ -361,7 +359,7 @@ function gutenberg_render_layout_support_flag( $block_content, $block ) { $used_layout = $default_layout; } - $gap_value = _wp_array_get( $block, array( 'attrs', 'style', 'spacing', 'blockGap' ) ); + $gap_value = _wp_array_get( $block, array( 'attrs', 'style', 'spacing', 'blockGap' ) ); // Skip if gap value contains unsupported characters. // 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. From 5293512d51d2741e69fc08b58e730741f2451dd6 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Wed, 2 Mar 2022 12:32:50 +1100 Subject: [PATCH 5/6] Fix linting issues with style engine class --- ...le-engine.php => class-wp-style-engine-gutenberg.php} | 9 +++++++-- lib/load.php | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) rename lib/{class-wp-style-engine.php => class-wp-style-engine-gutenberg.php} (92%) diff --git a/lib/class-wp-style-engine.php b/lib/class-wp-style-engine-gutenberg.php similarity index 92% rename from lib/class-wp-style-engine.php rename to lib/class-wp-style-engine-gutenberg.php index c516fb27dfcd35..7def187b443e0d 100644 --- a/lib/class-wp-style-engine.php +++ b/lib/class-wp-style-engine-gutenberg.php @@ -3,7 +3,6 @@ * WP_Style_Engine class * * @package Gutenberg - */ /** @@ -31,6 +30,9 @@ class WP_Style_Engine_Gutenberg { */ private static $instance = null; + /** + * Register action for outputting styles when the class is constructed. + */ public function __construct() { // Borrows the logic from `gutenberg_enqueue_block_support_styles`. $action_hook_name = 'wp_footer'; @@ -83,7 +85,7 @@ public function add_style( $key, $options ) { $style .= ' '; $style .= $rules; } else { - foreach( $rules as $rule => $value ) { + foreach ( $rules as $rule => $value ) { $style .= " {$rule}: {$value};\n"; } } @@ -94,6 +96,9 @@ public function add_style( $key, $options ) { return $class; } + /** + * Render registered styles for final output. + */ public function output_styles() { $style = implode( "\n", $this->registered_styles ); echo "\n"; diff --git a/lib/load.php b/lib/load.php index 29a1116b42c8c8..1697629b9a7120 100644 --- a/lib/load.php +++ b/lib/load.php @@ -123,7 +123,7 @@ function gutenberg_is_experiment_enabled( $name ) { // 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__ . '/class-wp-style-engine-gutenberg.php'; require __DIR__ . '/block-supports/elements.php'; require __DIR__ . '/block-supports/colors.php'; From c15b9cb5d037a765fe3242f2da4a4a5fcde079d0 Mon Sep 17 00:00:00 2001 From: ramonjd Date: Wed, 2 Mar 2022 14:25:41 +1100 Subject: [PATCH 6/6] Renaming 'default' to 'flow' to make it more meaningful on the frontend Storing styles as selector => rules so we can experiment with operations such as updating styles, retrieving classnames from registered styles after registrations and other fun. --- lib/block-supports/layout.php | 22 +++++++-------- lib/class-wp-style-engine-gutenberg.php | 36 ++++++++++++------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php index d57d4744477775..cfb5fbe36c5f8e 100644 --- a/lib/block-supports/layout.php +++ b/lib/block-supports/layout.php @@ -52,7 +52,7 @@ function gutenberg_generate_layout_style_flow( $layout, $has_block_gap_support = // Add universal styles for all default layouts. // Add left align rule. $class_names[] = $style_engine->add_style( - 'wp-layout-default', + 'wp-layout-flow', array( 'selector' => '.alignleft', 'rules' => array( @@ -65,7 +65,7 @@ function gutenberg_generate_layout_style_flow( $layout, $has_block_gap_support = // Add right align rule. $class_names[] = $style_engine->add_style( - 'wp-layout-default', + 'wp-layout-flow', array( 'selector' => '.alignright', 'rules' => array( @@ -79,7 +79,7 @@ function gutenberg_generate_layout_style_flow( $layout, $has_block_gap_support = if ( $content_size || $wide_size ) { // Add value specific content size. $class_names[] = $style_engine->add_style( - 'wp-layout-default-content-size', + 'wp-layout-flow-content-size', array( 'suffix' => $all_max_width_value, 'selector' => '> :where(:not(.alignleft):not(.alignright))', @@ -93,7 +93,7 @@ function gutenberg_generate_layout_style_flow( $layout, $has_block_gap_support = // Add value specific wide size. $class_names[] = $style_engine->add_style( - 'wp-layout-default-wide-size', + 'wp-layout-flow-wide-size', array( 'suffix' => $wide_max_width_value, 'selector' => '> .alignwide', @@ -105,7 +105,7 @@ function gutenberg_generate_layout_style_flow( $layout, $has_block_gap_support = // Add universal full width. $class_names[] = $style_engine->add_style( - 'wp-layout-default', + 'wp-layout-flow', array( 'selector' => '> .alignfull', 'rules' => array( @@ -118,7 +118,7 @@ function gutenberg_generate_layout_style_flow( $layout, $has_block_gap_support = if ( $has_block_gap_support ) { if ( ! $gap_value ) { $class_names[] = $style_engine->add_style( - 'wp-layout-default-global-gap', + 'wp-layout-flow-global-gap', array( 'selector' => '> *', 'rules' => array( @@ -128,7 +128,7 @@ function gutenberg_generate_layout_style_flow( $layout, $has_block_gap_support = ) ); $class_names[] = $style_engine->add_style( - 'wp-layout-default-global-gap', + 'wp-layout-flow-global-gap', array( 'selector' => '> * + *', 'rules' => array( @@ -139,7 +139,7 @@ function gutenberg_generate_layout_style_flow( $layout, $has_block_gap_support = ); } else { $class_names[] = $style_engine->add_style( - 'wp-layout-default-custom-gap', + 'wp-layout-flow-custom-gap', array( 'suffix' => $gap_value, 'selector' => '> *', @@ -150,7 +150,7 @@ function gutenberg_generate_layout_style_flow( $layout, $has_block_gap_support = ) ); $class_names[] = $style_engine->add_style( - 'wp-layout-default-custom-gap', + 'wp-layout-flow-custom-gap', array( 'suffix' => $gap_value, 'selector' => '> * + *', @@ -320,10 +320,10 @@ function gutenberg_generate_layout_style_flex( $layout, $has_block_gap_support = * @return array An array of class names corresponding to the generated layout CSS. */ function gutenberg_get_layout_style( $layout, $has_block_gap_support = false, $gap_value = null ) { - $layout_type = isset( $layout['type'] ) ? $layout['type'] : 'default'; + $layout_type = isset( $layout['type'] ) ? $layout['type'] : 'flow'; $class_names = array(); - if ( 'default' === $layout_type ) { + if ( 'flow' === $layout_type ) { $class_names = gutenberg_generate_layout_style_flow( $layout, $has_block_gap_support, $gap_value ); } elseif ( 'flex' === $layout_type ) { $class_names = gutenberg_generate_layout_style_flex( $layout, $has_block_gap_support, $gap_value ); diff --git a/lib/class-wp-style-engine-gutenberg.php b/lib/class-wp-style-engine-gutenberg.php index 7def187b443e0d..d970727943112f 100644 --- a/lib/class-wp-style-engine-gutenberg.php +++ b/lib/class-wp-style-engine-gutenberg.php @@ -61,8 +61,7 @@ public static function get_instance() { } /** - * Assemble the style rule from a list of rules, and store based on a key - * generated from the class name, the selector, and any values used as a suffix. + * Stores style rules for a given CSS selector (the key) and returns an associated classname. * * @param string $key A class name used to construct a key. * @param array $options An array of options, rules, and selector for constructing the rules. @@ -79,28 +78,29 @@ public function add_style( $key, $options ) { return; } - $style = "{$prefix}{$class}{$selector} {\n"; - - if ( is_string( $rules ) ) { - $style .= ' '; - $style .= $rules; - } else { - foreach ( $rules as $rule => $value ) { - $style .= " {$rule}: {$value};\n"; - } - } - $style .= "}\n"; - - $this->registered_styles[ $class . $selector ] = $style; + $this->registered_styles[ $prefix . $class . $selector ] = $rules; return $class; } /** - * Render registered styles for final output. + * Render registered styles as key { ...rules } for final output. */ public function output_styles() { - $style = implode( "\n", $this->registered_styles ); - echo "\n"; + $output = ''; + foreach ( $this->registered_styles as $selector => $rules ) { + $output .= "{$selector} {\n"; + + if ( is_string( $rules ) ) { + $output .= ' '; + $output .= $rules; + } else { + foreach ( $rules as $rule => $value ) { + $output .= " {$rule}: {$value};\n"; + } + } + $output .= "}\n"; + } + echo "\n"; } }