UNPKG

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