| @@ -15,9 +15,9 @@ | ||
| 15 | 15 | * Backward compatibility: WordPress applies default attribute values, so submenuVisibility |
| 16 | 16 | * will always have a value even for legacy blocks. We check the legacy openSubmenusOnClick |
| 17 | 17 | * attribute first to preserve original behavior for blocks saved before the migration. |
| 18 | 18 | * |
| 19 | - * @since 7.0.0 | |
| 19 | + * @since 6.9.0 | |
| 20 | 20 | * |
| 21 | 21 | * @param array $attributes Block attributes containing submenuVisibility and/or openSubmenusOnClick. |
| 22 | 22 | * @return string The visibility mode: 'hover', 'click', or 'always'. |
| 23 | 23 | */ |
| @@ -40,47 +40,8 @@ | ||
| 40 | 40 | return $submenu_visibility ?? 'hover'; |
| 41 | 41 | } |
| 42 | 42 | |
| 43 | 43 | /** |
| 44 | - * Returns the custom properties used by the Navigation block for a layout. | |
| 45 | - * | |
| 46 | - * @since 7.1.0 | |
| 47 | - * | |
| 48 | - * @param array $layout Layout configuration. | |
| 49 | - * @return array Navigation layout custom property declarations. | |
| 50 | - */ | |
| 51 | -function gutenberg_block_core_navigation_get_layout_custom_property_declarations( $layout ) { | |
| 52 | - $justification_values = array( | |
| 53 | - 'left' => 'flex-start', | |
| 54 | - 'center' => 'center', | |
| 55 | - 'right' => 'flex-end', | |
| 56 | - 'space-between' => 'space-between', | |
| 57 | - ); | |
| 58 | - $justify_content = is_array( $layout ) ? ( $layout['justifyContent'] ?? 'left' ) : 'left'; | |
| 59 | - if ( ! is_string( $justify_content ) || ! isset( $justification_values[ $justify_content ] ) ) { | |
| 60 | - $justify_content = 'left'; | |
| 61 | - } | |
| 62 | - | |
| 63 | - $justification = $justification_values[ $justify_content ]; | |
| 64 | - $is_vertical = is_array( $layout ) && 'vertical' === ( $layout['orientation'] ?? null ); | |
| 65 | - $align = 'center'; | |
| 66 | - $justify = $justification; | |
| 67 | - | |
| 68 | - if ( $is_vertical ) { | |
| 69 | - $align = in_array( $justify_content, array( 'center', 'right' ), true ) ? $justification : 'flex-start'; | |
| 70 | - $justify = 'left' === $justify_content ? 'initial' : $justification; | |
| 71 | - } | |
| 72 | - | |
| 73 | - return array( | |
| 74 | - '--navigation-layout-justification-setting' => $justification, | |
| 75 | - '--navigation-layout-direction' => $is_vertical ? 'column' : 'row', | |
| 76 | - '--navigation-layout-wrap' => is_array( $layout ) && 'nowrap' === ( $layout['flexWrap'] ?? null ) ? 'nowrap' : 'wrap', | |
| 77 | - '--navigation-layout-justify' => $justify, | |
| 78 | - '--navigation-layout-align' => $align, | |
| 79 | - ); | |
| 80 | -} | |
| 81 | - | |
| 82 | -/** | |
| 83 | 44 | * Helper functions used to render the navigation block. |
| 84 | 45 | * |
| 85 | 46 | * @since 6.5.0 |
| 86 | 47 | */ |
| @@ -463,13 +424,9 @@ | ||
| 463 | 424 | // Construct the full template part ID for get_block_file_template. |
| 464 | 425 | $full_template_part_id = $theme . '//' . $slug; |
| 465 | 426 | $block_template = get_block_file_template( $full_template_part_id, 'wp_template_part' ); |
| 466 | 427 | if ( isset( $block_template->content ) ) { |
| 467 | - // Expand shortcodes before parsing blocks, matching the order in | |
| 468 | - // `render_block_core_template_part()`. | |
| 469 | - $content = shortcode_unautop( $block_template->content ); | |
| 470 | - $content = do_shortcode( $content ); | |
| 471 | - $parsed_blocks = parse_blocks( $content ); | |
| 428 | + $parsed_blocks = parse_blocks( $block_template->content ); | |
| 472 | 429 | $blocks = gutenberg_block_core_navigation_filter_out_empty_blocks( $parsed_blocks ); |
| 473 | 430 | // Disable overlay menu for any navigation blocks within the overlay to prevent nested overlays. |
| 474 | 431 | $blocks = static::disable_overlay_menu_for_nested_navigation_blocks( $blocks ); |
| 475 | 432 | return new WP_Block_List( $blocks, $attributes ); |
| @@ -491,14 +448,8 @@ | ||
| 491 | 448 | |
| 492 | 449 | // Re-serialize, and run Block Hooks algorithm to inject hooked blocks. |
| 493 | 450 | $markup = serialize_blocks( $blocks ); |
| 494 | 451 | $markup = apply_block_hooks_to_content_from_post_object( $markup, $template_part_post ); |
| 495 | - | |
| 496 | - // Expand shortcodes before parsing blocks, matching the order in | |
| 497 | - // `render_block_core_template_part()`. | |
| 498 | - $markup = shortcode_unautop( $markup ); | |
| 499 | - $markup = do_shortcode( $markup ); | |
| 500 | - | |
| 501 | 452 | $blocks = parse_blocks( $markup ); |
| 502 | 453 | |
| 503 | 454 | // Disable overlay menu for any navigation blocks within the overlay to prevent nested overlays. |
| 504 | 455 | $blocks = static::disable_overlay_menu_for_nested_navigation_blocks( $blocks ); |
| @@ -745,23 +696,21 @@ | ||
| 745 | 696 | // This needs to happen before building classes so we know if overlay blocks actually exist. |
| 746 | 697 | if ( ! empty( $attributes['overlay'] ) ) { |
| 747 | 698 | // Get blocks from the overlay template part. |
| 748 | 699 | $overlay_blocks = static::get_overlay_blocks_from_template_part( $attributes['overlay'], $attributes ); |
| 700 | + // Check if overlay contains a navigation-overlay-close block. | |
| 701 | + $has_custom_overlay_close_block = gutenberg_block_core_navigation_block_tree_has_block_type( | |
| 702 | + $overlay_blocks, | |
| 703 | + 'core/navigation-overlay-close', | |
| 704 | + array( 'core/navigation' ) // Skip navigation blocks, as they cannot contain an overlay close block | |
| 705 | + ); | |
| 749 | 706 | // Render template part blocks directly without navigation container wrapper. |
| 750 | 707 | $overlay_blocks_html = static::get_template_part_blocks_html( $overlay_blocks ); |
| 751 | - // Check if overlay contains a navigation-overlay-close block (detect in rendered HTML so it works with patterns). | |
| 752 | - $has_custom_overlay_close_block = gutenberg_block_core_navigation_overlay_html_has_close_block( $overlay_blocks_html ); | |
| 753 | 708 | // Add Interactivity API directives to the overlay close block if present. |
| 754 | 709 | if ( $has_custom_overlay_close_block && $is_interactive ) { |
| 755 | 710 | $tags = new WP_HTML_Tag_Processor( $overlay_blocks_html ); |
| 756 | 711 | $overlay_blocks_html = gutenberg_block_core_navigation_add_directives_to_overlay_close( $tags ); |
| 757 | 712 | } |
| 758 | - // Images in the overlay are hidden until the menu is opened. Pre-set | |
| 759 | - // fetchpriority="low" so that when wp_filter_content_tags() processes the | |
| 760 | - // parent template part, it sees the attribute already present and calls | |
| 761 | - // wp_get_loading_optimization_attributes() with fetchpriority="low", which both prevents | |
| 762 | - // fetchpriority="high" from being added and stops the LCP counter from being incremented. | |
| 763 | - $overlay_blocks_html = gutenberg_block_core_navigation_set_overlay_image_fetch_priority( $overlay_blocks_html ); | |
| 764 | 713 | } |
| 765 | 714 | |
| 766 | 715 | $has_custom_overlay = ! empty( $overlay_blocks_html ); |
| 767 | 716 | |
| @@ -1139,30 +1088,8 @@ | ||
| 1139 | 1088 | } |
| 1140 | 1089 | } |
| 1141 | 1090 | |
| 1142 | 1091 | /** |
| 1143 | - * Checks if the overlay HTML contains a navigation-overlay-close block. | |
| 1144 | - * | |
| 1145 | - * Uses WP_HTML_Tag_Processor to detect the close button in rendered output, | |
| 1146 | - * so it works when the overlay uses patterns (pattern content is rendered at | |
| 1147 | - * output time, not in the block tree). | |
| 1148 | - * | |
| 1149 | - * @since 7.0.0 | |
| 1150 | - * | |
| 1151 | - * @param string $html The rendered overlay HTML. | |
| 1152 | - * @return bool True if a close button element is found. | |
| 1153 | - */ | |
| 1154 | -function gutenberg_block_core_navigation_overlay_html_has_close_block( $html ) { | |
| 1155 | - $tags = new WP_HTML_Tag_Processor( $html ); | |
| 1156 | - return $tags->next_tag( | |
| 1157 | - array( | |
| 1158 | - 'tag_name' => 'BUTTON', | |
| 1159 | - 'class_name' => 'wp-block-navigation-overlay-close', | |
| 1160 | - ) | |
| 1161 | - ); | |
| 1162 | -} | |
| 1163 | - | |
| 1164 | -/** | |
| 1165 | 1092 | * Add Interactivity API directives to the navigation-overlay-close block |
| 1166 | 1093 | * markup using the Tag Processor. |
| 1167 | 1094 | * |
| 1168 | 1095 | * @since 6.5.0 |
| @@ -1184,27 +1111,8 @@ | ||
| 1184 | 1111 | return $tags->get_updated_html(); |
| 1185 | 1112 | } |
| 1186 | 1113 | |
| 1187 | 1114 | /** |
| 1188 | - * Sets fetchpriority="low" on all IMG tags within the navigation overlay. | |
| 1189 | - * | |
| 1190 | - * Images in the overlay are hidden until the menu is opened, so they should | |
| 1191 | - * not compete with any actual LCP element image on the page. | |
| 1192 | - * | |
| 1193 | - * @since 7.0.0 | |
| 1194 | - * | |
| 1195 | - * @param string $overlay_blocks_html The rendered HTML of the overlay blocks. | |
| 1196 | - * @return string Modified HTML with fetchpriority="low" on all IMG tags. | |
| 1197 | - */ | |
| 1198 | -function gutenberg_block_core_navigation_set_overlay_image_fetch_priority( string $overlay_blocks_html ): string { | |
| 1199 | - $tags = new WP_HTML_Tag_Processor( $overlay_blocks_html ); | |
| 1200 | - while ( $tags->next_tag( 'IMG' ) ) { | |
| 1201 | - $tags->set_attribute( 'fetchpriority', 'low' ); | |
| 1202 | - } | |
| 1203 | - return $tags->get_updated_html(); | |
| 1204 | -} | |
| 1205 | - | |
| 1206 | -/** | |
| 1207 | 1115 | * Add Interactivity API directives to the navigation-submenu and page-list |
| 1208 | 1116 | * blocks markup using the Tag Processor. |
| 1209 | 1117 | * |
| 1210 | 1118 | * @since 6.3.0 |
| @@ -1237,10 +1145,10 @@ | ||
| 1237 | 1145 | $computed_visibility = gutenberg_block_core_navigation_get_submenu_visibility( $block_attributes ); |
| 1238 | 1146 | $open_on_hover = 'hover' === $computed_visibility; |
| 1239 | 1147 | |
| 1240 | 1148 | if ( $open_on_hover ) { |
| 1241 | - $tags->set_attribute( 'data-wp-on--pointerenter', 'actions.openMenuOnHover' ); | |
| 1242 | - $tags->set_attribute( 'data-wp-on--pointerleave', 'actions.closeMenuOnHover' ); | |
| 1149 | + $tags->set_attribute( 'data-wp-on--mouseenter', 'actions.openMenuOnHover' ); | |
| 1150 | + $tags->set_attribute( 'data-wp-on--mouseleave', 'actions.closeMenuOnHover' ); | |
| 1243 | 1151 | } |
| 1244 | 1152 | |
| 1245 | 1153 | // Add directives to the toggle submenu button. |
| 1246 | 1154 | if ( $tags->next_tag( |
| @@ -1393,8 +1301,19 @@ | ||
| 1393 | 1301 | return $font_sizes; |
| 1394 | 1302 | } |
| 1395 | 1303 | |
| 1396 | 1304 | /** |
| 1305 | + * Returns the top-level submenu SVG chevron icon. | |
| 1306 | + * | |
| 1307 | + * @since 5.9.0 | |
| 1308 | + * | |
| 1309 | + * @return string | |
| 1310 | + */ | |
| 1311 | +function gutenberg_block_core_navigation_render_submenu_icon() { | |
| 1312 | + return '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true" focusable="false"><path d="M1.50002 4L6.00002 8L10.5 4" stroke-width="1.5"></path></svg>'; | |
| 1313 | +} | |
| 1314 | + | |
| 1315 | +/** | |
| 1397 | 1316 | * Filter out empty "null" blocks from the block list. |
| 1398 | 1317 | * 'parse_blocks' includes a null block with '\n\n' as the content when |
| 1399 | 1318 | * it encounters whitespace. This is not a bug but rather how the parser |
| 1400 | 1319 | * is designed. |
| @@ -1594,137 +1513,8 @@ | ||
| 1594 | 1513 | ); |
| 1595 | 1514 | } |
| 1596 | 1515 | |
| 1597 | 1516 | add_action( 'init', 'gutenberg_register_block_core_navigation', 20 ); |
| 1598 | - | |
| 1599 | -/** | |
| 1600 | - * Adds Navigation block support classes to inner list containers. | |
| 1601 | - * | |
| 1602 | - * State block support adds the generated `wp-states-*` class to the outer | |
| 1603 | - * block wrapper. The Navigation block renders its menu items inside an inner | |
| 1604 | - * `wp-block-navigation__container` list, so the same state class is also needed | |
| 1605 | - * there for state styles to apply directly to the menu list. | |
| 1606 | - * | |
| 1607 | - * Navigation also uses layout classes on its outer wrapper to define custom | |
| 1608 | - * properties consumed by its inner containers. Viewport layout styles cannot | |
| 1609 | - * change those classes, so equivalent custom properties and a scoping class | |
| 1610 | - * are generated for each configured viewport layout. | |
| 1611 | - * | |
| 1612 | - * Currently this is required as a workaround because of how difficult it is for nav | |
| 1613 | - * child blocks to inherit styles through the complex responsive nav block html. The | |
| 1614 | - * bug in https://github.com/WordPress/gutenberg/issues/62690 also prevents inheritance. | |
| 1615 | - * | |
| 1616 | - * @since 7.1.0 | |
| 1617 | - * | |
| 1618 | - * @param string $block_content The block content. | |
| 1619 | - * @param array $block The full block, including name and attributes. | |
| 1620 | - * @return string The updated block content. | |
| 1621 | - */ | |
| 1622 | -function gutenberg_block_core_navigation_add_support_classes_to_container( $block_content, $block ) { | |
| 1623 | - if ( 'core/navigation' !== ( $block['blockName'] ?? null ) || empty( $block_content ) ) { | |
| 1624 | - return $block_content; | |
| 1625 | - } | |
| 1626 | - | |
| 1627 | - $attributes = is_array( $block['attrs'] ?? null ) ? $block['attrs'] : array(); | |
| 1628 | - $style = is_array( $attributes['style'] ?? null ) ? $attributes['style'] : array(); | |
| 1629 | - if ( | |
| 1630 | - defined( 'IS_GUTENBERG_PLUGIN' ) && | |
| 1631 | - IS_GUTENBERG_PLUGIN && | |
| 1632 | - function_exists( 'gutenberg_resolve_style_state_aliases' ) | |
| 1633 | - ) { | |
| 1634 | - $style = gutenberg_resolve_style_state_aliases( $style, 'core/navigation' ); | |
| 1635 | - } | |
| 1636 | - | |
| 1637 | - $global_settings = gutenberg_get_global_settings(); | |
| 1638 | - $viewport_settings = $global_settings['viewport'] ?? null; | |
| 1639 | - $responsive_media_queries = array(); | |
| 1640 | - if ( method_exists( 'WP_Theme_JSON_Gutenberg', 'get_viewport_media_queries' ) ) { | |
| 1641 | - $responsive_media_queries = WP_Theme_JSON_Gutenberg::get_viewport_media_queries( $viewport_settings ); | |
| 1642 | - } elseif ( method_exists( 'WP_Theme_JSON', 'get_viewport_media_queries' ) ) { | |
| 1643 | - $responsive_media_queries = WP_Theme_JSON::get_viewport_media_queries( $viewport_settings ); | |
| 1644 | - } | |
| 1645 | - | |
| 1646 | - $styles = array(); | |
| 1647 | - $base_layout = is_array( $attributes['layout'] ?? null ) ? $attributes['layout'] : array(); | |
| 1648 | - foreach ( $responsive_media_queries as $breakpoint => $media_query ) { | |
| 1649 | - $viewport_style = is_array( $style[ $breakpoint ] ?? null ) ? $style[ $breakpoint ] : array(); | |
| 1650 | - $viewport_layout = is_array( $viewport_style['layout'] ?? null ) ? $viewport_style['layout'] : array(); | |
| 1651 | - if ( empty( $viewport_layout ) ) { | |
| 1652 | - continue; | |
| 1653 | - } | |
| 1654 | - | |
| 1655 | - $styles[] = array( | |
| 1656 | - 'declarations' => gutenberg_block_core_navigation_get_layout_custom_property_declarations( | |
| 1657 | - array_replace( $base_layout, $viewport_layout ) | |
| 1658 | - ), | |
| 1659 | - 'rules_group' => $media_query, | |
| 1660 | - ); | |
| 1661 | - } | |
| 1662 | - | |
| 1663 | - $processor = new WP_HTML_Tag_Processor( $block_content ); | |
| 1664 | - if ( ! $processor->next_tag() ) { | |
| 1665 | - return $block_content; | |
| 1666 | - } | |
| 1667 | - | |
| 1668 | - $class_attribute = $processor->get_attribute( 'class' ); | |
| 1669 | - $state_class = null; | |
| 1670 | - if ( is_string( $class_attribute ) && preg_match( '/\bwp-states-[a-f0-9]{8}\b/', $class_attribute, $matches ) ) { | |
| 1671 | - $state_class = $matches[0]; | |
| 1672 | - } | |
| 1673 | - | |
| 1674 | - $layout_class = null; | |
| 1675 | - if ( ! empty( $styles ) ) { | |
| 1676 | - $layout_class = wp_unique_id( 'wp-block-navigation-' ); | |
| 1677 | - // The inner selector includes both Navigation classes so it overrides the | |
| 1678 | - // default layout custom properties set by `.wp-block-navigation.items-*`. | |
| 1679 | - $selector = ".wp-block-navigation.{$layout_class},.wp-block-navigation.wp-block-navigation__container.{$layout_class}"; | |
| 1680 | - foreach ( $styles as &$style_rule ) { | |
| 1681 | - $style_rule['selector'] = $selector; | |
| 1682 | - } | |
| 1683 | - unset( $style_rule ); | |
| 1684 | - | |
| 1685 | - $processor->add_class( $layout_class ); | |
| 1686 | - gutenberg_style_engine_get_stylesheet_from_css_rules( | |
| 1687 | - $styles, | |
| 1688 | - array( 'context' => 'block-supports' ) | |
| 1689 | - ); | |
| 1690 | - } | |
| 1691 | - | |
| 1692 | - if ( null === $state_class && null === $layout_class ) { | |
| 1693 | - return $block_content; | |
| 1694 | - } | |
| 1695 | - | |
| 1696 | - while ( $processor->next_tag() ) { | |
| 1697 | - // Custom overlay content can include nested Navigation blocks. | |
| 1698 | - // Avoid applying the outer Navigation classes to an inner nav block. | |
| 1699 | - if ( $processor->has_class( 'wp-block-navigation' ) && ! $processor->has_class( 'wp-block-navigation__container' ) ) { | |
| 1700 | - break; | |
| 1701 | - } | |
| 1702 | - | |
| 1703 | - if ( ! $processor->has_class( 'wp-block-navigation__container' ) ) { | |
| 1704 | - continue; | |
| 1705 | - } | |
| 1706 | - | |
| 1707 | - if ( null !== $layout_class ) { | |
| 1708 | - $processor->add_class( $layout_class ); | |
| 1709 | - } | |
| 1710 | - | |
| 1711 | - if ( null === $state_class ) { | |
| 1712 | - continue; | |
| 1713 | - } | |
| 1714 | - | |
| 1715 | - $class_attribute = $processor->get_attribute( 'class' ); | |
| 1716 | - if ( is_string( $class_attribute ) && preg_match( '/\bwp-states-[a-f0-9]{8}\b/', $class_attribute ) ) { | |
| 1717 | - continue; | |
| 1718 | - } | |
| 1719 | - | |
| 1720 | - $processor->add_class( $state_class ); | |
| 1721 | - } | |
| 1722 | - | |
| 1723 | - return $processor->get_updated_html(); | |
| 1724 | -} | |
| 1725 | - | |
| 1726 | -add_filter( 'render_block', 'gutenberg_block_core_navigation_add_support_classes_to_container', 11, 2 ); | |
| 1727 | 1517 | |
| 1728 | 1518 | /** |
| 1729 | 1519 | * Filter that changes the parsed attribute values of navigation blocks contain typographic presets to contain the values directly. |
| 1730 | 1520 | * |