Skip to content

Commit

Permalink
feat(ui5-rating-indicator): initial implementation (#1729)
Browse files Browse the repository at this point in the history
  • Loading branch information
fifoosid authored Jun 8, 2020
1 parent 681de1f commit a28f201
Show file tree
Hide file tree
Showing 13 changed files with 565 additions and 1 deletion.
4 changes: 4 additions & 0 deletions packages/base/src/UI5Element.js
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -306,6 +307,9 @@ class UI5Element extends HTMLElement {
if (propertyTypeClass === Integer) {
newValue = parseInt(newValue);
}
if (propertyTypeClass === Float) {
newValue = parseFloat(newValue);
}
this[nameInCamelCase] = newValue;
}
}
Expand Down
10 changes: 10 additions & 0 deletions packages/base/src/types/Float.js
Original file line number Diff line number Diff line change
@@ -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;
1 change: 1 addition & 0 deletions packages/main/bundle.esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
29 changes: 29 additions & 0 deletions packages/main/src/RatingIndicator.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<div class="ui5-rating-indicator-root"
role="slider"
aria-roledescription="{{_ariaRoleDescription}}"
aria-valuemin="0"
aria-valuenow="{{this.value}}"
aria-valuemax="{{this.maxValue}}"
aria-orientation="horizontal"
?aria-disabled="{{this.disabled}}"
?aria-readonly="{{this.readOnly}}"
tabindex="{{tabIndex}}"
@focusin="{{_onfocusin}}"
@focusout="{{_onfocusout}}"
@click="{{_onclick}}"
@keydown="{{_onkeydown}}"
>
<div
class="ui5-rating-indicator-stars-wrapper"
>
{{#each _stars}}
{{#if this.selected}}
<div class="ui5-rating-indicator-icon ui5-rating-indicator-active-icon" data-value="{{this.index}}">&#9733;</div>
{{else if this.halfStar}}
<div class="ui5-rating-indicator-icon ui5-rating-indicator-half-icon" data-value="{{this.index}}">&#9734;</div>
{{else}}
<div class="ui5-rating-indicator-icon" data-value="{{this.index}}">&#9734;</div>
{{/if}}
{{/each}}
</div>
</div>
256 changes: 256 additions & 0 deletions packages/main/src/RatingIndicator.js
Original file line number Diff line number Diff line change
@@ -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
* <br><br>
* <b>Note:</b> If you set a number which is not round, it would be shown as follows:
* <ul>
* <li>1.0 - 1.2 -> 1</li>
* <li>1.3 - 1.7 -> 1.5</li>
* <li>1.8 - 1.9 -> 2</li>
* <ul>
* @type {Float}
* @defaultvalue 0
* @public
*/
value: {
type: Float,
defaultValue: 0,
},

/**
* The number of displayed rating symbols
* @type {Integer}
* @defaultvalue 5
* @public
*/
maxValue: {
type: Integer,
defaultValue: 5,
},

/**
* Defines whether the <code>ui5-rating-indicator</code> 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
*
* <h3 class="comment-api-title">Overview</h3>
* 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 and overall ratings.
*
* <h3>Usage</h3>
* The preferred number of icons is between 5 and 7.
*
* <h3>Responsive Behavior</h3>
* You can change the size of the Rating Indicator by changing its <code>font-size</code> CSS property.
* <br>
* Example: <code><ui5-rating-indicator style="font-size: 3rem;"></ui5-rating-indicator></code>
*
* <h3>Usage</h3>
*
* For the <code>ui5-rating-indicator</code>
* <h3>ES6 Module Import</h3>
*
* <code>import @ui5/webcomponents/dist/RatingIndicator.js";</code>
*
* @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;
3 changes: 3 additions & 0 deletions packages/main/src/i18n/messagebundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
48 changes: 48 additions & 0 deletions packages/main/src/themes/RatingIndicator.css
Original file line number Diff line number Diff line change
@@ -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;
}
Loading

0 comments on commit a28f201

Please sign in to comment.