Gráficos y Estadísticaschartsvgzero-dependencysunbursthierarchydonutbreakdown
Sunburst Chart
Dona jerárquica de dos niveles con categorías padre en el interior y sus hijos en el anillo exterior.
Vista Previa en Vivo
Service → resource
- Compute
- Storage
- Network
- Data
View data
| Group | Item | Value | Share |
|---|---|---|---|
| Compute | VMs | $4.2k | 26.3% |
| Compute | Functions | $1.8k | 11.3% |
| Compute | Containers | $2.6k | 16.3% |
| Storage | Object | $1.9k | 11.9% |
| Storage | Block | $1.1k | 6.9% |
| Network | CDN | $1.4k | 8.8% |
| Network | Egress | $900 | 5.6% |
| Data | Postgres | $1.6k | 10.0% |
| Data | Redis | $500 | 3.1% |
Uso
Copia y pega este código directamente en tu componente React:
tsx
import { SunburstChart } from 'react-component-kit-ui/charts';
<SunburstChart data={[
{ label: 'Compute', children: [{ label: 'VMs', value: 4200 }, { label: 'Functions', value: 1800 }] },
{ label: 'Storage', children: [{ label: 'Object', value: 1900 }] },
]} prefix="$" />Instalación Manual
Si no estás usando el paquete npm, crea un archivo nuevo y pega el siguiente código: src/components/SunburstChart.tsx + SunburstChart.css:
SunburstChart.tsx
'use client';
import ChartFrame from './ChartFrame';
import { arcPath, colorAt, formatValue, sum } from './chart-utils';
import './SunburstChart.css';
/**
* SunburstChart — Two-level hierarchical donut: inner ring for parents, outer
* ring for children (shaded variants of the parent hue).
*
* @param {Array<{label: string, children: Array<{label: string, value: number}>}>} data - Hierarchy.
* @param {string} centerLabel - Caption in the middle.
* @param {string} prefix - Value prefix.
* @param {string} suffix - Value suffix.
* @param {string[]} colors - Optional per-parent colors.
* @param {string} variant - Card style (glass|solid|ghost).
* @param {string} size - Density (sm|md|lg).
*/
export interface SunburstChartProps {
title?: string;
subtitle?: string;
data?: Array<{ label: string; children: Array<{ label: string; value: number }> }>;
centerLabel?: string;
prefix?: string;
suffix?: string;
colors?: string[];
variant?: 'glass' | 'solid' | 'ghost';
size?: 'sm' | 'md' | 'lg';
className?: string;
}
export function SunburstChart({
title = 'Cloud spend breakdown',
subtitle = 'Service → resource',
data = [
{ label: 'Compute', children: [{ label: 'VMs', value: 4200 }, { label: 'Functions', value: 1800 }, { label: 'Containers', value: 2600 }] },
{ label: 'Storage', children: [{ label: 'Object', value: 1900 }, { label: 'Block', value: 1100 }] },
{ label: 'Network', children: [{ label: 'CDN', value: 1400 }, { label: 'Egress', value: 900 }] },
{ label: 'Data', children: [{ label: 'Postgres', value: 1600 }, { label: 'Redis', value: 500 }] },
],
centerLabel = 'Monthly spend',
prefix = '$',
suffix = '',
colors,
variant = 'glass',
size = 'md',
className = '',
}: SunburstChartProps) {
const S = 300;
const c = S / 2;
const total = sum(data.flatMap((d) => d.children.map((ch) => ch.value))) || 1;
const GAP = 0.01;
let a = 0;
const parents = data.slice(0, 8).map((d, pi) => {
const pv = sum(d.children.map((ch) => ch.value));
const a0 = a;
const sweep = (pv / total) * Math.PI * 2;
a += sweep;
const color = colorAt(pi, colors);
let ca = a0;
const kids = d.children.map((ch, ci) => {
const s = (ch.value / total) * Math.PI * 2;
const k0 = ca;
ca += s;
const mix = 100 - ci * (40 / Math.max(1, d.children.length - 1));
return { ...ch, d: arcPath(c, c, 146, 104, k0 + GAP / 2, k0 + s - GAP / 2), color: `color-mix(in srgb, ${color} ${mix}%, #0d0d0f)` };
});
return { label: d.label, value: pv, color, d: arcPath(c, c, 100, 62, a0 + GAP / 2, a0 + sweep - GAP / 2), kids };
});
const fmt = (v: number) => formatValue(v, prefix, suffix);
const pct = (v: number) => `${((v / total) * 100).toFixed(1)}%`;
return (
<>
<ChartFrame title={title} subtitle={subtitle} variant={variant} size={size} className={className} legend={parents.map((p) => ({ label: p.label, color: p.color }))} table={{ headers: ['Group', 'Item', 'Value', 'Share'], rows: parents.flatMap((p) => p.kids.map((k) => [p.label, k.label, fmt(k.value), pct(k.value)])) }}>
<div className="sbt" data-rk-355acb="">
<svg viewBox={`0 0 ${S} ${S}`} role="img" aria-label={`${title}: sunburst chart, total ${fmt(total)}`} data-rk-355acb="">
{parents.map((p, __i: number) => (
<g key={__i} data-rk-355acb="">
<path className="cf-mark cf-dim" d={p.d} fill={p.color} tabIndex={0} role="img" aria-label={`${p.label}: ${fmt(p.value)} (${pct(p.value)})`} data-tip-title={p.label} data-tip={`${p.color}::${fmt(p.value)} · ${pct(p.value)}`} data-rk-355acb="" />
{p.kids.map((k, __i: number) => (
<path key={__i} className="cf-mark cf-dim" d={k.d} fill={k.color} tabIndex={0} role="img" aria-label={`${p.label} / ${k.label}: ${fmt(k.value)}`} data-tip-title={`${p.label} › ${k.label}`} data-tip={`${k.color}::${fmt(k.value)} · ${pct(k.value)}`} data-rk-355acb="" />
))}
</g>
))}
<text x={c} y={c + 2} textAnchor="middle" className="sbt__total" data-rk-355acb="">{fmt(total)}</text>
<text x={c} y={c + 20} textAnchor="middle" className="cf-label" data-rk-355acb="">{centerLabel}</text>
</svg>
</div>
</ChartFrame>
</>
);
}
export default SunburstChart;SunburstChart.css
/* SunburstChart — ported from astro-component-kit (charts/SunburstChart.astro) */
.sbt:where([data-rk-355acb]) { max-width: 340px; margin: 0 auto; }
.sbt__total:where([data-rk-355acb]) { fill: var(--c-text-1, #fff); font-size: 22px; font-weight: 700; font-variant-numeric: tabular-nums; }Información Rápida
- Categoría
- Gráficos y Estadísticas
- Nombre de archivo
SunburstChart.tsx- Dependencias
- Ninguna — React + CSS puro
- Etiquetas
- chartsvgzero-dependencysunbursthierarchydonutbreakdown