mirror of
https://github.com/Sonny93/my-links.git
synced 2025-12-10 07:25:35 +00:00
feat: modal enter & exit transitions
This commit is contained in:
@@ -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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user