Table

Avatar de TanStack TableTanStack TableGitLab
Um elemento de tabela responsivo para exibir dados em linhas e colunas.

Uso

O componente Table é construído sobre o TanStack Table e usa o composable useVueTable para oferecer uma API flexível e totalmente type-safe.

Ele renderiza os seus dados em linhas e colunas e suporta ordenação, filtragem, paginação, seleção de linhas, expansão, agrupamento, fixação e virtualização, então você pode construir desde uma tabela de dados simples até um data grid completo.

#DateStatusAmount
#4600Mar 11, 15:30paid[email protected]€594.00
#4599Mar 11, 10:10failed[email protected]€276.00
#4598Mar 11, 08:50refunded[email protected]€315.00
#4597Mar 10, 19:45paid[email protected]€529.00
#4596Mar 10, 15:55paid[email protected]€639.00
#4595Mar 10, 13:40refunded[email protected]€428.00
#4594Mar 10, 09:15paid[email protected]€683.00
#4593Mar 9, 20:25failed[email protected]€947.00
#4592Mar 9, 18:45paid[email protected]€851.00
#4591Mar 9, 16:05paid[email protected]€762.00
#4590Mar 9, 14:20paid[email protected]€573.00
#4589Mar 9, 11:35failed[email protected]€389.00
#4588Mar 8, 22:50refunded[email protected]€701.00
#4587Mar 8, 20:15paid[email protected]€856.00
#4586Mar 8, 17:40paid[email protected]€492.00
#4585Mar 8, 14:55failed[email protected]€637.00
#4584Mar 8, 12:30paid[email protected]€784.00
#4583Mar 8, 09:45refunded[email protected]€345.00
#4582Mar 7, 23:10paid[email protected]€918.00
#4581Mar 7, 20:25paid[email protected]€567.00
0 of 0 row(s) selected.
Este exemplo demonstra o caso de uso mais comum do componente Table. Confira o código-fonte no GitHub.

Dados

Use a prop data como um array de objetos; as colunas serão geradas com base nas chaves dos objetos.

IdDateStatusEmailAmount
46002024-03-11T15:30:00paid[email protected]594
45992024-03-11T10:10:00failed[email protected]276
45982024-03-11T08:50:00refunded[email protected]315
45972024-03-10T19:45:00paid[email protected]529
45962024-03-10T15:55:00paid[email protected]639
<script setup lang="ts">
const data = ref([
  {
    id: '4600',
    date: '2024-03-11T15:30:00',
    status: 'paid',
    email: '[email protected]',
    amount: 594
  },
  {
    id: '4599',
    date: '2024-03-11T10:10:00',
    status: 'failed',
    email: '[email protected]',
    amount: 276
  },
  {
    id: '4598',
    date: '2024-03-11T08:50:00',
    status: 'refunded',
    email: '[email protected]',
    amount: 315
  },
  {
    id: '4597',
    date: '2024-03-10T19:45:00',
    status: 'paid',
    email: '[email protected]',
    amount: 529
  },
  {
    id: '4596',
    date: '2024-03-10T15:55:00',
    status: 'paid',
    email: '[email protected]',
    amount: 639
  }
])
</script>

<template>
  <NTable :data="data" class="flex-1" />
</template>
<script setup lang="ts">
import { ref } from 'vue'

const data = ref([
  {
    id: '4600',
    date: '2024-03-11T15:30:00',
    status: 'paid',
    email: '[email protected]',
    amount: 594
  },
  {
    id: '4599',
    date: '2024-03-11T10:10:00',
    status: 'failed',
    email: '[email protected]',
    amount: 276
  },
  {
    id: '4598',
    date: '2024-03-11T08:50:00',
    status: 'refunded',
    email: '[email protected]',
    amount: 315
  },
  {
    id: '4597',
    date: '2024-03-10T19:45:00',
    status: 'paid',
    email: '[email protected]',
    amount: 529
  },
  {
    id: '4596',
    date: '2024-03-10T15:55:00',
    status: 'paid',
    email: '[email protected]',
    amount: 639
  }
])
</script>

<template>
  <NTable :data="data" class="flex-1" />
</template>

Colunas

Use a prop columns como um array de objetos ColumnDef com propriedades como:

  • accessorKey: The key of the row object to use when extracting the value for the column.
  • header: The header to display for the column. If a string is passed, it can be used as a default for the column ID. If a function is passed, it will be passed a props object for the header and should return the rendered header value (the exact type depends on the adapter being used).
  • footer: The footer to display for the column. Works exactly like header, but is displayed under the table.
  • cell: The cell to display each row for the column. If a function is passed, it will be passed a props object for the cell and should return the rendered cell value (the exact type depends on the adapter being used).
  • meta: Extra properties for the column.
    • class:
      • td: The classes to apply to the td element.
      • th: The classes to apply to the th element.
    • style:
      • td: The style to apply to the td element.
      • th: The style to apply to the th element.
    • colspan:
      • td: The colspan attribute to apply to the td element.
    • rowspan:
      • td: The rowspan attribute to apply to the td element.

Para renderizar componentes ou outros elementos HTML, você precisará usar a função h do Vue dentro das props header e cell. Isso é diferente de outros componentes que usam slots, mas permite mais flexibilidade.

Você também pode usar slots para personalizar o cabeçalho e as células de dados da tabela.
#DateStatusE-mailAmount
#4600Mar 11, 15:30paid[email protected]€594.00
#4599Mar 11, 10:10failed[email protected]€276.00
#4598Mar 11, 08:50refunded[email protected]€315.00
#4597Mar 10, 19:45paid[email protected]€529.00
#4596Mar 10, 15:55paid[email protected]€639.00
Ao renderizar componentes com h, você pode usar a função resolveComponent ou importar de #components.

Meta

Use a prop meta como um objeto (TableMeta) para passar propriedades como:

  • class:
    • tr: The classes to apply to the tr element.
  • style:
    • tr: The style to apply to the tr element.
IDDateStatusE-mailAmount
4600Mar 11, 15:30paid[email protected]$594.00
4599Mar 11, 10:10failed[email protected]$276.00
4598Mar 11, 08:50refunded[email protected]$315.00
4597Mar 10, 19:45paid[email protected]$529.00
4596Mar 10, 15:55paid[email protected]$639.00

Carregando

Use a prop loading para exibir um estado de carregamento, a prop loading-color para alterar a cor dele e a prop loading-animation para alterar a animação dele.

IdDateStatusEmailAmount
46002024-03-11T15:30:00paid[email protected]594
45992024-03-11T10:10:00failed[email protected]276
45982024-03-11T08:50:00refunded[email protected]315
45972024-03-10T19:45:00paid[email protected]529
45962024-03-10T15:55:00paid[email protected]639
<script setup lang="ts">
const data = ref([
  {
    id: '4600',
    date: '2024-03-11T15:30:00',
    status: 'paid',
    email: '[email protected]',
    amount: 594
  },
  {
    id: '4599',
    date: '2024-03-11T10:10:00',
    status: 'failed',
    email: '[email protected]',
    amount: 276
  },
  {
    id: '4598',
    date: '2024-03-11T08:50:00',
    status: 'refunded',
    email: '[email protected]',
    amount: 315
  },
  {
    id: '4597',
    date: '2024-03-10T19:45:00',
    status: 'paid',
    email: '[email protected]',
    amount: 529
  },
  {
    id: '4596',
    date: '2024-03-10T15:55:00',
    status: 'paid',
    email: '[email protected]',
    amount: 639
  }
])
</script>

<template>
  <NTable
    loading
    loading-color="primary"
    loading-animation="carousel"
    :data="data"
    class="flex-1"
  />
</template>
<script setup lang="ts">
import { ref } from 'vue'

const data = ref([
  {
    id: '4600',
    date: '2024-03-11T15:30:00',
    status: 'paid',
    email: '[email protected]',
    amount: 594
  },
  {
    id: '4599',
    date: '2024-03-11T10:10:00',
    status: 'failed',
    email: '[email protected]',
    amount: 276
  },
  {
    id: '4598',
    date: '2024-03-11T08:50:00',
    status: 'refunded',
    email: '[email protected]',
    amount: 315
  },
  {
    id: '4597',
    date: '2024-03-10T19:45:00',
    status: 'paid',
    email: '[email protected]',
    amount: 529
  },
  {
    id: '4596',
    date: '2024-03-10T15:55:00',
    status: 'paid',
    email: '[email protected]',
    amount: 639
  }
])
</script>

<template>
  <NTable
    loading
    loading-color="primary"
    loading-animation="carousel"
    :data="data"
    class="flex-1"
  />
</template>

Fixo

Use a prop sticky para tornar o cabeçalho ou o rodapé fixos.

IdDateStatusEmailAmount
46002024-03-11T15:30:00paid[email protected]594
45992024-03-11T10:10:00failed[email protected]276
45982024-03-11T08:50:00refunded[email protected]315
45972024-03-10T19:45:00paid[email protected]529
45962024-03-10T15:55:00paid[email protected]639
45952024-03-10T15:55:00paid[email protected]639
45942024-03-10T15:55:00paid[email protected]639
<script setup lang="ts">
const data = ref([
  {
    id: '4600',
    date: '2024-03-11T15:30:00',
    status: 'paid',
    email: '[email protected]',
    amount: 594
  },
  {
    id: '4599',
    date: '2024-03-11T10:10:00',
    status: 'failed',
    email: '[email protected]',
    amount: 276
  },
  {
    id: '4598',
    date: '2024-03-11T08:50:00',
    status: 'refunded',
    email: '[email protected]',
    amount: 315
  },
  {
    id: '4597',
    date: '2024-03-10T19:45:00',
    status: 'paid',
    email: '[email protected]',
    amount: 529
  },
  {
    id: '4596',
    date: '2024-03-10T15:55:00',
    status: 'paid',
    email: '[email protected]',
    amount: 639
  },
  {
    id: '4595',
    date: '2024-03-10T15:55:00',
    status: 'paid',
    email: '[email protected]',
    amount: 639
  },
  {
    id: '4594',
    date: '2024-03-10T15:55:00',
    status: 'paid',
    email: '[email protected]',
    amount: 639
  }
])
</script>

<template>
  <NTable sticky :data="data" class="flex-1 max-h-[312px]" />
</template>
<script setup lang="ts">
import { ref } from 'vue'

const data = ref([
  {
    id: '4600',
    date: '2024-03-11T15:30:00',
    status: 'paid',
    email: '[email protected]',
    amount: 594
  },
  {
    id: '4599',
    date: '2024-03-11T10:10:00',
    status: 'failed',
    email: '[email protected]',
    amount: 276
  },
  {
    id: '4598',
    date: '2024-03-11T08:50:00',
    status: 'refunded',
    email: '[email protected]',
    amount: 315
  },
  {
    id: '4597',
    date: '2024-03-10T19:45:00',
    status: 'paid',
    email: '[email protected]',
    amount: 529
  },
  {
    id: '4596',
    date: '2024-03-10T15:55:00',
    status: 'paid',
    email: '[email protected]',
    amount: 639
  },
  {
    id: '4595',
    date: '2024-03-10T15:55:00',
    status: 'paid',
    email: '[email protected]',
    amount: 639
  },
  {
    id: '4594',
    date: '2024-03-10T15:55:00',
    status: 'paid',
    email: '[email protected]',
    amount: 639
  }
])
</script>

<template>
  <NTable sticky :data="data" class="flex-1 max-h-[312px]" />
</template>

Exemplos

Com ações de linha

Você pode adicionar uma nova coluna que renderiza um componente DropdownMenu dentro da cell para renderizar ações de linha.

#DateStatusE-mailAmount
#4600Mar 11, 15:30paid[email protected]€594.00
#4599Mar 11, 10:10failed[email protected]€276.00
#4598Mar 11, 08:50refunded[email protected]€315.00
#4597Mar 10, 19:45paid[email protected]€529.00
#4596Mar 10, 15:55paid[email protected]€639.00

Com linhas expansíveis

Você pode adicionar uma nova coluna que renderiza um componente Button dentro da cell para alternar o estado expansível de uma linha usando as APIs de Expanding do TanStack Table.

Você precisa definir o slot #expanded para renderizar o conteúdo expandido, que receberá a linha como parâmetro.
#DateStatusE-mailAmount
#4600Mar 11, 15:30paid[email protected]€594.00
#4599Mar 11, 10:10failed[email protected]€276.00
{
  "id": "4599",
  "date": "2024-03-11T10:10:00",
  "status": "failed",
  "email": "[email protected]",
  "amount": 276
}
#4598Mar 11, 08:50refunded[email protected]€315.00
#4597Mar 10, 19:45paid[email protected]€529.00
#4596Mar 10, 15:55paid[email protected]€639.00
Você pode usar a prop expanded para controlar o estado expansível das linhas (pode ser vinculada com v-model).
Você também poderia adicionar essa ação ao componente DropdownMenu dentro da coluna actions.

Com linhas agrupadas

Você pode agrupar as linhas com base no valor de uma coluna e exibir/ocultar as sublinhas por meio de algum botão adicionado à célula usando as APIs de Grouping do TanStack Table.

Partes importantes:

  • Add grouping prop with an array of column ids you want to group by.
  • Add grouping-options prop. It must include getGroupedRowModel, you can import it from @tanstack/vue-table or implement your own.
  • Expand rows via row.toggleExpanded() method on any cell of the row. Keep in mind, it also toggles #expanded slot.
  • Use aggregateFn on column definition to define how to aggregate the rows.
  • agregatedCell renderer on column definition only works if there is no cell renderer.
Item#DateE-mailAmount
Conta 1
3 recordsMar 11, 15:303 customers€1,548.00
Conta 2
2 recordsMar 11, 10:102 customers€805.00

Com fixação de linhas 4.6+

Você pode adicionar uma coluna que renderiza um componente Button dentro da cell para alternar o estado de fixação de uma linha usando as APIs de Row Pinning do TanStack Table. Linhas fixadas permanecem no topo ou na base da tabela, independentemente da ordenação ou filtragem.

DateStatusE-mailAmount
Mar 11, 10:10failed[email protected]€276.00
Mar 10, 19:45paid[email protected]€529.00
Mar 11, 15:30paid[email protected]€594.00
Mar 11, 08:50refunded[email protected]€315.00
Mar 10, 15:55paid[email protected]€639.00
Mar 10, 13:40refunded[email protected]€428.00
Mar 10, 09:15paid[email protected]€683.00
Mar 9, 20:25failed[email protected]€947.00
Mar 9, 18:45paid[email protected]€851.00
Mar 9, 16:05paid[email protected]€762.00
Você pode usar a prop row-pinning para controlar o estado de fixação das linhas (pode ser vinculada com v-model).

Com seleção de linhas

Você pode adicionar uma nova coluna que renderiza um componente Checkbox dentro do header e da cell para selecionar linhas usando as APIs de Row Selection do TanStack Table.

DateStatusE-mailAmount
Mar 11, 15:30paid[email protected]€594.00
Mar 11, 10:10failed[email protected]€276.00
Mar 11, 08:50refunded[email protected]€315.00
Mar 10, 19:45paid[email protected]€529.00
Mar 10, 15:55paid[email protected]€639.00
0 of 0 row(s) selected.
Você pode usar a prop row-selection para controlar o estado de seleção das linhas (pode ser vinculada com v-model).

Com evento de seleção da linha

Você pode adicionar um listener @select para tornar as linhas clicáveis com ou sem uma coluna de checkbox.

A função handler recebe as instâncias de Event e TableRow como primeiro e segundo argumentos, respectivamente.
DateStatusE-mailAmount
Mar 11, 15:30paid[email protected]€594.00
Mar 11, 10:10failed[email protected]€276.00
Mar 11, 08:50refunded[email protected]€315.00
Mar 10, 19:45paid[email protected]€529.00
Mar 10, 15:55paid[email protected]€639.00
0 of 0 row(s) selected.
Você pode usar isso para navegar até uma página, abrir um modal ou até selecionar a linha manualmente.

Com evento de menu de contexto da linha

Você pode adicionar um listener @contextmenu para tornar as linhas clicáveis com o botão direito e envolver a Table em um componente ContextMenu para exibir ações de linha, por exemplo.

A função handler recebe as instâncias de Event e TableRow como primeiro e segundo argumentos, respectivamente.
#DateStatusE-mailAmount
#4600Mar 11, 15:30paid[email protected]€594.00
#4599Mar 11, 10:10failed[email protected]€276.00
#4598Mar 11, 08:50refunded[email protected]€315.00
#4597Mar 10, 19:45paid[email protected]€529.00
#4596Mar 10, 15:55paid[email protected]€639.00

Com evento de hover da linha

Você pode adicionar um listener @hover para tornar as linhas sensíveis ao hover e usar um componente Popover ou Tooltip para exibir os detalhes da linha, por exemplo.

A função handler recebe as instâncias de Event e TableRow como primeiro e segundo argumentos, respectivamente.
#DateStatusE-mailAmount
#4600Mar 11, 15:30paid[email protected]€594.00
#4599Mar 11, 10:10failed[email protected]€276.00
#4598Mar 11, 08:50refunded[email protected]€315.00
#4597Mar 10, 19:45paid[email protected]€529.00
#4596Mar 10, 15:55paid[email protected]€639.00
Este exemplo é semelhante ao exemplo com cursor que acompanha do Popover e usa um refDebounced para evitar que o Popover abra e feche rápido demais ao mover o cursor de uma linha para outra.

Com rodapé de coluna

Você pode adicionar uma propriedade footer à definição da coluna para renderizar um rodapé para a coluna.

#DateStatusE-mailAmount
#4600Mar 11, 15:30paid[email protected]€594.00
#4599Mar 11, 10:10failed[email protected]€276.00
#4598Mar 11, 08:50refunded[email protected]€315.00
#4597Mar 10, 19:45paid[email protected]€529.00
#4596Mar 10, 15:55paid[email protected]€639.00
Total: €2,353.00

Com span de coluna

Você pode usar as propriedades colspan e rowspan no meta da coluna para mesclar células. Essas propriedades aceitam um valor estático ou uma função que recebe a célula e retorna o valor do span.

Ao usar rowspan, as células que são "absorvidas" pelo span de uma linha anterior precisam ser ocultadas visualmente. Use o metaclass com uma função que retorna 'hidden' para essas células.
CategoryNamePreçoStock
ElectronicsNotebook$999.0045
Telefone$699.00120
Tablet$499.0078
ClothingT-Shirt$29.00200
Jeans$59.00150

Com ordenação de colunas

Você pode atualizar o header de uma coluna para renderizar um componente Button dentro do header para alternar o estado de ordenação usando as APIs de Sorting do TanStack Table.

#DateStatusAmount
#4597Mar 10, 19:45paid[email protected]€529.00
#4596Mar 10, 15:55paid[email protected]€639.00
#4600Mar 11, 15:30paid[email protected]€594.00
#4599Mar 11, 10:10failed[email protected]€276.00
#4598Mar 11, 08:50refunded[email protected]€315.00
Você pode usar a prop sorting para controlar o estado de ordenação das colunas (pode ser vinculada com v-model).

Você também pode criar um componente reutilizável para tornar o cabeçalho de qualquer coluna ordenável.

#4596Mar 10, 15:55paid[email protected]€639.00
#4597Mar 10, 19:45paid[email protected]€529.00
#4598Mar 11, 08:50refunded[email protected]€315.00
#4599Mar 11, 10:10failed[email protected]€276.00
#4600Mar 11, 15:30paid[email protected]€594.00
Neste exemplo, usamos uma função para definir o cabeçalho da coluna, mas você também pode criar um componente de verdade.

Com fixação de colunas

Você pode atualizar o header de uma coluna para renderizar um componente Button dentro do header para alternar o estado de fixação usando as APIs de Pinning do TanStack Table.

Uma coluna fixada fica fixa no lado esquerdo ou direito da tabela. Ao usar fixação de colunas, você deve definir valores de size explícitos para as suas colunas para garantir o tratamento correto da largura das colunas, especialmente com várias colunas fixadas.
#46000000000000000000000000000000000000002024-03-11T15:30:00paid[email protected]€594,000.00
#45990000000000000000000000000000000000002024-03-11T10:10:00failed[email protected]€276,000.00
#45980000000000000000000000000000000000002024-03-11T08:50:00refunded[email protected]€315,000.00
#45970000000000000000000000000000000000002024-03-10T19:45:00paid[email protected]€5,290,000.00
#45960000000000000000000000000000000000002024-03-10T15:55:00paid[email protected]€639,000.00
Você pode usar a prop column-pinning para controlar o estado de fixação das colunas (pode ser vinculada com v-model).

Com visibilidade de colunas

Você pode usar um componente DropdownMenu para alternar a visibilidade das colunas usando as APIs de Column Visibility do TanStack Table.

DateStatusE-mailAmount
Mar 11, 15:30paid[email protected]€594.00
Mar 11, 10:10failed[email protected]€276.00
Mar 11, 08:50refunded[email protected]€315.00
Mar 10, 19:45paid[email protected]€529.00
Mar 10, 15:55paid[email protected]€639.00
Você pode usar a prop column-visibility para controlar o estado de visibilidade das colunas (pode ser vinculada com v-model).

Com filtros de coluna

Você pode usar um componente Input para filtrar as linhas por coluna usando as APIs de Column Filtering do TanStack Table.

#DateStatusE-mailAmount
#4600Mar 11, 15:30paid[email protected]€594.00
Você pode usar a prop column-filters para controlar o estado dos filtros das colunas (pode ser vinculada com v-model).

Com filtro global

Você pode usar um componente Input para filtrar as linhas usando as APIs de Global Filtering do TanStack Table.

#DateStatusE-mailAmount
#4599Mar 11, 10:10failed[email protected]€276.00
#4598Mar 11, 08:50refunded[email protected]€315.00
#4597Mar 10, 19:45paid[email protected]€529.00
#4596Mar 10, 15:55paid[email protected]€639.00
Você pode usar a prop global-filter para controlar o estado do filtro global (pode ser vinculada com v-model).

Com paginação

Você pode usar um componente Pagination para controlar o estado de paginação usando as APIs de Pagination.

Há diferentes abordagens de paginação, como explicado no Guia de Paginação. Neste exemplo, usamos paginação no lado do cliente, então precisamos passar manualmente a função getPaginationRowModel().

#DateE-mailAmount
#4600Mar 11, 15:30[email protected]€594.00
#4599Mar 11, 10:10[email protected]€276.00
#4598Mar 11, 08:50[email protected]€315.00
#4597Mar 10, 19:45[email protected]€529.00
#4596Mar 10, 15:55[email protected]€639.00
Você pode usar a prop pagination para controlar o estado de paginação (pode ser vinculada com v-model).

Com dados buscados

Você pode buscar dados de uma API e usá-los na Table.

IDNameE-mailCompany
Nenhum dado
Este exemplo usa useLazyFetch com server: false para buscar os dados no cliente sem bloquear a renderização inicial. O estado de carregamento verifica os status pending e idle para exibir um indicador de carregamento antes e durante a busca.

Com rolagem infinita

Se você usa paginação no lado do servidor, pode usar o composable useInfiniteScroll para carregar mais dados conforme o usuário rola.

IDAvatarFirst nameE-mailNome de usuário
Nenhum dado
Este exemplo usa useLazyFetch com server: false para buscar os dados no cliente sem bloquear a renderização inicial. O estado de carregamento verifica os status pending e idle para exibir um indicador de carregamento antes e durante a busca. Páginas adicionais são carregadas conforme o usuário rola.

Com arrastar e soltar

Você pode usar o composable useSortable de @vueuse/integrations para habilitar a funcionalidade de arrastar e soltar na Table. Essa integração envolve o Sortable.js para oferecer uma experiência de arrastar e soltar fluida.

Como o ref da tabela não expõe o elemento tbody, adicione uma classe única a ele via a prop :ui para mirá-lo com o useSortable (ex.: :ui="{ tbody: 'my-table-tbody' }").
#DateE-mailAmount
#4600Mar 11, 15:30[email protected]€594.00
#4599Mar 11, 10:10[email protected]€276.00
#4598Mar 11, 08:50[email protected]€315.00
#4597Mar 10, 19:45[email protected]€529.00

Com virtualização 4.1+

Use a prop virtualize para habilitar a virtualização em grandes conjuntos de dados, como um booleano ou um objeto com opções como { estimateSize: 65, overscan: 12 }. Você também pode passar outras opções do TanStack Virtual para personalizar o comportamento da virtualização. A prop sticky funciona em combinação com virtualize para manter o cabeçalho ou o rodapé visíveis ao rolar por grandes conjuntos de dados.

A fixação de linhas não é suportada quando a virtualização está habilitada.
#DateStatusE-mailAmount
É necessária uma restrição de altura na tabela para que a virtualização funcione corretamente (ex.: class="h-[400px]").

Com elemento de rolagem externo Soon

Passe uma função getScrollElement na prop virtualize para virtualizar em relação a um container de rolagem ancestral em vez do próprio root da tabela. Defina scrollMargin como o deslocamento da tabela em relação ao início do elemento de rolagem (ex.: a altura do conteúdo acima dela), para que um cabeçalho e o corpo da tabela compartilhem uma única barra de rolagem.

Payments

The title stays put while the wide table scrolls both axes under one scrollbar.

1000 rows
#DateClienteE-mailMethodStatusAmount
Nesse modo, o overflow do root da tabela é visible e o container externo controla a rolagem nos dois eixos, então dê a ele overflow-auto (não apenas overflow-y-auto) para manter tabelas largas roláveis horizontalmente. Um cabeçalho sticky então se ancora a esse container.

Com dados de árvore

Você pode usar a prop get-sub-rows para exibir dados hierárquicos (em árvore) na tabela. Por exemplo, se os seus objetos de dados têm um array children, defina :get-sub-rows="row => row.children" para habilitar linhas expansíveis.

#DateE-mailAmount
4600
Mar 11, 15:30[email protected]€594.00
4599
Mar 11, 10:10[email protected]€276.00
4598
Mar 11, 08:50[email protected]€315.00
4597
Mar 10, 19:45[email protected]€529.00
4589
Mar 9, 11:35[email protected]€389.00

Com slots

Você pode usar slots para personalizar o cabeçalho e as células de dados da tabela.

Use o slot #<column>-header para personalizar o cabeçalho de uma coluna. Você terá acesso às propriedades column, header e table no escopo do slot.

Use o slot #<column>-cell para personalizar a célula de uma coluna. Você terá acesso às propriedades cell, column, getValue, renderValue, row e table no escopo do slot.

IDNameE-mailRole
1
Lindsay Walton avatar

Lindsay Walton

Front-end Developer

[email protected]Member
2
Courtney Henry avatar

Courtney Henry

Designer

[email protected]Admin
3
Tom Cook avatar

Tom Cook

Director of Product

[email protected]Member
4
Whitney Francis avatar

Whitney Francis

Copywriter

[email protected]Admin
5
Leonard Krasner avatar

Leonard Krasner

Senior Designer

[email protected]Owner
6
Floyd Miles avatar

Floyd Miles

Principal Designer

[email protected]Member

API

Props

Prop Default Type
as'div'any

The element or component this component should render as.

data T[]
columns TableColumn<T, unknown>[]
caption string
meta TableMeta<T>

You can pass any object to options.meta and access it anywhere the table is available via table.options.meta.

virtualizefalseboolean | (Partial<Omit<VirtualizerOptions<Element, Element>, "count" | "estimateSize" | "overscan">> & { getScrollElement?: (() => Element | null) ; overscan?: number | undefined; estimateSize?: number | ((index: number) => number) | undefined; }) | undefined

Enable virtualization for large datasets. Note: row pinning is not supported when virtualization is enabled.

emptyt('table.noData') string

The text to display when the table is empty.

stickyfalseboolean | "header" | "footer"

Whether the table should have a sticky header or footer. True for both, 'header' for header only, 'footer' for footer only.

loadingboolean

Whether the table should be in loading state.

loadingColor'primary' "primary" | "secondary" | "accent" | "success" | "info" | "warning" | "error" | "neutral"
loadingAnimation'carousel' "carousel" | "carousel-inverse" | "swing" | "elastic"
watchOptions{ deep: true } WatchOptions<boolean>

Use the watchOptions prop to customize reactivity (for ex: disable deep watching for changes in your data or limiting the max traversal depth). This can improve performance by reducing unnecessary re-renders, but it should be used with caution as it may lead to unexpected behavior if not managed properly.

globalFilterOptions Omit<GlobalFilterOptions<T>, "onGlobalFilterChange">
columnFiltersOptions Omit<ColumnFiltersOptions<T>, "getFilteredRowModel" | "onColumnFiltersChange">
columnPinningOptions Omit<ColumnPinningOptions, "onColumnPinningChange">
columnSizingOptions Omit<ColumnSizingOptions, "onColumnSizingChange" | "onColumnSizingInfoChange">
visibilityOptions Omit<VisibilityOptions, "onColumnVisibilityChange">
sortingOptions Omit<SortingOptions<T>, "getSortedRowModel" | "onSortingChange">
groupingOptions Omit<GroupingOptions, "onGroupingChange">
expandedOptions Omit<ExpandedOptions<T>, "getExpandedRowModel" | "onExpandedChange">
rowSelectionOptions Omit<RowSelectionOptions<T>, "onRowSelectionChange">
rowPinningOptions Omit<RowPinningOptions<T>, "onRowPinningChange">
paginationOptions Omit<PaginationOptions, "onPaginationChange">
facetedOptions FacetedOptions<T>
onSelect (e: Event, row: TableRow<T>): void
onHover (e: Event, row: TableRow<T> | null): void
onContextmenu (e: Event, row: TableRow<T>): void | ((e: Event, row: TableRow<T>) => void)[]
state Partial<TableState>
onStateChange (updater: Updater<TableState>): void
renderFallbackValueany
_features TableFeature<any>[]

An array of extra features that you can add to the table instance.

autoResetAllboolean

Set this option to override any of the autoReset... feature options.

debugAllboolean

Set this option to true to output all debugging information to the console.

debugCellsboolean

Set this option to true to output cell debugging information to the console.

debugColumnsboolean

Set this option to true to output column debugging information to the console.

debugHeadersboolean

Set this option to true to output header debugging information to the console.

debugRowsboolean

Set this option to true to output row debugging information to the console.

debugTableboolean

Set this option to true to output table debugging information to the console.

defaultColumn Partial<ColumnDefBase<T, unknown> & StringHeaderIdentifier> | Partial<ColumnDefBase<T, unknown> & IdIdentifier<T, unknown>> | Partial<GroupColumnDefBase<T, unknown> & StringHeaderIdentifier> | Partial<GroupColumnDefBase<T, unknown> & IdIdentifier<T, unknown>> | Partial<AccessorKeyColumnDefBase<T, unknown> & Partial<StringHeaderIdentifier>> | Partial<AccessorKeyColumnDefBase<T, unknown> & Partial<IdIdentifier<T, unknown>>> | Partial<AccessorFnColumnDefBase<T, unknown> & StringHeaderIdentifier> | Partial<AccessorFnColumnDefBase<T, unknown> & IdIdentifier<T, unknown>>

Default column options to use for all column defs supplied to the table.

getRowId (originalRow: T, index: number, parent?: Row<T> | undefined): string

This optional function is used to derive a unique ID for any given row. If not provided the rows index is used (nested rows join together with . using their grandparents' index eg. index.index.index). If you need to identify individual rows that are originating from any server-side operations, it's suggested you use this function to return an ID that makes sense regardless of network IO/ambiguity eg. a userId, taskId, database ID field, etc.

getSubRows (originalRow: T, index: number): T[]

This optional function is used to access the sub rows for any given row. If you are using nested rows, you will need to use this function to return the sub rows object (or undefined) from the row.

initialState InitialTableState

Use this option to optionally pass initial state to the table. This state will be used when resetting various table states either automatically by the table (eg. options.autoResetPageIndex) or via functions like table.resetRowSelection(). Most reset function allow you optionally pass a flag to reset to a blank/default state instead of the initial state.

Table state will not be reset when this object changes, which also means that the initial state object does not need to be stable.

mergeOptions (defaultOptions: TableOptions<T>, options: Partial<TableOptions<T>>): TableOptions<T>

This option is used to optionally implement the merging of table options.

cellpadding string | number
cellspacing string | number
summary string
width string | number
globalFilter string
columnFilters ColumnFiltersState
columnOrder ColumnOrderState
columnVisibility VisibilityState
columnPinning ColumnPinningState
columnSizing ColumnSizingState
columnSizingInfo ColumnSizingInfoState
rowSelection RowSelectionState
rowPinning RowPinningState
sorting SortingState
grouping GroupingState
expanded true | Record<string, boolean>
pagination PaginationState
ui { root?: SlotClass; base?: SlotClass; caption?: SlotClass; thead?: SlotClass; tbody?: SlotClass; tfoot?: SlotClass; tr?: SlotClass; th?: SlotClass; td?: SlotClass; empty?: SlotClass; loading?: SlotClass; }
Este componente também suporta todos os atributos HTML nativos de <table>.

Slots

Slot Type
expanded{ row: Row<T>; }
empty{}
loading{}
caption{}
body-top{}
body-bottom{}

Expose

Você pode acessar a instância tipada do componente usando useTemplateRef.

<script setup lang="ts">
const table = useTemplateRef('table')
</script>

<template>
  <NTable ref="table" />
</template>

Isso dará a você acesso ao seguinte:

NameType
tableRefRef<HTMLTableElement | null>
tableApiTable

Tema

app.config.ts
export default defineAppConfig({
  ui: {
    table: {
      slots: {
        root: 'relative overflow-auto outline-accent/25 focus-visible:outline-3',
        base: 'min-w-full overflow-clip',
        caption: 'sr-only',
        thead: 'relative',
        tbody: 'isolate [&>tr[data-selectable=true]:hover>td]:bg-neutral-100 [&>tr]:data-[selectable=true]:focus-visible:outline-accent [&>tr:nth-child(even)>td]:bg-neutral-50',
        tfoot: 'relative',
        tr: 'data-[selected=true]:bg-accented',
        th: 'px-8 py-3.5 text-lg text-secondary text-left rtl:text-right font-semibold data-[sortable=true]:px-4 bg-clip-padding',
        td: 'px-8 py-4 text-base whitespace-nowrap not-last:border-r border-default bg-clip-padding',
        empty: 'py-6 text-center text-sm text-muted',
        loading: 'py-6 text-center'
      },
      variants: {
        pinned: {
          true: {
            th: 'sticky bg-default z-1',
            td: 'sticky bg-default z-1'
          }
        },
        sticky: {
          true: {
            thead: 'sticky top-0 inset-x-0 bg-default z-1',
            tfoot: 'sticky bottom-0 inset-x-0 bg-default z-1'
          },
          header: {
            thead: 'sticky top-0 inset-x-0 bg-default z-1'
          },
          footer: {
            tfoot: 'sticky bottom-0 inset-x-0 bg-default z-1'
          }
        },
        loading: {
          true: {
            thead: 'after:absolute after:z-1 after:h-px'
          }
        },
        empty: {
          true: {
            thead: 'hidden',
            tfoot: 'hidden'
          }
        },
        externalScroll: {
          true: {
            root: 'overflow-visible'
          }
        },
        loadingAnimation: {
          carousel: '',
          'carousel-inverse': '',
          swing: '',
          elastic: ''
        },
        loadingColor: {
          primary: '',
          secondary: '',
          accent: '',
          success: '',
          info: '',
          warning: '',
          error: '',
          neutral: ''
        }
      },
      compoundVariants: [
        {
          loading: true,
          loadingColor: 'primary',
          class: {
            thead: 'after:bg-primary'
          }
        },
        {
          loading: true,
          loadingColor: 'neutral',
          class: {
            thead: 'after:bg-inverted'
          }
        },
        {
          loading: true,
          loadingAnimation: 'carousel',
          class: {
            thead: 'after:animate-[carousel_2s_ease-in-out_infinite] rtl:after:animate-[carousel-rtl_2s_ease-in-out_infinite]'
          }
        },
        {
          loading: true,
          loadingAnimation: 'carousel-inverse',
          class: {
            thead: 'after:animate-[carousel-inverse_2s_ease-in-out_infinite] rtl:after:animate-[carousel-inverse-rtl_2s_ease-in-out_infinite]'
          }
        },
        {
          loading: true,
          loadingAnimation: 'swing',
          class: {
            thead: 'after:animate-[swing_2s_ease-in-out_infinite]'
          }
        },
        {
          loading: true,
          loadingAnimation: 'elastic',
          class: {
            thead: 'after:animate-[elastic_2s_ease-in-out_infinite]'
          }
        }
      ],
      defaultVariants: {
        loadingColor: 'accent',
        loadingAnimation: 'carousel'
      }
    }
  }
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nitro/ui/vite'

export default defineConfig({
  plugins: [
    vue(),
    ui({
      ui: {
        table: {
          slots: {
            root: 'relative overflow-auto outline-accent/25 focus-visible:outline-3',
            base: 'min-w-full overflow-clip',
            caption: 'sr-only',
            thead: 'relative',
            tbody: 'isolate [&>tr[data-selectable=true]:hover>td]:bg-neutral-100 [&>tr]:data-[selectable=true]:focus-visible:outline-accent [&>tr:nth-child(even)>td]:bg-neutral-50',
            tfoot: 'relative',
            tr: 'data-[selected=true]:bg-accented',
            th: 'px-8 py-3.5 text-lg text-secondary text-left rtl:text-right font-semibold data-[sortable=true]:px-4 bg-clip-padding',
            td: 'px-8 py-4 text-base whitespace-nowrap not-last:border-r border-default bg-clip-padding',
            empty: 'py-6 text-center text-sm text-muted',
            loading: 'py-6 text-center'
          },
          variants: {
            pinned: {
              true: {
                th: 'sticky bg-default z-1',
                td: 'sticky bg-default z-1'
              }
            },
            sticky: {
              true: {
                thead: 'sticky top-0 inset-x-0 bg-default z-1',
                tfoot: 'sticky bottom-0 inset-x-0 bg-default z-1'
              },
              header: {
                thead: 'sticky top-0 inset-x-0 bg-default z-1'
              },
              footer: {
                tfoot: 'sticky bottom-0 inset-x-0 bg-default z-1'
              }
            },
            loading: {
              true: {
                thead: 'after:absolute after:z-1 after:h-px'
              }
            },
            empty: {
              true: {
                thead: 'hidden',
                tfoot: 'hidden'
              }
            },
            externalScroll: {
              true: {
                root: 'overflow-visible'
              }
            },
            loadingAnimation: {
              carousel: '',
              'carousel-inverse': '',
              swing: '',
              elastic: ''
            },
            loadingColor: {
              primary: '',
              secondary: '',
              accent: '',
              success: '',
              info: '',
              warning: '',
              error: '',
              neutral: ''
            }
          },
          compoundVariants: [
            {
              loading: true,
              loadingColor: 'primary',
              class: {
                thead: 'after:bg-primary'
              }
            },
            {
              loading: true,
              loadingColor: 'neutral',
              class: {
                thead: 'after:bg-inverted'
              }
            },
            {
              loading: true,
              loadingAnimation: 'carousel',
              class: {
                thead: 'after:animate-[carousel_2s_ease-in-out_infinite] rtl:after:animate-[carousel-rtl_2s_ease-in-out_infinite]'
              }
            },
            {
              loading: true,
              loadingAnimation: 'carousel-inverse',
              class: {
                thead: 'after:animate-[carousel-inverse_2s_ease-in-out_infinite] rtl:after:animate-[carousel-inverse-rtl_2s_ease-in-out_infinite]'
              }
            },
            {
              loading: true,
              loadingAnimation: 'swing',
              class: {
                thead: 'after:animate-[swing_2s_ease-in-out_infinite]'
              }
            },
            {
              loading: true,
              loadingAnimation: 'elastic',
              class: {
                thead: 'after:animate-[elastic_2s_ease-in-out_infinite]'
              }
            }
          ],
          defaultVariants: {
            loadingColor: 'accent',
            loadingAnimation: 'carousel'
          }
        }
      }
    })
  ]
})
Some colors in compoundVariants are omitted for readability. Check out the source code on GitLab.

Changelog

No recent changes