[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

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

   1  <?php
   2  /**
   3   * Layout block support flag.
   4   *
   5   * @package WordPress
   6   * @since 5.8.0
   7   */
   8  
   9  /**
  10   * Returns layout definitions, keyed by layout type.
  11   *
  12   * Provides a common definition of slugs, classnames, base styles, and spacing styles for each layout type.
  13   * When making changes or additions to layout definitions, the corresponding JavaScript definitions should
  14   * also be updated.
  15   *
  16   * @since 6.3.0
  17   * @access private
  18   *
  19   * @return array[] Layout definitions.
  20   */
  21  function wp_get_layout_definitions() {
  22      $layout_definitions = array(
  23          'default'     => array(
  24              'name'          => 'default',
  25              'slug'          => 'flow',
  26              'className'     => 'is-layout-flow',
  27              'baseStyles'    => array(
  28                  array(
  29                      'selector' => ' > .alignleft',
  30                      'rules'    => array(
  31                          'float'               => 'left',
  32                          'margin-inline-start' => '0',
  33                          'margin-inline-end'   => '2em',
  34                      ),
  35                  ),
  36                  array(
  37                      'selector' => ' > .alignright',
  38                      'rules'    => array(
  39                          'float'               => 'right',
  40                          'margin-inline-start' => '2em',
  41                          'margin-inline-end'   => '0',
  42                      ),
  43                  ),
  44                  array(
  45                      'selector' => ' > .aligncenter',
  46                      'rules'    => array(
  47                          'margin-left'  => 'auto !important',
  48                          'margin-right' => 'auto !important',
  49                      ),
  50                  ),
  51              ),
  52              'spacingStyles' => array(
  53                  array(
  54                      'selector' => ' > :first-child:first-child',
  55                      'rules'    => array(
  56                          'margin-block-start' => '0',
  57                      ),
  58                  ),
  59                  array(
  60                      'selector' => ' > :last-child:last-child',
  61                      'rules'    => array(
  62                          'margin-block-end' => '0',
  63                      ),
  64                  ),
  65                  array(
  66                      'selector' => ' > *',
  67                      'rules'    => array(
  68                          'margin-block-start' => null,
  69                          'margin-block-end'   => '0',
  70                      ),
  71                  ),
  72              ),
  73          ),
  74          'constrained' => array(
  75              'name'          => 'constrained',
  76              'slug'          => 'constrained',
  77              'className'     => 'is-layout-constrained',
  78              'baseStyles'    => array(
  79                  array(
  80                      'selector' => ' > .alignleft',
  81                      'rules'    => array(
  82                          'float'               => 'left',
  83                          'margin-inline-start' => '0',
  84                          'margin-inline-end'   => '2em',
  85                      ),
  86                  ),
  87                  array(
  88                      'selector' => ' > .alignright',
  89                      'rules'    => array(
  90                          'float'               => 'right',
  91                          'margin-inline-start' => '2em',
  92                          'margin-inline-end'   => '0',
  93                      ),
  94                  ),
  95                  array(
  96                      'selector' => ' > .aligncenter',
  97                      'rules'    => array(
  98                          'margin-left'  => 'auto !important',
  99                          'margin-right' => 'auto !important',
 100                      ),
 101                  ),
 102                  array(
 103                      'selector' => ' > :where(:not(.alignleft):not(.alignright):not(.alignfull))',
 104                      'rules'    => array(
 105                          'max-width'    => 'var(--wp--style--global--content-size)',
 106                          'margin-left'  => 'auto !important',
 107                          'margin-right' => 'auto !important',
 108                      ),
 109                  ),
 110                  array(
 111                      'selector' => ' > .alignwide',
 112                      'rules'    => array(
 113                          'max-width' => 'var(--wp--style--global--wide-size)',
 114                      ),
 115                  ),
 116              ),
 117              'spacingStyles' => array(
 118                  array(
 119                      'selector' => ' > :first-child:first-child',
 120                      'rules'    => array(
 121                          'margin-block-start' => '0',
 122                      ),
 123                  ),
 124                  array(
 125                      'selector' => ' > :last-child:last-child',
 126                      'rules'    => array(
 127                          'margin-block-end' => '0',
 128                      ),
 129                  ),
 130                  array(
 131                      'selector' => ' > *',
 132                      'rules'    => array(
 133                          'margin-block-start' => null,
 134                          'margin-block-end'   => '0',
 135                      ),
 136                  ),
 137              ),
 138          ),
 139          'flex'        => array(
 140              'name'          => 'flex',
 141              'slug'          => 'flex',
 142              'className'     => 'is-layout-flex',
 143              'displayMode'   => 'flex',
 144              'baseStyles'    => array(
 145                  array(
 146                      'selector' => '',
 147                      'rules'    => array(
 148                          'flex-wrap'   => 'wrap',
 149                          'align-items' => 'center',
 150                      ),
 151                  ),
 152                  array(
 153                      'selector' => ' > *',
 154                      'rules'    => array(
 155                          'margin' => '0',
 156                      ),
 157                  ),
 158              ),
 159              'spacingStyles' => array(
 160                  array(
 161                      'selector' => '',
 162                      'rules'    => array(
 163                          'gap' => null,
 164                      ),
 165                  ),
 166              ),
 167          ),
 168          'grid'        => array(
 169              'name'          => 'grid',
 170              'slug'          => 'grid',
 171              'className'     => 'is-layout-grid',
 172              'displayMode'   => 'grid',
 173              'baseStyles'    => array(
 174                  array(
 175                      'selector' => ' > *',
 176                      'rules'    => array(
 177                          'margin' => '0',
 178                      ),
 179                  ),
 180              ),
 181              'spacingStyles' => array(
 182                  array(
 183                      'selector' => '',
 184                      'rules'    => array(
 185                          'gap' => null,
 186                      ),
 187                  ),
 188              ),
 189          ),
 190      );
 191  
 192      return $layout_definitions;
 193  }
 194  
 195  /**
 196   * Registers the layout block attribute for block types that support it.
 197   *
 198   * @since 5.8.0
 199   * @since 6.3.0 Check for layout support via the `layout` key with fallback to `__experimentalLayout`.
 200   * @access private
 201   *
 202   * @param WP_Block_Type $block_type Block Type.
 203   */
 204  function wp_register_layout_support( $block_type ) {
 205      $support_layout = block_has_support( $block_type, 'layout', false ) || block_has_support( $block_type, '__experimentalLayout', false );
 206      if ( $support_layout ) {
 207          if ( ! $block_type->attributes ) {
 208              $block_type->attributes = array();
 209          }
 210  
 211          if ( ! array_key_exists( 'layout', $block_type->attributes ) ) {
 212              $block_type->attributes['layout'] = array(
 213                  'type' => 'object',
 214              );
 215          }
 216      }
 217  }
 218  
 219  /**
 220   * Generates the CSS corresponding to the provided layout.
 221   *
 222   * @since 5.9.0
 223   * @since 6.1.0 Added `$block_spacing` param, use style engine to enqueue styles.
 224   * @since 6.3.0 Added grid layout type.
 225   * @access private
 226   *
 227   * @param string               $selector                      CSS selector.
 228   * @param array                $layout                        Layout object. The one that is passed has already checked
 229   *                                                            the existence of default block layout.
 230   * @param bool                 $has_block_gap_support         Optional. Whether the theme has support for the block gap. Default false.
 231   * @param string|string[]|null $gap_value                     Optional. The block gap value to apply. Default null.
 232   * @param bool                 $should_skip_gap_serialization Optional. Whether to skip applying the user-defined value set in the editor. Default false.
 233   * @param string               $fallback_gap_value            Optional. The block gap value to apply. Default '0.5em'.
 234   * @param array|null           $block_spacing                 Optional. Custom spacing set on the block. Default null.
 235   * @return string CSS styles on success. Else, empty string.
 236   */
 237  function wp_get_layout_style( $selector, $layout, $has_block_gap_support = false, $gap_value = null, $should_skip_gap_serialization = false, $fallback_gap_value = '0.5em', $block_spacing = null ) {
 238      $layout_type   = isset( $layout['type'] ) ? $layout['type'] : 'default';
 239      $layout_styles = array();
 240  
 241      if ( 'default' === $layout_type ) {
 242          if ( $has_block_gap_support ) {
 243              if ( is_array( $gap_value ) ) {
 244                  $gap_value = isset( $gap_value['top'] ) ? $gap_value['top'] : null;
 245              }
 246              if ( null !== $gap_value && ! $should_skip_gap_serialization ) {
 247                  // Get spacing CSS variable from preset value if provided.
 248                  if ( is_string( $gap_value ) && str_contains( $gap_value, 'var:preset|spacing|' ) ) {
 249                      $index_to_splice = strrpos( $gap_value, '|' ) + 1;
 250                      $slug            = _wp_to_kebab_case( substr( $gap_value, $index_to_splice ) );
 251                      $gap_value       = "var(--wp--preset--spacing--$slug)";
 252                  }
 253  
 254                  array_push(
 255                      $layout_styles,
 256                      array(
 257                          'selector'     => "$selector > *",
 258                          'declarations' => array(
 259                              'margin-block-start' => '0',
 260                              'margin-block-end'   => '0',
 261                          ),
 262                      ),
 263                      array(
 264                          'selector'     => "$selector$selector > * + *",
 265                          'declarations' => array(
 266                              'margin-block-start' => $gap_value,
 267                              'margin-block-end'   => '0',
 268                          ),
 269                      )
 270                  );
 271              }
 272          }
 273      } elseif ( 'constrained' === $layout_type ) {
 274          $content_size    = isset( $layout['contentSize'] ) ? $layout['contentSize'] : '';
 275          $wide_size       = isset( $layout['wideSize'] ) ? $layout['wideSize'] : '';
 276          $justify_content = isset( $layout['justifyContent'] ) ? $layout['justifyContent'] : 'center';
 277  
 278          $all_max_width_value  = $content_size ? $content_size : $wide_size;
 279          $wide_max_width_value = $wide_size ? $wide_size : $content_size;
 280  
 281          // Make sure there is a single CSS rule, and all tags are stripped for security.
 282          $all_max_width_value  = safecss_filter_attr( explode( ';', $all_max_width_value )[0] );
 283          $wide_max_width_value = safecss_filter_attr( explode( ';', $wide_max_width_value )[0] );
 284  
 285          $margin_left  = 'left' === $justify_content ? '0 !important' : 'auto !important';
 286          $margin_right = 'right' === $justify_content ? '0 !important' : 'auto !important';
 287  
 288          if ( $content_size || $wide_size ) {
 289              array_push(
 290                  $layout_styles,
 291                  array(
 292                      'selector'     => "$selector > :where(:not(.alignleft):not(.alignright):not(.alignfull))",
 293                      'declarations' => array(
 294                          'max-width'    => $all_max_width_value,
 295                          'margin-left'  => $margin_left,
 296                          'margin-right' => $margin_right,
 297                      ),
 298                  ),
 299                  array(
 300                      'selector'     => "$selector > .alignwide",
 301                      'declarations' => array( 'max-width' => $wide_max_width_value ),
 302                  ),
 303                  array(
 304                      'selector'     => "$selector .alignfull",
 305                      'declarations' => array( 'max-width' => 'none' ),
 306                  )
 307              );
 308  
 309              if ( isset( $block_spacing ) ) {
 310                  $block_spacing_values = wp_style_engine_get_styles(
 311                      array(
 312                          'spacing' => $block_spacing,
 313                      )
 314                  );
 315  
 316                  /*
 317                   * Handle negative margins for alignfull children of blocks with custom padding set.
 318                   * They're added separately because padding might only be set on one side.
 319                   */
 320                  if ( isset( $block_spacing_values['declarations']['padding-right'] ) ) {
 321                      $padding_right   = $block_spacing_values['declarations']['padding-right'];
 322                      $layout_styles[] = array(
 323                          'selector'     => "$selector > .alignfull",
 324                          'declarations' => array( 'margin-right' => "calc($padding_right * -1)" ),
 325                      );
 326                  }
 327                  if ( isset( $block_spacing_values['declarations']['padding-left'] ) ) {
 328                      $padding_left    = $block_spacing_values['declarations']['padding-left'];
 329                      $layout_styles[] = array(
 330                          'selector'     => "$selector > .alignfull",
 331                          'declarations' => array( 'margin-left' => "calc($padding_left * -1)" ),
 332                      );
 333                  }
 334              }
 335          }
 336  
 337          if ( 'left' === $justify_content ) {
 338              $layout_styles[] = array(
 339                  'selector'     => "$selector > :where(:not(.alignleft):not(.alignright):not(.alignfull))",
 340                  'declarations' => array( 'margin-left' => '0 !important' ),
 341              );
 342          }
 343  
 344          if ( 'right' === $justify_content ) {
 345              $layout_styles[] = array(
 346                  'selector'     => "$selector > :where(:not(.alignleft):not(.alignright):not(.alignfull))",
 347                  'declarations' => array( 'margin-right' => '0 !important' ),
 348              );
 349          }
 350  
 351          if ( $has_block_gap_support ) {
 352              if ( is_array( $gap_value ) ) {
 353                  $gap_value = isset( $gap_value['top'] ) ? $gap_value['top'] : null;
 354              }
 355              if ( null !== $gap_value && ! $should_skip_gap_serialization ) {
 356                  // Get spacing CSS variable from preset value if provided.
 357                  if ( is_string( $gap_value ) && str_contains( $gap_value, 'var:preset|spacing|' ) ) {
 358                      $index_to_splice = strrpos( $gap_value, '|' ) + 1;
 359                      $slug            = _wp_to_kebab_case( substr( $gap_value, $index_to_splice ) );
 360                      $gap_value       = "var(--wp--preset--spacing--$slug)";
 361                  }
 362  
 363                  array_push(
 364                      $layout_styles,
 365                      array(
 366                          'selector'     => "$selector > *",
 367                          'declarations' => array(
 368                              'margin-block-start' => '0',
 369                              'margin-block-end'   => '0',
 370                          ),
 371                      ),
 372                      array(
 373                          'selector'     => "$selector$selector > * + *",
 374                          'declarations' => array(
 375                              'margin-block-start' => $gap_value,
 376                              'margin-block-end'   => '0',
 377                          ),
 378                      )
 379                  );
 380              }
 381          }
 382      } elseif ( 'flex' === $layout_type ) {
 383          $layout_orientation = isset( $layout['orientation'] ) ? $layout['orientation'] : 'horizontal';
 384  
 385          $justify_content_options = array(
 386              'left'   => 'flex-start',
 387              'right'  => 'flex-end',
 388              'center' => 'center',
 389          );
 390  
 391          $vertical_alignment_options = array(
 392              'top'    => 'flex-start',
 393              'center' => 'center',
 394              'bottom' => 'flex-end',
 395          );
 396  
 397          if ( 'horizontal' === $layout_orientation ) {
 398              $justify_content_options    += array( 'space-between' => 'space-between' );
 399              $vertical_alignment_options += array( 'stretch' => 'stretch' );
 400          } else {
 401              $justify_content_options    += array( 'stretch' => 'stretch' );
 402              $vertical_alignment_options += array( 'space-between' => 'space-between' );
 403          }
 404  
 405          if ( ! empty( $layout['flexWrap'] ) && 'nowrap' === $layout['flexWrap'] ) {
 406              $layout_styles[] = array(
 407                  'selector'     => $selector,
 408                  'declarations' => array( 'flex-wrap' => 'nowrap' ),
 409              );
 410          }
 411  
 412          if ( $has_block_gap_support && isset( $gap_value ) ) {
 413              $combined_gap_value = '';
 414              $gap_sides          = is_array( $gap_value ) ? array( 'top', 'left' ) : array( 'top' );
 415  
 416              foreach ( $gap_sides as $gap_side ) {
 417                  $process_value = $gap_value;
 418                  if ( is_array( $gap_value ) ) {
 419                      $process_value = isset( $gap_value[ $gap_side ] ) ? $gap_value[ $gap_side ] : $fallback_gap_value;
 420                  }
 421                  // Get spacing CSS variable from preset value if provided.
 422                  if ( is_string( $process_value ) && str_contains( $process_value, 'var:preset|spacing|' ) ) {
 423                      $index_to_splice = strrpos( $process_value, '|' ) + 1;
 424                      $slug            = _wp_to_kebab_case( substr( $process_value, $index_to_splice ) );
 425                      $process_value   = "var(--wp--preset--spacing--$slug)";
 426                  }
 427                  $combined_gap_value .= "$process_value ";
 428              }
 429              $gap_value = trim( $combined_gap_value );
 430  
 431              if ( null !== $gap_value && ! $should_skip_gap_serialization ) {
 432                  $layout_styles[] = array(
 433                      'selector'     => $selector,
 434                      'declarations' => array( 'gap' => $gap_value ),
 435                  );
 436              }
 437          }
 438  
 439          if ( 'horizontal' === $layout_orientation ) {
 440              /*
 441               * Add this style only if is not empty for backwards compatibility,
 442               * since we intend to convert blocks that had flex layout implemented
 443               * by custom css.
 444               */
 445              if ( ! empty( $layout['justifyContent'] ) && array_key_exists( $layout['justifyContent'], $justify_content_options ) ) {
 446                  $layout_styles[] = array(
 447                      'selector'     => $selector,
 448                      'declarations' => array( 'justify-content' => $justify_content_options[ $layout['justifyContent'] ] ),
 449                  );
 450              }
 451  
 452              if ( ! empty( $layout['verticalAlignment'] ) && array_key_exists( $layout['verticalAlignment'], $vertical_alignment_options ) ) {
 453                  $layout_styles[] = array(
 454                      'selector'     => $selector,
 455                      'declarations' => array( 'align-items' => $vertical_alignment_options[ $layout['verticalAlignment'] ] ),
 456                  );
 457              }
 458          } else {
 459              $layout_styles[] = array(
 460                  'selector'     => $selector,
 461                  'declarations' => array( 'flex-direction' => 'column' ),
 462              );
 463              if ( ! empty( $layout['justifyContent'] ) && array_key_exists( $layout['justifyContent'], $justify_content_options ) ) {
 464                  $layout_styles[] = array(
 465                      'selector'     => $selector,
 466                      'declarations' => array( 'align-items' => $justify_content_options[ $layout['justifyContent'] ] ),
 467                  );
 468              } else {
 469                  $layout_styles[] = array(
 470                      'selector'     => $selector,
 471                      'declarations' => array( 'align-items' => 'flex-start' ),
 472                  );
 473              }
 474              if ( ! empty( $layout['verticalAlignment'] ) && array_key_exists( $layout['verticalAlignment'], $vertical_alignment_options ) ) {
 475                  $layout_styles[] = array(
 476                      'selector'     => $selector,
 477                      'declarations' => array( 'justify-content' => $vertical_alignment_options[ $layout['verticalAlignment'] ] ),
 478                  );
 479              }
 480          }
 481      } elseif ( 'grid' === $layout_type ) {
 482          if ( ! empty( $layout['columnCount'] ) ) {
 483              $layout_styles[] = array(
 484                  'selector'     => $selector,
 485                  'declarations' => array( 'grid-template-columns' => 'repeat(' . $layout['columnCount'] . ', minmax(0, 1fr))' ),
 486              );
 487          } else {
 488              $minimum_column_width = ! empty( $layout['minimumColumnWidth'] ) ? $layout['minimumColumnWidth'] : '12rem';
 489  
 490              $layout_styles[] = array(
 491                  'selector'     => $selector,
 492                  'declarations' => array( 'grid-template-columns' => 'repeat(auto-fill, minmax(min(' . $minimum_column_width . ', 100%), 1fr))' ),
 493              );
 494          }
 495  
 496          if ( $has_block_gap_support && isset( $gap_value ) ) {
 497              $combined_gap_value = '';
 498              $gap_sides          = is_array( $gap_value ) ? array( 'top', 'left' ) : array( 'top' );
 499  
 500              foreach ( $gap_sides as $gap_side ) {
 501                  $process_value = $gap_value;
 502                  if ( is_array( $gap_value ) ) {
 503                      $process_value = isset( $gap_value[ $gap_side ] ) ? $gap_value[ $gap_side ] : $fallback_gap_value;
 504                  }
 505                  // Get spacing CSS variable from preset value if provided.
 506                  if ( is_string( $process_value ) && str_contains( $process_value, 'var:preset|spacing|' ) ) {
 507                      $index_to_splice = strrpos( $process_value, '|' ) + 1;
 508                      $slug            = _wp_to_kebab_case( substr( $process_value, $index_to_splice ) );
 509                      $process_value   = "var(--wp--preset--spacing--$slug)";
 510                  }
 511                  $combined_gap_value .= "$process_value ";
 512              }
 513              $gap_value = trim( $combined_gap_value );
 514  
 515              if ( null !== $gap_value && ! $should_skip_gap_serialization ) {
 516                  $layout_styles[] = array(
 517                      'selector'     => $selector,
 518                      'declarations' => array( 'gap' => $gap_value ),
 519                  );
 520              }
 521          }
 522      }
 523  
 524      if ( ! empty( $layout_styles ) ) {
 525          /*
 526           * Add to the style engine store to enqueue and render layout styles.
 527           * Return compiled layout styles to retain backwards compatibility.
 528           * Since https://github.com/WordPress/gutenberg/pull/42452,
 529           * wp_enqueue_block_support_styles is no longer called in this block supports file.
 530           */
 531          return wp_style_engine_get_stylesheet_from_css_rules(
 532              $layout_styles,
 533              array(
 534                  'context'  => 'block-supports',
 535                  'prettify' => false,
 536              )
 537          );
 538      }
 539  
 540      return '';
 541  }
 542  
 543  /**
 544   * Renders the layout config to the block wrapper.
 545   *
 546   * @since 5.8.0
 547   * @since 6.3.0 Adds compound class to layout wrapper for global spacing styles.
 548   * @since 6.3.0 Check for layout support via the `layout` key with fallback to `__experimentalLayout`.
 549   * @access private
 550   *
 551   * @param string $block_content Rendered block content.
 552   * @param array  $block         Block object.
 553   * @return string Filtered block content.
 554   */
 555  function wp_render_layout_support_flag( $block_content, $block ) {
 556      $block_type            = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
 557      $block_supports_layout = block_has_support( $block_type, 'layout', false ) || block_has_support( $block_type, '__experimentalLayout', false );
 558      $layout_from_parent    = isset( $block['attrs']['style']['layout']['selfStretch'] ) ? $block['attrs']['style']['layout']['selfStretch'] : null;
 559  
 560      if ( ! $block_supports_layout && ! $layout_from_parent ) {
 561          return $block_content;
 562      }
 563  
 564      $outer_class_names = array();
 565  
 566      if ( 'fixed' === $layout_from_parent || 'fill' === $layout_from_parent ) {
 567          $container_content_class = wp_unique_id( 'wp-container-content-' );
 568  
 569          $child_layout_styles = array();
 570  
 571          if ( 'fixed' === $layout_from_parent && isset( $block['attrs']['style']['layout']['flexSize'] ) ) {
 572              $child_layout_styles[] = array(
 573                  'selector'     => ".$container_content_class",
 574                  'declarations' => array(
 575                      'flex-basis' => $block['attrs']['style']['layout']['flexSize'],
 576                      'box-sizing' => 'border-box',
 577                  ),
 578              );
 579          } elseif ( 'fill' === $layout_from_parent ) {
 580              $child_layout_styles[] = array(
 581                  'selector'     => ".$container_content_class",
 582                  'declarations' => array(
 583                      'flex-grow' => '1',
 584                  ),
 585              );
 586          }
 587  
 588          wp_style_engine_get_stylesheet_from_css_rules(
 589              $child_layout_styles,
 590              array(
 591                  'context'  => 'block-supports',
 592                  'prettify' => false,
 593              )
 594          );
 595  
 596          $outer_class_names[] = $container_content_class;
 597      }
 598  
 599      // Prep the processor for modifying the block output.
 600      $processor = new WP_HTML_Tag_Processor( $block_content );
 601  
 602      // Having no tags implies there are no tags onto which to add class names.
 603      if ( ! $processor->next_tag() ) {
 604          return $block_content;
 605      }
 606  
 607      /*
 608       * A block may not support layout but still be affected by a parent block's layout.
 609       *
 610       * In these cases add the appropriate class names and then return early; there's
 611       * no need to investigate on this block whether additional layout constraints apply.
 612       */
 613      if ( ! $block_supports_layout && ! empty( $outer_class_names ) ) {
 614          foreach ( $outer_class_names as $class_name ) {
 615              $processor->add_class( $class_name );
 616          }
 617          return $processor->get_updated_html();
 618      } elseif ( ! $block_supports_layout ) {
 619          // Ensure layout classnames are not injected if there is no layout support.
 620          return $block_content;
 621      }
 622  
 623      $global_settings = wp_get_global_settings();
 624      $fallback_layout = isset( $block_type->supports['layout']['default'] )
 625          ? $block_type->supports['layout']['default']
 626          : array();
 627      if ( empty( $fallback_layout ) ) {
 628          $fallback_layout = isset( $block_type->supports['__experimentalLayout']['default'] )
 629              ? $block_type->supports['__experimentalLayout']['default']
 630              : array();
 631      }
 632      $used_layout = isset( $block['attrs']['layout'] ) ? $block['attrs']['layout'] : $fallback_layout;
 633  
 634      $class_names        = array();
 635      $layout_definitions = wp_get_layout_definitions();
 636  
 637      /*
 638       * Uses an incremental ID that is independent per prefix to make sure that
 639       * rendering different numbers of blocks doesn't affect the IDs of other
 640       * blocks. Makes the CSS class names stable across paginations
 641       * for features like the enhanced pagination of the Query block.
 642       */
 643      $container_class = wp_unique_prefixed_id(
 644          'wp-container-' . sanitize_title( $block['blockName'] ) . '-is-layout-'
 645      );
 646  
 647      // Set the correct layout type for blocks using legacy content width.
 648      if ( isset( $used_layout['inherit'] ) && $used_layout['inherit'] || isset( $used_layout['contentSize'] ) && $used_layout['contentSize'] ) {
 649          $used_layout['type'] = 'constrained';
 650      }
 651  
 652      $root_padding_aware_alignments = isset( $global_settings['useRootPaddingAwareAlignments'] )
 653          ? $global_settings['useRootPaddingAwareAlignments']
 654          : false;
 655  
 656      if (
 657          $root_padding_aware_alignments &&
 658          isset( $used_layout['type'] ) &&
 659          'constrained' === $used_layout['type']
 660      ) {
 661          $class_names[] = 'has-global-padding';
 662      }
 663  
 664      /*
 665       * The following section was added to reintroduce a small set of layout classnames that were
 666       * removed in the 5.9 release (https://github.com/WordPress/gutenberg/issues/38719). It is
 667       * not intended to provide an extended set of classes to match all block layout attributes
 668       * here.
 669       */
 670      if ( ! empty( $block['attrs']['layout']['orientation'] ) ) {
 671          $class_names[] = 'is-' . sanitize_title( $block['attrs']['layout']['orientation'] );
 672      }
 673  
 674      if ( ! empty( $block['attrs']['layout']['justifyContent'] ) ) {
 675          $class_names[] = 'is-content-justification-' . sanitize_title( $block['attrs']['layout']['justifyContent'] );
 676      }
 677  
 678      if ( ! empty( $block['attrs']['layout']['flexWrap'] ) && 'nowrap' === $block['attrs']['layout']['flexWrap'] ) {
 679          $class_names[] = 'is-nowrap';
 680      }
 681  
 682      // Get classname for layout type.
 683      if ( isset( $used_layout['type'] ) ) {
 684          $layout_classname = isset( $layout_definitions[ $used_layout['type'] ]['className'] )
 685              ? $layout_definitions[ $used_layout['type'] ]['className']
 686              : '';
 687      } else {
 688          $layout_classname = isset( $layout_definitions['default']['className'] )
 689              ? $layout_definitions['default']['className']
 690              : '';
 691      }
 692  
 693      if ( $layout_classname && is_string( $layout_classname ) ) {
 694          $class_names[] = sanitize_title( $layout_classname );
 695      }
 696  
 697      /*
 698       * Only generate Layout styles if the theme has not opted-out.
 699       * Attribute-based Layout classnames are output in all cases.
 700       */
 701      if ( ! current_theme_supports( 'disable-layout-styles' ) ) {
 702  
 703          $gap_value = isset( $block['attrs']['style']['spacing']['blockGap'] )
 704              ? $block['attrs']['style']['spacing']['blockGap']
 705              : null;
 706          /*
 707           * Skip if gap value contains unsupported characters.
 708           * Regex for CSS value borrowed from `safecss_filter_attr`, and used here
 709           * to only match against the value, not the CSS attribute.
 710           */
 711          if ( is_array( $gap_value ) ) {
 712              foreach ( $gap_value as $key => $value ) {
 713                  $gap_value[ $key ] = $value && preg_match( '%[\\\(&=}]|/\*%', $value ) ? null : $value;
 714              }
 715          } else {
 716              $gap_value = $gap_value && preg_match( '%[\\\(&=}]|/\*%', $gap_value ) ? null : $gap_value;
 717          }
 718  
 719          $fallback_gap_value = isset( $block_type->supports['spacing']['blockGap']['__experimentalDefault'] )
 720              ? $block_type->supports['spacing']['blockGap']['__experimentalDefault']
 721              : '0.5em';
 722          $block_spacing      = isset( $block['attrs']['style']['spacing'] )
 723              ? $block['attrs']['style']['spacing']
 724              : null;
 725  
 726          /*
 727           * If a block's block.json skips serialization for spacing or spacing.blockGap,
 728           * don't apply the user-defined value to the styles.
 729           */
 730          $should_skip_gap_serialization = wp_should_skip_block_supports_serialization( $block_type, 'spacing', 'blockGap' );
 731  
 732          $block_gap             = isset( $global_settings['spacing']['blockGap'] )
 733              ? $global_settings['spacing']['blockGap']
 734              : null;
 735          $has_block_gap_support = isset( $block_gap );
 736  
 737          $style = wp_get_layout_style(
 738              ".$container_class.$container_class",
 739              $used_layout,
 740              $has_block_gap_support,
 741              $gap_value,
 742              $should_skip_gap_serialization,
 743              $fallback_gap_value,
 744              $block_spacing
 745          );
 746  
 747          // Only add container class and enqueue block support styles if unique styles were generated.
 748          if ( ! empty( $style ) ) {
 749              $class_names[] = $container_class;
 750          }
 751      }
 752  
 753      // Add combined layout and block classname for global styles to hook onto.
 754      $block_name    = explode( '/', $block['blockName'] );
 755      $class_names[] = 'wp-block-' . end( $block_name ) . '-' . $layout_classname;
 756  
 757      // Add classes to the outermost HTML tag if necessary.
 758      if ( ! empty( $outer_class_names ) ) {
 759          foreach ( $outer_class_names as $outer_class_name ) {
 760              $processor->add_class( $outer_class_name );
 761          }
 762      }
 763  
 764      /**
 765       * Attempts to refer to the inner-block wrapping element by its class attribute.
 766       *
 767       * When examining a block's inner content, if a block has inner blocks, then
 768       * the first content item will likely be a text (HTML) chunk immediately
 769       * preceding the inner blocks. The last HTML tag in that chunk would then be
 770       * an opening tag for an element that wraps the inner blocks.
 771       *
 772       * There's no reliable way to associate this wrapper in $block_content because
 773       * it may have changed during the rendering pipeline (as inner contents is
 774       * provided before rendering) and through previous filters. In many cases,
 775       * however, the `class` attribute will be a good-enough identifier, so this
 776       * code finds the last tag in that chunk and stores the `class` attribute
 777       * so that it can be used later when working through the rendered block output
 778       * to identify the wrapping element and add the remaining class names to it.
 779       *
 780       * It's also possible that no inner block wrapper even exists. If that's the
 781       * case this code could apply the class names to an invalid element.
 782       *
 783       * Example:
 784       *
 785       *     $block['innerBlocks']  = array( $list_item );
 786       *     $block['innerContent'] = array( '<ul class="list-wrapper is-unordered">', null, '</ul>' );
 787       *
 788       *     // After rendering, the initial contents may have been modified by other renderers or filters.
 789       *     $block_content = <<<HTML
 790       *         <figure>
 791       *             <ul class="annotated-list list-wrapper is-unordered">
 792       *                 <li>Code</li>
 793       *             </ul><figcaption>It's a list!</figcaption>
 794       *         </figure>
 795       *     HTML;
 796       *
 797       * Although it is possible that the original block-wrapper classes are changed in $block_content
 798       * from how they appear in $block['innerContent'], it's likely that the original class attributes
 799       * are still present in the wrapper as they are in this example. Frequently, additional classes
 800       * will also be present; rarely should classes be removed.
 801       *
 802       * @todo Find a better way to match the first inner block. If it's possible to identify where the
 803       *       first inner block starts, then it will be possible to find the last tag before it starts
 804       *       and then that tag, if an opening tag, can be solidly identified as a wrapping element.
 805       *       Can some unique value or class or ID be added to the inner blocks when they process
 806       *       so that they can be extracted here safely without guessing? Can the block rendering function
 807       *       return information about where the rendered inner blocks start?
 808       *
 809       * @var string|null
 810       */
 811      $inner_block_wrapper_classes = null;
 812      $first_chunk                 = isset( $block['innerContent'][0] ) ? $block['innerContent'][0] : null;
 813      if ( is_string( $first_chunk ) && count( $block['innerContent'] ) > 1 ) {
 814          $first_chunk_processor = new WP_HTML_Tag_Processor( $first_chunk );
 815          while ( $first_chunk_processor->next_tag() ) {
 816              $class_attribute = $first_chunk_processor->get_attribute( 'class' );
 817              if ( is_string( $class_attribute ) && ! empty( $class_attribute ) ) {
 818                  $inner_block_wrapper_classes = $class_attribute;
 819              }
 820          }
 821      }
 822  
 823      /*
 824       * If necessary, advance to what is likely to be an inner block wrapper tag.
 825       *
 826       * This advances until it finds the first tag containing the original class
 827       * attribute from above. If none is found it will scan to the end of the block
 828       * and fail to add any class names.
 829       *
 830       * If there is no block wrapper it won't advance at all, in which case the
 831       * class names will be added to the first and outermost tag of the block.
 832       * For cases where this outermost tag is the only tag surrounding inner
 833       * blocks then the outer wrapper and inner wrapper are the same.
 834       */
 835      do {
 836          if ( ! $inner_block_wrapper_classes ) {
 837              break;
 838          }
 839  
 840          $class_attribute = $processor->get_attribute( 'class' );
 841          if ( is_string( $class_attribute ) && str_contains( $class_attribute, $inner_block_wrapper_classes ) ) {
 842              break;
 843          }
 844      } while ( $processor->next_tag() );
 845  
 846      // Add the remaining class names.
 847      foreach ( $class_names as $class_name ) {
 848          $processor->add_class( $class_name );
 849      }
 850  
 851      return $processor->get_updated_html();
 852  }
 853  
 854  // Register the block support.
 855  WP_Block_Supports::get_instance()->register(
 856      'layout',
 857      array(
 858          'register_attribute' => 'wp_register_layout_support',
 859      )
 860  );
 861  add_filter( 'render_block', 'wp_render_layout_support_flag', 10, 2 );
 862  
 863  /**
 864   * For themes without theme.json file, make sure
 865   * to restore the inner div for the group block
 866   * to avoid breaking styles relying on that div.
 867   *
 868   * @since 5.8.0
 869   * @access private
 870   *
 871   * @param string $block_content Rendered block content.
 872   * @param array  $block         Block object.
 873   * @return string Filtered block content.
 874   */
 875  function wp_restore_group_inner_container( $block_content, $block ) {
 876      $tag_name                         = isset( $block['attrs']['tagName'] ) ? $block['attrs']['tagName'] : 'div';
 877      $group_with_inner_container_regex = sprintf(
 878          '/(^\s*<%1$s\b[^>]*wp-block-group(\s|")[^>]*>)(\s*<div\b[^>]*wp-block-group__inner-container(\s|")[^>]*>)((.|\S|\s)*)/U',
 879          preg_quote( $tag_name, '/' )
 880      );
 881  
 882      if (
 883          wp_theme_has_theme_json() ||
 884          1 === preg_match( $group_with_inner_container_regex, $block_content ) ||
 885          ( isset( $block['attrs']['layout']['type'] ) && 'flex' === $block['attrs']['layout']['type'] )
 886      ) {
 887          return $block_content;
 888      }
 889  
 890      /*
 891       * This filter runs after the layout classnames have been added to the block, so they
 892       * have to be removed from the outer wrapper and then added to the inner.
 893       */
 894      $layout_classes = array();
 895      $processor      = new WP_HTML_Tag_Processor( $block_content );
 896  
 897      if ( $processor->next_tag( array( 'class_name' => 'wp-block-group' ) ) ) {
 898          foreach ( $processor->class_list() as $class_name ) {
 899              if ( str_contains( $class_name, 'is-layout-' ) ) {
 900                  $layout_classes[] = $class_name;
 901                  $processor->remove_class( $class_name );
 902              }
 903          }
 904      }
 905  
 906      $content_without_layout_classes = $processor->get_updated_html();
 907      $replace_regex                  = sprintf(
 908          '/(^\s*<%1$s\b[^>]*wp-block-group[^>]*>)(.*)(<\/%1$s>\s*$)/ms',
 909          preg_quote( $tag_name, '/' )
 910      );
 911      $updated_content                = preg_replace_callback(
 912          $replace_regex,
 913          static function ( $matches ) {
 914              return $matches[1] . '<div class="wp-block-group__inner-container">' . $matches[2] . '</div>' . $matches[3];
 915          },
 916          $content_without_layout_classes
 917      );
 918  
 919      // Add layout classes to inner wrapper.
 920      if ( ! empty( $layout_classes ) ) {
 921          $processor = new WP_HTML_Tag_Processor( $updated_content );
 922          if ( $processor->next_tag( array( 'class_name' => 'wp-block-group__inner-container' ) ) ) {
 923              foreach ( $layout_classes as $class_name ) {
 924                  $processor->add_class( $class_name );
 925              }
 926          }
 927          $updated_content = $processor->get_updated_html();
 928      }
 929      return $updated_content;
 930  }
 931  
 932  add_filter( 'render_block_core/group', 'wp_restore_group_inner_container', 10, 2 );
 933  
 934  /**
 935   * For themes without theme.json file, make sure
 936   * to restore the outer div for the aligned image block
 937   * to avoid breaking styles relying on that div.
 938   *
 939   * @since 6.0.0
 940   * @access private
 941   *
 942   * @param string $block_content Rendered block content.
 943   * @param  array  $block        Block object.
 944   * @return string Filtered block content.
 945   */
 946  function wp_restore_image_outer_container( $block_content, $block ) {
 947      $image_with_align = "
 948  /# 1) everything up to the class attribute contents
 949  (
 950      ^\s*
 951      <figure\b
 952      [^>]*
 953      \bclass=
 954      [\"']
 955  )
 956  # 2) the class attribute contents
 957  (
 958      [^\"']*
 959      \bwp-block-image\b
 960      [^\"']*
 961      \b(?:alignleft|alignright|aligncenter)\b
 962      [^\"']*
 963  )
 964  # 3) everything after the class attribute contents
 965  (
 966      [\"']
 967      [^>]*
 968      >
 969      .*
 970      <\/figure>
 971  )/iUx";
 972  
 973      if (
 974          wp_theme_has_theme_json() ||
 975          0 === preg_match( $image_with_align, $block_content, $matches )
 976      ) {
 977          return $block_content;
 978      }
 979  
 980      $wrapper_classnames = array( 'wp-block-image' );
 981  
 982      // If the block has a classNames attribute these classnames need to be removed from the content and added back
 983      // to the new wrapper div also.
 984      if ( ! empty( $block['attrs']['className'] ) ) {
 985          $wrapper_classnames = array_merge( $wrapper_classnames, explode( ' ', $block['attrs']['className'] ) );
 986      }
 987      $content_classnames          = explode( ' ', $matches[2] );
 988      $filtered_content_classnames = array_diff( $content_classnames, $wrapper_classnames );
 989  
 990      return '<div class="' . implode( ' ', $wrapper_classnames ) . '">' . $matches[1] . implode( ' ', $filtered_content_classnames ) . $matches[3] . '</div>';
 991  }
 992  
 993  add_filter( 'render_block_core/image', 'wp_restore_image_outer_container', 10, 2 );


Generated : Sat Apr 20 08:20:01 2024 Cross-referenced by PHPXref