@openui5/sap.m
Version:
OpenUI5 UI Library sap.m
836 lines (722 loc) • 26.7 kB
JavaScript
/*!
* OpenUI5
* (c) Copyright 2026 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides control sap.m.Menu.
sap.ui.define([
'sap/m/library',
'sap/ui/core/library',
'sap/ui/core/Control',
'sap/m/ResponsivePopover',
'sap/m/Button',
'sap/m/Bar',
'sap/m/Title',
'sap/m/MenuItem',
'sap/m/MenuWrapper',
'sap/ui/core/Lib',
'sap/ui/Device',
"sap/ui/core/InvisibleText",
'sap/ui/base/ManagedObjectMetadata',
'sap/ui/core/EnabledPropagator',
'sap/base/i18n/Localization',
'sap/base/Log'
],
function(
library,
coreLibrary,
Control,
ResponsivePopover,
Button,
Bar,
Title,
MenuItem,
MenuWrapper,
Lib,
Device,
InvisibleText,
ManagedObjectMetadata,
EnabledPropagator,
Localization,
Log
) {
"use strict";
// Shortcut for sap.m.PlacementType
const PlacementType = library.PlacementType;
// Shortcut for sap.ui.core.ItemSelectionMode
const ItemSelectionMode = coreLibrary.ItemSelectionMode;
/**
* Constructor for a new Menu.
*
* @param {string} [sId] ID for the new control, generated automatically if no ID is given
* @param {object} [mSettings] Initial settings for the new control
*
* @class
* The <code>sap.m.Menu</code> control represents a hierarchical menu.
* When opened on mobile devices it occupies the whole screen.
*
* @extends sap.ui.core.Control
* @implements sap.ui.core.IContextMenu
*
* @author SAP SE
* @version 1.146.0
*
* @constructor
* @public
* @alias sap.m.Menu
*/
const Menu = Control.extend("sap.m.Menu", /** @lends sap.m.Menu.prototype */ {
metadata : {
interfaces: [
"sap.ui.core.IContextMenu"
],
library : "sap.m",
properties : {
/**
* Defines the <code>Menu</code> title.
*/
title : { type : "string", group : "Misc", defaultValue : null }
},
defaultAggregation: "items",
aggregations: {
/**
* Defines the items contained within this control.
*/
items: { type: "sap.m.IMenuItem", multiple: true, singularName: "item", bindable: "bindable", defaultClass: MenuItem, forwarding: { idSuffix: "-menuWrapper", aggregation: "items" } },
/**
* Internal Menu Wrapper control
*/
_menuWrapper: { type: "sap.m.MenuWrapper", multiple: false, visibility: "hidden" },
/**
* Internal aggregation that contains the inner <code>sap.m.ResponsivePopover</code> for mobile.
*/
_popover: { type: "sap.m.ResponsivePopover", multiple: false, visibility: "hidden" }
},
events: {
/**
* Fired when a <code>MenuItem</code> is selected.
*/
itemSelected: {
parameters: {
/**
* The <code>MenuItem</code> which was selected.
*/
item : {type : "sap.m.IMenuItem" }
}
},
/**
* Fired when the menu is closed.
*/
closed: {},
/**
* Fired when the menu is opened.
* @since 1.146
*/
open: {},
/**
* Fired before the menu is closed.
* This event can be prevented which effectively prevents the menu from closing.
* @since 1.131
*/
beforeClose : {
allowPreventDefault : true,
parameters: {
/**
* The <code>MenuItem</code> which was selected (if any).
* @since 1.136.0
*/
item : {type : "sap.m.IMenuItem" }
}
}
}
},
renderer: null // this is a ResponsivePopover control without a renderer
});
EnabledPropagator.call(Menu.prototype);
/**
* Initializes the control.
*
* @public
*/
Menu.prototype.init = function() {
const oMenuWrapper = this._createMenuWrapper(),
oPopover = this._createPopover();
oMenuWrapper.attachClosePopover(this.close, this);
oMenuWrapper.attachCloseItemSubmenu(this._collectSubmenusToClose, this);
oMenuWrapper.attachItemSelected(this._handleItemSelected, this);
oPopover.attachAfterClose(this._menuClosed, this);
oPopover.attachAfterOpen(this._menuOpened, this);
this._aSubmenusToClose = [];
this._openDuration = Device.system.phone ? null : 0;
};
Menu.prototype.updateItems = function(sReason, oEventInfo) {
// Special handling for the V4 ODataModel.
if (oEventInfo && oEventInfo.detailedReason === "AddVirtualContext") {
createVirtualItem(this);
return;
} else if (oEventInfo && oEventInfo.detailedReason === "RemoveVirtualContext") {
destroyVirtualItem(this);
return;
}
if (this._bReceivingData) {
//If we are receiving the data, this should be handled in oDataModel.
//The updateStarted event is already triggered before refreshItems.
//Here, the items binding is updated because the data has arrived from the server.
//At this point, we can reset/convert the flag for the next request.
this._bReceivingData = false;
} else {
//if the data is not requested, this should be handled with a JSON Model.
//Since the data is already in memory and not requested from the server, we do not need to change the flag.
//In this case, this._bReceivingData should always remain false.
this._updateStarted(sReason);
}
// for flat list update items aggregation
this.updateAggregation("items");
// items binding are updated
this._updateFinished();
};
Menu.prototype._updateStarted = function(sReason) {
// if data receiving/update is not started or ongoing
if (!this._bReceivingData && !this._bUpdating) {
this._bUpdating = true;
}
};
// called on after rendering to finalize item update finished
Menu.prototype._updateFinished = function() {
// check if data receiving/update is finished
if (!this._bReceivingData && this._bUpdating) {
setTimeout(function() {
this._getMenuWrapper()?._setHoveredItem(this._getMenuWrapper()._getNextFocusableItem(-1, 1), true);
}.bind(this), 0);
this._bUpdating = false;
}
};
// this gets called only with oData Model when first load
Menu.prototype.refreshItems = function(sReason) {
this._bRefreshItems = true;
// if data multiple time requested during the ongoing request
// UI5 cancels the previous requests then we should fire updateStarted once
if (!this._bReceivingData) {
// handle update started event
this._updateStarted(sReason);
this._bReceivingData = true;
}
this.refreshAggregation("items");
};
Menu.prototype.createItem = function(oContext, oBindingInfo, sIdSuffix) {
const oItem = oBindingInfo.factory(ManagedObjectMetadata.uid(sIdSuffix ? sIdSuffix : "clone"), oContext);
return oItem.setBindingContext(oContext, oBindingInfo.model);
};
/**
* Called from parent if the control is destroyed.
*/
Menu.prototype.exit = function() {
const oMenuWrapper = this._getMenuWrapper(),
oPopover = this._getPopover();
oMenuWrapper.detachClosePopover(this.close, this);
oMenuWrapper.detachCloseItemSubmenu(this._collectSubmenusToClose, this);
oMenuWrapper.detachItemSelected(this._handleItemSelected, this);
oPopover.detachAfterClose(this._menuClosed, this);
oPopover.detachAfterOpen(this._menuOpened, this);
oMenuWrapper.destroy();
oPopover.destroy();
};
/**
* Sets the title of the <code>Menu</code> in mobile view.
*
* @param {string} sTitle The new title of the <code>Menu</code>
* @returns {this} <code>this</code> to allow method chaining
* @public
*/
Menu.prototype.setTitle = function(sTitle) {
const oPopover = this._getPopover(),
oMenuWrapper = this._getMenuWrapper(),
oHeader = oPopover.getCustomHeader();
// set the text of the Title of the ResponsivePopover, which is located in the custom header's Bar contentMiddle aggregation
if (oHeader) {
oMenuWrapper.setTitle(sTitle);
// the Title is the only elemnet in the contentMiddle aggregation of the Bar we create as custom header
// so we can safely set the text of the first element
oHeader.getContentMiddle()[0].setText(sTitle);
}
this.setProperty("title", sTitle, true);
return this;
};
/**
* Opens the <code>Menu</code> next to the given control.
*
* @param {sap.ui.core.Control} oControl The control that defines the position for the menu
* @returns {this} <code>this</code> to allow method chaining
* @public
*/
Menu.prototype.openBy = function(oControl) {
const oPopover = this._getPopover();
if (!oControl) {
oControl = document.body;
}
this._openPopoverBy(oPopover, oControl);
this._getMenuWrapper()._bOpenedByMenuButton = oControl.isA && oControl.isA("sap.m.MenuButton"); // set flag on MenuWrapper for keyboard handling
this.bIgnoreOpenerFocus = true; // reset the flag to allow the opener to be focused after the menu is closed
return this;
};
/**
* Closes the <code>Menu</code> if the <code>beforeClose</code> event isn`t prevented.
*
* @param {sap.ui.base.Event} oEvent closePopover event
* @returns {this} <code>this</code> to allow method chaining
* @public
*/
Menu.prototype.close = function(oEvent) {
const oEventParameters = oEvent ? oEvent.getParameters() : {},
oBeforeCloseParameters = {},
oPopover = this._getPopover();
if (oEventParameters["origin"]) {
oBeforeCloseParameters["item"] = oEventParameters["origin"];
}
this._refreshSubmenusToClose();
if (oPopover && this.fireBeforeClose(oBeforeCloseParameters)) {
this._closeSubmenuPopovers();
oPopover._getPopup().setDurations(this._openDuration, 0);
oPopover.close();
}
return this;
};
/**
* Opens the given popover next to the specified control, using the configured open duration.
*
* @param {sap.m.ResponsivePopover} oPopover The popover instance to open.
* @param {sap.ui.core.Control|HTMLElement} oControl The control or DOM element that defines the position for the popover.
* @private
*/
Menu.prototype._openPopoverBy = function(oPopover, oControl) {
oPopover._getPopup().setDurations(this._openDuration, 0);
oPopover.openBy(oControl);
};
/**
* Captures the itemSelected event fired by the menu wrapper and fires the itemSelected event of the menu.
*
* @param {sap.ui.base.Event} oEvent itemSelected event fired by the menu wrapper
* @private
*/
Menu.prototype._handleItemSelected = function(oEvent) {
oEvent.cancelBubble();
this.fireItemSelected({item: oEvent.getParameter("item")});
this.bIgnoreOpenerFocus = false; // allow the opener to be focused after the menu is closed
};
/**
* Refreshes the list of submenus that should be closed by checking if any are still open.
*
* @private
*/
Menu.prototype._refreshSubmenusToClose = function() {
this._aSubmenusToClose = this._aSubmenusToClose.filter((oItem) => oItem._getPopover().isOpen());
};
/**
* Closes the submenus of the <code>Menu</code> that are still open.
*
* @private
*/
Menu.prototype._closeSubmenuPopovers = function() {
while (this._aSubmenusToClose.length) {
this._aSubmenusToClose.pop()._closeSubmenu();
}
};
/**
* Returns whether the <code>Menu</code> is currently open.
*
* @returns {boolean} true if menu is open
* @public
*/
Menu.prototype.isOpen = function() {
return this._getPopover().isOpen();
};
/**
* Provides a DOM reference ID for the menu container.
* @returns {string} The DOM reference ID for the menu container
*/
Menu.prototype.getDomRefId = function() {
const oPopoverDomRef = this._getPopover().getDomRef();
return oPopoverDomRef ? oPopoverDomRef.id : "";
};
/**
* Opens the menu as a context menu.
*
* @param {jQuery.Event | object} oEvent The event object or an object containing offsetX, offsetY
* values and left, top values for the element's position
* @param {sap.ui.core.Element|HTMLElement} oOpenerRef The reference of the opener
* @public
*/
Menu.prototype.openAsContextMenu = function(oEvent, oOpenerRef) {
const oPopover = this._getPopover(),
oOriginalEvent = oEvent ? oEvent.originalEvent || oEvent : null,
bPageCoordinates = oOriginalEvent && oOriginalEvent.button !== undefined && !!oOriginalEvent.type,
bOffsetCoordinates = oOriginalEvent && !bPageCoordinates && oOriginalEvent.type === undefined &&
(oOriginalEvent.offsetX !== undefined && oOriginalEvent.offsetY !== undefined),
bOpenerCoordinates = oOriginalEvent.type?.substr(0, 3) === "key" || (bPageCoordinates && oOpenerRef && (
(oOriginalEvent.pageX === undefined && oOriginalEvent.clientX === undefined) ||
(oOriginalEvent.pageY === undefined && oOriginalEvent.clientY === undefined)));
let oOpenerDomRef = oOpenerRef && oOpenerRef.getDomRef ? oOpenerRef.getDomRef() : oOpenerRef,
oPointerElement = document.getElementById("sapMMenuContextMenuPointer"),
oPointerParent = document.body,
oPointerSibling = null,
iX = 0,
iY = 0;
oPopover._getPopup().setDurations(this._openDuration, 0);
// explicitly close the popover if it is already open
if (oPopover.isOpen()) {
oPopover.close();
}
// on mobile devices, the popover is opened by the ResponsivePopover control on fullscreen,
// there's no need to use opener or do some positioning
if (Device.system.phone) {
oPopover.openBy();
return;
}
// if the opener reference is not provided, we try to get it from the event
if (!oOpenerRef && !bOffsetCoordinates) {
oOpenerDomRef = oEvent.srcControl ? oEvent.srcControl.getDomRef() : null;
if (!oOpenerDomRef) {
if (oOriginalEvent?.currentTarget) {
oOpenerDomRef = oOriginalEvent.currentTarget;
} else if (oOriginalEvent?.target) {
oOpenerDomRef = oOriginalEvent.target;
} else {
oOpenerDomRef = document.body;
}
}
}
// remove previously existing pointer element
if (oPointerElement) {
oPointerElement.remove();
}
// create a new pointer element
oPointerElement = document.createElement("div");
oPointerElement.id = "sapMMenuContextMenuPointer";
oPointerElement.className = "sapMMenuContextMenuPointer";
// if the opener DOM ref is provided, we should get its position data
const oOpenerData = oOpenerDomRef ? oOpenerDomRef.getBoundingClientRect() : null,
iScrollX = document.documentElement.scrollLeft || document.body.scrollLeft,
iScrollY = document.documentElement.scrollTop || document.body.scrollTop,
isRTL = Localization.getRTL();
// calculate the position of the pointer element
if ((!oOriginalEvent || bOpenerCoordinates) && oOpenerData) {
// opener is provided, no event is provided, or there are missing important coordinates in the event,
// we should use the opener's position data
if (isRTL) {
// In RTL, calculate distance from right edge of viewport to center of opener
iX = (document.documentElement.clientWidth - (oOpenerData.right - iScrollX)) + (oOpenerData.width / 2);
} else {
// In LTR, calculate distance from left edge of viewport to center of opener
iX = oOpenerData.left + (oOpenerData.width / 2) + iScrollX;
}
iY = oOpenerData.top + (oOpenerData.height / 2) + iScrollY;
} else if (oOriginalEvent && bPageCoordinates) {
// the event with coordinates is provided, we should use them
const iPageX = (oOriginalEvent.pageX || oOriginalEvent.clientX) + iScrollX;
const iPageY = (oOriginalEvent.pageY || oOriginalEvent.clientY) + iScrollY;
if (oOpenerDomRef.tagName && oOpenerDomRef.tagName.toLowerCase() === "tr") {
oPointerParent = oOpenerDomRef.firstChild ? oOpenerDomRef.firstChild : oOpenerDomRef;
oPointerSibling = oPointerParent.firstChild ? oPointerParent.firstChild : null;
} else {
oPointerParent = oOpenerDomRef;
oPointerSibling = oOpenerDomRef.firstChild ? oOpenerDomRef.firstChild : null;
}
// compute inline-start offset relative to opener considering RTL
if (oOpenerData) {
if (isRTL) {
iX = (oOpenerData.right + iScrollX) - iPageX;
} else {
iX = iPageX - (oOpenerData.left + iScrollX);
}
iY = iPageY - (oOpenerData.top + iScrollY);
} else {
// fallback if no opener data: use page coordinates
iX = iPageX;
iY = iPageY;
}
} else if (bOffsetCoordinates) {
// offsetX/offsetY coordinates are provided, we should use specified position
iX = oEvent.offsetX || 0;
iY = oEvent.offsetY || 0;
}
// insert the pointer element into the DOM and set its position
oPointerParent.insertBefore(oPointerElement, oPointerSibling);
oPointerElement.style.insetInlineStart = `${iX}px`;
oPointerElement.style.insetBlockStart = `${iY}px`;
oPointerElement.setAttribute("aria-hidden", "true");
oPopover.openBy(oPointerElement);
oPopover.attachAfterClose(this._onContextMenuClose, this);
};
/**
* Removes the pointer element from the DOM when the context menu is closed.
* @private
*/
Menu.prototype._onContextMenuClose = function() {
const oPointerElement = document.getElementById("sapMMenuContextMenuPointer"),
oPopover = this._getPopover();
oPointerElement && oPointerElement.remove();
oPopover.detachAfterClose(this._onContextMenuClose, this);
};
/**
* Override mutator public methods for CustomStyleClassSupport so it's properly propagated to the popover.
* Keep in mind we don't overwrite <code>hasStyleClass</code> method - we are only propagating the state.
* We don't mimic the popover custom style class support.
*
* @override
*/
["addStyleClass", "removeStyleClass", "toggleStyleClass"].forEach(function (sMethodName) {
Menu.prototype[sMethodName] = function (sClass, bSuppressInvalidate) {
const oPopover = this._getPopover();
this._getMenuWrapper()._processStyleClasses(sClass, sMethodName);
Control.prototype[sMethodName].apply(this, arguments);
if (sMethodName !== "toggleStyleClass" && oPopover) {
oPopover[sMethodName].apply(oPopover, arguments);
}
return this;
};
});
/**
* Returns an array containing the selected menu items.
* <b>Note:</b> Only items with <code>selected</code> property set that are members of <code>MenuItemGroup</code> with <code>ItemSelectionMode</code> property
* set to {@link sap.ui.core.ItemSelectionMode.SingleSelect} or {@link sap.ui.core.ItemSelectionMode.MultiSelect}> are taken into account.
*
* @since 1.127.0
* @public
* @returns {Array} Array of all selected items
*/
Menu.prototype.getSelectedItems = function() {
return this._getItems().filter((oItem) => oItem.getSelected && oItem.getSelected() && oItem._getItemSelectionMode() !== ItemSelectionMode.None);
};
/**
* Collects the items which submenus should be closed if necessary.
*
* @param {sap.ui.base.Event} oEvent closeItemSubmenu event
* @private
*/
Menu.prototype._collectSubmenusToClose = function(oEvent) {
const oItem = oEvent.getParameter("item");
this._refreshSubmenusToClose();
// check if the submenu is already in the list
if (this._aSubmenusToClose.indexOf(oItem) === -1) {
this._aSubmenusToClose.push(oItem);
}
};
/**
* Allows for any custom function to be called back when accessibility attributes
* of underlying menu are about to be rendered.
* The function is called once per MenuItem
*
* @param {function} fn The callback function
* @private
* @ui5-restricted ObjectPageLayoutABHelper
* @returns {void}
*/
Menu.prototype._setCustomEnhanceAccStateFunction = function(fn) {
this._fnEnhanceUnifiedMenuAccState = fn;
};
Menu.prototype._menuOpened = function() {
this.fireOpen();
};
Menu.prototype._menuClosed = function(oEvent) {
const oOpener = oEvent && oEvent.getParameter("openBy");
this.fireClosed();
if (oOpener && !this.bIgnoreOpenerFocus) {
this._getMenuWrapper()._bOpenedByMenuButton = false;
try {
oOpener.focus();
} catch (e) {
Log.warning("Menu.close cannot restore the focus on opener " + oOpener + ", " + e);
}
}
};
/**
* Checks if an item is a MenuItemGroup or not.
*
* @param {sap.m.IMenuItem} oItem The item to be checked
* @returns {boolean} Whether the item is a MenuItemGroup or not
* @private
*/
Menu.prototype._isMenuItemGroup = function(oItem) {
return !!oItem.getItemSelectionMode;
};
/**
* Returns list of items stored in <code>items</code> aggregation. If there are group items,
* the items of the group are returned instead of their group item.
*
* @returns {sap.m.MenuItem} List of all menu items
* @private
*/
Menu.prototype._getItems = function() {
const aItems = [];
const findItems = (aItemItems) => {
aItemItems.forEach((oItem) => {
if (!this._isMenuItemGroup(oItem)) {
aItems.push(oItem);
} else {
findItems(oItem.getItems());
}
});
};
findItems(this.getItems());
return aItems;
};
/* ResponsivePopover and MenuWrapper functionality */
/**
* Creates the internal MenuWrapper control.
* @param {boolean} bIsSubmenu Whether the menu in this wrapper is a submenu or not
* @returns {sap.m.MenuWrapper} The created MenuWrapper
* @private
*/
Menu.prototype._createMenuWrapper = function(bIsSubmenu) {
const oMenuWrapper = new MenuWrapper(this.getId() + "-menuWrapper", { isSubmenu: bIsSubmenu });
this.setAggregation("_menuWrapper", oMenuWrapper, true);
return oMenuWrapper;
};
/**
* Creates the ResponsivePopover that contains the actual menu.
* @returns {sap.m.ResponsivePopover} The created ResponsivePopover
* @private
*/
Menu.prototype._createPopover = function() {
let oPopover = this._getPopover();
if (oPopover) {
return oPopover;
}
const sDialogAccessibleNameId = Device.system.phone
? `${this.getId()}-title`
: InvisibleText.getStaticId("sap.m", "MENU_POPOVER_ACCESSIBLE_NAME");
const oMenuWrapper = this._getMenuWrapper(),
bRTL = Localization.getRTL(),
bIsSubmenu = oMenuWrapper.getIsSubmenu(),
iOffsetXCorrection = bRTL ? 4 : -4;
oPopover = new ResponsivePopover(this.getId() + "-rp", {
placement: this._getPopoverPlacement(),
showHeader: false,
showArrow: false,
showCloseButton: false,
verticalScrolling: true,
horizontalScrolling: false,
offsetX: bIsSubmenu ? iOffsetXCorrection : 1,
offsetY: bIsSubmenu ? 4 : 1,
content: oMenuWrapper,
ariaLabelledBy: [sDialogAccessibleNameId]
});
oPopover.addStyleClass("sapMMenu");
this.setAggregation("_popover", oPopover, true);
if (Device.system.phone) {
oPopover.setShowHeader(true);
oPopover.setEndButton(this._createCloseButton());
oPopover.setCustomHeader(this._createHeaderBar());
} else if (bIsSubmenu) {
oPopover.getAggregation("_popup")._adaptPositionParams = function() {
this._marginTop = 0;
this._marginLeft = 0;
this._marginRight = 0;
this._marginBottom = 0;
this._arrowOffset = 0;
this._offsets = ["0 0", "0 0", "0 0", "0 0"];
this._myPositions = ["begin bottom", "begin top", "begin top", "end top"];
this._atPositions = ["begin top", "end top", "begin bottom", "begin top"];
};
}
// this override is needed to fix the issue with the popover position flip
oPopover._oControl._getDocHeight = () => window.innerHeight + window.scrollY;
return oPopover;
};
/**
* Creates the back button for the Responsive Popover in mobile view.
* @returns {sap.m.Button} The back button
*/
Menu.prototype._createBackButton = function() {
return new Button(this.getId() + "-backbutton", {
icon : "sap-icon://nav-back",
press : (oEvent) => {
this._getMenuWrapper().fireClosePopover();
}
});
};
/**
* Creates the custom header bar for the Responsive Popover in mobile view.
* @returns {sap.m.Bar} The header bar
*/
Menu.prototype._createHeaderBar = function() {
const oMenuWrapper = this._getMenuWrapper(),
oHeaderBar = new Bar({
contentMiddle: new Title(this.getId() + "-title", { text: oMenuWrapper.getTitle() })
}),
bIsSubmenu = this._getMenuWrapper().getIsSubmenu();
if (bIsSubmenu) {
oHeaderBar.addContentLeft(this._createBackButton());
}
return oHeaderBar;
};
/**
* Creates the close button for the Responsive Popover for mobile view.
* @returns {sap.m.Button} The close button
*/
Menu.prototype._createCloseButton = function() {
const oRB = Lib.getResourceBundleFor("sap.m");
return new Button({
text: oRB.getText("MENU_CLOSE"),
press: () => {
this._getMenuWrapper().fireClosePopover({ bubbleToRoot: true });
}
});
};
/**
* Gets the internal MenuWrapper control.
* @returns {sap.m.MenuWrapper} The internal _menuWrapper aggregation
* @private
*/
Menu.prototype._getMenuWrapper = function() {
const oPopover = this._getPopover();
return oPopover ? oPopover.getContent()[0] : this.getAggregation("_menuWrapper");
};
/**
* Gets the internal ResponsivePopover.
* @private
* @returns {sap.m.ResponsivePopover} The internal _popover aggregation
*/
Menu.prototype._getPopover = function() {
return this.getAggregation("_popover");
};
/**
* Gets the placement type for the popover depending of LTR/RTL setting.
* @private
* @returns {sap.m.PlacementType} the placement type of the popover
*/
Menu.prototype._getPopoverPlacement = function() {
const bIsSubmenu = this._getMenuWrapper().getIsSubmenu();
if (bIsSubmenu) {
const bRTL = Localization.getRTL(),
sPlacement = bRTL ? PlacementType.HorizontalPreferredLeft : PlacementType.HorizontalPreferredRight;
return sPlacement;
}
return PlacementType.VerticalPreferredBottom;
};
/**
* Set extra content to the popover.
* @private
* @param {HTMLElement} oDomRef the DOM ref to be added as extra content to the popover
*/
Menu.prototype._setExtraContent = function(oDomRef) {
this._getPopover()._getPopup().setExtraContent([oDomRef]);
};
function createVirtualItem(oMenu) {
const oBinding = oMenu.getBinding("items");
const oBindingInfo = oMenu.getBindingInfo("items");
const iLength = oBindingInfo.length;
const iIndex = oBindingInfo.startIndex;
const oVirtualContext = oBinding.getContexts(iIndex, iLength)[0];
destroyVirtualItem(oMenu);
oMenu._oVirtualItem = oMenu.createItem(oVirtualContext, oBindingInfo, "virtual");
oMenu.addAggregation("dependents", oMenu._oVirtualItem, true);
}
function destroyVirtualItem(oMenu) {
if (oMenu._oVirtualItem) {
oMenu._oVirtualItem.destroy();
delete oMenu._oVirtualItem;
}
}
return Menu;
});