| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Block support to enable per-section styling of block types via 4 * block style variations. 5 * 6 * @package WordPress 7 * @since 6.6.0 8 */ 9 10 /** 11 * Determines the block style variation names within a CSS class string. 12 * 13 * @since 6.6.0 14 * 15 * @param string|null $class_string CSS class string to look for a variation in. 16 * @return string[]|null The block style variation names (empty if none found), or null if a string was not supplied. 17 * @phpstan-return ( $class_string is string ? list<non-empty-string> : null ) 18 */ 19 function wp_get_block_style_variation_name_from_class( $class_string ): ?array { 20 if ( ! is_string( $class_string ) ) { 21 return null; 22 } 23 24 preg_match_all( '/\bis-style-(?!default)(\S+)\b/', $class_string, $matches ); 25 return $matches[1]; 26 } 27 28 /** 29 * Recursively resolves any `ref` values within a block style variation's data. 30 * 31 * @since 6.6.0 32 * @access private 33 * 34 * @param array $variation_data Reference to the variation data being processed. 35 * @param array $theme_json Theme.json data to retrieve referenced values from. 36 */ 37 function wp_resolve_block_style_variation_ref_values( &$variation_data, $theme_json ) { 38 foreach ( $variation_data as $key => &$value ) { 39 // Only need to potentially process arrays. 40 if ( is_array( $value ) ) { 41 // If ref value is set, attempt to find its matching value and update it. 42 if ( array_key_exists( 'ref', $value ) ) { 43 // Clean up any invalid ref value. 44 if ( empty( $value['ref'] ) || ! is_string( $value['ref'] ) ) { 45 unset( $variation_data[ $key ] ); 46 } 47 48 $value_path = explode( '.', $value['ref'] ?? '' ); 49 $ref_value = _wp_array_get( $theme_json, $value_path ); 50 51 // Only update the current value if the referenced path matched a value. 52 if ( null === $ref_value ) { 53 unset( $variation_data[ $key ] ); 54 } else { 55 $value = $ref_value; 56 } 57 } else { 58 // Recursively look for ref instances. 59 wp_resolve_block_style_variation_ref_values( $value, $theme_json ); 60 } 61 } 62 } 63 } 64 /** 65 * Renders the block style variation's styles. 66 * 67 * In the case of nested blocks with variations applied, we want the parent 68 * variation's styles to be rendered before their descendants. This solves the 69 * issue of a block type being styled in both the parent and descendant: we want 70 * the descendant style to take priority, and this is done by loading it after, 71 * in the DOM order. This is why the variation stylesheet generation is in a 72 * different filter. 73 * 74 * @since 6.6.0 75 * @access private 76 * 77 * @param array $parsed_block The parsed block. 78 * @return array The parsed block with block style variation classname added. 79 */ 80 function wp_render_block_style_variation_support_styles( $parsed_block ) { 81 $classes = $parsed_block['attrs']['className'] ?? null; 82 $variations = wp_get_block_style_variation_name_from_class( $classes ); 83 84 if ( ! $variations ) { 85 return $parsed_block; 86 } 87 88 $tree = WP_Theme_JSON_Resolver::get_merged_data(); 89 $theme_json = $tree->get_raw_data(); 90 91 // Only the first block style variation with data is supported. 92 $variation_data = array(); 93 foreach ( $variations as $variation ) { 94 $variation_data = $theme_json['styles']['blocks'][ $parsed_block['blockName'] ]['variations'][ $variation ] ?? array(); 95 96 if ( ! empty( $variation_data ) ) { 97 break; 98 } 99 } 100 101 if ( empty( $variation_data ) ) { 102 return $parsed_block; 103 } 104 105 /* 106 * Recursively resolve any ref values with the appropriate value within the 107 * theme_json data. 108 */ 109 wp_resolve_block_style_variation_ref_values( $variation_data, $theme_json ); 110 111 $variation_instance = wp_unique_id( $variation . '--' ); 112 $class_name = "is-style-$variation_instance"; 113 $updated_class_name = $parsed_block['attrs']['className'] . " $class_name"; 114 115 /* 116 * Even though block style variations are effectively theme.json partials, 117 * they can't be processed completely as though they are. 118 * 119 * Block styles support custom selectors to direct specific types of styles 120 * to inner elements. For example, borders on Image block's get applied to 121 * the inner `img` element rather than the wrapping `figure`. 122 * 123 * The following relocates the "root" block style variation styles to 124 * under an appropriate blocks property to leverage the preexisting style 125 * generation for simple block style variations. This way they get the 126 * custom selectors they need. 127 * 128 * The inner elements and block styles for the variation itself are 129 * still included at the top level but scoped by the variation's selector 130 * when the stylesheet is generated. 131 */ 132 $elements_data = $variation_data['elements'] ?? array(); 133 $blocks_data = $variation_data['blocks'] ?? array(); 134 unset( $variation_data['elements'] ); 135 unset( $variation_data['blocks'] ); 136 137 _wp_array_set( 138 $blocks_data, 139 array( $parsed_block['blockName'], 'variations', $variation_instance ), 140 $variation_data 141 ); 142 143 $config = array( 144 'version' => WP_Theme_JSON::LATEST_SCHEMA, 145 'settings' => array( 146 'spacing' => array( 147 'blockGap' => true, 148 ), 149 ), 150 'styles' => array( 151 'elements' => $elements_data, 152 'blocks' => $blocks_data, 153 ), 154 ); 155 156 // Ensure variation state styles know about any custom viewport breakpoints. 157 if ( isset( $theme_json['settings']['viewport'] ) ) { 158 $config['settings']['viewport'] = $theme_json['settings']['viewport']; 159 } 160 161 // Turn off filter that excludes block nodes. They are needed here for the variation's inner block types. 162 if ( ! is_admin() ) { 163 remove_filter( 'wp_theme_json_get_style_nodes', 'wp_filter_out_block_nodes' ); 164 } 165 166 // Temporarily prevent variation instance from being sanitized while processing theme.json. 167 $styles_registry = WP_Block_Styles_Registry::get_instance(); 168 $styles_registry->register( $parsed_block['blockName'], array( 'name' => $variation_instance ) ); 169 170 $variation_theme_json = new WP_Theme_JSON( $config, 'blocks' ); 171 $variation_styles = $variation_theme_json->get_stylesheet( 172 array( 'styles' ), 173 array( 'custom' ), 174 array( 175 'include_block_style_variations' => true, 176 'skip_root_layout_styles' => true, 177 'scope' => ".$class_name", 178 ) 179 ); 180 181 // Clean up temporary block style now instance styles have been processed. 182 $styles_registry->unregister( $parsed_block['blockName'], $variation_instance ); 183 184 // Restore filter that excludes block nodes. 185 if ( ! is_admin() ) { 186 add_filter( 'wp_theme_json_get_style_nodes', 'wp_filter_out_block_nodes' ); 187 } 188 189 if ( empty( $variation_styles ) ) { 190 return $parsed_block; 191 } 192 193 wp_register_style( 'block-style-variation-styles', false, array( 'wp-block-library', 'global-styles' ) ); 194 wp_add_inline_style( 'block-style-variation-styles', $variation_styles ); 195 196 /* 197 * Add variation instance class name to block's className string so it can 198 * be enforced in the block markup via render_block filter. 199 */ 200 _wp_array_set( $parsed_block, array( 'attrs', 'className' ), $updated_class_name ); 201 202 return $parsed_block; 203 } 204 205 /** 206 * Ensures the variation block support class name generated and added to 207 * block attributes in the `render_block_data` filter gets applied to the 208 * block's markup. 209 * 210 * @since 6.6.0 211 * @access private 212 * 213 * @see wp_render_block_style_variation_support_styles 214 * 215 * @param string $block_content Rendered block content. 216 * @param array $block Block object. 217 * @return string Filtered block content. 218 */ 219 function wp_render_block_style_variation_class_name( $block_content, $block ) { 220 if ( ! $block_content || empty( $block['attrs']['className'] ) ) { 221 return $block_content; 222 } 223 224 $block_class_name = $block['attrs']['className']; 225 if ( ! is_string( $block_class_name ) ) { 226 return $block_content; 227 } 228 229 /* 230 * Matches a class prefixed by `is-style`, followed by the 231 * variation slug, then `--`, and finally an instance number. 232 */ 233 preg_match( '/\bis-style-(\S+?--\d+)\b/', $block_class_name, $matches ); 234 235 if ( empty( $matches ) ) { 236 return $block_content; 237 } 238 239 $tags = new WP_HTML_Tag_Processor( $block_content ); 240 241 if ( $tags->next_tag() ) { 242 /* 243 * Ensure the variation instance class name set in the 244 * `render_block_data` filter is applied in markup. 245 * See `wp_render_block_style_variation_support_styles`. 246 */ 247 $tags->add_class( $matches[0] ); 248 } 249 250 return $tags->get_updated_html(); 251 } 252 253 /** 254 * Enqueues styles for block style variations. 255 * 256 * @since 6.6.0 257 * @access private 258 */ 259 function wp_enqueue_block_style_variation_styles() { 260 wp_enqueue_style( 'block-style-variation-styles' ); 261 } 262 263 // Register the block support. 264 WP_Block_Supports::get_instance()->register( 'block-style-variation', array() ); 265 266 add_filter( 'render_block_data', 'wp_render_block_style_variation_support_styles', 10, 2 ); 267 add_filter( 'render_block', 'wp_render_block_style_variation_class_name', 10, 2 ); 268 add_action( 'wp_enqueue_scripts', 'wp_enqueue_block_style_variation_styles', 1 ); 269 270 /** 271 * Registers block style variations read in from theme.json partials. 272 * 273 * @since 6.6.0 274 * @access private 275 * 276 * @param array $variations Shared block style variations. 277 */ 278 function wp_register_block_style_variations_from_theme_json_partials( $variations ) { 279 if ( empty( $variations ) ) { 280 return; 281 } 282 283 $registry = WP_Block_Styles_Registry::get_instance(); 284 285 foreach ( $variations as $variation ) { 286 if ( empty( $variation['blockTypes'] ) || empty( $variation['styles'] ) ) { 287 continue; 288 } 289 290 $variation_name = $variation['slug'] ?? _wp_to_kebab_case( $variation['title'] ); 291 $variation_label = $variation['title'] ?? $variation_name; 292 293 foreach ( $variation['blockTypes'] as $block_type ) { 294 $registered_styles = $registry->get_registered_styles_for_block( $block_type ); 295 296 // Register block style variation if it hasn't already been registered. 297 if ( ! array_key_exists( $variation_name, $registered_styles ) ) { 298 register_block_style( 299 $block_type, 300 array( 301 'name' => $variation_name, 302 'label' => $variation_label, 303 ) 304 ); 305 } 306 } 307 } 308 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Fri Oct 9 08:20:33 2026 | Cross-referenced by PHPXref |