Repository Public
1import * as React from 'react';2import type { Health } from '@cr/shared';3import { cn } from '@/lib/cn';45export type Tone = 'neutral' | 'accent' | 'success' | 'warning' | 'error' | 'muted' | 'coral';6const TONES: Record<Tone, string> = {7 neutral: 'border-transparent bg-surface-2 text-text-2',8 accent: 'border-transparent bg-accent/10 text-accent',9 success: 'border-transparent bg-success-vivid/12 text-success',10 warning: 'border-transparent bg-warning-vivid/15 text-warning',11 error: 'border-transparent bg-error-vivid/12 text-error',12 muted: 'border-line-2 bg-transparent text-muted',13 coral: 'border-transparent bg-coral/15 text-coral-ink',14};15export function Badge({ tone = 'neutral', className, children, ...props }: React.HTMLAttributes<HTMLSpanElement> & { tone?: Tone }) {16 return (17 <span className={cn('inline-flex h-5 shrink-0 items-center gap-1 whitespace-nowrap rounded-full border px-2 text-[11px] font-medium leading-none', TONES[tone], className)} {...props}>18 {children}19 </span>20 );21}2223const DOT: Record<Tone, string> = {24 neutral: 'bg-faint', accent: 'bg-accent', success: 'bg-success-vivid', warning: 'bg-warning-vivid', error: 'bg-error-vivid', muted: 'bg-faint', coral: 'bg-coral',25};26export function Dot({ tone, pulse, className, label }: { tone: Tone; pulse?: boolean; className?: string; label?: string }) {27 return <span role={label ? 'img' : undefined} aria-label={label} className={cn('inline-block size-2 shrink-0 rounded-full', DOT[tone], pulse && 'anim-pulse', className)} />;28}2930export const HEALTH_TONE: Record<Health, Exclude<Tone, 'coral'>> = { ONLINE: 'success', OFFLINE: 'error', DEGRADED: 'warning', UNKNOWN: 'muted' };31/** Health indicator. UNKNOWN is a hollow ring so it can never be mistaken for a live state. */32export function StatusDot({ health, className, withLabel }: { health: Health; className?: string; withLabel?: boolean }) {33 const dot =34 health === 'UNKNOWN' ? (35 <span role="img" aria-label="UNKNOWN" className={cn('inline-block size-2 shrink-0 rounded-full border-[1.5px] border-faint', className)} />36 ) : (37 <Dot tone={HEALTH_TONE[health]} label={health} className={className} />38 );39 if (!withLabel) return dot;40 return (41 <span className="inline-flex items-center gap-1.5">42 {dot}43 <span className={cn('text-[11px] font-medium tracking-wide', health === 'UNKNOWN' ? 'text-muted' : 'text-text-2')}>{health}</span>44 </span>45 );46}47