/*! * UI development toolkit for HTML5 (OpenUI5) * (c) Copyright 2009-2018 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ // Provides control sap.ui.commons.MenuBar. sap.ui.define([ 'jquery.sap.global', './Menu', './MenuItem', './MenuItemBase', './library', 'sap/ui/core/Control', "./MenuBarRenderer" ], function(jQuery, Menu, MenuItem, MenuItemBase, library, Control, MenuBarRenderer) { "use strict"; /** * Constructor for a new MenuBar. * * @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 * Represents a user interface area which is the entry point for menus with their menu items. MenuBar is useful for applications which shall offer a * set of actions that shall be provided in a structured way. The MenuBar contains the menu titles from where users navigate to the single items. The control supports * for example long menu item texts, automated scrolling for menu items when the browser space is not large enough to display all items, defining images for single * or all items in a menu, automated layouting of items with or w/o image, and active/non-active items. * @extends sap.ui.core.Control * * @author SAP SE * @version 1.60.23 * * @constructor * @public * @deprecated Since version 1.38. Instead, use the sap.m.OverflowToolbar control. * @alias sap.ui.commons.MenuBar * @ui5-metamodel This control/element also will be described in the UI5 (legacy) designtime metamodel */ var MenuBar = Control.extend("sap.ui.commons.MenuBar", /** @lends sap.ui.commons.MenuBar.prototype */ { metadata : { library : "sap.ui.commons", properties : { /** * When the MenuBar is not enabled, automatically all single menu items are also displayed as 'disabled'. */ enabled : {type : "boolean", group : "Behavior", defaultValue : true}, /** * Specifies the width of the MenuBar */ width : {type : "sap.ui.core.CSSSize", group : "Dimension", defaultValue : '100%'}, /** * Available design options are Header and Standard. Note that design settings are theme-dependent. */ design : {type : "sap.ui.commons.MenuBarDesign", group : "Appearance", defaultValue : sap.ui.commons.MenuBarDesign.Standard} }, defaultAggregation : "items", aggregations : { /** * Aggregation of menu items. */ items : {type : "sap.ui.unified.MenuItem", multiple : true, singularName : "item"} } }}); /*Ensure MenuItemBase is loaded (incl. loading of unified library)*/ MenuItem.extend("sap.ui.commons._DelegatorMenuItem", { constructor: function(oAlterEgoItm) { MenuItem.apply(this); this.oAlterEgoItm = oAlterEgoItm; this.bNoSubMenu = true; var oSubmenu = this.oAlterEgoItm.getSubmenu(); if (oSubmenu) { var that = this; oSubmenu.getRootMenu = function() { return that.getParent(); }; this.bNoSubMenu = false; } }, exit: function() { if (!this.bNoSubMenu) { this.oAlterEgoItm.getSubmenu().getRootMenu = Menu.prototype.getRootMenu; } this.bNoSubMenu = true; this.oAlterEgoItm = null; }, getText: function() { return this.oAlterEgoItm.getText(); }, getIcon: function() { return this.oAlterEgoItm.getIcon(); }, getEnabled: function() { return this.oAlterEgoItm.getEnabled(); }, getVisible: function() { return this.oAlterEgoItm.getVisible(); }, getSubmenu: function() { return this.oAlterEgoItm.getSubmenu(); } }); (function() { /** * Initialize this control. * * @private */ MenuBar.prototype.init = function() { this.oOvrFlwMnu = null; this.sCurrentFocusedItemRefId = null; this.data("sap-ui-fastnavgroup", "true", true); // Define group for F6 handling }; /** * Does all the cleanup when the control is to be destroyed. * Called from Element's destroy() method. * @private */ MenuBar.prototype.exit = function (){ if (this.oOvrFlwMnu) { this.oOvrFlwMnu.destroy(); } this.oOvrFlwMnu = null; // Cleanup resize event registration if (this.sResizeListenerId) { sap.ui.core.ResizeHandler.deregister(this.sResizeListenerId); this.sResizeListenerId = null; } }; /** * Called before rendering starts by the renderer * (This is not the onBeforeRendering method which would be not called for the first rendering) * @private */ MenuBar.prototype.doBeforeRendering = function() { var aItems = this.getItems(); for (var i = 0; i < aItems.length; i++) { var oMenu = aItems[i].getSubmenu(); if (oMenu) { oMenu.setRootMenuTopStyle(this.getDesign() == sap.ui.commons.MenuBarDesign.Header); } } if (this.oOvrFlwMnu) { this.oOvrFlwMnu.setRootMenuTopStyle(this.getDesign() == sap.ui.commons.MenuBarDesign.Header); } // Cleanup resize event registration before re-rendering if (this.sResizeListenerId) { sap.ui.core.ResizeHandler.deregister(this.sResizeListenerId); this.sResizeListenerId = null; } }; /** * Called when the rendering is complete * @private */ MenuBar.prototype.onAfterRendering = function() { //Listen to resizing this.sResizeListenerId = sap.ui.core.ResizeHandler.register(this.getDomRef(), jQuery.proxy(this.onresize, this)); //Calculate the overflow this.onresize(); }; /** * Called when the control is resized * @private */ MenuBar.prototype.onresize = function(oEvent) { updateAfterResize(this); }; /** * Behavior implementation which is executed when the focus comes into the control or on one of its children. * * @param {jQuery.Event} oEvent * @private */ MenuBar.prototype.onfocusin = function(oEvent){ var sId = this.getId(); var jTarget = jQuery(oEvent.target); var jTargetId = jTarget.attr("id"); if (!jTargetId || jTargetId == sId || jTargetId == sId + "-area") { var jItems = this.$("area").children(); this.sCurrentFocusedItemRefId = jItems.length == 0 ? null : jQuery(jItems.get(0)).attr("id"); } else { // Make sure the parent menu item get the focus when a menu is closed via // keyboard in order to keep keyboard navigation working this.sCurrentFocusedItemRefId = jTargetId; } var oFocusElement = jQuery.sap.byId(this.sCurrentFocusedItemRefId).get(0); if (oFocusElement) { oFocusElement.focus(); } this.$().attr("tabindex", "-1"); }; /** * Behavior implementation which is executed when the focus leaves the control or one of its children. * * @param {jQuery.Event} oEvent * @private */ MenuBar.prototype.onfocusout = function(oEvent){ //Add the control to tab chain again to make tab in working (see onfocusin) this.$().attr("tabindex", "0"); }; /** * Function is called when mouse key is clicked down. * * @param {jQuery.Event} oEvent * @private */ MenuBar.prototype.onmousedown = function(oEvent) { var oMenuItem = _getMenuItem(this, oEvent); if (oMenuItem === "ovrflw") { this._bOvrFlwMnuSkipOpen = this.oOvrFlwMnu && this.oOvrFlwMnu.bOpen; } else if (oMenuItem) { var oMenu = oMenuItem.getSubmenu(); oMenuItem._bSkipOpen = oMenu && oMenu.bOpen; } }; /** * Function is called when mouse leaves the control. * * @param {jQuery.Event} oEvent * @private */ MenuBar.prototype.onmouseout = function(oEvent) { var oMenuItem = _getMenuItem(this, oEvent); if (oMenuItem === "ovrflw") { var jRef = get$Item(this, oEvent); if (this._bOvrFlwMnuSkipOpen && jQuery.sap.checkMouseEnterOrLeave(oEvent, jRef[0])) { this._bOvrFlwMnuSkipOpen = false; } } else if (oMenuItem) { var jRef = get$Item(this, oEvent); if (oMenuItem._bSkipOpen && jQuery.sap.checkMouseEnterOrLeave(oEvent, jRef[0])) { oMenuItem._bSkipOpen = false; } } }; /** * Behavior implementation which is executed when the user clicks. * * @param {jQuery.Event} oEvent * @private */ MenuBar.prototype.onclick = function(oEvent) { openItemMenu(this, oEvent, false); }; /** * Behavior implementation which is executed when the user presses the space or enter key. * * @param {jQuery.Event} oEvent * @private */ MenuBar.prototype.onsapselect = function(oEvent){ openItemMenu(this, oEvent, true); }; /** * Function is called when down key is pressed without a modifier key. * * @param {jQuery.Event} oEvent * @private */ MenuBar.prototype.onsapdown = function(oEvent){ openItemMenu(this, oEvent, true); }; /** * Function is called when down key is pressed with a modifier key. * * @param {jQuery.Event} oEvent * @private */ MenuBar.prototype.onsapdownmodifiers = function(oEvent){ if (oEvent.altKey) { openItemMenu(this, oEvent, true); } }; /** * Behavior implementation which is executed when the user presses the arrow left (RTL: arrow right) key. * * @param {jQuery.Event} oEvent * @private */ MenuBar.prototype.onsapprevious = function(oEvent){ if (oEvent.keyCode != jQuery.sap.KeyCodes.ARROW_UP) {//Ignore arrow up focusStep(this, oEvent, "prev"); } }; /** * Behavior implementation which is executed when the user presses the arrow right (RTL: arrow left) key. * * @param {jQuery.Event} oEvent * @private */ MenuBar.prototype.onsapnext = function(oEvent){ if (oEvent.keyCode != jQuery.sap.KeyCodes.ARROW_DOWN) {//Ignore arrow down focusStep(this, oEvent, "next"); } }; /** * Behavior implementation which is executed when the user presses the home/pos1 key. * * @param {jQuery.Event} oEvent * @private */ MenuBar.prototype.onsaphome = function(oEvent){ focusStep(this, oEvent, "first"); }; /** * Behavior implementation which is executed when the user presses the end key. * * @param {jQuery.Event} oEvent * @private */ MenuBar.prototype.onsapend = function(oEvent){ focusStep(this, oEvent, "last"); }; //********* Private ********* //Opens the corresponding menu of the selected menu item var openItemMenu = function(oThis, oEvent, bWithKeyboard) { oEvent.preventDefault(); oEvent.stopPropagation(); if (oThis.getEnabled()) { var oMenuItem = _getMenuItem(oThis, oEvent); if (oMenuItem === "ovrflw") { var jRef = get$Item(oThis, oEvent); if (oThis.oOvrFlwMnu && !oThis._bOvrFlwMnuSkipOpen) { var eDock = sap.ui.core.Popup.Dock; oThis.oOvrFlwMnu.open(bWithKeyboard, jRef.get(0), eDock.EndTop, eDock.EndBottom, jRef.get(0)); } } else if (oMenuItem) { if (oMenuItem.getEnabled()) { var jRef = get$Item(oThis, oEvent); var oMenu = oMenuItem.getSubmenu(); if (oMenu && !oMenuItem._bSkipOpen) { var eDock = sap.ui.core.Popup.Dock; oMenu.open(bWithKeyboard, jRef.get(0), eDock.BeginTop, eDock.BeginBottom, jRef.get(0)); } else if (!oMenu) { oMenuItem.fireSelect({item: oMenuItem}); } } } } //Resets all skip open flags oThis._bOvrFlwMnuSkipOpen = false; var aItems = oThis.getItems(); for (var i = 0; i < aItems.length; i++) { aItems[i]._bSkipOpen = false; } }; //Returns the jQuery Object of the item which was the target of the event (if exists) var get$Item = function(oThis, oEvent){ var jRef = jQuery(oEvent.target); if (!jRef.attr("itemidx")) { jRef = jRef.parent(); } return jRef.attr("itemidx") ? jRef : null; }; //Returns the item which was the target of the event (if exists) or "ovrflow" for the overflow var _getMenuItem = function(oThis, oEvent) { var jRef = get$Item(oThis, oEvent); if (jRef) { var sItemIdx = jRef.attr("itemidx"); if (sItemIdx) { if (sItemIdx == "ovrflw") { return "ovrflw"; } else { var iIdx = parseInt(sItemIdx, 10); var oMenuItem = oThis.getItems()[iIdx]; return oMenuItem; } } } return null; }; //Compute actual number of items currently hidden due to overflow var getVisibleItemCount = function(oThis){ var iVisibleItems = 0; var jAreaRef = oThis.$("area"); var jItems = jAreaRef.children(); var bRtl = sap.ui.getCore().getConfiguration().getRTL(); var lastOffsetLeft = (bRtl ? 100000 : 0); jItems.each(function(iIdx) { if (iIdx == 0) { return true; } var currentOffsetLeft = this.offsetLeft; var bLineBreak = (bRtl ? (currentOffsetLeft >= lastOffsetLeft) : (currentOffsetLeft <= lastOffsetLeft)); if (bLineBreak) { iVisibleItems = iIdx; return false; } else if (jQuery(this).attr("id") == oThis.getId() + "-ovrflw") { // This is the overflow button, there was no line break iVisibleItems = iIdx; return false; } else { // Regular item, to the right of the last one, so just proceed lastOffsetLeft = currentOffsetLeft; return true; } }); return iVisibleItems; }; //Handle the resize of the menubar var updateAfterResize = function(oThis){ var iVisibleItems = getVisibleItemCount(oThis); var _iVisibleItems = iVisibleItems; var jAreaRef = oThis.$("area"); var jItems = jAreaRef.children(); var jOvrFlwRef = oThis.$("ovrflw"); var bUpdateFocus = false; if (iVisibleItems < jItems.length - 1) { jOvrFlwRef.attr("style", "display:block;"); if (!oThis.oOvrFlwMnu) { oThis.oOvrFlwMnu = new Menu(oThis.getId() + "-ovrflwmnu"); oThis.oOvrFlwMnu.bUseTopStyle = oThis.getDesign() == sap.ui.commons.MenuBarDesign.Header; oThis.oOvrFlwMnu.attachItemSelect(function(oEvent){ var oItem = oEvent.getParameter("item"); if (!(oItem instanceof sap.ui.commons._DelegatorMenuItem)) { var oItemRootMenu = Menu.prototype.getRootMenu.apply(oItem.getParent()); oItemRootMenu.fireItemSelect({item: oItem}); } else if (oItem.bNoSubMenu && oItem instanceof sap.ui.commons._DelegatorMenuItem) { oItem.oAlterEgoItm.fireSelect({item: oItem.oAlterEgoItm}); } }); } oThis.oOvrFlwMnu.destroyItems(); var aItems = oThis.getItems(); for (var i = 0; i < aItems.length; i++) { var oItem = aItems[i]; if (iVisibleItems != 0) { if (oItem.getVisible()) { iVisibleItems--; } if (iVisibleItems == 0) { oThis.sLastVisibleItemId = oItem.getId(); } } else { oThis.oOvrFlwMnu.addItem(new sap.ui.commons._DelegatorMenuItem(oItem)); if (oItem.getId() == oThis.sCurrentFocusedItemRefId) { bUpdateFocus = true; } } } if (sap.ui.getCore().getConfiguration().getAccessibility()) { jItems.attr("aria-setsize", _iVisibleItems + 1); jOvrFlwRef.attr("aria-posinset", _iVisibleItems + 1); } } else { jOvrFlwRef.attr("style", "display:none;"); if (oThis.oOvrFlwMnu) { oThis.oOvrFlwMnu.destroyItems(); } oThis.sLastVisibleItemId = null; if (sap.ui.getCore().getConfiguration().getAccessibility()) { jItems.attr("aria-setsize", _iVisibleItems); jOvrFlwRef.attr("aria-posinset", 0); } } jAreaRef.scrollTop(0); if (bUpdateFocus) { oThis.sCurrentFocusedItemRefId = oThis.sLastVisibleItemId; jQuery.sap.byId(oThis.sLastVisibleItemId).get(0).focus(); } }; //Focus the next (depending on the given direction) step var focusStep = function(oThis, oEvent, sDir){ oEvent.stopPropagation(); oEvent.preventDefault(); if (!oThis.sCurrentFocusedItemRefId) { return; } var sFollowingFocusItemId = null; if (oThis.sLastVisibleItemId && ((oThis.sCurrentFocusedItemRefId == oThis.sLastVisibleItemId && sDir == "next") || sDir == "last")) { sFollowingFocusItemId = oThis.getId() + "-ovrflw"; } else if (oThis.sLastVisibleItemId && oThis.sCurrentFocusedItemRefId == oThis.getId() + "-ovrflw" && sDir == "prev") { sFollowingFocusItemId = oThis.sLastVisibleItemId; } else { var sFoo = sDir + "All"; var bIsJumpToEnd = false; if (sDir == "first") { sFoo = "prevAll"; bIsJumpToEnd = true; } else if (sDir == "last") { sFoo = "nextAll"; bIsJumpToEnd = true; } var jCurrentFocusItem = jQuery.sap.byId(oThis.sCurrentFocusedItemRefId); var jFollowingItems = jCurrentFocusItem[sFoo](":visible"); sFollowingFocusItemId = jQuery(jFollowingItems.get(bIsJumpToEnd ? jFollowingItems.length - 1 : 0)).attr("id"); } if (sFollowingFocusItemId) { oThis.sCurrentFocusedItemRefId = sFollowingFocusItemId; jQuery.sap.byId(sFollowingFocusItemId).get(0).focus(); } }; }()); return MenuBar; }, /* bExport= */ true);