Files
ComFi/contexts/ToastContext.tsx
MMrp89 1a57ac7754 feat: Initialize ComFi project with Vite
Setup project structure, dependencies, and basic configuration for the ComFi application. Includes initial setup for Vite, React, TypeScript, Tailwind CSS, and essential development tools. Defines core types and provides a basic README for local development.
2026-02-09 20:28:37 -03:00

83 lines
3.3 KiB
TypeScript

import React, { createContext, useContext, useState, useCallback } from 'react';
import { Toast } from '../types';
import { X, CheckCircle2, AlertCircle, Info, AlertTriangle } from 'lucide-react';
interface ToastContextData {
addToast: (toast: Omit<Toast, 'id'>) => void;
removeToast: (id: string) => void;
}
const ToastContext = createContext<ToastContextData>({} as ToastContextData);
export const ToastProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [toasts, setToasts] = useState<Toast[]>([]);
const addToast = useCallback(({ type, title, message, duration = 4000 }: Omit<Toast, 'id'>) => {
const id = Math.random().toString(36).substring(2, 9);
const newToast = { id, type, title, message, duration };
setToasts((state) => [...state, newToast]);
if (duration > 0) {
setTimeout(() => {
removeToast(id);
}, duration);
}
}, []);
const removeToast = useCallback((id: string) => {
setToasts((state) => state.filter((toast) => toast.id !== id));
}, []);
return (
<ToastContext.Provider value={{ addToast, removeToast }}>
{children}
{/* Toast Container Rendered Here Globally */}
<div className="fixed top-4 right-4 z-[9999] flex flex-col gap-2 pointer-events-none">
{toasts.map((toast) => (
<div
key={toast.id}
className={`pointer-events-auto min-w-[300px] max-w-sm rounded-xl p-4 shadow-xl border flex items-start gap-3 animate-slide-in-right transform transition-all duration-300 ${
toast.type === 'success' ? 'bg-white border-green-100' :
toast.type === 'error' ? 'bg-white border-red-100' :
toast.type === 'warning' ? 'bg-white border-amber-100' :
'bg-white border-blue-100'
}`}
>
<div className={`mt-0.5 ${
toast.type === 'success' ? 'text-green-500' :
toast.type === 'error' ? 'text-red-500' :
toast.type === 'warning' ? 'text-amber-500' :
'text-blue-500'
}`}>
{toast.type === 'success' && <CheckCircle2 size={20} />}
{toast.type === 'error' && <AlertCircle size={20} />}
{toast.type === 'warning' && <AlertTriangle size={20} />}
{toast.type === 'info' && <Info size={20} />}
</div>
<div className="flex-1">
<h4 className={`text-sm font-bold ${
toast.type === 'success' ? 'text-green-800' :
toast.type === 'error' ? 'text-red-800' :
toast.type === 'warning' ? 'text-amber-800' :
'text-blue-800'
}`}>{toast.title}</h4>
{toast.message && <p className="text-xs text-slate-500 mt-1 leading-relaxed">{toast.message}</p>}
</div>
<button onClick={() => removeToast(toast.id)} className="text-slate-400 hover:text-slate-600">
<X size={16} />
</button>
</div>
))}
</div>
</ToastContext.Provider>
);
};
export const useToast = () => {
const context = useContext(ToastContext);
if (!context) throw new Error('useToast must be used within a ToastProvider');
return context;
};