diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php index b26ed9df659c28..01ad8cae629451 100644 --- a/lib/block-supports/layout.php +++ b/lib/block-supports/layout.php @@ -39,9 +39,33 @@ 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 ) { + gutenberg_set_layout_style( $selector, $layout, $has_block_gap_support, $gap_value, $should_skip_gap_serialization, $fallback_gap_value, $block_spacing ); + + // Use a unique store to avoid conflicts with other stores and implementations. + $store = WP_Style_Engine_CSS_Rules_Store_Gutenberg::get_store( 'block-supports/layout/' . md5( $selector ) ); + $processor = new WP_Style_Engine_Processor_Gutenberg( $store ); + return $processor->get_css( true ); +} + +/** + * Adds the provided layout to the "block-supports/layout" Style-Engine store. + * + * @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 boolean $should_skip_gap_serialization Whether to skip applying the user-defined value set in the editor. + * @param string $fallback_gap_value The block gap value to apply. + * @param array $block_spacing Custom spacing set on the block. + * + * @return bool Returns true if the layout style was successfully generated, otherwise false. + */ +function gutenberg_set_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 = ''; + // Get the block-supports Style-Engine Store. + $store = WP_Style_Engine_CSS_Rules_Store_Gutenberg::get_store( 'block-supports/layout' ); + if ( 'default' === $layout_type ) { $content_size = isset( $layout['contentSize'] ) ? $layout['contentSize'] : ''; $wide_size = isset( $layout['wideSize'] ) ? $layout['wideSize'] : ''; @@ -55,14 +79,16 @@ 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 .= '}'; + $store->add_rule( "$selector > :where(:not(.alignleft):not(.alignright):not(.alignfull))" )->add_declarations( + 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; }"; + $store->add_rule( "$selector > .alignwide" )->add_declarations( array( 'max-width' => $wide_max_width_value ) ); + $store->add_rule( "$selector .alignfull" )->add_declarations( array( 'max-width' => 'none' ) ); if ( isset( $block_spacing ) ) { $block_spacing_values = gutenberg_style_engine_get_block_supports_styles( @@ -75,11 +101,11 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support // 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); }"; + $store->add_rule( "$selector > .alignfull" )->add_declarations( 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); }"; + $store->add_rule( "$selector > .alignfull" )->add_declarations( array( 'margin-left' => "calc($padding_left * -1)" ) ); } } } @@ -89,8 +115,18 @@ 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; }"; + $store->add_rule( "$selector > *" )->add_declarations( + array( + 'margin-block-start' => '0', + 'margin-block-end' => '0', + ) + ); + $store->add_rule( "$selector > * + *" )->add_declarations( + array( + 'margin-block-start' => $gap_value, + 'margin-block-end' => '0', + ) + ); } } } elseif ( 'flex' === $layout_type ) { @@ -113,7 +149,7 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support } if ( ! empty( $layout['flexWrap'] ) && 'nowrap' === $layout['flexWrap'] ) { - $style .= "$selector { flex-wrap: nowrap; }"; + $store->add_rule( $selector )->add_declarations( array( 'flex-wrap' => 'nowrap' ) ); } if ( $has_block_gap_support ) { @@ -123,9 +159,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 .= '}'; + $store->add_rule( $selector )->add_declarations( array( 'gap' => $gap_value ) ); } } @@ -136,29 +170,26 @@ 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 .= '}'; + $store->add_rule( $selector )->add_declarations( 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 .= '}'; + $store->add_rule( $selector )->add_declarations( array( 'align-items' => $vertical_alignment_options[ $layout['verticalAlignment'] ] ) ); } } else { - $style .= "$selector {"; - $style .= 'flex-direction: column;'; + $store->add_rule( $selector )->add_declarations( + 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'] ]};"; - } else { - $style .= 'align-items: flex-start;'; + $store->add_rule( $selector )->add_declarations( array( 'align-items' => $justify_content_options[ $layout['justifyContent'] ] ) ); } - $style .= '}'; } } - return $style; + return ! empty( $store->get_all_rules() ); } /** @@ -244,12 +275,12 @@ function gutenberg_render_layout_support_flag( $block_content, $block ) { // If a block's block.json skips serialization for spacing or spacing.blockGap, // don't apply the user-defined value to the styles. $should_skip_gap_serialization = gutenberg_should_skip_block_supports_serialization( $block_type, 'spacing', 'blockGap' ); - $style = gutenberg_get_layout_style( ".$block_classname.$container_class", $used_layout, $has_block_gap_support, $gap_value, $should_skip_gap_serialization, $fallback_gap_value, $block_spacing ); + $has_style = gutenberg_set_layout_style( ".$block_classname.$container_class", $used_layout, $has_block_gap_support, $gap_value, $should_skip_gap_serialization, $fallback_gap_value, $block_spacing ); // Only add container class and enqueue block support styles if unique styles were generated. - if ( ! empty( $style ) ) { + if ( $has_style ) { $class_names[] = $container_class; - wp_enqueue_block_support_styles( $style ); + gutenberg_enqueue_style_engine_store( 'block-supports/layout' ); } // This assumes the hook only applies to blocks with a single wrapper. diff --git a/lib/compat/wordpress-6.1/script-loader.php b/lib/compat/wordpress-6.1/script-loader.php index b3995178431e90..658bd0e3b349d6 100644 --- a/lib/compat/wordpress-6.1/script-loader.php +++ b/lib/compat/wordpress-6.1/script-loader.php @@ -23,10 +23,7 @@ * @param int $priority To set the priority for the add_action. */ function gutenberg_enqueue_block_support_styles( $style, $priority = 10 ) { - $action_hook_name = 'wp_footer'; - if ( wp_is_block_theme() ) { - $action_hook_name = 'wp_head'; - } + $action_hook_name = wp_is_block_theme() ? 'wp_head' : 'wp_footer'; add_action( $action_hook_name, static function () use ( $style ) { @@ -36,6 +33,27 @@ static function () use ( $style ) { ); } +/** + * This function takes care of adding inline styles + * from a Style-Engine store. + * + * @param string $store_name The name of the store. + */ +function gutenberg_enqueue_style_engine_store( $store_name ) { + $action_hook_name = wp_is_block_theme() ? 'wp_head' : 'wp_footer'; + add_action( + $action_hook_name, + static function () use ( $store_name ) { + $css = ( new WP_Style_Engine_Processor_Gutenberg( + WP_Style_Engine_CSS_Rules_Store_Gutenberg::get_store( $store_name ) + ) )->get_css( true ); + if ( $css ) { + echo ''; + } + } + ); +} + /** * This applies a filter to the list of style nodes that comes from `get_style_nodes` in WP_Theme_JSON. * This particular filter removes all of the blocks from the array. diff --git a/packages/style-engine/class-wp-style-engine-processor.php b/packages/style-engine/class-wp-style-engine-processor.php index 646ea43e7261a8..e23bda25f8b5c7 100644 --- a/packages/style-engine/class-wp-style-engine-processor.php +++ b/packages/style-engine/class-wp-style-engine-processor.php @@ -37,9 +37,11 @@ public function __construct( WP_Style_Engine_CSS_Rules_Store $store ) { /** * Get the CSS rules as a string. * + * @param bool $remove_printed_rules Whether to remove printed rules. + * * @return string The computed CSS. */ - public function get_css() { + public function get_css( $remove_printed_rules = false ) { // Combine CSS selectors that have identical declarations. $this->combine_rules_selectors(); @@ -47,7 +49,12 @@ public function get_css() { $css = ''; $rules = $this->store->get_all_rules(); foreach ( $rules as $rule ) { + // Add the CSS. $css .= $rule->get_css(); + if ( $remove_printed_rules ) { + // Remove the rule from the store to avoid double-rendering. + $this->store->remove_rule( $rule->get_selector() ); + } } return $css; }