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.
| # | Date | Status | Amount | |||
|---|---|---|---|---|---|---|
| #4600 | Mar 11, 15:30 | paid | [email protected] | €594.00 | ||
| #4599 | Mar 11, 10:10 | failed | [email protected] | €276.00 | ||
| #4598 | Mar 11, 08:50 | refunded | [email protected] | €315.00 | ||
| #4597 | Mar 10, 19:45 | paid | [email protected] | €529.00 | ||
| #4596 | Mar 10, 15:55 | paid | [email protected] | €639.00 | ||
| #4595 | Mar 10, 13:40 | refunded | [email protected] | €428.00 | ||
| #4594 | Mar 10, 09:15 | paid | [email protected] | €683.00 | ||
| #4593 | Mar 9, 20:25 | failed | [email protected] | €947.00 | ||
| #4592 | Mar 9, 18:45 | paid | [email protected] | €851.00 | ||
| #4591 | Mar 9, 16:05 | paid | [email protected] | €762.00 | ||
| #4590 | Mar 9, 14:20 | paid | [email protected] | €573.00 | ||
| #4589 | Mar 9, 11:35 | failed | [email protected] | €389.00 | ||
| #4588 | Mar 8, 22:50 | refunded | [email protected] | €701.00 | ||
| #4587 | Mar 8, 20:15 | paid | [email protected] | €856.00 | ||
| #4586 | Mar 8, 17:40 | paid | [email protected] | €492.00 | ||
| #4585 | Mar 8, 14:55 | failed | [email protected] | €637.00 | ||
| #4584 | Mar 8, 12:30 | paid | [email protected] | €784.00 | ||
| #4583 | Mar 8, 09:45 | refunded | [email protected] | €345.00 | ||
| #4582 | Mar 7, 23:10 | paid | [email protected] | €918.00 | ||
| #4581 | Mar 7, 20:25 | paid | [email protected] | €567.00 |
Table. Confira o código-fonte no GitHub.Use a prop data como um array de objetos; as colunas serão geradas com base nas chaves dos objetos.
| Id | Date | Status | Amount | |
|---|---|---|---|---|
| 4600 | 2024-03-11T15:30:00 | paid | [email protected] | 594 |
| 4599 | 2024-03-11T10:10:00 | failed | [email protected] | 276 |
| 4598 | 2024-03-11T08:50:00 | refunded | [email protected] | 315 |
| 4597 | 2024-03-10T19:45:00 | paid | [email protected] | 529 |
| 4596 | 2024-03-10T15:55:00 | paid | [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>
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.
| # | Date | Status | Amount | |
|---|---|---|---|---|
| #4600 | Mar 11, 15:30 | paid | [email protected] | €594.00 |
| #4599 | Mar 11, 10:10 | failed | [email protected] | €276.00 |
| #4598 | Mar 11, 08:50 | refunded | [email protected] | €315.00 |
| #4597 | Mar 10, 19:45 | paid | [email protected] | €529.00 |
| #4596 | Mar 10, 15:55 | paid | [email protected] | €639.00 |
h, você pode usar a função resolveComponent ou importar de #components.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.| ID | Date | Status | Amount | |
|---|---|---|---|---|
| 4600 | Mar 11, 15:30 | paid | [email protected] | $594.00 |
| 4599 | Mar 11, 10:10 | failed | [email protected] | $276.00 |
| 4598 | Mar 11, 08:50 | refunded | [email protected] | $315.00 |
| 4597 | Mar 10, 19:45 | paid | [email protected] | $529.00 |
| 4596 | Mar 10, 15:55 | paid | [email protected] | $639.00 |
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.
| Id | Date | Status | Amount | |
|---|---|---|---|---|
| 4600 | 2024-03-11T15:30:00 | paid | [email protected] | 594 |
| 4599 | 2024-03-11T10:10:00 | failed | [email protected] | 276 |
| 4598 | 2024-03-11T08:50:00 | refunded | [email protected] | 315 |
| 4597 | 2024-03-10T19:45:00 | paid | [email protected] | 529 |
| 4596 | 2024-03-10T15:55:00 | paid | [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>
Use a prop sticky para tornar o cabeçalho ou o rodapé fixos.
| Id | Date | Status | Amount | |
|---|---|---|---|---|
| 4600 | 2024-03-11T15:30:00 | paid | [email protected] | 594 |
| 4599 | 2024-03-11T10:10:00 | failed | [email protected] | 276 |
| 4598 | 2024-03-11T08:50:00 | refunded | [email protected] | 315 |
| 4597 | 2024-03-10T19:45:00 | paid | [email protected] | 529 |
| 4596 | 2024-03-10T15:55:00 | paid | [email protected] | 639 |
| 4595 | 2024-03-10T15:55:00 | paid | [email protected] | 639 |
| 4594 | 2024-03-10T15:55:00 | paid | [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>
Você pode adicionar uma nova coluna que renderiza um componente DropdownMenu dentro da cell para renderizar ações de linha.
| # | Date | Status | Amount | ||
|---|---|---|---|---|---|
| #4600 | Mar 11, 15:30 | paid | [email protected] | €594.00 | |
| #4599 | Mar 11, 10:10 | failed | [email protected] | €276.00 | |
| #4598 | Mar 11, 08:50 | refunded | [email protected] | €315.00 | |
| #4597 | Mar 10, 19:45 | paid | [email protected] | €529.00 | |
| #4596 | Mar 10, 15:55 | paid | [email protected] | €639.00 |
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.
#expanded para renderizar o conteúdo expandido, que receberá a linha como parâmetro.| # | Date | Status | Amount | ||
|---|---|---|---|---|---|
| #4600 | Mar 11, 15:30 | paid | [email protected] | €594.00 | |
| #4599 | Mar 11, 10:10 | failed | [email protected] | €276.00 | |
{
"id": "4599",
"date": "2024-03-11T10:10:00",
"status": "failed",
"email": "[email protected]",
"amount": 276
} | |||||
| #4598 | Mar 11, 08:50 | refunded | [email protected] | €315.00 | |
| #4597 | Mar 10, 19:45 | paid | [email protected] | €529.00 | |
| #4596 | Mar 10, 15:55 | paid | [email protected] | €639.00 | |
expanded para controlar o estado expansível das linhas (pode ser vinculada com v-model).DropdownMenu dentro da coluna actions.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.
grouping prop with an array of column ids you want to group by.grouping-options prop. It must include getGroupedRowModel, you can import it from @tanstack/vue-table or implement your own.row.toggleExpanded() method on any cell of the row. Keep in mind, it also toggles #expanded slot.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 | # | Date | Amount | |
|---|---|---|---|---|
Conta 1 | 3 records | Mar 11, 15:30 | 3 customers | €1,548.00 |
Conta 2 | 2 records | Mar 11, 10:10 | 2 customers | €805.00 |
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.
| Date | Status | Amount | ||
|---|---|---|---|---|
| Mar 11, 10:10 | failed | [email protected] | €276.00 | |
| Mar 10, 19:45 | paid | [email protected] | €529.00 | |
| Mar 11, 15:30 | paid | [email protected] | €594.00 | |
| Mar 11, 08:50 | refunded | [email protected] | €315.00 | |
| Mar 10, 15:55 | paid | [email protected] | €639.00 | |
| Mar 10, 13:40 | refunded | [email protected] | €428.00 | |
| Mar 10, 09:15 | paid | [email protected] | €683.00 | |
| Mar 9, 20:25 | failed | [email protected] | €947.00 | |
| Mar 9, 18:45 | paid | [email protected] | €851.00 | |
| Mar 9, 16:05 | paid | [email protected] | €762.00 |
row-pinning para controlar o estado de fixação das linhas (pode ser vinculada com v-model).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.
| Date | Status | Amount | ||
|---|---|---|---|---|
| Mar 11, 15:30 | paid | [email protected] | €594.00 | |
| Mar 11, 10:10 | failed | [email protected] | €276.00 | |
| Mar 11, 08:50 | refunded | [email protected] | €315.00 | |
| Mar 10, 19:45 | paid | [email protected] | €529.00 | |
| Mar 10, 15:55 | paid | [email protected] | €639.00 |
row-selection para controlar o estado de seleção das linhas (pode ser vinculada com v-model).Você pode adicionar um listener @select para tornar as linhas clicáveis com ou sem uma coluna de checkbox.
Event e TableRow como primeiro e segundo argumentos, respectivamente.| Date | Status | Amount | ||
|---|---|---|---|---|
| Mar 11, 15:30 | paid | [email protected] | €594.00 | |
| Mar 11, 10:10 | failed | [email protected] | €276.00 | |
| Mar 11, 08:50 | refunded | [email protected] | €315.00 | |
| Mar 10, 19:45 | paid | [email protected] | €529.00 | |
| Mar 10, 15:55 | paid | [email protected] | €639.00 |
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.
Event e TableRow como primeiro e segundo argumentos, respectivamente.| # | Date | Status | Amount | ||
|---|---|---|---|---|---|
| #4600 | Mar 11, 15:30 | paid | [email protected] | €594.00 | |
| #4599 | Mar 11, 10:10 | failed | [email protected] | €276.00 | |
| #4598 | Mar 11, 08:50 | refunded | [email protected] | €315.00 | |
| #4597 | Mar 10, 19:45 | paid | [email protected] | €529.00 | |
| #4596 | Mar 10, 15:55 | paid | [email protected] | €639.00 |
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.
Event e TableRow como primeiro e segundo argumentos, respectivamente.| # | Date | Status | Amount | ||
|---|---|---|---|---|---|
| #4600 | Mar 11, 15:30 | paid | [email protected] | €594.00 | |
| #4599 | Mar 11, 10:10 | failed | [email protected] | €276.00 | |
| #4598 | Mar 11, 08:50 | refunded | [email protected] | €315.00 | |
| #4597 | Mar 10, 19:45 | paid | [email protected] | €529.00 | |
| #4596 | Mar 10, 15:55 | paid | [email protected] | €639.00 |
refDebounced para evitar que o Popover abra e feche rápido demais ao mover o cursor de uma linha para outra.Você pode adicionar uma propriedade footer à definição da coluna para renderizar um rodapé para a coluna.
| # | Date | Status | Amount | |
|---|---|---|---|---|
| #4600 | Mar 11, 15:30 | paid | [email protected] | €594.00 |
| #4599 | Mar 11, 10:10 | failed | [email protected] | €276.00 |
| #4598 | Mar 11, 08:50 | refunded | [email protected] | €315.00 |
| #4597 | Mar 10, 19:45 | paid | [email protected] | €529.00 |
| #4596 | Mar 10, 15:55 | paid | [email protected] | €639.00 |
| Total: €2,353.00 |
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.
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.| Category | Name | Preço | Stock |
|---|---|---|---|
| Electronics | Notebook | $999.00 | 45 |
| Telefone | $699.00 | 120 | |
| Tablet | $499.00 | 78 | |
| Clothing | T-Shirt | $29.00 | 200 |
| Jeans | $59.00 | 150 |
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.
| # | Date | Status | Amount | |
|---|---|---|---|---|
| #4597 | Mar 10, 19:45 | paid | [email protected] | €529.00 |
| #4596 | Mar 10, 15:55 | paid | [email protected] | €639.00 |
| #4600 | Mar 11, 15:30 | paid | [email protected] | €594.00 |
| #4599 | Mar 11, 10:10 | failed | [email protected] | €276.00 |
| #4598 | Mar 11, 08:50 | refunded | [email protected] | €315.00 |
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.
| #4596 | Mar 10, 15:55 | paid | [email protected] | €639.00 |
| #4597 | Mar 10, 19:45 | paid | [email protected] | €529.00 |
| #4598 | Mar 11, 08:50 | refunded | [email protected] | €315.00 |
| #4599 | Mar 11, 10:10 | failed | [email protected] | €276.00 |
| #4600 | Mar 11, 15:30 | paid | [email protected] | €594.00 |
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.
size explícitos para as suas colunas para garantir o tratamento correto da largura das colunas, especialmente com várias colunas fixadas.| #4600000000000000000000000000000000000000 | 2024-03-11T15:30:00 | paid | [email protected] | €594,000.00 |
| #4599000000000000000000000000000000000000 | 2024-03-11T10:10:00 | failed | [email protected] | €276,000.00 |
| #4598000000000000000000000000000000000000 | 2024-03-11T08:50:00 | refunded | [email protected] | €315,000.00 |
| #4597000000000000000000000000000000000000 | 2024-03-10T19:45:00 | paid | [email protected] | €5,290,000.00 |
| #4596000000000000000000000000000000000000 | 2024-03-10T15:55:00 | paid | [email protected] | €639,000.00 |
column-pinning para controlar o estado de fixação das colunas (pode ser vinculada com v-model).Você pode usar um componente DropdownMenu para alternar a visibilidade das colunas usando as APIs de Column Visibility do TanStack Table.
| Date | Status | Amount | |
|---|---|---|---|
| Mar 11, 15:30 | paid | [email protected] | €594.00 |
| Mar 11, 10:10 | failed | [email protected] | €276.00 |
| Mar 11, 08:50 | refunded | [email protected] | €315.00 |
| Mar 10, 19:45 | paid | [email protected] | €529.00 |
| Mar 10, 15:55 | paid | [email protected] | €639.00 |
column-visibility para controlar o estado de visibilidade das colunas (pode ser vinculada com v-model).Você pode usar um componente Input para filtrar as linhas por coluna usando as APIs de Column Filtering do TanStack Table.
| # | Date | Status | Amount | |
|---|---|---|---|---|
| #4600 | Mar 11, 15:30 | paid | [email protected] | €594.00 |
column-filters para controlar o estado dos filtros das colunas (pode ser vinculada com v-model).Você pode usar um componente Input para filtrar as linhas usando as APIs de Global Filtering do TanStack Table.
| # | Date | Status | Amount | |
|---|---|---|---|---|
| #4599 | Mar 11, 10:10 | failed | [email protected] | €276.00 |
| #4598 | Mar 11, 08:50 | refunded | [email protected] | €315.00 |
| #4597 | Mar 10, 19:45 | paid | [email protected] | €529.00 |
| #4596 | Mar 10, 15:55 | paid | [email protected] | €639.00 |
global-filter para controlar o estado do filtro global (pode ser vinculada com v-model).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().
| # | Date | Amount | |
|---|---|---|---|
| #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 |
| #4596 | Mar 10, 15:55 | [email protected] | €639.00 |
pagination para controlar o estado de paginação (pode ser vinculada com v-model).Você pode buscar dados de uma API e usá-los na Table.
| ID | Name | Company | |
|---|---|---|---|
| Nenhum dado | |||
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.Se você usa paginação no lado do servidor, pode usar o composable useInfiniteScroll para carregar mais dados conforme o usuário rola.
| ID | Avatar | First name | Nome de usuário | |
|---|---|---|---|---|
| Nenhum dado | ||||
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.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.
:ui para mirá-lo com o useSortable (ex.: :ui="{ tbody: 'my-table-tbody' }").| # | Date | Amount | |
|---|---|---|---|
| #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 |
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.
| # | Date | Status | Amount |
|---|
class="h-[400px]").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.
The title stays put while the wide table scrolls both axes under one scrollbar.
| # | Date | Cliente | Method | Status | Amount |
|---|
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.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.
| # | Date | Amount | ||
|---|---|---|---|---|
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 | |
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.
| ID | Name | Role | ||
|---|---|---|---|---|
| 1 | Lindsay Walton Front-end Developer | [email protected] | Member | |
| 2 | Courtney Henry Designer | [email protected] | Admin | |
| 3 | Tom Cook Director of Product | [email protected] | Member | |
| 4 | Whitney Francis Copywriter | [email protected] | Admin | |
| 5 | Leonard Krasner Senior Designer | [email protected] | Owner | |
| 6 | Floyd Miles Principal Designer | [email protected] | Member |
| Prop | Default | Type |
|---|---|---|
as | 'div' | anyThe 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 | |
virtualize | false | boolean | (Partial<Omit<VirtualizerOptions<Element, Element>, "count" | "estimateSize" | "overscan">> & { getScrollElement?: (() => Element | null) ; overscan?: number | undefined; estimateSize?: number | ((index: number) => number) | undefined; }) | undefinedEnable virtualization for large datasets. Note: row pinning is not supported when virtualization is enabled. |
empty | t('table.noData') | stringThe text to display when the table is empty. |
sticky | false | boolean | "header" | "footer" Whether the table should have a sticky header or footer. True for both, 'header' for header only, 'footer' for footer only. |
loading | boolean 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 |
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 | |
renderFallbackValue | any | |
_features | TableFeature<any>[]An array of extra features that you can add to the table instance. | |
autoResetAll | boolean Set this option to override any of the | |
debugAll | boolean Set this option to | |
debugCells | boolean Set this option to | |
debugColumns | boolean Set this option to | |
debugHeaders | boolean Set this option to | |
debugRows | boolean Set this option to | |
debugTable | boolean Set this option to | |
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): stringThis 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 | |
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 | InitialTableStateUse 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. 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; } |
| Slot | Type |
|---|---|
expanded | { row: Row<T>; } |
empty | {} |
loading | {} |
caption | {} |
body-top | {} |
body-bottom | {} |
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:
| Name | Type |
|---|---|
tableRef | Ref<HTMLTableElement | null> |
tableApi | Table |
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'
}
}
}
})
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'
}
}
}
})
]
})