From 8d1fcd94c14781af495efca7f7c35ca10433f5c1 Mon Sep 17 00:00:00 2001 From: Boyan Rakilovski Date: Fri, 29 Mar 2024 10:58:32 +0200 Subject: [PATCH] refactor(ui5-menu): display custom list items (#8476) - This change is meant to ease the further implementation for custom content supplied by the application developers. - The menu width is adjusted based on the menu item's contents, in order to fulfill the accessibility standard. The menu item text will truncate in case there is insufficient space in the viewport to display it. Fixes: #7082 Related to: #6350 --- packages/main/src/Menu.hbs | 26 -------- packages/main/src/Menu.ts | 2 +- packages/main/src/MenuListItem.hbs | 35 ++++++++++ packages/main/src/MenuListItem.ts | 64 +++++++++++++++--- packages/main/src/themes/Menu.css | 80 ----------------------- packages/main/src/themes/MenuListItem.css | 74 ++++++++++++++++++++- 6 files changed, 165 insertions(+), 116 deletions(-) create mode 100644 packages/main/src/MenuListItem.hbs diff --git a/packages/main/src/Menu.hbs b/packages/main/src/Menu.hbs index fb5ed5f4e613..d8ea74784d3c 100644 --- a/packages/main/src/Menu.hbs +++ b/packages/main/src/Menu.hbs @@ -77,32 +77,6 @@ @keydown={{../_itemKeyDown}} @_focused={{../_onfocusin}} > -
- {{#if this.item.hasDummyIcon}} -
-
- {{/if}} - {{this.item.text}} - {{#if this.item.hasSubmenu}} -
- - -
- {{else if this.item._siblingsWithChildren}} -
-
- {{/if}} -
{{/each}} diff --git a/packages/main/src/Menu.ts b/packages/main/src/Menu.ts index 6df76f29b065..874181ebd2d9 100644 --- a/packages/main/src/Menu.ts +++ b/packages/main/src/Menu.ts @@ -547,7 +547,7 @@ class Menu extends UI5Element { const menuListItem = target.hasAttribute("ui5-menu-li") ? target as MenuListItem : (target.getRootNode() as ShadowRoot).host as MenuListItem; - const item = menuListItem.associatedItem as MenuItem; + const item = menuListItem.associatedItem; const mainMenu = this._findMainMenu(item); mainMenu?.fireEvent("item-focus", { ref: menuListItem, item }); } diff --git a/packages/main/src/MenuListItem.hbs b/packages/main/src/MenuListItem.hbs new file mode 100644 index 000000000000..23be170136cb --- /dev/null +++ b/packages/main/src/MenuListItem.hbs @@ -0,0 +1,35 @@ +{{>include "./ListItem.hbs"}} + +{{#*inline "listItemContent"}} + {{#if text}} +
{{text}}
+ {{/if}} + {{#if _additionalText}} + {{_additionalText}} + {{/if}} +{{/inline}} + +{{#*inline "iconBegin"}} + {{#if _siblingsWithIcon}} + {{#if hasIcon}} + + {{else}} +
+ {{/if}} + {{/if}} +{{/inline}} + +{{#*inline "iconEnd"}} + {{#if hasSubmenu}} +
+ + +
+ {{/if}} +{{/inline}} \ No newline at end of file diff --git a/packages/main/src/MenuListItem.ts b/packages/main/src/MenuListItem.ts index e8ef73c21420..66717b563595 100644 --- a/packages/main/src/MenuListItem.ts +++ b/packages/main/src/MenuListItem.ts @@ -1,7 +1,7 @@ import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; import property from "@ui5/webcomponents-base/dist/decorators/property.js"; -import StandardListItem from "./StandardListItem.js"; -import StandardListItemTemplate from "./generated/templates/StandardListItemTemplate.lit.js"; +import CustomListItem from "./CustomListItem.js"; +import MenuListItemTemplate from "./generated/templates/MenuListItemTemplate.lit.js"; import MenuItem from "./MenuItem.js"; import HasPopup from "./types/HasPopup.js"; @@ -11,22 +11,71 @@ import menuListItemCss from "./generated/themes/MenuListItem.css.js"; /** * @class * @constructor - * @extends StandardListItem + * @extends CustomListItem * @since 1.23.0 * @private */ @customElement({ tag: "ui5-menu-li", - template: StandardListItemTemplate, - styles: [StandardListItem.styles, menuListItemCss], + template: MenuListItemTemplate, + styles: [CustomListItem.styles, menuListItemCss], }) -class MenuListItem extends StandardListItem { +class MenuListItem extends CustomListItem { /** * Defines the associated MenuItem instance * @private */ @property({ type: Object }) - associatedItem?: MenuItem; + associatedItem!: MenuItem; + + /** + * Defines the icon to be displayed as graphical element within the component. + * The SAP-icons font provides numerous options. + * + * **Example:** + * + * See all the available icons in the [Icon Explorer](https://sdk.openui5.org/test-resources/sap/m/demokit/iconExplorer/webapp/index.html). + * @default "" + * @public + * @since 1.24.0 + */ + @property() + icon!: string; + + /** + * Defines the `additionalText`, displayed in the end of the menu item. + * + * **Note:** The additional text would not be displayed if the item has a submenu. + * @default "" + * @public + * @since 1.24.0 + */ + @property() + additionalText!: string; + + get text() { + return this.associatedItem?.text; + } + + get _additionalText() { + return this.associatedItem?._additionalText; + } + + get hasIcon() { + return !!this.associatedItem?.icon; + } + + get hasSubmenu() { + return !!(this.associatedItem?.items.length || this.associatedItem?.busy); + } + + get subMenuOpened() { + return !!this.associatedItem?._subMenu; + } + + get _siblingsWithIcon() { + return this.associatedItem?._siblingsWithIcon; + } get _focusable() { return true; @@ -34,7 +83,6 @@ class MenuListItem extends StandardListItem { get _accInfo() { const accInfoSettings = { - role: "menuitem", ariaHaspopup: this.associatedItem?.hasSubmenu ? HasPopup.Menu.toLowerCase() as Lowercase : undefined, }; diff --git a/packages/main/src/themes/Menu.css b/packages/main/src/themes/Menu.css index 7b2acc025fab..ad7c2213882b 100644 --- a/packages/main/src/themes/Menu.css +++ b/packages/main/src/themes/Menu.css @@ -8,47 +8,6 @@ .ui5-menu-rp[ui5-responsive-popover] { box-shadow: var(--sapContent_Shadow1); border-radius: var(--_ui5_menu_popover_border_radius); - max-width: 20rem; -} - -.ui5-menu-item-icon-end { - display: inline-block; - vertical-align: middle; - padding-inline-start: 0.5rem; - pointer-events: none; - position: absolute; - inset-inline-end: var(--_ui5_menu_item_submenu_icon_right); -} - -.ui5-menu-item-no-icon-end { - min-width: var(--_ui5_list_item_icon_size); - min-height: var(--_ui5_list_item_icon_size); - display: inline-block; - vertical-align: middle; - padding-inline-start: 0.5rem; - pointer-events: none; - inset-inline-end: var(--_ui5_menu_item_submenu_icon_right); -} - -.ui5-menu-item[additional-text] .ui5-menu-item-no-icon-end { - display: none; -} - -.ui5-menu-item-dummy-icon { - min-width: var(--_ui5_list_item_icon_size); - min-height: var(--_ui5_list_item_icon_size); - display: inline-block; - vertical-align: middle; - padding-inline-end: 0.5rem; - pointer-events: none; -} - -.ui5-menu-item-submenu-icon { - min-width: var(--_ui5_list_item_icon_size); - min-height: var(--_ui5_list_item_icon_size); - display: inline-block; - vertical-align: middle; - pointer-events: none; } .ui5-menu-busy-indicator { @@ -86,28 +45,6 @@ margin-right: 1rem; } -.ui5-menu-item::part(title) { - font-size: var(--sapFontSize); - padding-top: 0.125rem; -} - -.ui5-menu-item[icon]:not([is-phone])::part(title), -.ui5-menu-item[is-phone]:not([icon=""])::part(title) { - padding-top: 0; -} - -.ui5-menu-item:not([is-phone])::part(native-li) { - padding: var(--_ui5_menu_item_padding); -} - -.ui5-menu-item[starts-section] { - border-top: 1px solid var(--sapGroup_ContentBorderColor); -} - -.ui5-menu-item[active] .ui5-menu-item-icon-end { - color: var(--sapList_Active_TextColor); -} - .ui5-menu-rp[sub-menu] { margin-top: 0.25rem; margin-inline: var(--_ui5_menu_submenu_margin_offset); @@ -116,21 +53,4 @@ .ui5-menu-rp[sub-menu][actual-placement-type="Left"] { margin-top: 0.25rem; margin-inline: var(--_ui5_menu_submenu_placement_type_left_margin_offset); -} - -.ui5-menu-item::part(additional-text) { - margin-inline-start: var(--_ui5_menu_item_additional_text_start_margin); - color: var(--sapContent_LabelColor); - min-width: max-content; -} - -.ui5-menu-item-text { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - pointer-events: none; -} - -.ui5-menu-item-text:has(.ui5-menu-item-submenu-icon) { - padding-inline-end: 1rem; } \ No newline at end of file diff --git a/packages/main/src/themes/MenuListItem.css b/packages/main/src/themes/MenuListItem.css index 203d3678f0f2..c2b6548067dc 100644 --- a/packages/main/src/themes/MenuListItem.css +++ b/packages/main/src/themes/MenuListItem.css @@ -4,7 +4,7 @@ } :host([disabled])::part(content) { - opacity: var(--_ui5-listitembase_disabled_opacity); + opacity: var(--_ui5-listitembase_disabled_opacity); } /* hovered and active */ @@ -20,4 +20,76 @@ :host([focused]:not([active]):not([disabled])) { background-color: var(--sapList_Hover_Background); +} + +:host(:not([is-phone]))::part(native-li) { + padding: var(--_ui5_menu_item_padding); +} + +:host::part(additional-text) { + margin: unset; + margin-inline-start: 1rem; + color: var(--sapContent_LabelColor); + min-width: max-content; +} + +.ui5-menu-item-text { + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + pointer-events: none; + display: inline-block; +} + +.ui5-menu-item-dummy-icon { + visibility: hidden; +} + +:host::part(title) { + font-size: var(--sapFontSize); + padding-top: 0.125rem; +} + +:host([icon]:not([is-phone]))::part(title), +:host([is-phone]:not([icon=""]))::part(title) { + padding-top: 0; +} + +:host(:not([is-phone]))::part(native-li) { + padding: var(--_ui5_menu_item_padding); +} + +:host([starts-section]) { + border-top: 1px solid var(--sapGroup_ContentBorderColor); +} + +:host::part(content) { + padding-inline-end: 0.5rem; +} + +.ui5-menu-item-submenu-icon { + min-width: var(--_ui5_list_item_icon_size); + min-height: var(--_ui5_list_item_icon_size); + display: inline-block; + vertical-align: middle; + pointer-events: none; +} + +.ui5-menu-item-icon-end { + display: inline-block; + vertical-align: middle; + padding-inline-start: 0.5rem; + pointer-events: none; + position: absolute; + inset-inline-end: var(--_ui5_menu_item_submenu_icon_right); +} + +.ui5-menu-item-dummy-icon { + min-width: var(--_ui5_list_item_icon_size); + min-height: var(--_ui5_list_item_icon_size); + display: inline-block; + vertical-align: middle; + padding-inline-end: 0.5rem; + pointer-events: none; } \ No newline at end of file