Ir al contenido

Gráficos

Claritas separa los gráficos en dos grupos, y la diferencia importa a la hora de instalar.

Dónde vive Dependencias
Chart @openfactu/ui/charts recharts (peer dependency opcional)
Sparkline, Ring, StackedBar @openfactu/ui Ninguna: son SVG propio

Un solo componente para los cuatro tipos: línea, área, barras y donut. Absorbe el contenedor responsivo, el tooltip, los ejes y la rejilla derivados del tema, el formato de miles y el estado vacío.

import { Chart } from '@openfactu/ui/charts';
import { Card } from '@openfactu/ui';
const money = (v) => `${v.toLocaleString('es-ES')}`;
<Card title="Tendencia" subtitle="Ventas frente a compras, últimos 12 meses">
<Chart
type="line"
data={tendencia}
xKey="mes"
series={[
{ key: 'ventas', label: 'Ventas' },
{ key: 'compras', label: 'Compras' },
]}
valueFormat={money}
aria-label="Ventas y compras por mes"
/>
</Card>

Las series con el mismo stackId se apilan.

<Chart
type="area"
data={tendencia}
xKey="mes"
series={[
{ key: 'ventas', label: 'Ventas', stackId: 'a' },
{ key: 'compras', label: 'Compras', stackId: 'a' },
]}
valueFormat={money}
/>

layout="vertical" dibuja las barras en horizontal, que es lo que quieres para un ranking con nombres largos.

<Chart
type="bar"
layout="vertical"
data={topClientes}
xKey="cliente"
series={[{ key: 'total', label: 'Facturado' }]}
valueFormat={money}
height={240}
/>
<Chart
type="donut"
data={[
{ estado: 'Pagadas', valor: 62 },
{ estado: 'Contabilizadas', valor: 24 },
{ estado: 'Borrador', valor: 10 },
{ estado: 'Anuladas', valor: 4 },
]}
xKey="estado"
series={[{ key: 'valor' }]}
colorBy={{ Pagadas: 'positive', Anuladas: 'negative' }}
valueFormat={(v) => `${v} %`}
/>

En el donut, xKey es el campo de la etiqueta y colorBy asigna color por categoría.

<Chart type="line" data={[]} xKey="mes" series={SERIES} isLoading />
<Chart
type="bar"
data={[]}
xKey="mes"
series={SERIES}
emptyState={{ title: 'Sin movimientos', hint: 'No hay datos en el periodo elegido.' }}
/>
{/* la misma información, también en tabla */}
<Chart type="line" data={tendencia} xKey="mes" series={SERIES} tableView />

tableView añade debajo una tabla con los mismos datos: es la salida accesible del gráfico, y de paso resuelve el «¿me pasas el número exacto?».

Prop Tipo Por defecto Descripción
type 'line' | 'area' | 'bar' | 'donut' Tipo de gráfico
data Array<Record<string, any>> Filas de datos
xKey string Campo del eje de categorías, o de la etiqueta en el donut
series ChartSeries[] Series a dibujar
layout 'horizontal' | 'vertical' 'horizontal' vertical dibuja las barras en horizontal
height number 288 Alto en píxeles
valueFormat (value: number) => string Formato de los valores
xFormat (value: any) => string Formato de las etiquetas del eje
legend boolean con más de una serie Leyenda
showGrid boolean true Rejilla
colorBy Record<string, string> Color por categoría en el donut
isLoading boolean false Estado de carga
emptyState EmptyStateProps Estado vacío
tableView boolean false Añade debajo una tabla con los mismos datos
animate boolean true Anima el dibujo al entrar
aria-label string Descripción accesible
className string Clases
Prop Tipo Descripción
key string Campo dentro de cada fila de data
label string Nombre visible. Sin él se usa key
color ChartRole | string Rol semántico, hex explícito, o nada para la ranura que toque
stackId string Las series con el mismo stackId se apilan

La paleta de series no está elegida a ojo: el orden de las ocho ranuras se seleccionó ejecutando el validador de paletas y conservando solo una ordenación que supera todas las comprobaciones —incluida la separación para daltonismo— en claro y en oscuro.

De ahí salen tres reglas que el componente aplica solo:

  1. Los colores se reparten en orden y sin ciclar. A partir de la octava serie todas caen en la ranura neutra: es la señal de que hay demasiadas y toca agrupar en «Otros».
  2. El color va ligado a la entidad, no a su puesto en un ranking. Filtrar no repinta las series que quedan. Para fijar el color de una categoría, colorBy.
  3. Los roles semánticos están reservados. positive, negative, warning y neutral significan lo mismo en todos los gráficos y no se reparten como una serie más.
import { CHART_SERIES, CHART_ROLES, seriesColor, MAX_SERIES } from '@openfactu/ui/charts';
seriesColor(0, 'light'); // '#0d9488' — la primera ranura
seriesColor(2, 'dark'); // '#3987e5'
seriesColor(0, 'light', 'negative'); // '#b91c1c' — rol semántico
seriesColor(9, 'light'); // el neutro: hay más series que ranuras
MAX_SERIES; // 8

CHART_SERIES_SLOTS les pone nombre para poder referirse a ellas sin usar el índice: teal, orange, blue, yellow, magenta, green, violet y red.

Export Tipo Para qué
CHART_SERIES Record<'light' | 'dark', string[]> Las ocho ranuras, por modo
CHART_SERIES_SLOTS readonly string[] Nombre de cada ranura
CHART_ROLES { positive, negative, warning, neutral } Colores reservados, por modo
CHART_SEQUENTIAL Record<'light' | 'dark', string[]> Rampa de un tono para magnitudes continuas
CHART_CHROME { axis, grid, tooltipBg, tooltipBorder, tooltipText } Ejes, rejilla y tooltip, leídos del tema
seriesColor (index, mode, color?) => string Color definitivo de una serie
MAX_SERIES number Series a partir de las cuales conviene agrupar en «Otros»

La línea de tendencia en miniatura, para meter dentro de una celda o de un KpiCard. Es SVG propio: no arrastra recharts.

import { Sparkline } from '@openfactu/ui';
<Sparkline data={[12, 19, 14, 22, 28, 25, 31]} />
<Sparkline data={ventasPorDia} height={40} showLastPoint label="Ventas de los últimos 30 días" />
<Sparkline data={serie} color="var(--k-danger)" showArea={false} strokeWidth={2} />

Con menos de dos puntos no dibuja nada. Sin label se marca como decorativa para los lectores de pantalla.

Prop Tipo Por defecto Descripción
data number[] Serie de valores
color string el acento del tema Color del trazo
height number 32 Alto en píxeles
strokeWidth number 1.5 Grosor del trazo
showArea boolean true Rellena el área bajo la línea
showLastPoint boolean false Marca el último punto
maxY number el máximo de la serie Techo del eje Y
minY number 0 Suelo del eje Y
label string Descripción para lectores de pantalla
className string Clases

Una barra apilada con su leyenda: el reparto de un total en una sola línea.

import { StackedBar } from '@openfactu/ui';
<StackedBar
segments={[
{ key: 'pagadas', label: 'Pagadas', value: 62 },
{ key: 'emitidas', label: 'Emitidas', value: 24 },
{ key: 'borrador', label: 'Borrador', value: 10 },
{ key: 'vencidas', label: 'Vencidas', value: 4, color: 'var(--k-danger)' },
]}
valueFormat={(v) => `${v} facturas`}
ariaLabel="Reparto de facturas por estado"
/>

Los porcentajes se calculan sobre la suma de los segmentos, salvo que le des un total distinto —útil cuando la barra representa el consumo de una cuota.

Prop Tipo Por defecto Descripción
segments StackedBarSegment[] Tramos: { key, value, color?, label? }
total number la suma Total del que se calculan los porcentajes
height number 8 Grosor de la barra
showLegend boolean true Leyenda con etiqueta, valor y porcentaje
valueFormat (value: number) => string String(v) Formato del valor
ariaLabel string Descripción accesible del conjunto
className string Clases

Para un porcentaje suelto en forma de anillo, mira Ring.