From 33fb2d7ecddd7acbe67a83272498ebaf5ea4e479 Mon Sep 17 00:00:00 2001 From: Denis VOITURON Date: Fri, 28 Aug 2026 20:38:27 +0200 Subject: [PATCH] feat(Menu): add mouseout handling for submenu closure and improve positioning fallbacks --- .../src/Components/Menu/FluentMenu.ts | 34 ++++++++++++++++++- .../Components/Menu/FluentMenuList.razor.css | 8 +++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/Core.Scripts/src/Components/Menu/FluentMenu.ts b/src/Core.Scripts/src/Components/Menu/FluentMenu.ts index dd3b37eb5f..3649f044c1 100644 --- a/src/Core.Scripts/src/Components/Menu/FluentMenu.ts +++ b/src/Core.Scripts/src/Components/Menu/FluentMenu.ts @@ -1,8 +1,35 @@ -import { Menu, MenuList } from "@fluentui/web-components"; +import { Menu, MenuItem, MenuList } from "@fluentui/web-components"; import { DotNet } from "../../d-ts/Microsoft.JSInterop"; export namespace Microsoft.FluentUI.Blazor.Components.Menu { const menuAbortControllers = new Map(); + const menuListsWithMouseOutWorkaround = new WeakSet(); + + // Closes an open submenu when the pointer leaves, even if its parent menu item retains focus. + function handleMenuListMouseOut(event: MouseEvent) { + const relatedTarget = event.relatedTarget; + + for (const target of event.composedPath()) { + if (!(target instanceof MenuItem)) { + continue; + } + + const submenu = target.submenu; + if (!submenu?.matches(":popover-open")) { + continue; + } + + if (relatedTarget instanceof Node && target.contains(relatedTarget)) { + continue; + } + + if (submenu.contains(document.activeElement)) { + continue; + } + + submenu.togglePopover(false); + } + } /** * Initializes the menu by associating it with a trigger element and setting up the necessary styles for positioning. @@ -42,6 +69,11 @@ export namespace Microsoft.FluentUI.Blazor.Components.Menu { menuList.style["position-anchor" as any] = `--anchor-${triggerId}`; menu.slottedTriggersChanged(menu.slottedTriggers ?? [], [trigger]); + if (!menuListsWithMouseOutWorkaround.has(menuList)) { + menuList.addEventListener("mouseout", handleMenuListMouseOut); + menuListsWithMouseOutWorkaround.add(menuList); + } + menuAbortControllers.get(id)?.abort(); if (dotNetHelper) { const abortController = new AbortController(); diff --git a/src/Core/Components/Menu/FluentMenuList.razor.css b/src/Core/Components/Menu/FluentMenuList.razor.css index 63cf3ece72..1eb16de494 100644 --- a/src/Core/Components/Menu/FluentMenuList.razor.css +++ b/src/Core/Components/Menu/FluentMenuList.razor.css @@ -2,3 +2,11 @@ fluent-menu fluent-menu-list:not([popover]) { display: none; } + +/* Fix #5207 */ +fluent-menu-item>fluent-menu-list[slot="submenu"] { + position-try-fallbacks: + flip-inline, + flip-block, + flip-block flip-inline; +} \ No newline at end of file