| @@ -320,11 +320,11 @@ | ||
| 320 | 320 | |
| 321 | 321 | /** |
| 322 | 322 | * Returns child layout styles for a block affected by its parent's layout. |
| 323 | 323 | * |
| 324 | - * @param string $selector CSS selector. | |
| 325 | - * @param array $child_layout Child layout values. | |
| 326 | - * @param array $parent_layout Parent layout values. | |
| 324 | + * @param string $selector CSS selector. | |
| 325 | + * @param array $child_layout Child layout values. | |
| 326 | + * @param array $parent_layout Parent layout values. | |
| 327 | 327 | * @param array|null $viewport_overrides Optional. Child viewport layout overrides to emit. |
| 328 | 328 | * @return array Child layout style rules. |
| 329 | 329 | */ |
| 330 | 330 | function gutenberg_get_child_layout_style_rules( $selector, $child_layout, $parent_layout = array(), $viewport_overrides = null ) { |
| @@ -379,10 +379,19 @@ | ||
| 379 | 379 | $child_layout_declarations['flex-grow'] = '1'; |
| 380 | 380 | } |
| 381 | 381 | } |
| 382 | 382 | |
| 383 | - $column_start = $child_layout['columnStart'] ?? null; | |
| 384 | - $column_span = $child_layout['columnSpan'] ?? null; | |
| 383 | + /* | |
| 384 | + * Grid line numbers and spans are whole numbers. The editor stores them as numbers, but | |
| 385 | + * content saved by WordPress 6.3 to 6.6 stored them as numeric strings, and that | |
| 386 | + * migration only runs when a block is parsed in JavaScript, so the front end still sees | |
| 387 | + * strings. Accept any numeric value and cast it, and treat anything else as absent | |
| 388 | + * because it can't render as valid CSS. | |
| 389 | + */ | |
| 390 | + $column_start_attr = $child_layout['columnStart'] ?? null; | |
| 391 | + $column_start = is_numeric( $column_start_attr ) ? (int) $column_start_attr : null; | |
| 392 | + $column_span_attr = $child_layout['columnSpan'] ?? null; | |
| 393 | + $column_span = is_numeric( $column_span_attr ) ? (int) $column_span_attr : null; | |
| 385 | 394 | if ( null === $viewport_overrides || $has_viewport_property_override( 'columnStart' ) || $has_viewport_property_override( 'columnSpan' ) ) { |
| 386 | 395 | if ( $column_start && $column_span ) { |
| 387 | 396 | $child_layout_declarations['grid-column'] = "$column_start / span $column_span"; |
| 388 | 397 | } elseif ( $column_start ) { |
| @@ -391,10 +400,12 @@ | ||
| 391 | 400 | $child_layout_declarations['grid-column'] = "span $column_span"; |
| 392 | 401 | } |
| 393 | 402 | } |
| 394 | 403 | |
| 395 | - $row_start = $child_layout['rowStart'] ?? null; | |
| 396 | - $row_span = $child_layout['rowSpan'] ?? null; | |
| 404 | + $row_start_attr = $child_layout['rowStart'] ?? null; | |
| 405 | + $row_start = is_numeric( $row_start_attr ) ? (int) $row_start_attr : null; | |
| 406 | + $row_span_attr = $child_layout['rowSpan'] ?? null; | |
| 407 | + $row_span = is_numeric( $row_span_attr ) ? (int) $row_span_attr : null; | |
| 397 | 408 | if ( null === $viewport_overrides || $has_viewport_property_override( 'rowStart' ) || $has_viewport_property_override( 'rowSpan' ) ) { |
| 398 | 409 | if ( $row_start && $row_span ) { |
| 399 | 410 | $child_layout_declarations['grid-row'] = "$row_start / span $row_span"; |
| 400 | 411 | } elseif ( $row_start ) { |
| @@ -778,8 +789,19 @@ | ||
| 778 | 789 | $flex_vertical_alignment = $layout_for_styles['verticalAlignment'] ?? null; |
| 779 | 790 | |
| 780 | 791 | if ( 'horizontal' === $layout_orientation ) { |
| 781 | 792 | /* |
| 793 | + * `row` is the flex default, so the base layout never declares it. A viewport | |
| 794 | + * override that switches a vertical base layout to horizontal has to declare | |
| 795 | + * it explicitly, otherwise the base `flex-direction: column` keeps applying. | |
| 796 | + */ | |
| 797 | + if ( null !== $viewport_overrides && $has_viewport_property_override( 'orientation' ) ) { | |
| 798 | + $layout_styles[] = array( | |
| 799 | + 'selector' => $selector, | |
| 800 | + 'declarations' => array( 'flex-direction' => 'row' ), | |
| 801 | + ); | |
| 802 | + } | |
| 803 | + /* | |
| 782 | 804 | * Add this style only if is not empty for backwards compatibility, |
| 783 | 805 | * since we intend to convert blocks that had flex layout implemented |
| 784 | 806 | * by custom css. |
| 785 | 807 | */ |
| @@ -822,9 +844,18 @@ | ||
| 822 | 844 | } |
| 823 | 845 | } |
| 824 | 846 | } elseif ( 'grid' === $layout_type ) { |
| 825 | 847 | /* |
| 826 | - * If the gap value is an array, we use the "left" value because it represents the vertical gap, which | |
| 848 | + * Column and row counts are whole numbers, for the same reason as the grid line | |
| 849 | + * numbers in gutenberg_get_child_layout_style_rules(). | |
| 850 | + */ | |
| 851 | + $column_count_attr = $layout_for_styles['columnCount'] ?? null; | |
| 852 | + $column_count = is_numeric( $column_count_attr ) ? (int) $column_count_attr : null; | |
| 853 | + $row_count_attr = $layout_for_styles['rowCount'] ?? null; | |
| 854 | + $row_count = is_numeric( $row_count_attr ) ? (int) $row_count_attr : null; | |
| 855 | + | |
| 856 | + /* | |
| 857 | + * If the gap value is an array, we use the "left" value because it represents the horizontal gap, which | |
| 827 | 858 | * is the relevant one for computation of responsive grid columns. |
| 828 | 859 | */ |
| 829 | 860 | if ( is_array( $fallback_gap_value ) ) { |
| 830 | 861 | $responsive_gap_value = $fallback_gap_value['left'] ?? reset( $fallback_gap_value ); |
| @@ -851,12 +882,14 @@ | ||
| 851 | 882 | $index_to_splice = strrpos( $process_value, '|' ) + 1; |
| 852 | 883 | $slug = _wp_to_kebab_case( substr( $process_value, $index_to_splice ) ); |
| 853 | 884 | $process_value = "var(--wp--preset--spacing--$slug)"; |
| 854 | 885 | } |
| 886 | + if ( ! is_array( $gap_value ) || 'left' === $gap_side ) { | |
| 887 | + $responsive_gap_value = $process_value; | |
| 888 | + } | |
| 855 | 889 | $combined_gap_value .= "$process_value "; |
| 856 | 890 | } |
| 857 | - $gap_value = trim( $combined_gap_value ); | |
| 858 | - $responsive_gap_value = $gap_value; | |
| 891 | + $gap_value = trim( $combined_gap_value ); | |
| 859 | 892 | } |
| 860 | 893 | |
| 861 | 894 | // Ensure 0 values have a unit so they work in calc(). |
| 862 | 895 | if ( '0' === $responsive_gap_value || 0 === $responsive_gap_value ) { |
| @@ -866,14 +899,14 @@ | ||
| 866 | 899 | /* Styles should be output either if there are no viewport overrides (this is the default case), or if the user has set a new viewport-specific |
| 867 | 900 | * value for any of the grid properties. |
| 868 | 901 | */ |
| 869 | 902 | $should_output_grid_columns = null === $viewport_overrides || $has_viewport_property_override( 'minimumColumnWidth' ) || $has_viewport_property_override( 'columnCount' ) || $has_viewport_property_override( 'autoFit' ); |
| 870 | - $uses_gap_in_grid_columns = ! empty( $layout_for_styles['columnCount'] ) && ! empty( $layout_for_styles['minimumColumnWidth'] ); | |
| 903 | + $uses_gap_in_grid_columns = ! empty( $column_count ) && ! empty( $layout_for_styles['minimumColumnWidth'] ); | |
| 871 | 904 | if ( $has_block_gap_override && $uses_gap_in_grid_columns ) { |
| 872 | 905 | $should_output_grid_columns = true; |
| 873 | 906 | } |
| 874 | 907 | |
| 875 | - $should_output_grid_rows = ( null === $viewport_overrides || $has_viewport_property_override( 'rowCount' ) ) && ! empty( $layout_for_styles['columnCount'] ) && ! empty( $layout_for_styles['rowCount'] ); | |
| 908 | + $should_output_grid_rows = ( null === $viewport_overrides || $has_viewport_property_override( 'rowCount' ) ) && ! empty( $column_count ) && ! empty( $row_count ); | |
| 876 | 909 | $grid_declarations = array(); |
| 877 | 910 | |
| 878 | 911 | /* When enabled, columns stretch to fill the available space using |
| 879 | 912 | * `auto-fit`; otherwise empty tracks are preserved with `auto-fill`. |
| @@ -879,13 +912,13 @@ | ||
| 879 | 912 | * `auto-fit`; otherwise empty tracks are preserved with `auto-fill`. |
| 880 | 913 | */ |
| 881 | 914 | $auto_placement = ! empty( $layout_for_styles['autoFit'] ) ? 'auto-fit' : 'auto-fill'; |
| 882 | 915 | |
| 883 | - if ( $should_output_grid_columns && ! empty( $layout_for_styles['columnCount'] ) && ! empty( $layout_for_styles['minimumColumnWidth'] ) ) { | |
| 884 | - $max_value = 'max(min(' . $layout_for_styles['minimumColumnWidth'] . ', 100%), (100% - (' . $responsive_gap_value . ' * (' . $layout_for_styles['columnCount'] . ' - 1))) /' . $layout_for_styles['columnCount'] . ')'; | |
| 916 | + if ( $should_output_grid_columns && ! empty( $column_count ) && ! empty( $layout_for_styles['minimumColumnWidth'] ) ) { | |
| 917 | + $max_value = 'max(min(' . $layout_for_styles['minimumColumnWidth'] . ', 100%), (100% - (' . $responsive_gap_value . ' * (' . $column_count . ' - 1))) /' . $column_count . ')'; | |
| 885 | 918 | $grid_declarations['grid-template-columns'] = 'repeat(' . $auto_placement . ', minmax(' . $max_value . ', 1fr))'; |
| 886 | - } elseif ( $should_output_grid_columns && ! empty( $layout_for_styles['columnCount'] ) ) { | |
| 887 | - $grid_declarations['grid-template-columns'] = 'repeat(' . $layout_for_styles['columnCount'] . ', minmax(0, 1fr))'; | |
| 919 | + } elseif ( $should_output_grid_columns && ! empty( $column_count ) ) { | |
| 920 | + $grid_declarations['grid-template-columns'] = 'repeat(' . $column_count . ', minmax(0, 1fr))'; | |
| 888 | 921 | } elseif ( $should_output_grid_columns ) { |
| 889 | 922 | $minimum_column_width = ! empty( $layout_for_styles['minimumColumnWidth'] ) ? $layout_for_styles['minimumColumnWidth'] : '12rem'; |
| 890 | 923 | $grid_declarations['grid-template-columns'] = 'repeat(' . $auto_placement . ', minmax(min(' . $minimum_column_width . ', 100%), 1fr))'; |
| 891 | 924 | } |
| @@ -891,9 +924,9 @@ | ||
| 891 | 924 | } |
| 892 | 925 | |
| 893 | 926 | if ( ! empty( $grid_declarations ) ) { |
| 894 | 927 | $base_has_container_type = empty( $base_layout['columnCount'] ) || ( ! empty( $base_layout['columnCount'] ) && ! empty( $base_layout['minimumColumnWidth'] ) ); |
| 895 | - if ( empty( $layout_for_styles['columnCount'] ) || ! empty( $layout_for_styles['minimumColumnWidth'] ) ) { | |
| 928 | + if ( empty( $column_count ) || ! empty( $layout_for_styles['minimumColumnWidth'] ) ) { | |
| 896 | 929 | if ( null === $viewport_overrides || ! $base_has_container_type ) { |
| 897 | 930 | $grid_declarations['container-type'] = 'inline-size'; |
| 898 | 931 | } |
| 899 | 932 | } |
| @@ -905,9 +938,9 @@ | ||
| 905 | 938 | |
| 906 | 939 | if ( $should_output_grid_rows ) { |
| 907 | 940 | $layout_styles[] = array( |
| 908 | 941 | 'selector' => $selector, |
| 909 | - 'declarations' => array( 'grid-template-rows' => 'repeat(' . $layout_for_styles['rowCount'] . ', minmax(1rem, auto))' ), | |
| 942 | + 'declarations' => array( 'grid-template-rows' => 'repeat(' . $row_count . ', minmax(1rem, auto))' ), | |
| 910 | 943 | ); |
| 911 | 944 | } |
| 912 | 945 | |
| 913 | 946 | if ( $has_block_gap_support && $should_output_block_gap && null !== $gap_value && ! $should_skip_gap_serialization ) { |
| @@ -951,10 +984,10 @@ | ||
| 951 | 984 | * and increments on each call. The returned value is not universally unique, |
| 952 | 985 | * but it is unique across the life of the PHP process and it's stable per |
| 953 | 986 | * prefix. |
| 954 | 987 | * |
| 955 | - * @param string $prefix Prefix for the returned ID. | |
| 956 | - * @return string Incremental ID per prefix. | |
| 988 | + * @param string $prefix Prefix for the returned ID. | |
| 989 | + * @return string Incremental ID per prefix. | |
| 957 | 990 | */ |
| 958 | 991 | function gutenberg_incremental_id_per_prefix( $prefix = '' ) { |
| 959 | 992 | static $id_counters = array(); |
| 960 | 993 | if ( ! array_key_exists( $prefix, $id_counters ) ) { |
| @@ -983,15 +1016,13 @@ | ||
| 983 | 1016 | |
| 984 | 1017 | /** |
| 985 | 1018 | * Renders the layout config to the block wrapper. |
| 986 | 1019 | * |
| 987 | - * @param string $block_content Rendered block content. | |
| 988 | - * @param array $block Block object. | |
| 989 | - * @return string Filtered block content. | |
| 1020 | + * @param string $block_content Rendered block content. | |
| 1021 | + * @param array $block Block object. | |
| 1022 | + * @return string Filtered block content. | |
| 990 | 1023 | */ |
| 991 | 1024 | function gutenberg_render_layout_support_flag( $block_content, $block ) { |
| 992 | - static $global_styles = null; | |
| 993 | - | |
| 994 | 1025 | $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] ); |
| 995 | 1026 | $block_supports_layout = block_has_support( $block_type, array( 'layout' ), false ) || block_has_support( $block_type, array( '__experimentalLayout' ), false ); |
| 996 | 1027 | $style_attr = gutenberg_resolve_style_state_aliases( |
| 997 | 1028 | $block['attrs']['style'] ?? array(), |
| @@ -1206,12 +1237,10 @@ | ||
| 1206 | 1237 | $has_block_gap_support = isset( $block_gap ); |
| 1207 | 1238 | |
| 1208 | 1239 | // Get default blockGap value from global styles for use in layouts like grid. |
| 1209 | 1240 | // Check style variation first, then block-specific styles, then fall back to root styles. |
| 1210 | - $block_name = $block['blockName'] ?? ''; | |
| 1211 | - if ( null === $global_styles ) { | |
| 1212 | - $global_styles = gutenberg_get_global_styles(); | |
| 1213 | - } | |
| 1241 | + $block_name = $block['blockName'] ?? ''; | |
| 1242 | + $global_styles = gutenberg_get_global_styles(); | |
| 1214 | 1243 | |
| 1215 | 1244 | // Check if the block has an active style variation with a blockGap value. |
| 1216 | 1245 | // Only check the registry if the className contains a variation class to avoid unnecessary lookups. |
| 1217 | 1246 | $variation_block_gap_value = null; |
| @@ -1506,11 +1535,11 @@ | ||
| 1506 | 1535 | * For themes without theme.json file, make sure |
| 1507 | 1536 | * to restore the inner div for the group block |
| 1508 | 1537 | * to avoid breaking styles relying on that div. |
| 1509 | 1538 | * |
| 1510 | - * @param string $block_content Rendered block content. | |
| 1511 | - * @param array $block Block object. | |
| 1512 | - * @return string Filtered block content. | |
| 1539 | + * @param string $block_content Rendered block content. | |
| 1540 | + * @param array $block Block object. | |
| 1541 | + * @return string Filtered block content. | |
| 1513 | 1542 | */ |
| 1514 | 1543 | function gutenberg_restore_group_inner_container( $block_content, $block ) { |
| 1515 | 1544 | $tag_name_attr = $block['attrs']['tagName'] ?? null; |
| 1516 | 1545 | $tag_name = is_string( $tag_name_attr ) ? $tag_name_attr : 'div'; |
| @@ -1580,9 +1609,9 @@ | ||
| 1580 | 1609 | * to restore the outer div for the aligned image block |
| 1581 | 1610 | * to avoid breaking styles relying on that div. |
| 1582 | 1611 | * |
| 1583 | 1612 | * @param string $block_content Rendered block content. |
| 1584 | - * @param array $block Block object. | |
| 1613 | + * @param array $block Block object. | |
| 1585 | 1614 | * @return string Filtered block content. |
| 1586 | 1615 | */ |
| 1587 | 1616 | function gutenberg_restore_image_outer_container( $block_content, $block ) { |
| 1588 | 1617 | if ( wp_theme_has_theme_json() ) { |