[ 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 $class_string CSS class string to look for a variation in. 16 * 17 * @return array|null The block style variation name if found. 18 */ 19 function wp_get_block_style_variation_name_from_class( $class_string ) { 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] ?? null; 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 * Render 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 * 79 * @return array The parsed block with block style variation classname added. 80 */ 81 function wp_render_block_style_variation_support_styles( $parsed_block ) { 82 $classes = $parsed_block['attrs']['className'] ?? null; 83 $variations = wp_get_block_style_variation_name_from_class( $classes ); 84 85 if ( ! $variations ) { 86 return $parsed_block; 87 } 88 89 $tree = WP_Theme_JSON_Resolver::get_merged_data(); 90 $theme_json = $tree->get_raw_data(); 91 92 // Only the first block style variation with data is supported. 93 $variation_data = array(); 94 foreach ( $variations as $variation ) { 95 $variation_data = $theme_json['styles']['blocks'][ $parsed_block['blockName'] ]['variations'][ $variation ] ?? array(); 96 97 if ( ! empty( $variation_data ) ) { 98 break; 99 } 100 } 101 102 if ( empty( $variation_data ) ) { 103 return $parsed_block; 104 } 105 106 /* 107 * Recursively resolve any ref values with the appropriate value within the 108 * theme_json data. 109 */ 110 wp_resolve_block_style_variation_ref_values( $variation_data, $theme_json ); 111 112 $variation_instance = wp_unique_id( $variation . '--' ); 113 $class_name = "is-style-$variation_instance"; 114 $updated_class_name = $parsed_block['attrs']['className'] . " $class_name"; 115 116 /* 117 * Even though block style variations are effectively theme.json partials, 118 * they can't be processed completely as though they are. 119 * 120 * Block styles support custom selectors to direct specific types of styles 121 * to inner elements. For example, borders on Image block's get applied to 122 * the inner `img` element rather than the wrapping `figure`. 123 * 124 * The following relocates the "root" block style variation styles to 125 * under an appropriate blocks property to leverage the preexisting style 126 * generation for simple block style variations. This way they get the 127 * custom selectors they need. 128 * 129 * The inner elements and block styles for the variation itself are 130 * still included at the top level but scoped by the variation's selector 131 * when the stylesheet is generated. 132 */ 133 $elements_data = $variation_data['elements'] ?? array(); 134 $blocks_data = $variation_data['blocks'] ?? array(); 135 unset( $variation_data['elements'] ); 136 unset( $variation_data['blocks'] ); 137 138 _wp_array_set( 139 $blocks_data, 140 array( $parsed_block['blockName'], 'variations', $variation_instance ), 141 $variation_data 142 ); 143 144 $config = array( 145 'version' => WP_Theme_JSON::LATEST_SCHEMA, 146 'styles' => array( 147 'elements' => $elements_data, 148 'blocks' => $blocks_data, 149 ), 150 ); 151 152 // Turn off filter that excludes block nodes. They are needed here for the variation's inner block types. 153 if ( ! is_admin() ) { 154 remove_filter( 'wp_theme_json_get_style_nodes', 'wp_filter_out_block_nodes' ); 155 } 156 157 // Temporarily prevent variation instance from being sanitized while processing theme.json. 158 $styles_registry = WP_Block_Styles_Registry::get_instance(); 159 $styles_registry->register( $parsed_block['blockName'], array( 'name' => $variation_instance ) ); 160 161 $variation_theme_json = new WP_Theme_JSON( $config, 'blocks' ); 162 $variation_styles = $variation_theme_json->get_stylesheet( 163 array( 'styles' ), 164 array( 'custom' ), 165 array( 166 'include_block_style_variations' => true, 167 'skip_root_layout_styles' => true, 168 'scope' => ".$class_name", 169 ) 170 ); 171 172 // Clean up temporary block style now instance styles have been processed. 173 $styles_registry->unregister( $parsed_block['blockName'], $variation_instance ); 174 175 // Restore filter that excludes block nodes. 176 if ( ! is_admin() ) { 177 add_filter( 'wp_theme_json_get_style_nodes', 'wp_filter_out_block_nodes' ); 178 } 179 180 if ( empty( $variation_styles ) ) { 181 return $parsed_block; 182 } 183 184 wp_register_style( 'block-style-variation-styles', false, array( 'wp-block-library', 'global-styles' ) ); 185 wp_add_inline_style( 'block-style-variation-styles', $variation_styles ); 186 187 /* 188 * Add variation instance class name to block's className string so it can 189 * be enforced in the block markup via render_block filter. 190 */ 191 _wp_array_set( $parsed_block, array( 'attrs', 'className' ), $updated_class_name ); 192 193 return $parsed_block; 194 } 195 196 /** 197 * Ensure the variation block support class name generated and added to 198 * block attributes in the `render_block_data` filter gets applied to the 199 * block's markup. 200 * 201 * @see wp_render_block_style_variation_support_styles 202 * 203 * @since 6.6.0 204 * @access private 205 * 206 * @param string $block_content Rendered block content. 207 * @param array $block Block object. 208 * 209 * @return string Filtered block content. 210 */ 211 function wp_render_block_style_variation_class_name( $block_content, $block ) { 212 if ( ! $block_content || empty( $block['attrs']['className'] ) ) { 213 return $block_content; 214 } 215 216 /* 217 * Matches a class prefixed by `is-style`, followed by the 218 * variation slug, then `--`, and finally an instance number. 219 */ 220 preg_match( '/\bis-style-(\S+?--\d+)\b/', $block['attrs']['className'], $matches ); 221 222 if ( empty( $matches ) ) { 223 return $block_content; 224 } 225 226 $tags = new WP_HTML_Tag_Processor( $block_content ); 227 228 if ( $tags->next_tag() ) { 229 /* 230 * Ensure the variation instance class name set in the 231 * `render_block_data` filter is applied in markup. 232 * See `wp_render_block_style_variation_support_styles`. 233 */ 234 $tags->add_class( $matches[0] ); 235 } 236 237 return $tags->get_updated_html(); 238 } 239 240 /** 241 * Enqueues styles for block style variations. 242 * 243 * @since 6.6.0 244 * @access private 245 */ 246 function wp_enqueue_block_style_variation_styles() { 247 wp_enqueue_style( 'block-style-variation-styles' ); 248 } 249 250 // Register the block support. 251 WP_Block_Supports::get_instance()->register( 'block-style-variation', array() ); 252 253 add_filter( 'render_block_data', 'wp_render_block_style_variation_support_styles', 10, 2 ); 254 add_filter( 'render_block', 'wp_render_block_style_variation_class_name', 10, 2 ); 255 add_action( 'wp_enqueue_scripts', 'wp_enqueue_block_style_variation_styles', 1 ); 256 257 /** 258 * Registers block style variations read in from theme.json partials. 259 * 260 * @since 6.6.0 261 * @access private 262 * 263 * @param array $variations Shared block style variations. 264 */ 265 function wp_register_block_style_variations_from_theme_json_partials( $variations ) { 266 if ( empty( $variations ) ) { 267 return; 268 } 269 270 $registry = WP_Block_Styles_Registry::get_instance(); 271 272 foreach ( $variations as $variation ) { 273 if ( empty( $variation['blockTypes'] ) || empty( $variation['styles'] ) ) { 274 continue; 275 } 276 277 $variation_name = $variation['slug'] ?? _wp_to_kebab_case( $variation['title'] ); 278 $variation_label = $variation['title'] ?? $variation_name; 279 280 foreach ( $variation['blockTypes'] as $block_type ) { 281 $registered_styles = $registry->get_registered_styles_for_block( $block_type ); 282 283 // Register block style variation if it hasn't already been registered. 284 if ( ! array_key_exists( $variation_name, $registered_styles ) ) { 285 register_block_style( 286 $block_type, 287 array( 288 'name' => $variation_name, 289 'label' => $variation_label, 290 ) 291 ); 292 } 293 } 294 } 295 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Sat Nov 23 08:20:01 2024 | Cross-referenced by PHPXref |