Slide.jsx 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import { cloneElement, toChildArray } from 'preact'
  2. import { useEffect, useState, useRef } from 'preact/hooks'
  3. import classNames from 'classnames'
  4. const transitionName = 'uppy-transition-slideDownUp'
  5. const duration = 250
  6. /**
  7. * Vertical slide transition.
  8. *
  9. * This can take a _single_ child component, which _must_ accept a `className` prop.
  10. *
  11. * Currently this is specific to the `uppy-transition-slideDownUp` transition,
  12. * but it should be simple to extend this for any type of single-element
  13. * transition by setting the CSS name and duration as props.
  14. */
  15. function Slide ({ children }) {
  16. const [cachedChildren, setCachedChildren] = useState(null);
  17. const [className, setClassName] = useState('');
  18. const enterTimeoutRef = useRef();
  19. const leaveTimeoutRef = useRef();
  20. const animationFrameRef = useRef();
  21. const handleEnterTransition = () => {
  22. setClassName(`${transitionName}-enter`);
  23. cancelAnimationFrame(animationFrameRef.current);
  24. clearTimeout(leaveTimeoutRef.current);
  25. leaveTimeoutRef.current = undefined;
  26. animationFrameRef.current = requestAnimationFrame(() => {
  27. setClassName(`${transitionName}-enter ${transitionName}-enter-active`);
  28. enterTimeoutRef.current = setTimeout(() => {
  29. setClassName('');
  30. }, duration);
  31. });
  32. };
  33. const handleLeaveTransition = () => {
  34. setClassName(`${transitionName}-leave`);
  35. cancelAnimationFrame(animationFrameRef.current);
  36. clearTimeout(enterTimeoutRef.current);
  37. enterTimeoutRef.current = undefined;
  38. animationFrameRef.current = requestAnimationFrame(() => {
  39. setClassName(`${transitionName}-leave ${transitionName}-leave-active`);
  40. leaveTimeoutRef.current = setTimeout(() => {
  41. setCachedChildren(null);
  42. setClassName('');
  43. }, duration);
  44. });
  45. };
  46. useEffect(() => {
  47. const child = toChildArray(children)[0];
  48. if (cachedChildren === child) return;
  49. if (child && !cachedChildren) {
  50. handleEnterTransition();
  51. } else if (cachedChildren && !child && !leaveTimeoutRef.current) {
  52. handleLeaveTransition();
  53. }
  54. setCachedChildren(child);
  55. }, [children, cachedChildren]); // Dependency array to trigger effect on children change
  56. useEffect(() => {
  57. return () => {
  58. clearTimeout(enterTimeoutRef.current);
  59. clearTimeout(leaveTimeoutRef.current);
  60. cancelAnimationFrame(animationFrameRef.current);
  61. };
  62. }, []); // Cleanup useEffect
  63. if (!cachedChildren) return null;
  64. return cloneElement(cachedChildren, {
  65. className: classNames(className, cachedChildren.props.className),
  66. });
  67. };
  68. export default Slide