ldx-widgets
Version:
widgets
135 lines (105 loc) • 3.98 kB
text/coffeescript
###
This component is intended to be used inside of a modal or popover,
but could be used other places to confirm a save event was successful
Confirm Save Props
@props.saveState - string
This can be either 'pending' or 'complete' or 'failed'
- When 'pending', a spinner shows
- When 'complete', a green check mark flashes before calling the done callback
- When 'failed', a red x flashes before calling the fail callback
@props.done - method
This method will be called animationDuration ms after the saveState hits complete
It will usually be a method that closes the popover or modal
@props.fail - method
This method will be called animationDuration ms after the saveState hits failed
It will usually be a method that sets the parent's saveState back to null, which will remove this widget from the DOM
@props.scaleCheckmark - default 1
percent to scale the confirm check and spinner
@props.vTranslateCheckmark - default 0
number of pixels to move the checkmark up above the middle of the container
@props.animationDuration - default 800
ms over which the check scalling takes place
Tips for using this..
- It is absolutely positioned w/ all zeroes, to add to the top level of the render tree
- Create a local 'saveState' state on your component, and default it to null
- render like this:
ConfirmSave {
key: 'confirm'
done: @close
saveState: @state.saveState
} if @state.saveState?
This way, when you trigger the save, also call @setState({saveState: 'pending'})
Then pass a call back to the save action, which just calls @setState({saveState: 'complete'})
###
React = require 'react'
Animation = require 'ainojs-animation'
easing = require 'ainojs-easing'
Spinner = React.createFactory(require './spinner')
{div} = React.DOM
ConfirmSave = React.createClass
displayName: 'ConfirmSave'
getDefaultProps: ->
scaleCheckmark: 1
animationDuration: 800
vTranslateCheckmark: 0
getInitialState: ->
scale: .25
render: ->
{saveState, scaleCheckmark, vTranslateCheckmark} = @props
{scale} = @state
content = (
if saveState is 'pending'
Spinner {
lines: 12
length: 10
width: 3
radius: 8
color: 'white'
}
else
checkClassName = 'confirm-check'
checkClassName += ' failed' if saveState is 'failed'
div {
className: checkClassName
style:
transform: "scale(#{scale})"
msTransform: "scale(#{scale})"
WebkitTransform: "scale(#{scale})"
}
)
div {
className: 'confirm-save-wrap'
}, div {
className: 'confirm-frame'
style:
transform: "scale(#{scaleCheckmark}) translateY(#{vTranslateCheckmark}px)"
msTransform: "scale(#{scaleCheckmark}) translateY(#{vTranslateCheckmark}px)"
WebkitTransform: "scale(#{scaleCheckmark}) translateY(#{vTranslateCheckmark}px)"
}, content
componentWillMount: ->
# Handles the case when the saveState is never pending
if @props.saveState is 'complete' or @props.saveState is 'failed'
@animateImmediately = yes
componentDidMount: ->
@animateCheck() if @animateImmediately
componentDidUpdate: (prevProps) ->
return unless prevProps.saveState is 'pending' and (@props.saveState is 'complete' or @props.saveState is 'failed')
@animateCheck()
animateCheck: ->
if @animation?.isAnimating() then @animation.end()
{scale} = @state
{animationDuration} = @props
@animation = new Animation
duration: animationDuration
easing: easing('easeOutElastic')
.init {scale}
.on 'frame', @onFrame
.on 'complete', @end
.animateTo {scale: 1}
onFrame: (e) ->
if @isMounted() then @setState e.values
end: ->
{saveState, done, fail} = @props
if saveState is 'complete' then @props.done?()
else @props.fail?()
module.exports = ConfirmSave