1 | import _extends from 'babel-runtime/helpers/extends';
|
2 | import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
|
3 | import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn';
|
4 | import _inherits from 'babel-runtime/helpers/inherits';
|
5 |
|
6 | var _class, _temp;
|
7 |
|
8 | import React, { Component } from 'react';
|
9 | import PropTypes from 'prop-types';
|
10 | import classNames from 'classnames';
|
11 | import { polyfill } from 'react-lifecycles-compat';
|
12 |
|
13 | import Icon from '../icon';
|
14 | import { func, KEYCODE, obj } from '../util';
|
15 | import zhCN from '../locale/zh-cn';
|
16 |
|
17 | var noop = func.noop,
|
18 | bindCtx = func.bindCtx;
|
19 | var ENTER = KEYCODE.ENTER,
|
20 | LEFT = KEYCODE.LEFT,
|
21 | UP = KEYCODE.UP,
|
22 | RIGHT = KEYCODE.RIGHT,
|
23 | DOWN = KEYCODE.DOWN;
|
24 |
|
25 | var supportKeys = [ENTER, LEFT, UP, RIGHT, DOWN];
|
26 |
|
27 |
|
28 | var ICON_SIZE_MAP = {
|
29 | small: 'xs',
|
30 | medium: 'small',
|
31 | large: 'medium'
|
32 | };
|
33 |
|
34 |
|
35 | var Rating = (_temp = _class = function (_Component) {
|
36 | _inherits(Rating, _Component);
|
37 |
|
38 | Rating.currentValue = function currentValue(min, max, hoverValue, stateValue) {
|
39 | var value = hoverValue ? hoverValue : stateValue;
|
40 |
|
41 | value = value >= max ? max : value;
|
42 | value = value <= min ? min : value;
|
43 |
|
44 | return value || 0;
|
45 | };
|
46 |
|
47 | function Rating(props) {
|
48 | _classCallCheck(this, Rating);
|
49 |
|
50 | var _this = _possibleConstructorReturn(this, _Component.call(this, props));
|
51 |
|
52 | _this.saveRef = function (ref, i) {
|
53 | _this['refs-rating-icon-' + i] = ref;
|
54 | };
|
55 |
|
56 | _this.state = {
|
57 | value: 'value' in props ? props.value : props.defaultValue,
|
58 | hoverValue: 0,
|
59 | cleanedValue: null,
|
60 | iconSpace: 0,
|
61 | iconSize: 0,
|
62 | clicked: false
|
63 | };
|
64 | _this.timer = null;
|
65 |
|
66 | bindCtx(_this, ['handleClick', 'handleHover', 'handleLeave', 'onKeyDown']);
|
67 | return _this;
|
68 | }
|
69 |
|
70 | Rating.getDerivedStateFromProps = function getDerivedStateFromProps(nextProps, prevState) {
|
71 | var state = {};
|
72 | if ('value' in nextProps) {
|
73 | state.value = nextProps.value || 0;
|
74 | }
|
75 |
|
76 | if ('disabled' in nextProps || 'readOnly' in nextProps || 'isPreview' in nextProps || 'renderPreview' in nextProps) {
|
77 | state.disabled = nextProps.disabled || nextProps.readOnly || nextProps.isPreview && !('renderPreview' in nextProps);
|
78 | }
|
79 |
|
80 | return state;
|
81 | };
|
82 |
|
83 | Rating.prototype.componentDidMount = function componentDidMount() {
|
84 | this.getRenderResult();
|
85 | };
|
86 |
|
87 | Rating.prototype.componentWillUnmount = function componentWillUnmount() {
|
88 | this.clearTimer();
|
89 | };
|
90 |
|
91 |
|
92 |
|
93 |
|
94 | Rating.prototype.clearTimer = function clearTimer() {
|
95 | if (this.timer) {
|
96 | clearTimeout(this.timer);
|
97 | this.timer = null;
|
98 | }
|
99 | };
|
100 |
|
101 | Rating.prototype.getRenderResult = function getRenderResult() {
|
102 | var count = this.props.count;
|
103 | var _state = this.state,
|
104 | iconSpace = _state.iconSpace,
|
105 | iconSize = _state.iconSize;
|
106 |
|
107 | var icon = this['refs-rating-icon-0'];
|
108 |
|
109 | if (icon && this.underlayNode) {
|
110 | var newIconSize = icon.offsetWidth;
|
111 | var newIconSpace = (this.underlayNode.offsetWidth - count * newIconSize) / (count + 1);
|
112 |
|
113 | if (newIconSize !== iconSize || newIconSpace !== iconSpace) {
|
114 | this.setState({
|
115 | iconSpace: newIconSpace,
|
116 | iconSize: newIconSize
|
117 | });
|
118 | }
|
119 | }
|
120 | };
|
121 |
|
122 | Rating.prototype.getValue = function getValue(e) {
|
123 |
|
124 | this.getRenderResult();
|
125 |
|
126 | var _props = this.props,
|
127 | allowHalf = _props.allowHalf,
|
128 | count = _props.count,
|
129 | rtl = _props.rtl;
|
130 | var _state2 = this.state,
|
131 | iconSpace = _state2.iconSpace,
|
132 | iconSize = _state2.iconSize;
|
133 |
|
134 |
|
135 | var pos = e.pageX - this.underlayNode.getBoundingClientRect().left;
|
136 | var fullNum = Math.floor(pos / (iconSpace + iconSize));
|
137 | var surplusNum = (pos - fullNum * (iconSpace + iconSize) - iconSpace) / iconSize;
|
138 | var value = Number(fullNum) + Number(surplusNum.toFixed(1));
|
139 | if (value >= count) {
|
140 | value = count;
|
141 | } else if (allowHalf) {
|
142 | var floorValue = Math.floor(value);
|
143 | if (rtl) {
|
144 | value = value - 0.5 >= floorValue ? floorValue + 1.5 : floorValue + 1;
|
145 | } else {
|
146 | value = value - 0.5 >= floorValue ? floorValue + 1 : floorValue + 0.5;
|
147 | }
|
148 | } else {
|
149 | value = Math.floor(value) + 1;
|
150 | }
|
151 |
|
152 | return rtl ? count - value + 1 : value;
|
153 | };
|
154 |
|
155 | Rating.prototype.handleHover = function handleHover(e) {
|
156 | var _this2 = this;
|
157 |
|
158 | if (this.state.disabled) {
|
159 | return;
|
160 | }
|
161 |
|
162 | var value = this.getValue(e);
|
163 | var onHoverChange = this.props.onHoverChange;
|
164 | var cleanedValue = this.state.cleanedValue;
|
165 |
|
166 | if (cleanedValue !== value) {
|
167 | this.clearTimer();
|
168 |
|
169 | this.timer = setTimeout(function () {
|
170 | _this2.setState({ hoverValue: value, cleanedValue: null }, function () {
|
171 | onHoverChange(value);
|
172 | });
|
173 | }, 0);
|
174 | }
|
175 | };
|
176 |
|
177 | Rating.prototype.handleLeave = function handleLeave() {
|
178 | var onHoverChange = this.props.onHoverChange;
|
179 |
|
180 | if (this.state.disabled) {
|
181 | return;
|
182 | }
|
183 |
|
184 | this.clearTimer();
|
185 |
|
186 | this.setState({
|
187 | hoverValue: 0,
|
188 | cleanedValue: null
|
189 | });
|
190 | onHoverChange(undefined);
|
191 | };
|
192 |
|
193 | Rating.prototype.onKeyDown = function onKeyDown(e) {
|
194 | if (this.state.disabled) {
|
195 | return;
|
196 | }
|
197 |
|
198 | var _props2 = this.props,
|
199 | onKeyDown = _props2.onKeyDown,
|
200 | count = _props2.count;
|
201 | var disabled = this.state.disabled;
|
202 |
|
203 | if (disabled || supportKeys.indexOf(e.keyCode) < 0) {
|
204 | return !onKeyDown || onKeyDown(e);
|
205 | }
|
206 |
|
207 | var _state3 = this.state,
|
208 | hoverValue = _state3.hoverValue,
|
209 | value = _state3.value;
|
210 |
|
211 | var changingValue = hoverValue;
|
212 | if (changingValue === 0) {
|
213 | changingValue = value;
|
214 | }
|
215 |
|
216 | switch (e.keyCode) {
|
217 | case DOWN:
|
218 | case RIGHT:
|
219 | if (changingValue < count) {
|
220 | changingValue += 1;
|
221 | } else {
|
222 | changingValue = 1;
|
223 | }
|
224 | this.handleChecked(changingValue);
|
225 | break;
|
226 | case UP:
|
227 | case LEFT:
|
228 | if (changingValue > 1) {
|
229 | changingValue -= 1;
|
230 | } else {
|
231 | changingValue = count;
|
232 | }
|
233 | this.handleChecked(changingValue);
|
234 | break;
|
235 | case ENTER:
|
236 | this.props.onChange(changingValue);
|
237 | this.setState({
|
238 | value: changingValue,
|
239 | hoverValue: changingValue
|
240 | });
|
241 | break;
|
242 | }
|
243 | return !onKeyDown || onKeyDown(e);
|
244 | };
|
245 |
|
246 | Rating.prototype.handleChecked = function handleChecked(index) {
|
247 | if (this.state.disabled) {
|
248 | return;
|
249 | }
|
250 |
|
251 | this.setState({ hoverValue: index });
|
252 | };
|
253 |
|
254 | Rating.prototype.handleClick = function handleClick(e) {
|
255 | var _this3 = this;
|
256 |
|
257 | if (this.state.disabled) {
|
258 | return;
|
259 | }
|
260 | var allowClear = this.props.allowClear;
|
261 | var value = this.state.value;
|
262 |
|
263 | var newValue = this.getValue(e);
|
264 | var isReset = false;
|
265 | if (allowClear) {
|
266 | isReset = newValue === value;
|
267 | }
|
268 | this.handleLeave();
|
269 | if (newValue < 0) {
|
270 | return;
|
271 | }
|
272 |
|
273 | if (!('value' in this.props)) {
|
274 | this.setState({ value: isReset ? 0 : newValue, clicked: true });
|
275 | }
|
276 |
|
277 | this.props.onChange(isReset ? 0 : newValue);
|
278 | setTimeout(function () {
|
279 | _this3.setState({ clicked: false });
|
280 | }, 100);
|
281 | this.setState({
|
282 | cleanedValue: isReset ? newValue : null
|
283 | });
|
284 | };
|
285 |
|
286 | Rating.prototype.getOverlayWidth = function getOverlayWidth() {
|
287 | var _state4 = this.state,
|
288 | hoverValue = _state4.hoverValue,
|
289 | iconSpace = _state4.iconSpace,
|
290 | iconSize = _state4.iconSize;
|
291 |
|
292 |
|
293 | if (!iconSpace || !iconSize) {
|
294 | return 'auto';
|
295 | }
|
296 |
|
297 | var value = Rating.currentValue(0, this.props.count, hoverValue, this.state.value);
|
298 |
|
299 | var floorValue = Math.floor(value);
|
300 |
|
301 | return iconSize * value + (floorValue + 1) * iconSpace;
|
302 | };
|
303 |
|
304 | Rating.prototype.getInfoLeft = function getInfoLeft() {
|
305 | var _state5 = this.state,
|
306 | value = _state5.value,
|
307 | hoverValue = _state5.hoverValue,
|
308 | iconSpace = _state5.iconSpace,
|
309 | iconSize = _state5.iconSize;
|
310 |
|
311 | var infoValue = hoverValue || value;
|
312 | var ceilValue = Math.ceil(infoValue);
|
313 |
|
314 | return iconSize * (ceilValue - 1) + ceilValue * iconSpace;
|
315 | };
|
316 |
|
317 | Rating.prototype.render = function render() {
|
318 | var _classNames,
|
319 | _classNames2,
|
320 | _classNames3,
|
321 | _this4 = this;
|
322 |
|
323 | var _props3 = this.props,
|
324 | id = _props3.id,
|
325 | prefix = _props3.prefix,
|
326 | className = _props3.className,
|
327 | showGrade = _props3.showGrade,
|
328 | count = _props3.count,
|
329 | size = _props3.size,
|
330 | iconType = _props3.iconType,
|
331 | strokeMode = _props3.strokeMode,
|
332 | readAs = _props3.readAs,
|
333 | rtl = _props3.rtl,
|
334 | isPreview = _props3.isPreview,
|
335 | renderPreview = _props3.renderPreview,
|
336 | locale = _props3.locale;
|
337 | var disabled = this.state.disabled;
|
338 |
|
339 | var others = obj.pickOthers(Rating.propTypes, this.props);
|
340 | var _state6 = this.state,
|
341 | hoverValue = _state6.hoverValue,
|
342 | clicked = _state6.clicked;
|
343 |
|
344 | var underlay = [],
|
345 | overlay = [];
|
346 |
|
347 | var enableA11y = !!id;
|
348 |
|
349 |
|
350 | var value = Rating.currentValue(0, count, hoverValue, this.state.value);
|
351 |
|
352 |
|
353 | var sizeMap = ICON_SIZE_MAP[size];
|
354 |
|
355 | var _loop = function _loop(i) {
|
356 | var _classNames4;
|
357 |
|
358 | var isCurrent = Math.ceil(value - 1) === i;
|
359 | var iconCls = classNames((_classNames4 = {
|
360 | hover: hoverValue > 0 && isCurrent,
|
361 | clicked: clicked && isCurrent
|
362 | }, _classNames4[prefix + 'rating-symbol-icon'] = !iconType, _classNames4));
|
363 | var iconNode = iconType ? React.createElement(Icon, { type: iconType, size: sizeMap, className: iconCls }) : React.createElement(Icon, { type: 'favorites-filling', size: sizeMap, className: iconCls });
|
364 |
|
365 | var saveRefs = function saveRefs(ref) {
|
366 | _this4.saveRef(ref, i);
|
367 | };
|
368 |
|
369 | underlay.push(React.createElement(
|
370 | 'span',
|
371 | { ref: saveRefs, key: 'underlay-' + i, className: prefix + 'rating-icon' },
|
372 | iconNode
|
373 | ));
|
374 | if (enableA11y) {
|
375 | overlay.push(React.createElement('input', {
|
376 | id: id + '-' + prefix + 'star' + (i + 1),
|
377 | key: 'input-' + i,
|
378 | className: prefix + 'sr-only',
|
379 | 'aria-checked': i + 1 === parseInt(hoverValue),
|
380 | checked: i + 1 === parseInt(hoverValue),
|
381 | onChange: _this4.handleChecked.bind(_this4, i + 1),
|
382 | type: 'radio',
|
383 | name: 'rating'
|
384 | }));
|
385 | }
|
386 |
|
387 | overlay.push(React.createElement(
|
388 | 'label',
|
389 | {
|
390 | key: 'overlay-' + i,
|
391 | htmlFor: enableA11y ? id + '-' + prefix + 'star' + (i + 1) : null,
|
392 | className: prefix + 'rating-icon'
|
393 | },
|
394 | iconNode,
|
395 | enableA11y ? React.createElement(
|
396 | 'span',
|
397 | { className: prefix + 'sr-only' },
|
398 | readAs(i + 1)
|
399 | ) : null
|
400 | ));
|
401 | };
|
402 |
|
403 | for (var i = 0; i < count; i++) {
|
404 | _loop(i);
|
405 | }
|
406 |
|
407 | var ratingCls = classNames([prefix + 'rating', prefix + 'rating-' + size], (_classNames = {}, _classNames[prefix + 'rating-grade-low'] = value <= count * 0.4, _classNames[prefix + 'rating-grade-high'] = value > count * 0.4, _classNames[prefix + 'rating-stroke-mode'] = strokeMode, _classNames.hover = hoverValue > 0, _classNames), className);
|
408 |
|
409 | var baseCls = classNames(prefix + 'rating-base', (_classNames2 = {}, _classNames2[prefix + 'rating-base-disabled'] = disabled, _classNames2));
|
410 |
|
411 | var previewCls = classNames((_classNames3 = {}, _classNames3[prefix + 'form-preview'] = true, _classNames3[className] = !!className, _classNames3));
|
412 |
|
413 | var overlayStyle = {
|
414 | width: this.getOverlayWidth()
|
415 | };
|
416 | var infoStyle = {
|
417 | left: this.getInfoLeft(),
|
418 | display: hoverValue ? 'block' : 'none'
|
419 | };
|
420 |
|
421 | var finalProps = disabled ? {} : {
|
422 | onClick: this.handleClick,
|
423 | onMouseOver: this.handleHover,
|
424 | onMouseMove: this.handleHover,
|
425 | onMouseLeave: this.handleLeave
|
426 | };
|
427 |
|
428 | if (rtl) {
|
429 | others.dir = 'rtl';
|
430 | }
|
431 |
|
432 | if (isPreview && 'renderPreview' in this.props) {
|
433 | return React.createElement(
|
434 | 'div',
|
435 | _extends({ id: id }, others, { className: previewCls }),
|
436 | renderPreview(value, this.props)
|
437 | );
|
438 | }
|
439 |
|
440 | return React.createElement(
|
441 | 'div',
|
442 | _extends({
|
443 | id: id
|
444 | }, others, {
|
445 | className: ratingCls,
|
446 | onKeyDown: this.onKeyDown,
|
447 | tabIndex: '0',
|
448 | role: 'group',
|
449 | 'aria-label': locale.description
|
450 | }),
|
451 | React.createElement(
|
452 | 'div',
|
453 | _extends({ className: baseCls }, finalProps),
|
454 | React.createElement(
|
455 | 'div',
|
456 | { className: prefix + 'rating-underlay', ref: function ref(n) {
|
457 | return _this4.underlayNode = n;
|
458 | }, 'aria-hidden': true },
|
459 | underlay
|
460 | ),
|
461 | React.createElement(
|
462 | 'div',
|
463 | { className: prefix + 'rating-overlay', style: overlayStyle, onClick: function onClick(e) {
|
464 | return e.preventDefault();
|
465 | } },
|
466 | overlay
|
467 | )
|
468 | ),
|
469 | showGrade ? React.createElement(
|
470 | 'div',
|
471 | { className: prefix + 'rating-info', style: infoStyle },
|
472 | readAs(value)
|
473 | ) : null
|
474 | );
|
475 | };
|
476 |
|
477 | return Rating;
|
478 | }(Component), _class.propTypes = {
|
479 | prefix: PropTypes.string,
|
480 | |
481 |
|
482 |
|
483 | defaultValue: PropTypes.number,
|
484 | |
485 |
|
486 |
|
487 | value: PropTypes.number,
|
488 | |
489 |
|
490 |
|
491 | count: PropTypes.number,
|
492 | |
493 |
|
494 |
|
495 | showGrade: PropTypes.bool,
|
496 | |
497 |
|
498 |
|
499 | size: PropTypes.oneOf(['small', 'medium', 'large']),
|
500 | |
501 |
|
502 |
|
503 | allowHalf: PropTypes.bool,
|
504 | |
505 |
|
506 |
|
507 | allowClear: PropTypes.bool,
|
508 | |
509 |
|
510 |
|
511 |
|
512 | onChange: PropTypes.func,
|
513 | |
514 |
|
515 |
|
516 |
|
517 | onHoverChange: PropTypes.func,
|
518 | |
519 |
|
520 |
|
521 | disabled: PropTypes.bool,
|
522 | |
523 |
|
524 |
|
525 | readAs: PropTypes.func,
|
526 |
|
527 | iconType: PropTypes.string,
|
528 |
|
529 | strokeMode: PropTypes.bool,
|
530 | className: PropTypes.string,
|
531 | id: PropTypes.string,
|
532 | rtl: PropTypes.bool,
|
533 | |
534 |
|
535 |
|
536 | locale: PropTypes.object,
|
537 | |
538 |
|
539 |
|
540 | isPreview: PropTypes.bool,
|
541 | |
542 |
|
543 |
|
544 |
|
545 | renderPreview: PropTypes.func,
|
546 | |
547 |
|
548 |
|
549 | readOnly: PropTypes.bool
|
550 | }, _class.defaultProps = {
|
551 | prefix: 'next-',
|
552 | size: 'medium',
|
553 | disabled: false,
|
554 | readOnly: false,
|
555 | isPreview: false,
|
556 | count: 5,
|
557 | showGrade: false,
|
558 | defaultValue: 0,
|
559 | readAs: function readAs(val) {
|
560 | return val;
|
561 | },
|
562 | allowHalf: false,
|
563 | allowClear: false,
|
564 | onChange: noop,
|
565 | onHoverChange: noop,
|
566 | locale: zhCN.Rating
|
567 | }, _temp);
|
568 | Rating.displayName = 'Rating';
|
569 |
|
570 |
|
571 | export default polyfill(Rating); |
\ | No newline at end of file |