DataTable
Tabela genérica orientada a colunas, com busca, paginação server-side, loading e empty state resolvidos internamente.
| Nome | Função | Status |
|---|---|---|
| Ana Souza | Piloto | Ativo |
| Bruno Lima | Copiloto | Ativo |
| Carla Dias | Comissária | Inativo |
import { DataTable } from "@vincisys/eyeson";
export function DataTableBasicDemo() {
return (
<div className="w-full max-w-2xl">
<DataTable columns={columns} data={ALL_WORKERS.slice(0, 3)} />
</div>
);
} Instalação
import { DataTable } from '@vincisys/eyeson'; Uso
Construído sobre o Table (compound component) e @tanstack/react-table.
Você só define columns — busca, paginação server-side, loading e empty
state são resolvidos internamente a partir de props de comportamento.
import { DataTable } from '@vincisys/eyeson';
import type { ColumnDef } from '@tanstack/react-table';
type Worker = { id: string; name: string; role: string };
const columns: ColumnDef<Worker>[] = [
{ accessorKey: 'name', header: 'Nome' },
{ accessorKey: 'role', header: 'Função' },
];
export function Example() {
return <DataTable columns={columns} data={workers} />;
} Exemplos
Básico
Só columns + data.
| Nome | Função | Status |
|---|---|---|
| Ana Souza | Piloto | Ativo |
| Bruno Lima | Copiloto | Ativo |
| Carla Dias | Comissária | Inativo |
import { DataTable } from "@vincisys/eyeson";
export function DataTableBasicDemo() {
return (
<div className="w-full max-w-2xl">
<DataTable columns={columns} data={ALL_WORKERS.slice(0, 3)} />
</div>
);
} Loading
Passe isLoading — a tabela renderiza um skeleton no lugar das linhas.
| Nome | Função | Status |
|---|---|---|
import { DataTable } from "@vincisys/eyeson";
export function DataTableLoadingDemo() {
return (
<div className="w-full max-w-2xl">
<DataTable columns={columns} data={[]} isLoading />
</div>
);
} Empty state
Quando data está vazio, o DataTable renderiza o componente Empty
internamente. Personalize com emptyStateTitle/emptyStateDescription.
Nenhum tripulante encontrado
Ajuste os filtros e tente novamente.
import { DataTable } from "@vincisys/eyeson";
export function DataTableEmptyDemo() {
return (
<div className="w-full max-w-2xl">
<DataTable
columns={columns}
data={[]}
emptyStateTitle="Nenhum tripulante encontrado"
emptyStateDescription="Ajuste os filtros e tente novamente."
/>
</div>
);
} Dividers
dividers controla as linhas divisórias entre linhas e/ou colunas
("none", "row" — padrão, "column" ou "both"), repassado direto
para o Table interno.
dividers="row" (padrão)
| Nome | Função | Status |
|---|---|---|
| Ana Souza | Piloto | Ativo |
| Bruno Lima | Copiloto | Ativo |
| Carla Dias | Comissária | Inativo |
dividers="column"
| Nome | Função | Status |
|---|---|---|
| Ana Souza | Piloto | Ativo |
| Bruno Lima | Copiloto | Ativo |
| Carla Dias | Comissária | Inativo |
dividers="both"
| Nome | Função | Status |
|---|---|---|
| Ana Souza | Piloto | Ativo |
| Bruno Lima | Copiloto | Ativo |
| Carla Dias | Comissária | Inativo |
dividers="none"
| Nome | Função | Status |
|---|---|---|
| Ana Souza | Piloto | Ativo |
| Bruno Lima | Copiloto | Ativo |
| Carla Dias | Comissária | Inativo |
import { DataTable } from "@vincisys/eyeson";
export function DataTableDividersDemo() {
return (
<div className="flex w-full max-w-2xl flex-col gap-6">
<div>
<p className="mb-2 text-eds-neutral-elements text-sm">
dividers="row" (padrão)
</p>
<DataTable
columns={columns}
data={ALL_WORKERS.slice(0, 3)}
dividers="row"
/>
</div>
<div>
<p className="mb-2 text-eds-neutral-elements text-sm">
dividers="column"
</p>
<DataTable
columns={columns}
data={ALL_WORKERS.slice(0, 3)}
dividers="column"
/>
</div>
<div>
<p className="mb-2 text-eds-neutral-elements text-sm">
dividers="both"
</p>
<DataTable
columns={columns}
data={ALL_WORKERS.slice(0, 3)}
dividers="both"
/>
</div>
<div>
<p className="mb-2 text-eds-neutral-elements text-sm">
dividers="none"
</p>
<DataTable
columns={columns}
data={ALL_WORKERS.slice(0, 3)}
dividers="none"
/>
</div>
</div>
);
} Ordenação (manual)
sorting + onSortingChange tornam os cabeçalhos das colunas clicáveis
(ícone indica a direção atual). A ordenação é sempre manual/controlada
pelo consumidor: o DataTable nunca reordena data sozinho — use o
SortingState recebido para produzir os dados já ordenados, seja
refazendo uma busca remota (server-side) ou ordenando localmente.
Desabilite em uma coluna específica com enableSorting: false no
columnDef.
| Ana Souza | Piloto | Ativo |
| Bruno Lima | Copiloto | Ativo |
| Carla Dias | Comissária | Inativo |
| Diego Alves | Piloto | Ativo |
| Elena Rocha | Comissária | Ativo |
| Felipe Cruz | Copiloto | Inativo |
| Gabriela Melo | Piloto | Ativo |
| Henrique Nunes | Comissário | Ativo |
| Isabela Prado | Copiloto | Inativo |
| João Vitor | Piloto | Ativo |
import { DataTable } from "@vincisys/eyeson";
import { SortingState } from "@tanstack/react-table";
import { useMemo, useState } from "react";
export function DataTableSortingDemo() {
const [sorting, setSorting] = useState<SortingState>([]);
const sorted = useMemo(() => {
const sort = sorting[0];
if (!sort) return ALL_WORKERS;
const copy = [...ALL_WORKERS];
copy.sort((a, b) => {
const key = sort.id as keyof Worker;
const result = String(a[key]).localeCompare(String(b[key]));
return sort.desc ? -result : result;
});
return copy;
}, [sorting]);
return (
<div className="w-full max-w-2xl">
<DataTable
columns={columns}
data={sorted}
sorting={sorting}
onSortingChange={setSorting}
/>
</div>
);
} const [sorting, setSorting] = useState<SortingState>([]);
const { data } = useListWorkers({ ordering: sorting[0] });
<DataTable
columns={columns}
data={data}
sorting={sorting}
onSortingChange={setSorting}
/> Busca e paginação (server-side)
search renderiza um campo de busca acima da tabela (só aparece se você
passar essa prop). pagination + onPageChange controlam a paginação —
a paginação é sempre server-side: o DataTable não pagina data
sozinho, ele só mostra os controles e avisa quando a página muda. Os
controles de paginação só aparecem quando há mais de uma página.
| Nome | Função | Status |
|---|---|---|
| Ana Souza | Piloto | Ativo |
| Bruno Lima | Copiloto | Ativo |
import { DataTable } from "@vincisys/eyeson";
import { useMemo, useState } from "react";
export function DataTableSearchAndPaginationDemo() {
const [search, setSearch] = useState('');
const [page, setPage] = useState(1);
const limit = 2;
const filtered = useMemo(
() =>
ALL_WORKERS.filter((worker) =>
worker.name.toLowerCase().includes(search.toLowerCase()),
),
[search],
);
const paged = useMemo(
() => filtered.slice((page - 1) * limit, page * limit),
[filtered, page],
);
return (
<div className="w-full max-w-2xl">
<DataTable
columns={columns}
data={paged}
search={{
value: search,
onChange: (value) => {
setSearch(value);
setPage(1);
},
placeholder: 'Buscar tripulante...',
}}
pagination={{ page, limit, total: filtered.length }}
onPageChange={setPage}
emptyStateTitle="Nenhum tripulante encontrado"
/>
</div>
);
} const [search, setSearch] = useState('');
const [page, setPage] = useState(1);
const { data, total } = useListWorkers({ search, page });
<DataTable
columns={columns}
data={data}
search={{ value: search, onChange: setSearch, placeholder: 'Buscar...' }}
pagination={{ page, limit: 10, total }}
onPageChange={setPage}
emptyStateTitle="Nenhum resultado encontrado"
/> Itens por página
pageSize renderiza um seletor à esquerda dos controles de paginação,
permitindo o usuário escolher quantos itens ver por página. Requer
pagination — o valor exibido no seletor é sempre pagination.limit
(única fonte de verdade, evita o seletor e a paginação desincronizarem).
Assim como pagination, o DataTable não corta data sozinho — cabe
ao consumidor usar o novo valor para buscar/fatiar os dados. É comum
resetar pagination.page para 1 dentro de onChange, para evitar
cair numa página que deixou de existir.
| Nome | Função | Status |
|---|---|---|
| Ana Souza | Piloto | Ativo |
| Bruno Lima | Copiloto | Ativo |
| Carla Dias | Comissária | Inativo |
import { DataTable } from "@vincisys/eyeson";
import { useMemo, useState } from "react";
export function DataTablePageSizeDemo() {
const [page, setPage] = useState(1);
const [limit, setLimit] = useState(3);
const paged = useMemo(
() => ALL_WORKERS.slice((page - 1) * limit, page * limit),
[page, limit],
);
return (
<div className="w-full max-w-2xl">
<DataTable
columns={columns}
data={paged}
pagination={{ page, limit, total: ALL_WORKERS.length }}
onPageChange={setPage}
pageSize={{
options: [3, 5, 10],
onChange: (value) => {
setLimit(value);
setPage(1);
},
}}
/>
</div>
);
}