diff options
| author | zwlucas <lucas.fariamo08@gmail.com> | 2026-07-25 21:23:29 +0000 |
|---|---|---|
| committer | zwlucas <lucas.fariamo08@gmail.com> | 2026-07-25 21:23:29 +0000 |
| commit | ef55d9bb24430610b60c1b77821307cd72c08c55 (patch) | |
| tree | e6935a810b34370b0a4c821e55f6397aac50c832 /web/src/context/ToastContext.tsx | |
| download | helpdesk-master.tar.gz helpdesk-master.zip | |
Signed-off-by: zwlucas <lucas.fariamo08@gmail.com>
Diffstat (limited to 'web/src/context/ToastContext.tsx')
| -rw-r--r-- | web/src/context/ToastContext.tsx | 71 |
1 files changed, 71 insertions, 0 deletions
diff --git a/web/src/context/ToastContext.tsx b/web/src/context/ToastContext.tsx new file mode 100644 index 0000000..87b3894 --- /dev/null +++ b/web/src/context/ToastContext.tsx @@ -0,0 +1,71 @@ +/** + * ToastContext.tsx + * Sistema minimalista de Toast Notifications. + */ + +import { createContext, useContext, useState, useCallback, type ReactNode } from 'react'; +import { createPortal } from 'react-dom'; +import styles from './Toast.module.css'; + +type ToastType = 'info' | 'success' | 'error'; + +interface ToastData { + id: string; + message: string; + type: ToastType; +} + +interface ToastContextValue { + addToast: (message: string, type?: ToastType) => void; +} + +const ToastContext = createContext<ToastContextValue | null>(null); + +export function ToastProvider({ children }: { children: ReactNode }) { + const [toasts, setToasts] = useState<ToastData[]>([]); + + const addToast = useCallback((message: string, type: ToastType = 'info') => { + const id = Math.random().toString(36).substring(2, 9); + setToasts((prev) => [...prev, { id, message, type }]); + + // Remove automaticamente após 4 segundos + setTimeout(() => { + setToasts((prev) => prev.filter((t) => t.id !== id)); + }, 4000); + }, []); + + const removeToast = (id: string) => { + setToasts((prev) => prev.filter((t) => t.id !== id)); + }; + + return ( + <ToastContext.Provider value={{ addToast }}> + {children} + + {/* Portal renderiza os toasts no final do <body> */} + {createPortal( + <div className={styles.toastContainer}> + {toasts.map((toast) => ( + <div key={toast.id} className={`${styles.toast} ${styles[toast.type]}`}> + <span>{toast.message}</span> + <button + className={styles.closeBtn} + onClick={() => removeToast(toast.id)} + aria-label="Fechar" + > + × + </button> + </div> + ))} + </div>, + document.body + )} + </ToastContext.Provider> + ); +} + +export function useToast() { + const ctx = useContext(ToastContext); + if (!ctx) throw new Error('useToast deve ser usado dentro do ToastProvider'); + return ctx; +} |