| 1 |
import * as React from "react"; |
| 2 |
import * as ReactDOM from "react-dom"; |
| 3 |
import { motion } from "framer-motion"; |
| 4 |
import { nanoid } from "nanoid"; |
| 5 |
|
| 6 |
import NotificationContext from "../context/Notification"; |
| 7 |
|
| 8 |
type Notification = { |
| 9 |
message: string; |
| 10 |
type: "success" | "error"; |
| 11 |
}; |
| 12 |
|
| 13 |
const CLASSES = { |
| 14 |
success: "vm-text-green-500", |
| 15 |
error: "vm-text-red-600", |
| 16 |
}; |
| 17 |
|
| 18 |
const variants = { |
| 19 |
hidden: { |
| 20 |
y: 200, |
| 21 |
opacity: 0, |
| 22 |
}, |
| 23 |
visible: { |
| 24 |
y: 0, |
| 25 |
opacity: 1, |
| 26 |
}, |
| 27 |
}; |
| 28 |
|
| 29 |
const NotificationContainer = (props) => { |
| 30 |
const notifications = props.notifications; |
| 31 |
|
| 32 |
return ReactDOM.createPortal( |
| 33 |
<div className="vm-absolute vm-bottom-5 vm-w-full vm-z-10 vm-flex vm-flex-col vm-items-center vm-justify-center"> |
| 34 |
{notifications.map((n) => ( |
| 35 |
<motion.div |
| 36 |
key={n.id} |
| 37 |
variants={variants} |
| 38 |
initial="hidden" |
| 39 |
animate="visible" |
| 40 |
onClick={() => props.removeNotification(n.id)} |
| 41 |
className={`vm-bg-white vm-shadow-xl vm-p-4 vm-rounded vm-mb-3 vm-border vm-border-solid vm-border-gray-100 vm-font-semibold ${ |
| 42 |
CLASSES[n.type] |
| 43 |
}`} |
| 44 |
> |
| 45 |
{n.message} |
| 46 |
</motion.div> |
| 47 |
))} |
| 48 |
</div>, |
| 49 |
document.body |
| 50 |
); |
| 51 |
}; |
| 52 |
|
| 53 |
const NotificationProvider = ({ children }) => { |
| 54 |
const [notifications, setNotifications] = React.useState([]); |
| 55 |
|
| 56 |
const showNotification = React.useCallback( |
| 57 |
(type, message) => { |
| 58 |
setNotifications((notifications) => [ |
| 59 |
...notifications, |
| 60 |
{ id: nanoid(), message, type }, |
| 61 |
]); |
| 62 |
}, |
| 63 |
[setNotifications] |
| 64 |
); |
| 65 |
|
| 66 |
const removeNotification = React.useCallback( |
| 67 |
(id) => { |
| 68 |
setNotifications((notifications) => |
| 69 |
notifications.filter((t) => t.id !== id) |
| 70 |
); |
| 71 |
}, |
| 72 |
[setNotifications] |
| 73 |
); |
| 74 |
|
| 75 |
React.useEffect(() => { |
| 76 |
const timers = notifications.map((n) => { |
| 77 |
return setTimeout(() => { |
| 78 |
removeNotification(n.id); |
| 79 |
}, 3000); |
| 80 |
}); |
| 81 |
|
| 82 |
return () => { |
| 83 |
timers.map((timer) => clearTimeout(timer)); |
| 84 |
}; |
| 85 |
}, [notifications, removeNotification]); |
| 86 |
|
| 87 |
return ( |
| 88 |
<NotificationContext.Provider |
| 89 |
value={{ showNotification, removeNotification }} |
| 90 |
> |
| 91 |
<NotificationContainer |
| 92 |
notifications={notifications} |
| 93 |
removeNotification={removeNotification} |
| 94 |
/> |
| 95 |
{children} |
| 96 |
</NotificationContext.Provider> |
| 97 |
); |
| 98 |
}; |
| 99 |
|
| 100 |
export default NotificationProvider; |
| 101 |
|