Kobana UI
GitHub

DataTable

Tabela de dados com busca, filtros, paginação, sorting, seleção, ações e visibilidade de colunas. Baseada no dashboard Kobana Billing.

Carregando...

Instalação

npx @kobana/ui add data-table

Dependências instaladas automaticamente:

  • npm: @tanstack/react-table

Importação

import { DataTable } from "@/components/kobana/data-table"
import { DataTableColumnHeader } from "@/components/kobana/data-table"

Layout

O layout segue o padrão do dashboard Kobana Billing:

  1. Busca — Input com ícone de lupa no topo, debounce de 300ms
  2. Filtros + Refresh — Linha abaixo da busca com filtros à esquerda e botão de refresh à direita
  3. Tabela — Colunas com sorting por clique no header. Ícone de visibilidade de colunas no header da última coluna
  4. Paginação — "Mostrando X a Y de Z resultados", seletor de por página, navegação first/prev/next/last

Props

PropTipoDefaultDescrição
columnsColumnDef<TData>[]Definição das colunas (TanStack Table)
dataTData[]Dados da tabela
searchKeystringColuna para busca
searchPlaceholderstringPlaceholder da busca
onSearch(query: string) => voidBusca server-side (debounce 300ms)
isSearchingbooleanSpinner de busca
filterComponentReactNodeComponente de filtros customizado
paginationPaginationConfigPaginação server-side
onPageChange(page: number) => voidCallback de mudança de página
onPerPageChange(perPage: number) => voidCallback de items por página
selectablebooleanfalseHabilita seleção de linhas
bulkActionsBulkAction<TData>[]Ações em lote
rowActionsRowAction<TData>[]Ações por linha (menu ⋯)
isLoadingbooleanfalseEstado de loading (skeleton)
emptyStateReactNodeEstado vazio customizado
onRowClick(row: TData) => voidClick na linha
onRefresh() => voidBotão de refresh
isRefreshingbooleanSpinner no botão de refresh
columnLabelsRecord<string, string>Labels das colunas no dropdown de visibilidade
labelsDataTableLabelspt-BRLabels para i18n
classNamestringClasses adicionais

Sub-componentes

ComponenteResponsabilidade
DataTableToolbarBusca com debounce, filtros, refresh
DataTableColumnVisibilityÍcone no header da última coluna para mostrar/ocultar colunas
DataTableColumnHeaderHeader com sorting por clique (chevron ↑↓)
SimpleSortableHeaderHeader sortável sem contexto TanStack
DataTablePagination"Mostrando X a Y de Z", per-page, navegação
DataTableActionsMenu ⋯ de ações por linha
DataTableBulkActionsBarra de ações em lote
DataTableEmptyEstado vazio
DataTableLoadingSkeleton loading

Sorting

Use DataTableColumnHeader nas definições de colunas para habilitar sorting por clique:

const columns: ColumnDef<Product>[] = [
  {
    accessorKey: "name",
    header: ({ column }) => (
      <DataTableColumnHeader column={column} title="Produto" />
    ),
  },
  {
    accessorKey: "status",
    header: ({ column }) => (
      <DataTableColumnHeader column={column} title="Status" />
    ),
    cell: ({ row }) => <StatusBadge status={row.getValue("status")} />,
  },
  {
    accessorKey: "amount",
    header: ({ column }) => (
      <DataTableColumnHeader column={column} title="Preço" />
    ),
    cell: ({ row }) => formatCurrency(row.getValue("amount")),
  },
]

Para tabelas sem TanStack, use SimpleSortableHeader:

import { SimpleSortableHeader } from "@/components/kobana/data-table"

<SimpleSortableHeader
  title="Nome"
  sortKey="name"
  currentSortKey={sortKey}
  currentSortDirection={sortDir}
  onSort={handleSort}
/>

Visibilidade de Colunas

O ícone de configuração de colunas aparece automaticamente no header da última coluna da tabela. Colunas com enableHiding: false não aparecem no dropdown.

Use columnLabels para exibir nomes legíveis:

<DataTable
  columns={columns}
  data={products}
  columnLabels={{
    name: "Produto",
    status: "Status",
    amount: "Preço",
    createdAt: "Criado em",
  }}
/>

Uso Completo

<DataTable
  columns={columns}
  data={products}
  searchKey="name"
  searchPlaceholder="Buscar produtos..."
  onSearch={handleSearch}
  isSearching={isSearching}
  filterComponent={
    <FilterBar
      filters={[
        { key: "status", type: "select", label: "Status", options: statusOptions },
        { key: "type", type: "select", label: "Tipo", options: typeOptions },
      ]}
      values={filterValues}
      onChange={setFilterValues}
      onClear={() => setFilterValues({})}
    />
  }
  selectable
  bulkActions={[
    { label: "Arquivar", onClick: handleBulkArchive },
    { label: "Excluir", variant: "destructive", onClick: handleBulkDelete },
  ]}
  rowActions={[
    { label: "Editar", onClick: (row) => navigate(`/edit/${row.id}`) },
    { label: "Excluir", variant: "destructive", onClick: handleDelete },
  ]}
  pagination={{ page, perPage, total, totalPages }}
  onPageChange={setPage}
  onPerPageChange={setPerPage}
  onRefresh={refetch}
  columnLabels={{ name: "Produto", status: "Status", amount: "Preço" }}
/>

Labels (i18n)

<DataTable
  labels={{
    toolbar: { search: "Search", refresh: "Refresh" },
    pagination: {
      showing: "Showing", to: "to", of: "of", results: "results",
      perPage: "Per page", page: "Page", pageOf: "of",
    },
    selectAll: "Select all",
    selectRow: "Select row",
  }}
  // ...
/>

On this page