SPB Git

Repository Public

ring-gauge.tsx 1.7 KB 23 lines tsx

1import { cn } from '@/lib/cn';23/**4 * Arc gauge for a real measurement. `fraction === null` (never collected, or stale) renders an5 * empty dashed ring with "—": an unknown value is never drawn as a number.6 */7export function RingGauge({ fraction, label, size = 64, caption, dim }: { fraction: number | null; label: string; size?: number; caption?: string; dim?: boolean }) {8  const f = fraction == null || !Number.isFinite(fraction) ? null : Math.max(0, Math.min(1, fraction));9  const stroke = f == null ? 'var(--color-faint)' : f > 0.9 ? 'var(--color-error-vivid)' : f > 0.75 ? 'var(--color-warning-vivid)' : 'var(--color-accent)';10  return (11    <div className={cn('flex flex-col items-center gap-1', dim && 'opacity-60')} role="img" aria-label={`${label}: ${f == null ? 'unknown' : `${Math.round(f * 100)} percent`}`}>12      <div className="relative" style={{ width: size, height: size }}>13        <svg viewBox="0 0 36 36" className="size-full -rotate-90">14          <circle cx="18" cy="18" r="15" fill="none" stroke={f == null ? 'var(--color-line-3)' : 'var(--color-line-2)'} strokeWidth="3.4" strokeDasharray={f == null ? '2.2 3.4' : undefined} />15          {f != null && <circle cx="18" cy="18" r="15" fill="none" stroke={stroke} strokeWidth="3.4" strokeLinecap="round" pathLength={100} strokeDasharray={`${Math.max(f * 100, 1.5)} 100`} className="transition-[stroke-dasharray] duration-500" />}16        </svg>17        <span className="tnum absolute inset-0 flex items-center justify-center text-[13px] font-semibold text-text">{f == null ? '—' : `${Math.round(f * 100)}%`}</span>18      </div>19      <span className="text-[11px] font-medium text-muted">{caption ?? label}</span>20    </div>21  );22}23
Files main
Menu

Theme