Toaster component created

This commit is contained in:
2025-08-09 16:39:58 -04:00
parent 4e3c984125
commit 46aa1e4dda
8 changed files with 181 additions and 78 deletions

View File

@@ -0,0 +1,95 @@
import { DangerMessageBlock, SuccessMessageBlock, WarningMessageBlock } from "$/component/message";
import Toaster from "$/component/toaster/Toaster";
import type { Toast, ToastProviderProps, ToastProviderState } from "$/types/Toaster";
import moment from "moment";
import { createContext, useCallback, useContext, useMemo, useState } from "react";
const toastInitialState: ToastProviderState = {
toast: [],
hideToast: () => {},
addToast: () => "",
addSuccess: () => "",
addWarning: () => "",
addDanger: () => ""
};
export const ToasterProviderContext = createContext<ToastProviderState>(toastInitialState);
export default function ToasterProvider({
className,
children
}: ToastProviderProps){
const [ toast, setToast ] = useState<Toast[]>([]);
const hideToast = useCallback((id: string) => {
setToast((prev) => {
if(prev.length === 1 && prev[0].id === id){
const current = prev[0].hideTime > moment(new Date()).subtract(600, "ms").toDate() ? [...prev] : [];
if(current.length > 0){
setTimeout(() => hideToast(id), 600);
}
return current;
}
else{
return prev.filter((toast) => toast.id !== id);
}
});
}, [ toast ]);
const addToast = useCallback((message: React.ReactNode, duration?: number) => {
if(!duration){
duration = 5000;
}
const id = crypto.randomUUID();
setToast((prev) => [ ...prev, { id, message, duration, hideTime: moment(new Date()).add(duration, "ms").toDate() } ]);
setTimeout(() => hideToast(id), duration);
return id;
}, [ toast ]);
const addSuccess = useCallback((message: React.ReactNode, duration?: number) => {
return addToast(<SuccessMessageBlock>{message}</SuccessMessageBlock>, duration);
}, [ addToast ]);
const addWarning = useCallback((message: React.ReactNode, duration?: number) => {
return addToast(<WarningMessageBlock>{message}</WarningMessageBlock>, duration);
}, [ addToast ]);
const addDanger = useCallback((message: React.ReactNode, duration?: number) => {
return addToast(<DangerMessageBlock>{message}</DangerMessageBlock>, duration);
}, [ addToast ]);
const value: ToastProviderState = useMemo(() => ({
toast,
hideToast,
addToast,
addSuccess,
addWarning,
addDanger
}), [ toast, hideToast, addToast, addSuccess, addWarning, addDanger ]);
return (
<ToasterProviderContext.Provider value={value}>
<Toaster
toast={toast}
className={className}
/>
{children}
</ToasterProviderContext.Provider>
);
}
export function useToaster(){
const context = useContext(ToasterProviderContext);
if(!context){
throw new Error("useToaster must be used within a ToasterProvider");
}
return context;
}