diff --git a/packages/base/src/UI5Element.js b/packages/base/src/UI5Element.js index 9ab23904e245..9060f853f26f 100644 --- a/packages/base/src/UI5Element.js +++ b/packages/base/src/UI5Element.js @@ -11,6 +11,7 @@ import getConstructableStyle from "./theming/getConstructableStyle.js"; import createComponentStyleTag from "./theming/createComponentStyleTag.js"; import getEffectiveStyle from "./theming/getEffectiveStyle.js"; import Integer from "./types/Integer.js"; +import Float from "./types/Float.js"; import { kebabToCamelCase, camelToKebabCase } from "./util/StringHelper.js"; import isValidPropertyName from "./util/isValidPropertyName.js"; import isSlot from "./util/isSlot.js"; @@ -306,6 +307,9 @@ class UI5Element extends HTMLElement { if (propertyTypeClass === Integer) { newValue = parseInt(newValue); } + if (propertyTypeClass === Float) { + newValue = parseFloat(newValue); + } this[nameInCamelCase] = newValue; } } diff --git a/packages/base/src/types/Float.js b/packages/base/src/types/Float.js new file mode 100644 index 000000000000..37508022a7fc --- /dev/null +++ b/packages/base/src/types/Float.js @@ -0,0 +1,10 @@ +import DataType from "./DataType.js"; + +class Float extends DataType { + static isValid(value) { + // Assuming that integers are floats as well! + return Number(value) === value; + } +} + +export default Float; diff --git a/packages/main/bundle.esm.js b/packages/main/bundle.esm.js index 08b5728da6e8..df743600da70 100644 --- a/packages/main/bundle.esm.js +++ b/packages/main/bundle.esm.js @@ -57,6 +57,7 @@ import Select from "./dist/Select.js"; import Switch from "./dist/Switch.js"; import MessageStrip from "./dist/MessageStrip.js"; import MultiComboBox from "./dist/MultiComboBox.js"; +import RatingIndicator from "./dist/RatingIndicator.js"; import TabContainer from "./dist/TabContainer.js"; import Tab from "./dist/Tab.js"; import TabSeparator from "./dist/TabSeparator.js"; diff --git a/packages/main/src/RatingIndicator.hbs b/packages/main/src/RatingIndicator.hbs new file mode 100644 index 000000000000..cc111850e83a --- /dev/null +++ b/packages/main/src/RatingIndicator.hbs @@ -0,0 +1,29 @@ +
diff --git a/packages/main/src/RatingIndicator.js b/packages/main/src/RatingIndicator.js new file mode 100644 index 000000000000..b5ca70e6f9f0 --- /dev/null +++ b/packages/main/src/RatingIndicator.js @@ -0,0 +1,256 @@ +import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js"; +import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js"; +import { + isDown, + isUp, + isLeft, + isRight, + isSpace, + isEnter, +} from "@ui5/webcomponents-base/dist/Keys.js"; +import { fetchI18nBundle, getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js"; +import Integer from "@ui5/webcomponents-base/dist/types/Integer.js"; +import Float from "@ui5/webcomponents-base/dist/types/Float.js"; +import { + RATING_INDICATOR_TEXT, +} from "./generated/i18n/i18n-defaults.js"; +import RatingIndicatorTemplate from "./generated/templates/RatingIndicatorTemplate.lit.js"; + +// Styles +import RatingIndicatorCss from "./generated/themes/RatingIndicator.css.js"; + +/** + * @public + */ +const metadata = { + tag: "ui5-rating-indicator", + properties: /** @lends sap.ui.webcomponents.main.RatingIndicator.prototype */ { + + /** + * The indicated value of the rating + *ui5-rating-indicator
is disabled.
+ * @type {boolean}
+ * @defaultvalue false
+ * @public
+ */
+ disabled: {
+ type: Boolean,
+ },
+
+ /**
+ * @type {boolean}
+ * @defaultvalue false
+ * @public
+ */
+ readonly: {
+ type: Boolean,
+ },
+
+ /**
+ * @private
+ */
+ _stars: {
+ type: Object,
+ multiple: true,
+ },
+
+ /**
+ * @private
+ */
+ _focused: {
+ type: Boolean,
+ },
+ },
+ slots: /** @lends sap.ui.webcomponents.main.RatingIndicator.prototype */ {
+ //
+ },
+ events: /** @lends sap.ui.webcomponents.main.RatingIndicator.prototype */ {
+
+ /**
+ * The event is fired when the value changes.
+ *
+ * @event
+ * @public
+ */
+ change: {},
+ },
+};
+
+/**
+ * @class
+ *
+ * font-size
CSS property.
+ *
+ *
+ * ui5-rating-indicator
+ * import @ui5/webcomponents/dist/RatingIndicator.js";
+ *
+ * @constructor
+ * @author SAP SE
+ * @alias sap.ui.webcomponents.main.RatingIndicator
+ * @extends UI5Element
+ * @tagname ui5-rating-indicator
+ * @public
+ */
+class RatingIndicator extends UI5Element {
+ static get metadata() {
+ return metadata;
+ }
+
+ static get render() {
+ return litRender;
+ }
+
+ static get styles() {
+ return RatingIndicatorCss;
+ }
+
+ static get template() {
+ return RatingIndicatorTemplate;
+ }
+
+ static async onDefine() {
+ await Promise.all([
+ fetchI18nBundle("@ui5/webcomponents"),
+ ]);
+ }
+
+ constructor() {
+ super();
+
+ this._liveValue = null; // stores the value to determine when to fire change
+ this.i18nBundle = getI18nBundle("@ui5/webcomponents");
+ }
+
+ onBeforeRendering() {
+ this.calcState();
+ }
+
+ calcState() {
+ this._stars = [];
+
+ for (let i = 1; i < this.maxValue + 1; i++) {
+ const remainder = Math.round((this.value - Math.floor(this.value)) * 10);
+ let halfStar = false,
+ tempValue = this.value;
+
+ if (Math.floor(this.value) + 1 === i && remainder > 2 && remainder < 8) {
+ halfStar = true;
+ } else if (remainder <= 2) {
+ tempValue = Math.floor(this.value);
+ } else if (remainder >= 8) {
+ tempValue = Math.ceil(this.value);
+ }
+
+ this._stars.push({
+ selected: i <= tempValue,
+ index: i,
+ halfStar,
+ });
+ }
+ }
+
+ _onclick(event) {
+ if (this.disabled || this.readonly) {
+ return;
+ }
+
+ this.value = parseInt(event.target.getAttribute("data-value"));
+
+ if (this.value === 1 && this._liveValue === 1) {
+ this.value = 0;
+ }
+
+ if (this._liveValue !== this.value) {
+ this.fireEvent("change");
+ this._liveValue = this.value;
+ }
+ }
+
+ _onkeydown(event) {
+ if (this.disabled || this.readonly) {
+ return;
+ }
+
+ const down = isDown(event) || isLeft(event);
+ const up = isRight(event) || isUp(event) || isSpace(event) || isEnter(event);
+
+ if (down || up) {
+ event.preventDefault();
+
+ if (down && this.value > 0) {
+ this.value = Math.round(this.value - 1);
+ this.fireEvent("change");
+ } else if (up && this.value < this.maxValue) {
+ this.value = Math.round(this.value + 1);
+ this.fireEvent("change");
+ }
+ }
+ }
+
+ _onfocusin() {
+ if (this.disabled) {
+ return;
+ }
+
+ this._focused = true;
+ this._liveValue = this.value;
+ }
+
+ _onfocusout() {
+ this._focused = false;
+ }
+
+ get tabIndex() {
+ return this.disabled ? "-1" : "0";
+ }
+
+ get _ariaRoleDescription() {
+ return this.i18nBundle.getText(RATING_INDICATOR_TEXT);
+ }
+}
+
+RatingIndicator.define();
+
+export default RatingIndicator;
diff --git a/packages/main/src/i18n/messagebundle.properties b/packages/main/src/i18n/messagebundle.properties
index bce8a73f35de..0dbfa855ba83 100644
--- a/packages/main/src/i18n/messagebundle.properties
+++ b/packages/main/src/i18n/messagebundle.properties
@@ -265,6 +265,9 @@ MULTIINPUT_SHOW_MORE_TOKENS={0} More
#XTOL: Tooltip for panel expand title
PANEL_ICON=Expand/Collapse
+#XBUT: Rating indicator aria-roledescription text
+RATING_INDICATOR_TEXT=Rating Indicator
+
#XACT: ARIA description for the segmented button
SEGMENTEDBUTTON_ARIA_DESCRIPTION=Segmented button
diff --git a/packages/main/src/themes/RatingIndicator.css b/packages/main/src/themes/RatingIndicator.css
new file mode 100644
index 000000000000..0aa7b162fb43
--- /dev/null
+++ b/packages/main/src/themes/RatingIndicator.css
@@ -0,0 +1,48 @@
+:host(:not([hidden])) {
+ display: inline-block;
+ font-size: 1.5rem;
+ cursor: pointer;
+}
+
+:host([disabled]) {
+ opacity: .6;
+ cursor: initial;
+ outline: none;
+}
+
+:host([readonly]) {
+ cursor: initial;
+}
+
+:host([_focused]) {
+ outline: 1px dotted var(--sapContent_FocusColor);
+}
+
+.ui5-rating-indicator-root {
+ outline: none;
+}
+
+.ui5-rating-indicator-icon {
+ position: relative;
+ color: var(--sapContent_UnratedColor);
+ user-select: none;
+}
+
+.ui5-rating-indicator-icon.ui5-rating-indicator-active-icon {
+ color: var(--sapContent_RatedColor);
+}
+
+.ui5-rating-indicator-icon.ui5-rating-indicator-half-icon:before {
+ content: "\2605";
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 50%;
+ height: 100%;
+ color: var(--sapContent_RatedColor);
+ overflow: hidden;
+}
+
+.ui5-rating-indicator-stars-wrapper {
+ display: flex;
+}
diff --git a/packages/main/test/pages/RatingIndicator.html b/packages/main/test/pages/RatingIndicator.html
new file mode 100644
index 000000000000..0c3718279b29
--- /dev/null
+++ b/packages/main/test/pages/RatingIndicator.html
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+ ++ + +
++ + +
++ + + +