80 lines
2.4 KiB
TypeScript
80 lines
2.4 KiB
TypeScript
import React from 'react';
|
|
import { Box } from './primitives/Box';
|
|
import { Text } from './Text';
|
|
|
|
export interface CircularProgressProps {
|
|
value: number;
|
|
max?: number;
|
|
size?: number;
|
|
strokeWidth?: number;
|
|
intent?: 'primary' | 'success' | 'warning' | 'critical' | 'telemetry';
|
|
showValue?: boolean;
|
|
label?: string;
|
|
color?: string; // Alias for intent
|
|
}
|
|
|
|
export const CircularProgress = ({
|
|
value,
|
|
max = 100,
|
|
size = 64,
|
|
strokeWidth = 4,
|
|
intent = 'primary',
|
|
showValue = false,
|
|
label,
|
|
color: colorProp
|
|
}: CircularProgressProps) => {
|
|
const radius = (size - strokeWidth) / 2;
|
|
const circumference = radius * 2 * Math.PI;
|
|
const offset = circumference - (value / max) * circumference;
|
|
|
|
const intentColorMap = {
|
|
primary: 'var(--ui-color-intent-primary)',
|
|
success: 'var(--ui-color-intent-success)',
|
|
warning: 'var(--ui-color-intent-warning)',
|
|
critical: 'var(--ui-color-intent-critical)',
|
|
telemetry: 'var(--ui-color-intent-telemetry)',
|
|
};
|
|
|
|
const color = colorProp || intentColorMap[intent];
|
|
|
|
return (
|
|
<Box display="flex" flexDirection="col" alignItems="center" gap={2}>
|
|
<Box position="relative" width={size} height={size} display="flex" alignItems="center" justifyContent="center">
|
|
<svg width={size} height={size} className="transform -rotate-90">
|
|
<circle
|
|
cx={size / 2}
|
|
cy={size / 2}
|
|
r={radius}
|
|
stroke="var(--ui-color-bg-surface-muted)"
|
|
strokeWidth={strokeWidth}
|
|
fill="transparent"
|
|
/>
|
|
<circle
|
|
cx={size / 2}
|
|
cy={size / 2}
|
|
r={radius}
|
|
stroke={color}
|
|
strokeWidth={strokeWidth}
|
|
fill="transparent"
|
|
strokeDasharray={circumference}
|
|
style={{ strokeDashoffset: offset, transition: 'stroke-dashoffset 0.3s ease-in-out' }}
|
|
strokeLinecap="round"
|
|
/>
|
|
</svg>
|
|
{showValue && (
|
|
<Box position="absolute" inset={0} display="flex" alignItems="center" justifyContent="center">
|
|
<Text size="xs" weight="bold" variant="high">
|
|
{Math.round((value / max) * 100)}%
|
|
</Text>
|
|
</Box>
|
|
)}
|
|
</Box>
|
|
{label && (
|
|
<Text size="xs" weight="bold" variant="low" uppercase letterSpacing="wider">
|
|
{label}
|
|
</Text>
|
|
)}
|
|
</Box>
|
|
);
|
|
};
|