[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/block-supports/ -> block-style-variations.php (source)

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


Generated : Mon Jul 15 08:20:02 2024 Cross-referenced by PHPXref