| 4dfd09b | | | 1 | const presets: Record<string, { bg: string; text: string; border: string }> = { |
| 4dfd09b | | | 2 | open: { |
| bf6031c | | | 3 | bg: "var(--status-open-bg)", |
| bf6031c | | | 4 | text: "var(--status-open-text)", |
| bf6031c | | | 5 | border: "var(--status-open-border)", |
| 4dfd09b | | | 6 | }, |
| 4dfd09b | | | 7 | active: { |
| bf6031c | | | 8 | bg: "var(--status-open-bg)", |
| bf6031c | | | 9 | text: "var(--status-open-text)", |
| bf6031c | | | 10 | border: "var(--status-open-border)", |
| 4dfd09b | | | 11 | }, |
| 4dfd09b | | | 12 | running: { |
| bf6031c | | | 13 | bg: "var(--status-merged-bg)", |
| bf6031c | | | 14 | text: "var(--status-merged-text)", |
| bf6031c | | | 15 | border: "var(--status-merged-border)", |
| 4dfd09b | | | 16 | }, |
| 4dfd09b | | | 17 | merged: { |
| bf6031c | | | 18 | bg: "var(--status-merged-bg)", |
| bf6031c | | | 19 | text: "var(--status-merged-text)", |
| bf6031c | | | 20 | border: "var(--status-merged-border)", |
| 4dfd09b | | | 21 | }, |
| 4dfd09b | | | 22 | creating: { |
| bf6031c | | | 23 | bg: "var(--status-merged-bg)", |
| bf6031c | | | 24 | text: "var(--status-merged-text)", |
| bf6031c | | | 25 | border: "var(--status-merged-border)", |
| 4dfd09b | | | 26 | }, |
| 4dfd09b | | | 27 | passed: { |
| bf6031c | | | 28 | bg: "var(--status-open-bg)", |
| bf6031c | | | 29 | text: "var(--status-open-text)", |
| bf6031c | | | 30 | border: "var(--status-open-border)", |
| 4dfd09b | | | 31 | }, |
| 4dfd09b | | | 32 | closed: { |
| bf6031c | | | 33 | bg: "var(--status-closed-bg)", |
| bf6031c | | | 34 | text: "var(--status-closed-text)", |
| bf6031c | | | 35 | border: "var(--status-closed-border)", |
| 4dfd09b | | | 36 | }, |
| 4dfd09b | | | 37 | failed: { |
| bf6031c | | | 38 | bg: "var(--status-closed-bg)", |
| bf6031c | | | 39 | text: "var(--status-closed-text)", |
| bf6031c | | | 40 | border: "var(--status-closed-border)", |
| 4dfd09b | | | 41 | }, |
| 4dfd09b | | | 42 | error: { |
| bf6031c | | | 43 | bg: "var(--status-closed-bg)", |
| bf6031c | | | 44 | text: "var(--status-closed-text)", |
| bf6031c | | | 45 | border: "var(--status-closed-border)", |
| 4dfd09b | | | 46 | }, |
| 4dfd09b | | | 47 | pending: { |
| 4dfd09b | | | 48 | bg: "var(--bg-inset)", |
| 4dfd09b | | | 49 | text: "var(--text-faint)", |
| 4dfd09b | | | 50 | border: "var(--border)", |
| 4dfd09b | | | 51 | }, |
| 4dfd09b | | | 52 | cancelled: { |
| 4dfd09b | | | 53 | bg: "var(--bg-inset)", |
| 4dfd09b | | | 54 | text: "var(--text-faint)", |
| 4dfd09b | | | 55 | border: "var(--border)", |
| 4dfd09b | | | 56 | }, |
| 4dfd09b | | | 57 | neutral: { |
| 4dfd09b | | | 58 | bg: "var(--bg-inset)", |
| 4dfd09b | | | 59 | text: "var(--text-muted)", |
| 4dfd09b | | | 60 | border: "var(--border)", |
| 4dfd09b | | | 61 | }, |
| 4dfd09b | | | 62 | accent: { |
| 4dfd09b | | | 63 | bg: "var(--accent-subtle)", |
| 4dfd09b | | | 64 | text: "var(--accent)", |
| 4dfd09b | | | 65 | border: "var(--accent-subtle)", |
| 4dfd09b | | | 66 | }, |
| 4dfd09b | | | 67 | }; |
| 4dfd09b | | | 68 | |
| 4dfd09b | | | 69 | export interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> { |
| 4dfd09b | | | 70 | variant?: string; |
| 4dfd09b | | | 71 | colors?: { bg: string; text: string; border?: string }; |
| 4dfd09b | | | 72 | pill?: boolean; |
| 8a2c7d4 | | | 73 | compact?: boolean; |
| 4dfd09b | | | 74 | } |
| 4dfd09b | | | 75 | |
| 4dfd09b | | | 76 | export function Badge({ |
| 4dfd09b | | | 77 | variant, |
| 4dfd09b | | | 78 | colors, |
| 4dfd09b | | | 79 | pill = false, |
| 8a2c7d4 | | | 80 | compact = false, |
| 4dfd09b | | | 81 | className = "", |
| 4dfd09b | | | 82 | style, |
| 4dfd09b | | | 83 | children, |
| 4dfd09b | | | 84 | ...props |
| 4dfd09b | | | 85 | }: BadgeProps) { |
| 4dfd09b | | | 86 | const preset = variant ? presets[variant] : undefined; |
| 4dfd09b | | | 87 | const c = colors ?? preset ?? presets.neutral; |
| 4dfd09b | | | 88 | |
| a011f1e | | | 89 | const isRunning = variant === "running" || variant === "creating"; |
| a011f1e | | | 90 | |
| 8a2c7d4 | | | 91 | const compactStyles = compact |
| 8a2c7d4 | | | 92 | ? { width: "1.25rem", textAlign: "center" as const, padding: "0.125rem 0" } |
| 8a2c7d4 | | | 93 | : {}; |
| 8a2c7d4 | | | 94 | |
| f406a0f | | | 95 | const baseStyle = { |
| f406a0f | | | 96 | backgroundColor: c.bg, |
| f406a0f | | | 97 | color: c.text, |
| bf6031c | | | 98 | border: `1px solid ${c.border ?? c.bg}`, |
| f406a0f | | | 99 | borderRadius: "9999px", |
| f406a0f | | | 100 | textTransform: "uppercase" as const, |
| f406a0f | | | 101 | fontFamily: "system-ui, -apple-system, sans-serif", |
| f406a0f | | | 102 | letterSpacing: "0.04em", |
| f406a0f | | | 103 | ...compactStyles, |
| f406a0f | | | 104 | ...style, |
| f406a0f | | | 105 | }; |
| f406a0f | | | 106 | |
| a011f1e | | | 107 | if (isRunning) { |
| a011f1e | | | 108 | return ( |
| a011f1e | | | 109 | <span |
| 7deb6c8 | | | 110 | className={`badge-running-text-pulse text-xs font-bold ${compact ? "" : "px-1.5"} py-0.5 inline-block ${className}`} |
| f406a0f | | | 111 | style={baseStyle} |
| a011f1e | | | 112 | {...props} |
| a011f1e | | | 113 | > |
| b2bd123 | | | 114 | {children} |
| a011f1e | | | 115 | </span> |
| a011f1e | | | 116 | ); |
| a011f1e | | | 117 | } |
| a011f1e | | | 118 | |
| 4dfd09b | | | 119 | return ( |
| 4dfd09b | | | 120 | <span |
| 7deb6c8 | | | 121 | className={`text-xs font-bold ${compact ? "" : "px-1.5"} py-0.5 inline-block ${className}`} |
| f406a0f | | | 122 | style={baseStyle} |
| 4dfd09b | | | 123 | {...props} |
| 4dfd09b | | | 124 | > |
| 4dfd09b | | | 125 | {children} |
| 4dfd09b | | | 126 | </span> |
| 4dfd09b | | | 127 | ); |
| 4dfd09b | | | 128 | } |