summaryrefslogtreecommitdiff
path: root/src/components/Collapsible.tsx
blob: 47b882cdd3d3854390af88df8aeb3501d53d2e02 (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
import React from 'react';
import ResizeObserver from 'resize-observer-polyfill';

import { framerMotionResouce } from '../misc/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',
    overflowY: 'hidden',
    transition: { duration: 0.3 },
  },
};

const variantsCollpapsibleChildContainer = {
  open: {},
  closed: {},
};

// @ts-expect-error ts-migrate(2339) FIXME: Property 'isOpen' does not exist on type '{ childr... Remove this comment to see the full error message
const Collapsible = memo(({ children, isOpen }) => {
  const module = framerMotionResouce.read();
  const motion = module.motion;
  const previous = usePrevious(isOpen);
  // @ts-expect-error ts-migrate(2339) FIXME: Property 'height' does not exist on type 'MutableR... Remove this comment to see the full error message
  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;