92 lines
2.1 KiB
TypeScript
92 lines
2.1 KiB
TypeScript
import type { ReactNode } from "react";
|
|
|
|
export type BadgeVariant =
|
|
| "success"
|
|
| "warning"
|
|
| "danger"
|
|
| "info"
|
|
| "neutral";
|
|
export type BadgeSize = "sm" | "md";
|
|
|
|
export interface BadgeProps {
|
|
variant: BadgeVariant;
|
|
size?: BadgeSize;
|
|
icon?: ReactNode;
|
|
children: ReactNode;
|
|
}
|
|
|
|
const VARIANT_STYLES: Record<
|
|
BadgeVariant,
|
|
{ background: string; color: string; border: string }
|
|
> = {
|
|
success: {
|
|
background: "var(--success-soft)",
|
|
color: "var(--success)",
|
|
border: "transparent",
|
|
},
|
|
warning: {
|
|
background: "var(--warn-soft)",
|
|
color: "var(--warn)",
|
|
border: "transparent",
|
|
},
|
|
danger: {
|
|
background: "var(--danger-soft)",
|
|
color: "var(--danger)",
|
|
border: "transparent",
|
|
},
|
|
info: {
|
|
background: "var(--accent-soft)",
|
|
color: "var(--accent)",
|
|
border: "transparent",
|
|
},
|
|
neutral: {
|
|
background: "var(--bg-card-alt)",
|
|
color: "var(--fg-secondary)",
|
|
border: "var(--border-card)",
|
|
},
|
|
};
|
|
|
|
const SIZE_STYLES: Record<
|
|
BadgeSize,
|
|
{ fontSize: number; padding: string; gap: number }
|
|
> = {
|
|
sm: { fontSize: 11, padding: "2px 6px", gap: 3 },
|
|
md: { fontSize: 12, padding: "3px 8px", gap: 4 },
|
|
};
|
|
|
|
/**
|
|
* Badge — inline-flex semantic badge with variants aligned to design tokens.
|
|
* Variants: success | warning | danger | info | neutral.
|
|
* Icon slot accepts any ReactNode (use Lucide icons, size 12-14px).
|
|
*/
|
|
export function Badge({ variant, size = "md", icon, children }: BadgeProps) {
|
|
const vs = VARIANT_STYLES[variant];
|
|
const ss = SIZE_STYLES[size];
|
|
|
|
return (
|
|
<span
|
|
style={{
|
|
display: "inline-flex",
|
|
alignItems: "center",
|
|
gap: ss.gap,
|
|
background: vs.background,
|
|
color: vs.color,
|
|
border: `1px solid ${vs.border}`,
|
|
borderRadius: 6,
|
|
padding: ss.padding,
|
|
fontSize: ss.fontSize,
|
|
fontWeight: 500,
|
|
lineHeight: 1.4,
|
|
whiteSpace: "nowrap",
|
|
fontVariantNumeric: "tabular-nums",
|
|
}}
|
|
>
|
|
{icon && (
|
|
<span style={{ display: "flex", alignItems: "center", flexShrink: 0 }}>
|
|
{icon}
|
|
</span>
|
|
)}
|
|
{children}
|
|
</span>
|
|
);
|
|
}
|