54 lines
1.2 KiB
TypeScript
54 lines
1.2 KiB
TypeScript
|
|
/* StatusLight — 8px colored square indicator.
|
||
|
|
*
|
||
|
|
* The defining geometry of /status: square, not circle, faintly
|
||
|
|
* outlined so it reads on white. Five states map to the brand
|
||
|
|
* status palette. Optional pulse is for "in flight" / "running" rows. */
|
||
|
|
|
||
|
|
import { cn } from '@/lib/utils';
|
||
|
|
|
||
|
|
export type StatusLightState = 'ok' | 'warn' | 'error' | 'idle' | 'pending';
|
||
|
|
|
||
|
|
interface StatusLightProps {
|
||
|
|
state: StatusLightState;
|
||
|
|
size?: 'sm' | 'md' | 'lg';
|
||
|
|
pulse?: boolean;
|
||
|
|
label?: string;
|
||
|
|
className?: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
const sizeMap = {
|
||
|
|
sm: 'h-1.5 w-1.5',
|
||
|
|
md: 'h-2 w-2',
|
||
|
|
lg: 'h-3 w-3',
|
||
|
|
} as const;
|
||
|
|
|
||
|
|
const stateMap: Record<StatusLightState, string> = {
|
||
|
|
ok: 'bg-emerald-500',
|
||
|
|
warn: 'bg-amber-500',
|
||
|
|
error: 'bg-destructive',
|
||
|
|
idle: 'bg-muted-foreground/40',
|
||
|
|
pending: 'bg-primary',
|
||
|
|
};
|
||
|
|
|
||
|
|
export function StatusLight({
|
||
|
|
state,
|
||
|
|
size = 'md',
|
||
|
|
pulse = false,
|
||
|
|
label,
|
||
|
|
className,
|
||
|
|
}: StatusLightProps) {
|
||
|
|
return (
|
||
|
|
<span
|
||
|
|
role="status"
|
||
|
|
aria-label={label ?? state}
|
||
|
|
className={cn(
|
||
|
|
'inline-block ring-1 ring-foreground/10 align-middle',
|
||
|
|
sizeMap[size],
|
||
|
|
stateMap[state],
|
||
|
|
pulse && state === 'pending' && 'animate-pulse',
|
||
|
|
className,
|
||
|
|
)}
|
||
|
|
/>
|
||
|
|
);
|
||
|
|
}
|