UNPKG

@douyinfe/semi-ui

Version:

A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.

215 lines 6.49 kB
import React from 'react'; import cls from 'classnames'; import PropTypes from 'prop-types'; import { cssClasses, strings } from '@douyinfe/semi-foundation/lib/es/rating/constants'; import '@douyinfe/semi-foundation/lib/es/rating/rating.css'; import { IconStar } from '@douyinfe/semi-icons'; import { RatingItemFoundation } from '@douyinfe/semi-foundation/lib/es/rating/foundation'; import BaseComponent from '../_base/baseComponent'; export default class Item extends BaseComponent { constructor(props) { super(props); this.firstStar = null; this.secondStar = null; this.onHover = e => { const { onHover, index } = this.props; onHover(e, index); }; this.onClick = e => { const { onClick, index } = this.props; onClick(e, index); }; this.onFocus = (e, star) => { const { onFocus } = this.props; onFocus && onFocus(e); this.foundation.handleFocusVisible(e, star); }; this.onBlur = (e, star) => { const { onBlur } = this.props; onBlur && onBlur(e); this.foundation.handleBlur(e, star); }; this.onKeyDown = e => { const { onClick, index } = this.props; if (e.keyCode === 13) { onClick(e, index); } }; this.starFocus = () => { const { value, index, preventScroll } = this.props; if (value - index === 0.5) { this.firstStar.focus({ preventScroll }); } else { this.secondStar.focus({ preventScroll }); } }; this.saveFirstStar = node => { this.firstStar = node; }; this.saveSecondStar = node => { this.secondStar = node; }; this.state = { firstStarFocus: false, secondStarFocus: false }; this.foundation = new RatingItemFoundation(this.adapter); } get adapter() { return Object.assign(Object.assign({}, super.adapter), { setFirstStarFocus: value => { this.setState({ firstStarFocus: value }); }, setSecondStarFocus: value => { this.setState({ secondStarFocus: value }); } }); } render() { const { index, prefixCls, character, count, value, disabled, allowHalf, focused, size, ariaLabelPrefix } = this.props; const { firstStarFocus, secondStarFocus } = this.state; const starValue = index + 1; const diff = starValue - value; // const isHalf = allowHalf && value + 0.5 === starValue; const isHalf = allowHalf && diff < 1 && diff > 0; const firstWidth = 1 - diff; const isFull = starValue <= value; const isCustomSize = typeof size === 'number'; const starCls = cls(prefixCls, { [`${prefixCls}-half`]: isHalf, [`${prefixCls}-full`]: isFull, [`${prefixCls}-${size}`]: !isCustomSize }); const sizeStyle = isCustomSize ? { width: size, height: size, fontSize: size } : {}; const iconSize = isCustomSize ? 'inherit' : size === 'small' ? 'default' : 'extra-large'; const content = character ? character : /*#__PURE__*/React.createElement(IconStar, { size: iconSize, style: { display: 'block' } }); const isEmpty = index === count; const starWrapCls = cls(`${prefixCls}-wrapper`, { [`${prefixCls}-disabled`]: disabled, [`${cssClasses.PREFIX}-focus`]: (firstStarFocus || secondStarFocus) && value !== 0 }); const starWrapProps = { onClick: disabled ? null : this.onClick, onKeyDown: disabled ? null : this.onKeyDown, onMouseMove: disabled ? null : this.onHover, className: starWrapCls }; const AriaSetSize = allowHalf ? count * 2 + 1 : count + 1; const firstStarProps = { ref: this.saveFirstStar, role: "radio", 'aria-checked': value === index + 0.5, 'aria-posinset': 2 * index + 1, 'aria-setsize': AriaSetSize, 'aria-disabled': disabled, 'aria-label': `${index + 0.5} ${ariaLabelPrefix}s`, 'aria-labelledby': this.props['aria-describedby'], 'aria-describedby': this.props['aria-describedby'], className: cls(`${prefixCls}-first`, `${cssClasses.PREFIX}-no-focus`), tabIndex: !disabled && value === index + 0.5 ? 0 : -1, onFocus: e => { this.onFocus(e, 'first'); }, onBlur: e => { this.onBlur(e, 'first'); } }; const secondStarTabIndex = !disabled && (value === index + 1 || isEmpty && value === 0) ? 0 : -1; const secondStarProps = { ref: this.saveSecondStar, role: "radio", 'aria-checked': isEmpty ? value === 0 : value === index + 1, 'aria-posinset': allowHalf ? 2 * (index + 1) : index + 1, 'aria-setsize': AriaSetSize, 'aria-disabled': disabled, 'aria-label': `${isEmpty ? 0 : index + 1} ${ariaLabelPrefix}${index === 0 ? '' : 's'}`, 'aria-labelledby': this.props['aria-describedby'], 'aria-describedby': this.props['aria-describedby'], className: cls(`${prefixCls}-second`, `${cssClasses.PREFIX}-no-focus`), tabIndex: secondStarTabIndex, onFocus: e => { this.onFocus(e, 'second'); }, onBlur: e => { this.onBlur(e, 'second'); } }; return /*#__PURE__*/React.createElement("li", { className: starCls, style: Object.assign({}, sizeStyle), key: index }, /*#__PURE__*/React.createElement("div", Object.assign({}, starWrapProps), allowHalf && !isEmpty && /*#__PURE__*/React.createElement("div", Object.assign({}, firstStarProps, { style: { width: `${firstWidth * 100}%` } }), content), /*#__PURE__*/React.createElement("div", Object.assign({}, secondStarProps, { "x-semi-prop": "character" }), content))); } } Item.propTypes = { value: PropTypes.number, index: PropTypes.number, prefixCls: PropTypes.string, allowHalf: PropTypes.bool, onHover: PropTypes.func, onClick: PropTypes.func, character: PropTypes.node, focused: PropTypes.bool, disabled: PropTypes.bool, count: PropTypes.number, ariaLabelPrefix: PropTypes.string, size: PropTypes.oneOfType([PropTypes.oneOf(strings.SIZE_SET), PropTypes.number]), 'aria-describedby': PropTypes.string, onFocus: PropTypes.func, onBlur: PropTypes.func, preventScroll: PropTypes.bool };