データ表示
Progress Ring
SVGで描画する円形プログレスインジケーター。
インストール
npx shadcn add https://d-ui.daigo-suhara.com/registry/progress-ring.jsonサンプル
72%
使い方
import { ProgressRing } from "@/components/ui/progress-ring";
export default function Example() {
return <ProgressRing value={72} color="success" size={100} />;
}プロパティ
value必須number現在値
max任意number最大値
デフォルト:
100size任意number直径(px)
デフォルト:
80strokeWidth任意number線の太さ
デフォルト:
8color任意"primary" | "success" | "warning" | "danger"色
デフォルト:
"primary"showLabel任意boolean中央にパーセントを表示
デフォルト:
truelabel必須stringカスタムラベル
| 名前 | 型 | デフォルト | 説明 | |
|---|---|---|---|---|
| 必須 | value | number | — | 現在値 |
| 任意 | max | number | 100 | 最大値 |
| 任意 | size | number | 80 | 直径(px) |
| 任意 | strokeWidth | number | 8 | 線の太さ |
| 任意 | color | "primary" | "success" | "warning" | "danger" | "primary" | 色 |
| 任意 | showLabel | boolean | true | 中央にパーセントを表示 |
| 必須 | label | string | — | カスタムラベル |
ソースコード
import * as React from "react";
import { cn } from "@/lib/utils";
interface ProgressRingProps {
value: number;
max?: number;
size?: number;
strokeWidth?: number;
color?: "primary" | "success" | "warning" | "danger" | string;
showLabel?: boolean;
label?: string;
className?: string;
}
const colorMap: Record<string, string> = {
primary: "stroke-primary",
success: "stroke-emerald-500",
warning: "stroke-amber-500",
danger: "stroke-red-500",
};
export function ProgressRing({
value,
max = 100,
size = 80,
strokeWidth = 8,
color = "primary",
showLabel = true,
label,
className,
}: ProgressRingProps) {
const pct = Math.min(Math.max(value / max, 0), 1);
const radius = (size - strokeWidth) / 2;
const circumference = 2 * Math.PI * radius;
const offset = circumference * (1 - pct);
const colorClass = colorMap[color] ?? "";
const inlineColor = colorMap[color] ? undefined : color;
return (
<div
className={cn("relative inline-flex items-center justify-center", className)}
style={{ width: size, height: size }}
>
<svg
width={size}
height={size}
viewBox={`0 0 ${size} ${size}`}
className="-rotate-90"
>
{/* Track */}
<circle
cx={size / 2}
cy={size / 2}
r={radius}
fill="none"
strokeWidth={strokeWidth}
className="stroke-muted"
/>
{/* Progress */}
<circle
cx={size / 2}
cy={size / 2}
r={radius}
fill="none"
strokeWidth={strokeWidth}
strokeDasharray={circumference}
strokeDashoffset={offset}
strokeLinecap="round"
className={cn("transition-all duration-700", colorClass)}
style={inlineColor ? { stroke: inlineColor } : undefined}
/>
</svg>
{showLabel && (
<div className="absolute inset-0 flex flex-col items-center justify-center">
<span className="text-sm font-semibold tabular-nums leading-none">
{label ?? `${Math.round(pct * 100)}%`}
</span>
</div>
)}
</div>
);
}