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


Generated : Sun Oct 11 08:20:32 2026 Cross-referenced by PHPXref