2015-03-19 09:21:04 +08:00
|
|
|
# WHY IS THIS FILE HERE? ReactCSSTransitionGroup is causing
|
|
|
|
# inconsitency exceptions when you hammer on the animations and don't let them
|
|
|
|
# finish. This is from http://khan.github.io/react-components/#timeout-transition-group
|
|
|
|
# and uses timeouts to clean up elements rather than listeners on CSS events, which
|
|
|
|
# don't always seem to fire.
|
|
|
|
|
|
|
|
# https://github.com/facebook/react/issues/1707
|
|
|
|
|
|
|
|
React = require('react/addons')
|
2015-03-31 09:08:38 +08:00
|
|
|
PriorityUICoordinator = require('../priority-ui-coordinator')
|
2015-03-19 09:21:04 +08:00
|
|
|
ReactTransitionGroup = React.addons.TransitionGroup
|
|
|
|
TICK = 17
|
|
|
|
|
|
|
|
endEvents = ['webkitTransitionEnd', 'webkitAnimationEnd']
|
|
|
|
|
2015-04-25 02:33:10 +08:00
|
|
|
animationSupported = => true
|
2015-03-19 09:21:04 +08:00
|
|
|
|
|
|
|
###*
|
|
|
|
# Functions for element class management to replace dependency on jQuery
|
|
|
|
# addClass, removeClass and hasClass
|
|
|
|
###
|
|
|
|
|
2015-04-25 02:33:10 +08:00
|
|
|
addClass = (element, className) =>
|
2015-10-03 08:47:53 +08:00
|
|
|
return unless element
|
2015-05-22 05:41:30 +08:00
|
|
|
element.classList.add(className)
|
2015-03-19 09:21:04 +08:00
|
|
|
element
|
|
|
|
|
2015-04-25 02:33:10 +08:00
|
|
|
removeClass = (element, className) =>
|
2015-10-03 08:47:53 +08:00
|
|
|
return unless element
|
2015-05-22 05:41:30 +08:00
|
|
|
if element.classList.contains(className)
|
|
|
|
element.classList.remove(className)
|
2015-03-19 09:21:04 +08:00
|
|
|
element
|
|
|
|
|
2015-04-25 02:33:10 +08:00
|
|
|
|
2015-04-25 06:43:14 +08:00
|
|
|
###
|
|
|
|
Public: TimeoutTransitionGroup applies a CSS transition to the components added
|
|
|
|
or removed from it's children. It is similar to React's CSSTransitionGroup, but
|
|
|
|
that uses the 'transitionend' event, which browsers will not send for any
|
|
|
|
number of reasons, including the transitioning node not being painted.
|
|
|
|
|
|
|
|
This TimeoutTransitionGroup instead uses a user-defined timeout to determine
|
|
|
|
when it is a good time to remove the component. Currently there is only one
|
|
|
|
timeout specified, but in the future it would be nice to be able to specify
|
|
|
|
separate timeouts for enter and leave, in case the timeouts for those
|
|
|
|
animations differ. Even nicer would be some sort of inspection of the CSS to
|
|
|
|
automatically determine the duration of the animation or transition.
|
|
|
|
|
|
|
|
This is adapted from Facebook's CSSTransitionGroup which is in the React
|
|
|
|
addons and under the Apache 2.0 License.
|
|
|
|
|
|
|
|
Example:
|
2015-05-09 10:17:09 +08:00
|
|
|
```coffee
|
|
|
|
<TimeoutTransitionGroup
|
2015-04-25 06:43:14 +08:00
|
|
|
leaveTimeout={125}
|
|
|
|
enterTimeout={125}
|
|
|
|
transitionName="sheet-toolbar">
|
|
|
|
{toolbarElements[1..-1]}
|
|
|
|
</TimeoutTransitionGroup>
|
|
|
|
```
|
2015-05-14 02:44:28 +08:00
|
|
|
|
|
|
|
Section: Component Kit
|
2015-04-25 06:43:14 +08:00
|
|
|
###
|
2015-04-25 02:33:10 +08:00
|
|
|
class TimeoutTransitionGroupChild extends React.Component
|
|
|
|
|
|
|
|
transition: (animationType, finishCallback) =>
|
|
|
|
node = React.findDOMNode(@)
|
2015-10-01 01:12:12 +08:00
|
|
|
return unless node
|
2015-03-19 09:21:04 +08:00
|
|
|
className = @props.name + '-' + animationType
|
|
|
|
activeClassName = className + '-active'
|
|
|
|
|
2015-03-31 09:08:38 +08:00
|
|
|
# If you animate back and forth fast enough, you can call `transition`
|
|
|
|
# before a previous transition has finished. Make sure we cancel the
|
|
|
|
# old timeout.
|
|
|
|
if @animationTimeout
|
|
|
|
clearTimeout(@animationTimeout)
|
|
|
|
@animationTimeout = null
|
|
|
|
|
|
|
|
if @animationTaskId
|
|
|
|
PriorityUICoordinator.endPriorityTask(@animationTaskId)
|
|
|
|
@animationTaskId = null
|
|
|
|
|
|
|
|
# Block database responses, JSON parsing while we are in flight
|
|
|
|
@animationTaskId = PriorityUICoordinator.beginPriorityTask()
|
|
|
|
|
|
|
|
endListener = =>
|
|
|
|
removeClass(node, className)
|
|
|
|
removeClass(node, activeClassName)
|
2015-03-19 09:21:04 +08:00
|
|
|
# Usually this optional callback is used for informing an owner of
|
|
|
|
# a leave animation and telling it to remove the child.
|
|
|
|
finishCallback and finishCallback()
|
2015-03-31 09:08:38 +08:00
|
|
|
|
|
|
|
if @animationTaskId
|
|
|
|
PriorityUICoordinator.endPriorityTask(@animationTaskId)
|
|
|
|
@animationTaskId = null
|
|
|
|
@animationTimeout = null
|
2015-03-19 09:21:04 +08:00
|
|
|
return
|
|
|
|
|
|
|
|
if !animationSupported()
|
|
|
|
endListener()
|
|
|
|
else
|
|
|
|
if animationType == 'enter'
|
|
|
|
@animationTimeout = setTimeout(endListener, @props.enterTimeout)
|
|
|
|
else if animationType == 'leave'
|
|
|
|
@animationTimeout = setTimeout(endListener, @props.leaveTimeout)
|
2015-03-31 09:08:38 +08:00
|
|
|
|
|
|
|
addClass(node, className)
|
|
|
|
|
2015-03-19 09:21:04 +08:00
|
|
|
# Need to do this to actually trigger a transition.
|
|
|
|
@queueClass activeClassName
|
|
|
|
return
|
|
|
|
|
2015-04-25 02:33:10 +08:00
|
|
|
queueClass: (className) =>
|
2015-03-19 09:21:04 +08:00
|
|
|
@classNameQueue.push className
|
|
|
|
if !@timeout
|
|
|
|
@timeout = setTimeout(@flushClassNameQueue, TICK)
|
|
|
|
return
|
2015-05-09 10:17:09 +08:00
|
|
|
|
2015-04-25 02:33:10 +08:00
|
|
|
flushClassNameQueue: =>
|
|
|
|
@classNameQueue.forEach ((name) =>
|
|
|
|
addClass(React.findDOMNode(@), name)
|
|
|
|
return
|
|
|
|
).bind(this)
|
2015-03-19 09:21:04 +08:00
|
|
|
@classNameQueue.length = 0
|
|
|
|
@timeout = null
|
|
|
|
return
|
|
|
|
|
2015-04-25 02:33:10 +08:00
|
|
|
componentWillMount: =>
|
2015-03-19 09:21:04 +08:00
|
|
|
@classNameQueue = []
|
2015-03-31 09:08:38 +08:00
|
|
|
@animationTimeout = null
|
|
|
|
@animationTaskId = null
|
2015-03-19 09:21:04 +08:00
|
|
|
return
|
|
|
|
|
2015-04-25 02:33:10 +08:00
|
|
|
componentWillUnmount: =>
|
2015-03-19 09:21:04 +08:00
|
|
|
if @timeout
|
2015-03-31 09:08:38 +08:00
|
|
|
clearTimeout(@timeout)
|
2015-03-19 09:21:04 +08:00
|
|
|
if @animationTimeout
|
2015-03-31 09:08:38 +08:00
|
|
|
clearTimeout(@animationTimeout)
|
|
|
|
@animationTimeout = null
|
|
|
|
if @animationTaskId
|
|
|
|
PriorityUICoordinator.endPriorityTask(@animationTaskId)
|
|
|
|
@animationTaskId = null
|
2015-03-19 09:21:04 +08:00
|
|
|
return
|
|
|
|
|
2015-04-25 02:33:10 +08:00
|
|
|
componentWillEnter: (done) =>
|
2015-03-19 09:21:04 +08:00
|
|
|
if @props.enter
|
|
|
|
@transition 'enter', done
|
|
|
|
else
|
|
|
|
done()
|
|
|
|
return
|
|
|
|
|
2015-04-25 02:33:10 +08:00
|
|
|
componentWillLeave: (done) =>
|
2015-03-19 09:21:04 +08:00
|
|
|
if @props.leave
|
|
|
|
@transition 'leave', done
|
|
|
|
else
|
|
|
|
done()
|
|
|
|
return
|
|
|
|
|
2015-04-25 02:33:10 +08:00
|
|
|
render: =>
|
2015-03-19 09:21:04 +08:00
|
|
|
React.Children.only @props.children
|
|
|
|
|
2015-04-25 02:33:10 +08:00
|
|
|
class TimeoutTransitionGroup extends React.Component
|
|
|
|
@propTypes =
|
2015-03-19 09:21:04 +08:00
|
|
|
enterTimeout: React.PropTypes.number.isRequired
|
|
|
|
leaveTimeout: React.PropTypes.number.isRequired
|
|
|
|
transitionName: React.PropTypes.string.isRequired
|
|
|
|
transitionEnter: React.PropTypes.bool
|
|
|
|
transitionLeave: React.PropTypes.bool
|
|
|
|
|
2015-04-25 02:33:10 +08:00
|
|
|
@defaultProps =
|
2015-03-19 09:21:04 +08:00
|
|
|
transitionEnter: true
|
|
|
|
transitionLeave: true
|
|
|
|
|
2015-04-25 02:33:10 +08:00
|
|
|
_wrapChild: (child) =>
|
2015-03-19 09:21:04 +08:00
|
|
|
<TimeoutTransitionGroupChild
|
|
|
|
enterTimeout={@props.enterTimeout}
|
|
|
|
leaveTimeout={@props.leaveTimeout}
|
|
|
|
name={@props.transitionName}
|
|
|
|
enter={@props.transitionEnter}
|
|
|
|
leave={@props.transitionLeave}>
|
|
|
|
{child}
|
|
|
|
</TimeoutTransitionGroupChild>
|
|
|
|
|
2015-04-25 02:33:10 +08:00
|
|
|
render: =>
|
2015-03-19 09:21:04 +08:00
|
|
|
<ReactTransitionGroup
|
|
|
|
{...@props}
|
|
|
|
childFactory={@_wrapChild} />
|
|
|
|
|
|
|
|
|
2015-05-09 10:17:09 +08:00
|
|
|
module.exports = TimeoutTransitionGroup
|