summaryrefslogtreecommitdiff
path: root/src-migrate/common/components/elements/Modal.tsx
blob: ad1fe51b76e2381ba3c1825e69613c90070fafa8 (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
80
81
82
83
84
import { XMarkIcon } from "@heroicons/react/24/outline";
import { AnimatePresence, motion } from "framer-motion"
import { useEffect, useState } from "react";
import ReactDOM from "react-dom";
import { useWindowSize } from "usehooks-ts";
import clsxm from "~/common/libs/clsxm";


type Props = {
  children: React.ReactNode
  active: boolean
  title?: string
  close?: () => void,
  className?: string
}

const Modal = ({
  children,
  active = false,
  title,
  close,
  className
}: Props) => {
  const { width } = useWindowSize()
  const [rendered, setRendered] = useState<boolean>(false)

  useEffect(() => {
    setRendered(true)
  }, [])

  const modalClassNames = clsxm(
    "fixed bg-white max-h-[80vh] overflow-auto p-4 pt-0 z-[60] border-gray_r-6",
    {
      "left-1/2 -translate-x-1/2 translate-y-1/2 bottom-1/2 md:w-1/4 lg:w-1/3 border rounded-xl": width >= 768,
      "left-0 w-full border-t bottom-0 rounded-t-xl": width < 768
    },
    className
  )

  const variant = {
    initial: { bottom: width >= 768 ? '45%' : '-100%', opacity: 0 },
    animate: { bottom: width >= 768 ? '50%' : 0, opacity: 1 },
    exit: { bottom: width >= 768 ? '55%' : '-100%', opacity: 0 },
    transition: { ease: 'linear', duration: 0.25 }
  }

  return rendered && ReactDOM.createPortal(
    <AnimatePresence>
      {active && (
        <motion.div
          className="overlay"
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          exit={{ opacity: 0 }}
          onClick={close}
        />
      )}

      {active && (
        <motion.div
          {...variant}
          className={modalClassNames}
        >
          <div className='flex justify-between py-5 sticky top-0 bg-white'>
            <div className='font-semibold text-h-sm md:text-title-sm'>
              {title}
            </div>
            {close && (
              <button type='button' onClick={close}>
                <XMarkIcon className='w-5 stroke-2' />
              </button>
            )}
          </div>

          {children}
        </motion.div>
      )}

    </AnimatePresence>,
    document.querySelector('body')!
  )
}

export default Modal