81 lines
2.4 KiB
TypeScript
81 lines
2.4 KiB
TypeScript
import type { CSSProperties } from 'react';
|
||
import { cn } from '../../lib/cn';
|
||
|
||
export interface ConfidenceRingProps {
|
||
/** Confidence 0–1. */
|
||
value?: number;
|
||
/** Diameter in px. @default 72 */
|
||
size?: number;
|
||
stroke?: number;
|
||
showLabel?: boolean;
|
||
/** Autonomy threshold. @default 0.7 */
|
||
threshold?: number;
|
||
className?: string;
|
||
style?: CSSProperties;
|
||
}
|
||
|
||
/**
|
||
* Confidence ring (0–1). ≥0.70 emerald "Autonomous", <0.70 amber "Escalated".
|
||
* The signature trust signal across Aria's AI Decision Cards.
|
||
*/
|
||
export function ConfidenceRing({
|
||
value = 0,
|
||
size = 72,
|
||
stroke = 7,
|
||
showLabel = true,
|
||
threshold = 0.7,
|
||
className,
|
||
style,
|
||
}: ConfidenceRingProps) {
|
||
const v = Math.max(0, Math.min(1, value));
|
||
const autonomous = v >= threshold;
|
||
const color = autonomous ? 'var(--status-autonomous)' : 'var(--status-escalated)';
|
||
const r = (size - stroke) / 2;
|
||
const circ = 2 * Math.PI * r;
|
||
const dash = circ * v;
|
||
const pct = Math.round(v * 100);
|
||
|
||
return (
|
||
<div
|
||
className={cn('inline-flex flex-col items-center gap-1.5 font-sans', className)}
|
||
style={style}
|
||
>
|
||
<div className="relative" style={{ width: size, height: size }}>
|
||
<svg width={size} height={size} style={{ transform: 'rotate(-90deg)' }}>
|
||
<circle cx={size / 2} cy={size / 2} r={r} fill="none" stroke="var(--surface-sunk)" strokeWidth={stroke} />
|
||
<circle
|
||
cx={size / 2}
|
||
cy={size / 2}
|
||
r={r}
|
||
fill="none"
|
||
stroke={color}
|
||
strokeWidth={stroke}
|
||
strokeLinecap="round"
|
||
strokeDasharray={`${dash} ${circ}`}
|
||
style={{ transition: 'stroke-dasharray .6s cubic-bezier(.22,1,.36,1)' }}
|
||
/>
|
||
</svg>
|
||
<div className="absolute inset-0 flex flex-col items-center justify-center">
|
||
<span
|
||
className="font-numeric font-extrabold text-strong leading-none nums"
|
||
style={{ fontSize: size * 0.3 }}
|
||
>
|
||
{pct}
|
||
</span>
|
||
<span className="text-faint font-semibold" style={{ fontSize: size * 0.12 }}>
|
||
%
|
||
</span>
|
||
</div>
|
||
</div>
|
||
{showLabel && (
|
||
<span
|
||
className="text-2xs font-bold tracking-[0.04em] uppercase"
|
||
style={{ color }}
|
||
>
|
||
{autonomous ? 'Autonomous' : 'Escalated'}
|
||
</span>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|