Sprint 3.1 — Default colors → semantic (~15 files, ~99 replacements): - lime-500 → success, red-500 → destructive, cyan-500 → primary Sprint 3.2 — Hardcoded colors → semantic (~13 files, ~99 replacements): - text-white → text-foreground, bg-black → bg-background, bg-white → bg-card Sprint 3.3 — Legacy kodo-* → semantic (~27 files, ~122 replacements): - bg-kodo-ink → bg-card, bg-kodo-void → bg-background, text-kodo-steel → text-muted-foreground - Preserved kodo-cyan/magenta/lime/gold palette accents and gradients Sprint 3.4 — Arbitrary values → Tailwind scale (5 replacements): - min-h-[600px] → min-h-layout-page, min-h-[400px] → min-h-layout-page-sm - left-[50%] → left-1/2, min-h-[80px] → min-h-20, min-h-[40px] → min-h-10 Sprint 3.5 — Border-radius standardization (4 replacements): - Modal/dialog skeletons: rounded-lg → rounded-xl (convention) Co-authored-by: Cursor <cursoragent@cursor.com>
173 lines
4.1 KiB
TypeScript
173 lines
4.1 KiB
TypeScript
import * as React from 'react';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
/**
|
|
* BadgeProps - Propriétés du composant Badge
|
|
*
|
|
* @interface BadgeProps
|
|
* @extends React.HTMLAttributes<HTMLSpanElement>
|
|
*/
|
|
export interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
/**
|
|
* Texte du badge (alternative à children)
|
|
*/
|
|
label?: string;
|
|
|
|
/**
|
|
* Variant de couleur du badge
|
|
*
|
|
* - `cyan`: Cyan (par défaut)
|
|
* - `magenta`: Magenta
|
|
* - `lime`: Lime (vert)
|
|
* - `gold`: Or
|
|
* - `terminal`: Style terminal (monospace)
|
|
* - `default`, `primary`: Alias pour cyan
|
|
* - `success`: Alias pour lime
|
|
* - `warning`: Alias pour gold
|
|
* - `error`: Alias pour magenta
|
|
* - `secondary`: Alias pour magenta
|
|
*
|
|
* @default 'cyan'
|
|
*/
|
|
variant?:
|
|
| 'cyan'
|
|
| 'magenta'
|
|
| 'lime'
|
|
| 'gold'
|
|
| 'terminal'
|
|
| 'default'
|
|
| 'primary'
|
|
| 'success'
|
|
| 'warning'
|
|
| 'error'
|
|
| 'secondary';
|
|
|
|
/**
|
|
* Icône à afficher dans le badge
|
|
*/
|
|
icon?: React.ReactNode;
|
|
|
|
/**
|
|
* Taille du badge
|
|
*
|
|
* - `sm`: Petit (px-2 py-0.5 text-[10px])
|
|
* - `md`: Moyen (px-2.5 py-0.5 text-[10px]) - par défaut
|
|
* - `lg`: Grand (px-4 py-1 text-xs)
|
|
*
|
|
* @default 'md'
|
|
*/
|
|
size?: 'sm' | 'md' | 'lg';
|
|
|
|
/**
|
|
* Si `true`, affiche un point au lieu du texte
|
|
*
|
|
* @default false
|
|
*/
|
|
dot?: boolean;
|
|
|
|
/**
|
|
* Nombre à afficher dans le badge (pour notifications, etc.)
|
|
* Si fourni et > 0, affiche le nombre dans un badge secondaire
|
|
*/
|
|
count?: number;
|
|
|
|
/**
|
|
* Contenu du badge (alternative à label)
|
|
*/
|
|
children?: React.ReactNode;
|
|
}
|
|
|
|
/**
|
|
* Badge - Composant de badge avec design system Kodo
|
|
*
|
|
* Composant de badge pour afficher des labels, statuts ou compteurs.
|
|
* Supporte plusieurs variants de couleur, tailles et options d'affichage.
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* // Badge simple
|
|
* <Badge label="Nouveau" />
|
|
*
|
|
* // Badge avec variant
|
|
* <Badge variant="success">Actif</Badge>
|
|
*
|
|
* // Badge avec icône
|
|
* <Badge icon={<Star />} label="Premium" />
|
|
*
|
|
* // Badge avec compteur
|
|
* <Badge count={5}>Notifications</Badge>
|
|
* ```
|
|
*
|
|
* @component
|
|
* @param {BadgeProps} props - Propriétés du composant
|
|
* @returns {JSX.Element} Élément span stylisé comme un badge
|
|
*/
|
|
export const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(
|
|
(
|
|
{
|
|
label,
|
|
variant = 'cyan',
|
|
icon,
|
|
size = 'md',
|
|
dot,
|
|
count,
|
|
children,
|
|
className,
|
|
...props
|
|
},
|
|
ref,
|
|
) => {
|
|
// Map compatibility variants to Kodo variants
|
|
const variantMap: Record<string, string> = {
|
|
default: 'cyan',
|
|
primary: 'cyan',
|
|
success: 'lime',
|
|
warning: 'gold',
|
|
error: 'magenta',
|
|
secondary: 'magenta',
|
|
};
|
|
|
|
const actualVariant = variantMap[variant] || variant;
|
|
|
|
const styles: Record<string, string> = {
|
|
cyan: 'bg-muted/10 text-muted-foreground border-border/30',
|
|
magenta: 'bg-kodo-magenta/10 text-kodo-magenta border-kodo-magenta/30',
|
|
lime: 'bg-success/10 text-success border-success/30',
|
|
gold: 'bg-warning/10 text-warning border-warning/30',
|
|
terminal:
|
|
'bg-kodo-terminal/10 text-kodo-terminal border-kodo-terminal/30 font-mono',
|
|
};
|
|
|
|
const sizeStyles = {
|
|
sm: 'px-2 py-0.5 text-xs',
|
|
md: 'px-2.5 py-0.5 text-xs',
|
|
lg: 'px-4 py-1 text-xs',
|
|
};
|
|
|
|
const displayText = label || children;
|
|
const badgeVariant = actualVariant as keyof typeof styles;
|
|
|
|
return (
|
|
<span
|
|
ref={ref}
|
|
className={cn(
|
|
'inline-flex items-center gap-1.5 rounded-full font-bold uppercase tracking-widest border',
|
|
styles[badgeVariant] || styles.cyan,
|
|
sizeStyles[size],
|
|
className,
|
|
)}
|
|
{...props}
|
|
>
|
|
{dot && <span className="w-3 h-3 rounded-full bg-current" />}
|
|
{icon && <span className="w-3 h-3">{icon}</span>}
|
|
{displayText}
|
|
{count !== undefined && count > 0 && (
|
|
<span className="ml-1 px-1.5 py-0.5 rounded-full bg-current/20 text-xs">
|
|
{count}
|
|
</span>
|
|
)}
|
|
</span>
|
|
);
|
|
},
|
|
);
|
|
Badge.displayName = 'Badge';
|