[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

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

   1  <?php
   2  /**
   3   * Elements styles block support.
   4   *
   5   * @package WordPress
   6   * @since 5.8.0
   7   */
   8  
   9  /**
  10   * Gets the elements class names.
  11   *
  12   * @since 6.0.0
  13   * @access private
  14   *
  15   * @return string The unique class name.
  16   */
  17  function wp_get_elements_class_name(): string {
  18      return wp_unique_prefixed_id( 'wp-elements-' );
  19  }
  20  
  21  /**
  22   * Determines whether an elements class name should be added to the block.
  23   *
  24   * @since 6.6.0
  25   * @access private
  26   *
  27   * @param  array $block   Block object.
  28   * @param  array $options Per element type options e.g. whether to skip serialization.
  29   * @return bool Whether the block needs an elements class name.
  30   */
  31  function wp_should_add_elements_class_name( $block, $options ) {
  32      if ( ! isset( $block['attrs']['style']['elements'] ) ) {
  33          return false;
  34      }
  35  
  36      $element_color_properties = array(
  37          'button'  => array(
  38              'skip'  => $options['button']['skip'] ?? false,
  39              'paths' => array(
  40                  array( 'button', 'color', 'text' ),
  41                  array( 'button', 'color', 'background' ),
  42                  array( 'button', 'color', 'gradient' ),
  43              ),
  44          ),
  45          'link'    => array(
  46              'skip'  => $options['link']['skip'] ?? false,
  47              'paths' => array(
  48                  array( 'link', 'color', 'text' ),
  49                  array( 'link', ':hover', 'color', 'text' ),
  50              ),
  51          ),
  52          'heading' => array(
  53              'skip'  => $options['heading']['skip'] ?? false,
  54              'paths' => array(
  55                  array( 'heading', 'color', 'text' ),
  56                  array( 'heading', 'color', 'background' ),
  57                  array( 'heading', 'color', 'gradient' ),
  58                  array( 'h1', 'color', 'text' ),
  59                  array( 'h1', 'color', 'background' ),
  60                  array( 'h1', 'color', 'gradient' ),
  61                  array( 'h2', 'color', 'text' ),
  62                  array( 'h2', 'color', 'background' ),
  63                  array( 'h2', 'color', 'gradient' ),
  64                  array( 'h3', 'color', 'text' ),
  65                  array( 'h3', 'color', 'background' ),
  66                  array( 'h3', 'color', 'gradient' ),
  67                  array( 'h4', 'color', 'text' ),
  68                  array( 'h4', 'color', 'background' ),
  69                  array( 'h4', 'color', 'gradient' ),
  70                  array( 'h5', 'color', 'text' ),
  71                  array( 'h5', 'color', 'background' ),
  72                  array( 'h5', 'color', 'gradient' ),
  73                  array( 'h6', 'color', 'text' ),
  74                  array( 'h6', 'color', 'background' ),
  75                  array( 'h6', 'color', 'gradient' ),
  76              ),
  77          ),
  78      );
  79  
  80      $elements_style_attributes = $block['attrs']['style']['elements'];
  81  
  82      foreach ( $element_color_properties as $element_config ) {
  83          if ( $element_config['skip'] ) {
  84              continue;
  85          }
  86  
  87          foreach ( $element_config['paths'] as $path ) {
  88              if ( null !== _wp_array_get( $elements_style_attributes, $path, null ) ) {
  89                  return true;
  90              }
  91          }
  92      }
  93  
  94      return false;
  95  }
  96  
  97  /**
  98   * Render the elements stylesheet and adds elements class name to block as required.
  99   *
 100   * In the case of nested blocks we want the parent element styles to be rendered before their descendants.
 101   * This solves the issue of an element (e.g.: link color) being styled in both the parent and a descendant:
 102   * we want the descendant style to take priority, and this is done by loading it after, in DOM order.
 103   *
 104   * @since 6.0.0
 105   * @since 6.1.0 Implemented the style engine to generate CSS and classnames.
 106   * @since 6.6.0 Element block support class and styles are generated via the `render_block_data` filter instead of `pre_render_block`.
 107   * @access private
 108   *
 109   * @param array $parsed_block The parsed block.
 110   * @return array The same parsed block with elements classname added if appropriate.
 111   *
 112   * @phpstan-param array{
 113   *     blockName: string,
 114   *     attrs: array{
 115   *         className?: string,
 116   *         style?: array{
 117   *             elements?: array<string, array{
 118   *                 ":hover"?: array<string, string>,
 119   *                 ...
 120   *             }>,
 121   *         },
 122   *         ...
 123   *     },
 124   *     ...
 125   * } $parsed_block
 126   * @phpstan-return array{
 127   *     blockName: string,
 128   *     attrs: array{
 129   *         className?: string,
 130   *         ...
 131   *     },
 132   *     ...
 133   * }
 134   */
 135  function wp_render_elements_support_styles( $parsed_block ) {
 136      /*
 137       * The generation of element styles and classname were moved to the
 138       * `render_block_data` filter in 6.6.0 to avoid filtered attributes
 139       * breaking the application of the elements CSS class.
 140       *
 141       * @link https://github.com/WordPress/gutenberg/pull/59535
 142       *
 143       * The change in filter means, the argument types for this function
 144       * have changed and require deprecating.
 145       */
 146      if ( is_string( $parsed_block ) ) {
 147          _deprecated_argument(
 148              __FUNCTION__,
 149              '6.6.0',
 150              __( 'Use as a `pre_render_block` filter is deprecated. Use with `render_block_data` instead.' )
 151          );
 152      }
 153  
 154      $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $parsed_block['blockName'] );
 155      if ( ! $block_type ) {
 156          return $parsed_block;
 157      }
 158  
 159      $element_block_styles = $parsed_block['attrs']['style']['elements'] ?? null;
 160      if ( ! $element_block_styles ) {
 161          return $parsed_block;
 162      }
 163  
 164      $skip_link_color_serialization         = wp_should_skip_block_supports_serialization( $block_type, 'color', 'link' );
 165      $skip_heading_color_serialization      = wp_should_skip_block_supports_serialization( $block_type, 'color', 'heading' );
 166      $skip_button_color_serialization       = wp_should_skip_block_supports_serialization( $block_type, 'color', 'button' );
 167      $skips_all_element_color_serialization = $skip_link_color_serialization &&
 168          $skip_heading_color_serialization &&
 169          $skip_button_color_serialization;
 170  
 171      if ( $skips_all_element_color_serialization ) {
 172          return $parsed_block;
 173      }
 174  
 175      $options = array(
 176          'button'  => array( 'skip' => $skip_button_color_serialization ),
 177          'link'    => array( 'skip' => $skip_link_color_serialization ),
 178          'heading' => array( 'skip' => $skip_heading_color_serialization ),
 179      );
 180  
 181      if ( ! wp_should_add_elements_class_name( $parsed_block, $options ) ) {
 182          return $parsed_block;
 183      }
 184  
 185      $class_name         = wp_get_elements_class_name();
 186      $updated_class_name = isset( $parsed_block['attrs']['className'] ) ? $parsed_block['attrs']['className'] . " $class_name" : $class_name;
 187  
 188      _wp_array_set( $parsed_block, array( 'attrs', 'className' ), $updated_class_name );
 189  
 190      // Generate element styles based on selector and store in style engine for enqueuing.
 191      $element_types = array(
 192          'button'  => array(
 193              'selector' => ".$class_name .wp-element-button, .$class_name .wp-block-button__link",
 194              'skip'     => $skip_button_color_serialization,
 195          ),
 196          'link'    => array(
 197              'selector'       => ".$class_name a:where(:not(.wp-element-button))",
 198              'hover_selector' => ".$class_name a:where(:not(.wp-element-button)):hover",
 199              'skip'           => $skip_link_color_serialization,
 200          ),
 201          'heading' => array(
 202              'selector' => ".$class_name h1, .$class_name h2, .$class_name h3, .$class_name h4, .$class_name h5, .$class_name h6",
 203              'skip'     => $skip_heading_color_serialization,
 204              'elements' => array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ),
 205          ),
 206      );
 207  
 208      foreach ( $element_types as $element_type => $element_config ) {
 209          if ( $element_config['skip'] ) {
 210              continue;
 211          }
 212  
 213          $element_style_object = $element_block_styles[ $element_type ] ?? null;
 214  
 215          // Process primary element type styles.
 216          if ( $element_style_object ) {
 217              wp_style_engine_get_styles(
 218                  $element_style_object,
 219                  array(
 220                      'selector' => $element_config['selector'],
 221                      'context'  => 'block-supports',
 222                  )
 223              );
 224  
 225              if ( isset( $element_style_object[':hover'], $element_config['hover_selector'] ) ) {
 226                  wp_style_engine_get_styles(
 227                      $element_style_object[':hover'],
 228                      array(
 229                          'selector' => $element_config['hover_selector'],
 230                          'context'  => 'block-supports',
 231                      )
 232                  );
 233              }
 234          }
 235  
 236          // Process related elements e.g. h1-h6 for headings.
 237          if ( isset( $element_config['elements'] ) ) {
 238              foreach ( $element_config['elements'] as $element ) {
 239                  $element_style_object = $element_block_styles[ $element ] ?? null;
 240  
 241                  if ( $element_style_object ) {
 242                      wp_style_engine_get_styles(
 243                          $element_style_object,
 244                          array(
 245                              'selector' => ".$class_name $element",
 246                              'context'  => 'block-supports',
 247                          )
 248                      );
 249                  }
 250              }
 251          }
 252      }
 253  
 254      return $parsed_block;
 255  }
 256  
 257  /**
 258   * Ensure the elements block support class name generated, and added to
 259   * block attributes, in the `render_block_data` filter gets applied to the
 260   * block's markup.
 261   *
 262   * @see wp_render_elements_support_styles
 263   * @since 6.6.0
 264   *
 265   * @param string $block_content Rendered block content.
 266   * @param array  $block         Block object.
 267   * @return string               Filtered block content.
 268   *
 269   * @phpstan-param array{
 270   *     attrs: array{
 271   *         className?: string,
 272   *         ...
 273   *     },
 274   *     ...
 275   * } $block
 276   */
 277  function wp_render_elements_class_name( $block_content, $block ) {
 278      $class_name_attr   = $block['attrs']['className'] ?? null;
 279      $class_name_prefix = 'wp-elements-';
 280      if ( ! is_string( $class_name_attr ) || ! str_contains( $class_name_attr, $class_name_prefix ) ) {
 281          return $block_content;
 282      }
 283  
 284      // Parse out the 'wp-elements-*' class name.
 285      $matched_class_name = null;
 286      $token_delimiter    = " \t\f\r\n";
 287      $class_token        = strtok( $class_name_attr, $token_delimiter );
 288      while ( false !== $class_token ) {
 289          if ( str_starts_with( $class_token, $class_name_prefix ) ) {
 290              $matched_class_name = $class_token;
 291              break;
 292          }
 293          $class_token = strtok( $token_delimiter );
 294      }
 295      if ( null === $matched_class_name ) {
 296          return $block_content;
 297      }
 298  
 299      $tags = new WP_HTML_Tag_Processor( $block_content );
 300      if ( $tags->next_tag() ) {
 301          $tags->add_class( $matched_class_name );
 302      }
 303  
 304      return $tags->get_updated_html();
 305  }
 306  
 307  add_filter( 'render_block', 'wp_render_elements_class_name', 10, 2 );
 308  add_filter( 'render_block_data', 'wp_render_elements_support_styles', 10, 1 );


Generated : Sun Jun 21 08:20:10 2026 Cross-referenced by PHPXref