Skip to content

Commit

Permalink
feat(Rating): add rating component
Browse files Browse the repository at this point in the history
  • Loading branch information
levithomason committed Jul 23, 2016
1 parent 00ef6df commit 138e240
Show file tree
Hide file tree
Showing 11 changed files with 322 additions and 2 deletions.
8 changes: 8 additions & 0 deletions docs/app/Examples/modules/Rating/Types/Clearable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from 'react'
import { Rating } from 'stardust'

const RatingClearableExample = () => (
<Rating maxRating={5} clearable />
)

export default RatingClearableExample
18 changes: 18 additions & 0 deletions docs/app/Examples/modules/Rating/Types/Controlled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React, { Component } from 'react'
import { Rating } from 'stardust'

export default class RatingControlledExample extends Component {
state = { rating: 0 }

handleChange = (e) => this.setState({ rating: e.target.value })

render() {
return (
<div>
<input type='range' min={-1} max={5} value={this.state.rating} onChange={this.handleChange} />
<br />
<Rating rating={this.state.rating} maxRating={5} />
</div>
)
}
}
8 changes: 8 additions & 0 deletions docs/app/Examples/modules/Rating/Types/Disabled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from 'react'
import { Rating } from 'stardust'

const RatingDisabledExample = () => (
<Rating defaultRating={3} maxRating={5} disabled />
)

export default RatingDisabledExample
8 changes: 8 additions & 0 deletions docs/app/Examples/modules/Rating/Types/Heart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from 'react'
import { Rating } from 'stardust'

const RatingHeartExample = () => (
<Rating icon='heart' defaultRating={1} maxRating={3} />
)

export default RatingHeartExample
17 changes: 17 additions & 0 deletions docs/app/Examples/modules/Rating/Types/OnRate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React, { Component } from 'react'
import { Rating } from 'stardust'

export default class RatingOnRateExample extends Component {
state = {}

handleRate = (e, { rating, maxRating }) => this.setState({ rating, maxRating })

render() {
return (
<div>
<Rating maxRating={5} onRate={this.handleRate} />
<pre>{JSON.stringify(this.state, null, 2)}</pre>
</div>
)
}
}
8 changes: 8 additions & 0 deletions docs/app/Examples/modules/Rating/Types/Rating.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from 'react'
import { Rating } from 'stardust'

const RatingExample = () => (
<Rating />
)

export default RatingExample
8 changes: 8 additions & 0 deletions docs/app/Examples/modules/Rating/Types/Star.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from 'react'
import { Rating } from 'stardust'

const RatingStarExample = () => (
<Rating icon='star' defaultRating={3} maxRating={4} />
)

export default RatingStarExample
35 changes: 35 additions & 0 deletions docs/app/Examples/modules/Rating/Variations/Size.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from 'react'
import { Rating } from 'stardust'

const RatingSizeExample = () => (
<div>
<Rating maxRating={5} defaultRating={3} icon='star' size='mini' />
<br />
<br />

<Rating maxRating={5} defaultRating={3} icon='star' size='tiny' />
<br />
<br />

<Rating maxRating={5} defaultRating={3} icon='star' size='small' />
<br />
<br />

<Rating maxRating={5} defaultRating={3} icon='star' />
<br />
<br />

<Rating maxRating={5} defaultRating={3} icon='star' size='large' />
<br />
<br />

<Rating maxRating={5} defaultRating={3} icon='star' size='huge' />
<br />
<br />

<Rating maxRating={5} defaultRating={3} icon='star' size='massive' />
<br />
<br />
</div>
)
export default RatingSizeExample
57 changes: 57 additions & 0 deletions docs/app/Examples/modules/Rating/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React, { Component } from 'react'
import ComponentExample from 'docs/app/Components/ComponentDoc/ComponentExample'
import ExampleSection from 'docs/app/Components/ComponentDoc/ExampleSection'

export default class RatingExamples extends Component {
render() {
return (
<div>
<ExampleSection title='Types'>
<ComponentExample
title='Rating'
description='A basic rating'
examplePath='modules/Rating/Types/Rating'
/>
<ComponentExample
title='Star'
description='A rating can use a set of star icons'
examplePath='modules/Rating/Types/Star'
/>
<ComponentExample
title='Heart'
description='A rating can use a set of heart icons'
examplePath='modules/Rating/Types/Heart'
/>
<ComponentExample
title='Clearable'
description='A rating can be cleared by clicking again'
examplePath='modules/Rating/Types/Clearable'
/>
<ComponentExample
title='Disabled'
description='A rating can be disabled'
examplePath='modules/Rating/Types/Disabled'
/>
<ComponentExample
title='Controlled'
description='A rating can be a controlled component'
examplePath='modules/Rating/Types/Controlled'
/>
<ComponentExample
title='onRate Callback'
description='A rating calls back when the rating changes'
examplePath='modules/Rating/Types/OnRate'
/>
</ExampleSection>

<ExampleSection title='Variations'>
<ComponentExample
title='Size'
description='A rating can vary in size'
examplePath='modules/Rating/Variations/Size'
/>
</ExampleSection>
</div>
)
}
}
5 changes: 3 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,16 @@ export Rail from './elements/Rail/Rail'
// ----------------------------------------
export Accordion from './modules/Accordion/Accordion'
export Checkbox from './modules/Checkbox/Checkbox'
export Progress from './modules/Progress/Progress'
export Dropdown from './modules/Dropdown/Dropdown'

import _Modal from './modules/Modal/Modal'
export { _Modal as Modal }
export const ModalContent = deprecateComponent('ModalContent', 'Use "Modal.Content" instead.', _Modal.Content)
export const ModalFooter = deprecateComponent('ModalFooter', 'Use "Modal.Footer" instead.', _Modal.Footer)
export const ModalHeader = deprecateComponent('ModalHeader', 'Use "Modal.Header" instead.', _Modal.Header)

export Dropdown from './modules/Dropdown/Dropdown'
export Progress from './modules/Progress/Progress'
export Rating from './modules/Rating/Rating'

// ----------------------------------------
// Views
Expand Down
152 changes: 152 additions & 0 deletions src/modules/Rating/Rating.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import _ from 'lodash'
import cx from 'classnames'
import React, { PropTypes } from 'react'

import AutoControlledComponent from '../../utils/AutoControlledComponent'
import META from '../../utils/Meta'
import { getUnhandledProps } from '../../utils/propUtils'
import * as sui from '../../utils/semanticUtils'

const _meta = {
library: META.library.semanticUI,
name: 'Rating',
type: META.type.module,
props: {
clearable: [true, false, 'auto'],
icon: ['star', 'heart'],
size: _.without(sui.sizes, 'medium', 'big'),
},
}

class Rating extends AutoControlledComponent {
static propTypes = {
/** Additional className. */
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.oneOf(_meta.props.clearable),

/** A rating can use a set of icons. */
icon: PropTypes.oneOf(_meta.props.icon),

/** The total number of icons. */
maxRating: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
]),

/** The current number of active icons. */
rating: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
]),

/** The initial rating value. */
defaultRating: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
]),

/** A progress bar can vary in size. */
size: PropTypes.oneOf(_meta.props.size),

/** You can disable or enable interactive rating. Makes a read-only rating. */
disabled: PropTypes.bool,

/** Called with (event, { rating, maxRating }) after user selects a new rating. */
onRate: PropTypes.func,
}

static defaultProps = {
clearable: 'auto',
maxRating: 1,
}

static _meta = _meta

static autoControlledProps = [
'rating',
]

handleIconHover = (index) => {
const { disabled } = this.props
if (disabled) return

this.setState({
selectedIndex: index,
})
}

handleIconClick = (e, index) => {
const { clearable, disabled, maxRating, onRate } = this.props
const { rating } = this.state
let newRating = index + 1

if (disabled) return

// toggle rating
if (clearable === 'auto' && maxRating === 1) {
newRating = +!rating
}

// clear rating
if (clearable === true && newRating === rating) {
newRating = 0
}

// set rating
this.trySetState({ rating: newRating })
if (onRate) onRate(e, { rating: newRating, maxRating })
}

renderIcons = () => {
const { maxRating } = this.props
const { rating, selectedIndex } = this.state

return _.times(maxRating, (i) => {
const classes = cx(
selectedIndex >= i && 'selected',
rating >= i + 1 && 'active',
'icon'
)
return (
<i
key={i}
className={classes}
onClick={(e) => this.handleIconClick(e, i)}
onMouseEnter={() => this.handleIconHover(i)}
onMouseLeave={() => this.handleIconHover(-1)}
/>
)
})
}

render() {
const { className, disabled, icon, size } = this.props
const { selectedIndex } = this.state

const classes = cx(
'ui',
size,
icon,
disabled && 'disabled',
!disabled && selectedIndex >= 0 && 'selected',
'rating',
className,
)

const rest = getUnhandledProps(Rating, this.props)

return (
<div {...rest} className={classes}>
{this.renderIcons()}
</div>
)
}
}

export default Rating

0 comments on commit 138e240

Please sign in to comment.