diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php index e3bb3acfce296..f69ae13681ffe 100644 --- a/lib/block-supports/layout.php +++ b/lib/block-supports/layout.php @@ -39,9 +39,8 @@ function gutenberg_register_layout_support( $block_type ) { * @return string CSS style. */ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support = false, $gap_value = null, $should_skip_gap_serialization = false, $fallback_gap_value = '0.5em', $block_spacing = null ) { - $layout_type = isset( $layout['type'] ) ? $layout['type'] : 'default'; - - $style = ''; + $layout_type = isset( $layout['type'] ) ? $layout['type'] : 'default'; + $layout_styles = array(); if ( 'default' === $layout_type ) { $content_size = isset( $layout['contentSize'] ) ? $layout['contentSize'] : ''; $wide_size = isset( $layout['wideSize'] ) ? $layout['wideSize'] : ''; @@ -55,14 +54,14 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support $wide_max_width_value = wp_strip_all_tags( explode( ';', $wide_max_width_value )[0] ); if ( $content_size || $wide_size ) { - $style = "$selector > :where(:not(.alignleft):not(.alignright):not(.alignfull)) {"; - $style .= 'max-width: ' . esc_html( $all_max_width_value ) . ';'; - $style .= 'margin-left: auto !important;'; - $style .= 'margin-right: auto !important;'; - $style .= '}'; + $layout_styles[ "$selector > :where(:not(.alignleft):not(.alignright):not(.alignfull))" ] = array( + 'max-width' => $all_max_width_value, + 'margin-left' => 'auto !important', + 'margin-right' => 'auto !important', + ); - $style .= "$selector > .alignwide { max-width: " . esc_html( $wide_max_width_value ) . ';}'; - $style .= "$selector .alignfull { max-width: none; }"; + $layout_styles[ "$selector > .alignwide" ] = array( 'max-width' => $wide_max_width_value ); + $layout_styles[ "$selector .alignfull" ] = array( 'max-width' => 'none' ); if ( isset( $block_spacing ) ) { $block_spacing_values = gutenberg_style_engine_get_styles( @@ -74,12 +73,12 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support // Handle negative margins for alignfull children of blocks with custom padding set. // They're added separately because padding might only be set on one side. if ( isset( $block_spacing_values['declarations']['padding-right'] ) ) { - $padding_right = $block_spacing_values['declarations']['padding-right']; - $style .= "$selector > .alignfull { margin-right:calc($padding_right * -1); }"; + $padding_right = $block_spacing_values['declarations']['padding-right']; + $layout_styles[ "$selector > .alignfull" ] = array( 'margin-right' => "calc($padding_right * -1)" ); } if ( isset( $block_spacing_values['declarations']['padding-left'] ) ) { - $padding_left = $block_spacing_values['declarations']['padding-left']; - $style .= "$selector > .alignfull { margin-left: calc($padding_left * -1); }"; + $padding_left = $block_spacing_values['declarations']['padding-left']; + $layout_styles[ "$selector > .alignfull" ] = array( 'margin-left' => "calc($padding_left * -1)" ); } } } @@ -89,8 +88,16 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support $gap_value = isset( $gap_value['top'] ) ? $gap_value['top'] : null; } if ( $gap_value && ! $should_skip_gap_serialization ) { - $style .= "$selector > * { margin-block-start: 0; margin-block-end: 0; }"; - $style .= "$selector > * + * { margin-block-start: $gap_value; margin-block-end: 0; }"; + $layout_styles[ "$selector > *" ] = + array( + 'margin-block-start' => '0', + 'margin-block-end' => '0', + ); + $layout_styles[ "$selector > * + *" ] = + array( + 'margin-block-start' => $gap_value, + 'margin-block-end' => '0', + ); } } } elseif ( 'flex' === $layout_type ) { @@ -113,7 +120,7 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support } if ( ! empty( $layout['flexWrap'] ) && 'nowrap' === $layout['flexWrap'] ) { - $style .= "$selector { flex-wrap: nowrap; }"; + $layout_styles[ $selector ] = array( 'flex-wrap' => 'nowrap' ); } if ( $has_block_gap_support ) { @@ -123,9 +130,7 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support $gap_value = $gap_row === $gap_column ? $gap_row : $gap_row . ' ' . $gap_column; } if ( $gap_value && ! $should_skip_gap_serialization ) { - $style .= "$selector {"; - $style .= "gap: $gap_value;"; - $style .= '}'; + $layout_styles[ $selector ] = array( 'gap' => $gap_value ); } } @@ -136,29 +141,29 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support * by custom css. */ if ( ! empty( $layout['justifyContent'] ) && array_key_exists( $layout['justifyContent'], $justify_content_options ) ) { - $style .= "$selector {"; - $style .= "justify-content: {$justify_content_options[ $layout['justifyContent'] ]};"; - $style .= '}'; + $layout_styles[ $selector ] = array( 'justify-content' => $justify_content_options[ $layout['justifyContent'] ] ); } if ( ! empty( $layout['verticalAlignment'] ) && array_key_exists( $layout['verticalAlignment'], $vertical_alignment_options ) ) { - $style .= "$selector {"; - $style .= "align-items: {$vertical_alignment_options[ $layout['verticalAlignment'] ]};"; - $style .= '}'; + $layout_styles[ $selector ] = array( 'align-items' => $vertical_alignment_options[ $layout['verticalAlignment'] ] ); } } else { - $style .= "$selector {"; - $style .= 'flex-direction: column;'; + $layout_styles[ $selector ] = array( + 'flex-direction' => 'column', + 'align-items' => 'flex-start', + ); if ( ! empty( $layout['justifyContent'] ) && array_key_exists( $layout['justifyContent'], $justify_content_options ) ) { - $style .= "align-items: {$justify_content_options[ $layout['justifyContent'] ]};"; + $layout_styles[ $selector ] = array( 'align-items' => $justify_content_options[ $layout['justifyContent'] ] ); } else { - $style .= 'align-items: flex-start;'; + $layout_styles[ $selector ] = array( 'align-items' => 'flex-start' ); } - $style .= '}'; } } - return $style; + if ( ! empty( $layout_styles ) ) { + gutenberg_style_engine_add_to_store( 'layout-block-supports', $layout_styles ); + return gutenberg_style_engine_get_stylesheet( 'layout-block-supports' ); + } } /** @@ -249,7 +254,6 @@ function gutenberg_render_layout_support_flag( $block_content, $block ) { // Only add container class and enqueue block support styles if unique styles were generated. if ( ! empty( $style ) ) { $class_names[] = $container_class; - wp_enqueue_block_support_styles( $style ); } // This assumes the hook only applies to blocks with a single wrapper. diff --git a/packages/style-engine/class-wp-style-engine.php b/packages/style-engine/class-wp-style-engine.php index 684ca1029dfaf..de3de9e20805e 100644 --- a/packages/style-engine/class-wp-style-engine.php +++ b/packages/style-engine/class-wp-style-engine.php @@ -36,7 +36,8 @@ class WP_Style_Engine { * @var array */ private static $stores = array( - 'block-supports' => null, + 'layout-block-supports' => null, + 'block-supports' => null, ); /** @@ -320,16 +321,29 @@ public static function get_instance() { * @param array $css_declarations An array of parsed CSS property => CSS value pairs. * @param string $store_key A valid key corresponding to an existing store in static::$stores. * - * @return string A compiled CSS string. + * @return void. */ - public function store_css_rule( $css_selector, $css_declarations, $store_key ) { + public static function store_css_rule( $css_selector, $css_declarations, $store_key ) { if ( ! $css_selector || ! isset( static::$stores[ $store_key ] ) ) { - return false; + return; } $css_declarations = new WP_Style_Engine_CSS_Declarations( $css_declarations ); $stored_css_rule = static::$stores[ $store_key ]->add_rule( $css_selector ); $stored_css_rule->add_declarations( $css_declarations ); - return true; + } + + /** + * Returns a store by store key. + * + * @param string $store_key A valid key corresponding to an existing store in static::$stores. + * + * @return WP_Style_Engine_CSS_Rules_Store|null The store, if found, otherwise `null`. + */ + public static function get_store( $store_key ) { + if ( ! isset( static::$stores[ $store_key ] ) ) { + return null; + } + return static::$stores[ $store_key ]; } /** @@ -368,19 +382,14 @@ protected static function render_styles( $callable, $priority = 10 ) { * Fetches, processes and compiles stored styles, then renders them to the page. */ public static function process_and_enqueue_stored_styles() { - foreach ( static::$stores as $store_key => $store_instance ) { - if ( ! $store_instance ) { - continue; - } - - $processor = new WP_Style_Engine_Processor( $store_instance ); - $styles_output = $processor->get_css(); - - if ( ! empty( $styles_output ) ) { - wp_register_style( $store_key, false, array(), true, true ); - wp_add_inline_style( $store_key, $styles_output ); - wp_enqueue_style( $store_key ); - } + // 1. Block supports + // @TODO we could loop through static::$stores to enqueue and get the key. + $styles_output = static::compile_stylesheet_from_store( 'block-supports' ) . static::compile_stylesheet_from_store( 'layout-block-supports' ); + + if ( ! empty( $styles_output ) ) { + wp_register_style( 'block-supports', false, array(), true, true ); + wp_add_inline_style( 'block-supports', $styles_output ); + wp_enqueue_style( 'block-supports' ); } } @@ -522,42 +531,6 @@ protected static function get_css_declarations( $style_value, $style_definition, return $css_declarations; } - /** - * Returns compiled CSS from parsed css_declarations. - * - * @param array $css_declarations An array of parsed CSS property => CSS value pairs. - * @param string $css_selector When a selector is passed, the function will return a full CSS rule `$selector { ...rules }`, otherwise a concatenated string of properties and values. - * - * @return string A compiled CSS string. - */ - public function compile_css( $css_declarations, $css_selector ) { - if ( empty( $css_declarations ) || ! is_array( $css_declarations ) ) { - return ''; - } - - // Return an entire rule if there is a selector. - if ( $css_selector ) { - $css_rule = new WP_Style_Engine_CSS_Rule( $css_selector, $css_declarations ); - return $css_rule->get_css(); - } else { - $css_declarations = new WP_Style_Engine_CSS_Declarations( $css_declarations ); - return $css_declarations->get_declarations_string(); - } - } - /** - * Returns a string of classnames, - * - * @param string $classnames A flat array of classnames. - * - * @return string A string of classnames separate by a space. - */ - public function compile_classnames( $classnames ) { - if ( empty( $classnames ) || ! is_array( $classnames ) ) { - return null; - } - return implode( ' ', array_unique( $classnames ) ); - } - /** * Style value parser that returns a CSS definition array comprising style properties * that have keys representing individual style properties, otherwise known as longhand CSS properties. @@ -604,6 +577,59 @@ protected static function get_individual_property_css_declarations( $style_value } return $css_declarations; } + + /** + * Returns compiled CSS from parsed css_declarations. + * + * @param array $css_declarations An array of parsed CSS property => CSS value pairs. + * @param string $css_selector When a selector is passed, the function will return a full CSS rule `$selector { ...rules }`, otherwise a concatenated string of properties and values. + * + * @return string A compiled CSS string. + */ + public function compile_css( $css_declarations, $css_selector ) { + if ( empty( $css_declarations ) || ! is_array( $css_declarations ) ) { + return ''; + } + + // Return an entire rule if there is a selector. + if ( $css_selector ) { + $css_rule = new WP_Style_Engine_CSS_Rule( $css_selector, $css_declarations ); + return $css_rule->get_css(); + } else { + $css_declarations = new WP_Style_Engine_CSS_Declarations( $css_declarations ); + return $css_declarations->get_declarations_string(); + } + } + + /** + * Returns a string of classnames, + * + * @param string $classnames A flat array of classnames. + * + * @return string A string of classnames separate by a space. + */ + public function compile_classnames( $classnames ) { + if ( empty( $classnames ) || ! is_array( $classnames ) ) { + return null; + } + return implode( ' ', array_unique( $classnames ) ); + } + + /** + * Returns a compiled stylesheet from stored CSS rules. + * + * @param string $store_key A valid key corresponding to an existing store in static::$stores. + * + * @return string A compiled stylesheet from stored CSS rules. + */ + public static function compile_stylesheet_from_store( $store_key ) { + $store = static::get_store( $store_key ); + if ( $store ) { + $processor = new WP_Style_Engine_Processor( $store ); + return $processor->get_css(); + } + return ''; + } } /** @@ -650,7 +676,7 @@ function wp_style_engine_get_styles( $block_styles, $options = array() ) { $styles_output['css'] = $style_engine->compile_css( $parsed_styles['css_declarations'], $options['selector'] ); $styles_output['declarations'] = $parsed_styles['css_declarations']; if ( true === $options['enqueue'] ) { - $style_engine->store_css_rule( $options['selector'], $parsed_styles['css_declarations'], 'block-supports' ); + $style_engine::store_css_rule( $options['selector'], $parsed_styles['css_declarations'], 'block-supports' ); } } @@ -664,22 +690,47 @@ function wp_style_engine_get_styles( $block_styles, $options = array() ) { } /** - * Global public interface method to parse block styles from a single block style object - * and then, via the `enqueue` flag, will enqueue them for rendering on the frontend in a block-supports inline style tag. + * Global public interface method to register styles to be enqueued and rendered. + * + * @access public + * + * @param string $store_key A valid store key. + * @param array $css_rules array( + * 'selector' => (string) A CSS selector. + * 'css_declarations' => (boolean) An array of CSS definitions, e.g., array( "$property" => "$value" ). + * );. + * + * @return WP_Style_Engine_CSS_Rules_Store|null The store, if found, otherwise `null`. + */ +function wp_style_engine_add_to_store( $store_key, $css_rules = array() ) { + if ( empty( $store_key ) || empty( $css_rules ) ) { + return null; + } + if ( class_exists( 'WP_Style_Engine' ) ) { + $style_engine = WP_Style_Engine::get_instance(); + foreach ( $css_rules as $selector => $css_declarations ) { + $style_engine::store_css_rule( $selector, $css_declarations, $store_key ); + } + return $style_engine::get_store( $store_key ); + } +} + +/** + * Returns a compiled stylesheet from stored CSS rules. * * @access public * - * @param string $selector A CSS selector. - * @param array $css_declarations An array of CSS definitions, e.g., array( "$property" => "$value" ). + * @param string $store_key A valid store key. * - * @return boolean Whether the storage process was successful. + * @return string A compiled stylesheet from stored CSS rules. */ -function wp_style_engine_enqueue_block_supports_styles( $selector, $css_declarations ) { - if ( empty( $selector ) || empty( $css_declarations ) ) { - return false; +function wp_style_engine_get_stylesheet( $store_key ) { + if ( empty( $store_key ) ) { + return null; } if ( class_exists( 'WP_Style_Engine' ) ) { - return WP_Style_Engine::get_instance()->store_css_rule( $selector, $css_declarations, 'block-supports' ); + return WP_Style_Engine::get_instance()::compile_stylesheet_from_store( $store_key ); } - return false; + return null; } +