feat: modal enter & exit transitions

This commit is contained in:
Sonny
2023-05-17 02:12:44 +02:00
parent 95c5ca78bc
commit 050c4e2324
4 changed files with 33 additions and 20 deletions

View File

@@ -1,7 +1,8 @@
import { ReactNode } from "react"; import { ReactNode, useId } from "react";
import { createPortal } from "react-dom"; import { createPortal } from "react-dom";
import { GrClose } from "react-icons/gr"; import { GrClose } from "react-icons/gr";
import { motion } from "framer-motion";
import styles from "./modal.module.scss"; import styles from "./modal.module.scss";
interface ModalProps { interface ModalProps {
@@ -22,12 +23,25 @@ export default function Modal({
noHeader = false, noHeader = false,
padding = "1em 1.5em", padding = "1em 1.5em",
}: ModalProps) { }: ModalProps) {
const modalId = useId();
const handleWrapperClick = (event) => const handleWrapperClick = (event) =>
event.target.classList?.[0] === styles["modal-wrapper"] && close(); event.target.classList?.[0] === styles["modal-wrapper"] && close();
return createPortal( return createPortal(
<div className={styles["modal-wrapper"]} onClick={handleWrapperClick}> <motion.div
<div className={styles["modal-container"]} style={{ padding }}> className={styles["modal-wrapper"]}
onClick={handleWrapperClick}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0, transition: { duration: 0.1 } }}
>
<motion.div
className={styles["modal-container"]}
style={{ padding }}
initial={{ opacity: 0, y: -15 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -15, transition: { duration: 0.1 } }}
>
{!noHeader && ( {!noHeader && (
<div className={styles["modal-header"]}> <div className={styles["modal-header"]}>
<h3>{title}</h3> <h3>{title}</h3>
@@ -42,8 +56,8 @@ export default function Modal({
</div> </div>
)} )}
<div className={styles["modal-body"]}>{children}</div> <div className={styles["modal-body"]}>{children}</div>
</div> </motion.div>
</div>, </motion.div>,
document.body document.body
); );
} }

View File

@@ -1,5 +1,4 @@
@import "styles/colors.scss"; @import "styles/colors.scss";
@import "styles/keyframes.scss";
.modal-wrapper { .modal-wrapper {
z-index: 9999; z-index: 9999;
@@ -13,7 +12,6 @@
display: flex; display: flex;
align-items: center; align-items: center;
flex-direction: column; flex-direction: column;
animation: opacityin 0.3s both;
} }
.modal-container { .modal-container {
@@ -25,7 +23,6 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
flex-direction: column; flex-direction: column;
animation: fadeintop 0.3s both;
box-shadow: 0 0 1em 0px rgba($black, 0.25); box-shadow: 0 0 1em 0px rgba($black, 0.25);
} }

View File

@@ -38,8 +38,8 @@ export default function CategoryItem({
transition={{ transition={{
type: "spring", type: "spring",
stiffness: 260, stiffness: 260,
damping: 20, damping: 25,
delay: index * 0.025, delay: index * 0.02,
duration: 200, duration: 200,
}} }}
className={className} className={className}

View File

@@ -9,7 +9,7 @@ import SearchModal from "components/SearchModal/SearchModal";
import SideMenu from "components/SideMenu/SideMenu"; import SideMenu from "components/SideMenu/SideMenu";
import * as Keys from "constants/keys"; import * as Keys from "constants/keys";
import { motion } from "framer-motion"; import { AnimatePresence, motion } from "framer-motion";
import { Category, Link, SearchItem } from "types"; import { Category, Link, SearchItem } from "types";
import { prisma } from "utils/back"; import { prisma } from "utils/back";
import { BuildCategory } from "utils/front"; import { BuildCategory } from "utils/front";
@@ -174,6 +174,7 @@ function Home(props: HomeProps) {
openSearchModal={modal.open} openSearchModal={modal.open}
/> />
<Links category={categoryActive} toggleFavorite={toggleFavorite} /> <Links category={categoryActive} toggleFavorite={toggleFavorite} />
<AnimatePresence>
{modal.isShowing && ( {modal.isShowing && (
<SearchModal <SearchModal
close={modal.close} close={modal.close}
@@ -183,6 +184,7 @@ function Home(props: HomeProps) {
handleSelectCategory={handleSelectCategory} handleSelectCategory={handleSelectCategory}
/> />
)} )}
</AnimatePresence>
</motion.div> </motion.div>
); );
} }