veza/apps/web/src/components/ui/Toast.tsx
senke 503e6f00b6 feat(a11y): comprehensive accessibility & view states improvements
Sprint 1 — Quick A11y wins:
- progress.tsx: role=progressbar + aria-value* + aria-label
- switch.tsx: role=switch + aria-checked
- skeleton.tsx: aria-hidden=true
- alert.tsx, Toast.tsx, SelectTrigger.tsx: aria-labels on close buttons
- PostCard.tsx: alt on images + aria-labels on icon buttons
- ProductCard.tsx: aria-labels on play/view buttons
- modal.tsx: role=dialog + aria-modal + aria-labelledby
- input.tsx: error state + aria-invalid + aria-describedby
- FAB.tsx: forward aria-label from label prop

Sprint 2 — Structural A11y + View States:
- tabs/: full ARIA tablist/tab/tabpanel + arrow key navigation
- radio-group.tsx: role=radio + arrow key navigation
- select/: aria-activedescendant + full keyboard navigation
- List.tsx + card.tsx: focus-visible states on interactive elements
- DashboardPage, LibraryPage, LiveView, QueueView: error states
- WishlistView, AdminDashboard, AnalyticsView, SellerDashboard: loading/empty states

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-09 23:04:35 +01:00

111 lines
2.7 KiB
TypeScript

import React, { useEffect } from 'react';
import { X, CheckCircle, AlertCircle, Info } from 'lucide-react';
/**
* ToastMessage - Interface pour un message de toast
*
* @interface ToastMessage
*/
export interface ToastMessage {
/**
* Identifiant unique du toast
*/
id: string;
/**
* Type de toast
*
* - `success`: Message de succès (vert/lime)
* - `error`: Message d'erreur (rouge)
* - `info`: Message d'information (cyan)
*/
type: 'success' | 'error' | 'info';
/**
* Message à afficher
*/
message: string;
}
/**
* ToastProps - Propriétés du composant Toast
*
* @interface ToastProps
* @extends ToastMessage
*/
interface ToastProps extends ToastMessage {
/**
* Fonction appelée pour fermer le toast
*
* @param {string} id - Identifiant du toast à fermer
*/
onClose: (id: string) => void;
}
/**
* Toast - Composant de notification toast avec design system Kodo
*
* Composant de notification toast qui s'affiche temporairement et se ferme automatiquement
* après 4 secondes. Supporte trois types : success, error et info.
*
* @example
* ```tsx
* // Toast de succès
* <Toast
* id="1"
* type="success"
* message="Opération réussie"
* onClose={(id) => removeToast(id)}
* />
*
* // Toast d'erreur
* <Toast
* id="2"
* type="error"
* message="Une erreur s'est produite"
* onClose={(id) => removeToast(id)}
* />
* ```
*
* @component
* @param {ToastProps} props - Propriétés du composant
* @returns {JSX.Element} Élément div stylisé comme un toast avec animation
*/
export const Toast: React.FC<ToastProps> = ({ id, type, message, onClose }) => {
useEffect(() => {
const timer = setTimeout(() => {
onClose(id);
}, 4000);
return () => clearTimeout(timer);
}, [id, onClose]);
const styles = {
success: 'border-success text-foreground bg-card/90',
error: 'border-destructive text-foreground bg-card/90',
info: 'border-border text-foreground bg-card/90',
};
const icons = {
success: <CheckCircle className="w-5 h-5 text-success" />,
error: <AlertCircle className="w-5 h-5 text-destructive" />,
info: <Info className="w-5 h-5 text-muted-foreground" />,
};
return (
<div
role="alert"
className={`flex items-center gap-4 p-4 rounded-lg border shadow-2xl min-w-72 animate-slideInRight backdrop-blur-md mb-3 ${styles[type]}`}
>
{icons[type]}
<p className="flex-1 text-sm font-medium">{message}</p>
<button
onClick={() => onClose(id)}
className="text-muted-foreground hover:text-foreground transition-colors"
aria-label="Fermer la notification"
>
<X className="w-4 h-4" />
</button>
</div>
);
};