mirror of
https://bitbucket.org/Mattrixwv/mattrixwvreactcomponents.git
synced 2025-12-06 21:53:57 -05:00
Toaster component created
This commit is contained in:
32
lib/component/toaster/Toaster.tsx
Normal file
32
lib/component/toaster/Toaster.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import type { ToasterProps } from "$/types/Toaster";
|
||||
import { Transition } from "@headlessui/react";
|
||||
import clsx from "clsx";
|
||||
|
||||
|
||||
export default function Toaster({
|
||||
toast,
|
||||
className
|
||||
}: ToasterProps){
|
||||
return (
|
||||
<Transition
|
||||
show={toast.length > 1 || (toast.length === 1 && toast[0].hideTime > new Date())}
|
||||
enter="transform transition duration-500"
|
||||
enterFrom="-translate-y-[25vh]"
|
||||
enterTo="translate-y-0 ease-out"
|
||||
leave="transform transition duration-500"
|
||||
leaveFrom="translate-y-0 ease-in"
|
||||
leaveTo="-translate-y-[25vh]"
|
||||
>
|
||||
<div
|
||||
className={clsx(
|
||||
"fixed top-16 left-1/2 -translate-x-1/2 z-100",
|
||||
"flex flex-col items-center justify-center",
|
||||
"shadow-lg shadow-black/40",
|
||||
className
|
||||
)}
|
||||
>
|
||||
{ toast.map((toast) => (<div key={toast.id}>{toast.message}</div>)) }
|
||||
</div>
|
||||
</Transition>
|
||||
);
|
||||
}
|
||||
95
lib/providers/toaster/ToasterProvider.tsx
Normal file
95
lib/providers/toaster/ToasterProvider.tsx
Normal 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;
|
||||
}
|
||||
24
lib/types/Toaster.d.ts
vendored
Normal file
24
lib/types/Toaster.d.ts
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
export interface Toast {
|
||||
id: string;
|
||||
message: React.ReactNode;
|
||||
hideTime: Date;
|
||||
}
|
||||
|
||||
export interface ToastProviderState {
|
||||
toast: Toast[];
|
||||
hideToast: (id: string) => void;
|
||||
addToast: (message: ReactNode, duration?: number) => string;
|
||||
addSuccess: (message: ReactNode, duration?: number) => string;
|
||||
addWarning: (message: ReactNode, duration?: number) => string;
|
||||
addDanger: (message: ReactNode, duration?: number) => string;
|
||||
}
|
||||
|
||||
export interface ToastProviderProps {
|
||||
className?: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface ToasterProps {
|
||||
toast: Toast[];
|
||||
className?: string;
|
||||
}
|
||||
Reference in New Issue
Block a user