ldx-widgets
Version:
widgets
168 lines (134 loc) • 5.16 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 'warning', a yellow exclamation mark flashes before calling the done callback
- When 'failed', a red x flashes before calling the fail callback
@props.saveMessage - string
A message that will be displayed alongside the saveState imagery
@props.dismissBtnText - string
Text that will be displayed as the dismiss button
@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, span, button} = React.DOM
ConfirmSave = React.createClass
displayName: 'ConfirmSave'
getDefaultProps: ->
dismissBtnText: 'Dismiss'
scaleCheckmark: 1
animationDuration: 800
vTranslateCheckmark: 0
saveMessage: ''
getInitialState: ->
scale: .25
render: ->
{dismissBtnText, saveState, scaleCheckmark, vTranslateCheckmark, saveMessage} = @props
{scale} = @state
if saveState is 'pending'
content = Spinner {
lines: 12
length: 10
width: 3
radius: 8
color: 'white'
}
# Use a separate component for messages
else if saveMessage
checkClassName = 'confirm-check-message'
checkClassName += ' failed' if saveState is 'failed'
checkClassName += ' warning' if saveState is 'warning'
# Don't do a scale animation when there's a message
scaleCheckmark = 1
content = [
div {
key: 'icon'
className: checkClassName
},
span {
className: 'confirm-message'
}, saveMessage
div {
key: 'dismiss'
className: 'message-dismiss'
},
button {
onClick: @end
}, dismissBtnText
]
else
checkClassName = 'confirm-check'
checkClassName += ' failed' if saveState is 'failed'
checkClassName += ' warning' if saveState is 'warning'
content = div {
className: checkClassName
style:
transform: "scale(#{scale})"
msTransform: "scale(#{scale})"
WebkitTransform: "scale(#{scale})"
}
div {
className: 'confirm-save-wrap' + if saveMessage then ' has-message' else ''
}, 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 'warning' or @props.saveState is 'failed')
@animateCheck()
animateCheck: ->
if @animation?.isAnimating() then @animation.end()
{scale} = @state
{animationDuration, saveMessage} = @props
@animation = new Animation
duration: animationDuration
easing: easing('easeOutElastic')
.init {scale}
.on 'frame', @onFrame
.on 'complete', if saveMessage then -> else @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