UNPKG

@openui5/sap.m

Version:

OpenUI5 UI Library sap.m

664 lines (574 loc) 19.9 kB
/*! * UI development toolkit for HTML5 (OpenUI5) * (c) Copyright 2009-2022 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ // Provides control sap.m.Toolbar. sap.ui.define([ './BarInPageEnabler', './ToolbarLayoutData', './ToolbarSpacer', './library', 'sap/ui/core/Control', 'sap/ui/core/EnabledPropagator', 'sap/ui/core/ResizeHandler', './ToolbarRenderer', "sap/ui/thirdparty/jquery" ], function( BarInPageEnabler, ToolbarLayoutData, ToolbarSpacer, library, Control, EnabledPropagator, ResizeHandler, ToolbarRenderer, jQuery ) { "use strict"; var ToolbarDesign = library.ToolbarDesign, ToolbarStyle = library.ToolbarStyle; /** * Constructor for a new <code>Toolbar</code>. * * @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 * Horizontal container most commonly used to display buttons, labels, selects and various * other input controls. * * <h3>Overview</h3> * * By default, the <code>Toolbar</code> items are shrinkable if they have percent-based width * (for example, {@link sap.m.Input} and {@link sap.m.Slider}) or implement the * {@link sap.ui.core.IShrinkable} interface (for example, {@link sap.m.Text} and {@link sap.m.Label}). * This behavior can be overridden by providing {@link sap.m.ToolbarLayoutData} for the <code>Toolbar</code> items. * * <b>Note:</b> It is recommended that you use {@link sap.m.OverflowToolbar} over <code>sap.m.Toolbar</code>, * unless you want to avoid the overflow behavior in favor of shrinking. * * <h3>Usage</h3> * * You can add a visual separator between the preceding and succeeding {@link sap.m.Toolbar} item * with the use of the {@link sap.m.ToolbarSeparator}. The separator is theme dependent and can be * a padding, a margin or a line. * * To add horizontal space between the <code>Toolbar</code> items, use the {@link sap.m.ToolbarSpacer}. * You can define the width of the horizontal space or make it flexible to cover the remaining space * between the <code>Toolbar</code> items (for example, to to push an item to the edge of the <code>Toolbar</code>. * * <b>Note:</b> {@link sap.m.ToolbarLayoutData} should not be used together with {@link sap.m.ToolbarSpacer}. * * @see {@link fiori:https://experience.sap.com/fiori-design-web/toolbar-overview/ Toolbar} * * @extends sap.ui.core.Control * @implements sap.ui.core.Toolbar,sap.m.IBar * * @author SAP SE * @version 1.60.39 * * @constructor * @public * @since 1.16 * @alias sap.m.Toolbar * @ui5-metamodel This control/element also will be described in the UI5 (legacy) designtime metamodel */ var Toolbar = Control.extend("sap.m.Toolbar", /** @lends sap.m.Toolbar.prototype */ { metadata : { interfaces : [ "sap.ui.core.Toolbar", "sap.m.IBar" ], library : "sap.m", properties : { /** * Defines the width of the control. * By default, Toolbar is a block element. If the width is not explicitly set, the control will assume its natural size. */ width : {type : "sap.ui.core.CSSSize", group : "Appearance", defaultValue : null}, /** * Indicates that the whole toolbar is clickable. The Press event is fired only if Active is set to true. * Note: This property should be used when there are no interactive controls inside the toolbar and the toolbar itself is meant to be interactive. */ active : {type : "boolean", group : "Behavior", defaultValue : false}, /** * Sets the enabled property of all controls defined in the content aggregation. * Note: This property does not apply to the toolbar itself, but rather to its items. */ enabled : {type : "boolean", group : "Behavior", defaultValue : true}, /** * Defines the height of the control. By default, the <code>height</code> * property depends on the used theme and the <code>design</code> property. * * <b>Note:</b> It is not recommended to use this property if the * <code>sapMTBHeader-CTX</code> class is used */ height : {type : "sap.ui.core.CSSSize", group : "Appearance", defaultValue : ''}, /** * Defines the toolbar design. * * <b>Note:</b> Design settings are theme-dependent. They also determine the default height of the toolbar. * @since 1.16.8 */ design : {type : "sap.m.ToolbarDesign", group : "Appearance", defaultValue : ToolbarDesign.Auto}, /** * Defines the visual style of the <code>Toolbar</code>. * * <b>Note:</b> The visual styles are theme-dependent. * @since 1.54 */ style : {type : "sap.m.ToolbarStyle", group : "Appearance", defaultValue : ToolbarStyle.Standard} }, defaultAggregation : "content", aggregations : { /** * The content of the toolbar. */ content : {type : "sap.ui.core.Control", multiple : true, singularName : "content"} }, associations : { /** * Association to controls / ids which label this control (see WAI-ARIA attribute aria-labelledby). */ ariaLabelledBy : {type : "sap.ui.core.Control", multiple : true, singularName : "ariaLabelledBy"} }, events : { /** * Fired when the user clicks on the toolbar, if the Active property is set to "true". */ press : { parameters : { /** * The toolbar item that was pressed */ srcControl : {type : "sap.ui.core.Control"} } } }, designtime: "sap/m/designtime/Toolbar.designtime" }}); EnabledPropagator.call(Toolbar.prototype); // shrinkable class name Toolbar.shrinkClass = "sapMTBShrinkItem"; /* * Checks whether the given width is relative or not * * @static * @protected * @param {String} sWidth * @return {boolean} */ Toolbar.isRelativeWidth = function(sWidth) { return /^([-+]?\d+%|auto|inherit|)$/i.test(sWidth); }; /* * Returns the original width(currently only control's width) via Control ID * TODO: This function is not smart enough to detect DOM width changes * But tracking width changes is also expensive * (last and original width values must be keep in DOM and need update) * For now we only support calling setWidth from the control * And controls return correct width values even default value applied with CSS * * @static * @protected * @param {String} sId Control ID * @return {String} width */ Toolbar.getOrigWidth = function(sId) { var oControl = sap.ui.getCore().byId(sId); if (!oControl || !oControl.getWidth) { return ""; } return oControl.getWidth(); }; /* * Checks if the given control is shrinkable or not and marks according to second param * Percent widths and text nodes(without fixed width) are shrinkable * Controls that implement IShrinkable interface should shrink * ToolbarSpacer is already shrinkable if it does not have fixed width * * @static * @protected * @param {sap.ui.core.Control} oControl UI5 Control * @param {String} [sShrinkClass] Shrink item class name * @returns {true|false|undefined|Object} */ Toolbar.checkShrinkable = function(oControl, sShrinkClass) { if (oControl instanceof ToolbarSpacer) { return this.isRelativeWidth(oControl.getWidth()); } // remove old class sShrinkClass = sShrinkClass || this.shrinkClass; oControl.removeStyleClass(sShrinkClass); // ignore the controls has fixed width var sWidth = this.getOrigWidth(oControl.getId()); if (!this.isRelativeWidth(sWidth)) { return; } // check shrinkable via layout data var oLayout = oControl.getLayoutData(); if (oLayout instanceof ToolbarLayoutData) { return oLayout.getShrinkable() && oControl.addStyleClass(sShrinkClass); } // is percent item? // does implement shrinkable interface? if (sWidth.indexOf("%") > 0 || oControl.getMetadata().isInstanceOf("sap.ui.core.IShrinkable")) { return oControl.addStyleClass(sShrinkClass); } // is text element? var oDomRef = oControl.getDomRef(); if (oDomRef && (oDomRef.firstChild || {}).nodeType == 3) { return oControl.addStyleClass(sShrinkClass); } }; Toolbar.prototype.init = function() { // define group for F6 handling this.data("sap-ui-fastnavgroup", "true", true); // content delegate reference this._oContentDelegate = { onAfterRendering: this._onAfterContentRendering }; }; Toolbar.prototype.onBeforeRendering = function() { this._cleanup(); }; Toolbar.prototype.onAfterRendering = function() { // if there is no shrinkable item, layout is not needed if (!this._checkContents()) { return; } // layout the toolbar this._doLayout(); }; Toolbar.prototype.exit = function() { this._cleanup(); }; Toolbar.prototype.onLayoutDataChange = function() { this.rerender(); }; Toolbar.prototype.addContent = function(oControl) { this.addAggregation("content", oControl); this._onContentInserted(oControl); return this; }; Toolbar.prototype.insertContent = function(oControl, iIndex) { this.insertAggregation("content", oControl, iIndex); this._onContentInserted(oControl); return this; }; Toolbar.prototype.removeContent = function(vContent) { vContent = this.removeAggregation("content", vContent); this._onContentRemoved(vContent); return vContent; }; Toolbar.prototype.removeAllContent = function() { var aContents = this.removeAllAggregation("content") || []; aContents.forEach(this._onContentRemoved, this); return aContents; }; // handle tap for active toolbar, do nothing if already handled Toolbar.prototype.ontap = function(oEvent) { if (this.getActive() && !oEvent.isMarked()) { oEvent.setMarked(); this.firePress({ srcControl : oEvent.srcControl }); } }; // fire press event when enter is hit on the active toolbar Toolbar.prototype.onsapenter = function(oEvent) { if (this.getActive() && oEvent.srcControl === this && !oEvent.isMarked()) { oEvent.setMarked(); this.firePress({ srcControl : this }); } }; // keyboard space handling mimic the enter event Toolbar.prototype.onsapspace = Toolbar.prototype.onsapenter; // mark to inform active handling is done by toolbar Toolbar.prototype.ontouchstart = function(oEvent) { this.getActive() && oEvent.setMarked(); }; // mark shrinkable contents and render layout data // returns shrinkable and flexible content count Toolbar.prototype._checkContents = function() { var iShrinkableItemCount = 0; this.getContent().forEach(function(oControl) { if (Toolbar.checkShrinkable(oControl)) { iShrinkableItemCount++; } }); return iShrinkableItemCount; }; // apply the layout calculation according to flexbox support Toolbar.prototype._doLayout = function() { if (ToolbarRenderer.hasNewFlexBoxSupport) { return; } this._resetOverflow(); }; // reset overflow and mark with classname if overflows Toolbar.prototype._resetOverflow = function() { this._deregisterResize(); var $This = this.$(); var oDomRef = $This[0] || {}; $This.removeClass("sapMTBOverflow"); var bOverflow = oDomRef.scrollWidth > oDomRef.clientWidth; bOverflow && $This.addClass("sapMTBOverflow"); this._iEndPoint = this._getEndPoint(); this._registerResize(); }; // gets called when new control is inserted into content aggregation Toolbar.prototype._onContentInserted = function(oControl) { if (oControl) { oControl.attachEvent("_change", this._onContentPropertyChanged, this); oControl.addEventDelegate(this._oContentDelegate, oControl); } }; // gets called when a control is removed from content aggregation Toolbar.prototype._onContentRemoved = function(oControl) { if (oControl) { oControl.detachEvent("_change", this._onContentPropertyChanged, this); oControl.removeEventDelegate(this._oContentDelegate, oControl); } }; // gets called after content is (re)rendered // here "this" points to the control not to the toolbar Toolbar.prototype._onAfterContentRendering = function() { var oLayout = this.getLayoutData(); if (oLayout instanceof ToolbarLayoutData) { oLayout.applyProperties(); } }; // gets called when any content property is changed Toolbar.prototype._onContentPropertyChanged = function(oEvent) { if (oEvent.getParameter("name") != "width") { return; } // check and mark percent widths var oControl = oEvent.getSource(); var bPercent = oControl.getWidth().indexOf("%") > 0; oControl.toggleStyleClass(Toolbar.shrinkClass, bPercent); }; // register interval timer to detect inner content size is changed Toolbar.prototype._registerContentResize = function() { sap.ui.getCore().attachIntervalTimer(this._handleContentResize, this); }; // deregister interval timer for inner content Toolbar.prototype._deregisterContentResize = function() { sap.ui.getCore().detachIntervalTimer(this._handleContentResize, this); }; // register toolbar resize handler Toolbar.prototype._registerToolbarResize = function() { // register resize handler only if toolbar has relative width if (Toolbar.isRelativeWidth(this.getWidth())) { var fnResizeProxy = jQuery.proxy(this._handleToolbarResize, this); this._sResizeListenerId = ResizeHandler.register(this, fnResizeProxy); } }; // deregister toolbar resize handlers Toolbar.prototype._deregisterToolbarResize = function() { sap.ui.getCore().detachIntervalTimer(this._handleContentResize, this); if (this._sResizeListenerId) { ResizeHandler.deregister(this._sResizeListenerId); this._sResizeListenerId = ""; } }; // register resize handlers Toolbar.prototype._registerResize = function() { this._registerToolbarResize(); this._registerContentResize(); }; // deregister resize handlers Toolbar.prototype._deregisterResize = function() { this._deregisterToolbarResize(); this._deregisterContentResize(); }; // cleanup resize handlers Toolbar.prototype._cleanup = function() { this._deregisterResize(); }; // get the end position of last content Toolbar.prototype._getEndPoint = function() { var oLastChild = (this.getDomRef() || {}).lastElementChild; if (oLastChild) { var iEndPoint = oLastChild.offsetLeft; if (!sap.ui.getCore().getConfiguration().getRTL()) { iEndPoint += oLastChild.offsetWidth; } } return iEndPoint || 0; }; // handle toolbar resize Toolbar.prototype._handleToolbarResize = function() { this._handleResize(false); }; // handle inner content resize Toolbar.prototype._handleContentResize = function() { this._handleResize(true); }; // generic resize handler Toolbar.prototype._handleResize = function(bCheckEndPoint) { // check whether end point is changed or not if (bCheckEndPoint && this._iEndPoint == this._getEndPoint()) { return; } // re-layout the toolbar this._doLayout(); }; Toolbar.prototype._getAccessibilityRole = function () { var aContent = this.getContent(), sRole = this._getRootAccessibilityRole(); if (this.getActive() && (!aContent || aContent.length === 0)) { sRole = "button"; } return sRole; }; /* * Augment design property setter. * 2nd parameter can be used to define auto design context. * Note: When the second parameter is used, Toolbar does not rerender. This should be done by the setter. * * @param {sap.m.ToolbarDesign} sDesign The design for the Toolbar. * @param {boolean} [bSetAutoDesign] Determines auto design context * @returns {sap.m.Toolbar} */ Toolbar.prototype.setDesign = function(sDesign, bSetAutoDesign) { if (!bSetAutoDesign) { return this.setProperty("design", sDesign); } this._sAutoDesign = this.validateProperty("design", sDesign); return this; }; Toolbar.prototype.setStyle = function(sNewStyle) { var sTbStyleClass, bEnable; if (this.getStyle() === sNewStyle) { return this; } this.setProperty("style", sNewStyle, true /* suppress invalidate */); if (this.getDomRef()) { Object.keys(ToolbarStyle).forEach(function(sStyleKey) { sTbStyleClass = "sapMTB" + sStyleKey; bEnable = (sStyleKey === sNewStyle); this.$().toggleClass(sTbStyleClass, bEnable); }, this); } return this; }; /** * Returns the currently applied design property of the Toolbar. * * @returns {sap.m.ToolbarDesign} The <code>sap.m.ToolbarDesign</code> instance */ Toolbar.prototype.getActiveDesign = function() { var sDesign = this.getDesign(); if (sDesign != ToolbarDesign.Auto) { return sDesign; } return this._sAutoDesign || sDesign; }; /** * Returns the first sap.m.Title control instance inside the toolbar for the accessibility * * @returns {sap.m.Title|undefined} The <code>sap.m.Title</code> instance or undefined * @since 1.44 * @protected */ Toolbar.prototype.getTitleControl = function() { if (!sap.m.Title) { return; } var aContent = this.getContent(); for (var i = 0; i < aContent.length; i++) { var oContent = aContent[i]; if (oContent instanceof sap.m.Title && oContent.getVisible()) { return oContent; } } }; /** * Returns the first sap.m.Title control id inside the toolbar for the accessibility * * @returns {String} The <code>sap.m.Title</code> ID * @since 1.28 * @protected */ Toolbar.prototype.getTitleId = function() { var oTitle = this.getTitleControl(); return oTitle ? oTitle.getId() : ""; }; /////////////////////////// // Bar in page delegation /////////////////////////// /** * Returns if the bar is sensitive to the container context. Implementation of the IBar interface * @returns {boolean} isContextSensitive * @protected * @function */ Toolbar.prototype.isContextSensitive = BarInPageEnabler.prototype.isContextSensitive; /** * Sets the HTML tag of the root domref * @param {string} sTag * @returns {sap.m.IBar} this for chaining * @protected * @function */ Toolbar.prototype.setHTMLTag = BarInPageEnabler.prototype.setHTMLTag; /** * Gets the HTML tag of the root domref * @returns {string} the HTML-tag * @protected * @function */ Toolbar.prototype.getHTMLTag = BarInPageEnabler.prototype.getHTMLTag; /** * Sets classes and HTML tag according to the context of the page. Possible contexts are header, footer, subheader * @returns {sap.m.IBar} <code>this</code> for chaining * @protected * @function */ Toolbar.prototype.applyTagAndContextClassFor = BarInPageEnabler.prototype.applyTagAndContextClassFor; /** * Sets classes according to the context of the page. Possible contexts are header, footer and subheader. * @returns {sap.m.IBar} <code>this</code> for chaining * @protected * @function */ Toolbar.prototype._applyContextClassFor = BarInPageEnabler.prototype._applyContextClassFor; /** * Sets HTML tag according to the context of the page. Possible contexts are header, footer and subheader. * @returns {sap.m.IBar} <code>this</code> for chaining * @protected * @function */ Toolbar.prototype._applyTag = BarInPageEnabler.prototype._applyTag; /** * Get context options of the Page. * * Possible contexts are header, footer, subheader. * @param {string} sContext allowed values are header, footer, subheader. * @returns {object|null} * @private */ Toolbar.prototype._getContextOptions = BarInPageEnabler.prototype._getContextOptions; /** * Gets accessibility role of the Root HTML element. * * @param {string} sRole AccessibilityRole of the root Element * @returns {sap.m.IBar} <code>this</code> to allow method chaining * @private */ Toolbar.prototype._setRootAccessibilityRole = BarInPageEnabler.prototype._setRootAccessibilityRole; /** * Gets accessibility role of the Root HTML element. * * @returns {string} Accessibility role * @private */ Toolbar.prototype._getRootAccessibilityRole = BarInPageEnabler.prototype._getRootAccessibilityRole; return Toolbar; });