---
title: "Tabela de dados"
description: "Uma tabela do Nuxt UI com 2.000 tarefas, virtualizada e pesquisável, ordenada arrastando as linhas pelas alças."
canonical_url: "https://zizigy.github.io/vue-dnd-kit/pt/docs/examples/data-table"
---

> Index of every page on this site: [llms.txt](https://zizigy.github.io/vue-dnd-kit/llms.txt)

<example height="34rem" hint="Arraste uma tarefa pela alça; pesquise, depois arraste: as tarefas ocultas mantêm os seus lugares" name="ui-table" title="Tabela de dados">



</example>

O `UTable` do Nuxt UI renderiza a tabela, as linhas e o virtualizador dela: só as linhas que estão na
tela ficam no DOM. O Vue DnD Kit as ordena. A tabela é a da biblioteca do jeito que vem, com os seus
slots e as suas classes `ui`; nada dela é reescrito.

## O que observar

- **TaskTable.vue**: a lista ordenável é o `<tbody>` da tabela, encontrado pelo `tableRef` que o
`UTable` expõe. A tabela recebe o rascunho da lista como `data`, então as linhas se movem enquanto
você arrasta, e `get-row-id` dá às linhas a key da tarefa, como a lista faz. `axis: 'y'` mantém o
arraste na coluna dele, e `restrictToFirstScrollableAncestor`, dentro da tabela: o contêiner de
rolagem dela é o primeiro elemento rolável em volta das linhas. Expanda a demo: a tabela preenche
a tela.
- **RowGrip.vue**: o `UTable` renderiza as linhas, então nenhum componente seu é uma linha. A alça
na primeira célula (o slot `#grip-cell`) torna a linha em volta dela o item, e é o handle dele.
- **A busca** é o `filter` da lista: a tabela mostra as tarefas que correspondem, e um drop grava de
volta o array inteiro, com as tarefas ocultas onde estavam.
- **tasks.ts**: 2.000 tarefas em um `shallowRef`: a biblioteca nunca olha dentro delas.

## Uma linha que a tabela renderiza

A alça chama `makeSortableItem` com um getter para a linha: o `<tr>` em volta dela, depois de
renderizado. Como handle, a alça recebe o foco e os atributos ARIA, e o resto da linha fica livre
para cliques e seleção de texto.

```ts [RowGrip.vue]
const grip = useTemplateRef<HTMLElement>('grip');
makeSortableItem(() => grip.value?.closest('tr') ?? null, {
  id: () => props.task.id,
  handle: grip,
  label: () => props.task.title,
});
```

```vue [TaskTable.vue]
<UTable ref="table" :data="items" :get-row-id="(task: Task) => task.id" …>
  <template #grip-cell="{ row }">
    <RowGrip :task="row.original" />
  </template>
</UTable>
```

O overlay é a cópia da linha feita pela biblioteca, em uma tabela própria com as larguras das
células. Uma linha de tabela não desenha sombra, então o `overlay-class` do provider dá uma ao
conteúdo do overlay, com o fundo da tabela e um anel. As classes `ui` da linha tingem o lugar dela
enquanto ela viaja (`data-[dnd-placeholder]:bg-primary/5`).

```vue [index.vue]
<DnDProvider overlay-class="overflow-hidden rounded-md bg-default shadow-xl ring ring-accented">
  <TaskTable />
</DnDProvider>
```

## Linhas virtualizadas

`virtualize` dá à tabela o TanStack Virtual: as linhas na tela e mais 8 em volta delas (`overscan`)
são renderizadas, entre duas linhas espaçadoras.

```vue [TaskTable.vue]
<UTable
  :data="items"
  :watch-options="{ deep: false }"
  :virtualize="{ estimateSize: 45, overscan: 8, onChange: () => captureLayout() }"
  class="min-h-0 flex-1"
/>
```

- `estimateSize` é um primeiro palpite: o virtualizador mede cada linha conforme a renderiza. Uma
medição move as linhas abaixo dela, e `onChange` chama o `captureLayout()` da lista, então a
biblioteca anima esse movimento durante um arraste em vez de deixá-lo pular.
- A tabela é o contêiner de rolagem, e a linha na mão fica dentro dela: perto da borda de cima ou de
baixo, um arraste a rola, e novas linhas são renderizadas conforme entram na tela.
- O rascunho é um array novo depois de cada movimento, então a tabela não precisa observar dentro
dele (`deep: false`).
- Role para longe da linha que você está segurando, e ela sai do intervalo renderizado e é
desmontada. O arraste continua: o overlay ainda a mostra, e ela pousa onde o rascunho a coloca. Uma
lista virtual sua pode mantê-la montada com `rangeExtractor` (veja
[Listas virtuais](https://zizigy.github.io/docs/guides/virtual-lists#tanstack-virtual)). Uma tabela dispõe as linhas entre
linhas espaçadoras, onde uma linha renderizada longe das outras as empurraria para o lado, então
esta a deixa ir.

## Pesquise, depois arraste

```ts [TaskTable.vue]
const { items, captureLayout } = makeSortableList(() => table.value?.tableRef?.tBodies[0] ?? null, {
  items: tasks,
  filter: (task) => {
    const text = query.value.trim().toLowerCase();
    return !text || `${task.id} ${task.title} ${task.assignee}`.toLowerCase().includes(text);
  },
  axis: 'y',
  modifiers: [restrictToFirstScrollableAncestor],
  label: 'Tasks',
});
```

Pesquise "Mia", mova uma das tarefas dela para cima de outra e depois limpe a busca: as tarefas dela
estão na nova ordem, e todas as outras tarefas estão onde estavam. O `e.to.index` do drop conta as
linhas mostradas; `e.result` contém o array inteiro.

<tip>

**Outras bibliotecas de UI.** O Nuxt UI renderiza elementos simples e repassa as suas classes e
atributos, como fazem as primitivas do Reka UI sobre as quais ele é construído: um ref, um slot e uma
classe são tudo o que é preciso. Em outras bibliotecas, uma tabela também renderiza as linhas, e a
mesma receita funciona:

- **A linha.** Um slot que renderiza a linha inteira (`#item` do Vuetify, `#body` do Quasar) dá a
você o elemento dela: torne-o o item. Onde você só recebe células (o `#body` de uma `Column` do
PrimeVue, o slot padrão de uma coluna do Element Plus, o `render` do Naive UI), coloque uma alça
em uma delas e torne o `closest('tr')` dela o item, como aqui.
- **A lista.** O `<tbody>` dentro do elemento raiz da tabela: um template ref na tabela dá a você o
componente, e o `$el` dele, o elemento.
- **As keys e os dados.** Dê às linhas a key do seu id (`item-value`, `row-key`, `dataKey`) e passe
para a tabela o rascunho da lista como os dados dela.
- **Ordenação própria desligada.** Deixe de fora a reordenação de linhas da própria tabela e a
ordenação por colunas: a ordem é a da lista.
- **Tabelas virtuais** posicionam as linhas entre espaçadores ou em caixas absolutas: os dois
funcionam, e um arraste sobrevive à desmontagem da sua linha.

</tip>

<note>

O botão do StackBlitz abre os arquivos em um projeto Vite configurado para o Nuxt UI, como a
documentação dele faz para Vue: o plugin `@nuxt/ui/vite`, o Tailwind CSS e `UApp` em volta do
exemplo. Em um app Nuxt com o módulo `@nuxt/ui`, copie a pasta e renderize `index.vue` em uma
página.

</note>

## Teclado e leitores de tela

Chegue com Tab à alça de uma tarefa e pressione <kbd value="Space">



</kbd>

 para pegar a linha.
<kbd value="ArrowUp">



</kbd>

 e <kbd value="ArrowDown">



</kbd>

 a movem, <kbd value="Space">



</kbd>

 a solta e
<kbd value="Escape">



</kbd>

 a devolve ao lugar. As linhas têm um `label`, e a lista também: os leitores de
tela ouvem "Fix the login form dropped at position 3 of 2000 in Tasks.", não keys.

Leia mais: [Listas virtuais](https://zizigy.github.io/docs/guides/virtual-lists) ·
[Mostrar parte dos itens](https://zizigy.github.io/docs/guides/sortable-lists#show-some-of-the-items) ·
[Estilização: Tailwind e kits de UI](https://zizigy.github.io/docs/guides/styling#tailwind-css) ·
[Acessibilidade](https://zizigy.github.io/docs/guides/accessibility)

## Sitemap

See the full [sitemap](https://zizigy.github.io/vue-dnd-kit/sitemap.md) for all pages.
