veza/apps/web/src/components/ui/alert.tsx
senke 99581d2058 feat(ui): semantic tokens in modal, button, card, alert
- Modal: title text-white → text-foreground
- Button: secondary/ghost/glass use bg-muted/30, border-border
- Card: spotlight bg-black/40 → bg-card/80; surface border-white/* → border-border
- Alert: AlertTitle text-white → text-foreground

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-10 09:29:48 +01:00

185 lines
4.8 KiB
TypeScript

import * as React from 'react';
import { AlertCircle, CheckCircle, Info, AlertTriangle, X } from 'lucide-react';
import { cn } from '@/lib/utils';
/**
* AlertProps - Propriétés du composant Alert
*
* @interface AlertProps
* @extends React.HTMLAttributes<HTMLDivElement>
*/
export interface AlertProps extends React.HTMLAttributes<HTMLDivElement> {
/**
* Variant de l'alerte - Type et couleur selon le contexte
*
* - `info`: Information générale (cyan)
* - `success`: Succès ou confirmation (lime)
* - `warning`: Avertissement (gold)
* - `error`: Erreur ou danger (red)
* - `default`: Alias pour `info` (compatibilité)
* - `destructive`: Alias pour `error` (compatibilité)
*
* @default 'default'
*/
variant?:
| 'info'
| 'success'
| 'warning'
| 'error'
| 'default'
| 'destructive';
/**
* Titre de l'alerte affiché en gras
*
* @example
* ```tsx
* <Alert variant="success" title="Succès">
* Opération réussie
* </Alert>
* ```
*/
title?: string;
/**
* Fonction appelée lors du clic sur le bouton de fermeture
* Si fournie, un bouton de fermeture (X) est affiché
*
* @example
* ```tsx
* <Alert variant="info" onClose={() => setShowAlert(false)}>
* Message d'information
* </Alert>
* ```
*/
onClose?: () => void;
}
/**
* Alert - Composant d'alerte avec design system Kodo
*
* Composant d'alerte pour afficher des messages d'information, de succès, d'avertissement ou d'erreur.
* Inclut automatiquement une icône selon le variant et supporte un titre et un bouton de fermeture.
*
* @example
* ```tsx
* // Alerte d'information
* <Alert variant="info" title="Information">
* Ceci est un message d'information
* </Alert>
*
* // Alerte de succès
* <Alert variant="success" title="Succès">
* Opération réussie avec succès
* </Alert>
*
* // Alerte avec fermeture
* <Alert variant="warning" onClose={() => setShow(false)}>
* Attention: Cette action est irréversible
* </Alert>
* ```
*
* @example
* ```tsx
* // Utilisation avec sous-composants
* <Alert variant="error">
* <AlertTitle>Erreur</AlertTitle>
* <AlertDescription>
* Une erreur s'est produite lors du traitement
* </AlertDescription>
* </Alert>
* ```
*
* @component
* @param {AlertProps} props - Propriétés du composant
* @returns {JSX.Element} Élément div stylisé comme une alerte avec role="alert"
*/
const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
(
{ variant = 'default', title, onClose, className, children, ...props },
ref,
) => {
// Map shadcn variants to Kodo variants
const variantMap: Record<string, string> = {
destructive: 'error',
default: 'info',
};
const actualVariant = variantMap[variant] || variant;
const styles = {
info: 'bg-muted/10 border-border/30 text-muted-foreground',
success: 'bg-success/10 border-success/30 text-success',
warning: 'bg-warning/10 border-warning/30 text-warning',
error: 'bg-destructive/10 border-destructive/30 text-destructive',
};
const icons = {
info: <Info className="w-5 h-5 flex-shrink-0" />,
success: <CheckCircle className="w-5 h-5 flex-shrink-0" />,
warning: <AlertTriangle className="w-5 h-5 flex-shrink-0" />,
error: <AlertCircle className="w-5 h-5 flex-shrink-0" />,
};
const alertVariant = actualVariant as keyof typeof styles;
return (
<div
ref={ref}
role="alert"
className={cn(
'p-4 rounded-lg border flex gap-4',
styles[alertVariant] || styles.info,
className,
)}
{...props}
>
{icons[alertVariant] || icons.info}
<div className="flex-1">
{title && <h5 className="font-bold mb-1">{title}</h5>}
<div className="text-sm opacity-90">{children}</div>
</div>
{onClose && (
<button
onClick={onClose}
className="opacity-70 hover:opacity-100 transition-opacity"
aria-label="Fermer l'alerte"
>
<X className="w-4 h-4" />
</button>
)}
</div>
);
},
);
Alert.displayName = 'Alert';
const AlertTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h5
ref={ref}
className={cn(
'mb-1 font-bold leading-none tracking-tight text-foreground',
className,
)}
{...props}
/>
));
AlertTitle.displayName = 'AlertTitle';
const AlertDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn('text-sm opacity-90 [&_p]:leading-relaxed', className)}
{...props}
/>
));
AlertDescription.displayName = 'AlertDescription';
export { Alert, AlertTitle, AlertDescription };