UNPKG

@openui5/sap.m

Version:

OpenUI5 UI Library sap.m

973 lines (829 loc) 31.8 kB
/*! * 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.RatingIndicator. sap.ui.define([ './library', "sap/base/i18n/Localization", 'sap/ui/core/Control', "sap/ui/core/Lib", 'sap/ui/core/StaticArea', 'sap/ui/core/theming/Parameters', './RatingIndicatorRenderer', "sap/ui/events/KeyCodes", "sap/base/Log", "sap/ui/thirdparty/jquery", 'sap/ui/core/LabelEnablement' ], function(library, Localization, Control, Library, StaticArea, Parameters, RatingIndicatorRenderer, KeyCodes, Log, jQuery, LabelEnablement) { "use strict"; // shortcut for sap.m.RatingIndicatorVisualMode var RatingIndicatorVisualMode = library.RatingIndicatorVisualMode; /** * Constructor for a new RatingIndicator. * * @param {string} [sId] ID for the new control, generated automatically if no ID is given * @param {object} [mSettings] Initial settings for the new control * Enables users to rate an item on a numeric scale. * @class * The rating indicator is used to display a specific number of icons that are used to rate an item. Additionally it is also used to display the average over all ratings. * <h3>Structure</h3> * <ul> * <li>The rating indicator can use different icons (default: stars) which are defined as URIs in the properties <code>iconHovered</code>, <code>iconSelected</code> and <code>iconUnselected</code>.</li> * <li>The rating indicator can display half-values ({@link sap.m.RatingIndicatorVisualMode visualMode} = Half) when it is used to show the average. Half-values can't be selected by the user.</li> * </ul> * <h3>Usage</h3> * The preferred number of icons is between 5 (default) and 7. * <h3>Responsive Behavior</h3> * You can display icons in 4 recommended sizes: * <ul> * <li>large - 32px</li> * <li>medium(default) - 24px</li> * <li>small - 22px</li> * <li>XS - 12px</li> * </ul> * <b>Note:</b> It is not recommended to use the XS size as an editable rating indicator. If an editable rating indicator is needed then it is recommended to set the size S or above to be compliant with minimum touch size.</h4> * <b>Note:</b> If no icon size is set, the rating indicator will set it according to the content density.</h4> * @extends sap.ui.core.Control * * @author SAP SE * @version 1.146.0 * * @constructor * @public * @since 1.14 * @alias sap.m.RatingIndicator * @see {@link fiori:https://experience.sap.com/fiori-design-web/rating-indicator/ Rating Indicator} */ var RatingIndicator = Control.extend("sap.m.RatingIndicator", /** @lends sap.m.RatingIndicator.prototype */ { metadata: { interfaces: ["sap.ui.core.IFormContent"], library: "sap.m", properties: { /** * Value "true" is required to let the user rate with this control. It is recommended to set this parameter to "false" for the "Small" size which is meant for indicating a value only */ enabled: {type: "boolean", group: "Behavior", defaultValue: true}, /** * The number of displayed rating symbols */ maxValue: {type: "int", group: "Behavior", defaultValue: 5}, /** * The indicated value of the rating */ value: {type: "float", group: "Behavior", defaultValue: 0, bindable: "bindable"}, /** * The Size of the image or icon to be displayed. The default value depends on the theme. Please be sure that the size is corresponding to a full pixel value as some browsers don't support subpixel calculations. Recommended size is 1.5rem (24px) for normal, 1.375rem (22px) for small, and 2rem (32px) for large icons correspondingly. */ iconSize: {type: "sap.ui.core.CSSSize", group: "Behavior", defaultValue: null}, /** * The URI to the icon font icon or image that will be displayed for selected rating symbols. A star icon will be used if the property is not set */ iconSelected: {type: "sap.ui.core.URI", group: "Behavior", defaultValue: null}, /** * The URI to the icon font icon or image that will be displayed for all unselected rating symbols. A star icon will be used if the property is not set */ iconUnselected: {type: "sap.ui.core.URI", group: "Behavior", defaultValue: null}, /** * The URI to the icon font icon or image that will be displayed for hovered rating symbols. A star icon will be used if the property is not set */ iconHovered: {type: "sap.ui.core.URI", group: "Behavior", defaultValue: null}, /** * Defines how float values are visualized: Full, Half (see enumeration RatingIndicatorVisualMode) */ visualMode: {type: "sap.m.RatingIndicatorVisualMode", group: "Behavior", defaultValue: RatingIndicatorVisualMode.Half}, /** * The RatingIndicator in displayOnly mode is not interactive, not editable, not focusable, and not in the tab chain. This setting is used for forms in review mode. * @since 1.50.0 */ displayOnly : {type : "boolean", group : "Behavior", defaultValue : false}, /** * Defines whether the user is allowed to edit the RatingIndicator. If editable is false the control is focusable, and in the tab chain but not interactive. * @since 1.52.0 */ editable : {type : "boolean", group : "Behavior", defaultValue : true}, /** * Indicates that the control is required. This property is only needed for accessibility purposes when a single relationship between * the control and a label (see aggregation <code>labelFor</code> of <code>sap.m.Label</code>) cannot be established * (e.g. one label should label multiple controls). * @since 1.116 */ required : {type : "boolean", group : "Misc", defaultValue : false} }, associations: { /** * Association to controls / ids which describe this control (see WAI-ARIA attribute aria-describedby). */ ariaDescribedBy: {type: "sap.ui.core.Control", multiple: true, singularName: "ariaDescribedBy"}, /** * 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: { /** * The event is fired when the user has done a rating. */ change: { parameters: { /** * The rated value */ value: {type: "int"} } }, /** * This event is triggered during the dragging period, each time the rating value changes. */ liveChange: { parameters: { /** * The current value of the rating after a live change event. */ value: {type: "float"} } } }, designtime: "sap/m/designtime/RatingIndicator.designtime" }, renderer: RatingIndicatorRenderer }); /* =========================================================== */ /* temporary flags for jslint syntax check */ /* =========================================================== */ /*jslint nomen: false */ /* =========================================================== */ /* begin: API methods */ /* =========================================================== */ RatingIndicator.sizeMapppings = {}; RatingIndicator.iconPaddingMappings = {}; RatingIndicator.paddingValueMappping = {}; /** * Initializes the control. * * @private */ RatingIndicator.prototype.init = function () { // deactivate text selection on drag events this.allowTextSelection(false); this._iIconCounter = 0; this._fHoverValue = 0; this._oResourceBundle = Library.getResourceBundleFor('sap.m'); }; /** * Sets the rating value. The method is automatically checking whether the value is in the valid range of 0-{@link #getMaxValue maxValue} and if it is a valid number. Calling the setter with null or undefined will reset the value to it's default. * * @param {float|string} vValue The rating value to be set. * @returns {this} Returns <code>this</code> to facilitate method chaining. * @override * @public */ RatingIndicator.prototype.setValue = function (vValue) { // Allow passing float values as strings to support oData v2. Edm.Double type format var fValue = typeof vValue !== "string" ? vValue : Number(vValue); // validates the property and sets null/undefined values to the default fValue = this.validateProperty("value", fValue); // do not set negative values (will be returned by calculation function if there is an error) if (fValue < 0) { return this; } // check for valid numbers if (isNaN(fValue)) { Log.warning('Ignored new rating value "' + vValue + '" because it is NAN'); // check if the number is in the range 0-maxValue (only if control is rendered) // if control is not rendered it is handled by onBeforeRendering() } else if (this.$().length && (fValue > this.getMaxValue())) { Log.warning('Ignored new rating value "' + fValue + '" because it is out of range (0-' + this.getMaxValue() + ')'); } else { fValue = this._roundValueToVisualMode(fValue); this.setProperty("value", fValue); // always set hover value to current value to allow keyboard / mouse / touch navigation this._fHoverValue = fValue; } return this; }; /** * Handler for theme changing * * @param {jQuery.Event} oEvent The event object passed to the event handler. */ RatingIndicator.prototype.onThemeChanged = function (oEvent) { this.invalidate(); // triggers a re-rendering }; /** * Called before rendering starts by the renderer to readjust values outside the range. * * @private */ RatingIndicator.prototype.onBeforeRendering = function () { var fVal = this.getValue(); var iMVal = this.getMaxValue(); if (fVal > iMVal) { this.setValue(iMVal); Log.warning("Set value to maxValue because value is > maxValue (" + fVal + " > " + iMVal + ")."); } else if (fVal < 0) { this.setValue(0); Log.warning("Set value to 0 because value is < 0 (" + fVal + " < 0)."); } var sIconSize = this.getIconSize(); if (sIconSize) { this._setRegularSizes(sIconSize); } else if (this.getDisplayOnly()) { this._setDisplayOnlySizes(); } else { this._setContentDensitySizes(); } }; RatingIndicator.prototype._isRequired = function () { return this.getRequired() || LabelEnablement.isRequired(this); }; RatingIndicator.prototype._setDisplayOnlySizes = function () { var sIconSize = "sapUiRIIconSizeDisplayOnly", sIconPaddingSize = "sapUiRIIconPaddingDisplayOnly"; if (RatingIndicator.sizeMapppings[sIconSize] && RatingIndicator.paddingValueMappping[sIconPaddingSize]) { this._iPxIconSize = RatingIndicator.sizeMapppings[sIconSize]; this._iPxPaddingSize = RatingIndicator.paddingValueMappping[sIconPaddingSize]; return; } var mParamеters = Object.assign({ // add global styles as default "sapUiRIIconSizeDisplayOnly": "1.5rem", "sapUiRIIconPaddingDisplayOnly": "0.1875rem" }, Parameters.get({ name: [sIconSize, sIconPaddingSize], callback: function(mParams) { this.setIconAndPaddingSizes(sIconSize, sIconPaddingSize, mParams[sIconSize], mParams[sIconPaddingSize]); }.bind(this) })); this.setIconAndPaddingSizes(sIconSize, sIconPaddingSize, mParamеters[sIconSize], mParamеters[sIconPaddingSize]); }; RatingIndicator.prototype._setContentDensitySizes = function () { var sDensityMode = this._getDensityMode(); var sSizeKey = "sapUiRIIconSize" + sDensityMode; var sPaddingKey = "sapUiRIIconPadding" + sDensityMode; if (RatingIndicator.sizeMapppings[sSizeKey] && RatingIndicator.paddingValueMappping[sPaddingKey]) { this._iPxIconSize = RatingIndicator.sizeMapppings[sSizeKey]; this._iPxPaddingSize = RatingIndicator.paddingValueMappping[sPaddingKey]; return; } var mParamеters = Parameters.get({ name: [sSizeKey, sPaddingKey], callback: function(mParams) { this.setIconAndPaddingSizes(sSizeKey, sPaddingKey, mParams[sSizeKey], mParams[sPaddingKey]); }.bind(this) }); if (mParamеters) { this.setIconAndPaddingSizes(sSizeKey, sPaddingKey, mParamеters[sSizeKey], mParamеters[sPaddingKey]); } }; RatingIndicator.prototype._setRegularSizes = function (sIconSize) { RatingIndicator.sizeMapppings[sIconSize] = RatingIndicator.sizeMapppings[sIconSize] || this._toPx(sIconSize); var iPxIconSize = RatingIndicator.sizeMapppings[sIconSize]; RatingIndicator.iconPaddingMappings[iPxIconSize] = RatingIndicator.iconPaddingMappings[iPxIconSize] || "sapUiRIIconPadding" + this._getIconSizeLabel(iPxIconSize); var sPaddingClass = RatingIndicator.iconPaddingMappings[iPxIconSize]; if (RatingIndicator.paddingValueMappping[sPaddingClass]) { this._iPxIconSize = RatingIndicator.sizeMapppings[sIconSize]; this._iPxPaddingSize = RatingIndicator.paddingValueMappping[sPaddingClass]; return; } var sParam = Parameters.get({ name: sPaddingClass, callback: function (sPadding) { this.setIconAndPaddingSizes(sIconSize, sPaddingClass, RatingIndicator.sizeMapppings[sIconSize], sPadding); }.bind(this) }); if (sParam) { this.setIconAndPaddingSizes(sIconSize, sPaddingClass, RatingIndicator.sizeMapppings[sIconSize], sParam); } }; RatingIndicator.prototype.setIconAndPaddingSizes = function (sSizeKey, sPaddingKey, sSize, sPadding) { RatingIndicator.sizeMapppings[sSizeKey] = this._toPx(sSize); RatingIndicator.paddingValueMappping[sPaddingKey] = this._toPx(sPadding); this._iPxIconSize = RatingIndicator.sizeMapppings[sSizeKey]; this._iPxPaddingSize = RatingIndicator.paddingValueMappping[sPaddingKey]; }; /** * Called by the framework when rendering is completed. * * @private */ RatingIndicator.prototype.onAfterRendering = function () { this._updateAriaValues(); }; /** * Destroys the control. * * @private */ RatingIndicator.prototype.exit = function () { this._iIconCounter = null; this._fStartValue = null; this._iPxIconSize = null; this._iPxPaddingSize = null; this._fHoverValue = null; this._oResourceBundle = null; }; /* =========================================================== */ /* end: API methods */ /* =========================================================== */ /* =========================================================== */ /* begin: internal methods and properties */ /* =========================================================== */ /** * get the form factor (Cozy/Compact/Condensed) * @returns {string} The form factor * @private */ RatingIndicator.prototype._getDensityMode = function () { var aDensityModes = [ {name: "Cozy", style: "sapUiSizeCozy"}, {name: "Compact", style: "sapUiSizeCompact"}, {name: "Condensed", style: "sapUiSizeCondensed"} ], sDensityClass, sDensityMode, i; for (i in aDensityModes) { sDensityClass = aDensityModes[i].style; if (jQuery("html").hasClass(sDensityClass) || jQuery("." + sDensityClass).length > 0) { sDensityMode = aDensityModes[i].name; } } return sDensityMode || aDensityModes[0].name; }; /** * Get icon size label * @param {number} iPxIconSize The size of the icon in pixels * @returns {string} The icon size * @private */ RatingIndicator.prototype._getIconSizeLabel = function (iPxIconSize) { switch (true) { case (iPxIconSize >= 32): return "L"; case (iPxIconSize >= 24): return "M"; case (iPxIconSize >= 22): return "S"; case (iPxIconSize >= 12): return "XS"; default: return "M"; } }; RatingIndicator.prototype._toPx = function (cssSize) { var vScopeVal = Math.round(cssSize), oScopeTest; if (isNaN(vScopeVal)) { if (RegExp("^(auto|0)$|^[+-\.]?[0-9].?([0-9]+)?(px|em|rem|ex|%|in|cm|mm|pt|pc)$").test(cssSize)) { oScopeTest = jQuery('<div>&nbsp;</div>') .css({"display": "none", "width": cssSize, "margin": 0, "padding": 0, "height": "auto", "line-height": 1, "border": 0, "overflow": "hidden"}) .appendTo(StaticArea.getDomRef()); vScopeVal = oScopeTest.width(); oScopeTest.remove(); } else { return false; } } return Math.round(vScopeVal); }; /** * Updates the controls's interface to reflect a value change of the rating. * * @param {float} fValue the rating value to be set * @param {boolean} bHover if this parameter is set to true, the hover mode is activated and the value is displayed with {@link #getIconHovered iconHovered} instead of {@link #getIconSelected iconSelected} * @private */ RatingIndicator.prototype._updateUI = function (fValue, bHover) { // save a reference on all needed DOM elements var $SelectedDiv = this.$("sel"), $UnselectedContainerDiv = this.$("unsel-wrapper"), $HoveredDiv = this.$("hov"), // calculate padding, size, and measurement fIconSize = this._iPxIconSize, fIconPadding = this._iPxPaddingSize, sIconSizeMeasure = "px", iSymbolCount = this.getMaxValue(), // calculate the width for the selected elements and the complete width iSelectedWidth = fValue * fIconSize + (Math.round(fValue) - 1) * fIconPadding, iWidth = iSymbolCount * (fIconSize + fIconPadding) - fIconPadding; // always set hover value to current value to allow keyboard / mouse / touch navigation this._fHoverValue = fValue; if (iSelectedWidth < 0) { // width should not be negative iSelectedWidth = 0; } this._updateAriaValues(fValue); // adjust unselected container with the remaining width $UnselectedContainerDiv.width((iWidth - iSelectedWidth) + sIconSizeMeasure); // update the DOM elements to reflect the value by setting the width of the div elements if (bHover) { // hide selected div & adjust hover div $HoveredDiv.width(iSelectedWidth + sIconSizeMeasure); $SelectedDiv.hide(); $HoveredDiv.show(); } else { // hide hovered div & adjust selected div $SelectedDiv.width(iSelectedWidth + sIconSizeMeasure); $HoveredDiv.hide(); $SelectedDiv.show(); } Log.debug("Updated rating UI with value " + fValue + " and hover mode " + bHover); }; /** * Updates the ARIA values. * @param {string} newValue The new ARIA value * @private */ RatingIndicator.prototype._updateAriaValues = function (newValue) { var $this = this.$(); var fValue; if (newValue === undefined) { fValue = this.getValue(); } else { fValue = newValue; } var fMaxValue = this.getMaxValue(); $this.attr("aria-valuenow", fValue); $this.attr("aria-valuemax", fMaxValue); var sValueText = this._oResourceBundle.getText("RATING_VALUEARIATEXT", [fValue, fMaxValue]); $this.attr("aria-valuetext", sValueText); }; /** * Calculated the selected value based on the event position of the tap/move/click event. * This function is called by the event handlers to determine the {@link #getValue value} of the rating. * * @param {jQuery.Event} oEvent The event object passed to the event handler. * @returns {float} The rounded rating value based on {@link #getVisualMode visualMode}. * @private */ RatingIndicator.prototype._calculateSelectedValue = function (oEvent) { var selectedValue = 0, percentageWidth = 0.0, oControlRoot = this.$(), fControlPadding = (oControlRoot.innerWidth() - oControlRoot.width()) / 2, oEventPosition, bRtl = Localization.getRTL(); if (oEvent.targetTouches) { oEventPosition = oEvent.targetTouches[0]; } else { oEventPosition = oEvent; } // get the event position for tap/touch/click events if (!oEventPosition || !oEventPosition.pageX) { // desktop fallback oEventPosition = oEvent; if ((!oEventPosition || !oEventPosition.pageX) && oEvent.changedTouches) { // touchend fallback oEventPosition = oEvent.changedTouches[0]; } } // if an event position is not present we stop if (!oEventPosition.pageX) { // TODO: find out why this happens return parseFloat(selectedValue); } // check if event is happening inside of the control area (minus padding of the control) if (oEventPosition.pageX < oControlRoot.offset().left) { selectedValue = 0; } else if ((oEventPosition.pageX - oControlRoot.offset().left) > oControlRoot.innerWidth() - fControlPadding) { selectedValue = this.getMaxValue(); } else { // calculate the selected value based on the percentage value of the event position percentageWidth = (oEventPosition.pageX - oControlRoot.offset().left - fControlPadding) / oControlRoot.width(); selectedValue = percentageWidth * this.getMaxValue(); } // rtl support if (bRtl) { selectedValue = this.getMaxValue() - selectedValue; } // return rounded value based on the control's visual mode return this._roundValueToVisualMode(selectedValue, true); }; /** * Rounds the float value according to the parameter {@link #getVisualMode visualMode}: * - A value of "Full" will result in integer values. * - A value of "Half" will result in float values rounded to 0.5. * * @param {float} fValue The rating value. * @param {boolean} bInputMode whether the given value represents user input * @returns {float} The rounded rating value. * @private */ RatingIndicator.prototype._roundValueToVisualMode = function (fValue, bInputMode) { if (bInputMode) { // we only support full selection of stars if (fValue < 0.25) { // to be able to also select 0 stars fValue = 0; } else if (fValue < this.getMaxValue() - 0.4) { // to optimize selection behaviour //threshold is increased to take into account the font's stroke width // BCP: 1870119890 fValue += 0.4; } fValue = Math.round(fValue); } else { // for display we round to the correct behavior if (this.getVisualMode() === RatingIndicatorVisualMode.Full) { fValue = Math.round(fValue); } else if (this.getVisualMode() === RatingIndicatorVisualMode.Half) { fValue = Math.round(fValue * 2) / 2; } } return parseFloat(fValue); }; /** * Gets the new value after a single value increase. * * @returns {float} The increased rating value. * @private */ RatingIndicator.prototype._getIncreasedValue = function () { var iMaxValue = this.getMaxValue(), fValue = this.getValue() + this._getValueChangeStep(); if (fValue > iMaxValue) { fValue = iMaxValue; } return fValue; }; /** * Gets the new value after a single value decrease. * * @returns {float} The decreased rating value. * @private */ RatingIndicator.prototype._getDecreasedValue = function () { var fValue = this.getValue() - this._getValueChangeStep(); if (fValue < 0) { fValue = 0; } return fValue; }; /** * Gets the step that should be used for single keyboard value change operation. * * @returns {float} The value change step. * @private */ RatingIndicator.prototype._getValueChangeStep = function () { var sVisualMode = this.getVisualMode(), fStep; switch (sVisualMode) { case RatingIndicatorVisualMode.Full: fStep = 1; break; case RatingIndicatorVisualMode.Half: // If the value is half, we return 0.5 in order to allow/force only full value selection via keyboard. if (this.getValue() % 1 === 0.5) { fStep = 0.5; } else { fStep = 1; } break; default: Log.warning("VisualMode not supported", sVisualMode); } return fStep; }; /* =========================================================== */ /* end: internal methods */ /* =========================================================== */ /* =========================================================== */ /* begin: event handlers */ /* =========================================================== */ /** * Handle the touch start event happening on the rating. * The UI will be updated accordingly to show a preview of the rating value without actually setting the value. * * @param {jQuery.Event} oEvent The event object. * @private */ RatingIndicator.prototype.ontouchstart = function (oEvent) { if (oEvent.which == 2 || oEvent.which == 3 || !this.getEnabled() || this.getDisplayOnly() || !this.getEditable()) { return; } // mark the event for components that needs to know if the event was handled by this Control oEvent.setMarked(); if (!this._touchEndProxy) { this._touchEndProxy = jQuery.proxy(this._ontouchend, this); } if (!this._touchMoveProxy) { this._touchMoveProxy = jQuery.proxy(this._ontouchmove, this); } // here also bound to the mouseup mousemove event to enable it working in // desktop browsers jQuery(document).on("touchend.sapMRI touchcancel.sapMRI mouseup.sapMRI", this._touchEndProxy); jQuery(document).on("touchmove.sapMRI mousemove.sapMRI", this._touchMoveProxy); this._fStartValue = this.getValue(); var fValue = this._calculateSelectedValue(oEvent); if (fValue >= 0 && fValue <= this.getMaxValue()) { this._updateUI(fValue, true); if (this._fStartValue !== fValue) { // if the value if not the same this.fireLiveChange({value: fValue}); } } }; /** * Handle the touch move event on the rating. * The UI will be updated accordingly to show a preview of the rating value without actually setting the value. * * @param {jQuery.Event} oEvent The event object. * @private */ RatingIndicator.prototype._ontouchmove = function (oEvent) { if (oEvent.isMarked("delayedMouseEvent")) { return; } // note: prevent native document scrolling oEvent.preventDefault(); if (this.getEnabled()) { var fValue = this._calculateSelectedValue(oEvent); if (fValue >= 0 && fValue <= this.getMaxValue()) { this._updateUI(fValue, true); if (this._fStartValue !== fValue) { // if the value if not the same this.fireLiveChange({value: fValue}); this._fStartValue = fValue; // update the start value to the new one } } } }; /** * Handle the touch end event on the rating. * A change event will be fired when the touch ends, the value will be set, and the UI will be updated accordingly. * * @param {jQuery.Event} oEvent The event object. * @private */ RatingIndicator.prototype._ontouchend = function (oEvent) { if (oEvent.isMarked("delayedMouseEvent")) { return; } if (this.getEnabled()) { var fValue = this._calculateSelectedValue(oEvent); // When same rating is chosen, set the rating to 0 if (this.getValue() === fValue) { fValue = 0; } this._updateUI(fValue, false); if (this.getValue() !== fValue) { // if the value if not the same this.setProperty("value", fValue, true); this.fireLiveChange({value: fValue}); this.fireChange({value: fValue}); } jQuery(document).off("touchend.sapMRI touchcancel.sapMRI mouseup.sapMRI", this._touchEndProxy); jQuery(document).off("touchmove.sapMRI mousemove.sapMRI", this._touchMoveProxy); // remove unused properties delete this._fStartValue; } }; /** * Handle the touch end event. * * @param {jQuery.Event} oEvent The event object. * @private */ RatingIndicator.prototype.ontouchcancel = RatingIndicator.prototype.ontouchend; /** * Keyboard navigation event when the user presses Arrow Right (Left in RTL case) or Arrow Up. * * @param {jQuery.Event} oEvent The event object. * @private */ RatingIndicator.prototype.onsapincrease = function (oEvent) { var fValue = this._getIncreasedValue(); this._handleKeyboardValueChange(oEvent, fValue); }; /** * Keyboard navigation event when the user presses Arrow Left (Right in RTL case) or Arrow Down. * * @param {jQuery.Event} oEvent The event object. * @private */ RatingIndicator.prototype.onsapdecrease = function (oEvent) { var fValue = this._getDecreasedValue(); this._handleKeyboardValueChange(oEvent, fValue); }; /** * Keyboard navigation event when the user presses Home. * * @param {jQuery.Event} oEvent oEvent The event object. * @private */ RatingIndicator.prototype.onsaphome = function (oEvent) { var fValue = 0; this._handleKeyboardValueChange(oEvent, fValue); }; /** * Keyboard navigation event when the user presses End. * * @param {jQuery.Event} oEvent oEvent The event object. * @private */ RatingIndicator.prototype.onsapend = function (oEvent) { var fValue = this.getMaxValue(); this._handleKeyboardValueChange(oEvent, fValue); }; /** * Keyboard navigation event when the user presses Enter or Space. * * @param {jQuery.Event} oEvent The event object. * @private */ RatingIndicator.prototype.onsapselect = function (oEvent) { var fValue; if (this.getValue() === this.getMaxValue()) { // if the max value is reached, set to 0 fValue = 0; } else { fValue = this._getIncreasedValue(); } this._handleKeyboardValueChange(oEvent, fValue); }; /** * Keyboard handling event when the user presses number keys. * * @param {jQuery.Event} oEvent oEvent The event object. * @returns {boolean} False, if the control is in read-only mode * @private */ RatingIndicator.prototype.onkeyup = function (oEvent) { var iMaxValue = this.getMaxValue(); if (!this.getEnabled() || this.getDisplayOnly() || !this.getEditable()) { return false; } switch (oEvent.which) { case KeyCodes.DIGIT_0: case KeyCodes.NUMPAD_0: this.setValue(0); break; case KeyCodes.DIGIT_1: case KeyCodes.NUMPAD_1: this.setValue(1); break; case KeyCodes.DIGIT_2: case KeyCodes.NUMPAD_2: this.setValue(Math.min(2, iMaxValue)); break; case KeyCodes.DIGIT_3: case KeyCodes.NUMPAD_3: this.setValue(Math.min(3, iMaxValue)); break; case KeyCodes.DIGIT_4: case KeyCodes.NUMPAD_4: this.setValue(Math.min(4, iMaxValue)); break; case KeyCodes.DIGIT_5: case KeyCodes.NUMPAD_5: this.setValue(Math.min(5, iMaxValue)); break; case KeyCodes.DIGIT_6: case KeyCodes.NUMPAD_6: this.setValue(Math.min(6, iMaxValue)); break; case KeyCodes.DIGIT_7: case KeyCodes.NUMPAD_7: this.setValue(Math.min(7, iMaxValue)); break; case KeyCodes.DIGIT_8: case KeyCodes.NUMPAD_8: this.setValue(Math.min(8, iMaxValue)); break; case KeyCodes.DIGIT_9: case KeyCodes.NUMPAD_9: this.setValue(Math.min(9, iMaxValue)); break; } }; /** * Handle the event and set the new value. * * @param {jQuery.Event} oEvent The event object. * @param {float} fValue The new value that should be set. * @private */ RatingIndicator.prototype._handleKeyboardValueChange = function (oEvent, fValue) { if (!this.getEnabled() || this.getDisplayOnly() || !this.getEditable()) { return; } if (fValue !== this.getValue()) { this.setValue(fValue); this.fireLiveChange({value: fValue}); this.fireChange({value: fValue}); } // stop browsers default behavior if (oEvent) { oEvent.preventDefault(); oEvent.stopPropagation(); } }; /* =========================================================== */ /* end: event handlers */ /* =========================================================== */ /** * @returns {{role: string, type: string, description: string, focusable: boolean, enabled: boolean, editable: boolean}} Current accessibility state of the control * @see sap.ui.core.Control#getAccessibilityInfo * @protected */ RatingIndicator.prototype.getAccessibilityInfo = function () { var oBundle = Library.getResourceBundleFor("sap.m"); return { role: "slider", type: oBundle.getText("ACC_CTR_TYPE_RATING"), description: oBundle.getText("ACC_CTR_STATE_RATING", [this.getValue(), this.getMaxValue()]), focusable: this.getEnabled() && !this.getDisplayOnly(), enabled: this.getEnabled(), editable: this.getEditable() }; }; return RatingIndicator; });