Data Table
Una tabla de datos sobre la lista de Drive: columnas por definición, orden con la cabecera, búsqueda, páginas o «Cargar más», selección con casillas, vacío, esqueletos y grupos.
import { DataTable } from "sebs7n-ui/data-table"Ejemplos
Facturas emitidas
Orden con la cabecera, búsqueda sin tildes ni mayúsculas, selección con casillas y páginas. La fila elegida va en el acento mientras la tabla tiene el foco, como la lista de Drive.
import { Button } from "sebs7n-ui"
import { DataTable } from "sebs7n-ui/data-table"
import { PlusIcon } from "lucide-react"
import { useState } from "react"
function Basico() {
const [elegidas, setElegidas] = useState<string[]>([])
return (
<div className="w-full">
<DataTable
aria-label="Facturas emitidas"
columns={COLUMNAS}
data={FACTURAS}
defaultSort={{ id: "fecha", direction: "desc" }}
filter
getRowId={(row) => row.id}
getRowLabel={(row) => `${row.numero} de ${row.cliente}`}
onSelectedChange={setElegidas}
pageSize={8}
selectable
selected={elegidas}
toolbar={
<>
{elegidas.length > 0 && <span className="text-callout text-label-secondary">{elegidas.length} elegidas</span>}
<Button size="sm">
<PlusIcon />
Nueva factura
</Button>
</>
}
/>
</div>
)
}Por estado, con «Cargar más»
`groupBy` arma un grupo por estado con el título de Drive y su contador; `paging="more"` suma filas abajo en vez de paginar.
import { DataTable } from "sebs7n-ui/data-table"
function Agrupada() {
return (
<div className="w-full">
<DataTable
aria-label="Facturas por estado"
columns={COLUMNAS.filter((column) => column.id !== "estado")}
data={[...FACTURAS].sort((a, b) => ESTADOS.indexOf(a.estado) - ESTADOS.indexOf(b.estado))}
getRowId={(row) => row.id}
groupBy={(row) => row.estado}
pageSize={8}
paging="more"
/>
</div>
)
}Cargando y vacía
Mientras llegan los datos, filas de esqueleto y `aria-busy`; sin filas, la tabla lo dice.
import { Button } from "sebs7n-ui"
import { DataTable } from "sebs7n-ui/data-table"
import { useState } from "react"
function Estados() {
const [cargando, setCargando] = useState(true)
return (
<div className="flex w-full flex-col gap-4">
<Button className="self-start" onClick={() => setCargando(!cargando)} size="sm" variant="secondary">
{cargando ? "Mostrar vacía" : "Volver a cargar"}
</Button>
<DataTable
aria-label="Facturas del mes"
columns={COLUMNAS}
data={[]}
empty="Todavía no emitiste facturas este mes."
getRowId={(row) => row.id}
loading={cargando}
loadingRows={4}
/>
</div>
)
}Props
Generadas del TypeScript del paquete. Las propias del componente, más las heredadas del primitivo que tienen algo que explicar —marcadas «heredada de Base UI»—. El resto está en la línea «hereda de».
DataTable
| Prop | Tipo | Por defecto | Descripción |
|---|---|---|---|
columns* | DataTableColumn<T>[] | — | Las columnas: id, header, value (ordenar y buscar), cell (dibujar), sortable, numeric, searchable, className (el ancho). |
data* | T[] | — | Las filas. |
getRowId* | (row: T) => string | — | El id estable de cada fila: la key y lo que guarda la selección. |
aria-label | string | — | Nombre accesible del elemento. |
defaultPage | number | 1 | La página al arrancar. |
defaultQuery | string | "" | La búsqueda al arrancar. |
defaultSelected | string[] | [] | Los elegidos al arrancar. |
defaultSort | DataTableSort | null | El orden al arrancar. |
density | "default" | "compact" | — | La densidad de la Table: default (filas de 41, la de Drive) o compact (32). |
empty | React.ReactNode | — | Lo que dice la tabla sin filas. Por defecto, «Sin resultados». |
filter | boolean | false | Muestra la búsqueda arriba. |
filters | React.ReactNode | — | Lo que va pegado a la derecha de la búsqueda, en la misma fila mientras entre: los selectores de filtro. |
getRowLabel | (row: T) => string | — | El nombre de la fila para su casilla. Por defecto, el valor de la primera columna. |
groupBy | (row: T) => string | — | El título del grupo de cada fila: agrupa en el orden en que aparecen, con su contador. |
labels | Partial<Labels["dataTable"]> | — | Textos: search, selectAll, selectRow, loadMore, empty, result, results, item, items. |
loading | boolean | false | Filas de esqueleto en lugar de los datos, y aria-busy. |
loadingRows | number | — | Cuántas filas de esqueleto. Por defecto pageSize o 5. |
locale | string | — | El locale del orden alfabético. |
onPageChange | (page: number) => void | — | Se llama con la página nueva. |
onQueryChange | (query: string) => void | — | Se llama con el texto nuevo. |
onSelectedChange | (ids: string[]) => void | — | Se llama con los ids elegidos. |
onSortChange | (sort: DataTableSort) => void | — | Se llama con el orden nuevo. |
page | number | — | La página, en base 1. Pasarla la vuelve controlada. |
pageSize | number | — | Filas por página. Sin él, todas. |
paging | "pages" | "more" | "pages" | pages (default, con Pagination) o more (un botón «Cargar más»). |
query | string | — | El texto de la búsqueda. Pasarlo lo vuelve controlado. |
selectable | boolean | false | Una casilla por fila y la de todas en la cabecera. |
selected | string[] | — | Los ids elegidos, en el orden de data. Pasarlo lo vuelve controlado. |
sort | DataTableSort | — | El orden ({ id, direction } o null). Pasarlo lo vuelve controlado. |
toolbar | React.ReactNode | — | Lo que va a la derecha de la búsqueda: filtros, «Nueva factura». |
className | string | — | Se fusiona con las clases del componente vía cn() (tailwind-merge): lo que pongas gana. |
Teclado
- Tab
- Recorre la búsqueda, los botones de orden de la cabecera, las casillas y la paginación, en ese orden.
- Enter / Espacio
- En una cabecera, ordena: ascendente, descendente, sin orden. En una casilla, la marca.
Accesibilidad
- Es una
<table>de verdad: nombre obligatorio conaria-label. El lector anuncia columnas y filas y navega celda por celda con sus atajos de tabla. - La columna ordenada lleva
aria-sort(ascending/descending) en su<th>; el orden se cambia con un<button>adentro de la cabecera. La flecha es decorativa. - La búsqueda es un
searchboxcon nombre («Buscar»), y el total («12 resultados») sale por una regiónstatusque se anuncia al cambiar. - Cada casilla se nombra con la fila (
getRowLabel, por defecto la primera columna): «Seleccionar Acme S.A.». La de la cabecera, «Seleccionar todas», queda enmixedcon algunas. - Cargando, la tabla está
aria-busyy los esqueletos no tienen texto. - Los grupos son
TableGroupHeader(<th scope="rowgroup">) en un<tbody>por grupo: el lector los asocia a sus filas.
Reglas de uso
- Para datos que la persona ordena, busca o elige: facturas, clientes, movimientos. Una lista de pocas filas fijas es una
Tablea mano. valuees lo que se ordena y se busca;celllo que se dibuja (unBadge, un importe formateado). Números y fechas se ordenan como tales; el texto, con el orden del idioma y sin distinguir tildes.- El orden y la búsqueda son del cliente, sobre
data. Para datos del servidor, controlásort,queryypagey pasá endatala página que llegó. - «Seleccionar todas» marca las que se ven (la página actual), no las que el filtro esconde.
paging="more"para un historial que se lee de arriba abajo;pagespara buscar una fila puntual.- Solo por subpath (
sebs7n-ui/data-table): no está en el barrel, por peso.