Slide.jsx 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import { cloneElement, Component, toChildArray } from 'preact'
  2. import classNames from 'classnames'
  3. const transitionName = 'uppy-transition-slideDownUp'
  4. const duration = 250
  5. /**
  6. * Vertical slide transition.
  7. *
  8. * This can take a _single_ child component, which _must_ accept a `className` prop.
  9. *
  10. * Currently this is specific to the `uppy-transition-slideDownUp` transition,
  11. * but it should be simple to extend this for any type of single-element
  12. * transition by setting the CSS name and duration as props.
  13. */
  14. class Slide extends Component {
  15. constructor (props) {
  16. super(props)
  17. this.state = {
  18. cachedChildren: null,
  19. className: '',
  20. }
  21. }
  22. // TODO: refactor to stable lifecycle method
  23. // eslint-disable-next-line
  24. componentWillUpdate (nextProps) {
  25. const { cachedChildren } = this.state
  26. const child = toChildArray(nextProps.children)[0]
  27. if (cachedChildren === child) return null
  28. const patch = {
  29. cachedChildren: child,
  30. }
  31. // Enter transition
  32. if (child && !cachedChildren) {
  33. patch.className = `${transitionName}-enter`
  34. cancelAnimationFrame(this.animationFrame)
  35. clearTimeout(this.leaveTimeout)
  36. this.leaveTimeout = undefined
  37. this.animationFrame = requestAnimationFrame(() => {
  38. // Force it to render before we add the active class
  39. // this.base.getBoundingClientRect()
  40. this.setState({
  41. className: `${transitionName}-enter ${transitionName}-enter-active`,
  42. })
  43. this.enterTimeout = setTimeout(() => {
  44. this.setState({ className: '' })
  45. }, duration)
  46. })
  47. }
  48. // Leave transition
  49. if (cachedChildren && !child && this.leaveTimeout === undefined) {
  50. patch.cachedChildren = cachedChildren
  51. patch.className = `${transitionName}-leave`
  52. cancelAnimationFrame(this.animationFrame)
  53. clearTimeout(this.enterTimeout)
  54. this.enterTimeout = undefined
  55. this.animationFrame = requestAnimationFrame(() => {
  56. this.setState({
  57. className: `${transitionName}-leave ${transitionName}-leave-active`,
  58. })
  59. this.leaveTimeout = setTimeout(() => {
  60. this.setState({
  61. cachedChildren: null,
  62. className: '',
  63. })
  64. }, duration)
  65. })
  66. }
  67. // eslint-disable-next-line
  68. this.setState(patch)
  69. }
  70. render () {
  71. const { cachedChildren, className } = this.state
  72. if (!cachedChildren) {
  73. return null
  74. }
  75. return cloneElement(cachedChildren, {
  76. className: classNames(className, cachedChildren.props.className),
  77. })
  78. }
  79. }
  80. export default Slide