diff options
| author | IT Fixcomart <it@fixcomart.co.id> | 2023-11-21 06:10:12 +0000 |
|---|---|---|
| committer | IT Fixcomart <it@fixcomart.co.id> | 2023-11-21 06:10:12 +0000 |
| commit | 0fc062268c71d53f8777c358b31e2a540d60d404 (patch) | |
| tree | 0183a00dcfb14583c7dfa80da082c21630afe375 /src-migrate/common/components | |
| parent | 6ebe202147269100cd63ef125e877e8f693a27a1 (diff) | |
| parent | 6a6ce21e5a552b0dc6cd541710a87fd0a0fd9d20 (diff) | |
Merged in refactor/all (pull request #116)
Refactor/all
Diffstat (limited to 'src-migrate/common/components')
3 files changed, 126 insertions, 0 deletions
diff --git a/src-migrate/common/components/elements/Modal.tsx b/src-migrate/common/components/elements/Modal.tsx new file mode 100644 index 00000000..8e488a3a --- /dev/null +++ b/src-migrate/common/components/elements/Modal.tsx @@ -0,0 +1,90 @@ +import { XMarkIcon } from "@heroicons/react/24/outline"; +import { AnimatePresence, motion } from "framer-motion" +import { useRouter } from "next/router"; +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, + mode?: "mobile" | "desktop" +} + +const Modal = ({ + children, + active = false, + title, + close, + className, + mode +}: Props) => { + const router = useRouter() + const { width } = useWindowSize() + const [rendered, setRendered] = useState<boolean>(false) + + mode = mode || width >= 768 ? "desktop" : "mobile" + + 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 w-11/12 md:w-1/4 lg:w-1/3 border rounded-xl": mode === 'desktop', + "left-0 w-full border-t bottom-0 rounded-t-xl": mode === 'mobile' + }, + className + ) + + const variant = { + initial: { bottom: mode === 'desktop' ? '45%' : '-100%', opacity: 0 }, + animate: { bottom: mode === 'desktop' ? '50%' : 0, opacity: 1 }, + exit: { bottom: mode === 'desktop' ? '55%' : '-100%', opacity: 0 }, + transition: { ease: 'linear', duration: 0.25 } + } + + return rendered && ReactDOM.createPortal( + <AnimatePresence key={router.asPath}> + {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
\ No newline at end of file diff --git a/src-migrate/common/components/elements/ReCaptcha.tsx b/src-migrate/common/components/elements/ReCaptcha.tsx new file mode 100644 index 00000000..1bc31d90 --- /dev/null +++ b/src-migrate/common/components/elements/ReCaptcha.tsx @@ -0,0 +1,17 @@ +import ReCAPTCHA, { ReCAPTCHAProps } from "react-google-recaptcha" + +const GOOGLE_RECAPTCHA_KEY = process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE || '' + +type Props = Omit<ReCAPTCHAProps, 'sitekey'> & { + sitekey?: string; +} + +const ReCaptcha = (props: Props) => { + const { sitekey, ...rest } = props + + return ( + <ReCAPTCHA sitekey={sitekey || GOOGLE_RECAPTCHA_KEY} {...rest} /> + ) +} + +export default ReCaptcha
\ No newline at end of file diff --git a/src-migrate/common/components/skeleton/PageContentSkeleton.tsx b/src-migrate/common/components/skeleton/PageContentSkeleton.tsx new file mode 100644 index 00000000..bf85cff1 --- /dev/null +++ b/src-migrate/common/components/skeleton/PageContentSkeleton.tsx @@ -0,0 +1,19 @@ +const PageContentSkeleton = () => { + return ( + <div className="animate-pulse grid gap-y-4"> + <div className="w-full h-10 bg-gray-300 rounded" /> + <div className="h-2" /> + <div className="w-full h-4 bg-gray-300 rounded" /> + <div className="w-full h-4 bg-gray-300 rounded" /> + <div className="w-full h-4 bg-gray-300 rounded" /> + <div className="w-8/12 h-4 bg-gray-300 rounded" /> + <div className="h-2" /> + <div className="w-full h-4 bg-gray-300 rounded" /> + <div className="w-full h-4 bg-gray-300 rounded" /> + <div className="w-full h-4 bg-gray-300 rounded" /> + <div className="w-1/2 h-4 bg-gray-300 rounded" /> + </div> + ) +} + +export default PageContentSkeleton
\ No newline at end of file |
