| @@ -5,226 +5,14 @@ | ||
| 5 | 5 | * @package gutenberg |
| 6 | 6 | */ |
| 7 | 7 | |
| 8 | 8 | /** |
| 9 | - * Get the first style variation name from a className string that matches a registered style. | |
| 10 | - * | |
| 11 | - * @param string $class_name CSS class string for a block. | |
| 12 | - * @param array<string, array<string, mixed>> $registered_styles Currently registered block styles. | |
| 13 | - * @return string|null The name of the first registered variation, or null if none found. | |
| 14 | - */ | |
| 15 | -function gutenberg_get_block_style_variation_name_from_registered_style( string $class_name, array $registered_styles = array() ): ?string { | |
| 16 | - if ( ! $class_name ) { | |
| 17 | - return null; | |
| 18 | - } | |
| 19 | - | |
| 20 | - $registered_names = array_filter( array_column( $registered_styles, 'name' ) ); | |
| 21 | - | |
| 22 | - $prefix = 'is-style-'; | |
| 23 | - $length = strlen( $prefix ); | |
| 24 | - | |
| 25 | - foreach ( explode( ' ', $class_name ) as $class ) { | |
| 26 | - if ( str_starts_with( $class, $prefix ) ) { | |
| 27 | - $variation = substr( $class, $length ); | |
| 28 | - if ( 'default' !== $variation && in_array( $variation, $registered_names, true ) ) { | |
| 29 | - return $variation; | |
| 30 | - } | |
| 31 | - } | |
| 32 | - } | |
| 33 | - | |
| 34 | - return null; | |
| 35 | -} | |
| 36 | - | |
| 37 | -/** | |
| 38 | - * Returns layout definitions, keyed by layout type. | |
| 39 | - * | |
| 40 | - * Provides a common definition of slugs, classnames, base styles, and spacing styles for each layout type. | |
| 41 | - * When making changes or additions to layout definitions, the corresponding JavaScript definitions should | |
| 42 | - * also be updated. | |
| 43 | - * | |
| 44 | - * @return array[] Layout definitions. | |
| 45 | - */ | |
| 46 | -function gutenberg_get_layout_definitions() { | |
| 47 | - $layout_definitions = array( | |
| 48 | - 'default' => array( | |
| 49 | - 'name' => 'default', | |
| 50 | - 'slug' => 'flow', | |
| 51 | - 'className' => 'is-layout-flow', | |
| 52 | - 'baseStyles' => array( | |
| 53 | - array( | |
| 54 | - 'selector' => ' > .alignleft', | |
| 55 | - 'rules' => array( | |
| 56 | - 'float' => 'left', | |
| 57 | - 'margin-inline-start' => '0', | |
| 58 | - 'margin-inline-end' => '2em', | |
| 59 | - ), | |
| 60 | - ), | |
| 61 | - array( | |
| 62 | - 'selector' => ' > .alignright', | |
| 63 | - 'rules' => array( | |
| 64 | - 'float' => 'right', | |
| 65 | - 'margin-inline-start' => '2em', | |
| 66 | - 'margin-inline-end' => '0', | |
| 67 | - ), | |
| 68 | - ), | |
| 69 | - array( | |
| 70 | - 'selector' => ' > .aligncenter', | |
| 71 | - 'rules' => array( | |
| 72 | - 'margin-left' => 'auto !important', | |
| 73 | - 'margin-right' => 'auto !important', | |
| 74 | - ), | |
| 75 | - ), | |
| 76 | - ), | |
| 77 | - 'spacingStyles' => array( | |
| 78 | - array( | |
| 79 | - 'selector' => ' > :first-child', | |
| 80 | - 'rules' => array( | |
| 81 | - 'margin-block-start' => '0', | |
| 82 | - ), | |
| 83 | - ), | |
| 84 | - array( | |
| 85 | - 'selector' => ' > :last-child', | |
| 86 | - 'rules' => array( | |
| 87 | - 'margin-block-end' => '0', | |
| 88 | - ), | |
| 89 | - ), | |
| 90 | - array( | |
| 91 | - 'selector' => ' > *', | |
| 92 | - 'rules' => array( | |
| 93 | - 'margin-block-start' => null, | |
| 94 | - 'margin-block-end' => '0', | |
| 95 | - ), | |
| 96 | - ), | |
| 97 | - ), | |
| 98 | - ), | |
| 99 | - 'constrained' => array( | |
| 100 | - 'name' => 'constrained', | |
| 101 | - 'slug' => 'constrained', | |
| 102 | - 'className' => 'is-layout-constrained', | |
| 103 | - 'baseStyles' => array( | |
| 104 | - array( | |
| 105 | - 'selector' => ' > .alignleft', | |
| 106 | - 'rules' => array( | |
| 107 | - 'float' => 'left', | |
| 108 | - 'margin-inline-start' => '0', | |
| 109 | - 'margin-inline-end' => '2em', | |
| 110 | - ), | |
| 111 | - ), | |
| 112 | - array( | |
| 113 | - 'selector' => ' > .alignright', | |
| 114 | - 'rules' => array( | |
| 115 | - 'float' => 'right', | |
| 116 | - 'margin-inline-start' => '2em', | |
| 117 | - 'margin-inline-end' => '0', | |
| 118 | - ), | |
| 119 | - ), | |
| 120 | - array( | |
| 121 | - 'selector' => ' > .aligncenter', | |
| 122 | - 'rules' => array( | |
| 123 | - 'margin-left' => 'auto !important', | |
| 124 | - 'margin-right' => 'auto !important', | |
| 125 | - ), | |
| 126 | - ), | |
| 127 | - array( | |
| 128 | - 'selector' => ' > :where(:not(.alignleft):not(.alignright):not(.alignfull))', | |
| 129 | - 'rules' => array( | |
| 130 | - 'max-width' => 'var(--wp--style--global--content-size)', | |
| 131 | - 'margin-left' => 'auto !important', | |
| 132 | - 'margin-right' => 'auto !important', | |
| 133 | - ), | |
| 134 | - ), | |
| 135 | - array( | |
| 136 | - 'selector' => ' > .alignwide', | |
| 137 | - 'rules' => array( | |
| 138 | - 'max-width' => 'var(--wp--style--global--wide-size)', | |
| 139 | - ), | |
| 140 | - ), | |
| 141 | - ), | |
| 142 | - 'spacingStyles' => array( | |
| 143 | - array( | |
| 144 | - 'selector' => ' > :first-child', | |
| 145 | - 'rules' => array( | |
| 146 | - 'margin-block-start' => '0', | |
| 147 | - ), | |
| 148 | - ), | |
| 149 | - array( | |
| 150 | - 'selector' => ' > :last-child', | |
| 151 | - 'rules' => array( | |
| 152 | - 'margin-block-end' => '0', | |
| 153 | - ), | |
| 154 | - ), | |
| 155 | - array( | |
| 156 | - 'selector' => ' > *', | |
| 157 | - 'rules' => array( | |
| 158 | - 'margin-block-start' => null, | |
| 159 | - 'margin-block-end' => '0', | |
| 160 | - ), | |
| 161 | - ), | |
| 162 | - ), | |
| 163 | - ), | |
| 164 | - 'flex' => array( | |
| 165 | - 'name' => 'flex', | |
| 166 | - 'slug' => 'flex', | |
| 167 | - 'className' => 'is-layout-flex', | |
| 168 | - 'displayMode' => 'flex', | |
| 169 | - 'baseStyles' => array( | |
| 170 | - array( | |
| 171 | - 'selector' => '', | |
| 172 | - 'rules' => array( | |
| 173 | - 'flex-wrap' => 'wrap', | |
| 174 | - 'align-items' => 'center', | |
| 175 | - ), | |
| 176 | - ), | |
| 177 | - array( | |
| 178 | - 'selector' => ' > :is(*, div)', // :is(*, div) instead of just * increases the specificity by 001. | |
| 179 | - 'rules' => array( | |
| 180 | - 'margin' => '0', | |
| 181 | - ), | |
| 182 | - ), | |
| 183 | - ), | |
| 184 | - 'spacingStyles' => array( | |
| 185 | - array( | |
| 186 | - 'selector' => '', | |
| 187 | - 'rules' => array( | |
| 188 | - 'gap' => null, | |
| 189 | - ), | |
| 190 | - ), | |
| 191 | - ), | |
| 192 | - ), | |
| 193 | - 'grid' => array( | |
| 194 | - 'name' => 'grid', | |
| 195 | - 'slug' => 'grid', | |
| 196 | - 'className' => 'is-layout-grid', | |
| 197 | - 'displayMode' => 'grid', | |
| 198 | - 'baseStyles' => array( | |
| 199 | - array( | |
| 200 | - 'selector' => ' > :is(*, div)', // :is(*, div) instead of just * increases the specificity by 001. | |
| 201 | - 'rules' => array( | |
| 202 | - 'margin' => '0', | |
| 203 | - ), | |
| 204 | - ), | |
| 205 | - ), | |
| 206 | - 'spacingStyles' => array( | |
| 207 | - array( | |
| 208 | - 'selector' => '', | |
| 209 | - 'rules' => array( | |
| 210 | - 'gap' => null, | |
| 211 | - ), | |
| 212 | - ), | |
| 213 | - ), | |
| 214 | - ), | |
| 215 | - ); | |
| 216 | - | |
| 217 | - return $layout_definitions; | |
| 218 | -} | |
| 219 | - | |
| 220 | -/** | |
| 221 | 9 | * Registers the layout block attribute for block types that support it. |
| 222 | 10 | * |
| 223 | 11 | * @param WP_Block_Type $block_type Block Type. |
| 224 | 12 | */ |
| 225 | 13 | function gutenberg_register_layout_support( $block_type ) { |
| 226 | - $support_layout = block_has_support( $block_type, array( 'layout' ), false ) || block_has_support( $block_type, array( '__experimentalLayout' ), false ); | |
| 14 | + $support_layout = gutenberg_block_has_support( $block_type, array( '__experimentalLayout' ), false ); | |
| 227 | 15 | if ( $support_layout ) { |
| 228 | 16 | if ( ! $block_type->attributes ) { |
| 229 | 17 | $block_type->attributes = array(); |
| 230 | 18 | } |
| @@ -237,422 +25,52 @@ | ||
| 237 | 25 | } |
| 238 | 26 | } |
| 239 | 27 | |
| 240 | 28 | /** |
| 241 | - * Returns the child-layout-only subset of a layout object. | |
| 29 | + * Generates the CSS corresponding to the provided layout. | |
| 242 | 30 | * |
| 243 | - * Child layout keys are the ones controlling how the block lays itself out | |
| 244 | - * inside its parent's grid or flex container. | |
| 31 | + * @param string $selector CSS selector. | |
| 32 | + * @param array $layout Layout object. The one that is passed has already checked the existance of default block layout. | |
| 33 | + * @param boolean $has_block_gap_support Whether the theme has support for the block gap. | |
| 245 | 34 | * |
| 246 | - * @param mixed $layout Layout object. | |
| 247 | - * @return array Child layout values, or an empty array. | |
| 35 | + * @return string CSS style. | |
| 248 | 36 | */ |
| 249 | -function gutenberg_get_layout_child_values( $layout ) { | |
| 250 | - if ( ! is_array( $layout ) ) { | |
| 251 | - return array(); | |
| 252 | - } | |
| 37 | +function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support = false ) { | |
| 38 | + $layout_type = isset( $layout['type'] ) ? $layout['type'] : 'default'; | |
| 253 | 39 | |
| 254 | - return array_intersect_key( | |
| 255 | - $layout, | |
| 256 | - array_flip( | |
| 257 | - array( 'selfStretch', 'flexSize', 'columnStart', 'columnSpan', 'rowStart', 'rowSpan' ) | |
| 258 | - ) | |
| 259 | - ); | |
| 260 | -} | |
| 261 | - | |
| 262 | -/** | |
| 263 | - * Returns the container-layout subset of a layout object (everything except child layout keys). | |
| 264 | - * | |
| 265 | - * @param mixed $layout Layout object. | |
| 266 | - * @return array Container layout values, or an empty array. | |
| 267 | - */ | |
| 268 | -function gutenberg_get_layout_container_values( $layout ) { | |
| 269 | - if ( ! is_array( $layout ) ) { | |
| 270 | - return array(); | |
| 271 | - } | |
| 272 | - | |
| 273 | - return array_diff_key( | |
| 274 | - $layout, | |
| 275 | - array_flip( | |
| 276 | - array( 'selfStretch', 'flexSize', 'columnStart', 'columnSpan', 'rowStart', 'rowSpan' ) | |
| 277 | - ) | |
| 278 | - ); | |
| 279 | -} | |
| 280 | - | |
| 281 | -/** | |
| 282 | - * Sanitizes a block gap value before layout style generation. | |
| 283 | - * | |
| 284 | - * Regex for CSS value borrowed from `safecss_filter_attr`, used here to only match | |
| 285 | - * against the value, not the CSS attribute. | |
| 286 | - * | |
| 287 | - * @param string|array|null $gap_value Block gap value. | |
| 288 | - * @return string|array|null Sanitized block gap value. | |
| 289 | - */ | |
| 290 | -function gutenberg_sanitize_block_gap_value( $gap_value ) { | |
| 291 | - if ( is_array( $gap_value ) ) { | |
| 292 | - foreach ( $gap_value as $key => $value ) { | |
| 293 | - $gap_value[ $key ] = $value && preg_match( '%[\\\(&=}]|/\*%', $value ) ? null : $value; | |
| 294 | - } | |
| 295 | - return $gap_value; | |
| 296 | - } | |
| 297 | - | |
| 298 | - return $gap_value && preg_match( '%[\\\(&=}]|/\*%', $gap_value ) ? null : $gap_value; | |
| 299 | -} | |
| 300 | - | |
| 301 | -/** | |
| 302 | - * Returns child layout styles for a block affected by its parent's layout. | |
| 303 | - * | |
| 304 | - * @param string $selector CSS selector. | |
| 305 | - * @param array $child_layout Child layout values. | |
| 306 | - * @param array $parent_layout Parent layout values. | |
| 307 | - * @param array|null $viewport_overrides Optional. Child viewport layout overrides to emit. | |
| 308 | - * @return array Child layout style rules. | |
| 309 | - */ | |
| 310 | -function gutenberg_get_child_layout_style_rules( $selector, $child_layout, $parent_layout = array(), $viewport_overrides = null ) { | |
| 311 | - $base_child_layout = is_array( $child_layout ) ? $child_layout : array(); | |
| 312 | - $viewport_overrides = is_array( $viewport_overrides ) ? $viewport_overrides : null; | |
| 313 | - $child_layout = null === $viewport_overrides ? $base_child_layout : array_replace( $base_child_layout, $viewport_overrides ); | |
| 314 | - $child_layout_declarations = array(); | |
| 315 | - $child_layout_styles = array(); | |
| 316 | - $has_viewport_property_override = static function ( $property ) use ( $viewport_overrides ) { | |
| 317 | - return array_key_exists( $property, $viewport_overrides ); | |
| 318 | - }; | |
| 319 | - | |
| 320 | - $self_stretch = $child_layout['selfStretch'] ?? null; | |
| 321 | - $base_self_stretch = $base_child_layout['selfStretch'] ?? null; | |
| 322 | - | |
| 323 | - /* | |
| 324 | - * These are the serialized `selfStretch` values. `max` used to be called | |
| 325 | - * "Fixed" in the UI, but was renamed and replaced by `fixedNoShrink`. | |
| 326 | - */ | |
| 327 | - $flex_child_layout_values = array( | |
| 328 | - 'fit' => 'fit', | |
| 329 | - 'grow' => 'fill', | |
| 330 | - 'max' => 'fixed', | |
| 331 | - 'fixed' => 'fixedNoShrink', | |
| 332 | - ); | |
| 333 | - $flex_size_values = array( | |
| 334 | - $flex_child_layout_values['max'], | |
| 335 | - $flex_child_layout_values['fixed'], | |
| 336 | - ); | |
| 337 | - | |
| 338 | - if ( null === $viewport_overrides || $has_viewport_property_override( 'selfStretch' ) || $has_viewport_property_override( 'flexSize' ) ) { | |
| 339 | - if ( | |
| 340 | - null !== $viewport_overrides && | |
| 341 | - ( $flex_child_layout_values['fit'] === $self_stretch || $flex_child_layout_values['grow'] === $self_stretch ) && | |
| 342 | - in_array( $base_self_stretch, $flex_size_values, true ) && | |
| 343 | - isset( $base_child_layout['flexSize'] ) | |
| 344 | - ) { | |
| 345 | - $child_layout_declarations['flex-basis'] = 'unset'; | |
| 346 | - if ( $flex_child_layout_values['fixed'] === $base_self_stretch ) { | |
| 347 | - $child_layout_declarations['flex-shrink'] = 'unset'; | |
| 348 | - } | |
| 349 | - } | |
| 350 | - if ( in_array( $self_stretch, $flex_size_values, true ) && isset( $child_layout['flexSize'] ) ) { | |
| 351 | - $child_layout_declarations['flex-basis'] = $child_layout['flexSize']; | |
| 352 | - if ( $flex_child_layout_values['fixed'] === $self_stretch ) { | |
| 353 | - $child_layout_declarations['flex-shrink'] = '0'; | |
| 354 | - } elseif ( null !== $viewport_overrides && $flex_child_layout_values['fixed'] === $base_self_stretch ) { | |
| 355 | - $child_layout_declarations['flex-shrink'] = 'unset'; | |
| 356 | - } | |
| 357 | - $child_layout_declarations['box-sizing'] = 'border-box'; | |
| 358 | - } elseif ( $flex_child_layout_values['grow'] === $self_stretch ) { | |
| 359 | - $child_layout_declarations['flex-grow'] = '1'; | |
| 360 | - } | |
| 361 | - } | |
| 362 | - | |
| 363 | - $column_start = $child_layout['columnStart'] ?? null; | |
| 364 | - $column_span = $child_layout['columnSpan'] ?? null; | |
| 365 | - if ( null === $viewport_overrides || $has_viewport_property_override( 'columnStart' ) || $has_viewport_property_override( 'columnSpan' ) ) { | |
| 366 | - if ( $column_start && $column_span ) { | |
| 367 | - $child_layout_declarations['grid-column'] = "$column_start / span $column_span"; | |
| 368 | - } elseif ( $column_start ) { | |
| 369 | - $child_layout_declarations['grid-column'] = "$column_start"; | |
| 370 | - } elseif ( $column_span ) { | |
| 371 | - $child_layout_declarations['grid-column'] = "span $column_span"; | |
| 372 | - } | |
| 373 | - } | |
| 374 | - | |
| 375 | - $row_start = $child_layout['rowStart'] ?? null; | |
| 376 | - $row_span = $child_layout['rowSpan'] ?? null; | |
| 377 | - if ( null === $viewport_overrides || $has_viewport_property_override( 'rowStart' ) || $has_viewport_property_override( 'rowSpan' ) ) { | |
| 378 | - if ( $row_start && $row_span ) { | |
| 379 | - $child_layout_declarations['grid-row'] = "$row_start / span $row_span"; | |
| 380 | - } elseif ( $row_start ) { | |
| 381 | - $child_layout_declarations['grid-row'] = "$row_start"; | |
| 382 | - } elseif ( $row_span ) { | |
| 383 | - $child_layout_declarations['grid-row'] = "span $row_span"; | |
| 384 | - } | |
| 385 | - } | |
| 386 | - | |
| 387 | - if ( ! empty( $child_layout_declarations ) ) { | |
| 388 | - $child_layout_styles[] = array( | |
| 389 | - 'selector' => $selector, | |
| 390 | - 'declarations' => $child_layout_declarations, | |
| 391 | - ); | |
| 392 | - } | |
| 393 | - | |
| 394 | - $minimum_column_width = $parent_layout['minimumColumnWidth'] ?? null; | |
| 395 | - $column_count = $parent_layout['columnCount'] ?? null; | |
| 396 | - | |
| 397 | - /* | |
| 398 | - * If columnSpan or columnStart is set, and the parent grid is responsive, i.e. if it has a minimumColumnWidth set, | |
| 399 | - * the columnSpan should be removed once the grid is smaller than the span, and columnStart should be removed | |
| 400 | - * once the grid has less columns than the start. | |
| 401 | - * If there's a minimumColumnWidth, the grid is responsive. But if the minimumColumnWidth value wasn't changed, it won't be set. | |
| 402 | - * In that case, if columnCount doesn't exist, we can assume that the grid is responsive. | |
| 403 | - */ | |
| 404 | - if ( null === $viewport_overrides && ( $column_span || $column_start ) && ( $minimum_column_width || ! $column_count ) ) { | |
| 405 | - $column_span_number = floatval( $column_span ); | |
| 406 | - $column_start_number = floatval( $column_start ); | |
| 407 | - $parent_column_width = $minimum_column_width ? $minimum_column_width : '12rem'; | |
| 408 | - $parent_column_value = floatval( $parent_column_width ); | |
| 409 | - $parent_column_unit = explode( $parent_column_value, $parent_column_width ); | |
| 410 | - | |
| 411 | - $num_cols_to_break_at = 2; | |
| 412 | - if ( $column_span_number && $column_start_number ) { | |
| 413 | - $num_cols_to_break_at = $column_start_number + $column_span_number - 1; | |
| 414 | - } elseif ( $column_span_number ) { | |
| 415 | - $num_cols_to_break_at = $column_span_number; | |
| 416 | - } else { | |
| 417 | - $num_cols_to_break_at = $column_start_number; | |
| 418 | - } | |
| 419 | - | |
| 420 | - /* | |
| 421 | - * If there is no unit, the width has somehow been mangled so we reset both unit and value | |
| 422 | - * to defaults. | |
| 423 | - * Additionally, the unit should be one of px, rem or em, so that also needs to be checked. | |
| 424 | - */ | |
| 425 | - if ( count( $parent_column_unit ) <= 1 ) { | |
| 426 | - $parent_column_unit = 'rem'; | |
| 427 | - $parent_column_value = 12; | |
| 428 | - } else { | |
| 429 | - $parent_column_unit = $parent_column_unit[1]; | |
| 430 | - | |
| 431 | - if ( ! in_array( $parent_column_unit, array( 'px', 'rem', 'em' ), true ) ) { | |
| 432 | - $parent_column_unit = 'rem'; | |
| 433 | - } | |
| 434 | - } | |
| 435 | - | |
| 436 | - /* | |
| 437 | - * A default gap value is used for this computation because custom gap values may not be | |
| 438 | - * viable to use in the computation of the container query value. | |
| 439 | - */ | |
| 440 | - $default_gap_value = 'px' === $parent_column_unit ? 24 : 1.5; | |
| 441 | - $container_query_value = $num_cols_to_break_at * $parent_column_value + ( $num_cols_to_break_at - 1 ) * $default_gap_value; | |
| 442 | - $minimum_container_query_value = $parent_column_value * 2 + $default_gap_value - 1; | |
| 443 | - $container_query_value = max( $container_query_value, $minimum_container_query_value ) . $parent_column_unit; | |
| 444 | - // If a span is set we want to preserve it as long as possible, otherwise we just reset the value. | |
| 445 | - $grid_column_value = $column_span && $column_span > 1 ? '1/-1' : 'auto'; | |
| 446 | - | |
| 447 | - $child_layout_styles[] = array( | |
| 448 | - 'rules_group' => "@container (max-width: $container_query_value )", | |
| 449 | - 'selector' => $selector, | |
| 450 | - 'declarations' => array( | |
| 451 | - 'grid-column' => $grid_column_value, | |
| 452 | - 'grid-row' => 'auto', | |
| 453 | - ), | |
| 454 | - ); | |
| 455 | - } | |
| 456 | - | |
| 457 | - return $child_layout_styles; | |
| 458 | -} | |
| 459 | - | |
| 460 | -/** | |
| 461 | - * Generates the CSS corresponding to the provided layout. | |
| 462 | - * | |
| 463 | - * @param string $selector CSS selector. | |
| 464 | - * @param array $layout Layout object. The one that is passed has already checked | |
| 465 | - * the existence of default block layout. | |
| 466 | - * @param bool $has_block_gap_support Optional. Whether the theme has support for the block gap. Default false. | |
| 467 | - * @param string|string[]|null $gap_value Optional. The block gap value to apply. Default null. | |
| 468 | - * @param bool $should_skip_gap_serialization Optional. Whether to skip applying the user-defined value set in the editor. Default false. | |
| 469 | - * @param string|array $fallback_gap_value Optional. The block gap value to apply. If it's an array expected properties are "top" and/or "left". Default '0.5em'. | |
| 470 | - * @param array|null $block_spacing Optional. Custom spacing set on the block. Default null. | |
| 471 | - * @param array $options Optional. Extra options for internal callers. Default empty array. | |
| 472 | - * @return string CSS styles, or empty string. | |
| 473 | - */ | |
| 474 | -function gutenberg_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, $options = array() ) { | |
| 475 | - $base_layout = is_array( $layout ) ? $layout : array(); | |
| 476 | - $viewport_overrides = $options['viewport_overrides'] ?? null; | |
| 477 | - $layout_for_styles = null === $viewport_overrides ? $base_layout : array_replace( $base_layout, $viewport_overrides ); | |
| 478 | - $layout_type = $base_layout['type'] ?? 'default'; | |
| 479 | - $rules_group = $options['rules_group'] ?? null; | |
| 480 | - $has_block_gap_override = ! empty( $options['has_block_gap_override'] ); | |
| 481 | - $should_output_block_gap = null === $viewport_overrides || $has_block_gap_override; | |
| 482 | - $has_viewport_property_override = static function ( $property ) use ( $viewport_overrides ) { | |
| 483 | - return array_key_exists( $property, $viewport_overrides ); | |
| 484 | - }; | |
| 485 | - $layout_styles = array(); | |
| 486 | - | |
| 40 | + $style = ''; | |
| 487 | 41 | if ( 'default' === $layout_type ) { |
| 488 | - if ( $has_block_gap_support && $should_output_block_gap ) { | |
| 489 | - if ( is_array( $gap_value ) ) { | |
| 490 | - $gap_value = $gap_value['top'] ?? null; | |
| 491 | - } | |
| 492 | - if ( null !== $gap_value && ! $should_skip_gap_serialization ) { | |
| 493 | - // Get spacing CSS variable from preset value if provided. | |
| 494 | - if ( is_string( $gap_value ) && str_contains( $gap_value, 'var:preset|spacing|' ) ) { | |
| 495 | - $index_to_splice = strrpos( $gap_value, '|' ) + 1; | |
| 496 | - $slug = _wp_to_kebab_case( substr( $gap_value, $index_to_splice ) ); | |
| 497 | - $gap_value = "var(--wp--preset--spacing--$slug)"; | |
| 498 | - } | |
| 42 | + $content_size = isset( $layout['contentSize'] ) ? $layout['contentSize'] : null; | |
| 43 | + $wide_size = isset( $layout['wideSize'] ) ? $layout['wideSize'] : null; | |
| 499 | 44 | |
| 500 | - array_push( | |
| 501 | - $layout_styles, | |
| 502 | - array( | |
| 503 | - 'selector' => "$selector > *", | |
| 504 | - 'declarations' => array( | |
| 505 | - 'margin-block-start' => '0', | |
| 506 | - 'margin-block-end' => '0', | |
| 507 | - ), | |
| 508 | - ), | |
| 509 | - array( | |
| 510 | - 'selector' => "$selector > * + *", | |
| 511 | - 'declarations' => array( | |
| 512 | - 'margin-block-start' => $gap_value, | |
| 513 | - 'margin-block-end' => '0', | |
| 514 | - ), | |
| 515 | - ) | |
| 516 | - ); | |
| 517 | - } | |
| 518 | - } | |
| 519 | - } elseif ( 'constrained' === $layout_type ) { | |
| 520 | - $content_size = $layout_for_styles['contentSize'] ?? ''; | |
| 521 | - $wide_size = $layout_for_styles['wideSize'] ?? ''; | |
| 522 | - $justify_content = $layout_for_styles['justifyContent'] ?? 'center'; | |
| 523 | - | |
| 524 | 45 | $all_max_width_value = $content_size ? $content_size : $wide_size; |
| 525 | 46 | $wide_max_width_value = $wide_size ? $wide_size : $content_size; |
| 526 | 47 | |
| 527 | 48 | // Make sure there is a single CSS rule, and all tags are stripped for security. |
| 528 | - $all_max_width_value = safecss_filter_attr( explode( ';', $all_max_width_value )[0] ); | |
| 529 | - $wide_max_width_value = safecss_filter_attr( explode( ';', $wide_max_width_value )[0] ); | |
| 49 | + // TODO: Use `safecss_filter_attr` instead - once https://core.trac.wordpress.org/ticket/46197 is patched. | |
| 50 | + $all_max_width_value = wp_strip_all_tags( explode( ';', $all_max_width_value )[0] ); | |
| 51 | + $wide_max_width_value = wp_strip_all_tags( explode( ';', $wide_max_width_value )[0] ); | |
| 530 | 52 | |
| 531 | - $margin_left = 'left' === $justify_content ? '0 !important' : 'auto !important'; | |
| 532 | - $margin_right = 'right' === $justify_content ? '0 !important' : 'auto !important'; | |
| 53 | + $style = ''; | |
| 54 | + if ( $content_size || $wide_size ) { | |
| 55 | + $style = "$selector > * {"; | |
| 56 | + $style .= 'max-width: ' . esc_html( $all_max_width_value ) . ';'; | |
| 57 | + $style .= 'margin-left: auto !important;'; | |
| 58 | + $style .= 'margin-right: auto !important;'; | |
| 59 | + $style .= '}'; | |
| 533 | 60 | |
| 534 | - $has_justify_content_override = null !== $viewport_overrides && $has_viewport_property_override( 'justifyContent' ); | |
| 535 | - $should_output_constrained_sizes = null === $viewport_overrides || $has_viewport_property_override( 'contentSize' ) || $has_viewport_property_override( 'wideSize' ); | |
| 536 | - if ( $should_output_constrained_sizes && ( $content_size || $wide_size ) ) { | |
| 537 | - $content_size_declarations = array( | |
| 538 | - 'max-width' => $all_max_width_value, | |
| 539 | - ); | |
| 540 | - | |
| 541 | - if ( null === $viewport_overrides || $has_justify_content_override ) { | |
| 542 | - $content_size_declarations['margin-left'] = $margin_left; | |
| 543 | - $content_size_declarations['margin-right'] = $margin_right; | |
| 544 | - } | |
| 545 | - | |
| 546 | - array_push( | |
| 547 | - $layout_styles, | |
| 548 | - array( | |
| 549 | - 'selector' => "$selector > :where(:not(.alignleft):not(.alignright):not(.alignfull))", | |
| 550 | - 'declarations' => $content_size_declarations, | |
| 551 | - ), | |
| 552 | - array( | |
| 553 | - 'selector' => "$selector > .alignwide", | |
| 554 | - 'declarations' => array( 'max-width' => $wide_max_width_value ), | |
| 555 | - ), | |
| 556 | - array( | |
| 557 | - 'selector' => "$selector .alignfull", | |
| 558 | - 'declarations' => array( 'max-width' => 'none' ), | |
| 559 | - ) | |
| 560 | - ); | |
| 61 | + $style .= "$selector > .alignwide { max-width: " . esc_html( $wide_max_width_value ) . ';}'; | |
| 62 | + $style .= "$selector .alignfull { max-width: none; }"; | |
| 561 | 63 | } |
| 562 | 64 | |
| 563 | - if ( null === $viewport_overrides && isset( $block_spacing ) ) { | |
| 564 | - $block_spacing_values = gutenberg_style_engine_get_styles( | |
| 565 | - array( | |
| 566 | - 'spacing' => $block_spacing, | |
| 567 | - ) | |
| 568 | - ); | |
| 569 | - | |
| 570 | - /* | |
| 571 | - * Handle negative margins for alignfull children of blocks with custom padding set. | |
| 572 | - * They're added separately because padding might only be set on one side. | |
| 573 | - */ | |
| 574 | - if ( isset( $block_spacing_values['declarations']['padding-right'] ) ) { | |
| 575 | - $padding_right = $block_spacing_values['declarations']['padding-right']; | |
| 576 | - // Add unit if 0. | |
| 577 | - if ( '0' === $padding_right ) { | |
| 578 | - $padding_right = '0px'; | |
| 579 | - } | |
| 580 | - $layout_styles[] = array( | |
| 581 | - 'selector' => "$selector > .alignfull", | |
| 582 | - 'declarations' => array( 'margin-right' => "calc($padding_right * -1)" ), | |
| 583 | - ); | |
| 584 | - } | |
| 585 | - if ( isset( $block_spacing_values['declarations']['padding-left'] ) ) { | |
| 586 | - $padding_left = $block_spacing_values['declarations']['padding-left']; | |
| 587 | - // Add unit if 0. | |
| 588 | - if ( '0' === $padding_left ) { | |
| 589 | - $padding_left = '0px'; | |
| 590 | - } | |
| 591 | - $layout_styles[] = array( | |
| 592 | - 'selector' => "$selector > .alignfull", | |
| 593 | - 'declarations' => array( 'margin-left' => "calc($padding_left * -1)" ), | |
| 594 | - ); | |
| 595 | - } | |
| 65 | + $style .= "$selector .alignleft { float: left; margin-right: 2em; }"; | |
| 66 | + $style .= "$selector .alignright { float: right; margin-left: 2em; }"; | |
| 67 | + if ( $has_block_gap_support ) { | |
| 68 | + $style .= "$selector > * { margin-top: 0; margin-bottom: 0; }"; | |
| 69 | + $style .= "$selector > * + * { margin-top: var( --wp--style--block-gap ); margin-bottom: 0; }"; | |
| 596 | 70 | } |
| 597 | - | |
| 598 | - if ( $has_justify_content_override && ! $should_output_constrained_sizes ) { | |
| 599 | - $layout_styles[] = array( | |
| 600 | - 'selector' => "$selector > :where(:not(.alignleft):not(.alignright):not(.alignfull))", | |
| 601 | - 'declarations' => array( | |
| 602 | - 'margin-left' => $margin_left, | |
| 603 | - 'margin-right' => $margin_right, | |
| 604 | - ), | |
| 605 | - ); | |
| 606 | - } elseif ( null === $viewport_overrides ) { | |
| 607 | - if ( 'left' === $justify_content ) { | |
| 608 | - $layout_styles[] = array( | |
| 609 | - 'selector' => "$selector > :where(:not(.alignleft):not(.alignright):not(.alignfull))", | |
| 610 | - 'declarations' => array( 'margin-left' => '0 !important' ), | |
| 611 | - ); | |
| 612 | - } | |
| 613 | - | |
| 614 | - if ( 'right' === $justify_content ) { | |
| 615 | - $layout_styles[] = array( | |
| 616 | - 'selector' => "$selector > :where(:not(.alignleft):not(.alignright):not(.alignfull))", | |
| 617 | - 'declarations' => array( 'margin-right' => '0 !important' ), | |
| 618 | - ); | |
| 619 | - } | |
| 620 | - } | |
| 621 | - | |
| 622 | - if ( $has_block_gap_support && $should_output_block_gap ) { | |
| 623 | - if ( is_array( $gap_value ) ) { | |
| 624 | - $gap_value = $gap_value['top'] ?? null; | |
| 625 | - } | |
| 626 | - if ( null !== $gap_value && ! $should_skip_gap_serialization ) { | |
| 627 | - // Get spacing CSS variable from preset value if provided. | |
| 628 | - if ( is_string( $gap_value ) && str_contains( $gap_value, 'var:preset|spacing|' ) ) { | |
| 629 | - $index_to_splice = strrpos( $gap_value, '|' ) + 1; | |
| 630 | - $slug = _wp_to_kebab_case( substr( $gap_value, $index_to_splice ) ); | |
| 631 | - $gap_value = "var(--wp--preset--spacing--$slug)"; | |
| 632 | - } | |
| 633 | - | |
| 634 | - array_push( | |
| 635 | - $layout_styles, | |
| 636 | - array( | |
| 637 | - 'selector' => "$selector > *", | |
| 638 | - 'declarations' => array( | |
| 639 | - 'margin-block-start' => '0', | |
| 640 | - 'margin-block-end' => '0', | |
| 641 | - ), | |
| 642 | - ), | |
| 643 | - array( | |
| 644 | - 'selector' => "$selector > * + *", | |
| 645 | - 'declarations' => array( | |
| 646 | - 'margin-block-start' => $gap_value, | |
| 647 | - 'margin-block-end' => '0', | |
| 648 | - ), | |
| 649 | - ) | |
| 650 | - ); | |
| 651 | - } | |
| 652 | - } | |
| 653 | 71 | } elseif ( 'flex' === $layout_type ) { |
| 654 | - $layout_orientation = $layout_for_styles['orientation'] ?? 'horizontal'; | |
| 72 | + $layout_orientation = isset( $layout['orientation'] ) ? $layout['orientation'] : 'horizontal'; | |
| 655 | 73 | |
| 656 | 74 | $justify_content_options = array( |
| 657 | 75 | 'left' => 'flex-start', |
| 658 | 76 | 'right' => 'flex-end', |
| @@ -658,263 +76,65 @@ | ||
| 658 | 76 | 'right' => 'flex-end', |
| 659 | 77 | 'center' => 'center', |
| 660 | 78 | ); |
| 661 | 79 | |
| 662 | - $vertical_alignment_options = array( | |
| 663 | - 'top' => 'flex-start', | |
| 664 | - 'center' => 'center', | |
| 665 | - 'bottom' => 'flex-end', | |
| 666 | - ); | |
| 667 | - | |
| 668 | 80 | if ( 'horizontal' === $layout_orientation ) { |
| 669 | - $justify_content_options += array( 'space-between' => 'space-between' ); | |
| 670 | - $vertical_alignment_options += array( 'stretch' => 'stretch' ); | |
| 671 | - } else { | |
| 672 | - $justify_content_options += array( 'stretch' => 'stretch' ); | |
| 673 | - $vertical_alignment_options += array( 'space-between' => 'space-between' ); | |
| 81 | + $justify_content_options += array( 'space-between' => 'space-between' ); | |
| 674 | 82 | } |
| 675 | 83 | |
| 676 | - $should_output_flex_wrap = null === $viewport_overrides || $has_viewport_property_override( 'flexWrap' ); | |
| 677 | - $should_output_flex_orientation = null === $viewport_overrides || $has_viewport_property_override( 'orientation' ); | |
| 678 | - $should_output_flex_justification = null === $viewport_overrides || $has_viewport_property_override( 'justifyContent' ) || $has_viewport_property_override( 'orientation' ); | |
| 679 | - $should_output_flex_alignment = null === $viewport_overrides || $has_viewport_property_override( 'verticalAlignment' ) || $has_viewport_property_override( 'orientation' ); | |
| 84 | + $flex_wrap_options = array( 'wrap', 'nowrap' ); | |
| 85 | + $flex_wrap = ! empty( $layout['flexWrap'] ) && in_array( $layout['flexWrap'], $flex_wrap_options, true ) ? | |
| 86 | + $layout['flexWrap'] : | |
| 87 | + 'wrap'; | |
| 680 | 88 | |
| 681 | - if ( $should_output_flex_wrap && ! empty( $layout_for_styles['flexWrap'] ) && 'nowrap' === $layout_for_styles['flexWrap'] ) { | |
| 682 | - $layout_styles[] = array( | |
| 683 | - 'selector' => $selector, | |
| 684 | - 'declarations' => array( 'flex-wrap' => 'nowrap' ), | |
| 685 | - ); | |
| 89 | + $style = "$selector {"; | |
| 90 | + $style .= 'display: flex;'; | |
| 91 | + if ( $has_block_gap_support ) { | |
| 92 | + $style .= 'gap: var( --wp--style--block-gap, 0.5em );'; | |
| 93 | + } else { | |
| 94 | + $style .= 'gap: 0.5em;'; | |
| 686 | 95 | } |
| 687 | - | |
| 688 | - if ( $has_block_gap_support && $should_output_block_gap && isset( $gap_value ) ) { | |
| 689 | - $combined_gap_value = ''; | |
| 690 | - $gap_sides = is_array( $gap_value ) ? array( 'top', 'left' ) : array( 'top' ); | |
| 691 | - | |
| 692 | - foreach ( $gap_sides as $gap_side ) { | |
| 693 | - $process_value = $gap_value; | |
| 694 | - if ( is_array( $gap_value ) ) { | |
| 695 | - if ( is_array( $fallback_gap_value ) ) { | |
| 696 | - $fallback_value = $fallback_gap_value[ $gap_side ] ?? reset( $fallback_gap_value ); | |
| 697 | - } else { | |
| 698 | - $fallback_value = $fallback_gap_value; | |
| 699 | - } | |
| 700 | - $process_value = $gap_value[ $gap_side ] ?? $fallback_value; | |
| 701 | - } | |
| 702 | - // Get spacing CSS variable from preset value if provided. | |
| 703 | - if ( is_string( $process_value ) && str_contains( $process_value, 'var:preset|spacing|' ) ) { | |
| 704 | - $index_to_splice = strrpos( $process_value, '|' ) + 1; | |
| 705 | - $slug = _wp_to_kebab_case( substr( $process_value, $index_to_splice ) ); | |
| 706 | - $process_value = "var(--wp--preset--spacing--$slug)"; | |
| 707 | - } | |
| 708 | - $combined_gap_value .= "$process_value "; | |
| 709 | - } | |
| 710 | - $gap_value = trim( $combined_gap_value ); | |
| 711 | - | |
| 712 | - if ( null !== $gap_value && ! $should_skip_gap_serialization ) { | |
| 713 | - $layout_styles[] = array( | |
| 714 | - 'selector' => $selector, | |
| 715 | - 'declarations' => array( 'gap' => $gap_value ), | |
| 716 | - ); | |
| 717 | - } | |
| 718 | - } | |
| 719 | - | |
| 96 | + $style .= "flex-wrap: $flex_wrap;"; | |
| 720 | 97 | if ( 'horizontal' === $layout_orientation ) { |
| 721 | - /* | |
| 98 | + $style .= 'align-items: center;'; | |
| 99 | + /** | |
| 722 | 100 | * Add this style only if is not empty for backwards compatibility, |
| 723 | 101 | * since we intend to convert blocks that had flex layout implemented |
| 724 | 102 | * by custom css. |
| 725 | 103 | */ |
| 726 | - if ( $should_output_flex_justification && ! empty( $layout_for_styles['justifyContent'] ) && array_key_exists( $layout_for_styles['justifyContent'], $justify_content_options ) ) { | |
| 727 | - $layout_styles[] = array( | |
| 728 | - 'selector' => $selector, | |
| 729 | - 'declarations' => array( 'justify-content' => $justify_content_options[ $layout_for_styles['justifyContent'] ] ), | |
| 730 | - ); | |
| 104 | + if ( ! empty( $layout['justifyContent'] ) && array_key_exists( $layout['justifyContent'], $justify_content_options ) ) { | |
| 105 | + $style .= "justify-content: {$justify_content_options[ $layout['justifyContent'] ]};"; | |
| 106 | + if ( ! empty( $layout['setCascadingProperties'] ) && $layout['setCascadingProperties'] ) { | |
| 107 | + // --layout-justification-setting allows children to inherit the value regardless or row or column direction. | |
| 108 | + $style .= "--layout-justification-setting: {$justify_content_options[ $layout['justifyContent'] ]};"; | |
| 109 | + $style .= '--layout-direction: row;'; | |
| 110 | + $style .= "--layout-wrap: $flex_wrap;"; | |
| 111 | + $style .= "--layout-justify: {$justify_content_options[ $layout['justifyContent'] ]};"; | |
| 112 | + $style .= '--layout-align: center;'; | |
| 113 | + } | |
| 731 | 114 | } |
| 732 | - | |
| 733 | - if ( $should_output_flex_alignment && ! empty( $layout_for_styles['verticalAlignment'] ) && array_key_exists( $layout_for_styles['verticalAlignment'], $vertical_alignment_options ) ) { | |
| 734 | - $layout_styles[] = array( | |
| 735 | - 'selector' => $selector, | |
| 736 | - 'declarations' => array( 'align-items' => $vertical_alignment_options[ $layout_for_styles['verticalAlignment'] ] ), | |
| 737 | - ); | |
| 738 | - } | |
| 739 | 115 | } else { |
| 740 | - if ( $should_output_flex_orientation ) { | |
| 741 | - $layout_styles[] = array( | |
| 742 | - 'selector' => $selector, | |
| 743 | - 'declarations' => array( 'flex-direction' => 'column' ), | |
| 744 | - ); | |
| 745 | - } | |
| 746 | - if ( $should_output_flex_justification && ! empty( $layout_for_styles['justifyContent'] ) && array_key_exists( $layout_for_styles['justifyContent'], $justify_content_options ) ) { | |
| 747 | - $layout_styles[] = array( | |
| 748 | - 'selector' => $selector, | |
| 749 | - 'declarations' => array( 'align-items' => $justify_content_options[ $layout_for_styles['justifyContent'] ] ), | |
| 750 | - ); | |
| 751 | - } elseif ( $should_output_flex_justification ) { | |
| 752 | - $layout_styles[] = array( | |
| 753 | - 'selector' => $selector, | |
| 754 | - 'declarations' => array( 'align-items' => 'flex-start' ), | |
| 755 | - ); | |
| 756 | - } | |
| 757 | - if ( $should_output_flex_alignment && ! empty( $layout_for_styles['verticalAlignment'] ) && array_key_exists( $layout_for_styles['verticalAlignment'], $vertical_alignment_options ) ) { | |
| 758 | - $layout_styles[] = array( | |
| 759 | - 'selector' => $selector, | |
| 760 | - 'declarations' => array( 'justify-content' => $vertical_alignment_options[ $layout_for_styles['verticalAlignment'] ] ), | |
| 761 | - ); | |
| 762 | - } | |
| 763 | - } | |
| 764 | - } elseif ( 'grid' === $layout_type ) { | |
| 765 | - /* | |
| 766 | - * If the gap value is an array, we use the "left" value because it represents the vertical gap, which | |
| 767 | - * is the relevant one for computation of responsive grid columns. | |
| 768 | - */ | |
| 769 | - if ( is_array( $fallback_gap_value ) ) { | |
| 770 | - $responsive_gap_value = $fallback_gap_value['left'] ?? reset( $fallback_gap_value ); | |
| 771 | - } else { | |
| 772 | - $responsive_gap_value = $fallback_gap_value; | |
| 773 | - } | |
| 774 | - | |
| 775 | - if ( $has_block_gap_support && isset( $gap_value ) ) { | |
| 776 | - $combined_gap_value = ''; | |
| 777 | - $gap_sides = is_array( $gap_value ) ? array( 'top', 'left' ) : array( 'top' ); | |
| 778 | - | |
| 779 | - foreach ( $gap_sides as $gap_side ) { | |
| 780 | - $process_value = $gap_value; | |
| 781 | - if ( is_array( $gap_value ) ) { | |
| 782 | - if ( is_array( $fallback_gap_value ) ) { | |
| 783 | - $fallback_value = $fallback_gap_value[ $gap_side ] ?? reset( $fallback_gap_value ); | |
| 784 | - } else { | |
| 785 | - $fallback_value = $fallback_gap_value; | |
| 786 | - } | |
| 787 | - $process_value = $gap_value[ $gap_side ] ?? $fallback_value; | |
| 116 | + $style .= 'flex-direction: column;'; | |
| 117 | + if ( ! empty( $layout['justifyContent'] ) && array_key_exists( $layout['justifyContent'], $justify_content_options ) ) { | |
| 118 | + $style .= "align-items: {$justify_content_options[ $layout['justifyContent'] ]};"; | |
| 119 | + if ( ! empty( $layout['setCascadingProperties'] ) && $layout['setCascadingProperties'] ) { | |
| 120 | + // --layout-justification-setting allows children to inherit the value regardless or row or column direction. | |
| 121 | + $style .= "--layout-justification-setting: {$justify_content_options[ $layout['justifyContent'] ]};"; | |
| 122 | + $style .= '--layout-direction: column;'; | |
| 123 | + $style .= '--layout-justify: initial;'; | |
| 124 | + $style .= "--layout-align: {$justify_content_options[ $layout['justifyContent'] ]};"; | |
| 788 | 125 | } |
| 789 | - // Get spacing CSS variable from preset value if provided. | |
| 790 | - if ( is_string( $process_value ) && str_contains( $process_value, 'var:preset|spacing|' ) ) { | |
| 791 | - $index_to_splice = strrpos( $process_value, '|' ) + 1; | |
| 792 | - $slug = _wp_to_kebab_case( substr( $process_value, $index_to_splice ) ); | |
| 793 | - $process_value = "var(--wp--preset--spacing--$slug)"; | |
| 794 | - } | |
| 795 | - $combined_gap_value .= "$process_value "; | |
| 796 | 126 | } |
| 797 | - $gap_value = trim( $combined_gap_value ); | |
| 798 | - $responsive_gap_value = $gap_value; | |
| 799 | 127 | } |
| 128 | + $style .= '}'; | |
| 800 | 129 | |
| 801 | - // Ensure 0 values have a unit so they work in calc(). | |
| 802 | - if ( '0' === $responsive_gap_value || 0 === $responsive_gap_value ) { | |
| 803 | - $responsive_gap_value = '0px'; | |
| 804 | - } | |
| 805 | - | |
| 806 | - $should_output_grid_columns = null === $viewport_overrides || $has_viewport_property_override( 'minimumColumnWidth' ) || $has_viewport_property_override( 'columnCount' ); | |
| 807 | - $uses_gap_in_grid_columns = ! empty( $layout_for_styles['columnCount'] ) && ! empty( $layout_for_styles['minimumColumnWidth'] ); | |
| 808 | - if ( $has_block_gap_override && $uses_gap_in_grid_columns ) { | |
| 809 | - $should_output_grid_columns = true; | |
| 810 | - } | |
| 811 | - | |
| 812 | - $should_output_grid_rows = ( null === $viewport_overrides || $has_viewport_property_override( 'rowCount' ) ) && ! empty( $layout_for_styles['columnCount'] ) && ! empty( $layout_for_styles['rowCount'] ); | |
| 813 | - $grid_declarations = array(); | |
| 814 | - | |
| 815 | - if ( $should_output_grid_columns && ! empty( $layout_for_styles['columnCount'] ) && ! empty( $layout_for_styles['minimumColumnWidth'] ) ) { | |
| 816 | - $max_value = 'max(min(' . $layout_for_styles['minimumColumnWidth'] . ', 100%), (100% - (' . $responsive_gap_value . ' * (' . $layout_for_styles['columnCount'] . ' - 1))) /' . $layout_for_styles['columnCount'] . ')'; | |
| 817 | - $grid_declarations['grid-template-columns'] = 'repeat(auto-fill, minmax(' . $max_value . ', 1fr))'; | |
| 818 | - } elseif ( $should_output_grid_columns && ! empty( $layout_for_styles['columnCount'] ) ) { | |
| 819 | - $grid_declarations['grid-template-columns'] = 'repeat(' . $layout_for_styles['columnCount'] . ', minmax(0, 1fr))'; | |
| 820 | - } elseif ( $should_output_grid_columns ) { | |
| 821 | - $minimum_column_width = ! empty( $layout_for_styles['minimumColumnWidth'] ) ? $layout_for_styles['minimumColumnWidth'] : '12rem'; | |
| 822 | - $grid_declarations['grid-template-columns'] = 'repeat(auto-fill, minmax(min(' . $minimum_column_width . ', 100%), 1fr))'; | |
| 823 | - } | |
| 824 | - | |
| 825 | - if ( ! empty( $grid_declarations ) ) { | |
| 826 | - $base_has_container_type = empty( $base_layout['columnCount'] ) || ( ! empty( $base_layout['columnCount'] ) && ! empty( $base_layout['minimumColumnWidth'] ) ); | |
| 827 | - if ( empty( $layout_for_styles['columnCount'] ) || ! empty( $layout_for_styles['minimumColumnWidth'] ) ) { | |
| 828 | - if ( null === $viewport_overrides || ! $base_has_container_type ) { | |
| 829 | - $grid_declarations['container-type'] = 'inline-size'; | |
| 830 | - } | |
| 831 | - } | |
| 832 | - $layout_styles[] = array( | |
| 833 | - 'selector' => $selector, | |
| 834 | - 'declarations' => $grid_declarations, | |
| 835 | - ); | |
| 836 | - } | |
| 837 | - | |
| 838 | - if ( $should_output_grid_rows ) { | |
| 839 | - $layout_styles[] = array( | |
| 840 | - 'selector' => $selector, | |
| 841 | - 'declarations' => array( 'grid-template-rows' => 'repeat(' . $layout_for_styles['rowCount'] . ', minmax(1rem, auto))' ), | |
| 842 | - ); | |
| 843 | - } | |
| 844 | - | |
| 845 | - if ( $has_block_gap_support && $should_output_block_gap && null !== $gap_value && ! $should_skip_gap_serialization ) { | |
| 846 | - $layout_styles[] = array( | |
| 847 | - 'selector' => $selector, | |
| 848 | - 'declarations' => array( 'gap' => $gap_value ), | |
| 849 | - ); | |
| 850 | - } | |
| 130 | + $style .= "$selector > * { margin: 0; }"; | |
| 851 | 131 | } |
| 852 | 132 | |
| 853 | - if ( ! empty( $layout_styles ) ) { | |
| 854 | - if ( ! empty( $rules_group ) ) { | |
| 855 | - foreach ( $layout_styles as $index => $layout_style ) { | |
| 856 | - $layout_styles[ $index ]['rules_group'] = $rules_group; | |
| 857 | - } | |
| 858 | - } | |
| 859 | - | |
| 860 | - /* | |
| 861 | - * Add to the style engine store to enqueue and render layout styles. | |
| 862 | - * Return compiled layout styles to retain backwards compatibility. | |
| 863 | - * Since https://github.com/WordPress/gutenberg/pull/42452, | |
| 864 | - * wp_enqueue_block_support_styles is no longer called in this block supports file. | |
| 865 | - */ | |
| 866 | - return gutenberg_style_engine_get_stylesheet_from_css_rules( | |
| 867 | - $layout_styles, | |
| 868 | - array( | |
| 869 | - 'context' => 'block-supports', | |
| 870 | - 'prettify' => false, | |
| 871 | - ) | |
| 872 | - ); | |
| 873 | - } | |
| 874 | - | |
| 875 | - return ''; | |
| 133 | + return $style; | |
| 876 | 134 | } |
| 877 | 135 | |
| 878 | 136 | /** |
| 879 | - * Generates an incremental ID that is independent per each different prefix. | |
| 880 | - * | |
| 881 | - * It is similar to `wp_unique_id`, but each prefix has it's own internal ID | |
| 882 | - * counter to make each prefix independent from each other. The ID starts at 1 | |
| 883 | - * and increments on each call. The returned value is not universally unique, | |
| 884 | - * but it is unique across the life of the PHP process and it's stable per | |
| 885 | - * prefix. | |
| 886 | - * | |
| 887 | - * @param string $prefix Prefix for the returned ID. | |
| 888 | - * @return string Incremental ID per prefix. | |
| 889 | - */ | |
| 890 | -function gutenberg_incremental_id_per_prefix( $prefix = '' ) { | |
| 891 | - static $id_counters = array(); | |
| 892 | - if ( ! array_key_exists( $prefix, $id_counters ) ) { | |
| 893 | - $id_counters[ $prefix ] = 0; | |
| 894 | - } | |
| 895 | - return $prefix . (string) ++$id_counters[ $prefix ]; | |
| 896 | -} | |
| 897 | - | |
| 898 | -/** | |
| 899 | - * Generates a unique ID based on the structure and values of a given array. | |
| 900 | - * | |
| 901 | - * This function serializes the array into a JSON string and generates a hash | |
| 902 | - * that serves as a unique identifier. Optionally, a prefix can be added to | |
| 903 | - * the generated ID for context or categorization. | |
| 904 | - * | |
| 905 | - * @param array $data The input array to generate an ID from. | |
| 906 | - * @param string $prefix Optional. A prefix to prepend to the generated ID. Default ''. | |
| 907 | - * | |
| 908 | - * @return string The generated unique ID for the array. | |
| 909 | - */ | |
| 910 | -function gutenberg_unique_id_from_values( array $data, string $prefix = '' ): string { | |
| 911 | - $serialized = wp_json_encode( $data ); | |
| 912 | - $hash = substr( md5( $serialized ), 0, 8 ); | |
| 913 | - return $prefix . $hash; | |
| 914 | -} | |
| 915 | - | |
| 916 | -/** | |
| 917 | 137 | * Renders the layout config to the block wrapper. |
| 918 | 138 | * |
| 919 | 139 | * @param string $block_content Rendered block content. |
| 920 | 140 | * @param array $block Block object. |
| @@ -920,472 +140,51 @@ | ||
| 920 | 140 | * @param array $block Block object. |
| 921 | 141 | * @return string Filtered block content. |
| 922 | 142 | */ |
| 923 | 143 | function gutenberg_render_layout_support_flag( $block_content, $block ) { |
| 924 | - static $global_styles = null; | |
| 144 | + $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] ); | |
| 145 | + $support_layout = gutenberg_block_has_support( $block_type, array( '__experimentalLayout' ), false ); | |
| 925 | 146 | |
| 926 | - $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] ); | |
| 927 | - $block_supports_layout = block_has_support( $block_type, array( 'layout' ), false ) || block_has_support( $block_type, array( '__experimentalLayout' ), false ); | |
| 928 | - $style_attr = gutenberg_resolve_style_state_aliases( | |
| 929 | - $block['attrs']['style'] ?? array(), | |
| 930 | - $block['blockName'] | |
| 931 | - ); | |
| 932 | - // If there is any value in style -> layout, the block has a child layout. | |
| 933 | - $child_layout = $style_attr['layout'] ?? null; | |
| 934 | - | |
| 935 | - // Collect responsive viewport child layout overrides so that a block with | |
| 936 | - // only responsive child layout (no base child layout) is still processed. | |
| 937 | - $viewport_child_layouts = array(); | |
| 938 | - foreach ( WP_Theme_JSON_Gutenberg::RESPONSIVE_BREAKPOINTS as $breakpoint => $media_query ) { | |
| 939 | - $viewport_child = gutenberg_get_layout_child_values( $style_attr[ $breakpoint ]['layout'] ?? null ); | |
| 940 | - if ( ! empty( $viewport_child ) ) { | |
| 941 | - $viewport_child_layouts[ $breakpoint ] = array( | |
| 942 | - 'media_query' => $media_query, | |
| 943 | - 'child_layout' => $viewport_child, | |
| 944 | - ); | |
| 945 | - } | |
| 946 | - } | |
| 947 | - | |
| 948 | - if ( ! $block_supports_layout && ! $child_layout && empty( $viewport_child_layouts ) ) { | |
| 147 | + if ( ! $support_layout ) { | |
| 949 | 148 | return $block_content; |
| 950 | 149 | } |
| 951 | 150 | |
| 952 | - $outer_class_names = array(); | |
| 953 | - | |
| 954 | - // Child layout specific logic. | |
| 955 | - if ( $child_layout || ! empty( $viewport_child_layouts ) ) { | |
| 956 | - $base_child_layout = gutenberg_get_layout_child_values( $child_layout ); | |
| 957 | - $parent_layout = $block['parentLayout'] ?? array(); | |
| 958 | - | |
| 959 | - /* | |
| 960 | - * Generates a unique class for child block layout styles. | |
| 961 | - * | |
| 962 | - * To ensure consistent class generation across different page renders, | |
| 963 | - * only properties that affect layout styling are used. These properties | |
| 964 | - * come from `$block['attrs']['style']['layout']`, viewport overrides in | |
| 965 | - * `$block['attrs']['style'][$breakpoint]['layout']`, and | |
| 966 | - * `$block['parentLayout']`. | |
| 967 | - * | |
| 968 | - * As long as these properties coincide, the generated class will be the same. | |
| 969 | - */ | |
| 970 | - $container_content_hash_input = array( | |
| 971 | - 'layout' => $base_child_layout, | |
| 972 | - 'parentLayout' => array_intersect_key( | |
| 973 | - $parent_layout, | |
| 974 | - array_flip( array( 'minimumColumnWidth', 'columnCount' ) ) | |
| 975 | - ), | |
| 976 | - ); | |
| 977 | - foreach ( $viewport_child_layouts as $breakpoint => $viewport_data ) { | |
| 978 | - $container_content_hash_input[ $breakpoint ] = $viewport_data['child_layout']; | |
| 151 | + $block_gap = wp_get_global_settings( array( 'spacing', 'blockGap' ) ); | |
| 152 | + $default_layout = wp_get_global_settings( array( 'layout' ) ); | |
| 153 | + $has_block_gap_support = isset( $block_gap ) ? null !== $block_gap : false; | |
| 154 | + $default_block_layout = _wp_array_get( $block_type->supports, array( '__experimentalLayout', 'default' ), array() ); | |
| 155 | + $used_layout = isset( $block['attrs']['layout'] ) ? $block['attrs']['layout'] : $default_block_layout; | |
| 156 | + if ( isset( $used_layout['inherit'] ) && $used_layout['inherit'] ) { | |
| 157 | + if ( ! $default_layout ) { | |
| 158 | + return $block_content; | |
| 979 | 159 | } |
| 980 | - $container_content_class = gutenberg_unique_id_from_values( | |
| 981 | - $container_content_hash_input, | |
| 982 | - 'wp-container-content-' | |
| 983 | - ); | |
| 984 | - | |
| 985 | - $child_layout_styles = gutenberg_get_child_layout_style_rules( | |
| 986 | - ".$container_content_class", | |
| 987 | - $base_child_layout, | |
| 988 | - $parent_layout | |
| 989 | - ); | |
| 990 | - | |
| 991 | - // Emit responsive child layout CSS using the same container-content class | |
| 992 | - // so that base and responsive child layout share the exact same selector. | |
| 993 | - foreach ( $viewport_child_layouts as $viewport_data ) { | |
| 994 | - $viewport_child_styles = gutenberg_get_child_layout_style_rules( | |
| 995 | - ".$container_content_class", | |
| 996 | - $base_child_layout, | |
| 997 | - $parent_layout, | |
| 998 | - $viewport_data['child_layout'] | |
| 999 | - ); | |
| 1000 | - foreach ( $viewport_child_styles as $index => $rule ) { | |
| 1001 | - $viewport_child_styles[ $index ]['rules_group'] = $viewport_data['media_query']; | |
| 1002 | - } | |
| 1003 | - | |
| 1004 | - $child_layout_styles = array_merge( $child_layout_styles, $viewport_child_styles ); | |
| 1005 | - } | |
| 1006 | - | |
| 1007 | - /* | |
| 1008 | - * Add to the style engine store to enqueue and render layout styles. | |
| 1009 | - * Return styles here just to check if any exist. | |
| 1010 | - */ | |
| 1011 | - $child_css = gutenberg_style_engine_get_stylesheet_from_css_rules( | |
| 1012 | - $child_layout_styles, | |
| 1013 | - array( | |
| 1014 | - 'context' => 'block-supports', | |
| 1015 | - 'prettify' => false, | |
| 1016 | - ) | |
| 1017 | - ); | |
| 1018 | - | |
| 1019 | - if ( $child_css ) { | |
| 1020 | - $outer_class_names[] = $container_content_class; | |
| 1021 | - } | |
| 160 | + $used_layout = $default_layout; | |
| 1022 | 161 | } |
| 1023 | 162 | |
| 1024 | - // Prep the processor for modifying the block output. | |
| 1025 | - $processor = new WP_HTML_Tag_Processor( $block_content ); | |
| 163 | + $id = uniqid(); | |
| 164 | + $style = gutenberg_get_layout_style( ".wp-container-$id", $used_layout, $has_block_gap_support ); | |
| 165 | + // This assumes the hook only applies to blocks with a single wrapper. | |
| 166 | + // I think this is a reasonable limitation for that particular hook. | |
| 167 | + $content = preg_replace( | |
| 168 | + '/' . preg_quote( 'class="', '/' ) . '/', | |
| 169 | + 'class="wp-container-' . $id . ' ', | |
| 170 | + $block_content, | |
| 171 | + 1 | |
| 172 | + ); | |
| 1026 | 173 | |
| 1027 | - // Having no tags implies there are no tags onto which to add class names. | |
| 1028 | - if ( ! $processor->next_tag() ) { | |
| 1029 | - return $block_content; | |
| 1030 | - } | |
| 1031 | - | |
| 1032 | - /* | |
| 1033 | - * A block may not support layout but still be affected by a parent block's layout. | |
| 1034 | - * | |
| 1035 | - * In these cases add the appropriate class names and then return early; there's | |
| 1036 | - * no need to investigate on this block whether additional layout constraints apply. | |
| 1037 | - */ | |
| 1038 | - if ( ! $block_supports_layout && ! empty( $outer_class_names ) ) { | |
| 1039 | - foreach ( $outer_class_names as $class_name ) { | |
| 1040 | - $processor->add_class( $class_name ); | |
| 174 | + // Ideally styles should be loaded in the head, but blocks may be parsed | |
| 175 | + // after that, so loading in the footer for now. | |
| 176 | + // See https://core.trac.wordpress.org/ticket/53494. | |
| 177 | + add_action( | |
| 178 | + 'wp_footer', | |
| 179 | + function () use ( $style ) { | |
| 180 | + echo '<style>' . $style . '</style>'; | |
| 1041 | 181 | } |
| 1042 | - return $processor->get_updated_html(); | |
| 1043 | - } elseif ( ! $block_supports_layout ) { | |
| 1044 | - // Ensure layout classnames are not injected if there is no layout support. | |
| 1045 | - return $block_content; | |
| 1046 | - } | |
| 182 | + ); | |
| 1047 | 183 | |
| 1048 | - $global_settings = gutenberg_get_global_settings(); | |
| 1049 | - $fallback_layout = $block_type->supports['layout']['default'] ?? array(); | |
| 1050 | - if ( empty( $fallback_layout ) ) { | |
| 1051 | - $fallback_layout = $block_type->supports['__experimentalLayout']['default'] ?? array(); | |
| 1052 | - } | |
| 1053 | - $used_layout = $block['attrs']['layout'] ?? $fallback_layout; | |
| 1054 | - | |
| 1055 | - $class_names = array(); | |
| 1056 | - $layout_definitions = gutenberg_get_layout_definitions(); | |
| 1057 | - | |
| 1058 | - // Set the correct layout type for blocks using legacy content width. | |
| 1059 | - if ( isset( $used_layout['inherit'] ) && $used_layout['inherit'] || isset( $used_layout['contentSize'] ) && $used_layout['contentSize'] ) { | |
| 1060 | - $used_layout['type'] = 'constrained'; | |
| 1061 | - } | |
| 1062 | - | |
| 1063 | - $root_padding_aware_alignments = $global_settings['useRootPaddingAwareAlignments'] ?? false; | |
| 1064 | - | |
| 1065 | - if ( $root_padding_aware_alignments && isset( $used_layout['type'] ) && 'constrained' === $used_layout['type'] ) { | |
| 1066 | - $class_names[] = 'has-global-padding'; | |
| 1067 | - } | |
| 1068 | - | |
| 1069 | - /* | |
| 1070 | - * The following section was added to reintroduce a small set of layout classnames that were | |
| 1071 | - * removed in the 5.9 release (https://github.com/WordPress/gutenberg/issues/38719). It is | |
| 1072 | - * not intended to provide an extended set of classes to match all block layout attributes | |
| 1073 | - * here. | |
| 1074 | - */ | |
| 1075 | - if ( ! empty( $block['attrs']['layout']['orientation'] ) ) { | |
| 1076 | - $class_names[] = 'is-' . sanitize_title( $block['attrs']['layout']['orientation'] ); | |
| 1077 | - } | |
| 1078 | - | |
| 1079 | - if ( ! empty( $block['attrs']['layout']['justifyContent'] ) ) { | |
| 1080 | - $class_names[] = 'is-content-justification-' . sanitize_title( $block['attrs']['layout']['justifyContent'] ); | |
| 1081 | - } | |
| 1082 | - | |
| 1083 | - if ( ! empty( $block['attrs']['layout']['flexWrap'] ) && 'nowrap' === $block['attrs']['layout']['flexWrap'] ) { | |
| 1084 | - $class_names[] = 'is-nowrap'; | |
| 1085 | - } | |
| 1086 | - | |
| 1087 | - // Get classname for layout type. | |
| 1088 | - if ( isset( $used_layout['type'] ) ) { | |
| 1089 | - $layout_classname = $layout_definitions[ $used_layout['type'] ]['className'] ?? ''; | |
| 1090 | - } else { | |
| 1091 | - $layout_classname = $layout_definitions['default']['className'] ?? ''; | |
| 1092 | - } | |
| 1093 | - | |
| 1094 | - if ( $layout_classname && is_string( $layout_classname ) ) { | |
| 1095 | - $class_names[] = sanitize_title( $layout_classname ); | |
| 1096 | - } | |
| 1097 | - | |
| 1098 | - /* | |
| 1099 | - * Only generate Layout styles if the theme has not opted-out. | |
| 1100 | - * Attribute-based Layout classnames are output in all cases. | |
| 1101 | - */ | |
| 1102 | - if ( ! current_theme_supports( 'disable-layout-styles' ) ) { | |
| 1103 | - | |
| 1104 | - $gap_value = gutenberg_sanitize_block_gap_value( $block['attrs']['style']['spacing']['blockGap'] ?? null ); | |
| 1105 | - | |
| 1106 | - $fallback_gap_value = $block_type->supports['spacing']['blockGap']['__experimentalDefault'] ?? '0.5em'; | |
| 1107 | - $block_spacing = $block['attrs']['style']['spacing'] ?? null; | |
| 1108 | - | |
| 1109 | - /* | |
| 1110 | - * If a block's block.json skips serialization for spacing or spacing.blockGap, | |
| 1111 | - * don't apply the user-defined value to the styles. | |
| 1112 | - */ | |
| 1113 | - $should_skip_gap_serialization = wp_should_skip_block_supports_serialization( $block_type, 'spacing', 'blockGap' ); | |
| 1114 | - | |
| 1115 | - $block_gap = $global_settings['spacing']['blockGap'] ?? null; | |
| 1116 | - $has_block_gap_support = isset( $block_gap ); | |
| 1117 | - | |
| 1118 | - // Get default blockGap value from global styles for use in layouts like grid. | |
| 1119 | - // Check style variation first, then block-specific styles, then fall back to root styles. | |
| 1120 | - $block_name = $block['blockName'] ?? ''; | |
| 1121 | - if ( null === $global_styles ) { | |
| 1122 | - $global_styles = gutenberg_get_global_styles(); | |
| 1123 | - } | |
| 1124 | - | |
| 1125 | - // Check if the block has an active style variation with a blockGap value. | |
| 1126 | - // Only check the registry if the className contains a variation class to avoid unnecessary lookups. | |
| 1127 | - $variation_block_gap_value = null; | |
| 1128 | - $block_class_name = $block['attrs']['className'] ?? ''; | |
| 1129 | - if ( $block_class_name && str_contains( $block_class_name, 'is-style-' ) && $block_name ) { | |
| 1130 | - $styles_registry = WP_Block_Styles_Registry::get_instance(); | |
| 1131 | - $registered_styles = $styles_registry->get_registered_styles_for_block( $block_name ); | |
| 1132 | - $variation_name = gutenberg_get_block_style_variation_name_from_registered_style( $block_class_name, $registered_styles ); | |
| 1133 | - if ( $variation_name ) { | |
| 1134 | - $variation_block_gap_value = $global_styles['blocks'][ $block_name ]['variations'][ $variation_name ]['spacing']['blockGap'] ?? null; | |
| 1135 | - } | |
| 1136 | - } | |
| 1137 | - | |
| 1138 | - $global_block_gap_value = $variation_block_gap_value ?? $global_styles['blocks'][ $block_name ]['spacing']['blockGap'] ?? $global_styles['spacing']['blockGap'] ?? null; | |
| 1139 | - | |
| 1140 | - if ( null !== $global_block_gap_value ) { | |
| 1141 | - $fallback_gap_value = $global_block_gap_value; | |
| 1142 | - } | |
| 1143 | - | |
| 1144 | - /* | |
| 1145 | - * We generate a unique ID based on all the data required to obtain the | |
| 1146 | - * corresponding layout style. This way, the CSS class names keep the same | |
| 1147 | - * even for different blocks with the same layout definition. We need this to | |
| 1148 | - * make the CSS class names stable across paginations for features like the | |
| 1149 | - * enhanced pagination of the Query block. | |
| 1150 | - */ | |
| 1151 | - $container_class_hash_input = array( | |
| 1152 | - $used_layout, | |
| 1153 | - $has_block_gap_support, | |
| 1154 | - $gap_value, | |
| 1155 | - $should_skip_gap_serialization, | |
| 1156 | - $fallback_gap_value, | |
| 1157 | - $block_spacing, | |
| 1158 | - ); | |
| 1159 | - | |
| 1160 | - foreach ( array_keys( WP_Theme_JSON_Gutenberg::RESPONSIVE_BREAKPOINTS ) as $breakpoint ) { | |
| 1161 | - $viewport_style = $style_attr[ $breakpoint ] ?? null; | |
| 1162 | - if ( ! is_array( $viewport_style ) ) { | |
| 1163 | - continue; | |
| 1164 | - } | |
| 1165 | - | |
| 1166 | - $viewport_container_layout = gutenberg_get_layout_container_values( $viewport_style['layout'] ?? null ); | |
| 1167 | - if ( ! empty( $viewport_container_layout ) ) { | |
| 1168 | - $container_class_hash_input[] = array( | |
| 1169 | - 'breakpoint' => $breakpoint, | |
| 1170 | - 'layout' => $viewport_container_layout, | |
| 1171 | - ); | |
| 1172 | - } | |
| 1173 | - | |
| 1174 | - if ( isset( $viewport_style['spacing']['blockGap'] ) ) { | |
| 1175 | - $container_class_hash_input[] = array( | |
| 1176 | - 'breakpoint' => $breakpoint, | |
| 1177 | - 'blockGap' => gutenberg_sanitize_block_gap_value( $viewport_style['spacing']['blockGap'] ), | |
| 1178 | - ); | |
| 1179 | - } | |
| 1180 | - } | |
| 1181 | - | |
| 1182 | - $container_class = gutenberg_unique_id_from_values( | |
| 1183 | - $container_class_hash_input, | |
| 1184 | - 'wp-container-' . sanitize_title( $block['blockName'] ) . '-is-layout-' | |
| 1185 | - ); | |
| 1186 | - | |
| 1187 | - $style = gutenberg_get_layout_style( | |
| 1188 | - ".$container_class", | |
| 1189 | - $used_layout, | |
| 1190 | - $has_block_gap_support, | |
| 1191 | - $gap_value, | |
| 1192 | - $should_skip_gap_serialization, | |
| 1193 | - $fallback_gap_value, | |
| 1194 | - $block_spacing | |
| 1195 | - ); | |
| 1196 | - | |
| 1197 | - // Only add container class and enqueue block support styles if unique styles were generated. | |
| 1198 | - if ( ! empty( $style ) ) { | |
| 1199 | - $class_names[] = $container_class; | |
| 1200 | - } | |
| 1201 | - | |
| 1202 | - /* | |
| 1203 | - * Emit responsive container layout styles using the same $container_class | |
| 1204 | - * selector as the base layout so they target the inner block wrapper. | |
| 1205 | - */ | |
| 1206 | - foreach ( WP_Theme_JSON_Gutenberg::RESPONSIVE_BREAKPOINTS as $breakpoint => $media_query ) { | |
| 1207 | - $viewport_style = $style_attr[ $breakpoint ] ?? null; | |
| 1208 | - if ( ! is_array( $viewport_style ) ) { | |
| 1209 | - continue; | |
| 1210 | - } | |
| 1211 | - | |
| 1212 | - $viewport_container_layout = gutenberg_get_layout_container_values( $viewport_style['layout'] ?? null ); | |
| 1213 | - $has_viewport_layout = ! empty( $viewport_container_layout ); | |
| 1214 | - $has_viewport_block_gap = isset( $viewport_style['spacing']['blockGap'] ); | |
| 1215 | - | |
| 1216 | - if ( ! $has_viewport_layout && ! $has_viewport_block_gap ) { | |
| 1217 | - continue; | |
| 1218 | - } | |
| 1219 | - | |
| 1220 | - $viewport_gap_value = $has_viewport_block_gap | |
| 1221 | - ? gutenberg_sanitize_block_gap_value( $viewport_style['spacing']['blockGap'] ) | |
| 1222 | - : $gap_value; | |
| 1223 | - $viewport_block_spacing = is_array( $viewport_style['spacing'] ?? null ) | |
| 1224 | - ? array_replace( is_array( $block_spacing ) ? $block_spacing : array(), $viewport_style['spacing'] ) | |
| 1225 | - : $block_spacing; | |
| 1226 | - | |
| 1227 | - $viewport_styles = gutenberg_get_layout_style( | |
| 1228 | - ".$container_class", | |
| 1229 | - $used_layout, | |
| 1230 | - $has_block_gap_support, | |
| 1231 | - $viewport_gap_value, | |
| 1232 | - $should_skip_gap_serialization, | |
| 1233 | - $fallback_gap_value, | |
| 1234 | - $viewport_block_spacing, | |
| 1235 | - array( | |
| 1236 | - 'rules_group' => $media_query, | |
| 1237 | - 'viewport_overrides' => $viewport_container_layout, | |
| 1238 | - 'has_block_gap_override' => $has_viewport_block_gap, | |
| 1239 | - ) | |
| 1240 | - ); | |
| 1241 | - | |
| 1242 | - if ( ! empty( $viewport_styles ) && ! in_array( $container_class, $class_names, true ) ) { | |
| 1243 | - $class_names[] = $container_class; | |
| 1244 | - } | |
| 1245 | - } | |
| 1246 | - } | |
| 1247 | - | |
| 1248 | - // Add combined layout and block classname for global styles to hook onto. | |
| 1249 | - $split_block_name = explode( '/', $block['blockName'] ); | |
| 1250 | - $full_block_name = 'core' === $split_block_name[0] ? end( $split_block_name ) : implode( '-', $split_block_name ); | |
| 1251 | - $class_names[] = 'wp-block-' . $full_block_name . '-' . $layout_classname; | |
| 1252 | - | |
| 1253 | - // Add classes to the outermost HTML tag if necessary. | |
| 1254 | - if ( ! empty( $outer_class_names ) ) { | |
| 1255 | - foreach ( $outer_class_names as $outer_class_name ) { | |
| 1256 | - $processor->add_class( $outer_class_name ); | |
| 1257 | - } | |
| 1258 | - } | |
| 1259 | - | |
| 1260 | - /* | |
| 1261 | - * Attempts to refer to the inner-block wrapping element by its class attribute. | |
| 1262 | - * | |
| 1263 | - * When examining a block's inner content, if a block has inner blocks, then | |
| 1264 | - * the first content item will likely be a text (HTML) chunk immediately | |
| 1265 | - * preceding the inner blocks. The last HTML tag in that chunk would then be | |
| 1266 | - * an opening tag for an element that wraps the inner blocks. | |
| 1267 | - * | |
| 1268 | - * There's no reliable way to associate this wrapper in $block_content because | |
| 1269 | - * it may have changed during the rendering pipeline (as inner contents is | |
| 1270 | - * provided before rendering) and through previous filters. In many cases, | |
| 1271 | - * however, the `class` attribute will be a good-enough identifier, so this | |
| 1272 | - * code finds the last tag in that chunk and stores the `class` attribute | |
| 1273 | - * so that it can be used later when working through the rendered block output | |
| 1274 | - * to identify the wrapping element and add the remaining class names to it. | |
| 1275 | - * | |
| 1276 | - * It's also possible that no inner block wrapper even exists. If that's the | |
| 1277 | - * case this code could apply the class names to an invalid element. | |
| 1278 | - * | |
| 1279 | - * Example: | |
| 1280 | - * | |
| 1281 | - * $block['innerBlocks'] = array( $list_item ); | |
| 1282 | - * $block['innerContent'] = array( '<ul class="list-wrapper is-unordered">', null, '</ul>' ); | |
| 1283 | - * | |
| 1284 | - * // After rendering, the initial contents may have been modified by other renderers or filters. | |
| 1285 | - * $block_content = <<<HTML | |
| 1286 | - * <figure> | |
| 1287 | - * <ul class="annotated-list list-wrapper is-unordered"> | |
| 1288 | - * <li>Code</li> | |
| 1289 | - * </ul><figcaption>It's a list!</figcaption> | |
| 1290 | - * </figure> | |
| 1291 | - * HTML; | |
| 1292 | - * | |
| 1293 | - * Although it is possible that the original block-wrapper classes are changed in $block_content | |
| 1294 | - * from how they appear in $block['innerContent'], it's likely that the original class attributes | |
| 1295 | - * are still present in the wrapper as they are in this example. Frequently, additional classes | |
| 1296 | - * will also be present; rarely should classes be removed. | |
| 1297 | - * | |
| 1298 | - * @todo Find a better way to match the first inner block. If it's possible to identify where the | |
| 1299 | - * first inner block starts, then it will be possible to find the last tag before it starts | |
| 1300 | - * and then that tag, if an opening tag, can be solidly identified as a wrapping element. | |
| 1301 | - * Can some unique value or class or ID be added to the inner blocks when they process | |
| 1302 | - * so that they can be extracted here safely without guessing? Can the block rendering function | |
| 1303 | - * return information about where the rendered inner blocks start? | |
| 1304 | - * | |
| 1305 | - * @var string|null | |
| 1306 | - */ | |
| 1307 | - $inner_block_wrapper_classes = null; | |
| 1308 | - $first_chunk = $block['innerContent'][0] ?? null; | |
| 1309 | - if ( is_string( $first_chunk ) && count( $block['innerContent'] ) > 1 ) { | |
| 1310 | - $first_chunk_processor = new WP_HTML_Tag_Processor( $first_chunk ); | |
| 1311 | - /* | |
| 1312 | - * Use a stack to track open elements as tags are visited. Void elements | |
| 1313 | - * (those without a matching closing tag) are excluded so they don't | |
| 1314 | - * accumulate on the stack. At the end of the chunk, every element still | |
| 1315 | - * on the stack is unclosed — meaning its closing tag lives in a later | |
| 1316 | - * innerContent entry alongside the inner blocks, which makes it the | |
| 1317 | - * inner-block container. Elements that open and close within this chunk | |
| 1318 | - * are siblings that precede the inner blocks and should be ignored. | |
| 1319 | - * The last unclosed element with a class attribute is the best candidate | |
| 1320 | - * for the inner-block wrapper. | |
| 1321 | - */ | |
| 1322 | - $tag_stack = array(); | |
| 1323 | - while ( $first_chunk_processor->next_tag( array( 'tag_closers' => 'visit' ) ) ) { | |
| 1324 | - if ( $first_chunk_processor->is_tag_closer() ) { | |
| 1325 | - array_pop( $tag_stack ); | |
| 1326 | - } elseif ( ! WP_HTML_Processor::is_void( $first_chunk_processor->get_tag() ) ) { | |
| 1327 | - $tag_stack[] = $first_chunk_processor->get_attribute( 'class' ); | |
| 1328 | - } | |
| 1329 | - } | |
| 1330 | - foreach ( array_reverse( $tag_stack ) as $class_attribute ) { | |
| 1331 | - if ( is_string( $class_attribute ) && ! empty( $class_attribute ) ) { | |
| 1332 | - $inner_block_wrapper_classes = $class_attribute; | |
| 1333 | - break; | |
| 1334 | - } | |
| 1335 | - } | |
| 1336 | - } | |
| 1337 | - | |
| 1338 | - /* | |
| 1339 | - * If necessary, advance to what is likely to be an inner block wrapper tag. | |
| 1340 | - * | |
| 1341 | - * This advances until it finds the first tag containing the original class | |
| 1342 | - * attribute from above. If none is found it will scan to the end of the block | |
| 1343 | - * and fail to add any class names. | |
| 1344 | - * | |
| 1345 | - * If there is no block wrapper it won't advance at all, in which case the | |
| 1346 | - * class names will be added to the first and outermost tag of the block. | |
| 1347 | - * For cases where this outermost tag is the only tag surrounding inner | |
| 1348 | - * blocks then the outer wrapper and inner wrapper are the same. | |
| 1349 | - */ | |
| 1350 | - do { | |
| 1351 | - if ( ! $inner_block_wrapper_classes ) { | |
| 1352 | - break; | |
| 1353 | - } | |
| 1354 | - | |
| 1355 | - $class_attribute = $processor->get_attribute( 'class' ); | |
| 1356 | - if ( is_string( $class_attribute ) && str_contains( $class_attribute, $inner_block_wrapper_classes ) ) { | |
| 1357 | - break; | |
| 1358 | - } | |
| 1359 | - } while ( $processor->next_tag() ); | |
| 1360 | - | |
| 1361 | - // Add the remaining class names. | |
| 1362 | - foreach ( $class_names as $class_name ) { | |
| 1363 | - $processor->add_class( $class_name ); | |
| 1364 | - } | |
| 1365 | - | |
| 1366 | - return $processor->get_updated_html(); | |
| 184 | + return $content; | |
| 1367 | 185 | } |
| 1368 | 186 | |
| 1369 | -/* | |
| 1370 | - * Add a `render_block_data` filter to fetch the parent block layout data. | |
| 1371 | - */ | |
| 1372 | -add_filter( | |
| 1373 | - 'render_block_data', | |
| 1374 | - function ( $parsed_block, $source_block, $parent_block ) { | |
| 1375 | - /* | |
| 1376 | - * Check if the parent block exists and if it has a layout attribute. | |
| 1377 | - * If it does, add the parent layout to the parsed block. | |
| 1378 | - */ | |
| 1379 | - if ( $parent_block && isset( $parent_block->parsed_block['attrs']['layout'] ) ) { | |
| 1380 | - $parsed_block['parentLayout'] = $parent_block->parsed_block['attrs']['layout']; | |
| 1381 | - } | |
| 1382 | - return $parsed_block; | |
| 1383 | - }, | |
| 1384 | - 10, | |
| 1385 | - 3 | |
| 1386 | -); | |
| 1387 | - | |
| 1388 | 187 | // Register the block support. (overrides core one). |
| 1389 | 188 | WP_Block_Supports::get_instance()->register( |
| 1390 | 189 | 'layout', |
| 1391 | 190 | array( |
| @@ -1391,9 +190,8 @@ | ||
| 1391 | 190 | array( |
| 1392 | 191 | 'register_attribute' => 'gutenberg_register_layout_support', |
| 1393 | 192 | ) |
| 1394 | 193 | ); |
| 1395 | - | |
| 1396 | 194 | if ( function_exists( 'wp_render_layout_support_flag' ) ) { |
| 1397 | 195 | remove_filter( 'render_block', 'wp_render_layout_support_flag' ); |
| 1398 | 196 | } |
| 1399 | 197 | add_filter( 'render_block', 'gutenberg_render_layout_support_flag', 10, 2 ); |
| @@ -1407,129 +205,36 @@ | ||
| 1407 | 205 | * @param array $block Block object. |
| 1408 | 206 | * @return string Filtered block content. |
| 1409 | 207 | */ |
| 1410 | 208 | function gutenberg_restore_group_inner_container( $block_content, $block ) { |
| 1411 | - $tag_name = $block['attrs']['tagName'] ?? 'div'; | |
| 209 | + $tag_name = isset( $block['attrs']['tagName'] ) ? $block['attrs']['tagName'] : 'div'; | |
| 1412 | 210 | $group_with_inner_container_regex = sprintf( |
| 1413 | 211 | '/(^\s*<%1$s\b[^>]*wp-block-group(\s|")[^>]*>)(\s*<div\b[^>]*wp-block-group__inner-container(\s|")[^>]*>)((.|\S|\s)*)/U', |
| 1414 | 212 | preg_quote( $tag_name, '/' ) |
| 1415 | 213 | ); |
| 1416 | 214 | if ( |
| 1417 | - wp_theme_has_theme_json() || | |
| 215 | + 'core/group' !== $block['blockName'] || | |
| 216 | + WP_Theme_JSON_Resolver_Gutenberg::theme_has_support() || | |
| 1418 | 217 | 1 === preg_match( $group_with_inner_container_regex, $block_content ) || |
| 1419 | - ( isset( $block['attrs']['layout']['type'] ) && ( 'flex' === $block['attrs']['layout']['type'] || 'grid' === $block['attrs']['layout']['type'] ) ) | |
| 218 | + ( isset( $block['attrs']['layout']['type'] ) && 'default' !== $block['attrs']['layout']['type'] ) | |
| 1420 | 219 | ) { |
| 1421 | 220 | return $block_content; |
| 1422 | 221 | } |
| 1423 | 222 | |
| 1424 | - /* | |
| 1425 | - * This filter runs after the layout classnames have been added to the block, so they | |
| 1426 | - * have to be removed from the outer wrapper and then added to the inner. | |
| 1427 | - */ | |
| 1428 | - $layout_classes = array(); | |
| 1429 | - $processor = new WP_HTML_Tag_Processor( $block_content ); | |
| 1430 | - | |
| 1431 | - if ( $processor->next_tag( array( 'class_name' => 'wp-block-group' ) ) ) { | |
| 1432 | - foreach ( $processor->class_list() as $class_name ) { | |
| 1433 | - if ( str_contains( $class_name, 'layout' ) ) { | |
| 1434 | - array_push( $layout_classes, $class_name ); | |
| 1435 | - $processor->remove_class( $class_name ); | |
| 1436 | - } | |
| 1437 | - } | |
| 1438 | - } | |
| 1439 | - | |
| 1440 | - $content_without_layout_classes = $processor->get_updated_html(); | |
| 1441 | - $replace_regex = sprintf( | |
| 223 | + $replace_regex = sprintf( | |
| 1442 | 224 | '/(^\s*<%1$s\b[^>]*wp-block-group[^>]*>)(.*)(<\/%1$s>\s*$)/ms', |
| 1443 | 225 | preg_quote( $tag_name, '/' ) |
| 1444 | 226 | ); |
| 1445 | - $updated_content = preg_replace_callback( | |
| 227 | + $updated_content = preg_replace_callback( | |
| 1446 | 228 | $replace_regex, |
| 1447 | - static function ( $matches ) { | |
| 229 | + function( $matches ) { | |
| 1448 | 230 | return $matches[1] . '<div class="wp-block-group__inner-container">' . $matches[2] . '</div>' . $matches[3]; |
| 1449 | 231 | }, |
| 1450 | - $content_without_layout_classes | |
| 232 | + $block_content | |
| 1451 | 233 | ); |
| 1452 | - | |
| 1453 | - // Add layout classes to inner wrapper. | |
| 1454 | - if ( ! empty( $layout_classes ) ) { | |
| 1455 | - $processor = new WP_HTML_Tag_Processor( $updated_content ); | |
| 1456 | - if ( $processor->next_tag( array( 'class_name' => 'wp-block-group__inner-container' ) ) ) { | |
| 1457 | - foreach ( $layout_classes as $class_name ) { | |
| 1458 | - $processor->add_class( $class_name ); | |
| 1459 | - } | |
| 1460 | - } | |
| 1461 | - $updated_content = $processor->get_updated_html(); | |
| 1462 | - } | |
| 1463 | - | |
| 1464 | 234 | return $updated_content; |
| 1465 | 235 | } |
| 1466 | 236 | |
| 1467 | 237 | if ( function_exists( 'wp_restore_group_inner_container' ) ) { |
| 1468 | - remove_filter( 'render_block', 'wp_restore_group_inner_container', 10 ); | |
| 1469 | - remove_filter( 'render_block_core/group', 'wp_restore_group_inner_container', 10 ); | |
| 238 | + remove_filter( 'render_block', 'wp_restore_group_inner_container', 10, 2 ); | |
| 1470 | 239 | } |
| 1471 | -add_filter( 'render_block_core/group', 'gutenberg_restore_group_inner_container', 10, 2 ); | |
| 1472 | - | |
| 1473 | -/** | |
| 1474 | - * For themes without theme.json file, make sure | |
| 1475 | - * to restore the outer div for the aligned image block | |
| 1476 | - * to avoid breaking styles relying on that div. | |
| 1477 | - * | |
| 1478 | - * @param string $block_content Rendered block content. | |
| 1479 | - * @param array $block Block object. | |
| 1480 | - * @return string Filtered block content. | |
| 1481 | - */ | |
| 1482 | -function gutenberg_restore_image_outer_container( $block_content, $block ) { | |
| 1483 | - if ( wp_theme_has_theme_json() ) { | |
| 1484 | - return $block_content; | |
| 1485 | - } | |
| 1486 | - | |
| 1487 | - $figure_processor = new WP_HTML_Tag_Processor( $block_content ); | |
| 1488 | - if ( | |
| 1489 | - ! $figure_processor->next_tag( 'FIGURE' ) || | |
| 1490 | - ! $figure_processor->has_class( 'wp-block-image' ) || | |
| 1491 | - ! ( | |
| 1492 | - $figure_processor->has_class( 'alignleft' ) || | |
| 1493 | - $figure_processor->has_class( 'aligncenter' ) || | |
| 1494 | - $figure_processor->has_class( 'alignright' ) | |
| 1495 | - ) | |
| 1496 | - ) { | |
| 1497 | - return $block_content; | |
| 1498 | - } | |
| 1499 | - | |
| 1500 | - /* | |
| 1501 | - * The next section of code wraps the existing figure in a new DIV element. | |
| 1502 | - * While doing it, it needs to transfer the layout and the additional CSS | |
| 1503 | - * class names from the original figure upward to the wrapper. | |
| 1504 | - * | |
| 1505 | - * Example: | |
| 1506 | - * | |
| 1507 | - * // From this… | |
| 1508 | - * <!-- wp:image {"className":"hires"} --> | |
| 1509 | - * <figure class="wp-block-image wide hires">… | |
| 1510 | - * | |
| 1511 | - * // To this… | |
| 1512 | - * <div class="wp-block-image hires"><figure class="wide">… | |
| 1513 | - */ | |
| 1514 | - $wrapper_processor = new WP_HTML_Tag_Processor( '<div>' ); | |
| 1515 | - $wrapper_processor->next_token(); | |
| 1516 | - $wrapper_processor->set_attribute( | |
| 1517 | - 'class', | |
| 1518 | - is_string( $block['attrs']['className'] ?? null ) | |
| 1519 | - ? "wp-block-image {$block['attrs']['className']}" | |
| 1520 | - : 'wp-block-image' | |
| 1521 | - ); | |
| 1522 | - | |
| 1523 | - // And remove them from the existing content; it has been transferred upward. | |
| 1524 | - $figure_processor->remove_class( 'wp-block-image' ); | |
| 1525 | - foreach ( $wrapper_processor->class_list() as $class_name ) { | |
| 1526 | - $figure_processor->remove_class( $class_name ); | |
| 1527 | - } | |
| 1528 | - | |
| 1529 | - return "{$wrapper_processor->get_updated_html()}{$figure_processor->get_updated_html()}</div>"; | |
| 1530 | -} | |
| 1531 | - | |
| 1532 | -if ( function_exists( 'wp_restore_image_outer_container' ) ) { | |
| 1533 | - remove_filter( 'render_block_core/image', 'wp_restore_image_outer_container', 10 ); | |
| 1534 | -} | |
| 1535 | -add_filter( 'render_block_core/image', 'gutenberg_restore_image_outer_container', 10, 2 ); | |
| 240 | +add_filter( 'render_block', 'gutenberg_restore_group_inner_container', 10, 2 ); | |