semantic-ui-react
Version:
The official Semantic-UI-React integration.
145 lines (119 loc) • 4.02 kB
JavaScript
import cx from 'clsx'
import _ from 'lodash'
import PropTypes from 'prop-types'
import React from 'react'
import {
ModernAutoControlledComponent as Component,
getElementType,
getUnhandledProps,
SUI,
useKeyOnly,
} from '../../lib'
import RatingIcon from './RatingIcon'
/**
* A rating indicates user interest in content.
*/
export default class Rating extends Component {
handleIconClick = (e, { index }) => {
const { clearable, disabled, maxRating, onRate } = this.props
const { rating } = this.state
if (disabled) return
// default newRating is the clicked icon
// allow toggling a binary rating
// allow clearing ratings
let newRating = index + 1
if (clearable === 'auto' && maxRating === 1) {
newRating = +!rating
} else if (clearable === true && newRating === rating) {
newRating = 0
}
// set rating
this.setState({ rating: newRating, isSelecting: false })
if (onRate) onRate(e, { ...this.props, rating: newRating })
}
handleIconMouseEnter = (e, { index }) => {
if (this.props.disabled) return
this.setState({ selectedIndex: index, isSelecting: true })
}
handleMouseLeave = (...args) => {
_.invoke(this.props, 'onMouseLeave', ...args)
if (this.props.disabled) return
this.setState({ selectedIndex: -1, isSelecting: false })
}
render() {
const { className, disabled, icon, maxRating, size } = this.props
const { rating, selectedIndex, isSelecting } = this.state
const classes = cx(
'ui',
icon,
size,
useKeyOnly(disabled, 'disabled'),
useKeyOnly(isSelecting && !disabled && selectedIndex >= 0, 'selected'),
'rating',
className,
)
const rest = getUnhandledProps(Rating, this.props)
const ElementType = getElementType(Rating, this.props)
return (
<ElementType
{...rest}
className={classes}
role='radiogroup'
onMouseLeave={this.handleMouseLeave}
tabIndex={disabled ? 0 : -1}
>
{_.times(maxRating, (i) => (
<RatingIcon
tabIndex={disabled ? -1 : 0}
active={rating >= i + 1}
aria-checked={rating === i + 1}
aria-posinset={i + 1}
aria-setsize={maxRating}
index={i}
key={i}
onClick={this.handleIconClick}
onMouseEnter={this.handleIconMouseEnter}
selected={selectedIndex >= i && isSelecting}
/>
))}
</ElementType>
)
}
}
Rating.propTypes = {
/** An element type to render as (string or function). */
as: PropTypes.elementType,
/** Additional classes. */
className: PropTypes.string,
/**
* You can clear the rating by clicking on the current start rating.
* By default a rating will be only clearable if there is 1 icon.
* Setting to `true`/`false` will allow or disallow a user to clear their rating.
*/
clearable: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(['auto'])]),
/** The initial rating value. */
defaultRating: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
/** You can disable or enable interactive rating. Makes a read-only rating. */
disabled: PropTypes.bool,
/** A rating can use a set of star or heart icons. */
icon: PropTypes.oneOf(['star', 'heart']),
/** The total number of icons. */
maxRating: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
/**
* Called after user selects a new rating.
*
* @param {SyntheticEvent} event - React's original SyntheticEvent.
* @param {object} data - All props and proposed rating.
*/
onRate: PropTypes.func,
/** The current number of active icons. */
rating: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
/** A progress bar can vary in size. */
size: PropTypes.oneOf(_.without(SUI.SIZES, 'medium', 'big')),
}
Rating.autoControlledProps = ['rating']
Rating.defaultProps = {
clearable: 'auto',
maxRating: 1,
}
Rating.Icon = RatingIcon