Merge pull request #92 from asharonbaltazar/feat-add-notif-timeout

Add Notification Timeout
This commit is contained in:
mv-turtle
2022-12-07 19:59:39 -05:00
committed by GitHub
3 changed files with 58 additions and 28 deletions
@@ -1,3 +1,4 @@
import { useEffect, useRef } from "react";
import { faXmarkCircle } from "@fortawesome/free-solid-svg-icons"; import { faXmarkCircle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import classnames from "classnames"; import classnames from "classnames";
@@ -5,14 +6,35 @@ import classnames from "classnames";
import { Notification as NotificationType } from "./NotificationProvider"; import { Notification as NotificationType } from "./NotificationProvider";
interface NotificationProps { interface NotificationProps {
notification: NotificationType; notification: Required<NotificationType>;
clearNotification: (text?: string) => void; clearNotification: (text: string) => void;
} }
const Notification = ({ const Notification = ({
notification, notification,
clearNotification, clearNotification,
}: NotificationProps) => { }: NotificationProps) => {
const timeout = useRef<number>();
const handleClearNotification = () => clearNotification(notification.text);
const setNotifTimeout = () => {
timeout.current = window.setTimeout(
handleClearNotification,
notification.timeoutMs
);
};
const cancelNotifTimeout = () => {
clearTimeout(timeout.current);
};
useEffect(() => {
setNotifTimeout();
return cancelNotifTimeout;
}, []);
return ( return (
<div <div
className={classnames( className={classnames(
@@ -20,6 +42,7 @@ const Notification = ({
{ {
"bg-green-600": notification.type === "success", "bg-green-600": notification.type === "success",
"bg-red-500": notification.type === "error", "bg-red-500": notification.type === "error",
"bg-blue-500": notification.type === "info",
} }
)} )}
role="alert" role="alert"
@@ -2,15 +2,16 @@ import { createContext, ReactNode, useContext, useState } from "react";
import Notifications from "./Notifications"; import Notifications from "./Notifications";
type NotificationType = "success" | "error"; type NotificationType = "success" | "error" | "info";
export type Notification = { export type Notification = {
text: string; text: string;
type: NotificationType; type?: NotificationType;
timeoutMs?: number;
}; };
type NotificationContextState = { type NotificationContextState = {
createNotification: ({ text, type }: Notification) => void; createNotification: (newNotification: Notification) => void;
}; };
const NotificationContext = createContext<NotificationContextState>({ const NotificationContext = createContext<NotificationContextState>({
@@ -24,26 +25,30 @@ interface NotificationProviderProps {
} }
const NotificationProvider = ({ children }: NotificationProviderProps) => { const NotificationProvider = ({ children }: NotificationProviderProps) => {
const [notifications, setNotifications] = useState<Notification[]>([]); const [notifications, setNotifications] = useState<Required<Notification>[]>(
[]
);
const clearNotification = (text?: string) => { const clearNotification = (text: string) => {
if (text) { return setNotifications((state) =>
return setNotifications((state) => state.filter((notif) => notif.text !== text)
state.filter((notif) => notif.text !== text) );
);
}
return setNotifications([]);
}; };
const createNotification = ({ text, type = "success" }: Notification) => { const createNotification = ({
text,
type = "success",
timeoutMs = 2000,
}: Notification) => {
const doesNotifExist = notifications.some((notif) => notif.text === text); const doesNotifExist = notifications.some((notif) => notif.text === text);
if (doesNotifExist) { if (doesNotifExist) {
return; return;
} }
return setNotifications((state) => [...state, { text, type }]); const newNotification: Required<Notification> = { text, type, timeoutMs };
return setNotifications((state) => [...state, newNotification]);
}; };
return ( return (
@@ -2,25 +2,27 @@ import Notification from "./Notification";
import { Notification as NotificationType } from "./NotificationProvider"; import { Notification as NotificationType } from "./NotificationProvider";
interface NoticationsProps { interface NoticationsProps {
notifications: NotificationType[]; notifications: Required<NotificationType>[];
clearNotification: (text?: string) => void; clearNotification: (text: string) => void;
} }
const Notifications = ({ const Notifications = ({
notifications, notifications,
clearNotification, clearNotification,
}: NoticationsProps) => { }: NoticationsProps) => {
if (!notifications.length) {
return null;
}
return ( return (
<div className="hidden fixed z-50 top-1 w-full inset-x-0 pointer-events-none md:flex justify-center"> <div className="hidden fixed z-50 md:flex md:flex-col-reverse bottom-1 gap-y-2 w-96 h-full right-1 pointer-events-none">
<div className="flex flex-col gap-y-2 w-96"> {notifications.map((notif) => (
{notifications.map((notif) => ( <Notification
<Notification key={notif.text}
key={notif.text} notification={notif}
notification={notif} clearNotification={clearNotification}
clearNotification={clearNotification} />
/> ))}
))}
</div>
</div> </div>
); );
}; };