refactor(ui): migrar componente Badge a HeroUI
This commit is contained in:
1 parent
ac1249084a
commit
78f0f6d50a
1 file changed
+67
-68
+67
-68
@@ -1,18 +1,49 @@
|
||||
"use client";
|
||||
import React from 'react';
|
||||
import { Chip as HeroUIChip, ChipProps as HeroUIChipProps } from '@heroui/react';
|
||||
import { tv } from '@heroui/styles';
|
||||
import { FiX } from 'react-icons/fi';
|
||||
|
||||
export interface BadgeProps {
|
||||
children: React.ReactNode;
|
||||
variant?:
|
||||
| 'default'
|
||||
| 'primary'
|
||||
| 'secondary'
|
||||
| 'success'
|
||||
| 'warning'
|
||||
| 'danger'
|
||||
| 'info'
|
||||
| 'outline';
|
||||
// 1. Organizamos los estilos con 'tv' usando variantes estándar
|
||||
const badgeStyles = tv({
|
||||
base: "inline-flex items-center select-none border transition-colors",
|
||||
variants: {
|
||||
customVariant: {
|
||||
default: 'bg-slate-100 text-slate-800 border-slate-200 dark:bg-slate-800/80 dark:text-slate-200 dark:border-slate-700',
|
||||
primary: 'bg-cyan-50 text-cyan-700 border-cyan-200 dark:bg-cyan-950/60 dark:text-cyan-300 dark:border-cyan-500/30',
|
||||
secondary: 'bg-purple-50 text-purple-700 border-purple-200 dark:bg-purple-950/60 dark:text-purple-300 dark:border-purple-500/30',
|
||||
success: 'bg-emerald-50 text-emerald-700 border-emerald-200 dark:bg-emerald-950/60 dark:text-emerald-300 dark:border-emerald-500/30',
|
||||
warning: 'bg-amber-50 text-amber-700 border-amber-200 dark:bg-amber-950/60 dark:text-amber-300 dark:border-amber-500/30',
|
||||
danger: 'bg-rose-50 text-rose-700 border-rose-200 dark:bg-rose-950/60 dark:text-rose-300 dark:border-rose-500/30',
|
||||
info: 'bg-sky-50 text-sky-700 border-sky-200 dark:bg-sky-950/60 dark:text-sky-300 dark:border-sky-500/30',
|
||||
outline: 'bg-transparent text-slate-700 border-slate-300 dark:text-slate-300 dark:border-slate-700',
|
||||
},
|
||||
customSize: {
|
||||
sm: 'text-[10px] px-2 py-0.5 gap-1 rounded-md font-semibold h-auto min-w-min',
|
||||
md: 'text-xs px-2.5 py-1 gap-1.5 rounded-lg font-medium h-auto min-w-min',
|
||||
lg: 'text-sm px-3 py-1.5 gap-2 rounded-xl font-medium h-auto min-w-min',
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
customVariant: "default",
|
||||
customSize: "md",
|
||||
}
|
||||
});
|
||||
|
||||
// Mapeo auxiliar de colores para los dots según la variante
|
||||
const dotColorMap: Record<NonNullable<BadgeProps['variant']>, string> = {
|
||||
default: 'bg-slate-500',
|
||||
primary: 'bg-cyan-500',
|
||||
secondary: 'bg-purple-500',
|
||||
success: 'bg-emerald-500',
|
||||
warning: 'bg-amber-500',
|
||||
danger: 'bg-rose-500',
|
||||
info: 'bg-sky-500',
|
||||
outline: 'bg-slate-400',
|
||||
};
|
||||
|
||||
export interface BadgeProps extends Omit<HeroUIChipProps, 'variant' | 'size' | 'color' | 'className' | 'children'> {
|
||||
variant?: 'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'outline';
|
||||
size?: 'sm' | 'md' | 'lg';
|
||||
dot?: boolean;
|
||||
dotPulse?: boolean;
|
||||
@@ -20,50 +51,12 @@ export interface BadgeProps {
|
||||
removable?: boolean;
|
||||
onRemove?: () => void;
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
const variantStyles: Record<NonNullable<BadgeProps['variant']>, { badge: string; dot: string }> = {
|
||||
default: {
|
||||
badge: 'bg-slate-100 text-slate-800 border-slate-200 dark:bg-slate-800/80 dark:text-slate-200 dark:border-slate-700',
|
||||
dot: 'bg-slate-500',
|
||||
},
|
||||
primary: {
|
||||
badge: 'bg-cyan-50 text-cyan-700 border-cyan-200 dark:bg-cyan-950/60 dark:text-cyan-300 dark:border-cyan-500/30',
|
||||
dot: 'bg-cyan-500',
|
||||
},
|
||||
secondary: {
|
||||
badge: 'bg-purple-50 text-purple-700 border-purple-200 dark:bg-purple-950/60 dark:text-purple-300 dark:border-purple-500/30',
|
||||
dot: 'bg-purple-500',
|
||||
},
|
||||
success: {
|
||||
badge: 'bg-emerald-50 text-emerald-700 border-emerald-200 dark:bg-emerald-950/60 dark:text-emerald-300 dark:border-emerald-500/30',
|
||||
dot: 'bg-emerald-500',
|
||||
},
|
||||
warning: {
|
||||
badge: 'bg-amber-50 text-amber-700 border-amber-200 dark:bg-amber-950/60 dark:text-amber-300 dark:border-amber-500/30',
|
||||
dot: 'bg-amber-500',
|
||||
},
|
||||
danger: {
|
||||
badge: 'bg-rose-50 text-rose-700 border-rose-200 dark:bg-rose-950/60 dark:text-rose-300 dark:border-rose-500/30',
|
||||
dot: 'bg-rose-500',
|
||||
},
|
||||
info: {
|
||||
badge: 'bg-sky-50 text-sky-700 border-sky-200 dark:bg-sky-950/60 dark:text-sky-300 dark:border-sky-500/30',
|
||||
dot: 'bg-sky-500',
|
||||
},
|
||||
outline: {
|
||||
badge: 'bg-transparent text-slate-700 border-slate-300 dark:text-slate-300 dark:border-slate-700',
|
||||
dot: 'bg-slate-400',
|
||||
},
|
||||
};
|
||||
|
||||
const sizeStyles = {
|
||||
sm: 'text-[10px] px-2 py-0.5 gap-1 rounded-md font-semibold',
|
||||
md: 'text-xs px-2.5 py-1 gap-1.5 rounded-lg font-medium',
|
||||
lg: 'text-sm px-3 py-1.5 gap-2 rounded-xl font-medium',
|
||||
};
|
||||
|
||||
export const Badge: React.FC<BadgeProps> = ({
|
||||
export const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(
|
||||
(
|
||||
{
|
||||
children,
|
||||
variant = 'default',
|
||||
size = 'md',
|
||||
@@ -73,27 +66,30 @@ export const Badge: React.FC<BadgeProps> = ({
|
||||
removable = false,
|
||||
onRemove,
|
||||
className = '',
|
||||
}) => {
|
||||
const currentVariant = variantStyles[variant];
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const dotColor = dotColorMap[variant];
|
||||
|
||||
return (
|
||||
<span
|
||||
className={`
|
||||
inline-flex items-center select-none border transition-colors
|
||||
${currentVariant.badge}
|
||||
${sizeStyles[size]}
|
||||
${className}
|
||||
`}
|
||||
<HeroUIChip
|
||||
ref={ref}
|
||||
// En HeroUI v3 se usa className en singular (soporta string directo desde tv)
|
||||
className={badgeStyles({
|
||||
customVariant: variant,
|
||||
customSize: size,
|
||||
className
|
||||
})}
|
||||
{...props}
|
||||
>
|
||||
{/* Optional Dot indicator */}
|
||||
{dot && (
|
||||
<span className="relative flex h-1.5 w-1.5 shrink-0">
|
||||
{dotPulse && (
|
||||
<span
|
||||
className={`animate-ping absolute inline-flex h-full w-full rounded-full opacity-75 ${currentVariant.dot}`}
|
||||
/>
|
||||
<span className={`animate-ping absolute inline-flex h-full w-full rounded-full opacity-75 ${dotColor}`} />
|
||||
)}
|
||||
<span className={`relative inline-flex rounded-full h-1.5 w-1.5 ${currentVariant.dot}`} />
|
||||
<span className={`relative inline-flex rounded-full h-1.5 w-1.5 ${dotColor}`} />
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -117,6 +113,9 @@ export const Badge: React.FC<BadgeProps> = ({
|
||||
<FiX className="text-xs" />
|
||||
</button>
|
||||
)}
|
||||
</span>
|
||||
</HeroUIChip>
|
||||
);
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
Badge.displayName = 'Badge';
|
||||
Reference in new issue
Block a user