Skip to content

Commit

Permalink
Allow opening submenu on focus alone and simplify styling needs.
Browse files Browse the repository at this point in the history
  • Loading branch information
felixarntz committed Feb 11, 2025
1 parent d15daed commit f68e509
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 77 deletions.
8 changes: 5 additions & 3 deletions packages/block-library/src/navigation/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,9 @@ function block_core_navigation_add_directives_to_submenu( $tags, $block_attribut
// Add directives to the parent `<li>`.
$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
Expand Down Expand Up @@ -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};" );

Expand Down
70 changes: 18 additions & 52 deletions packages/block-library/src/navigation/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
Expand All @@ -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
Expand All @@ -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;
}
}
Expand Down Expand Up @@ -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.
Expand Down
51 changes: 29 additions & 22 deletions packages/block-library/src/navigation/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ const { state, actions } = store(
actions.openMenu( 'hover' );
}
},

closeMenuOnHover() {
const { type, overlayOpenedBy } = getContext();
if (
Expand All @@ -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' ) {
Expand All @@ -101,6 +127,7 @@ const { state, actions } = store(
}
}
},

handleMenuKeydown( event ) {
const { type, firstFocusableElement, lastFocusableElement } =
getContext();
Expand All @@ -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();
Expand Down Expand Up @@ -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 ) {
Expand Down

0 comments on commit f68e509

Please sign in to comment.