/*! * 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. */ sap.ui.define([ './library', 'sap/ui/core/Control', 'sap/m/GenericTile', 'sap/ui/Device', 'sap/ui/core/Icon', './SlideTileRenderer', "sap/ui/events/KeyCodes", "sap/ui/events/PseudoEvents", "sap/ui/thirdparty/jquery" ], function( library, Control, GenericTile, Device, Icon, SlideTileRenderer, KeyCodes, PseudoEvents, jQuery ) { "use strict"; var TileSizeBehavior = library.TileSizeBehavior; /** * Constructor for a new sap.m.SlideTile control. * * @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 control that displays multiple GenericTile controls as changing slides. * @extends sap.ui.core.Control * * @author SAP SE * @version 1.60.23 * @since 1.34 * * @public * @alias sap.m.SlideTile * @ui5-metamodel This control/element also will be described in the UI5 (legacy) designtime metamodel */ var SlideTile = Control.extend("sap.m.SlideTile", /** @lends sap.m.SlideTile.prototype */ { metadata: { library: "sap.m", properties: { /** * The time of the slide display in milliseconds. */ displayTime: {type: "int", group: "Appearance", defaultValue: 5000}, /** * The time of the slide changing in milliseconds. */ transitionTime: {type: "int", group: "Appearance", defaultValue: 500}, /** * Changes the visualization in order to enable additional actions with the SlideTile control. * @since 1.46.0 */ scope: {type: "sap.m.GenericTileScope", group: "Misc", defaultValue: "Display"}, /** * If set to TileSizeBehavior.Small, the tile size is the same as it would be on a small-screened phone (374px wide and lower), * regardless of the screen size of the actual device being used. * If set to TileSizeBehavior.Responsive, the tile size adapts to the size of the screen. * This property has to be set consistently for the SlideTile along with all its inner GenericTile * elements, so that they match one another visually. */ sizeBehavior: {type: "sap.m.TileSizeBehavior", defaultValue: TileSizeBehavior.Responsive}, /** * Width of the control. * @since 1.60.19 */ width: {type: "sap.ui.core.CSSSize", group: "Appearance"} }, defaultAggregation: "tiles", aggregations: { /** * The set of Generic Tiles to be shown in the control. */ tiles: {type: "sap.m.GenericTile", multiple: true, singularName: "tile", bindable: "bindable"}, /** * The pause/play icon that is being used to display the pause/play state of the control. */ _pausePlayIcon: {type: "sap.ui.core.Icon", multiple: false, visibility: "hidden"} }, events: { /** * The event is fired when the user chooses the tile. The event is available only in Actions scope. * @since 1.46.0 */ press: { parameters: { /** * The current scope the SlideTile was in when the event occurred. * @since 1.46.0 */ scope: {type: "sap.m.GenericTileScope"}, /** * The action that was pressed on the tile. In the Actions scope, the available actions are Press and Remove. * @since 1.46.0 */ action: {type: "string"}, /** * The Element's DOM Element. * In Actions scope the domRef points to the DOM Element of the remove button (if pressed) or the more icon. * @since 1.46.0 */ domRef: {type: "any"} } } } } }); /* --- Lifecycle Handling --- */ /** * Init function for the control */ SlideTile.prototype.init = function () { this._oRb = sap.ui.getCore().getLibraryResourceBundle("sap.m"); this.setAggregation("_pausePlayIcon", new Icon({ id: this.getId() + "-pause-play-icon", src: "sap-icon://media-pause", color: "#ffffff", size: "1rem", noTabStop: true }), true); }; /** * Handler for beforerendering */ SlideTile.prototype.onBeforeRendering = function () { // initialize SlideTile scope with SlideTile CSS class name GenericTile.prototype._initScopeContent.call(this, "sapMST"); var bActionsView = this.getScope() === library.GenericTileScope.Actions; // According to the scope of SlideTile, displays corresponding view of GenericTiles for (var i = 0; i < this.getTiles().length; i++) { this.getTiles()[i].showActionsView(bActionsView); } // save the current tile index to let the tile be displayed in Actions scope if (this._iCurrentTile >= 0) { this._iLastTile = this._iCurrentTile; } this._bNeedInvalidate = false; this._stopAnimation(); this._sWidth = this._sHeight = undefined; this._iCurrentTile = this._iPreviousTile = undefined; }; /** * Handler for afterrendering */ SlideTile.prototype.onAfterRendering = function () { this._setupResizeClassHandler(); var cTiles = this.getTiles().length, sScope = this.getScope(); this._removeGTFocus(); this._iCurrAnimationTime = 0; this._bAnimationPause = false; // if the last displayed tile exists, then scrolls to this tile. Otherwise displays first tile. if (this._iLastTile >= 0 && cTiles > 1) { this._scrollToTile(this._iLastTile); } else { this._scrollToNextTile(); } if (cTiles > 1 && sScope === library.GenericTileScope.Display) { this._startAnimation(); } // in actions scope, the more icon color is changed when the displayed tile has news content (dark background) if (sScope === library.GenericTileScope.Actions && this._iCurrentTile >= 0 && this._hasNewsContent(this._iCurrentTile)) { this.addStyleClass("sapMSTDarkBackground"); } }; /** * Exit function for the control */ SlideTile.prototype.exit = function () { this._stopAnimation(); if (this._oMoreIcon) { this._oMoreIcon.destroy(); } if (this._oRemoveButton) { this._oRemoveButton.destroy(); } }; /* --- Event Handling --- */ /** * Handler for tap * * @param {sap.ui.base.Event} oEvent which was fired */ SlideTile.prototype.ontap = function (oEvent) { var sScope = this.getScope(); this.$().focus(); if (sScope === library.GenericTileScope.Actions) { var oParams = this._getEventParams(oEvent); this.firePress(oParams); oEvent.preventDefault(); } }; /** * Handler for touchstart * * @param {sap.ui.base.Event} oEvent which was fired */ SlideTile.prototype.ontouchstart = function (oEvent) { if (this.getScope() === library.GenericTileScope.Display) { // hover of SlideTile should not be triggered when user only touch the Play/Pause button on mobile devices if (jQuery(oEvent.target).hasClass("sapMSTIconClickTapArea")) { this.addStyleClass("sapMSTIconPressed"); } else { this.addStyleClass("sapMSTHvr"); } } }; /** * Handler for touchend * * @param {sap.ui.base.Event} oEvent which was fired */ SlideTile.prototype.ontouchend = function (oEvent) { this.removeStyleClass("sapMSTHvr"); }; /** * Handler for touchcancel * * @param {sap.ui.base.Event} oEvent which was fired */ SlideTile.prototype.ontouchcancel = function (oEvent) { if (this.hasStyleClass("sapMSTIconPressed")) { this.removeStyleClass("sapMSTIconPressed"); } else { this.removeStyleClass("sapMSTHvr"); } }; /** * Handler for keydown event * * @param {sap.ui.base.Event} oEvent which was fired */ SlideTile.prototype.onkeydown = function (oEvent) { if (this.getScope() === library.GenericTileScope.Display) { if (PseudoEvents.events.sapenter.fnCheck(oEvent)) { var oGenericTile = this.getTiles()[this._iCurrentTile]; oGenericTile.onkeydown(oEvent); } } }; /** * Handler for keyup event * * @param {sap.ui.base.Event} oEvent which was fired */ SlideTile.prototype.onkeyup = function (oEvent) { var oParams; if (this.getScope() === library.GenericTileScope.Display) { if (PseudoEvents.events.sapenter.fnCheck(oEvent)) { var oGenericTile = this.getTiles()[this._iCurrentTile]; oGenericTile.onkeyup(oEvent); return; } if (PseudoEvents.events.sapspace.fnCheck(oEvent)) { this._toggleAnimation(); } if (oEvent.which === KeyCodes.B && this._bAnimationPause) { this._scrollToNextTile(true, true); } if (oEvent.which === KeyCodes.F && this._bAnimationPause) { this._scrollToNextTile(true, false); } } else if (this.getScope() === library.GenericTileScope.Actions) { if (PseudoEvents.events.sapselect.fnCheck(oEvent)) { this.firePress(this._getEventParams(oEvent)); oEvent.preventDefault(); } else if (PseudoEvents.events.sapdelete.fnCheck(oEvent) || PseudoEvents.events.sapbackspace.fnCheck(oEvent)) { oParams = { scope: this.getScope(), action: GenericTile._Action.Remove, domRef: this._oRemoveButton.getPopupAnchorDomRef() }; this.firePress(oParams); oEvent.preventDefault(); } } }; /** * Handler for mouseup event * * @param {sap.ui.base.Event} oEvent which was fired */ SlideTile.prototype.onmouseup = function (oEvent) { if (this.getScope() === library.GenericTileScope.Display) { if (this.hasStyleClass("sapMSTIconPressed")) { this._toggleAnimation(); this.removeStyleClass("sapMSTIconPressed"); } } }; /** * Handler for mousedown event * * @param {sap.ui.base.Event} oEvent which was fired */ SlideTile.prototype.onmousedown = function (oEvent) { if (jQuery(oEvent.target).hasClass("sapMSTIconClickTapArea")) { this.addStyleClass("sapMSTIconPressed"); } }; /** * Handles the focusout event. * * @private * @param {jQuery.Event} oEvent Event object */ SlideTile.prototype.onfocusout = function (oEvent) { if (this.getScope() === library.GenericTileScope.Actions) { return; } if (this.getTiles().length > 1 && !this._isFocusInsideST() && this._bAnimationPause) { this._startAnimation(); this._updatePausePlayIcon(); } }; /* --- Public methods --- */ // Overwrites setScope of SlideTile control to be able to call method _setTilePressState SlideTile.prototype.setScope = function (value) { if (this.getScope() !== value) { if (value === library.GenericTileScope.Actions) { this.setProperty("scope", value, true); // Invalidate after the sliding animation is done this._bNeedInvalidate = true; this._stopAnimation(this._bNeedInvalidate); } else { this.setProperty("scope", value); } this._setTilePressState(); } return this; }; /* --- Helpers --- */ /** * @private */ SlideTile.prototype._setupResizeClassHandler = function () { var fnCheckMedia = function () { if (this.getSizeBehavior() === TileSizeBehavior.Small || window.matchMedia("(max-width: 374px)").matches) { this.$().addClass("sapMTileSmallPhone"); } else { this.$().removeClass("sapMTileSmallPhone"); } }.bind(this); jQuery(window).resize(fnCheckMedia); fnCheckMedia(); }; /** * Checks if the focus is inside of SlideTile * * @private * @returns {boolean} True if focus is inside of SlideTile */ SlideTile.prototype._isFocusInsideST = function () { return this.$()[0] === document.activeElement || this.$().find(document.activeElement).length; }; /** * Removes the focus of tiles in SlideTile * * @private */ SlideTile.prototype._removeGTFocus = function () { for (var i = 0; i < this.getTiles().length; i++) { this.getTiles()[i].$().removeAttr("tabindex"); } }; /** * Toggles the animation * * @private */ SlideTile.prototype._toggleAnimation = function () { if (this.getTiles().length > 1) { if (this._bAnimationPause) { this._startAnimation(); } else { this._stopAnimation(); } } this._updatePausePlayIcon(); }; /** * Stops the animation * * @param {boolean} needInvalidate decides whether invalidates the control for setScope * @private */ SlideTile.prototype._stopAnimation = function (needInvalidate) { this._iCurrAnimationTime += Date.now() - this._iStartTime; clearTimeout(this._sTimerId); if (this._iCurrentTile != undefined) { var oWrapperTo = this.$("wrapper-" + this._iCurrentTile); oWrapperTo.stop(); } if (this._iPreviousTile != undefined) { var oWrapperFrom = this.$("wrapper-" + this._iPreviousTile); oWrapperFrom.stop(); } this._bAnimationPause = true; if (this._iCurrAnimationTime > this.getDisplayTime()) { this._scrollToNextTile(true); //Completes the animation and stops } else { if (this.getTiles()[this._iCurrentTile]) { this._setAriaDescriptor(); } if (needInvalidate) { this.invalidate(); } } }; /** * Starts the animation * * @private */ SlideTile.prototype._startAnimation = function () { var iDisplayTime = this.getDisplayTime() - this._iCurrAnimationTime; clearTimeout(this._sTimerId); this._sTimerId = setTimeout(function () { this._scrollToNextTile(); }.bind(this), iDisplayTime); this._iStartTime = Date.now(); this._bAnimationPause = false; }; /** * Scrolls to the tile with given index * * @private * @param {int} tileIndex Index of the tile in the tiles aggregation */ SlideTile.prototype._scrollToTile = function (tileIndex) { if (tileIndex >= 0) { var oWrapperTo = this.$("wrapper-" + tileIndex); var sDir = sap.ui.getCore().getConfiguration().getRTL() ? "right" : "left"; this._changeSizeTo(tileIndex); oWrapperTo.css(sDir, "0rem"); this._iCurrentTile = tileIndex; if (this.getTiles()[tileIndex]) { this._setAriaDescriptor(); } this._updateTilesIndicator(); } }; /** * Scrolls to the next tile, forward or backward * * @private * @param {Boolean} pause Triggers if the animation gets paused or not * @param {Boolean} backward Sets the direction backward or forward */ SlideTile.prototype._scrollToNextTile = function (pause, backward) { var iTransitionTime = this._iCurrAnimationTime - this.getDisplayTime(), bFirstAnimation, iNxtTile, oWrapperFrom, oWrapperTo, sWidthFrom, fWidthTo, fWidthFrom, bChangeSizeBefore, sDir, oDir; iTransitionTime = this.getTransitionTime() - (iTransitionTime > 0 ? iTransitionTime : 0); bFirstAnimation = iTransitionTime === this.getTransitionTime(); if (bFirstAnimation) { if (backward) { iNxtTile = this._getPreviousTileIndex(this._iCurrentTile); } else { iNxtTile = this._getNextTileIndex(this._iCurrentTile); } this._iPreviousTile = this._iCurrentTile; this._iCurrentTile = iNxtTile; } oWrapperTo = this.$("wrapper-" + this._iCurrentTile); sDir = sap.ui.getCore().getConfiguration().getRTL() ? "right" : "left"; if (jQuery.isNumeric(this._iPreviousTile)) { oWrapperFrom = this.$("wrapper-" + this._iPreviousTile); sWidthFrom = oWrapperFrom.css("width"); fWidthTo = parseFloat(oWrapperTo.css("width")); fWidthFrom = parseFloat(sWidthFrom); bChangeSizeBefore = fWidthFrom < fWidthTo; if (bChangeSizeBefore) { this._changeSizeTo(this._iCurrentTile); } if (bFirstAnimation) { oWrapperTo.css(sDir, sWidthFrom); } oDir = {}; if (backward) { oDir[sDir] = sWidthFrom; } else { oDir[sDir] = "-" + sWidthFrom; } oWrapperFrom.animate(oDir, { duration: iTransitionTime, done: function () { if (!bChangeSizeBefore) { this._changeSizeTo(this._iCurrentTile); } oWrapperFrom.css(sDir, ""); }.bind(this) }); if (backward) { oDir[sDir] = "-" + sWidthFrom; oWrapperTo.animate(oDir, 0); } oDir[sDir] = "0rem"; oWrapperTo.animate(oDir, { duration: iTransitionTime, done: function () { this._iCurrAnimationTime = 0; if (this._bNeedInvalidate) { this.invalidate(); } if (!pause) { this._startAnimation(); } }.bind(this) }); } else { this._changeSizeTo(this._iCurrentTile); oWrapperTo.css(sDir, "0rem"); } if (this.getTiles()[this._iCurrentTile]) { this._setAriaDescriptor(); } this._updateTilesIndicator(); }; /** * Sets the ARIA descriptor * * @private */ SlideTile.prototype._setAriaDescriptor = function () { var sText, sScope, aTiles, oCurrentTile; sScope = this.getScope(); aTiles = this.getTiles(); oCurrentTile = aTiles[this._iCurrentTile]; sText = oCurrentTile._getAriaText().replace(/\s/g, " ");// Gets Tile's ARIA text and collapses whitespaces if (sScope === library.GenericTileScope.Actions) { sText = this._oRb.getText("GENERICTILE_ACTIONS_ARIA_TEXT") + "\n" + sText; } else if (aTiles.length > 1 && sScope === library.GenericTileScope.Display) { sText += "\n" + this._oRb.getText("SLIDETILE_MULTIPLE_CONTENT") + "\n" + this._oRb.getText("SLIDETILE_TOGGLE_SLIDING"); if (this._bAnimationPause) { sText += "\n" + this._oRb.getText("SLIDETILE_SCROLL_BACK") + "\n" + this._oRb.getText("SLIDETILE_SCROLL_FORWARD"); } } sText += "\n" + this._oRb.getText("SLIDETILE_ACTIVATE"); this.$().attr("aria-label", sText); }; /** * Changes the size to given size * * @private * @param {int} tileIndex Index of the element in the tiles aggregation */ SlideTile.prototype._changeSizeTo = function (tileIndex) { var oTile = this.getTiles()[tileIndex]; if (!oTile) { return; } if (this._sFrameType) { this.$().removeClass(this._sFrameType); } if (this._sSize) { this.$().removeClass(this._sSize); } this.$().addClass(oTile.getFrameType()).addClass(oTile.getSize()); this._sFrameType = oTile.getFrameType(); this._sSize = oTile.getSize(); }; /** * Returns the index of the previous tile based on the current index * * @private * @param {int} tileIndex Index of the element in the tiles aggregation * @returns {int} Index of the previous tile */ SlideTile.prototype._getPreviousTileIndex = function (tileIndex) { if (tileIndex > 0) { return tileIndex - 1; } else { return this.getTiles().length - 1; } }; /** * Returns the index of the next tile based on the current index * * @private * @param {int} tileIndex Index of the element in the tiles aggregation * @returns {int} Index of the next tile */ SlideTile.prototype._getNextTileIndex = function (tileIndex) { if (tileIndex + 1 < this.getTiles().length) { return tileIndex + 1; } else { return 0; } }; /** * Updates multiple tiles indicator * * @private */ SlideTile.prototype._updateTilesIndicator = function () { var $currentBullet; for (var i = 0; i < this.getTiles().length; i++) { $currentBullet = this.$("tileIndicator-" + i); if (i === this._iCurrentTile) { $currentBullet.addClass("sapMSTActive"); } else { $currentBullet.removeClass("sapMSTActive"); } } }; /** * Sets information about the animation state on the icon * * @private */ SlideTile.prototype._updatePausePlayIcon = function () { if (this._bAnimationPause) { this.getAggregation("_pausePlayIcon").setSrc("sap-icon://media-play"); this.$().removeClass("sapMSTPauseIcon"); } else { this.getAggregation("_pausePlayIcon").setSrc("sap-icon://media-pause"); this.$().addClass("sapMSTPauseIcon"); } }; /** * Disables or enables the press event of the GenericTiles inside the SlideTile * * @private */ SlideTile.prototype._setTilePressState = function () { var oTiles = this.getTiles(), bTilePressEnabled = this.getScope() === library.GenericTileScope.Display;//if scope is 'Display', enable press events of GenericTiles for (var i = 0; i < oTiles.length; i++) { oTiles[i].setPressEnabled(bTilePressEnabled); } }; /** * Checks if the given tile has NewsContent * * @param {int} tileIndex Index of the tile in the tiles aggregation * @returns {boolean} True when the tile has NewsContent, otherwise false * @private */ SlideTile.prototype._hasNewsContent = function (tileIndex) { var aTileContent = this.getTiles()[tileIndex].getTileContent(); for (var i = 0; i < aTileContent.length; i++) { if (aTileContent[i]._getContentType() === "News") { return true; } } return false; }; /** * Determines the current action depending on the tile's scope. * @param {sap.ui.base.Event} oEvent which was fired * @returns {object} An object containing the tile's scope and the action which triggered the event * @private */ SlideTile.prototype._getEventParams = GenericTile.prototype._getEventParams; return SlideTile; });