summaryrefslogtreecommitdiff
path: root/src/components/Collapsible.js
blob: 22016feac5a53f7ba6f92054b066e0492de1c9f5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import React from 'react';
import ResizeObserver from 'resize-observer-polyfill';
import { motion } from 'framer-motion';

const { memo, useState, useRef, useEffect } = React;

function usePrevious(value) {
  const ref = useRef();
  useEffect(() => void (ref.current = value), [value]);
  return ref.current;
}

function useMeasure() {
  const ref = useRef();
  const [bounds, set] = useState({ height: 0 });
  useEffect(() => {
    const ro = new ResizeObserver(([entry]) => set(entry.contentRect));
    if (ref.current) ro.observe(ref.current);
    return () => ro.disconnect();
  }, []);
  return [ref, bounds];
}

const variantsCollpapsibleWrap = {
  initialOpen: {
    height: 'auto',
    transition: { duration: 0 }
  },
  open: height => ({
    height,
    opacity: 1,
    visibility: 'visible',
    transition: { duration: 0.3 }
  }),
  closed: {
    height: 0,
    opacity: 0,
    visibility: 'hidden',
    transition: { duration: 0.3 }
  }
};

const variantsCollpapsibleChildContainer = {
  open: {
    x: 0
  },
  closed: {
    x: 20
  }
};

const Collapsible = memo(({ children, isOpen }) => {
  const previous = usePrevious(isOpen);
  const [refToMeature, { height }] = useMeasure();
  return (
    <div>
      <motion.div
        animate={
          isOpen && previous === isOpen
            ? 'initialOpen'
            : isOpen
            ? 'open'
            : 'closed'
        }
        custom={height}
        variants={variantsCollpapsibleWrap}
      >
        <motion.div
          variants={variantsCollpapsibleChildContainer}
          ref={refToMeature}
        >
          {children}
        </motion.div>
      </motion.div>
    </div>
  );
});

export default Collapsible;