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>Área apilada
Sección titulada «Área apilada»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}/>Ranking en barras horizontales
Sección titulada «Ranking en barras horizontales»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.
Carga, vacío y tabla
Sección titulada «Carga, vacío y tabla»<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 |
ChartSeries
Sección titulada «ChartSeries»| 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
Sección titulada «La paleta»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:
- 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».
- 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. - Los roles semánticos están reservados.
positive,negative,warningyneutralsignifican 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 ranuraseriesColor(2, 'dark'); // '#3987e5'seriesColor(0, 'light', 'negative'); // '#b91c1c' — rol semánticoseriesColor(9, 'light'); // el neutro: hay más series que ranuras
MAX_SERIES; // 8Las ocho ranuras
Sección titulada «Las ocho ranuras»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» |
Sparkline
Sección titulada «Sparkline»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 |
StackedBar
Sección titulada «StackedBar»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.