diff --git a/packages/block-library/src/navigation/index.php b/packages/block-library/src/navigation/index.php index f4d56465ea8ccc..bfc428c8914e24 100644 --- a/packages/block-library/src/navigation/index.php +++ b/packages/block-library/src/navigation/index.php @@ -811,7 +811,9 @@ function block_core_navigation_add_directives_to_submenu( $tags, $block_attribut // Add directives to the parent `
  • `. $tags->set_attribute( 'data-wp-interactive', 'core/navigation' ); $tags->set_attribute( 'data-wp-context', '{ "submenuOpenedBy": { "click": false, "hover": false, "focus": false }, "type": "submenu", "modal": null }' ); - $tags->set_attribute( 'data-wp-on--focusout', 'actions.handleMenuFocusout' ); + // TODO: Check if this is intended. Before this was on the submenu container, but then caused it to never open from focus alone. + $tags->set_attribute( 'data-wp-on--focusin', 'actions.openMenuOnFocus' ); + $tags->set_attribute( 'data-wp-on--focusout', 'actions.closeMenuOnFocus' ); $tags->set_attribute( 'style', "anchor-name: --{$anchor_name};" ); // This is a fix for Safari. Without it, Safari doesn't change the active @@ -847,9 +849,9 @@ function block_core_navigation_add_directives_to_submenu( $tags, $block_attribut $id = $anchor_name; $tags->set_attribute( 'id', $id ); } - $tags->set_attribute( 'popover', isset( $block_attributes['openSubmenusOnClick'] ) && $block_attributes['openSubmenusOnClick'] ? 'auto' : 'hint' ); + // TODO: `popover=hint` is not supported yet, but eventually it should be used if the menu opens on hover. + $tags->set_attribute( 'popover', /* isset( $block_attributes['openSubmenusOnClick'] ) && $block_attributes['openSubmenusOnClick'] ? 'auto' : 'hint' */ 'auto' ); $tags->set_attribute( 'data-wp-on--toggle', 'actions.handleToggle' ); - $tags->set_attribute( 'data-wp-on-async--focus', 'actions.openMenuOnFocus' ); $tags->set_attribute( 'data-wp-watch', 'callbacks.setModal' ); $tags->set_attribute( 'style', "position-anchor: --{$anchor_name};" ); diff --git a/packages/block-library/src/navigation/style.scss b/packages/block-library/src/navigation/style.scss index e293b37f4ebaf1..d57125114a708b 100644 --- a/packages/block-library/src/navigation/style.scss +++ b/packages/block-library/src/navigation/style.scss @@ -159,6 +159,12 @@ $navigation-icon-size: 24px; background-color: inherit; color: inherit; position: absolute; + margin: 0; + padding: 0; + right: auto; + left: anchor(left); + top: anchor(bottom); + bottom: auto; display: flex; flex-direction: column; align-items: normal; @@ -170,18 +176,21 @@ $navigation-icon-size: 24px; } visibility: hidden; + // Submenu indentation when there's no background. + margin-left: -1px; // Border width. + // Don't take up space when the menu is collapsed. width: 0; height: 0; overflow: hidden; // Overflow is necessary to set, otherwise submenu items will take up space. - &[popover] { - margin: 0; - padding: 0; - right: auto; - left: anchor(left); - top: anchor(bottom); - bottom: auto; + &:popover-open { + visibility: visible; + overflow: visible; + opacity: 1; + width: auto; + height: auto; + min-width: 200px; } // Submenu items. @@ -203,16 +212,11 @@ $navigation-icon-size: 24px; margin: 0; } - // Submenu indentation when there's no background. - left: -1px; // Border width. - top: 100%; - // Indentation for all submenus. // Nested submenus sit to the left on large breakpoints. // On smaller breakpoints, they open vertically, accordion-style. @include break-medium { .wp-block-navigation__submenu-container { - left: 100%; top: -1px; // Border width. // Prevent the menu from disappearing when the mouse is over the gap @@ -238,50 +242,17 @@ $navigation-icon-size: 24px; } } } - - // Custom menu items. - // Show submenus on hover unless they open on click. - &:not(.open-on-click):hover > .wp-block-navigation__submenu-container { - visibility: visible; - overflow: visible; - opacity: 1; - width: auto; - height: auto; - min-width: 200px; - } - - // Keep submenus open when focus is within. - &:not(.open-on-click):not(.open-on-hover-click):focus-within > .wp-block-navigation__submenu-container { - visibility: visible; - overflow: visible; - opacity: 1; - width: auto; - height: auto; - min-width: 200px; - } - - // Show submenus on click. - .wp-block-navigation-submenu__toggle[aria-expanded="true"] ~ .wp-block-navigation__submenu-container { - visibility: visible; - overflow: visible; - opacity: 1; - width: auto; - height: auto; - min-width: 200px; - } } // Submenu indentation when there's a background. .wp-block-navigation.has-background .has-child .wp-block-navigation__submenu-container { - left: 0; - top: 100%; + margin-left: 0; // There's no border on submenus when there are backgrounds. @include break-medium { .wp-block-navigation__submenu-container { - left: 100%; top: 0; } } @@ -392,12 +363,7 @@ button.wp-block-navigation-item__content { // First submenu. .wp-block-navigation__submenu-container { left: auto; - right: 0; - - &[popover] { - left: auto; - right: anchor(right); - } + right: anchor(right); // Nested submenus. // On smaller breakpoints, nested menus open downwards. diff --git a/packages/block-library/src/navigation/view.js b/packages/block-library/src/navigation/view.js index 28c9589e059009..faafa79da30fea 100644 --- a/packages/block-library/src/navigation/view.js +++ b/packages/block-library/src/navigation/view.js @@ -66,6 +66,7 @@ const { state, actions } = store( actions.openMenu( 'hover' ); } }, + closeMenuOnHover() { const { type, overlayOpenedBy } = getContext(); if ( @@ -77,12 +78,37 @@ const { state, actions } = store( actions.closeMenu( 'hover' ); } }, + openMenuOnFocus() { const { type } = getContext(); + window.console.log( 'openMenuOnFocus', type ); if ( type === 'submenu' ) { actions.openMenu( 'focus' ); } }, + + closeMenuOnFocus( event ) { + const { modal, type } = getContext(); + window.console.log( 'closeMenuOnFocus', type ); + // If focus is outside modal, and in the document, close menu + // event.target === The element losing focus + // event.relatedTarget === The element receiving focus (if any) + // When focusout is outside the document, + // `window.document.activeElement` doesn't change. + + // The event.relatedTarget is null when something outside the navigation menu is clicked. This is only necessary for Safari. + // TODO: Double-check this change here. See https://github.com/WordPress/gutenberg/pull/60406#issuecomment-2641303669. + if ( + type === 'submenu' && + ( event.relatedTarget === null || + ( ! modal?.contains( event.relatedTarget ) && + event.target !== window.document.activeElement ) ) + ) { + actions.closeMenu( 'click' ); + actions.closeMenu( 'focus' ); + } + }, + toggleMenuOnClick() { const ctx = getContext(); if ( ctx.type === 'submenu' ) { @@ -101,6 +127,7 @@ const { state, actions } = store( } } }, + handleMenuKeydown( event ) { const { type, firstFocusableElement, lastFocusableElement } = getContext(); @@ -126,26 +153,6 @@ const { state, actions } = store( } } }, - handleMenuFocusout( event ) { - const { modal, type } = getContext(); - // If focus is outside modal, and in the document, close menu - // event.target === The element losing focus - // event.relatedTarget === The element receiving focus (if any) - // When focusout is outside the document, - // `window.document.activeElement` doesn't change. - - // The event.relatedTarget is null when something outside the navigation menu is clicked. This is only necessary for Safari. - // TODO: Double-check this change here. See https://github.com/WordPress/gutenberg/pull/60406#issuecomment-2641303669. - if ( - type === 'submenu' && - ( event.relatedTarget === null || - ( ! modal?.contains( event.relatedTarget ) && - event.target !== window.document.activeElement ) ) - ) { - actions.closeMenu( 'click' ); - actions.closeMenu( 'focus' ); - } - }, handleToggle( event ) { const ctx = getContext(); @@ -192,14 +199,14 @@ const { state, actions } = store( openMenu( menuOpenedOn = 'click' ) { const ctx = getContext(); - window.console.log( 'openMenu', ctx.modal ); + //window.console.log( 'openMenu', ctx.modal ); state.menuOpenedBy[ menuOpenedOn ] = true; ctx.modal?.showPopover(); }, closeMenu( menuClosedOn = 'click' ) { const ctx = getContext(); - window.console.log( 'closeMenu', ctx.modal ); + //window.console.log( 'closeMenu', ctx.modal ); state.menuOpenedBy[ menuClosedOn ] = false; // Check if the menu is still open or not. if ( ! state.isMenuOpen ) {