34 |
react-stars examples
35 |
Star rating component for your React projects
36 | Custom size, preset value, not editable
37 |
38 | Custom character, custom colors, 10 stars
39 |
40 | Editable, preset value, half stars off
41 |
42 |
Github |
43 |
NPM package
44 |
,
45 | document.getElementById('root')
46 | )
47 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-stars",
3 | "version": "2.2.3",
4 | "description": "Simple star rating component for your React projects",
5 | "main": "dist/react-stars.js",
6 | "repository": "https://github.com/n49/react-stars.git",
7 | "babel": {
8 | "presets": [
9 | "react",
10 | "es2015"
11 | ]
12 | },
13 | "ava": {
14 | "babel": {
15 | "presets": [
16 | "es2015",
17 | "react"
18 | ]
19 | },
20 | "require": [
21 | "babel-register"
22 | ]
23 | },
24 | "scripts": {
25 | "build": "babel src --out-dir dist",
26 | "dev": "babel src --out-dir dist --watch",
27 | "build-example": "webpack -p --config=webpack.production.config.js",
28 | "dev-example": "webpack-dev-server . --hot --inline"
29 | },
30 | "keywords": [
31 | "star",
32 | "rating",
33 | "react",
34 | "star",
35 | "rating",
36 | "component",
37 | "raty"
38 | ],
39 | "author": "Oleg Berman",
40 | "license": "ISC",
41 | "devDependencies": {
42 | "ava": "^0.14.0",
43 | "babel-cli": "^6.6.5",
44 | "babel-loader": "^6.2.4",
45 | "babel-preset-es2015": "^6.6.0",
46 | "babel-preset-react": "^6.5.0",
47 | "prop-types": "^15.5.10",
48 | "react-addons-test-utils": "^15.0.1",
49 | "react-dom": "^15.4.1",
50 | "react": "^15.4.1",
51 | "webpack": "^1.12.15",
52 | "webpack-dev-server": "^1.14.1",
53 | "babel-plugin-transform-object-assign": "^6.8.0"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/react-stars.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | const parentStyles = {
5 | overflow: 'hidden',
6 | position: 'relative'
7 | }
8 |
9 | const defaultStyles = {
10 | position: 'relative',
11 | overflow: 'hidden',
12 | cursor: 'pointer',
13 | display: 'block',
14 | float: 'left'
15 | }
16 |
17 | const getHalfStarStyles = (color, uniqueness) => {
18 | return `
19 | .react-stars-${uniqueness}:before {
20 | position: absolute;
21 | overflow: hidden;
22 | display: block;
23 | z-index: 1;
24 | top: 0; left: 0;
25 | width: 50%;
26 | content: attr(data-forhalf);
27 | color: ${color};
28 | }`
29 | }
30 |
31 | class ReactStars extends Component {
32 |
33 | constructor(props) {
34 |
35 | super(props)
36 |
37 | // set defaults
38 |
39 | props = Object.assign({}, props)
40 |
41 | this.state = {
42 | uniqueness: (Math.random() + '').replace('.', ''),
43 | value: props.value || 0,
44 | stars: [],
45 | halfStar: {
46 | at: Math.floor(props.value),
47 | hidden: props.half && props.value % 1 < 0.5
48 | }
49 | }
50 |
51 | this.state.config = {
52 | count: props.count,
53 | size: props.size,
54 | char: props.char,
55 | // default color of inactive star
56 | color1: props.color1,
57 | // color of an active star
58 | color2: props.color2,
59 | half: props.half,
60 | edit: props.edit,
61 | }
62 |
63 | }
64 |
65 | componentDidMount() {
66 | this.setState({
67 | stars: this.getStars(this.state.value)
68 | })
69 | }
70 |
71 | componentWillReceiveProps(props) {
72 | this.setState({
73 | stars: this.getStars(props.value),
74 | value: props.value,
75 | halfStar: {
76 | at: Math.floor(props.value),
77 | hidden: this.state.config.half && props.value % 1 < 0.5
78 | },
79 | config: Object.assign({}, this.state.config, {
80 | count: props.count,
81 | size: props.size,
82 | char: props.char,
83 | color1: props.color1,
84 | color2: props.color2,
85 | half: props.half,
86 | edit: props.edit
87 | })
88 | })
89 | }
90 |
91 | isDecimal(value) {
92 | return value % 1 !== 0
93 | }
94 |
95 | getRate() {
96 | let stars
97 | if (this.state.config.half) {
98 | stars = Math.floor(this.state.value)
99 | } else {
100 | stars = Math.round(this.state.value)
101 | }
102 | return stars
103 | }
104 |
105 | getStars(activeCount) {
106 | if (typeof activeCount === 'undefined') {
107 | activeCount = this.getRate()
108 | }
109 | let stars = []
110 | for (let i = 0; i < this.state.config.count; i++) {
111 | stars.push({
112 | active: i <= activeCount - 1
113 | })
114 | }
115 | return stars
116 | }
117 |
118 | mouseOver(event) {
119 | let { config, halfStar } = this.state
120 | if (!config.edit) return;
121 | let index = Number(event.target.getAttribute('data-index'))
122 | if (config.half) {
123 | const isAtHalf = this.moreThanHalf(event, config.size)
124 | halfStar.hidden = isAtHalf
125 | if (isAtHalf) index = index + 1
126 | halfStar.at = index
127 | } else {
128 | index = index + 1
129 | }
130 | this.setState({
131 | stars: this.getStars(index)
132 | })
133 | }
134 |
135 | moreThanHalf(event, size) {
136 | let { target } = event
137 | var mouseAt = event.clientX - target.getBoundingClientRect().left
138 | mouseAt = Math.round(Math.abs(mouseAt))
139 | return mouseAt > size / 2
140 | }
141 |
142 | mouseLeave() {
143 | const { value, halfStar, config } = this.state
144 | if (!config.edit) return
145 | if (config.half) {
146 | halfStar.hidden = !this.isDecimal(value)
147 | halfStar.at = Math.floor(this.state.value)
148 | }
149 | this.setState({
150 | stars: this.getStars()
151 | })
152 | }
153 |
154 | clicked(event) {
155 | const { config, halfStar } = this.state
156 | if (!config.edit) return
157 | let index = Number(event.target.getAttribute('data-index'))
158 | let value
159 | if (config.half) {
160 | const isAtHalf = this.moreThanHalf(event, config.size)
161 | halfStar.hidden = isAtHalf
162 | if (isAtHalf) index = index + 1
163 | value = isAtHalf ? index : index + .5
164 | halfStar.at = index
165 | } else {
166 | value = index = index + 1
167 | }
168 | this.setState({
169 | value: value,
170 | stars: this.getStars(index)
171 | })
172 | this.props.onChange(value)
173 | }
174 |
175 | renderHalfStarStyleElement() {
176 | const { config, uniqueness } = this.state
177 | return (
178 |
181 | )
182 | }
183 |
184 | renderStars() {
185 | const { halfStar, stars, uniqueness, config } = this.state
186 | const { color1, color2, size, char, half, edit } = config
187 | return stars.map((star, i) => {
188 | let starClass = ''
189 | if (half && !halfStar.hidden && halfStar.at === i) {
190 | starClass = `react-stars-${uniqueness}`
191 | }
192 | const style = Object.assign({}, defaultStyles, {
193 | color: star.active ? color2 : color1,
194 | cursor: edit ? 'pointer' : 'default',
195 | fontSize: `${size}px`
196 | })
197 | return (
198 |
222 | {this.state.config.half ?
223 | this.renderHalfStarStyleElement() : ''}
224 | {this.renderStars()}
225 |
226 | )
227 | }
228 |
229 | }
230 |
231 | ReactStars.propTypes = {
232 | className: PropTypes.string,
233 | edit: PropTypes.bool,
234 | half: PropTypes.bool,
235 | value: PropTypes.number,
236 | count: PropTypes.number,
237 | char: PropTypes.string,
238 | size: PropTypes.number,
239 | color1: PropTypes.string,
240 | color2: PropTypes.string
241 | }
242 |
243 | ReactStars.defaultProps = {
244 | edit: true,
245 | half: true,
246 | value: 0,
247 | count: 5,
248 | char: '★',
249 | size: 15,
250 | color1: 'gray',
251 | color2: '#ffd700',
252 |
253 | onChange: () => { }
254 | };
255 |
256 | export default ReactStars
257 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 |
5 | entry: ['./example/index.js'],
6 |
7 | debug: true,
8 |
9 | devtool: 'inline-source-map',
10 |
11 | output: {
12 | filename: './example/bundle.js',
13 | pathinfo: true
14 | },
15 |
16 | module: {
17 | loaders: [{
18 | loader: 'babel-loader',
19 | test: /\.js$/,
20 | exclude: /node_modules/,
21 | query: {
22 | cacheDirectory: true,
23 | presets: ['react', 'es2015']
24 | }
25 | }]
26 | }
27 |
28 | };
29 |
--------------------------------------------------------------------------------
/webpack.production.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var webpack = require('webpack')
4 |
5 | module.exports = {
6 |
7 | entry: ['./example/index.js'],
8 |
9 | debug: false,
10 |
11 | output: {
12 | filename: './example/bundle.js',
13 | pathinfo: true
14 | },
15 |
16 | module: {
17 | loaders: [{
18 | loader: 'babel-loader',
19 | test: /\.js$/,
20 | exclude: /node_modules/,
21 | query: {
22 | cacheDirectory: true,
23 | presets: ['react', 'es2015']
24 | }
25 | }]
26 | },
27 |
28 | plugins: [
29 | new webpack.DefinePlugin({
30 | 'process.env': {
31 | 'NODE_ENV': JSON.stringify('production')
32 | }
33 | }),
34 | new webpack.optimize.UglifyJsPlugin({minimize: true})
35 | ]
36 |
37 | };
38 |
--------------------------------------------------------------------------------