From 2b3d3d507ce05ba5013e120c5f1c764e0de68cc7 Mon Sep 17 00:00:00 2001 From: Andreas Reinhold / reini Date: Mon, 14 Sep 2026 18:26:51 +0200 Subject: [PATCH] Scope the submenu reopen guard to the browser's own light dismiss A submenu's close() marked every close it caused as recent, so the 250ms guard meant to stop a trigger's own click reopening a popover the same press just light-dismissed also blocked a deliberate keyboard reopen (Enter/Space right after ArrowLeft) that happened to land within that window. Only a close the browser made on its own now counts toward the guard; close() marks its own hidePopover() call explicit so it never seeds it. Fixes the "opens a submenu with Right, Enter or Space" browser test (plan step 36, actions group). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9 --- resources/js/menu.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/resources/js/menu.js b/resources/js/menu.js index 1b35a7f1..b6144882 100644 --- a/resources/js/menu.js +++ b/resources/js/menu.js @@ -45,7 +45,9 @@ const ITEMS = '[role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradi // A popover="auto" closes on the press that lands on its trigger, and the click that follows // would open it again. A close this recent is taken as that press. It is timed from // `beforetoggle`, which fires as the popover closes: `toggle` is queued, and arrives after that -// click. +// click. Only a close the browser made on its own (light dismiss) sets it — `close()` marks its +// own as explicit, so a keyboard command that shuts a menu never guards against reopening it a +// moment later, only the browser's own light dismiss racing the click it precedes does. const REOPEN_GUARD_MS = 250 // A submenu opens after the pointer has rested on its item for a moment, and closes a moment after @@ -56,6 +58,7 @@ const HOVER_CLOSE_MS = 320 const menu = () => ({ closedAt: -Infinity, + closingExplicitly: false, anchored: null, returnFocus: true, focusWasInside: false, @@ -104,7 +107,11 @@ const menu = () => ({ this.focusWasInside = event.newState === 'closed' && menu.contains(document.activeElement) if (event.newState === 'closed') { - this.closedAt = performance.now() + if (!this.closingExplicitly) { + this.closedAt = performance.now() + } + + this.closingExplicitly = false } }) @@ -336,6 +343,7 @@ const menu = () => ({ } if (this.$refs.menu.matches(':popover-open')) { + this.closingExplicitly = true this.$refs.menu.hidePopover() }