← Components/Data Visualization

DonutChart

donutchart-1779379671725.tsx
'use client'
import { useState } from 'react'

const SEGMENTS = [
  { label: 'Buttons', value: 12, color: '#C9A84C' },
  { label: 'Forms', value: 10, color: '#6366f1' },
  { label: 'Navigation', value: 8, color: '#22c55e' },
  { label: 'Dashboard', value: 7, color: '#f59e0b' },
  { label: 'Charts', value: 6, color: '#ec4899' },
  { label: 'Other', value: 77, color: '#334155' },
]

export default function DonutChart() {
  const [hovered, setHovered] = useState<number | null>(null)

  const total = SEGMENTS.reduce((a, s) => a + s.value, 0)
  const CX = 100, CY = 100, R = 70, r = 42

  let angle = -90
  const paths = SEGMENTS.map((seg, i) => {
    const pct = seg.value / total
    const startAngle = angle
    angle += pct * 360
    const endAngle = angle

    const a1 = (startAngle * Math.PI) / 180
    const a2 = (endAngle * Math.PI) / 180
    const x1 = CX + R * Math.cos(a1), y1 = CY + R * Math.sin(a1)
    const x2 = CX + R * Math.cos(a2), y2 = CY + R * Math.sin(a2)
    const ix1 = CX + r * Math.cos(a1), iy1 = CY + r * Math.sin(a1)
    const ix2 = CX + r * Math.cos(a2), iy2 = CY + r * Math.sin(a2)
    const large = pct > 0.5 ? 1 : 0

    return {
      d: `M ${x1} ${y1} A ${R} ${R} 0 ${large} 1 ${x2} ${y2} L ${ix2} ${iy2} A ${r} ${r} 0 ${large} 0 ${ix1} ${iy1} Z`,
      mid: startAngle + pct * 180,
      ...seg, i,
    }
  })

  const hov = hovered !== null ? SEGMENTS[hovered] : null

  return (
    <div style={{ background: '#0D0D0D', border: '1px solid rgba(255,255,255,0.06)', borderRadius: 16, padding: 24, display: 'flex', gap: 24, flexWrap: 'wrap' }}>
      <div style={{ position: 'relative', width: 200, flexShrink: 0 }}>
        <svg viewBox="0 0 200 200" width={200} height={200}>
          {paths.map((p, i) => {
            const scale = hovered === i ? 1.04 : 1
            return (
              <path key={i} d={p.d} fill={p.color} opacity={hovered === null || hovered === i ? 1 : 0.4}
                style={{ transformOrigin: '100px 100px', transform: `scale(${scale})`, transition: 'all 0.15s', cursor: 'pointer' }}
                onMouseEnter={() => setHovered(i)} onMouseLeave={() => setHovered(null)} />
            )
          })}
          {/* Center */}
          <text x={CX} y={CY - 6} textAnchor="middle" fontSize={hov ? 22 : 26} fontWeight={800} fill="#F5F5F0">
            {hov ? hov.value : total}
          </text>
          <text x={CX} y={CY + 14} textAnchor="middle" fontSize={10} fill="rgba(255,255,255,0.4)">
            {hov ? hov.label : 'components'}
          </text>
          {hov && (
            <text x={CX} y={CY + 28} textAnchor="middle" fontSize={10} fill={hov.color}>
              {((hov.value / total) * 100).toFixed(1)}%
            </text>
          )}
        </svg>
      </div>

      <div style={{ flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: 8 }}>
        <div style={{ color: '#F5F5F0', fontSize: 14, fontWeight: 700, marginBottom: 4 }}>By Category</div>
        {SEGMENTS.map((seg, i) => (
          <div key={i} onMouseEnter={() => setHovered(i)} onMouseLeave={() => setHovered(null)}
            style={{ display: 'flex', alignItems: 'center', gap: 8, cursor: 'pointer', opacity: hovered === null || hovered === i ? 1 : 0.4, transition: 'opacity 0.15s' }}>
            <div style={{ width: 10, height: 10, borderRadius: 2, background: seg.color, flexShrink: 0 }} />
            <div style={{ flex: 1, color: 'rgba(255,255,255,0.7)', fontSize: 12 }}>{seg.label}</div>
            <div style={{ color: '#F5F5F0', fontSize: 12, fontWeight: 600 }}>{seg.value}</div>
            <div style={{ color: seg.color, fontSize: 11 }}>{((seg.value / total) * 100).toFixed(0)}%</div>
          </div>
        ))}
      </div>
    </div>
  )
}

Component info

CategoryData Visualization
Frameworkreact
TierFREE
Views0
Copies0

About

Interactive SVG donut chart with hover segments, center stats, legend, and animated draw

More from Data Visualization

'use client'
import { useEffect, useState } from 'react'

interface Metric {
  label: string
  value: number
  max: number
  color: string
  unit: string
}

const metrics: Metric[] = [
  { label: 'CPU Usage', value: 72, max: 100, color: '#C9A84C', un
RadialProgress
Data Visualization