diff --git a/packages/main/src/Menu.ts b/packages/main/src/Menu.ts index 5428fa605ef..2e82baa7fb0 100644 --- a/packages/main/src/Menu.ts +++ b/packages/main/src/Menu.ts @@ -1,4 +1,4 @@ -import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js"; +import UI5Element, { type ChangeInfo } from "@ui5/webcomponents-base/dist/UI5Element.js"; import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; import property from "@ui5/webcomponents-base/dist/decorators/property.js"; import slot from "@ui5/webcomponents-base/dist/decorators/slot.js"; @@ -234,6 +234,13 @@ class Menu extends UI5Element { @property({ converter: DOMReferenceConverter }) opener?: HTMLElement | string; + /** + * Indicates if the menu item has a submenu. + * @private + */ + @property({ type: Boolean, noAttribute: true }) + _newItemsAdded?: boolean; + /** * Defines the items of this component. * @@ -277,6 +284,27 @@ class Menu extends UI5Element { this._menuItems.forEach(item => { item._siblingsWithIcon = siblingsWithIcon; }); + + if (this._newItemsAdded) { + if (this._menuItems.length > 0) { + const hasSelectedItem = this._menuItems.some(item => item.selected); + + if (!hasSelectedItem) { + this._menuItems[0].focus(); + } + + if (this._menuItems[0].focused) { + this._newItemsAdded = false; + } + } + } + } + + onInvalidation(changeInfo: ChangeInfo) { + // if the change is due to a new item being added, focus the first item + if (changeInfo.reason === "children" && changeInfo.type === "slot") { + this._newItemsAdded = true; + } } _close() { diff --git a/packages/main/src/MenuItem.ts b/packages/main/src/MenuItem.ts index 8c47629bd6a..071f6c3bdd1 100644 --- a/packages/main/src/MenuItem.ts +++ b/packages/main/src/MenuItem.ts @@ -7,6 +7,7 @@ import AriaHasPopup from "@ui5/webcomponents-base/dist/types/AriaHasPopup.js"; import type { AccessibilityAttributes } from "@ui5/webcomponents-base/dist/types.js"; import "@ui5/webcomponents-icons/dist/nav-back.js"; import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js"; +import type { ChangeInfo } from "@ui5/webcomponents-base/dist/UI5Element.js"; import type { ListItemAccessibilityAttributes } from "./ListItem.js"; import ListItem from "./ListItem.js"; import ResponsivePopover from "./ResponsivePopover.js"; @@ -166,6 +167,13 @@ class MenuItem extends ListItem implements IMenuItem { @property({ type: Boolean, noAttribute: true }) _siblingsWithIcon = false; + /** + * Indicates if the menu item has a submenu. + * @private + */ + @property({ type: Boolean, noAttribute: true }) + _newItemsAdded?: boolean; + /** * Defines the items of this component. * @@ -259,6 +267,27 @@ class MenuItem extends ListItem implements IMenuItem { this._menuItems.forEach(item => { item._siblingsWithIcon = siblingsWithIcon; }); + + if (this._newItemsAdded) { + if (this._menuItems.length > 0) { + const hasSelectedItem = this._menuItems.some(item => item.selected); + + if (!hasSelectedItem) { + this._menuItems[0].focus(); + } + + if (this._menuItems[0].focused) { + this._newItemsAdded = false; + } + } + } + } + + onInvalidation(changeInfo: ChangeInfo) { + // if the change is due to a new item being added, focus the first item + if (changeInfo.reason === "children" && changeInfo.type === "slot") { + this._newItemsAdded = true; + } } get _focusable() { diff --git a/packages/main/test/pages/Menu.html b/packages/main/test/pages/Menu.html index 5619953ca74..be000280d85 100644 --- a/packages/main/test/pages/Menu.html +++ b/packages/main/test/pages/Menu.html @@ -21,13 +21,13 @@ - + @@ -43,6 +43,9 @@ + + + Clicked menu item text @@ -119,6 +122,9 @@ + + Delayed +