"use client"; import { clsx } from "clsx"; import { createContext, FC, PropsWithChildren, Reducer, useCallback, useContext, useReducer, } from "react"; import { CheckCircleIcon } from "../../icons/CheckCircleIcon.js"; import { ErrorIcon } from "../../icons/ErrorIcon.js"; type ToastShape = { text: string; type: "confirmation" | "error"; id: string; }; type State = { toasts: ToastShape[]; }; type Action = | { type: "ADD_TOAST"; payload: ToastShape; } | { type: "REMOVE_TOAST"; payload: string; }; const reducer: Reducer = (state, action) => { switch (action.type) { case "ADD_TOAST": return { ...state, toasts: [...state.toasts, action.payload], }; case "REMOVE_TOAST": return { ...state, toasts: state.toasts.filter((toast) => toast.id !== action.payload), }; default: return state; } }; const Toast: FC<{ toast: ToastShape }> = ({ toast }) => { const dispatch = useContext(Context); const remove = () => { dispatch({ type: "REMOVE_TOAST", payload: toast.id, }); }; return (
{toast.type === "error" && } {toast.type === "confirmation" && }
{toast.text}
); }; const Context = createContext<(action: Action) => void>(() => undefined); export const useToast = () => { const dispatch = useContext(Context); return useCallback( (text: string, type: ToastShape["type"]) => { dispatch({ type: "ADD_TOAST", payload: { text, type, id: Math.random().toString(36).slice(2), }, }); }, [dispatch] ); }; export const WithToasts: FC = ({ children }) => { const [state, dispatch] = useReducer(reducer, { toasts: [] }); return (
{children}
{state.toasts.map((toast) => ( ))}
); };