├── .babelrc
├── .eslintrc
├── .gitignore
├── LICENSE.md
├── README.md
├── RELEASE.md
├── assets
└── images
│ ├── star-empty.png
│ ├── star-full.png
│ ├── star-grey.png
│ ├── star-red.png
│ └── star-yellow.png
├── index.d.ts
├── index.html
├── lib
├── react-rating.cjs.js
├── react-rating.esm.js
├── react-rating.umd.js
├── react-rating.umd.js.map
├── react-rating.umd.min.js
└── react-rating.umd.min.js.map
├── package-lock.json
├── package.json
├── rollup.config.js
├── scripts
└── gh-pages.sh
├── src
├── Rating.js
├── RatingAPILayer.js
├── RatingSymbol.js
├── react-rating.js
├── utils.js
└── utils
│ ├── noop.js
│ └── style.js
├── test
├── Rating-test.js
├── RatingContainer-test.js
└── RatingSymbol-test.js
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-env",
4 | "@babel/preset-react"
5 | ],
6 | "plugins": [
7 | ["transform-react-remove-prop-types", {
8 | "mode": "remove",
9 | "removeImport": true
10 | }]
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "extends": "eslint:recommended",
4 | "parserOptions": {
5 | "sourceType": "module",
6 | "ecmaVersion": 6,
7 | "ecmaFeatures": {
8 | "jsx": true
9 | }
10 | },
11 | "env": {
12 | "es6": true,
13 | "browser": true,
14 | "node": true
15 | },
16 | "plugins": [
17 | "react"
18 | ],
19 | "globals": {
20 | "__DEV__": true
21 | },
22 | "rules": {
23 | "indent": ["error", 2],
24 | "func-names": ["error", "never"],
25 | "comma-dangle": ["error", "never"],
26 | "linebreak-style": "off",
27 | "no-unused-vars": ["warn", {
28 | "ignoreRestSiblings": true,
29 | "args": "none"
30 | }],
31 | "no-trailing-spaces": "error",
32 | "no-throw-literal": "off",
33 | "no-restricted-syntax": "off",
34 | "no-prototype-builtins": "off",
35 | "no-underscore-dangle": "off",
36 | "no-confusing-arrow": ["error", {"allowParens": true}],
37 | "no-param-reassign": ["error", { "props": false }],
38 | "camelcase": ["error", {properties: "never"}],
39 | "react/forbid-prop-types": "off",
40 | "react/jsx-uses-vars": "error"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # =========================
2 | # Operating System Files
3 | # =========================
4 |
5 | # OSX
6 | # =========================
7 |
8 | .DS_Store
9 | .AppleDouble
10 | .LSOverride
11 |
12 | # Thumbnails
13 | ._*
14 |
15 | # Files that might appear in the root of a volume
16 | .DocumentRevisions-V100
17 | .fseventsd
18 | .Spotlight-V100
19 | .TemporaryItems
20 | .Trashes
21 | .VolumeIcon.icns
22 |
23 | # Directories potentially created on remote AFP share
24 | .AppleDB
25 | .AppleDesktop
26 | Network Trash Folder
27 | Temporary Items
28 | .apdisk
29 |
30 | # =========================
31 | # Custom rules
32 | # =========================
33 |
34 | # Ignore npm modules
35 | node_modules
36 |
37 | # Ignore vim temporary files
38 | *.swp
39 |
40 | # Ignore dist folder
41 |
42 |
43 | # Ignore IDE files
44 | .project
45 | .vscode
46 | .settings
47 | .cache
48 |
49 | # Logs
50 | logs
51 | *.log
52 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 dreyescat (http://github.com/dreyescat)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://badge.fury.io/js/react-rating)
2 |
3 | # React Rating
4 |
5 | React Rating is a [react](https://github.com/facebook/react) rating component which supports custom symbols both with [inline styles](https://facebook.github.io/react/tips/inline-styles.html) and glyphicons found in popular CSS Toolkits like [Fontawesome](http://fortawesome.github.io/Font-Awesome/icons/) or [Bootstrap](http://getbootstrap.com/components/).
6 |
7 | This React component was inspired by the jQuery plugin [bootstrap-rating](https://github.com/dreyescat/bootstrap-rating).
8 |
9 | ## Demo
10 |
11 | See [react-rating](http://dreyescat.github.io/react-rating/) in action.
12 |
13 | ## Installation
14 |
15 | You can install `react-rating` component using the *npm* package manager:
16 |
17 | ```bash
18 | npm install --save react-rating
19 | ```
20 |
21 | ### Dependencies
22 |
23 | The `react-rating` component [peer depends](https://docs.npmjs.com/files/package.json#peerdependencies) on the [React](http://facebook.github.io/react/) library.
24 |
25 | You can install React using *npm* too:
26 |
27 | ```bash
28 | npm install --save react
29 | ```
30 |
31 | ## Upgrade Warning
32 |
33 | If you are using a version of React Rating < v1.0 be aware that **there are API changes between anything < v1.0 and v1.0 .** See the **Properties** and **Deprecated Properties and Callbacks** sections below for a documentation of the current API and how it compares to the old.
34 |
35 | ## Usage
36 |
37 | 1. Require the Rating Component
38 |
39 | ```javascript
40 | var Rating = require('react-rating');
41 | ```
42 |
43 | 2. Start using it
44 |
45 | With raw javascript:
46 |
47 | ```javascript
48 | React.createElement(Rating)
49 | ```
50 |
51 | Or with JSX:
52 |
53 | ```jsx
54 |
55 | ```
56 |
57 | ## Properties
58 |
59 | Property | Type | Default | Description
60 | --- | --- | --- | ---
61 | `start` | *number* | 0 | Range starting value (exclusive).
62 | `stop` | *number* | 5 | Range stop value (inclusive).
63 | `step` | *number* | 1 | Describes how many values each Symbol represents. For example, for a `start` value of 0, a `stop` value of 10 and a `step` of 2, we will end up with 5 Symbols, with each Symbol representing value increments of 2.
64 | `fractions` | *number* | 1 | Number of equal subdivisions that can be selected as a rating in each Symbol. For example, for a `fractions` value of 2, you will be able to select a rating with a precision of down to half a Symbol. Must be >= 1
65 | `initialRating` | *number* | 0 | The value that will be used as an initial rating. This is the old `initialRate`.
66 | `placeholderRating` | *number* | 0 | If you do not define an `initialRating` value, you can use a placeholder rating. Visually, this will have the same result as if you had defined an `initialRating` value. If `initialRating` is set `placeholderRating` is not taken into account. This is the old `placeholderRate`
67 | `readonly` | *bool* | false | Whether the rating can be modified or not.
68 | `quiet` | *bool* | false | Whether to animate rate hovering or not.
69 | `direction` | *ltr* or *rtl* | ltr | The direction of the rating element contents
70 | `emptySymbol` | *element* or *object* or *string* or *array* | Style.empty | React element, inline style object, or classes applied to the rating symbols when empty. Can also be an array of such symbols that will be applied in a circular manner (round-robin). This is the old `empty`.
71 | `fullSymbol` | *element* or *object* or *string* or *array* | Style.full | React element, inline style object, or classes applied to the rating symbols when full. Can also be an array of such symbols that will be applied in a circular manner (round-robin). This is the old `full`.
72 | `placeholderSymbol` | *element* or *object* or *string* or *array* | Style.placeholder | React element, inline style object, or classes applied to the placeholder rating symbols. Can also be an array of such symbols that will be applied in a circular manner (round-robin). This is the old `placeholder`.
73 |
74 | ## Callbacks
75 |
76 | Callback | Type | Description
77 | --- | --- | ---
78 | `onChange` | function (value) {} | Gets called with the `value` when a different value than the currently set is selected.
79 | `onClick` | function (value) {} | Gets called with the `value` when a symbol is clicked. The value is equal to the value that corresponds to that part of the symbol.
80 | `onHover` | function (value) {} | Gets called with the `value` when you hover over a symbol. The value is equal to the value that corresponds to that part of the symbol. Gets called in `quiet` mode too. When hover ends, gets called with no `value` (i.e. `undefined` as the value).
81 |
82 | ## Deprecated Properties and Callbacks
83 |
84 | This is a list of deprecated properties and callbacks from versions older than v1.0
85 |
86 | * `onRate`
87 | * `initialRate`
88 | * `placeholderRate`
89 | * `empty`
90 | * `full`
91 | * `placeholder`
92 |
93 | ## License
94 |
95 | [MIT License](https://github.com/dreyescat/react-rating/blob/master/LICENSE.md)
96 |
--------------------------------------------------------------------------------
/RELEASE.md:
--------------------------------------------------------------------------------
1 | # Release process
2 |
3 | 1. Make sure you have a clean and up-to-date working directory pointing to master branch
4 | 1. Create a new version. Try to follow [Semantic Versioning](https://semver.org/).
5 | ```bash
6 | npm version [patch|minor|major]
7 | ```
8 | 1. Publish version to npm. You will need to be logged in. Check [npm adduser](https://docs.npmjs.com/cli/adduser).
9 | ```bash
10 | npm publish
11 | ```
12 | 1. Update demo page.
13 | ```bash
14 | npm run gh-pages
15 | ```
16 |
--------------------------------------------------------------------------------
/assets/images/star-empty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dreyescat/react-rating/bd070757452e1aa75907f2a156fda2119fb7121a/assets/images/star-empty.png
--------------------------------------------------------------------------------
/assets/images/star-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dreyescat/react-rating/bd070757452e1aa75907f2a156fda2119fb7121a/assets/images/star-full.png
--------------------------------------------------------------------------------
/assets/images/star-grey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dreyescat/react-rating/bd070757452e1aa75907f2a156fda2119fb7121a/assets/images/star-grey.png
--------------------------------------------------------------------------------
/assets/images/star-red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dreyescat/react-rating/bd070757452e1aa75907f2a156fda2119fb7121a/assets/images/star-red.png
--------------------------------------------------------------------------------
/assets/images/star-yellow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dreyescat/react-rating/bd070757452e1aa75907f2a156fda2119fb7121a/assets/images/star-yellow.png
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | // Type definitions for react-rating 1.0.6 by
2 | // Project: https://github.com/dreyescat/react-rating
3 | // Definitions by: Kyle Davis
4 | // Konrad Szwarc
5 |
6 | import { Dictionary } from "lodash";
7 | import * as React from "react";
8 |
9 | declare class Rating extends React.Component {}
10 | declare namespace Rating {
11 |
12 | }
13 |
14 | export interface RatingComponentProps {
15 | /**
16 | * Range starting value (exclusive).
17 | *
18 | * Default value: 0
19 | */
20 | start?: number;
21 |
22 | /**
23 | * Range stop value (inclusive).
24 | *
25 | * Default value: 5
26 | */
27 | stop?: number;
28 |
29 | /**
30 | * Describes how many values each Symbol represents. For example,
31 | * for a start value of 0, a stop value of 10 and a step of 2,
32 | * we will end up with 5 Symbols, with each Symbol representing
33 | * value increments of 2.
34 | *
35 | * Default value: 1
36 | */
37 | step?: number;
38 |
39 | /**
40 | * Number of equal subdivisions that can be selected as a rating
41 | * in each Symbol. For example, for a fractions value of 2, you
42 | * will be able to select a rating with a precision of down to
43 | * half a Symbol. Must be >= 1
44 | *
45 | * Default value: 1
46 | */
47 | fractions?: number;
48 |
49 | /**
50 | * Range starting value (exclusive).
51 | *
52 | * Default value: 0
53 | */
54 | initialRating?: number;
55 |
56 | /**
57 | * The value that will be used as an initial rating. This is the
58 | * old initialRate.
59 | *
60 | * Default value: 0
61 | */
62 | className?: string;
63 |
64 | /**
65 | * If you do not define an initialRating value, you can use a
66 | * placeholder rating. Visually, this will have the same result
67 | * as if you had defined an initialRating value. If initialRating
68 | * is set placeholderRating is not taken into account. This is
69 | * the old placeholderRate
70 | *
71 | * Default value: 0
72 | */
73 | placeholderRating?: number;
74 |
75 | /**
76 | * Whether the rating can be modified or not.
77 | *
78 | * Default value: false
79 | */
80 | readonly?: boolean;
81 |
82 | /**
83 | * Whether to animate rate hovering or not.
84 | *
85 | * Default value: false
86 | */
87 | quiet?: boolean;
88 |
89 | /**
90 | * The direction of the rating element contents
91 | *
92 | * Default value: "ltr"
93 | */
94 | direction?: "rtl" | "ltr";
95 |
96 | /**
97 | * React element, inline style object, or classes applied to
98 | * the rating symbols when empty. Can also be an array of such
99 | * symbols that will be applied in a circular manner (round-robin).
100 | * This is the old empty.
101 | *
102 | * Default value: Style.empty
103 | */
104 | emptySymbol?: string | string[] | JSX.Element[] | JSX.Element;
105 |
106 | /**
107 | * React element, inline style object, or classes applied to the rating
108 | * symbols when full. Can also be an array of such symbols that will be
109 | * applied in a circular manner (round-robin). This is the old full.
110 | *
111 | * Default value: Style.full
112 | */
113 | fullSymbol?: string | string[] | JSX.Element[] | JSX.Element;
114 |
115 | /**
116 | * React element, inline style object, or classes applied to the
117 | * placeholder rating symbols. Can also be an array of such symbols
118 | * that will be applied in a circular manner (round-robin). This
119 | * is the old placeholder.
120 | *
121 | * Default value: Style.placeholder
122 | */
123 | placeholderSymbol?: string | string[] | JSX.Element[] | JSX.Element;
124 |
125 | /**
126 | * Gets called with the value when a different value than the currently
127 | * set is selected.
128 | */
129 | onChange?: (value: number) => void;
130 |
131 | /**
132 | * Gets called with the value when you hover over a symbol. The value
133 | * is equal to the value that corresponds to that part of the symbol.
134 | * Gets called in quiet mode too. When hover ends, gets called with
135 | * no value (i.e. undefined as the value).
136 | */
137 | onHover?: (value: number) => void;
138 |
139 | /**
140 | * Gets called with the value when a symbol is clicked. The value
141 | * is equal to the value that corresponds to that part of the symbol.
142 | */
143 | onClick?: (value: number) => void;
144 | }
145 |
146 | export default Rating;
147 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | React Rating
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | star-empty
28 |
29 |
30 |
31 | star-full
32 |
33 |
34 |
35 |
36 |
57 |
58 |
59 | Default rating
60 |
61 | Reset rating
62 |
63 | Readonly rating
64 |
65 | Readonly fractional rating
66 |
67 | React svg element rating
68 |
69 | React span element rating
70 |
71 | React img element rating
72 |
73 | Fontawesome Five Star rating
74 |
75 | Fontawesome Thumbs Up/Down rating (showcases background icon hiding)
76 |
77 | Bootstrap Five Heart rating
78 |
79 | Fractional rating
80 |
81 | Alert when rate changes
82 |
83 | Update a label when rate moves
84 |
85 |
86 | Update a label when rate moves "quietly"
87 |
88 |
89 | Colored rating
90 |
91 | Mixed symbols
92 |
93 | Custom each symbol
94 |
95 | 1 to 10 rating
96 |
97 | 5 to 10 rating
98 |
99 | 1 to 10 with step 2 (odd numbers)
100 |
101 | 10 to 1 with step -2 (odd numbers between [1..10] inverted order)
102 |
103 | Rating with placeholder
104 |
105 |
106 |
442 |
443 |
444 |
--------------------------------------------------------------------------------
/lib/react-rating.cjs.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
4 |
5 | var React = _interopDefault(require('react'));
6 |
7 | function _typeof(obj) {
8 | if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
9 | _typeof = function (obj) {
10 | return typeof obj;
11 | };
12 | } else {
13 | _typeof = function (obj) {
14 | return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
15 | };
16 | }
17 |
18 | return _typeof(obj);
19 | }
20 |
21 | function _classCallCheck(instance, Constructor) {
22 | if (!(instance instanceof Constructor)) {
23 | throw new TypeError("Cannot call a class as a function");
24 | }
25 | }
26 |
27 | function _defineProperties(target, props) {
28 | for (var i = 0; i < props.length; i++) {
29 | var descriptor = props[i];
30 | descriptor.enumerable = descriptor.enumerable || false;
31 | descriptor.configurable = true;
32 | if ("value" in descriptor) descriptor.writable = true;
33 | Object.defineProperty(target, descriptor.key, descriptor);
34 | }
35 | }
36 |
37 | function _createClass(Constructor, protoProps, staticProps) {
38 | if (protoProps) _defineProperties(Constructor.prototype, protoProps);
39 | if (staticProps) _defineProperties(Constructor, staticProps);
40 | return Constructor;
41 | }
42 |
43 | function _defineProperty(obj, key, value) {
44 | if (key in obj) {
45 | Object.defineProperty(obj, key, {
46 | value: value,
47 | enumerable: true,
48 | configurable: true,
49 | writable: true
50 | });
51 | } else {
52 | obj[key] = value;
53 | }
54 |
55 | return obj;
56 | }
57 |
58 | function _extends() {
59 | _extends = Object.assign || function (target) {
60 | for (var i = 1; i < arguments.length; i++) {
61 | var source = arguments[i];
62 |
63 | for (var key in source) {
64 | if (Object.prototype.hasOwnProperty.call(source, key)) {
65 | target[key] = source[key];
66 | }
67 | }
68 | }
69 |
70 | return target;
71 | };
72 |
73 | return _extends.apply(this, arguments);
74 | }
75 |
76 | function _objectSpread(target) {
77 | for (var i = 1; i < arguments.length; i++) {
78 | var source = arguments[i] != null ? arguments[i] : {};
79 | var ownKeys = Object.keys(source);
80 |
81 | if (typeof Object.getOwnPropertySymbols === 'function') {
82 | ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) {
83 | return Object.getOwnPropertyDescriptor(source, sym).enumerable;
84 | }));
85 | }
86 |
87 | ownKeys.forEach(function (key) {
88 | _defineProperty(target, key, source[key]);
89 | });
90 | }
91 |
92 | return target;
93 | }
94 |
95 | function _inherits(subClass, superClass) {
96 | if (typeof superClass !== "function" && superClass !== null) {
97 | throw new TypeError("Super expression must either be null or a function");
98 | }
99 |
100 | subClass.prototype = Object.create(superClass && superClass.prototype, {
101 | constructor: {
102 | value: subClass,
103 | writable: true,
104 | configurable: true
105 | }
106 | });
107 | if (superClass) _setPrototypeOf(subClass, superClass);
108 | }
109 |
110 | function _getPrototypeOf(o) {
111 | _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
112 | return o.__proto__ || Object.getPrototypeOf(o);
113 | };
114 | return _getPrototypeOf(o);
115 | }
116 |
117 | function _setPrototypeOf(o, p) {
118 | _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
119 | o.__proto__ = p;
120 | return o;
121 | };
122 |
123 | return _setPrototypeOf(o, p);
124 | }
125 |
126 | function _assertThisInitialized(self) {
127 | if (self === void 0) {
128 | throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
129 | }
130 |
131 | return self;
132 | }
133 |
134 | function _possibleConstructorReturn(self, call) {
135 | if (call && (typeof call === "object" || typeof call === "function")) {
136 | return call;
137 | }
138 |
139 | return _assertThisInitialized(self);
140 | }
141 |
142 | var style = {
143 | display: 'inline-block',
144 | borderRadius: '50%',
145 | border: '5px double white',
146 | width: 30,
147 | height: 30
148 | };
149 | var Style = {
150 | empty: _objectSpread({}, style, {
151 | backgroundColor: '#ccc'
152 | }),
153 | full: _objectSpread({}, style, {
154 | backgroundColor: 'black'
155 | }),
156 | placeholder: _objectSpread({}, style, {
157 | backgroundColor: 'red'
158 | })
159 | };
160 |
161 | // Return the corresponding React node for an icon.
162 | var _iconNode = function _iconNode(icon) {
163 | // If it is already a React Element just return it.
164 | if (React.isValidElement(icon)) {
165 | return icon;
166 | } // If it is an object, try to use it as a CSS style object.
167 |
168 |
169 | if (_typeof(icon) === 'object' && icon !== null) {
170 | return React.createElement("span", {
171 | style: icon
172 | });
173 | } // If it is a string, use it as class names.
174 |
175 |
176 | if (Object.prototype.toString.call(icon) === '[object String]') {
177 | return React.createElement("span", {
178 | className: icon
179 | });
180 | }
181 | };
182 |
183 | var RatingSymbol =
184 | /*#__PURE__*/
185 | function (_React$PureComponent) {
186 | _inherits(RatingSymbol, _React$PureComponent);
187 |
188 | function RatingSymbol() {
189 | _classCallCheck(this, RatingSymbol);
190 |
191 | return _possibleConstructorReturn(this, _getPrototypeOf(RatingSymbol).apply(this, arguments));
192 | }
193 |
194 | _createClass(RatingSymbol, [{
195 | key: "render",
196 | value: function render() {
197 | var _iconContainerStyle;
198 |
199 | var _this$props = this.props,
200 | index = _this$props.index,
201 | inactiveIcon = _this$props.inactiveIcon,
202 | activeIcon = _this$props.activeIcon,
203 | percent = _this$props.percent,
204 | direction = _this$props.direction,
205 | readonly = _this$props.readonly,
206 | onClick = _this$props.onClick,
207 | onMouseMove = _this$props.onMouseMove,
208 | onTouchEnd = _this$props.onTouchEnd;
209 |
210 | var backgroundNode = _iconNode(inactiveIcon);
211 |
212 | var showbgIcon = percent < 100;
213 | var bgIconContainerStyle = showbgIcon ? {} : {
214 | visibility: 'hidden'
215 | };
216 |
217 | var iconNode = _iconNode(activeIcon);
218 |
219 | var iconContainerStyle = (_iconContainerStyle = {
220 | display: 'inline-block',
221 | position: 'absolute',
222 | overflow: 'hidden',
223 | top: 0
224 | }, _defineProperty(_iconContainerStyle, direction === 'rtl' ? 'right' : 'left', 0), _defineProperty(_iconContainerStyle, "width", "".concat(percent, "%")), _iconContainerStyle);
225 | var style = {
226 | cursor: !readonly ? 'pointer' : 'inherit',
227 | display: 'inline-block',
228 | position: 'relative'
229 | };
230 |
231 | function handleMouseMove(e) {
232 | if (onMouseMove) {
233 | onMouseMove(index, e);
234 | }
235 | }
236 |
237 | function handleMouseClick(e) {
238 | if (onClick) {
239 | // [Supporting both TouchEvent and MouseEvent](https://developer.mozilla.org/en-US/docs/Web/API/Touch_events/Supporting_both_TouchEvent_and_MouseEvent)
240 | // We must prevent firing click event twice on touch devices.
241 | e.preventDefault();
242 | onClick(index, e);
243 | }
244 | }
245 |
246 | function handleTouchEnd(e) {
247 | if (onTouchEnd) {
248 | onTouchEnd(index, e);
249 | }
250 | }
251 |
252 | return React.createElement("span", {
253 | style: style,
254 | onClick: handleMouseClick,
255 | onMouseMove: handleMouseMove,
256 | onTouchMove: handleMouseMove,
257 | onTouchEnd: handleTouchEnd
258 | }, React.createElement("span", {
259 | style: bgIconContainerStyle
260 | }, backgroundNode), React.createElement("span", {
261 | style: iconContainerStyle
262 | }, iconNode));
263 | }
264 | }]);
265 |
266 | return RatingSymbol;
267 | }(React.PureComponent); // Define propTypes only in development. They will be void in production.
268 |
269 | var Rating =
270 | /*#__PURE__*/
271 | function (_React$PureComponent) {
272 | _inherits(Rating, _React$PureComponent);
273 |
274 | function Rating(props) {
275 | var _this;
276 |
277 | _classCallCheck(this, Rating);
278 |
279 | _this = _possibleConstructorReturn(this, _getPrototypeOf(Rating).call(this, props));
280 | _this.state = {
281 | // Indicates the value that is displayed to the user in the form of symbols.
282 | // It can be either 0 (for no displayed symbols) or (0, end]
283 | displayValue: _this.props.value,
284 | // Indicates if the user is currently hovering over the rating element
285 | interacting: false
286 | };
287 | _this.onMouseLeave = _this.onMouseLeave.bind(_assertThisInitialized(_assertThisInitialized(_this)));
288 | _this.symbolMouseMove = _this.symbolMouseMove.bind(_assertThisInitialized(_assertThisInitialized(_this)));
289 | _this.symbolClick = _this.symbolClick.bind(_assertThisInitialized(_assertThisInitialized(_this)));
290 | _this.symbolEnd = _this.symbolEnd.bind(_assertThisInitialized(_assertThisInitialized(_this)));
291 | return _this;
292 | }
293 |
294 | _createClass(Rating, [{
295 | key: "UNSAFE_componentWillReceiveProps",
296 | value: function UNSAFE_componentWillReceiveProps(nextProps) {
297 | var valueChanged = this.props.value !== nextProps.value;
298 | this.setState(function (prevState) {
299 | return {
300 | displayValue: valueChanged ? nextProps.value : prevState.displayValue
301 | };
302 | });
303 | } // NOTE: This callback is a little bit fragile. Needs some "care" because
304 | // it relies on brittle state kept with different props and state
305 | // combinations to try to figure out from where we are coming, I mean, what
306 | // caused this update.
307 |
308 | }, {
309 | key: "componentDidUpdate",
310 | value: function componentDidUpdate(prevProps, prevState) {
311 | // When hover ends, call this.props.onHover with no value.
312 | if (prevState.interacting && !this.state.interacting) {
313 | return this.props.onHover();
314 | } // When hover over.
315 | // Hover in should only be emitted while we are hovering (interacting),
316 | // unless we changed the value, usually originated by an onClick event.
317 | // We do not want to emit a hover in event again on the clicked
318 | // symbol.
319 |
320 |
321 | if (this.state.interacting && prevProps.value == this.props.value) {
322 | this.props.onHover(this.state.displayValue);
323 | }
324 | }
325 | }, {
326 | key: "symbolEnd",
327 | value: function symbolEnd(symbolIndex, event) {
328 | // Do not raise the click event on quiet mode when a touch end is received.
329 | // On quiet mode the touch end event only notifies that we have left the
330 | // symbol. We wait for the actual click event to call the symbolClick.
331 | // On not quiet mode we simulate the click event on touch end and we just
332 | // prevent the real on click event to be raised.
333 | // NOTE: I know how we manage click events on touch devices is a little bit
334 | // weird because we do not notify the starting value that was clicked but
335 | // the last (touched) value.
336 | if (!this.props.quiet) {
337 | this.symbolClick(symbolIndex, event);
338 | event.preventDefault();
339 | } // On touch end we are "leaving" the symbol.
340 |
341 |
342 | this.onMouseLeave();
343 | }
344 | }, {
345 | key: "symbolClick",
346 | value: function symbolClick(symbolIndex, event) {
347 | var value = this.calculateDisplayValue(symbolIndex, event);
348 | this.props.onClick(value, event);
349 | }
350 | }, {
351 | key: "symbolMouseMove",
352 | value: function symbolMouseMove(symbolIndex, event) {
353 | var value = this.calculateDisplayValue(symbolIndex, event); // This call should cause an update only if the state changes.
354 | // Mainly the first time the mouse enters and whenever the value changes.
355 | // So DidComponentUpdate is NOT called for every mouse movement.
356 |
357 | this.setState({
358 | interacting: !this.props.readonly,
359 | displayValue: value
360 | });
361 | }
362 | }, {
363 | key: "onMouseLeave",
364 | value: function onMouseLeave() {
365 | this.setState({
366 | displayValue: this.props.value,
367 | interacting: false
368 | });
369 | }
370 | }, {
371 | key: "calculateDisplayValue",
372 | value: function calculateDisplayValue(symbolIndex, event) {
373 | var percentage = this.calculateHoverPercentage(event); // Get the closest top fraction.
374 |
375 | var fraction = Math.ceil(percentage % 1 * this.props.fractions) / this.props.fractions; // Truncate decimal trying to avoid float precission issues.
376 |
377 | var precision = Math.pow(10, 3);
378 | var displayValue = symbolIndex + (Math.floor(percentage) + Math.floor(fraction * precision) / precision); // ensure the returned value is greater than 0 and lower than totalSymbols
379 |
380 | return displayValue > 0 ? displayValue > this.props.totalSymbols ? this.props.totalSymbols : displayValue : 1 / this.props.fractions;
381 | }
382 | }, {
383 | key: "calculateHoverPercentage",
384 | value: function calculateHoverPercentage(event) {
385 | var clientX = event.nativeEvent.type.indexOf("touch") > -1 ? event.nativeEvent.type.indexOf("touchend") > -1 ? event.changedTouches[0].clientX : event.touches[0].clientX : event.clientX;
386 | var targetRect = event.target.getBoundingClientRect();
387 | var delta = this.props.direction === 'rtl' ? targetRect.right - clientX : clientX - targetRect.left; // Returning 0 if the delta is negative solves the flickering issue
388 |
389 | return delta < 0 ? 0 : delta / targetRect.width;
390 | }
391 | }, {
392 | key: "render",
393 | value: function render() {
394 | var _this$props = this.props,
395 | readonly = _this$props.readonly,
396 | quiet = _this$props.quiet,
397 | totalSymbols = _this$props.totalSymbols,
398 | value = _this$props.value,
399 | placeholderValue = _this$props.placeholderValue,
400 | direction = _this$props.direction,
401 | emptySymbol = _this$props.emptySymbol,
402 | fullSymbol = _this$props.fullSymbol,
403 | placeholderSymbol = _this$props.placeholderSymbol,
404 | className = _this$props.className,
405 | id = _this$props.id,
406 | style = _this$props.style,
407 | tabIndex = _this$props.tabIndex;
408 | var _this$state = this.state,
409 | displayValue = _this$state.displayValue,
410 | interacting = _this$state.interacting;
411 | var symbolNodes = [];
412 | var empty = [].concat(emptySymbol);
413 | var full = [].concat(fullSymbol);
414 | var placeholder = [].concat(placeholderSymbol);
415 | var shouldDisplayPlaceholder = placeholderValue !== 0 && value === 0 && !interacting; // The value that will be used as base for calculating how to render the symbols
416 |
417 | var renderedValue;
418 |
419 | if (shouldDisplayPlaceholder) {
420 | renderedValue = placeholderValue;
421 | } else {
422 | renderedValue = quiet ? value : displayValue;
423 | } // The amount of full symbols
424 |
425 |
426 | var fullSymbols = Math.floor(renderedValue);
427 |
428 | for (var i = 0; i < totalSymbols; i++) {
429 | var percent = void 0; // Calculate each symbol's fullness percentage
430 |
431 | if (i - fullSymbols < 0) {
432 | percent = 100;
433 | } else if (i - fullSymbols === 0) {
434 | percent = (renderedValue - i) * 100;
435 | } else {
436 | percent = 0;
437 | }
438 |
439 | symbolNodes.push(React.createElement(RatingSymbol, _extends({
440 | key: i,
441 | index: i,
442 | readonly: readonly,
443 | inactiveIcon: empty[i % empty.length],
444 | activeIcon: shouldDisplayPlaceholder ? placeholder[i % full.length] : full[i % full.length],
445 | percent: percent,
446 | direction: direction
447 | }, !readonly && {
448 | onClick: this.symbolClick,
449 | onMouseMove: this.symbolMouseMove,
450 | onTouchMove: this.symbolMouseMove,
451 | onTouchEnd: this.symbolEnd
452 | })));
453 | }
454 |
455 | return React.createElement("span", _extends({
456 | id: id,
457 | style: _objectSpread({}, style, {
458 | display: 'inline-block',
459 | direction: direction
460 | }),
461 | className: className,
462 | tabIndex: tabIndex,
463 | "aria-label": this.props['aria-label']
464 | }, !readonly && {
465 | onMouseLeave: this.onMouseLeave
466 | }), symbolNodes);
467 | }
468 | }]);
469 |
470 | return Rating;
471 | }(React.PureComponent); // Define propTypes only in development.
472 |
473 | function noop() {}
474 |
475 | noop._name = 'react_rating_noop';
476 |
477 | var RatingAPILayer =
478 | /*#__PURE__*/
479 | function (_React$PureComponent) {
480 | _inherits(RatingAPILayer, _React$PureComponent);
481 |
482 | function RatingAPILayer(props) {
483 | var _this;
484 |
485 | _classCallCheck(this, RatingAPILayer);
486 |
487 | _this = _possibleConstructorReturn(this, _getPrototypeOf(RatingAPILayer).call(this, props));
488 | _this.state = {
489 | value: props.initialRating
490 | };
491 | _this.handleClick = _this.handleClick.bind(_assertThisInitialized(_assertThisInitialized(_this)));
492 | _this.handleHover = _this.handleHover.bind(_assertThisInitialized(_assertThisInitialized(_this)));
493 | return _this;
494 | }
495 |
496 | _createClass(RatingAPILayer, [{
497 | key: "UNSAFE_componentWillReceiveProps",
498 | value: function UNSAFE_componentWillReceiveProps(nextProps) {
499 | this.setState({
500 | value: nextProps.initialRating
501 | });
502 | }
503 | }, {
504 | key: "handleClick",
505 | value: function handleClick(value, e) {
506 | var _this2 = this;
507 |
508 | var newValue = this.translateDisplayValueToValue(value);
509 | this.props.onClick(newValue); // Avoid calling setState if not necessary. Micro optimisation.
510 |
511 | if (this.state.value !== newValue) {
512 | // If we have a new value trigger onChange callback.
513 | this.setState({
514 | value: newValue
515 | }, function () {
516 | return _this2.props.onChange(_this2.state.value);
517 | });
518 | }
519 | }
520 | }, {
521 | key: "handleHover",
522 | value: function handleHover(displayValue) {
523 | var value = displayValue === undefined ? displayValue : this.translateDisplayValueToValue(displayValue);
524 | this.props.onHover(value);
525 | }
526 | }, {
527 | key: "translateDisplayValueToValue",
528 | value: function translateDisplayValueToValue(displayValue) {
529 | var translatedValue = displayValue * this.props.step + this.props.start; // minimum value cannot be equal to start, since it's exclusive
530 |
531 | return translatedValue === this.props.start ? translatedValue + 1 / this.props.fractions : translatedValue;
532 | }
533 | }, {
534 | key: "tranlateValueToDisplayValue",
535 | value: function tranlateValueToDisplayValue(value) {
536 | if (value === undefined) {
537 | return 0;
538 | }
539 |
540 | return (value - this.props.start) / this.props.step;
541 | }
542 | }, {
543 | key: "render",
544 | value: function render() {
545 | var _this$props = this.props,
546 | step = _this$props.step,
547 | emptySymbol = _this$props.emptySymbol,
548 | fullSymbol = _this$props.fullSymbol,
549 | placeholderSymbol = _this$props.placeholderSymbol,
550 | readonly = _this$props.readonly,
551 | quiet = _this$props.quiet,
552 | fractions = _this$props.fractions,
553 | direction = _this$props.direction,
554 | start = _this$props.start,
555 | stop = _this$props.stop,
556 | id = _this$props.id,
557 | className = _this$props.className,
558 | style = _this$props.style,
559 | tabIndex = _this$props.tabIndex;
560 |
561 | function calculateTotalSymbols(start, stop, step) {
562 | return Math.floor((stop - start) / step);
563 | }
564 |
565 | return React.createElement(Rating, {
566 | id: id,
567 | style: style,
568 | className: className,
569 | tabIndex: tabIndex,
570 | "aria-label": this.props['aria-label'],
571 | totalSymbols: calculateTotalSymbols(start, stop, step),
572 | value: this.tranlateValueToDisplayValue(this.state.value),
573 | placeholderValue: this.tranlateValueToDisplayValue(this.props.placeholderRating),
574 | readonly: readonly,
575 | quiet: quiet,
576 | fractions: fractions,
577 | direction: direction,
578 | emptySymbol: emptySymbol,
579 | fullSymbol: fullSymbol,
580 | placeholderSymbol: placeholderSymbol,
581 | onClick: this.handleClick,
582 | onHover: this.handleHover
583 | });
584 | }
585 | }]);
586 |
587 | return RatingAPILayer;
588 | }(React.PureComponent);
589 |
590 | RatingAPILayer.defaultProps = {
591 | start: 0,
592 | stop: 5,
593 | step: 1,
594 | readonly: false,
595 | quiet: false,
596 | fractions: 1,
597 | direction: 'ltr',
598 | onHover: noop,
599 | onClick: noop,
600 | onChange: noop,
601 | emptySymbol: Style.empty,
602 | fullSymbol: Style.full,
603 | placeholderSymbol: Style.placeholder
604 | }; // Define propTypes only in development.
605 |
606 | module.exports = RatingAPILayer;
607 |
--------------------------------------------------------------------------------
/lib/react-rating.esm.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | function _typeof(obj) {
4 | if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
5 | _typeof = function (obj) {
6 | return typeof obj;
7 | };
8 | } else {
9 | _typeof = function (obj) {
10 | return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
11 | };
12 | }
13 |
14 | return _typeof(obj);
15 | }
16 |
17 | function _classCallCheck(instance, Constructor) {
18 | if (!(instance instanceof Constructor)) {
19 | throw new TypeError("Cannot call a class as a function");
20 | }
21 | }
22 |
23 | function _defineProperties(target, props) {
24 | for (var i = 0; i < props.length; i++) {
25 | var descriptor = props[i];
26 | descriptor.enumerable = descriptor.enumerable || false;
27 | descriptor.configurable = true;
28 | if ("value" in descriptor) descriptor.writable = true;
29 | Object.defineProperty(target, descriptor.key, descriptor);
30 | }
31 | }
32 |
33 | function _createClass(Constructor, protoProps, staticProps) {
34 | if (protoProps) _defineProperties(Constructor.prototype, protoProps);
35 | if (staticProps) _defineProperties(Constructor, staticProps);
36 | return Constructor;
37 | }
38 |
39 | function _defineProperty(obj, key, value) {
40 | if (key in obj) {
41 | Object.defineProperty(obj, key, {
42 | value: value,
43 | enumerable: true,
44 | configurable: true,
45 | writable: true
46 | });
47 | } else {
48 | obj[key] = value;
49 | }
50 |
51 | return obj;
52 | }
53 |
54 | function _extends() {
55 | _extends = Object.assign || function (target) {
56 | for (var i = 1; i < arguments.length; i++) {
57 | var source = arguments[i];
58 |
59 | for (var key in source) {
60 | if (Object.prototype.hasOwnProperty.call(source, key)) {
61 | target[key] = source[key];
62 | }
63 | }
64 | }
65 |
66 | return target;
67 | };
68 |
69 | return _extends.apply(this, arguments);
70 | }
71 |
72 | function _objectSpread(target) {
73 | for (var i = 1; i < arguments.length; i++) {
74 | var source = arguments[i] != null ? arguments[i] : {};
75 | var ownKeys = Object.keys(source);
76 |
77 | if (typeof Object.getOwnPropertySymbols === 'function') {
78 | ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) {
79 | return Object.getOwnPropertyDescriptor(source, sym).enumerable;
80 | }));
81 | }
82 |
83 | ownKeys.forEach(function (key) {
84 | _defineProperty(target, key, source[key]);
85 | });
86 | }
87 |
88 | return target;
89 | }
90 |
91 | function _inherits(subClass, superClass) {
92 | if (typeof superClass !== "function" && superClass !== null) {
93 | throw new TypeError("Super expression must either be null or a function");
94 | }
95 |
96 | subClass.prototype = Object.create(superClass && superClass.prototype, {
97 | constructor: {
98 | value: subClass,
99 | writable: true,
100 | configurable: true
101 | }
102 | });
103 | if (superClass) _setPrototypeOf(subClass, superClass);
104 | }
105 |
106 | function _getPrototypeOf(o) {
107 | _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
108 | return o.__proto__ || Object.getPrototypeOf(o);
109 | };
110 | return _getPrototypeOf(o);
111 | }
112 |
113 | function _setPrototypeOf(o, p) {
114 | _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
115 | o.__proto__ = p;
116 | return o;
117 | };
118 |
119 | return _setPrototypeOf(o, p);
120 | }
121 |
122 | function _assertThisInitialized(self) {
123 | if (self === void 0) {
124 | throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
125 | }
126 |
127 | return self;
128 | }
129 |
130 | function _possibleConstructorReturn(self, call) {
131 | if (call && (typeof call === "object" || typeof call === "function")) {
132 | return call;
133 | }
134 |
135 | return _assertThisInitialized(self);
136 | }
137 |
138 | var style = {
139 | display: 'inline-block',
140 | borderRadius: '50%',
141 | border: '5px double white',
142 | width: 30,
143 | height: 30
144 | };
145 | var Style = {
146 | empty: _objectSpread({}, style, {
147 | backgroundColor: '#ccc'
148 | }),
149 | full: _objectSpread({}, style, {
150 | backgroundColor: 'black'
151 | }),
152 | placeholder: _objectSpread({}, style, {
153 | backgroundColor: 'red'
154 | })
155 | };
156 |
157 | // Return the corresponding React node for an icon.
158 | var _iconNode = function _iconNode(icon) {
159 | // If it is already a React Element just return it.
160 | if (React.isValidElement(icon)) {
161 | return icon;
162 | } // If it is an object, try to use it as a CSS style object.
163 |
164 |
165 | if (_typeof(icon) === 'object' && icon !== null) {
166 | return React.createElement("span", {
167 | style: icon
168 | });
169 | } // If it is a string, use it as class names.
170 |
171 |
172 | if (Object.prototype.toString.call(icon) === '[object String]') {
173 | return React.createElement("span", {
174 | className: icon
175 | });
176 | }
177 | };
178 |
179 | var RatingSymbol =
180 | /*#__PURE__*/
181 | function (_React$PureComponent) {
182 | _inherits(RatingSymbol, _React$PureComponent);
183 |
184 | function RatingSymbol() {
185 | _classCallCheck(this, RatingSymbol);
186 |
187 | return _possibleConstructorReturn(this, _getPrototypeOf(RatingSymbol).apply(this, arguments));
188 | }
189 |
190 | _createClass(RatingSymbol, [{
191 | key: "render",
192 | value: function render() {
193 | var _iconContainerStyle;
194 |
195 | var _this$props = this.props,
196 | index = _this$props.index,
197 | inactiveIcon = _this$props.inactiveIcon,
198 | activeIcon = _this$props.activeIcon,
199 | percent = _this$props.percent,
200 | direction = _this$props.direction,
201 | readonly = _this$props.readonly,
202 | onClick = _this$props.onClick,
203 | onMouseMove = _this$props.onMouseMove,
204 | onTouchEnd = _this$props.onTouchEnd;
205 |
206 | var backgroundNode = _iconNode(inactiveIcon);
207 |
208 | var showbgIcon = percent < 100;
209 | var bgIconContainerStyle = showbgIcon ? {} : {
210 | visibility: 'hidden'
211 | };
212 |
213 | var iconNode = _iconNode(activeIcon);
214 |
215 | var iconContainerStyle = (_iconContainerStyle = {
216 | display: 'inline-block',
217 | position: 'absolute',
218 | overflow: 'hidden',
219 | top: 0
220 | }, _defineProperty(_iconContainerStyle, direction === 'rtl' ? 'right' : 'left', 0), _defineProperty(_iconContainerStyle, "width", "".concat(percent, "%")), _iconContainerStyle);
221 | var style = {
222 | cursor: !readonly ? 'pointer' : 'inherit',
223 | display: 'inline-block',
224 | position: 'relative'
225 | };
226 |
227 | function handleMouseMove(e) {
228 | if (onMouseMove) {
229 | onMouseMove(index, e);
230 | }
231 | }
232 |
233 | function handleMouseClick(e) {
234 | if (onClick) {
235 | // [Supporting both TouchEvent and MouseEvent](https://developer.mozilla.org/en-US/docs/Web/API/Touch_events/Supporting_both_TouchEvent_and_MouseEvent)
236 | // We must prevent firing click event twice on touch devices.
237 | e.preventDefault();
238 | onClick(index, e);
239 | }
240 | }
241 |
242 | function handleTouchEnd(e) {
243 | if (onTouchEnd) {
244 | onTouchEnd(index, e);
245 | }
246 | }
247 |
248 | return React.createElement("span", {
249 | style: style,
250 | onClick: handleMouseClick,
251 | onMouseMove: handleMouseMove,
252 | onTouchMove: handleMouseMove,
253 | onTouchEnd: handleTouchEnd
254 | }, React.createElement("span", {
255 | style: bgIconContainerStyle
256 | }, backgroundNode), React.createElement("span", {
257 | style: iconContainerStyle
258 | }, iconNode));
259 | }
260 | }]);
261 |
262 | return RatingSymbol;
263 | }(React.PureComponent); // Define propTypes only in development. They will be void in production.
264 |
265 | var Rating =
266 | /*#__PURE__*/
267 | function (_React$PureComponent) {
268 | _inherits(Rating, _React$PureComponent);
269 |
270 | function Rating(props) {
271 | var _this;
272 |
273 | _classCallCheck(this, Rating);
274 |
275 | _this = _possibleConstructorReturn(this, _getPrototypeOf(Rating).call(this, props));
276 | _this.state = {
277 | // Indicates the value that is displayed to the user in the form of symbols.
278 | // It can be either 0 (for no displayed symbols) or (0, end]
279 | displayValue: _this.props.value,
280 | // Indicates if the user is currently hovering over the rating element
281 | interacting: false
282 | };
283 | _this.onMouseLeave = _this.onMouseLeave.bind(_assertThisInitialized(_assertThisInitialized(_this)));
284 | _this.symbolMouseMove = _this.symbolMouseMove.bind(_assertThisInitialized(_assertThisInitialized(_this)));
285 | _this.symbolClick = _this.symbolClick.bind(_assertThisInitialized(_assertThisInitialized(_this)));
286 | _this.symbolEnd = _this.symbolEnd.bind(_assertThisInitialized(_assertThisInitialized(_this)));
287 | return _this;
288 | }
289 |
290 | _createClass(Rating, [{
291 | key: "UNSAFE_componentWillReceiveProps",
292 | value: function UNSAFE_componentWillReceiveProps(nextProps) {
293 | var valueChanged = this.props.value !== nextProps.value;
294 | this.setState(function (prevState) {
295 | return {
296 | displayValue: valueChanged ? nextProps.value : prevState.displayValue
297 | };
298 | });
299 | } // NOTE: This callback is a little bit fragile. Needs some "care" because
300 | // it relies on brittle state kept with different props and state
301 | // combinations to try to figure out from where we are coming, I mean, what
302 | // caused this update.
303 |
304 | }, {
305 | key: "componentDidUpdate",
306 | value: function componentDidUpdate(prevProps, prevState) {
307 | // When hover ends, call this.props.onHover with no value.
308 | if (prevState.interacting && !this.state.interacting) {
309 | return this.props.onHover();
310 | } // When hover over.
311 | // Hover in should only be emitted while we are hovering (interacting),
312 | // unless we changed the value, usually originated by an onClick event.
313 | // We do not want to emit a hover in event again on the clicked
314 | // symbol.
315 |
316 |
317 | if (this.state.interacting && prevProps.value == this.props.value) {
318 | this.props.onHover(this.state.displayValue);
319 | }
320 | }
321 | }, {
322 | key: "symbolEnd",
323 | value: function symbolEnd(symbolIndex, event) {
324 | // Do not raise the click event on quiet mode when a touch end is received.
325 | // On quiet mode the touch end event only notifies that we have left the
326 | // symbol. We wait for the actual click event to call the symbolClick.
327 | // On not quiet mode we simulate the click event on touch end and we just
328 | // prevent the real on click event to be raised.
329 | // NOTE: I know how we manage click events on touch devices is a little bit
330 | // weird because we do not notify the starting value that was clicked but
331 | // the last (touched) value.
332 | if (!this.props.quiet) {
333 | this.symbolClick(symbolIndex, event);
334 | event.preventDefault();
335 | } // On touch end we are "leaving" the symbol.
336 |
337 |
338 | this.onMouseLeave();
339 | }
340 | }, {
341 | key: "symbolClick",
342 | value: function symbolClick(symbolIndex, event) {
343 | var value = this.calculateDisplayValue(symbolIndex, event);
344 | this.props.onClick(value, event);
345 | }
346 | }, {
347 | key: "symbolMouseMove",
348 | value: function symbolMouseMove(symbolIndex, event) {
349 | var value = this.calculateDisplayValue(symbolIndex, event); // This call should cause an update only if the state changes.
350 | // Mainly the first time the mouse enters and whenever the value changes.
351 | // So DidComponentUpdate is NOT called for every mouse movement.
352 |
353 | this.setState({
354 | interacting: !this.props.readonly,
355 | displayValue: value
356 | });
357 | }
358 | }, {
359 | key: "onMouseLeave",
360 | value: function onMouseLeave() {
361 | this.setState({
362 | displayValue: this.props.value,
363 | interacting: false
364 | });
365 | }
366 | }, {
367 | key: "calculateDisplayValue",
368 | value: function calculateDisplayValue(symbolIndex, event) {
369 | var percentage = this.calculateHoverPercentage(event); // Get the closest top fraction.
370 |
371 | var fraction = Math.ceil(percentage % 1 * this.props.fractions) / this.props.fractions; // Truncate decimal trying to avoid float precission issues.
372 |
373 | var precision = Math.pow(10, 3);
374 | var displayValue = symbolIndex + (Math.floor(percentage) + Math.floor(fraction * precision) / precision); // ensure the returned value is greater than 0 and lower than totalSymbols
375 |
376 | return displayValue > 0 ? displayValue > this.props.totalSymbols ? this.props.totalSymbols : displayValue : 1 / this.props.fractions;
377 | }
378 | }, {
379 | key: "calculateHoverPercentage",
380 | value: function calculateHoverPercentage(event) {
381 | var clientX = event.nativeEvent.type.indexOf("touch") > -1 ? event.nativeEvent.type.indexOf("touchend") > -1 ? event.changedTouches[0].clientX : event.touches[0].clientX : event.clientX;
382 | var targetRect = event.target.getBoundingClientRect();
383 | var delta = this.props.direction === 'rtl' ? targetRect.right - clientX : clientX - targetRect.left; // Returning 0 if the delta is negative solves the flickering issue
384 |
385 | return delta < 0 ? 0 : delta / targetRect.width;
386 | }
387 | }, {
388 | key: "render",
389 | value: function render() {
390 | var _this$props = this.props,
391 | readonly = _this$props.readonly,
392 | quiet = _this$props.quiet,
393 | totalSymbols = _this$props.totalSymbols,
394 | value = _this$props.value,
395 | placeholderValue = _this$props.placeholderValue,
396 | direction = _this$props.direction,
397 | emptySymbol = _this$props.emptySymbol,
398 | fullSymbol = _this$props.fullSymbol,
399 | placeholderSymbol = _this$props.placeholderSymbol,
400 | className = _this$props.className,
401 | id = _this$props.id,
402 | style = _this$props.style,
403 | tabIndex = _this$props.tabIndex;
404 | var _this$state = this.state,
405 | displayValue = _this$state.displayValue,
406 | interacting = _this$state.interacting;
407 | var symbolNodes = [];
408 | var empty = [].concat(emptySymbol);
409 | var full = [].concat(fullSymbol);
410 | var placeholder = [].concat(placeholderSymbol);
411 | var shouldDisplayPlaceholder = placeholderValue !== 0 && value === 0 && !interacting; // The value that will be used as base for calculating how to render the symbols
412 |
413 | var renderedValue;
414 |
415 | if (shouldDisplayPlaceholder) {
416 | renderedValue = placeholderValue;
417 | } else {
418 | renderedValue = quiet ? value : displayValue;
419 | } // The amount of full symbols
420 |
421 |
422 | var fullSymbols = Math.floor(renderedValue);
423 |
424 | for (var i = 0; i < totalSymbols; i++) {
425 | var percent = void 0; // Calculate each symbol's fullness percentage
426 |
427 | if (i - fullSymbols < 0) {
428 | percent = 100;
429 | } else if (i - fullSymbols === 0) {
430 | percent = (renderedValue - i) * 100;
431 | } else {
432 | percent = 0;
433 | }
434 |
435 | symbolNodes.push(React.createElement(RatingSymbol, _extends({
436 | key: i,
437 | index: i,
438 | readonly: readonly,
439 | inactiveIcon: empty[i % empty.length],
440 | activeIcon: shouldDisplayPlaceholder ? placeholder[i % full.length] : full[i % full.length],
441 | percent: percent,
442 | direction: direction
443 | }, !readonly && {
444 | onClick: this.symbolClick,
445 | onMouseMove: this.symbolMouseMove,
446 | onTouchMove: this.symbolMouseMove,
447 | onTouchEnd: this.symbolEnd
448 | })));
449 | }
450 |
451 | return React.createElement("span", _extends({
452 | id: id,
453 | style: _objectSpread({}, style, {
454 | display: 'inline-block',
455 | direction: direction
456 | }),
457 | className: className,
458 | tabIndex: tabIndex,
459 | "aria-label": this.props['aria-label']
460 | }, !readonly && {
461 | onMouseLeave: this.onMouseLeave
462 | }), symbolNodes);
463 | }
464 | }]);
465 |
466 | return Rating;
467 | }(React.PureComponent); // Define propTypes only in development.
468 |
469 | function noop() {}
470 |
471 | noop._name = 'react_rating_noop';
472 |
473 | var RatingAPILayer =
474 | /*#__PURE__*/
475 | function (_React$PureComponent) {
476 | _inherits(RatingAPILayer, _React$PureComponent);
477 |
478 | function RatingAPILayer(props) {
479 | var _this;
480 |
481 | _classCallCheck(this, RatingAPILayer);
482 |
483 | _this = _possibleConstructorReturn(this, _getPrototypeOf(RatingAPILayer).call(this, props));
484 | _this.state = {
485 | value: props.initialRating
486 | };
487 | _this.handleClick = _this.handleClick.bind(_assertThisInitialized(_assertThisInitialized(_this)));
488 | _this.handleHover = _this.handleHover.bind(_assertThisInitialized(_assertThisInitialized(_this)));
489 | return _this;
490 | }
491 |
492 | _createClass(RatingAPILayer, [{
493 | key: "UNSAFE_componentWillReceiveProps",
494 | value: function UNSAFE_componentWillReceiveProps(nextProps) {
495 | this.setState({
496 | value: nextProps.initialRating
497 | });
498 | }
499 | }, {
500 | key: "handleClick",
501 | value: function handleClick(value, e) {
502 | var _this2 = this;
503 |
504 | var newValue = this.translateDisplayValueToValue(value);
505 | this.props.onClick(newValue); // Avoid calling setState if not necessary. Micro optimisation.
506 |
507 | if (this.state.value !== newValue) {
508 | // If we have a new value trigger onChange callback.
509 | this.setState({
510 | value: newValue
511 | }, function () {
512 | return _this2.props.onChange(_this2.state.value);
513 | });
514 | }
515 | }
516 | }, {
517 | key: "handleHover",
518 | value: function handleHover(displayValue) {
519 | var value = displayValue === undefined ? displayValue : this.translateDisplayValueToValue(displayValue);
520 | this.props.onHover(value);
521 | }
522 | }, {
523 | key: "translateDisplayValueToValue",
524 | value: function translateDisplayValueToValue(displayValue) {
525 | var translatedValue = displayValue * this.props.step + this.props.start; // minimum value cannot be equal to start, since it's exclusive
526 |
527 | return translatedValue === this.props.start ? translatedValue + 1 / this.props.fractions : translatedValue;
528 | }
529 | }, {
530 | key: "tranlateValueToDisplayValue",
531 | value: function tranlateValueToDisplayValue(value) {
532 | if (value === undefined) {
533 | return 0;
534 | }
535 |
536 | return (value - this.props.start) / this.props.step;
537 | }
538 | }, {
539 | key: "render",
540 | value: function render() {
541 | var _this$props = this.props,
542 | step = _this$props.step,
543 | emptySymbol = _this$props.emptySymbol,
544 | fullSymbol = _this$props.fullSymbol,
545 | placeholderSymbol = _this$props.placeholderSymbol,
546 | readonly = _this$props.readonly,
547 | quiet = _this$props.quiet,
548 | fractions = _this$props.fractions,
549 | direction = _this$props.direction,
550 | start = _this$props.start,
551 | stop = _this$props.stop,
552 | id = _this$props.id,
553 | className = _this$props.className,
554 | style = _this$props.style,
555 | tabIndex = _this$props.tabIndex;
556 |
557 | function calculateTotalSymbols(start, stop, step) {
558 | return Math.floor((stop - start) / step);
559 | }
560 |
561 | return React.createElement(Rating, {
562 | id: id,
563 | style: style,
564 | className: className,
565 | tabIndex: tabIndex,
566 | "aria-label": this.props['aria-label'],
567 | totalSymbols: calculateTotalSymbols(start, stop, step),
568 | value: this.tranlateValueToDisplayValue(this.state.value),
569 | placeholderValue: this.tranlateValueToDisplayValue(this.props.placeholderRating),
570 | readonly: readonly,
571 | quiet: quiet,
572 | fractions: fractions,
573 | direction: direction,
574 | emptySymbol: emptySymbol,
575 | fullSymbol: fullSymbol,
576 | placeholderSymbol: placeholderSymbol,
577 | onClick: this.handleClick,
578 | onHover: this.handleHover
579 | });
580 | }
581 | }]);
582 |
583 | return RatingAPILayer;
584 | }(React.PureComponent);
585 |
586 | RatingAPILayer.defaultProps = {
587 | start: 0,
588 | stop: 5,
589 | step: 1,
590 | readonly: false,
591 | quiet: false,
592 | fractions: 1,
593 | direction: 'ltr',
594 | onHover: noop,
595 | onClick: noop,
596 | onChange: noop,
597 | emptySymbol: Style.empty,
598 | fullSymbol: Style.full,
599 | placeholderSymbol: Style.placeholder
600 | }; // Define propTypes only in development.
601 |
602 | export default RatingAPILayer;
603 |
--------------------------------------------------------------------------------
/lib/react-rating.umd.js:
--------------------------------------------------------------------------------
1 | (function (global, factory) {
2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('react')) :
3 | typeof define === 'function' && define.amd ? define(['react'], factory) :
4 | (global = global || self, global.ReactRating = factory(global.React));
5 | }(this, function (React) { 'use strict';
6 |
7 | React = React && React.hasOwnProperty('default') ? React['default'] : React;
8 |
9 | function _typeof(obj) {
10 | if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
11 | _typeof = function (obj) {
12 | return typeof obj;
13 | };
14 | } else {
15 | _typeof = function (obj) {
16 | return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
17 | };
18 | }
19 |
20 | return _typeof(obj);
21 | }
22 |
23 | function _classCallCheck(instance, Constructor) {
24 | if (!(instance instanceof Constructor)) {
25 | throw new TypeError("Cannot call a class as a function");
26 | }
27 | }
28 |
29 | function _defineProperties(target, props) {
30 | for (var i = 0; i < props.length; i++) {
31 | var descriptor = props[i];
32 | descriptor.enumerable = descriptor.enumerable || false;
33 | descriptor.configurable = true;
34 | if ("value" in descriptor) descriptor.writable = true;
35 | Object.defineProperty(target, descriptor.key, descriptor);
36 | }
37 | }
38 |
39 | function _createClass(Constructor, protoProps, staticProps) {
40 | if (protoProps) _defineProperties(Constructor.prototype, protoProps);
41 | if (staticProps) _defineProperties(Constructor, staticProps);
42 | return Constructor;
43 | }
44 |
45 | function _defineProperty(obj, key, value) {
46 | if (key in obj) {
47 | Object.defineProperty(obj, key, {
48 | value: value,
49 | enumerable: true,
50 | configurable: true,
51 | writable: true
52 | });
53 | } else {
54 | obj[key] = value;
55 | }
56 |
57 | return obj;
58 | }
59 |
60 | function _extends() {
61 | _extends = Object.assign || function (target) {
62 | for (var i = 1; i < arguments.length; i++) {
63 | var source = arguments[i];
64 |
65 | for (var key in source) {
66 | if (Object.prototype.hasOwnProperty.call(source, key)) {
67 | target[key] = source[key];
68 | }
69 | }
70 | }
71 |
72 | return target;
73 | };
74 |
75 | return _extends.apply(this, arguments);
76 | }
77 |
78 | function _objectSpread(target) {
79 | for (var i = 1; i < arguments.length; i++) {
80 | var source = arguments[i] != null ? arguments[i] : {};
81 | var ownKeys = Object.keys(source);
82 |
83 | if (typeof Object.getOwnPropertySymbols === 'function') {
84 | ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) {
85 | return Object.getOwnPropertyDescriptor(source, sym).enumerable;
86 | }));
87 | }
88 |
89 | ownKeys.forEach(function (key) {
90 | _defineProperty(target, key, source[key]);
91 | });
92 | }
93 |
94 | return target;
95 | }
96 |
97 | function _inherits(subClass, superClass) {
98 | if (typeof superClass !== "function" && superClass !== null) {
99 | throw new TypeError("Super expression must either be null or a function");
100 | }
101 |
102 | subClass.prototype = Object.create(superClass && superClass.prototype, {
103 | constructor: {
104 | value: subClass,
105 | writable: true,
106 | configurable: true
107 | }
108 | });
109 | if (superClass) _setPrototypeOf(subClass, superClass);
110 | }
111 |
112 | function _getPrototypeOf(o) {
113 | _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
114 | return o.__proto__ || Object.getPrototypeOf(o);
115 | };
116 | return _getPrototypeOf(o);
117 | }
118 |
119 | function _setPrototypeOf(o, p) {
120 | _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
121 | o.__proto__ = p;
122 | return o;
123 | };
124 |
125 | return _setPrototypeOf(o, p);
126 | }
127 |
128 | function _assertThisInitialized(self) {
129 | if (self === void 0) {
130 | throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
131 | }
132 |
133 | return self;
134 | }
135 |
136 | function _possibleConstructorReturn(self, call) {
137 | if (call && (typeof call === "object" || typeof call === "function")) {
138 | return call;
139 | }
140 |
141 | return _assertThisInitialized(self);
142 | }
143 |
144 | var style = {
145 | display: 'inline-block',
146 | borderRadius: '50%',
147 | border: '5px double white',
148 | width: 30,
149 | height: 30
150 | };
151 | var Style = {
152 | empty: _objectSpread({}, style, {
153 | backgroundColor: '#ccc'
154 | }),
155 | full: _objectSpread({}, style, {
156 | backgroundColor: 'black'
157 | }),
158 | placeholder: _objectSpread({}, style, {
159 | backgroundColor: 'red'
160 | })
161 | };
162 |
163 | // Return the corresponding React node for an icon.
164 | var _iconNode = function _iconNode(icon) {
165 | // If it is already a React Element just return it.
166 | if (React.isValidElement(icon)) {
167 | return icon;
168 | } // If it is an object, try to use it as a CSS style object.
169 |
170 |
171 | if (_typeof(icon) === 'object' && icon !== null) {
172 | return React.createElement("span", {
173 | style: icon
174 | });
175 | } // If it is a string, use it as class names.
176 |
177 |
178 | if (Object.prototype.toString.call(icon) === '[object String]') {
179 | return React.createElement("span", {
180 | className: icon
181 | });
182 | }
183 | };
184 |
185 | var RatingSymbol =
186 | /*#__PURE__*/
187 | function (_React$PureComponent) {
188 | _inherits(RatingSymbol, _React$PureComponent);
189 |
190 | function RatingSymbol() {
191 | _classCallCheck(this, RatingSymbol);
192 |
193 | return _possibleConstructorReturn(this, _getPrototypeOf(RatingSymbol).apply(this, arguments));
194 | }
195 |
196 | _createClass(RatingSymbol, [{
197 | key: "render",
198 | value: function render() {
199 | var _iconContainerStyle;
200 |
201 | var _this$props = this.props,
202 | index = _this$props.index,
203 | inactiveIcon = _this$props.inactiveIcon,
204 | activeIcon = _this$props.activeIcon,
205 | percent = _this$props.percent,
206 | direction = _this$props.direction,
207 | readonly = _this$props.readonly,
208 | onClick = _this$props.onClick,
209 | onMouseMove = _this$props.onMouseMove,
210 | onTouchEnd = _this$props.onTouchEnd;
211 |
212 | var backgroundNode = _iconNode(inactiveIcon);
213 |
214 | var showbgIcon = percent < 100;
215 | var bgIconContainerStyle = showbgIcon ? {} : {
216 | visibility: 'hidden'
217 | };
218 |
219 | var iconNode = _iconNode(activeIcon);
220 |
221 | var iconContainerStyle = (_iconContainerStyle = {
222 | display: 'inline-block',
223 | position: 'absolute',
224 | overflow: 'hidden',
225 | top: 0
226 | }, _defineProperty(_iconContainerStyle, direction === 'rtl' ? 'right' : 'left', 0), _defineProperty(_iconContainerStyle, "width", "".concat(percent, "%")), _iconContainerStyle);
227 | var style = {
228 | cursor: !readonly ? 'pointer' : 'inherit',
229 | display: 'inline-block',
230 | position: 'relative'
231 | };
232 |
233 | function handleMouseMove(e) {
234 | if (onMouseMove) {
235 | onMouseMove(index, e);
236 | }
237 | }
238 |
239 | function handleMouseClick(e) {
240 | if (onClick) {
241 | // [Supporting both TouchEvent and MouseEvent](https://developer.mozilla.org/en-US/docs/Web/API/Touch_events/Supporting_both_TouchEvent_and_MouseEvent)
242 | // We must prevent firing click event twice on touch devices.
243 | e.preventDefault();
244 | onClick(index, e);
245 | }
246 | }
247 |
248 | function handleTouchEnd(e) {
249 | if (onTouchEnd) {
250 | onTouchEnd(index, e);
251 | }
252 | }
253 |
254 | return React.createElement("span", {
255 | style: style,
256 | onClick: handleMouseClick,
257 | onMouseMove: handleMouseMove,
258 | onTouchMove: handleMouseMove,
259 | onTouchEnd: handleTouchEnd
260 | }, React.createElement("span", {
261 | style: bgIconContainerStyle
262 | }, backgroundNode), React.createElement("span", {
263 | style: iconContainerStyle
264 | }, iconNode));
265 | }
266 | }]);
267 |
268 | return RatingSymbol;
269 | }(React.PureComponent); // Define propTypes only in development. They will be void in production.
270 |
271 | var Rating =
272 | /*#__PURE__*/
273 | function (_React$PureComponent) {
274 | _inherits(Rating, _React$PureComponent);
275 |
276 | function Rating(props) {
277 | var _this;
278 |
279 | _classCallCheck(this, Rating);
280 |
281 | _this = _possibleConstructorReturn(this, _getPrototypeOf(Rating).call(this, props));
282 | _this.state = {
283 | // Indicates the value that is displayed to the user in the form of symbols.
284 | // It can be either 0 (for no displayed symbols) or (0, end]
285 | displayValue: _this.props.value,
286 | // Indicates if the user is currently hovering over the rating element
287 | interacting: false
288 | };
289 | _this.onMouseLeave = _this.onMouseLeave.bind(_assertThisInitialized(_assertThisInitialized(_this)));
290 | _this.symbolMouseMove = _this.symbolMouseMove.bind(_assertThisInitialized(_assertThisInitialized(_this)));
291 | _this.symbolClick = _this.symbolClick.bind(_assertThisInitialized(_assertThisInitialized(_this)));
292 | _this.symbolEnd = _this.symbolEnd.bind(_assertThisInitialized(_assertThisInitialized(_this)));
293 | return _this;
294 | }
295 |
296 | _createClass(Rating, [{
297 | key: "UNSAFE_componentWillReceiveProps",
298 | value: function UNSAFE_componentWillReceiveProps(nextProps) {
299 | var valueChanged = this.props.value !== nextProps.value;
300 | this.setState(function (prevState) {
301 | return {
302 | displayValue: valueChanged ? nextProps.value : prevState.displayValue
303 | };
304 | });
305 | } // NOTE: This callback is a little bit fragile. Needs some "care" because
306 | // it relies on brittle state kept with different props and state
307 | // combinations to try to figure out from where we are coming, I mean, what
308 | // caused this update.
309 |
310 | }, {
311 | key: "componentDidUpdate",
312 | value: function componentDidUpdate(prevProps, prevState) {
313 | // When hover ends, call this.props.onHover with no value.
314 | if (prevState.interacting && !this.state.interacting) {
315 | return this.props.onHover();
316 | } // When hover over.
317 | // Hover in should only be emitted while we are hovering (interacting),
318 | // unless we changed the value, usually originated by an onClick event.
319 | // We do not want to emit a hover in event again on the clicked
320 | // symbol.
321 |
322 |
323 | if (this.state.interacting && prevProps.value == this.props.value) {
324 | this.props.onHover(this.state.displayValue);
325 | }
326 | }
327 | }, {
328 | key: "symbolEnd",
329 | value: function symbolEnd(symbolIndex, event) {
330 | // Do not raise the click event on quiet mode when a touch end is received.
331 | // On quiet mode the touch end event only notifies that we have left the
332 | // symbol. We wait for the actual click event to call the symbolClick.
333 | // On not quiet mode we simulate the click event on touch end and we just
334 | // prevent the real on click event to be raised.
335 | // NOTE: I know how we manage click events on touch devices is a little bit
336 | // weird because we do not notify the starting value that was clicked but
337 | // the last (touched) value.
338 | if (!this.props.quiet) {
339 | this.symbolClick(symbolIndex, event);
340 | event.preventDefault();
341 | } // On touch end we are "leaving" the symbol.
342 |
343 |
344 | this.onMouseLeave();
345 | }
346 | }, {
347 | key: "symbolClick",
348 | value: function symbolClick(symbolIndex, event) {
349 | var value = this.calculateDisplayValue(symbolIndex, event);
350 | this.props.onClick(value, event);
351 | }
352 | }, {
353 | key: "symbolMouseMove",
354 | value: function symbolMouseMove(symbolIndex, event) {
355 | var value = this.calculateDisplayValue(symbolIndex, event); // This call should cause an update only if the state changes.
356 | // Mainly the first time the mouse enters and whenever the value changes.
357 | // So DidComponentUpdate is NOT called for every mouse movement.
358 |
359 | this.setState({
360 | interacting: !this.props.readonly,
361 | displayValue: value
362 | });
363 | }
364 | }, {
365 | key: "onMouseLeave",
366 | value: function onMouseLeave() {
367 | this.setState({
368 | displayValue: this.props.value,
369 | interacting: false
370 | });
371 | }
372 | }, {
373 | key: "calculateDisplayValue",
374 | value: function calculateDisplayValue(symbolIndex, event) {
375 | var percentage = this.calculateHoverPercentage(event); // Get the closest top fraction.
376 |
377 | var fraction = Math.ceil(percentage % 1 * this.props.fractions) / this.props.fractions; // Truncate decimal trying to avoid float precission issues.
378 |
379 | var precision = Math.pow(10, 3);
380 | var displayValue = symbolIndex + (Math.floor(percentage) + Math.floor(fraction * precision) / precision); // ensure the returned value is greater than 0 and lower than totalSymbols
381 |
382 | return displayValue > 0 ? displayValue > this.props.totalSymbols ? this.props.totalSymbols : displayValue : 1 / this.props.fractions;
383 | }
384 | }, {
385 | key: "calculateHoverPercentage",
386 | value: function calculateHoverPercentage(event) {
387 | var clientX = event.nativeEvent.type.indexOf("touch") > -1 ? event.nativeEvent.type.indexOf("touchend") > -1 ? event.changedTouches[0].clientX : event.touches[0].clientX : event.clientX;
388 | var targetRect = event.target.getBoundingClientRect();
389 | var delta = this.props.direction === 'rtl' ? targetRect.right - clientX : clientX - targetRect.left; // Returning 0 if the delta is negative solves the flickering issue
390 |
391 | return delta < 0 ? 0 : delta / targetRect.width;
392 | }
393 | }, {
394 | key: "render",
395 | value: function render() {
396 | var _this$props = this.props,
397 | readonly = _this$props.readonly,
398 | quiet = _this$props.quiet,
399 | totalSymbols = _this$props.totalSymbols,
400 | value = _this$props.value,
401 | placeholderValue = _this$props.placeholderValue,
402 | direction = _this$props.direction,
403 | emptySymbol = _this$props.emptySymbol,
404 | fullSymbol = _this$props.fullSymbol,
405 | placeholderSymbol = _this$props.placeholderSymbol,
406 | className = _this$props.className,
407 | id = _this$props.id,
408 | style = _this$props.style,
409 | tabIndex = _this$props.tabIndex;
410 | var _this$state = this.state,
411 | displayValue = _this$state.displayValue,
412 | interacting = _this$state.interacting;
413 | var symbolNodes = [];
414 | var empty = [].concat(emptySymbol);
415 | var full = [].concat(fullSymbol);
416 | var placeholder = [].concat(placeholderSymbol);
417 | var shouldDisplayPlaceholder = placeholderValue !== 0 && value === 0 && !interacting; // The value that will be used as base for calculating how to render the symbols
418 |
419 | var renderedValue;
420 |
421 | if (shouldDisplayPlaceholder) {
422 | renderedValue = placeholderValue;
423 | } else {
424 | renderedValue = quiet ? value : displayValue;
425 | } // The amount of full symbols
426 |
427 |
428 | var fullSymbols = Math.floor(renderedValue);
429 |
430 | for (var i = 0; i < totalSymbols; i++) {
431 | var percent = void 0; // Calculate each symbol's fullness percentage
432 |
433 | if (i - fullSymbols < 0) {
434 | percent = 100;
435 | } else if (i - fullSymbols === 0) {
436 | percent = (renderedValue - i) * 100;
437 | } else {
438 | percent = 0;
439 | }
440 |
441 | symbolNodes.push(React.createElement(RatingSymbol, _extends({
442 | key: i,
443 | index: i,
444 | readonly: readonly,
445 | inactiveIcon: empty[i % empty.length],
446 | activeIcon: shouldDisplayPlaceholder ? placeholder[i % full.length] : full[i % full.length],
447 | percent: percent,
448 | direction: direction
449 | }, !readonly && {
450 | onClick: this.symbolClick,
451 | onMouseMove: this.symbolMouseMove,
452 | onTouchMove: this.symbolMouseMove,
453 | onTouchEnd: this.symbolEnd
454 | })));
455 | }
456 |
457 | return React.createElement("span", _extends({
458 | id: id,
459 | style: _objectSpread({}, style, {
460 | display: 'inline-block',
461 | direction: direction
462 | }),
463 | className: className,
464 | tabIndex: tabIndex,
465 | "aria-label": this.props['aria-label']
466 | }, !readonly && {
467 | onMouseLeave: this.onMouseLeave
468 | }), symbolNodes);
469 | }
470 | }]);
471 |
472 | return Rating;
473 | }(React.PureComponent); // Define propTypes only in development.
474 |
475 | function noop() {}
476 |
477 | noop._name = 'react_rating_noop';
478 |
479 | var RatingAPILayer =
480 | /*#__PURE__*/
481 | function (_React$PureComponent) {
482 | _inherits(RatingAPILayer, _React$PureComponent);
483 |
484 | function RatingAPILayer(props) {
485 | var _this;
486 |
487 | _classCallCheck(this, RatingAPILayer);
488 |
489 | _this = _possibleConstructorReturn(this, _getPrototypeOf(RatingAPILayer).call(this, props));
490 | _this.state = {
491 | value: props.initialRating
492 | };
493 | _this.handleClick = _this.handleClick.bind(_assertThisInitialized(_assertThisInitialized(_this)));
494 | _this.handleHover = _this.handleHover.bind(_assertThisInitialized(_assertThisInitialized(_this)));
495 | return _this;
496 | }
497 |
498 | _createClass(RatingAPILayer, [{
499 | key: "UNSAFE_componentWillReceiveProps",
500 | value: function UNSAFE_componentWillReceiveProps(nextProps) {
501 | this.setState({
502 | value: nextProps.initialRating
503 | });
504 | }
505 | }, {
506 | key: "handleClick",
507 | value: function handleClick(value, e) {
508 | var _this2 = this;
509 |
510 | var newValue = this.translateDisplayValueToValue(value);
511 | this.props.onClick(newValue); // Avoid calling setState if not necessary. Micro optimisation.
512 |
513 | if (this.state.value !== newValue) {
514 | // If we have a new value trigger onChange callback.
515 | this.setState({
516 | value: newValue
517 | }, function () {
518 | return _this2.props.onChange(_this2.state.value);
519 | });
520 | }
521 | }
522 | }, {
523 | key: "handleHover",
524 | value: function handleHover(displayValue) {
525 | var value = displayValue === undefined ? displayValue : this.translateDisplayValueToValue(displayValue);
526 | this.props.onHover(value);
527 | }
528 | }, {
529 | key: "translateDisplayValueToValue",
530 | value: function translateDisplayValueToValue(displayValue) {
531 | var translatedValue = displayValue * this.props.step + this.props.start; // minimum value cannot be equal to start, since it's exclusive
532 |
533 | return translatedValue === this.props.start ? translatedValue + 1 / this.props.fractions : translatedValue;
534 | }
535 | }, {
536 | key: "tranlateValueToDisplayValue",
537 | value: function tranlateValueToDisplayValue(value) {
538 | if (value === undefined) {
539 | return 0;
540 | }
541 |
542 | return (value - this.props.start) / this.props.step;
543 | }
544 | }, {
545 | key: "render",
546 | value: function render() {
547 | var _this$props = this.props,
548 | step = _this$props.step,
549 | emptySymbol = _this$props.emptySymbol,
550 | fullSymbol = _this$props.fullSymbol,
551 | placeholderSymbol = _this$props.placeholderSymbol,
552 | readonly = _this$props.readonly,
553 | quiet = _this$props.quiet,
554 | fractions = _this$props.fractions,
555 | direction = _this$props.direction,
556 | start = _this$props.start,
557 | stop = _this$props.stop,
558 | id = _this$props.id,
559 | className = _this$props.className,
560 | style = _this$props.style,
561 | tabIndex = _this$props.tabIndex;
562 |
563 | function calculateTotalSymbols(start, stop, step) {
564 | return Math.floor((stop - start) / step);
565 | }
566 |
567 | return React.createElement(Rating, {
568 | id: id,
569 | style: style,
570 | className: className,
571 | tabIndex: tabIndex,
572 | "aria-label": this.props['aria-label'],
573 | totalSymbols: calculateTotalSymbols(start, stop, step),
574 | value: this.tranlateValueToDisplayValue(this.state.value),
575 | placeholderValue: this.tranlateValueToDisplayValue(this.props.placeholderRating),
576 | readonly: readonly,
577 | quiet: quiet,
578 | fractions: fractions,
579 | direction: direction,
580 | emptySymbol: emptySymbol,
581 | fullSymbol: fullSymbol,
582 | placeholderSymbol: placeholderSymbol,
583 | onClick: this.handleClick,
584 | onHover: this.handleHover
585 | });
586 | }
587 | }]);
588 |
589 | return RatingAPILayer;
590 | }(React.PureComponent);
591 |
592 | RatingAPILayer.defaultProps = {
593 | start: 0,
594 | stop: 5,
595 | step: 1,
596 | readonly: false,
597 | quiet: false,
598 | fractions: 1,
599 | direction: 'ltr',
600 | onHover: noop,
601 | onClick: noop,
602 | onChange: noop,
603 | emptySymbol: Style.empty,
604 | fullSymbol: Style.full,
605 | placeholderSymbol: Style.placeholder
606 | }; // Define propTypes only in development.
607 |
608 | return RatingAPILayer;
609 |
610 | }));
611 | //# sourceMappingURL=react-rating.umd.js.map
612 |
--------------------------------------------------------------------------------
/lib/react-rating.umd.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"react-rating.umd.js","sources":["../src/utils/style.js","../src/RatingSymbol.js","../src/Rating.js","../src/utils/noop.js","../src/RatingAPILayer.js"],"sourcesContent":["var style = {\n display: 'inline-block',\n borderRadius: '50%',\n border: '5px double white',\n width: 30,\n height: 30\n};\n\nexport default {\n empty: {\n ...style,\n backgroundColor: '#ccc'\n },\n full: {\n ...style,\n backgroundColor: 'black'\n },\n placeholder: {\n ...style,\n backgroundColor: 'red'\n }\n};\n","import React from 'react';\nimport PropTypes from 'prop-types';\n\n// Return the corresponding React node for an icon.\nconst _iconNode = (icon) => {\n // If it is already a React Element just return it.\n if (React.isValidElement(icon)) {\n return icon;\n }\n // If it is an object, try to use it as a CSS style object.\n if (typeof icon === 'object' && icon !== null) {\n return ;\n }\n // If it is a string, use it as class names.\n if (Object.prototype.toString.call(icon) === '[object String]') {\n return ;\n }\n};\n\nclass RatingSymbol extends React.PureComponent {\n render() {\n const {\n index,\n inactiveIcon,\n activeIcon,\n percent,\n direction,\n readonly,\n onClick,\n onMouseMove,\n onTouchEnd\n } = this.props;\n const backgroundNode = _iconNode(inactiveIcon);\n const showbgIcon = percent < 100;\n const bgIconContainerStyle = showbgIcon\n ? {}\n : {\n visibility: 'hidden'\n };\n const iconNode = _iconNode(activeIcon);\n const iconContainerStyle = {\n display: 'inline-block',\n position: 'absolute',\n overflow: 'hidden',\n top: 0,\n [direction === 'rtl' ? 'right' : 'left']: 0,\n width: `${percent}%`\n };\n const style = {\n cursor: !readonly ? 'pointer' : 'inherit',\n display: 'inline-block',\n position: 'relative'\n };\n\n function handleMouseMove(e) {\n if (onMouseMove) {\n onMouseMove(index, e);\n }\n }\n\n function handleMouseClick(e) {\n if (onClick) {\n // [Supporting both TouchEvent and MouseEvent](https://developer.mozilla.org/en-US/docs/Web/API/Touch_events/Supporting_both_TouchEvent_and_MouseEvent)\n // We must prevent firing click event twice on touch devices.\n e.preventDefault();\n onClick(index, e);\n }\n }\n\n function handleTouchEnd(e) {\n if (onTouchEnd) {\n onTouchEnd(index, e);\n }\n }\n\n return (\n \n \n {backgroundNode}\n \n \n {iconNode}\n \n \n );\n }\n}\n\n// Define propTypes only in development. They will be void in production.\nRatingSymbol.propTypes = typeof __DEV__ !== 'undefined' && __DEV__ && {\n index: PropTypes.number.isRequired,\n readonly: PropTypes.bool.isRequired,\n inactiveIcon: PropTypes.oneOfType([\n PropTypes.string,\n PropTypes.object,\n PropTypes.element\n ]).isRequired,\n activeIcon: PropTypes.oneOfType([\n PropTypes.string,\n PropTypes.object,\n PropTypes.element\n ]).isRequired,\n percent: PropTypes.number.isRequired,\n direction: PropTypes.string.isRequired,\n onClick: PropTypes.func,\n onMouseMove: PropTypes.func,\n onTouchMove: PropTypes.func,\n onTouchEnd: PropTypes.func\n};\n\nexport default RatingSymbol;\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport Symbol from './RatingSymbol';\n\nclass Rating extends React.PureComponent {\n constructor(props) {\n super(props);\n this.state = {\n // Indicates the value that is displayed to the user in the form of symbols.\n // It can be either 0 (for no displayed symbols) or (0, end]\n displayValue: this.props.value,\n // Indicates if the user is currently hovering over the rating element\n interacting: false\n };\n this.onMouseLeave = this.onMouseLeave.bind(this);\n this.symbolMouseMove = this.symbolMouseMove.bind(this);\n this.symbolClick = this.symbolClick.bind(this);\n this.symbolEnd = this.symbolEnd.bind(this);\n }\n\n UNSAFE_componentWillReceiveProps(nextProps) {\n const valueChanged = this.props.value !== nextProps.value;\n this.setState((prevState) => ({\n displayValue: valueChanged ? nextProps.value : prevState.displayValue\n }));\n }\n\n // NOTE: This callback is a little bit fragile. Needs some \"care\" because\n // it relies on brittle state kept with different props and state\n // combinations to try to figure out from where we are coming, I mean, what\n // caused this update.\n componentDidUpdate(prevProps, prevState) {\n // When hover ends, call this.props.onHover with no value.\n if (prevState.interacting && !this.state.interacting) {\n return this.props.onHover();\n }\n\n // When hover over.\n // Hover in should only be emitted while we are hovering (interacting),\n // unless we changed the value, usually originated by an onClick event.\n // We do not want to emit a hover in event again on the clicked\n // symbol.\n if (this.state.interacting && prevProps.value == this.props.value) {\n this.props.onHover(this.state.displayValue);\n }\n }\n\n symbolEnd(symbolIndex, event) {\n // Do not raise the click event on quiet mode when a touch end is received.\n // On quiet mode the touch end event only notifies that we have left the\n // symbol. We wait for the actual click event to call the symbolClick.\n // On not quiet mode we simulate the click event on touch end and we just\n // prevent the real on click event to be raised.\n // NOTE: I know how we manage click events on touch devices is a little bit\n // weird because we do not notify the starting value that was clicked but\n // the last (touched) value.\n if (!this.props.quiet) {\n this.symbolClick(symbolIndex, event);\n event.preventDefault();\n }\n // On touch end we are \"leaving\" the symbol.\n this.onMouseLeave();\n }\n\n symbolClick(symbolIndex, event) {\n const value = this.calculateDisplayValue(symbolIndex, event);\n this.props.onClick(value, event);\n }\n\n symbolMouseMove(symbolIndex, event) {\n const value = this.calculateDisplayValue(symbolIndex, event);\n // This call should cause an update only if the state changes.\n // Mainly the first time the mouse enters and whenever the value changes.\n // So DidComponentUpdate is NOT called for every mouse movement.\n this.setState({\n interacting: !this.props.readonly,\n displayValue: value\n });\n }\n\n onMouseLeave() {\n this.setState({\n displayValue: this.props.value,\n interacting: false\n });\n }\n\n calculateDisplayValue(symbolIndex, event) {\n const percentage = this.calculateHoverPercentage(event);\n // Get the closest top fraction.\n const fraction = Math.ceil(percentage % 1 * this.props.fractions) / this.props.fractions;\n // Truncate decimal trying to avoid float precission issues.\n const precision = 10 ** 3;\n const displayValue =\n symbolIndex + (Math.floor(percentage) + Math.floor(fraction * precision) / precision);\n // ensure the returned value is greater than 0 and lower than totalSymbols\n return displayValue > 0 ? displayValue > this.props.totalSymbols ? this.props.totalSymbols : displayValue : 1 / this.props.fractions;\n }\n\n calculateHoverPercentage(event) {\n const clientX = event.nativeEvent.type.indexOf(\"touch\") > -1\n ? event.nativeEvent.type.indexOf(\"touchend\") > -1\n ? event.changedTouches[0].clientX\n : event.touches[0].clientX\n : event.clientX;\n\n const targetRect = event.target.getBoundingClientRect();\n const delta = this.props.direction === 'rtl'\n ? targetRect.right - clientX\n : clientX - targetRect.left;\n\n // Returning 0 if the delta is negative solves the flickering issue\n return delta < 0 ? 0 : delta / targetRect.width;\n }\n\n render() {\n const {\n readonly,\n quiet,\n totalSymbols,\n value,\n placeholderValue,\n direction,\n emptySymbol,\n fullSymbol,\n placeholderSymbol,\n className,\n id,\n style,\n tabIndex\n } = this.props;\n const { displayValue, interacting } = this.state;\n const symbolNodes = [];\n const empty = [].concat(emptySymbol);\n const full = [].concat(fullSymbol);\n const placeholder = [].concat(placeholderSymbol);\n const shouldDisplayPlaceholder =\n placeholderValue !== 0 &&\n value === 0 &&\n !interacting;\n\n // The value that will be used as base for calculating how to render the symbols\n let renderedValue;\n if (shouldDisplayPlaceholder) {\n renderedValue = placeholderValue;\n } else {\n renderedValue = quiet ? value : displayValue;\n }\n\n // The amount of full symbols\n const fullSymbols = Math.floor(renderedValue);\n\n for (let i = 0; i < totalSymbols; i++) {\n let percent;\n // Calculate each symbol's fullness percentage\n if (i - fullSymbols < 0) {\n percent = 100;\n } else if (i - fullSymbols === 0) {\n percent = (renderedValue - i) * 100;\n } else {\n percent = 0;\n }\n\n symbolNodes.push(\n \n );\n }\n\n return (\n \n {symbolNodes}\n \n );\n }\n}\n\n// Define propTypes only in development.\nRating.propTypes = typeof __DEV__ !== 'undefined' && __DEV__ && {\n totalSymbols: PropTypes.number.isRequired,\n value: PropTypes.number.isRequired, // Always >= 0\n placeholderValue: PropTypes.number.isRequired,\n readonly: PropTypes.bool.isRequired,\n quiet: PropTypes.bool.isRequired,\n fractions: PropTypes.number.isRequired,\n direction: PropTypes.string.isRequired,\n emptySymbol: PropTypes.oneOfType([\n // Array of class names and/or style objects.\n PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.element])),\n // Class names.\n PropTypes.string,\n // Style objects.\n PropTypes.object,\n // React element\n PropTypes.element\n ]).isRequired,\n fullSymbol: PropTypes.oneOfType([\n // Array of class names and/or style objects.\n PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.element])),\n // Class names.\n PropTypes.string,\n // Style objects.\n PropTypes.object,\n // React element\n PropTypes.element\n ]).isRequired,\n placeholderSymbol: PropTypes.oneOfType([\n // Array of class names and/or style objects.\n PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.element])),\n // Class names.\n PropTypes.string,\n // Style objects.\n PropTypes.object,\n // React element\n PropTypes.element\n ]),\n onClick: PropTypes.func.isRequired,\n onHover: PropTypes.func.isRequired\n};\n\nexport default Rating;\n","function noop() {}\nnoop._name = 'react_rating_noop';\n\nexport default noop;\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport Style from './utils/style';\nimport Rating from './Rating';\nimport noop from './utils/noop';\n\nclass RatingAPILayer extends React.PureComponent {\n constructor(props) {\n super(props);\n this.state = {\n value: props.initialRating\n };\n this.handleClick = this.handleClick.bind(this);\n this.handleHover = this.handleHover.bind(this);\n }\n\n UNSAFE_componentWillReceiveProps(nextProps) {\n this.setState({\n value: nextProps.initialRating\n });\n }\n\n handleClick(value, e) {\n const newValue = this.translateDisplayValueToValue(value);\n this.props.onClick(newValue);\n // Avoid calling setState if not necessary. Micro optimisation.\n if (this.state.value !== newValue) {\n // If we have a new value trigger onChange callback.\n this.setState({\n value: newValue\n }, () => this.props.onChange(this.state.value));\n }\n }\n\n handleHover(displayValue) {\n const value = displayValue === undefined\n ? displayValue\n : this.translateDisplayValueToValue(displayValue);\n this.props.onHover(value);\n }\n\n translateDisplayValueToValue(displayValue) {\n const translatedValue = displayValue * this.props.step + this.props.start;\n // minimum value cannot be equal to start, since it's exclusive\n return translatedValue === this.props.start\n ? translatedValue + 1 / this.props.fractions\n : translatedValue;\n }\n\n tranlateValueToDisplayValue(value) {\n if (value === undefined) {\n return 0;\n }\n return (value - this.props.start) / this.props.step;\n }\n\n render() {\n const {\n step,\n emptySymbol,\n fullSymbol,\n placeholderSymbol,\n readonly,\n quiet,\n fractions,\n direction,\n start,\n stop,\n id,\n className,\n style,\n tabIndex\n } = this.props;\n\n function calculateTotalSymbols(start, stop, step) {\n return Math.floor((stop - start) / step);\n }\n\n return (\n \n );\n }\n}\n\nRatingAPILayer.defaultProps = {\n start: 0,\n stop: 5,\n step: 1,\n readonly: false,\n quiet: false,\n fractions: 1,\n direction: 'ltr',\n onHover: noop,\n onClick: noop,\n onChange: noop,\n emptySymbol: Style.empty,\n fullSymbol: Style.full,\n placeholderSymbol: Style.placeholder\n};\n\n// Define propTypes only in development.\nRatingAPILayer.propTypes = typeof __DEV__ !== 'undefined' && __DEV__ && {\n start: PropTypes.number,\n stop: PropTypes.number,\n step: PropTypes.number,\n initialRating: PropTypes.number,\n placeholderRating: PropTypes.number,\n readonly: PropTypes.bool,\n quiet: PropTypes.bool,\n fractions: PropTypes.number,\n direction: PropTypes.string,\n emptySymbol: PropTypes.oneOfType([\n // Array of class names and/or style objects.\n PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.element])),\n // Class names.\n PropTypes.string,\n // Style objects.\n PropTypes.object,\n // React element\n PropTypes.element\n ]),\n fullSymbol: PropTypes.oneOfType([\n // Array of class names and/or style objects.\n PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.element])),\n // Class names.\n PropTypes.string,\n // Style objects.\n PropTypes.object,\n // React element\n PropTypes.element\n ]),\n placeholderSymbol: PropTypes.oneOfType([\n // Array of class names and/or style objects.\n PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.element])),\n // Class names.\n PropTypes.string,\n // Style objects.\n PropTypes.object,\n // React element\n PropTypes.element\n ]),\n onHover: PropTypes.func,\n onClick: PropTypes.func,\n onChange: PropTypes.func\n};\n\nexport default RatingAPILayer;\n"],"names":["style","display","borderRadius","border","width","height","empty","backgroundColor","full","placeholder","_iconNode","icon","React","isValidElement","Object","prototype","toString","call","RatingSymbol","props","index","inactiveIcon","activeIcon","percent","direction","readonly","onClick","onMouseMove","onTouchEnd","backgroundNode","showbgIcon","bgIconContainerStyle","visibility","iconNode","iconContainerStyle","position","overflow","top","cursor","handleMouseMove","e","handleMouseClick","preventDefault","handleTouchEnd","PureComponent","Rating","state","displayValue","value","interacting","onMouseLeave","bind","symbolMouseMove","symbolClick","symbolEnd","nextProps","valueChanged","setState","prevState","prevProps","onHover","symbolIndex","event","quiet","calculateDisplayValue","percentage","calculateHoverPercentage","fraction","Math","ceil","fractions","precision","floor","totalSymbols","clientX","nativeEvent","type","indexOf","changedTouches","touches","targetRect","target","getBoundingClientRect","delta","right","left","placeholderValue","emptySymbol","fullSymbol","placeholderSymbol","className","id","tabIndex","symbolNodes","concat","shouldDisplayPlaceholder","renderedValue","fullSymbols","i","push","_Symbol","length","onTouchMove","noop","_name","RatingAPILayer","initialRating","handleClick","handleHover","newValue","translateDisplayValueToValue","onChange","undefined","translatedValue","step","start","stop","calculateTotalSymbols","tranlateValueToDisplayValue","placeholderRating","defaultProps","Style"],"mappings":"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"}
--------------------------------------------------------------------------------
/lib/react-rating.umd.min.js:
--------------------------------------------------------------------------------
1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("react")):"function"==typeof define&&define.amd?define(["react"],t):(e=e||self).ReactRating=t(e.React)}(this,function(e){"use strict";function t(e){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function n(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){for(var n=0;n0?r>this.props.totalSymbols?this.props.totalSymbols:r:1/this.props.fractions}},{key:"calculateHoverPercentage",value:function(e){var t=e.nativeEvent.type.indexOf("touch")>-1?e.nativeEvent.type.indexOf("touchend")>-1?e.changedTouches[0].clientX:e.touches[0].clientX:e.clientX,n=e.target.getBoundingClientRect(),o="rtl"===this.props.direction?n.right-t:t-n.left;return o<0?0:o/n.width}},{key:"render",value:function(){var t,n=this.props,o=n.readonly,i=n.quiet,r=n.totalSymbols,s=n.value,u=n.placeholderValue,c=n.direction,p=n.emptySymbol,y=n.fullSymbol,h=n.placeholderSymbol,f=n.className,d=n.id,b=n.style,m=n.tabIndex,g=this.state,k=g.displayValue,S=g.interacting,M=[],C=[].concat(p),O=[].concat(y),V=[].concat(h),E=0!==u&&0===s&&!S;t=E?u:i?s:k;for(var w=Math.floor(t),P=0;P {\n // If it is already a React Element just return it.\n if (React.isValidElement(icon)) {\n return icon;\n }\n // If it is an object, try to use it as a CSS style object.\n if (typeof icon === 'object' && icon !== null) {\n return ;\n }\n // If it is a string, use it as class names.\n if (Object.prototype.toString.call(icon) === '[object String]') {\n return ;\n }\n};\n\nclass RatingSymbol extends React.PureComponent {\n render() {\n const {\n index,\n inactiveIcon,\n activeIcon,\n percent,\n direction,\n readonly,\n onClick,\n onMouseMove,\n onTouchEnd\n } = this.props;\n const backgroundNode = _iconNode(inactiveIcon);\n const showbgIcon = percent < 100;\n const bgIconContainerStyle = showbgIcon\n ? {}\n : {\n visibility: 'hidden'\n };\n const iconNode = _iconNode(activeIcon);\n const iconContainerStyle = {\n display: 'inline-block',\n position: 'absolute',\n overflow: 'hidden',\n top: 0,\n [direction === 'rtl' ? 'right' : 'left']: 0,\n width: `${percent}%`\n };\n const style = {\n cursor: !readonly ? 'pointer' : 'inherit',\n display: 'inline-block',\n position: 'relative'\n };\n\n function handleMouseMove(e) {\n if (onMouseMove) {\n onMouseMove(index, e);\n }\n }\n\n function handleMouseClick(e) {\n if (onClick) {\n // [Supporting both TouchEvent and MouseEvent](https://developer.mozilla.org/en-US/docs/Web/API/Touch_events/Supporting_both_TouchEvent_and_MouseEvent)\n // We must prevent firing click event twice on touch devices.\n e.preventDefault();\n onClick(index, e);\n }\n }\n\n function handleTouchEnd(e) {\n if (onTouchEnd) {\n onTouchEnd(index, e);\n }\n }\n\n return (\n \n \n {backgroundNode}\n \n \n {iconNode}\n \n \n );\n }\n}\n\n// Define propTypes only in development. They will be void in production.\nRatingSymbol.propTypes = typeof __DEV__ !== 'undefined' && __DEV__ && {\n index: PropTypes.number.isRequired,\n readonly: PropTypes.bool.isRequired,\n inactiveIcon: PropTypes.oneOfType([\n PropTypes.string,\n PropTypes.object,\n PropTypes.element\n ]).isRequired,\n activeIcon: PropTypes.oneOfType([\n PropTypes.string,\n PropTypes.object,\n PropTypes.element\n ]).isRequired,\n percent: PropTypes.number.isRequired,\n direction: PropTypes.string.isRequired,\n onClick: PropTypes.func,\n onMouseMove: PropTypes.func,\n onTouchMove: PropTypes.func,\n onTouchEnd: PropTypes.func\n};\n\nexport default RatingSymbol;\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport Symbol from './RatingSymbol';\n\nclass Rating extends React.PureComponent {\n constructor(props) {\n super(props);\n this.state = {\n // Indicates the value that is displayed to the user in the form of symbols.\n // It can be either 0 (for no displayed symbols) or (0, end]\n displayValue: this.props.value,\n // Indicates if the user is currently hovering over the rating element\n interacting: false\n };\n this.onMouseLeave = this.onMouseLeave.bind(this);\n this.symbolMouseMove = this.symbolMouseMove.bind(this);\n this.symbolClick = this.symbolClick.bind(this);\n this.symbolEnd = this.symbolEnd.bind(this);\n }\n\n UNSAFE_componentWillReceiveProps(nextProps) {\n const valueChanged = this.props.value !== nextProps.value;\n this.setState((prevState) => ({\n displayValue: valueChanged ? nextProps.value : prevState.displayValue\n }));\n }\n\n // NOTE: This callback is a little bit fragile. Needs some \"care\" because\n // it relies on brittle state kept with different props and state\n // combinations to try to figure out from where we are coming, I mean, what\n // caused this update.\n componentDidUpdate(prevProps, prevState) {\n // When hover ends, call this.props.onHover with no value.\n if (prevState.interacting && !this.state.interacting) {\n return this.props.onHover();\n }\n\n // When hover over.\n // Hover in should only be emitted while we are hovering (interacting),\n // unless we changed the value, usually originated by an onClick event.\n // We do not want to emit a hover in event again on the clicked\n // symbol.\n if (this.state.interacting && prevProps.value == this.props.value) {\n this.props.onHover(this.state.displayValue);\n }\n }\n\n symbolEnd(symbolIndex, event) {\n // Do not raise the click event on quiet mode when a touch end is received.\n // On quiet mode the touch end event only notifies that we have left the\n // symbol. We wait for the actual click event to call the symbolClick.\n // On not quiet mode we simulate the click event on touch end and we just\n // prevent the real on click event to be raised.\n // NOTE: I know how we manage click events on touch devices is a little bit\n // weird because we do not notify the starting value that was clicked but\n // the last (touched) value.\n if (!this.props.quiet) {\n this.symbolClick(symbolIndex, event);\n event.preventDefault();\n }\n // On touch end we are \"leaving\" the symbol.\n this.onMouseLeave();\n }\n\n symbolClick(symbolIndex, event) {\n const value = this.calculateDisplayValue(symbolIndex, event);\n this.props.onClick(value, event);\n }\n\n symbolMouseMove(symbolIndex, event) {\n const value = this.calculateDisplayValue(symbolIndex, event);\n // This call should cause an update only if the state changes.\n // Mainly the first time the mouse enters and whenever the value changes.\n // So DidComponentUpdate is NOT called for every mouse movement.\n this.setState({\n interacting: !this.props.readonly,\n displayValue: value\n });\n }\n\n onMouseLeave() {\n this.setState({\n displayValue: this.props.value,\n interacting: false\n });\n }\n\n calculateDisplayValue(symbolIndex, event) {\n const percentage = this.calculateHoverPercentage(event);\n // Get the closest top fraction.\n const fraction = Math.ceil(percentage % 1 * this.props.fractions) / this.props.fractions;\n // Truncate decimal trying to avoid float precission issues.\n const precision = 10 ** 3;\n const displayValue =\n symbolIndex + (Math.floor(percentage) + Math.floor(fraction * precision) / precision);\n // ensure the returned value is greater than 0 and lower than totalSymbols\n return displayValue > 0 ? displayValue > this.props.totalSymbols ? this.props.totalSymbols : displayValue : 1 / this.props.fractions;\n }\n\n calculateHoverPercentage(event) {\n const clientX = event.nativeEvent.type.indexOf(\"touch\") > -1\n ? event.nativeEvent.type.indexOf(\"touchend\") > -1\n ? event.changedTouches[0].clientX\n : event.touches[0].clientX\n : event.clientX;\n\n const targetRect = event.target.getBoundingClientRect();\n const delta = this.props.direction === 'rtl'\n ? targetRect.right - clientX\n : clientX - targetRect.left;\n\n // Returning 0 if the delta is negative solves the flickering issue\n return delta < 0 ? 0 : delta / targetRect.width;\n }\n\n render() {\n const {\n readonly,\n quiet,\n totalSymbols,\n value,\n placeholderValue,\n direction,\n emptySymbol,\n fullSymbol,\n placeholderSymbol,\n className,\n id,\n style,\n tabIndex\n } = this.props;\n const { displayValue, interacting } = this.state;\n const symbolNodes = [];\n const empty = [].concat(emptySymbol);\n const full = [].concat(fullSymbol);\n const placeholder = [].concat(placeholderSymbol);\n const shouldDisplayPlaceholder =\n placeholderValue !== 0 &&\n value === 0 &&\n !interacting;\n\n // The value that will be used as base for calculating how to render the symbols\n let renderedValue;\n if (shouldDisplayPlaceholder) {\n renderedValue = placeholderValue;\n } else {\n renderedValue = quiet ? value : displayValue;\n }\n\n // The amount of full symbols\n const fullSymbols = Math.floor(renderedValue);\n\n for (let i = 0; i < totalSymbols; i++) {\n let percent;\n // Calculate each symbol's fullness percentage\n if (i - fullSymbols < 0) {\n percent = 100;\n } else if (i - fullSymbols === 0) {\n percent = (renderedValue - i) * 100;\n } else {\n percent = 0;\n }\n\n symbolNodes.push(\n \n );\n }\n\n return (\n \n {symbolNodes}\n \n );\n }\n}\n\n// Define propTypes only in development.\nRating.propTypes = typeof __DEV__ !== 'undefined' && __DEV__ && {\n totalSymbols: PropTypes.number.isRequired,\n value: PropTypes.number.isRequired, // Always >= 0\n placeholderValue: PropTypes.number.isRequired,\n readonly: PropTypes.bool.isRequired,\n quiet: PropTypes.bool.isRequired,\n fractions: PropTypes.number.isRequired,\n direction: PropTypes.string.isRequired,\n emptySymbol: PropTypes.oneOfType([\n // Array of class names and/or style objects.\n PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.element])),\n // Class names.\n PropTypes.string,\n // Style objects.\n PropTypes.object,\n // React element\n PropTypes.element\n ]).isRequired,\n fullSymbol: PropTypes.oneOfType([\n // Array of class names and/or style objects.\n PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.element])),\n // Class names.\n PropTypes.string,\n // Style objects.\n PropTypes.object,\n // React element\n PropTypes.element\n ]).isRequired,\n placeholderSymbol: PropTypes.oneOfType([\n // Array of class names and/or style objects.\n PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.element])),\n // Class names.\n PropTypes.string,\n // Style objects.\n PropTypes.object,\n // React element\n PropTypes.element\n ]),\n onClick: PropTypes.func.isRequired,\n onHover: PropTypes.func.isRequired\n};\n\nexport default Rating;\n","function noop() {}\nnoop._name = 'react_rating_noop';\n\nexport default noop;\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport Style from './utils/style';\nimport Rating from './Rating';\nimport noop from './utils/noop';\n\nclass RatingAPILayer extends React.PureComponent {\n constructor(props) {\n super(props);\n this.state = {\n value: props.initialRating\n };\n this.handleClick = this.handleClick.bind(this);\n this.handleHover = this.handleHover.bind(this);\n }\n\n UNSAFE_componentWillReceiveProps(nextProps) {\n this.setState({\n value: nextProps.initialRating\n });\n }\n\n handleClick(value, e) {\n const newValue = this.translateDisplayValueToValue(value);\n this.props.onClick(newValue);\n // Avoid calling setState if not necessary. Micro optimisation.\n if (this.state.value !== newValue) {\n // If we have a new value trigger onChange callback.\n this.setState({\n value: newValue\n }, () => this.props.onChange(this.state.value));\n }\n }\n\n handleHover(displayValue) {\n const value = displayValue === undefined\n ? displayValue\n : this.translateDisplayValueToValue(displayValue);\n this.props.onHover(value);\n }\n\n translateDisplayValueToValue(displayValue) {\n const translatedValue = displayValue * this.props.step + this.props.start;\n // minimum value cannot be equal to start, since it's exclusive\n return translatedValue === this.props.start\n ? translatedValue + 1 / this.props.fractions\n : translatedValue;\n }\n\n tranlateValueToDisplayValue(value) {\n if (value === undefined) {\n return 0;\n }\n return (value - this.props.start) / this.props.step;\n }\n\n render() {\n const {\n step,\n emptySymbol,\n fullSymbol,\n placeholderSymbol,\n readonly,\n quiet,\n fractions,\n direction,\n start,\n stop,\n id,\n className,\n style,\n tabIndex\n } = this.props;\n\n function calculateTotalSymbols(start, stop, step) {\n return Math.floor((stop - start) / step);\n }\n\n return (\n \n );\n }\n}\n\nRatingAPILayer.defaultProps = {\n start: 0,\n stop: 5,\n step: 1,\n readonly: false,\n quiet: false,\n fractions: 1,\n direction: 'ltr',\n onHover: noop,\n onClick: noop,\n onChange: noop,\n emptySymbol: Style.empty,\n fullSymbol: Style.full,\n placeholderSymbol: Style.placeholder\n};\n\n// Define propTypes only in development.\nRatingAPILayer.propTypes = typeof __DEV__ !== 'undefined' && __DEV__ && {\n start: PropTypes.number,\n stop: PropTypes.number,\n step: PropTypes.number,\n initialRating: PropTypes.number,\n placeholderRating: PropTypes.number,\n readonly: PropTypes.bool,\n quiet: PropTypes.bool,\n fractions: PropTypes.number,\n direction: PropTypes.string,\n emptySymbol: PropTypes.oneOfType([\n // Array of class names and/or style objects.\n PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.element])),\n // Class names.\n PropTypes.string,\n // Style objects.\n PropTypes.object,\n // React element\n PropTypes.element\n ]),\n fullSymbol: PropTypes.oneOfType([\n // Array of class names and/or style objects.\n PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.element])),\n // Class names.\n PropTypes.string,\n // Style objects.\n PropTypes.object,\n // React element\n PropTypes.element\n ]),\n placeholderSymbol: PropTypes.oneOfType([\n // Array of class names and/or style objects.\n PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.element])),\n // Class names.\n PropTypes.string,\n // Style objects.\n PropTypes.object,\n // React element\n PropTypes.element\n ]),\n onHover: PropTypes.func,\n onClick: PropTypes.func,\n onChange: PropTypes.func\n};\n\nexport default RatingAPILayer;\n"],"names":["style","display","borderRadius","border","width","height","empty","backgroundColor","full","placeholder","_iconNode","icon","React","isValidElement","_typeof","Object","prototype","toString","call","className","RatingSymbol","PureComponent","this","props","index","inactiveIcon","activeIcon","percent","direction","readonly","onClick","onMouseMove","onTouchEnd","backgroundNode","bgIconContainerStyle","visibility","iconNode","iconContainerStyle","position","overflow","top","cursor","handleMouseMove","e","preventDefault","onTouchMove","Rating","state","displayValue","_this","value","interacting","onMouseLeave","bind","symbolMouseMove","symbolClick","symbolEnd","nextProps","valueChanged","setState","prevState","prevProps","onHover","symbolIndex","event","quiet","calculateDisplayValue","percentage","calculateHoverPercentage","fraction","Math","ceil","fractions","precision","floor","totalSymbols","clientX","nativeEvent","type","indexOf","changedTouches","touches","targetRect","target","getBoundingClientRect","delta","right","left","renderedValue","placeholderValue","emptySymbol","fullSymbol","placeholderSymbol","id","tabIndex","symbolNodes","concat","shouldDisplayPlaceholder","fullSymbols","i","push","_Symbol","key","length","noop","_name","RatingAPILayer","initialRating","handleClick","handleHover","newValue","translateDisplayValueToValue","_this2","onChange","undefined","translatedValue","step","start","stop","calculateTotalSymbols","tranlateValueToDisplayValue","placeholderRating","defaultProps","Style"],"mappings":"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"}
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-rating",
3 | "version": "2.0.6",
4 | "description": "A rating react component with custom symbols",
5 | "main": "lib/react-rating.cjs.js",
6 | "module": "lib/react-rating.esm.js",
7 | "typings": "./index.d.ts",
8 | "files": [
9 | "index.d.ts",
10 | "README.md",
11 | "LICENSE.md",
12 | "lib/"
13 | ],
14 | "scripts": {
15 | "test": "mocha --compilers js:@babel/register --recursive",
16 | "lint": "eslint src --ext .js,.jsx",
17 | "dev": "rollup -c -w",
18 | "build": "rollup -c",
19 | "version": "npm run build && git add -A lib",
20 | "postversion": "git push && git push --tags",
21 | "gh-pages": "./scripts/gh-pages.sh"
22 | },
23 | "repository": {
24 | "type": "git",
25 | "url": "https://github.com/dreyescat/react-rating.git"
26 | },
27 | "keywords": [
28 | "rating",
29 | "react",
30 | "component",
31 | "react-component",
32 | "bootstrap",
33 | "fontawesome"
34 | ],
35 | "author": "dreyescat",
36 | "license": "MIT",
37 | "bugs": {
38 | "url": "https://github.com/dreyescat/react-rating/issues"
39 | },
40 | "homepage": "https://github.com/dreyescat/react-rating",
41 | "peerDependencies": {
42 | "react": ">=16.3.0",
43 | "react-dom": ">=16.3.0"
44 | },
45 | "devDependencies": {
46 | "@babel/core": "^7.0.0",
47 | "@babel/preset-env": "^7.0.0",
48 | "@babel/preset-react": "^7.0.0",
49 | "@babel/register": "^7.0.0",
50 | "@babel/standalone": "^7.0.0",
51 | "babel-eslint": "^9.0.0",
52 | "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
53 | "bootstrap": "^3.3.5",
54 | "chai": "^4.1.2",
55 | "cross-env": "^5.2.0",
56 | "eslint": "^5.5.0",
57 | "eslint-plugin-react": "^7.11.1",
58 | "font-awesome": "^4.3.0",
59 | "mocha": "^5.2.0",
60 | "prismjs": "^1.6.0",
61 | "prop-types": "^15.6.0",
62 | "react": "^16.0.0",
63 | "react-dom": "^16.0.0",
64 | "react-test-renderer": "^16.4.2",
65 | "rollup": "^1.10.1",
66 | "rollup-plugin-babel": "^4.3.2",
67 | "rollup-plugin-terser": "^4.0.4"
68 | },
69 | "dependencies": {
70 | "@types/lodash": "^4.14.105",
71 | "@types/react": "^16.0.40"
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import babel from 'rollup-plugin-babel';
2 | import {terser} from 'rollup-plugin-terser';
3 | import pkg from './package.json';
4 |
5 | const umd = pkg.main.replace(/\.cjs\.js$/, '.umd.js');
6 |
7 | export default [
8 | // UMD build
9 | {
10 | input: 'src/react-rating.js',
11 | output: {
12 | file: umd,
13 | format: 'umd',
14 | name: 'ReactRating',
15 | globals: {
16 | react: 'React'
17 | },
18 | sourcemap: true
19 | },
20 | plugins: [
21 | babel({
22 | exclude: 'node_modules/**'
23 | }),
24 | ],
25 | external: [
26 | 'react'
27 | ]
28 | },
29 | // Minified UMD build
30 | {
31 | input: 'src/react-rating.js',
32 | output: {
33 | file: umd.replace(/\.js/, '.min.js'),
34 | format: 'umd',
35 | name: 'ReactRating',
36 | globals: {
37 | react: 'React'
38 | },
39 | sourcemap: true
40 | },
41 | plugins: [
42 | babel({
43 | exclude: 'node_modules/**'
44 | }),
45 | terser()
46 | ],
47 | external: [
48 | 'react'
49 | ]
50 | },
51 | // CommonJS (for Node) and ES module (for bundlers) build.
52 | {
53 | input: 'src/react-rating.js',
54 | output: [
55 | {
56 | file: pkg.main,
57 | format: 'cjs'
58 | },
59 | {
60 | file: pkg.module,
61 | format: 'es'
62 | }
63 | ],
64 | plugins: [
65 | babel({
66 | exclude: 'node_modules/**'
67 | })
68 | ],
69 | external: [
70 | 'react'
71 | ]
72 | }
73 | ];
74 |
--------------------------------------------------------------------------------
/scripts/gh-pages.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Clone remote repository to dist and point to gh-pages branch
4 | # http://stackoverflow.com/questions/1911109/clone-a-specific-git-branch
5 | # Clone gh-pages preventing fetching of all branches add --single-branch
6 | git clone -b gh-pages https://github.com/dreyescat/react-rating.git dist
7 |
8 | # Checkout changes from master
9 | # Using git without having to change directory (-C dist)
10 | # http://stackoverflow.com/questions/5083224/git-pull-while-not-in-a-git-directory
11 | git -C dist checkout origin/master -- :/index.html :/lib :/assets
12 |
13 | # Sync dependencies keeping full path (-R)
14 | rsync -avR node_modules/react/umd dist
15 | rsync -avR node_modules/react-dom/umd dist
16 |
17 | rsync -avR node_modules/bootstrap/dist dist
18 |
19 | rsync -avR node_modules/font-awesome/css dist
20 | rsync -avR node_modules/font-awesome/fonts dist
21 |
22 | rsync -avR node_modules/prismjs/themes/prism.css dist
23 | rsync -avR node_modules/prismjs/prism.js dist
24 | rsync -avR node_modules/prismjs/components/prism-jsx.min.js dist
25 |
26 | rsync -avR node_modules/@babel/standalone/babel.min.js
27 |
28 | # Add synched dependencies changes
29 | git -C dist add .
30 |
31 | # Commit with the last commit message from master (usually release version)
32 | git -C dist commit -m "$(git log origin/master -1 --pretty=%B)"
33 |
34 | # Push to remote gh-pages
35 | git -C dist push origin gh-pages
36 |
37 | # Remove temporary dist folder
38 | rm -rf dist
39 |
--------------------------------------------------------------------------------
/src/Rating.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import Symbol from './RatingSymbol';
4 |
5 | class Rating extends React.PureComponent {
6 | constructor(props) {
7 | super(props);
8 | this.state = {
9 | // Indicates the value that is displayed to the user in the form of symbols.
10 | // It can be either 0 (for no displayed symbols) or (0, end]
11 | displayValue: this.props.value,
12 | // Indicates if the user is currently hovering over the rating element
13 | interacting: false
14 | };
15 | this.onMouseLeave = this.onMouseLeave.bind(this);
16 | this.symbolMouseMove = this.symbolMouseMove.bind(this);
17 | this.symbolClick = this.symbolClick.bind(this);
18 | this.symbolEnd = this.symbolEnd.bind(this);
19 | }
20 |
21 | UNSAFE_componentWillReceiveProps(nextProps) {
22 | const valueChanged = this.props.value !== nextProps.value;
23 | this.setState((prevState) => ({
24 | displayValue: valueChanged ? nextProps.value : prevState.displayValue
25 | }));
26 | }
27 |
28 | // NOTE: This callback is a little bit fragile. Needs some "care" because
29 | // it relies on brittle state kept with different props and state
30 | // combinations to try to figure out from where we are coming, I mean, what
31 | // caused this update.
32 | componentDidUpdate(prevProps, prevState) {
33 | // When hover ends, call this.props.onHover with no value.
34 | if (prevState.interacting && !this.state.interacting) {
35 | return this.props.onHover();
36 | }
37 |
38 | // When hover over.
39 | // Hover in should only be emitted while we are hovering (interacting),
40 | // unless we changed the value, usually originated by an onClick event.
41 | // We do not want to emit a hover in event again on the clicked
42 | // symbol.
43 | if (this.state.interacting && prevProps.value == this.props.value) {
44 | this.props.onHover(this.state.displayValue);
45 | }
46 | }
47 |
48 | symbolEnd(symbolIndex, event) {
49 | // Do not raise the click event on quiet mode when a touch end is received.
50 | // On quiet mode the touch end event only notifies that we have left the
51 | // symbol. We wait for the actual click event to call the symbolClick.
52 | // On not quiet mode we simulate the click event on touch end and we just
53 | // prevent the real on click event to be raised.
54 | // NOTE: I know how we manage click events on touch devices is a little bit
55 | // weird because we do not notify the starting value that was clicked but
56 | // the last (touched) value.
57 | if (!this.props.quiet) {
58 | this.symbolClick(symbolIndex, event);
59 | event.preventDefault();
60 | }
61 | // On touch end we are "leaving" the symbol.
62 | this.onMouseLeave();
63 | }
64 |
65 | symbolClick(symbolIndex, event) {
66 | const value = this.calculateDisplayValue(symbolIndex, event);
67 | this.props.onClick(value, event);
68 | }
69 |
70 | symbolMouseMove(symbolIndex, event) {
71 | const value = this.calculateDisplayValue(symbolIndex, event);
72 | // This call should cause an update only if the state changes.
73 | // Mainly the first time the mouse enters and whenever the value changes.
74 | // So DidComponentUpdate is NOT called for every mouse movement.
75 | this.setState({
76 | interacting: !this.props.readonly,
77 | displayValue: value
78 | });
79 | }
80 |
81 | onMouseLeave() {
82 | this.setState({
83 | displayValue: this.props.value,
84 | interacting: false
85 | });
86 | }
87 |
88 | calculateDisplayValue(symbolIndex, event) {
89 | const percentage = this.calculateHoverPercentage(event);
90 | // Get the closest top fraction.
91 | const fraction = Math.ceil(percentage % 1 * this.props.fractions) / this.props.fractions;
92 | // Truncate decimal trying to avoid float precission issues.
93 | const precision = 10 ** 3;
94 | const displayValue =
95 | symbolIndex + (Math.floor(percentage) + Math.floor(fraction * precision) / precision);
96 | // ensure the returned value is greater than 0 and lower than totalSymbols
97 | return displayValue > 0 ? displayValue > this.props.totalSymbols ? this.props.totalSymbols : displayValue : 1 / this.props.fractions;
98 | }
99 |
100 | calculateHoverPercentage(event) {
101 | const clientX = event.nativeEvent.type.indexOf("touch") > -1
102 | ? event.nativeEvent.type.indexOf("touchend") > -1
103 | ? event.changedTouches[0].clientX
104 | : event.touches[0].clientX
105 | : event.clientX;
106 |
107 | const targetRect = event.target.getBoundingClientRect();
108 | const delta = this.props.direction === 'rtl'
109 | ? targetRect.right - clientX
110 | : clientX - targetRect.left;
111 |
112 | // Returning 0 if the delta is negative solves the flickering issue
113 | return delta < 0 ? 0 : delta / targetRect.width;
114 | }
115 |
116 | render() {
117 | const {
118 | readonly,
119 | quiet,
120 | totalSymbols,
121 | value,
122 | placeholderValue,
123 | direction,
124 | emptySymbol,
125 | fullSymbol,
126 | placeholderSymbol,
127 | className,
128 | id,
129 | style,
130 | tabIndex
131 | } = this.props;
132 | const { displayValue, interacting } = this.state;
133 | const symbolNodes = [];
134 | const empty = [].concat(emptySymbol);
135 | const full = [].concat(fullSymbol);
136 | const placeholder = [].concat(placeholderSymbol);
137 | const shouldDisplayPlaceholder =
138 | placeholderValue !== 0 &&
139 | value === 0 &&
140 | !interacting;
141 |
142 | // The value that will be used as base for calculating how to render the symbols
143 | let renderedValue;
144 | if (shouldDisplayPlaceholder) {
145 | renderedValue = placeholderValue;
146 | } else {
147 | renderedValue = quiet ? value : displayValue;
148 | }
149 |
150 | // The amount of full symbols
151 | const fullSymbols = Math.floor(renderedValue);
152 |
153 | for (let i = 0; i < totalSymbols; i++) {
154 | let percent;
155 | // Calculate each symbol's fullness percentage
156 | if (i - fullSymbols < 0) {
157 | percent = 100;
158 | } else if (i - fullSymbols === 0) {
159 | percent = (renderedValue - i) * 100;
160 | } else {
161 | percent = 0;
162 | }
163 |
164 | symbolNodes.push(
165 |
182 | );
183 | }
184 |
185 | return (
186 |
196 | {symbolNodes}
197 |
198 | );
199 | }
200 | }
201 |
202 | // Define propTypes only in development.
203 | Rating.propTypes = typeof __DEV__ !== 'undefined' && __DEV__ && {
204 | totalSymbols: PropTypes.number.isRequired,
205 | value: PropTypes.number.isRequired, // Always >= 0
206 | placeholderValue: PropTypes.number.isRequired,
207 | readonly: PropTypes.bool.isRequired,
208 | quiet: PropTypes.bool.isRequired,
209 | fractions: PropTypes.number.isRequired,
210 | direction: PropTypes.string.isRequired,
211 | emptySymbol: PropTypes.oneOfType([
212 | // Array of class names and/or style objects.
213 | PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.element])),
214 | // Class names.
215 | PropTypes.string,
216 | // Style objects.
217 | PropTypes.object,
218 | // React element
219 | PropTypes.element
220 | ]).isRequired,
221 | fullSymbol: PropTypes.oneOfType([
222 | // Array of class names and/or style objects.
223 | PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.element])),
224 | // Class names.
225 | PropTypes.string,
226 | // Style objects.
227 | PropTypes.object,
228 | // React element
229 | PropTypes.element
230 | ]).isRequired,
231 | placeholderSymbol: PropTypes.oneOfType([
232 | // Array of class names and/or style objects.
233 | PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.element])),
234 | // Class names.
235 | PropTypes.string,
236 | // Style objects.
237 | PropTypes.object,
238 | // React element
239 | PropTypes.element
240 | ]),
241 | onClick: PropTypes.func.isRequired,
242 | onHover: PropTypes.func.isRequired
243 | };
244 |
245 | export default Rating;
246 |
--------------------------------------------------------------------------------
/src/RatingAPILayer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import Style from './utils/style';
4 | import Rating from './Rating';
5 | import noop from './utils/noop';
6 |
7 | class RatingAPILayer extends React.PureComponent {
8 | constructor(props) {
9 | super(props);
10 | this.state = {
11 | value: props.initialRating
12 | };
13 | this.handleClick = this.handleClick.bind(this);
14 | this.handleHover = this.handleHover.bind(this);
15 | }
16 |
17 | UNSAFE_componentWillReceiveProps(nextProps) {
18 | this.setState({
19 | value: nextProps.initialRating
20 | });
21 | }
22 |
23 | handleClick(value, e) {
24 | const newValue = this.translateDisplayValueToValue(value);
25 | this.props.onClick(newValue);
26 | // Avoid calling setState if not necessary. Micro optimisation.
27 | if (this.state.value !== newValue) {
28 | // If we have a new value trigger onChange callback.
29 | this.setState({
30 | value: newValue
31 | }, () => this.props.onChange(this.state.value));
32 | }
33 | }
34 |
35 | handleHover(displayValue) {
36 | const value = displayValue === undefined
37 | ? displayValue
38 | : this.translateDisplayValueToValue(displayValue);
39 | this.props.onHover(value);
40 | }
41 |
42 | translateDisplayValueToValue(displayValue) {
43 | const translatedValue = displayValue * this.props.step + this.props.start;
44 | // minimum value cannot be equal to start, since it's exclusive
45 | return translatedValue === this.props.start
46 | ? translatedValue + 1 / this.props.fractions
47 | : translatedValue;
48 | }
49 |
50 | tranlateValueToDisplayValue(value) {
51 | if (value === undefined) {
52 | return 0;
53 | }
54 | return (value - this.props.start) / this.props.step;
55 | }
56 |
57 | render() {
58 | const {
59 | step,
60 | emptySymbol,
61 | fullSymbol,
62 | placeholderSymbol,
63 | readonly,
64 | quiet,
65 | fractions,
66 | direction,
67 | start,
68 | stop,
69 | id,
70 | className,
71 | style,
72 | tabIndex
73 | } = this.props;
74 |
75 | function calculateTotalSymbols(start, stop, step) {
76 | return Math.floor((stop - start) / step);
77 | }
78 |
79 | return (
80 |
99 | );
100 | }
101 | }
102 |
103 | RatingAPILayer.defaultProps = {
104 | start: 0,
105 | stop: 5,
106 | step: 1,
107 | readonly: false,
108 | quiet: false,
109 | fractions: 1,
110 | direction: 'ltr',
111 | onHover: noop,
112 | onClick: noop,
113 | onChange: noop,
114 | emptySymbol: Style.empty,
115 | fullSymbol: Style.full,
116 | placeholderSymbol: Style.placeholder
117 | };
118 |
119 | // Define propTypes only in development.
120 | RatingAPILayer.propTypes = typeof __DEV__ !== 'undefined' && __DEV__ && {
121 | start: PropTypes.number,
122 | stop: PropTypes.number,
123 | step: PropTypes.number,
124 | initialRating: PropTypes.number,
125 | placeholderRating: PropTypes.number,
126 | readonly: PropTypes.bool,
127 | quiet: PropTypes.bool,
128 | fractions: PropTypes.number,
129 | direction: PropTypes.string,
130 | emptySymbol: PropTypes.oneOfType([
131 | // Array of class names and/or style objects.
132 | PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.element])),
133 | // Class names.
134 | PropTypes.string,
135 | // Style objects.
136 | PropTypes.object,
137 | // React element
138 | PropTypes.element
139 | ]),
140 | fullSymbol: PropTypes.oneOfType([
141 | // Array of class names and/or style objects.
142 | PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.element])),
143 | // Class names.
144 | PropTypes.string,
145 | // Style objects.
146 | PropTypes.object,
147 | // React element
148 | PropTypes.element
149 | ]),
150 | placeholderSymbol: PropTypes.oneOfType([
151 | // Array of class names and/or style objects.
152 | PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.element])),
153 | // Class names.
154 | PropTypes.string,
155 | // Style objects.
156 | PropTypes.object,
157 | // React element
158 | PropTypes.element
159 | ]),
160 | onHover: PropTypes.func,
161 | onClick: PropTypes.func,
162 | onChange: PropTypes.func
163 | };
164 |
165 | export default RatingAPILayer;
166 |
--------------------------------------------------------------------------------
/src/RatingSymbol.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | // Return the corresponding React node for an icon.
5 | const _iconNode = (icon) => {
6 | // If it is already a React Element just return it.
7 | if (React.isValidElement(icon)) {
8 | return icon;
9 | }
10 | // If it is an object, try to use it as a CSS style object.
11 | if (typeof icon === 'object' && icon !== null) {
12 | return ;
13 | }
14 | // If it is a string, use it as class names.
15 | if (Object.prototype.toString.call(icon) === '[object String]') {
16 | return ;
17 | }
18 | };
19 |
20 | class RatingSymbol extends React.PureComponent {
21 | render() {
22 | const {
23 | index,
24 | inactiveIcon,
25 | activeIcon,
26 | percent,
27 | direction,
28 | readonly,
29 | onClick,
30 | onMouseMove,
31 | onTouchEnd
32 | } = this.props;
33 | const backgroundNode = _iconNode(inactiveIcon);
34 | const showbgIcon = percent < 100;
35 | const bgIconContainerStyle = showbgIcon
36 | ? {}
37 | : {
38 | visibility: 'hidden'
39 | };
40 | const iconNode = _iconNode(activeIcon);
41 | const iconContainerStyle = {
42 | display: 'inline-block',
43 | position: 'absolute',
44 | overflow: 'hidden',
45 | top: 0,
46 | [direction === 'rtl' ? 'right' : 'left']: 0,
47 | width: `${percent}%`
48 | };
49 | const style = {
50 | cursor: !readonly ? 'pointer' : 'inherit',
51 | display: 'inline-block',
52 | position: 'relative'
53 | };
54 |
55 | function handleMouseMove(e) {
56 | if (onMouseMove) {
57 | onMouseMove(index, e);
58 | }
59 | }
60 |
61 | function handleMouseClick(e) {
62 | if (onClick) {
63 | // [Supporting both TouchEvent and MouseEvent](https://developer.mozilla.org/en-US/docs/Web/API/Touch_events/Supporting_both_TouchEvent_and_MouseEvent)
64 | // We must prevent firing click event twice on touch devices.
65 | e.preventDefault();
66 | onClick(index, e);
67 | }
68 | }
69 |
70 | function handleTouchEnd(e) {
71 | if (onTouchEnd) {
72 | onTouchEnd(index, e);
73 | }
74 | }
75 |
76 | return (
77 |
84 |
85 | {backgroundNode}
86 |
87 |
88 | {iconNode}
89 |
90 |
91 | );
92 | }
93 | }
94 |
95 | // Define propTypes only in development. They will be void in production.
96 | RatingSymbol.propTypes = typeof __DEV__ !== 'undefined' && __DEV__ && {
97 | index: PropTypes.number.isRequired,
98 | readonly: PropTypes.bool.isRequired,
99 | inactiveIcon: PropTypes.oneOfType([
100 | PropTypes.string,
101 | PropTypes.object,
102 | PropTypes.element
103 | ]).isRequired,
104 | activeIcon: PropTypes.oneOfType([
105 | PropTypes.string,
106 | PropTypes.object,
107 | PropTypes.element
108 | ]).isRequired,
109 | percent: PropTypes.number.isRequired,
110 | direction: PropTypes.string.isRequired,
111 | onClick: PropTypes.func,
112 | onMouseMove: PropTypes.func,
113 | onTouchMove: PropTypes.func,
114 | onTouchEnd: PropTypes.func
115 | };
116 |
117 | export default RatingSymbol;
118 |
--------------------------------------------------------------------------------
/src/react-rating.js:
--------------------------------------------------------------------------------
1 | import RatingAPILayer from './RatingAPILayer';
2 | export default RatingAPILayer;
3 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dreyescat/react-rating/bd070757452e1aa75907f2a156fda2119fb7121a/src/utils.js
--------------------------------------------------------------------------------
/src/utils/noop.js:
--------------------------------------------------------------------------------
1 | function noop() {}
2 | noop._name = 'react_rating_noop';
3 |
4 | export default noop;
5 |
--------------------------------------------------------------------------------
/src/utils/style.js:
--------------------------------------------------------------------------------
1 | var style = {
2 | display: 'inline-block',
3 | borderRadius: '50%',
4 | border: '5px double white',
5 | width: 30,
6 | height: 30
7 | };
8 |
9 | export default {
10 | empty: {
11 | ...style,
12 | backgroundColor: '#ccc'
13 | },
14 | full: {
15 | ...style,
16 | backgroundColor: 'black'
17 | },
18 | placeholder: {
19 | ...style,
20 | backgroundColor: 'red'
21 | }
22 | };
23 |
--------------------------------------------------------------------------------
/test/Rating-test.js:
--------------------------------------------------------------------------------
1 | // Use expect BDD assertion style.
2 | var expect = require('chai').expect;
3 | var React = require('react');
4 | var TestUtils = require('react-dom/test-utils');
5 | var createRenderer = require('react-test-renderer/shallow').createRenderer;
6 | import Rating from '../src/Rating';
7 | var Style = require('../src/utils/style.js');
8 |
9 | var render = function (component) {
10 | var renderer = createRenderer();
11 | renderer.render(component);
12 | return renderer.getRenderOutput()
13 | };
14 |
15 | describe('Rating', function () {
16 | describe('with total symbols of 5', function () {
17 | var rating;
18 |
19 | beforeEach(function () {
20 | rating = render(
21 |
22 | );
23 | });
24 |
25 | it('should render a 5 symbol rating', function () {
26 | var Symbol = require('../src/RatingSymbol');
27 | var children = rating.props.children;
28 | var symbols = children.filter(function (child) {
29 | return TestUtils.isElementOfType(child, Symbol);
30 | });
31 | expect(children).to.have.same.length(symbols.length).which.is.length(5);
32 | });
33 |
34 | it('should have all symbols empty', function () {
35 | rating.props.children.forEach(function (symbol) {
36 | expect(symbol.props.percent).to.be.equal(0);
37 | });
38 | });
39 | });
40 |
41 | describe('with an initial rate of 3', function () {
42 | var rating;
43 |
44 | beforeEach(function () {
45 | rating = render( );
46 | });
47 |
48 | it('should render a 5 symbol rating with the 3 first symbols full', function () {
49 | rating.props.children.forEach(function (symbol, i) {
50 | expect(symbol.props.percent).to.be.equal(i < 3 ? 100 : 0);
51 | });
52 | });
53 | });
54 |
55 | describe('that is readonly', function () {
56 | var rating;
57 |
58 | beforeEach(function () {
59 | rating = render( );
60 | });
61 |
62 | it('should have all symbols readonly', function () {
63 | rating.props.children.forEach(function (symbol, i) {
64 | expect(symbol.props.readonly).to.be.false;
65 | });
66 | });
67 | it('should not have mouse move handler', function () {
68 | var noop = require('../src/utils/noop');
69 | rating.props.children.forEach(function (symbol, i) {
70 | expect(symbol.props.onMouseMove).to.equal(noop);
71 | });
72 | });
73 |
74 | it('should not have click handler', function () {
75 | var noop = require('../src/utils/noop');
76 | rating.props.children.forEach(function (symbol, i) {
77 | expect(symbol.props.onClick).to.equal(noop);
78 | });
79 | });
80 |
81 | it('should not have mouse leave handler', function () {
82 | var noop = require('../src/utils/noop');
83 | expect(rating.props.onMouseLeave).to.equal(noop);
84 | });
85 | });
86 |
87 | /////////////////////////////////////////////////////////////////////////////
88 | // Custom symbol style
89 | /////////////////////////////////////////////////////////////////////////////
90 | describe('with custom class name style', function () {
91 | var rating,
92 | empty = 'fa fa-star-o fa-2x',
93 | full = 'fa fa-star fa-2x';
94 |
95 | beforeEach(function () {
96 | rating = render( );
97 | });
98 |
99 | it('should render all symbols with custom style', function () {
100 | rating.props.children.forEach(function (symbol) {
101 | expect(symbol.props.icon).to.be.equal(full);
102 | expect(symbol.props.background).to.be.equal(empty);
103 | });
104 | });
105 | });
106 |
107 | describe('with custom inline style', function () {
108 | var rating;
109 |
110 | beforeEach(function () {
111 | rating = render( );
112 | });
113 |
114 | it('should render all symbols with custom style', function () {
115 | rating.props.children.forEach(function (symbol) {
116 | expect(symbol.props.icon).to.be.equal(Style.full);
117 | expect(symbol.props.background).to.be.equal(Style.empty);
118 | });
119 | });
120 | });
121 |
122 | describe('with custom list of class name styles', function () {
123 | var rating,
124 | empty = ['fa fa-star-o fa-2x', 'fa fa-heart-o fa-2x'],
125 | full = ['fa fa-star fa-2x', 'fa fa-heart-o fa-2x'];
126 |
127 | beforeEach(function () {
128 | rating = render( );
129 | });
130 |
131 | it('should render symbols with circular custom style', function () {
132 | rating.props.children.forEach(function (symbol, i) {
133 | expect(symbol.props.icon).to.be.equal(full[i % 2]);
134 | expect(symbol.props.background).to.be.equal(empty[i % 2]);
135 | });
136 | });
137 | });
138 | });
139 |
--------------------------------------------------------------------------------
/test/RatingContainer-test.js:
--------------------------------------------------------------------------------
1 | // Use expect BDD assertion style.
2 | var expect = require('chai').expect;
3 | var React = require('react');
4 | var TestUtils = require('react-dom/test-utils');
5 | var createRenderer = require('react-test-renderer/shallow').createRenderer;
6 | import RatingContainer from '../src/RatingAPILayer';
7 |
8 | var render = function (component) {
9 | var renderer = createRenderer();
10 | renderer.render(component);
11 | return renderer.getRenderOutput()
12 | };
13 |
14 | describe('RatingContainer', function () {
15 | describe('with a stop range of 10', function () {
16 | var rating;
17 |
18 | beforeEach(function () {
19 | rating = render( );
20 | });
21 |
22 | it('should render a 10 symbol rating', function () {
23 | expect(rating.props.totalSymbols).to.be.equal(10);
24 | });
25 | });
26 |
27 | describe('with a range (5, 10]', function () {
28 | var rating;
29 |
30 | beforeEach(function () {
31 | rating = render( );
32 | });
33 |
34 | it('should render a 5 symbol rating', function () {
35 | expect(rating.props.totalSymbols).to.be.equal(5);
36 | });
37 | });
38 |
39 | describe('with a range (0, 0]', function () {
40 | var rating;
41 |
42 | beforeEach(function () {
43 | rating = render( );
44 | });
45 |
46 | it('should render a 0 symbol rating', function () {
47 | expect(rating.props.totalSymbols).to.be.equal(0);
48 | });
49 | });
50 |
51 | describe('with a range (0, 10] step 2', function () {
52 | var rating;
53 |
54 | beforeEach(function () {
55 | rating = render( );
56 | });
57 |
58 | it('should render a 5 symbol rating', function () {
59 | expect(rating.props.totalSymbols).to.be.equal(5);
60 | });
61 | });
62 |
63 | describe('with a range (10, 0] step -2', function () {
64 | var rating;
65 |
66 | beforeEach(function () {
67 | rating = render( );
68 | });
69 |
70 | it('should render a 5 symbol rating', function () {
71 | expect(rating.props.totalSymbols).to.be.equal(5);
72 | });
73 | });
74 | });
75 |
--------------------------------------------------------------------------------
/test/RatingSymbol-test.js:
--------------------------------------------------------------------------------
1 | // Use expect BDD assertion style.
2 | var expect = require('chai').expect;
3 | var React = require('react');
4 | var TestUtils = require('react-dom/test-utils');
5 | var createRenderer = require('react-test-renderer/shallow').createRenderer;
6 | import RatingSymbol from '../src/RatingSymbol';
7 |
8 | var render = function (component) {
9 | var renderer = createRenderer();
10 | renderer.render(component);
11 | return renderer.getRenderOutput()
12 | };
13 |
14 | describe('RatingSymbol', function () {
15 | describe('with inline object icon and background', function () {
16 | var symbol,
17 | Style = require('../src/utils/style.js'),
18 | icon = Style.full,
19 | background= Style.empty;
20 |
21 | beforeEach(function () {
22 | symbol = render(
23 |
24 | );
25 | });
26 |
27 | it('should have inline styled background', function () {
28 | var backgroundNode = symbol.props.children[0];
29 | expect(backgroundNode.props.style).to.be.equal(background);
30 | });
31 |
32 | it('should have inline styled foreground', function () {
33 | var iconNode = symbol.props.children[1].props.children;
34 | expect(iconNode.props.style).to.be.equal(icon);
35 | });
36 |
37 | it('should show pointer cursor', function () {
38 | expect(symbol.props.style.cursor).to.be.equal('pointer');
39 | });
40 | });
41 |
42 | describe('with class name icon and background', function () {
43 | var symbol,
44 | icon = 'fa fa-star fa-2x',
45 | background = 'fa fa-star-o fa-2x';
46 |
47 | beforeEach(function () {
48 | symbol = render( );
49 | });
50 |
51 | it('should have class styled background', function () {
52 | var backgroundNode = symbol.props.children[0];
53 | expect(backgroundNode.props.className).to.contain(background);
54 | });
55 |
56 | it('should have class styled foreground', function () {
57 | var iconNode = symbol.props.children[1].props.children;
58 | expect(iconNode.props.className).to.contain(icon);
59 | });
60 | });
61 |
62 | describe('with React element icon and background', function () {
63 | var symbol;
64 |
65 | beforeEach(function () {
66 | symbol = render(+} background={- } />);
67 | });
68 |
69 | it('should have a React element background', function () {
70 | var backgroundNode = symbol.props.children[0];
71 | expect(TestUtils.isElement(backgroundNode));
72 | });
73 |
74 | it('should have a React element foreground', function () {
75 | var foregroundNode = symbol.props.children[0];
76 | expect(TestUtils.isElement(foregroundNode));
77 | });
78 | });
79 |
80 | describe('with 25 percent icon', function () {
81 | var symbol,
82 | Style = require('../src/utils/style.js'),
83 | icon = Style.full,
84 | background= Style.empty;
85 |
86 | beforeEach(function () {
87 | symbol = render( );
91 | });
92 |
93 | it('should show 25% icon width', function () {
94 | var iconContainerNode = symbol.props.children[1];
95 | expect(iconContainerNode.props.style.width).to.be.equal('25%');
96 | });
97 | });
98 |
99 | describe('readonly', function () {
100 | var symbol,
101 | Style = require('../src/utils/style.js'),
102 | icon = Style.full,
103 | background= Style.empty;
104 |
105 | beforeEach(function () {
106 | symbol = render(
107 |
108 | );
109 | });
110 |
111 | it('should show auto cursor', function () {
112 | expect(symbol.props.style.cursor).to.be.equal('auto');
113 | });
114 | });
115 |
116 | });
117 |
--------------------------------------------------------------------------------