1 estrella en GitHub = 1 componente nuevo.

Dar una estrella
Gráficos y Estadísticaschartsvgzero-dependencysegmentedpart-to-wholestoragebreakdown

Segmented Bar Chart

Una barra segmentada de parte-del-todo con etiquetas y desglose; más clara que una tarta.

Vista Previa en Vivo

Storage usage

512 GB plan

402 GB
45%24%16%9%
  • Media182 GB
  • Backups96 GB
  • Databases64 GB
  • Logs38 GB
  • Other22 GB
View data
SegmentValueShare
Media182 GB45.3%
Backups96 GB23.9%
Databases64 GB15.9%
Logs38 GB9.5%
Other22 GB5.5%

Uso

Copia y pega este código directamente en tu componente React:

tsx
import { SegmentedBarChart } from 'react-component-kit-ui/charts';

<SegmentedBarChart segments={[
    { label: 'Media', value: 182 },
    { label: 'Backups', value: 96 },
    { label: 'Other', value: 22 },
  ]} suffix=" GB" />

Instalación Manual

Si no estás usando el paquete npm, crea un archivo nuevo y pega el siguiente código: src/components/SegmentedBarChart.tsx + SegmentedBarChart.css:

SegmentedBarChart.tsx
'use client';

import type { CSSProperties } from 'react';
import ChartFrame from './ChartFrame';
import { colorAt, formatValue, sum } from './chart-utils';
import './SegmentedBarChart.css';

/** "a: b; --c: d" → style object */
function st(css: string | undefined): CSSProperties {
  const o: Record<string, string> = {};
  for (const decl of String(css ?? '').split(';')) {
    const i = decl.indexOf(':');
    if (i < 0) continue;
    const k = decl.slice(0, i).trim();
    if (k) o[k.startsWith('--') ? k : k.replace(/-([a-z])/g, (_, c) => c.toUpperCase())] = decl.slice(i + 1).trim();
  }
  return o as CSSProperties;
}

/**
 * SegmentedBarChart — A single tall part-to-whole bar with labelled segments
 * and a breakdown list. A clearer alternative to a pie for ≤ 6 parts.
 *
 * @param {Array<{label: string, value: number}>} segments - Parts of the whole.
 * @param {string} total - Optional formatted total for the header.
 * @param {string} prefix - Value prefix.
 * @param {string} suffix - Value suffix.
 * @param {string[]} colors - Optional per-segment colors.
 * @param {string} variant - Card style (glass|solid|ghost).
 * @param {string} size - Density (sm|md|lg).
 */
export interface SegmentedBarChartProps {
  title?: string;
  subtitle?: string;
  segments?: Array<{ label: string; value: number }>;
  total?: string;
  prefix?: string;
  suffix?: string;
  colors?: string[];
  variant?: 'glass' | 'solid' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
  className?: string;
}

export function SegmentedBarChart({
  title = 'Storage usage',
  subtitle = '512 GB plan',
  segments = [
    { label: 'Media', value: 182 },
    { label: 'Backups', value: 96 },
    { label: 'Databases', value: 64 },
    { label: 'Logs', value: 38 },
    { label: 'Other', value: 22 },
  ],
  total,
  prefix = '',
  suffix = ' GB',
  colors,
  variant = 'glass',
  size = 'md',
  className = '',
}: SegmentedBarChartProps) {
  const tot = sum(segments.map((s) => s.value)) || 1;
  const fmt = (v: number) => formatValue(v, prefix, suffix, false);
  const segs = segments.slice(0, 8).map((s, i) => ({ ...s, pct: (s.value / tot) * 100, color: colorAt(i, colors) }));
  return (
    <>
      <ChartFrame title={title} subtitle={subtitle} value={total ?? fmt(tot)} variant={variant} size={size} className={className} table={{ headers: ['Segment', 'Value', 'Share'], rows: segs.map((s) => [s.label, fmt(s.value), `${s.pct.toFixed(1)}%`]) }}>
        <div className="sb__bar" role="list" aria-label={`${title} breakdown`} data-rk-740040="">
          {segs.map((s, __i: number) => (
            <span key={__i} className="sb__seg cf-mark" role="listitem" tabIndex={0} style={st(`flex-grow:${s.value}; background:${s.color}`)} aria-label={`${s.label}: ${fmt(s.value)} (${s.pct.toFixed(1)}%)`} data-tip-title={s.label} data-tip={`${s.color}::${fmt(s.value)} · ${s.pct.toFixed(1)}%`} data-rk-740040="">
              {s.pct >= 9 && <span className="sb__pct" data-rk-740040="">{s.pct.toFixed(0)}%</span>}
            </span>
          ))}
        </div>
        <ul className="sb__list" data-rk-740040="">
          {segs.map((s, __i: number) => (
            <li key={__i} data-rk-740040="">
              <span className="sb__dot" style={st(`background:${s.color}`)} aria-hidden="true" data-rk-740040=""></span>
              <span className="sb__name" data-rk-740040="">{s.label}</span>
              <span className="sb__val" data-rk-740040="">{fmt(s.value)}</span>
            </li>
          ))}
        </ul>
      </ChartFrame>
    </>
  );
}

export default SegmentedBarChart;
SegmentedBarChart.css
/* SegmentedBarChart — ported from astro-component-kit (charts/SegmentedBarChart.astro) */

.sb__bar:where([data-rk-740040]) {
  display: flex;
  gap: 2px;
  height: 44px;
  border-radius: 8px;
  overflow: hidden;
}
.sb__seg:where([data-rk-740040]) {
  flex-basis: 0;
  min-width: 4px;
  display: flex;
  align-items: center;
  padding-left: 0.6rem;
  transition: filter 0.2s ease;
}
.sb__seg:hover:where([data-rk-740040]) { filter: brightness(1.15); }
.sb__seg:focus-visible:where([data-rk-740040]) { outline: 2px solid var(--c-text-1, #fff); outline-offset: -2px; }
.sb__pct:where([data-rk-740040]) {
  font-size: 0.75rem;
  font-weight: 700;
  color: #fff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.45);
}
.sb__list:where([data-rk-740040]) {
  list-style: none;
  margin: 1rem 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 0.6rem 1.25rem;
}
.sb__list:where([data-rk-740040]) li:where([data-rk-740040]) { display: flex; align-items: center; gap: 0.5rem; font-size: 0.8125rem; }
.sb__dot:where([data-rk-740040]) { width: 10px; height: 10px; border-radius: 3px; flex-shrink: 0; }
.sb__name:where([data-rk-740040]) { color: var(--c-text-2, #a3a3a3); }
.sb__val:where([data-rk-740040]) { margin-left: auto; color: var(--c-text-1, #fff); font-weight: 600; font-variant-numeric: tabular-nums; }

Información Rápida

Categoría
Gráficos y Estadísticas
Nombre de archivo
SegmentedBarChart.tsx
Dependencias
Ninguna — React + CSS puro
Etiquetas
chartsvgzero-dependencysegmentedpart-to-wholestoragebreakdown