This commit is contained in:
Luiz Silva 2026-01-28 09:44:10 -03:00
parent 92662a0b13
commit 933ba17ae8

View file

@ -1,11 +1,221 @@
<template>
<!-- Render é feito no script via função render para suportar VNodeChild em células -->
<div />
<div class="eli-tabela">
<div v-if="carregando" class="eli-tabela eli-tabela--carregando" aria-busy="true">
Carregando...
</div>
<div v-else-if="erro" class="eli-tabela eli-tabela--erro" role="alert">
<div class="eli-tabela__erro-titulo">Erro</div>
<div class="eli-tabela__erro-mensagem">{{ erro }}</div>
</div>
<div v-else-if="!linhas.length" class="eli-tabela eli-tabela--vazio">
{{ tabela.mensagemVazio ?? "Nenhum registro encontrado." }}
</div>
<template v-else>
<div v-if="exibirBusca || temAcoesCabecalho" class="eli-tabela__cabecalho">
<EliTabelaCaixaDeBusca
v-if="exibirBusca"
:modelo="valorBusca"
@buscar="atualizarBusca"
/>
<div v-if="temAcoesCabecalho" class="eli-tabela__acoes-cabecalho">
<button
v-for="(botao, indice) in acoesCabecalho"
:key="`${botao.rotulo}-${indice}`"
type="button"
class="eli-tabela__acoes-cabecalho-botao"
:style="botao.cor ? { backgroundColor: botao.cor, color: '#fff' } : undefined"
@click="botao.acao"
>
<component
v-if="botao.icone"
:is="botao.icone"
class="eli-tabela__acoes-cabecalho-icone"
:size="16"
:stroke-width="2"
/>
<span class="eli-tabela__acoes-cabecalho-rotulo">{{ botao.rotulo }}</span>
</button>
</div>
</div>
<table class="eli-tabela__table">
<thead class="eli-tabela__thead">
<tr class="eli-tabela__tr eli-tabela__tr--header">
<th
v-for="(coluna, idx) in tabela.colunas"
:key="`th-${idx}`"
class="eli-tabela__th"
:class="[
isOrdenavel(coluna) ? 'eli-tabela__th--ordenavel' : undefined,
obterClasseAlinhamento(coluna.alinhamento),
]"
scope="col"
>
<button
v-if="isOrdenavel(coluna)"
type="button"
class="eli-tabela__th-botao"
:class="[
colunaOrdenacao === String(coluna.coluna_ordem) ? 'eli-tabela__th-botao--ativo' : undefined,
]"
@click="alternarOrdenacao(String(coluna.coluna_ordem))"
>
<span class="eli-tabela__th-texto">{{ coluna.rotulo }}</span>
<component
v-if="colunaOrdenacao === String(coluna.coluna_ordem)"
:is="direcaoOrdenacao === 'asc' ? ArrowUp : ArrowDown"
class="eli-tabela__th-icone"
:size="16"
:stroke-width="2"
aria-hidden="true"
/>
<ArrowUp
v-else
class="eli-tabela__th-icone eli-tabela__th-icone--oculto"
:size="16"
:stroke-width="2"
aria-hidden="true"
/>
</button>
<span v-else class="eli-tabela__th-label">{{ coluna.rotulo }}</span>
</th>
<th
v-if="temAcoes"
class="eli-tabela__th eli-tabela__th--acoes"
scope="col"
>
Ações
</th>
</tr>
</thead>
<tbody class="eli-tabela__tbody">
<tr
v-for="(linha, i) in linhas"
:key="`tr-${i}`"
class="eli-tabela__tr"
:class="[i % 2 === 1 ? 'eli-tabela__tr--zebra' : undefined]"
>
<td
v-for="(coluna, j) in tabela.colunas"
:key="`td-${i}-${j}`"
class="eli-tabela__td"
:class="[
coluna.acao ? 'eli-tabela__td--clicavel' : undefined,
obterClasseAlinhamento(coluna.alinhamento),
]"
@click="coluna.acao ? () => coluna.acao?.() : undefined"
>
<span
v-if="Boolean(coluna.truncar)"
class="eli-tabela__celula-conteudo"
:style="coluna.largura_maxima ? { maxWidth: obterMaxWidth(coluna.largura_maxima) } : undefined"
:title="obterTooltipCelula(coluna.celula(linha as never))"
>
<EliTabelaCelula :celula="(coluna.celula(linha as never) as never)" />
</span>
<EliTabelaCelula
v-else
:celula="(coluna.celula(linha as never) as never)"
/>
</td>
<td
v-if="temAcoes"
class="eli-tabela__td eli-tabela__td--acoes"
:key="`td-${i}-acoes`"
>
<div
class="eli-tabela__acoes-container"
:class="[menuAberto === i ? 'eli-tabela__acoes-container--aberto' : undefined]"
>
<button
class="eli-tabela__acoes-toggle"
type="button"
:id="`eli-tabela-acoes-toggle-${i}`"
:disabled="!possuiAcoes(i)"
:aria-haspopup="'menu'"
:aria-expanded="menuAberto === i ? 'true' : 'false'"
:aria-controls="possuiAcoes(i) ? `eli-tabela-acoes-menu-${i}` : undefined"
:aria-label="possuiAcoes(i) ? 'Ações da linha' : 'Nenhuma ação disponível'"
:title="possuiAcoes(i) ? 'Ações' : 'Nenhuma ação disponível'"
@click.stop="toggleMenu(i)"
>
<MoreVertical class="eli-tabela__acoes-toggle-icone" :size="18" :stroke-width="2" />
</button>
<ul
v-if="menuAberto === i && possuiAcoes(i)"
:id="`eli-tabela-acoes-menu-${i}`"
class="eli-tabela__acoes-menu"
role="menu"
:aria-labelledby="`eli-tabela-acoes-toggle-${i}`"
>
<li
v-for="item in acoesDisponiveisPorLinha(i)"
:key="`acao-${i}-${item.indice}`"
class="eli-tabela__acoes-item"
role="none"
>
<button
type="button"
class="eli-tabela__acoes-item-botao"
:style="{ color: item.acao.cor }"
role="menuitem"
:aria-label="item.acao.rotulo"
:title="item.acao.rotulo"
@click.stop="
() => {
menuAberto = null;
item.acao.acao(linha as never);
}
"
>
<component
:is="item.acao.icone"
class="eli-tabela__acoes-item-icone"
:size="16"
:stroke-width="2"
/>
<span class="eli-tabela__acoes-item-texto">{{ item.acao.rotulo }}</span>
</button>
</li>
</ul>
</div>
</td>
</tr>
</tbody>
</table>
<EliTabelaPaginacao
v-if="totalPaginas > 1 && quantidade > 0"
:pagina="paginaAtual"
:totalPaginas="totalPaginas"
:maximoBotoes="tabela.maximo_botoes_paginacao"
@alterar="irParaPagina"
/>
</template>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, h, onBeforeUnmount, onMounted, PropType, ref, watch } from "vue";
import type { ComponentPublicInstance } from "vue";
import {
computed,
defineComponent,
onBeforeUnmount,
onMounted,
PropType,
ref,
watch,
} from "vue";
import { ArrowDown, ArrowUp, MoreVertical } from "lucide-vue-next";
import { codigosResposta } from "p-respostas";
import EliTabelaCaixaDeBusca from "./EliTabelaCaixaDeBusca.vue";
@ -16,11 +226,16 @@ import type { EliTabelaConsulta } from "./types-eli-tabela";
export default defineComponent({
name: "EliTabela",
inheritAttrs: false,
components: {
EliTabelaCaixaDeBusca,
EliTabelaPaginacao,
EliTabelaCelula,
ArrowUp,
ArrowDown,
MoreVertical,
},
props: {
tabela: {
// Observação: este componente é generic-friendly.
// Usamos `any` aqui para permitir passar `EliTabelaConsulta<T>` de qualquer T
// sem brigar com invariância do TS (por causa do callback `celula(linha: T)`).
type: Object as PropType<EliTabelaConsulta<any>>,
required: true,
},
@ -30,16 +245,22 @@ export default defineComponent({
const erro = ref<string | null>(null);
const linhas = ref<unknown[]>([]);
const quantidade = ref<number>(0);
const acoesVisiveis = ref<boolean[][]>([]);
const menuAberto = ref<number | null>(null);
const menuElementos = new Map<number, HTMLElement>();
const valorBusca = ref<string>("");
const paginaAtual = ref(1);
const colunaOrdenacao = ref<string | null>(null);
const direcaoOrdenacao = ref<"asc" | "desc">("asc");
const tabela = computed(() => props.tabela);
const exibirBusca = computed(() => Boolean(props.tabela.mostrarCaixaDeBusca));
const acoesCabecalho = computed(() => props.tabela.acoesTabela ?? []);
const temAcoesCabecalho = computed(() => acoesCabecalho.value.length > 0);
const registrosPorConsulta = computed(() => {
const valor = props.tabela.registros_por_consulta;
if (typeof valor === "number" && valor > 0) {
@ -47,19 +268,19 @@ export default defineComponent({
}
return 10;
});
const totalPaginas = computed(() => {
const limite = registrosPorConsulta.value;
if (!limite || limite <= 0) {
return 1;
}
if (!limite || limite <= 0) return 1;
const total = quantidade.value;
if (!total) {
return 1;
}
if (!total) return 1;
return Math.max(1, Math.ceil(total / limite));
});
const temAcoes = computed(() => (props.tabela.acoesLinha ?? []).length > 0);
let carregamentoSequencial = 0;
function registrarMenuElemento(indice: number, elemento: HTMLElement | null) {
@ -70,40 +291,40 @@ export default defineComponent({
}
}
function criarRegistradorMenu(indice: number) {
return (elemento: Element | ComponentPublicInstance | null) => {
if (elemento instanceof HTMLElement) {
registrarMenuElemento(indice, elemento);
} else {
registrarMenuElemento(indice, null);
}
};
function handleClickFora(evento: MouseEvent) {
if (menuAberto.value === null) return;
const container = menuElementos.get(menuAberto.value);
if (container && container.contains(evento.target as Node)) return;
menuAberto.value = null;
}
function isOrdenavel(coluna: any) {
return coluna?.coluna_ordem !== undefined && coluna?.coluna_ordem !== null;
}
function alternarOrdenacao(chave?: string) {
if (!chave) {
if (!chave) return;
if (colunaOrdenacao.value === chave) {
direcaoOrdenacao.value = direcaoOrdenacao.value === "asc" ? "desc" : "asc";
void carregar();
return;
}
if (colunaOrdenacao.value === chave) {
direcaoOrdenacao.value =
direcaoOrdenacao.value === "asc" ? "desc" : "asc";
void carregar();
colunaOrdenacao.value = chave;
direcaoOrdenacao.value = "asc";
if (paginaAtual.value !== 1) {
paginaAtual.value = 1;
} else {
colunaOrdenacao.value = chave;
direcaoOrdenacao.value = "asc";
if (paginaAtual.value !== 1) {
paginaAtual.value = 1;
} else {
void carregar();
}
void carregar();
}
}
function atualizarBusca(texto: string) {
if (valorBusca.value === texto) {
return;
}
if (valorBusca.value === texto) return;
valorBusca.value = texto;
if (paginaAtual.value !== 1) {
paginaAtual.value = 1;
@ -119,40 +340,19 @@ export default defineComponent({
}
}
function handleClickFora(evento: MouseEvent) {
if (menuAberto.value === null) {
return;
}
const container = menuElementos.get(menuAberto.value);
if (container && container.contains(evento.target as Node)) {
return;
}
menuAberto.value = null;
}
function obterClasseAlinhamento(alinhamento?: string) {
if (alinhamento === "direita") {
return "eli-tabela__celula--direita";
}
if (alinhamento === "centro") {
return "eli-tabela__celula--centro";
}
if (alinhamento === "direita") return "eli-tabela__celula--direita";
if (alinhamento === "centro") return "eli-tabela__celula--centro";
return "eli-tabela__celula--esquerda";
}
function obterMaxWidth(largura?: number | string) {
if (largura === undefined || largura === null) {
return undefined;
}
if (largura === undefined || largura === null) return undefined;
return typeof largura === "number" ? `${largura}px` : String(largura);
}
function obterTooltipCelula(celula: unknown) {
if (!Array.isArray(celula)) {
return undefined;
}
if (!Array.isArray(celula)) return undefined;
const tipo = celula[0];
const dados = celula[1] as any;
@ -168,39 +368,35 @@ export default defineComponent({
return undefined;
}
function renderErro(mensagem: string) {
return h(
"div",
{
class: "eli-tabela eli-tabela--erro",
role: "alert",
},
[
h("div", { class: "eli-tabela__erro-titulo" }, "Erro"),
h("div", { class: "eli-tabela__erro-mensagem" }, mensagem),
]
);
function acoesDisponiveisPorLinha(i: number) {
const acoesLinha = props.tabela.acoesLinha ?? [];
const visibilidade = acoesVisiveis.value[i] ?? [];
return acoesLinha
.map((acao, indice) => {
const fallbackVisivel =
acao.exibir === undefined
? true
: typeof acao.exibir === "boolean"
? acao.exibir
: false;
return {
acao,
indice,
visivel: visibilidade[indice] ?? fallbackVisivel,
};
})
.filter((item) => item.visivel);
}
function renderVazio(mensagem?: string) {
return h(
"div",
{
class: "eli-tabela eli-tabela--vazio",
},
mensagem ?? "Nenhum registro encontrado."
);
function possuiAcoes(i: number) {
return acoesDisponiveisPorLinha(i).length > 0;
}
function renderCarregando() {
return h(
"div",
{
class: "eli-tabela eli-tabela--carregando",
"aria-busy": "true",
},
"Carregando..."
);
function toggleMenu(i: number) {
if (!possuiAcoes(i)) return;
menuAberto.value = menuAberto.value === i ? null : i;
}
async function carregar() {
@ -238,9 +434,7 @@ export default defineComponent({
const tabelaConfig = props.tabela;
const res = await tabelaConfig.consulta(parametrosConsulta);
if (idCarregamento !== carregamentoSequencial) {
return;
}
if (idCarregamento !== carregamentoSequencial) return;
if (res.cod !== codigosResposta.sucesso) {
linhas.value = [];
@ -255,18 +449,13 @@ export default defineComponent({
linhas.value = valores;
quantidade.value = total;
const totalPaginasRecalculado = Math.max(
1,
Math.ceil((total || 0) / limite)
);
const totalPaginasRecalculado = Math.max(1, Math.ceil((total || 0) / limite));
if (paginaAtual.value > totalPaginasRecalculado) {
paginaAtual.value = totalPaginasRecalculado;
return;
}
const acoesLinhaConfiguradas = tabelaConfig.acoesLinha ?? [];
if (!acoesLinhaConfiguradas.length) {
acoesVisiveis.value = [];
return;
@ -274,14 +463,8 @@ export default defineComponent({
const preResultado = valores.map(() =>
acoesLinhaConfiguradas.map((acao) => {
if (acao.exibir === undefined) {
return true;
}
if (typeof acao.exibir === "boolean") {
return acao.exibir;
}
if (acao.exibir === undefined) return true;
if (typeof acao.exibir === "boolean") return acao.exibir;
return false;
})
);
@ -292,13 +475,8 @@ export default defineComponent({
valores.map(async (linha) =>
Promise.all(
acoesLinhaConfiguradas.map(async (acao) => {
if (acao.exibir === undefined) {
return true;
}
if (typeof acao.exibir === "boolean") {
return acao.exibir;
}
if (acao.exibir === undefined) return true;
if (typeof acao.exibir === "boolean") return acao.exibir;
try {
const resultado = acao.exibir(linha as never);
@ -315,9 +493,7 @@ export default defineComponent({
acoesVisiveis.value = visibilidade;
}
} catch (e) {
if (idCarregamento !== carregamentoSequencial) {
return;
}
if (idCarregamento !== carregamentoSequencial) return;
linhas.value = [];
quantidade.value = 0;
@ -334,6 +510,11 @@ export default defineComponent({
void carregar();
});
onBeforeUnmount(() => {
document.removeEventListener("click", handleClickFora);
menuElementos.clear();
});
watch(
() => props.tabela.mostrarCaixaDeBusca,
(mostrar) => {
@ -349,15 +530,9 @@ export default defineComponent({
);
watch(paginaAtual, (nova, antiga) => {
if (nova !== antiga) {
void carregar();
}
if (nova !== antiga) void carregar();
});
onBeforeUnmount(() => {
document.removeEventListener("click", handleClickFora);
menuElementos.clear();
});
watch(
() => props.tabela,
() => {
@ -390,366 +565,45 @@ export default defineComponent({
menuElementos.clear();
});
return () => {
const tabela = props.tabela;
return {
// state
tabela,
carregando,
erro,
linhas,
quantidade,
menuAberto,
valorBusca,
paginaAtual,
colunaOrdenacao,
direcaoOrdenacao,
totalPaginas,
if (carregando.value) {
return renderCarregando();
}
// computed
exibirBusca,
acoesCabecalho,
temAcoesCabecalho,
temAcoes,
if (erro.value) {
return renderErro(erro.value);
}
// icons
ArrowUp,
ArrowDown,
MoreVertical,
const colunas = tabela.colunas;
const acoesLinha = tabela.acoesLinha ?? [];
const temAcoes = acoesLinha.length > 0;
// helpers
isOrdenavel,
obterClasseAlinhamento,
obterMaxWidth,
obterTooltipCelula,
if (!linhas.value.length) {
return renderVazio(tabela.mensagemVazio);
}
const cabecalho = colunas.map((coluna) => {
const chaveOrdenacao =
coluna.coluna_ordem !== undefined
? (coluna.coluna_ordem as unknown as string)
: undefined;
const ordenavel = Boolean(chaveOrdenacao);
const ativa = ordenavel && colunaOrdenacao.value === chaveOrdenacao;
const iconeOrdenacao = ordenavel
? ativa
? h(direcaoOrdenacao.value === "asc" ? ArrowUp : ArrowDown, {
class: "eli-tabela__th-icone",
size: 16,
strokeWidth: 2,
"aria-hidden": "true",
})
: h(ArrowUp, {
class: "eli-tabela__th-icone eli-tabela__th-icone--oculto",
size: 16,
strokeWidth: 2,
"aria-hidden": "true",
})
: null;
const conteudo = ordenavel
? h(
"button",
{
type: "button",
class: [
"eli-tabela__th-botao",
ativa ? "eli-tabela__th-botao--ativo" : undefined,
],
onClick: () => alternarOrdenacao(chaveOrdenacao),
},
[
h("span", { class: "eli-tabela__th-texto" }, coluna.rotulo),
iconeOrdenacao,
]
)
: h("span", { class: "eli-tabela__th-label" }, coluna.rotulo);
return h(
"th",
{
class: [
"eli-tabela__th",
ordenavel ? "eli-tabela__th--ordenavel" : undefined,
obterClasseAlinhamento(coluna.alinhamento),
],
scope: "col",
},
conteudo
);
});
if (temAcoes) {
cabecalho.push(
h(
"th",
{ class: "eli-tabela__th eli-tabela__th--acoes", scope: "col" },
"Ações"
)
);
}
const conteudoTabela: ReturnType<typeof h>[] = [];
if (exibirBusca.value || temAcoesCabecalho.value) {
const botoes = acoesCabecalho.value.map((botao, indice) =>
h(
"button",
{
key: `${botao.rotulo}-${indice}`,
type: "button",
class: "eli-tabela__acoes-cabecalho-botao",
// Quando `cor` for informada, tratamos como cor de destaque do botão.
// Também ajustamos o texto/ícone para branco para manter contraste.
style: botao.cor
? {
backgroundColor: botao.cor,
color: "#fff",
}
: undefined,
onClick: botao.acao,
},
[
botao.icone
? h(botao.icone, {
class: "eli-tabela__acoes-cabecalho-icone",
size: 16,
strokeWidth: 2,
})
: null,
h("span", { class: "eli-tabela__acoes-cabecalho-rotulo" }, botao.rotulo),
]
)
);
conteudoTabela.push(
h("div", { class: "eli-tabela__cabecalho" }, [
exibirBusca.value
? h(EliTabelaCaixaDeBusca, {
modelo: valorBusca.value,
onBuscar: atualizarBusca,
})
: null,
temAcoesCabecalho.value
? h("div", { class: "eli-tabela__acoes-cabecalho" }, botoes)
: null,
])
);
}
conteudoTabela.push(
h("table", { class: "eli-tabela__table" }, [
h(
"thead",
{ class: "eli-tabela__thead" },
h(
"tr",
{ class: "eli-tabela__tr eli-tabela__tr--header" },
cabecalho
)
),
h(
"tbody",
{ class: "eli-tabela__tbody" },
linhas.value.map((linha, i) => {
const celulas = colunas.map((coluna, j) =>
(() => {
const celula = coluna.celula(linha as never);
const truncar = Boolean(coluna.truncar);
const tooltip = truncar ? obterTooltipCelula(celula) : undefined;
const conteudo = h(EliTabelaCelula, {
celula: celula as never,
});
const conteudoFinal = truncar
? h(
"span",
{
class: "eli-tabela__celula-conteudo",
style: coluna.largura_maxima
? {
maxWidth: obterMaxWidth(coluna.largura_maxima),
}
: undefined,
title: tooltip,
},
conteudo
)
: conteudo;
return h(
"td",
{
class: [
"eli-tabela__td",
coluna.acao ? "eli-tabela__td--clicavel" : undefined,
obterClasseAlinhamento(coluna.alinhamento),
],
key: `${i}-${j}`,
onClick: coluna.acao ? () => coluna.acao?.() : undefined,
},
conteudoFinal
);
})()
);
if (temAcoes) {
const visibilidade = acoesVisiveis.value[i] ?? [];
const acoesDisponiveis = acoesLinha
.map((acao, indice) => {
const fallbackVisivel =
acao.exibir === undefined
? true
: typeof acao.exibir === "boolean"
? acao.exibir
: false;
return {
acao,
indice,
visivel: visibilidade[indice] ?? fallbackVisivel,
};
})
.filter((item) => item.visivel);
const possuiAcoes = acoesDisponiveis.length > 0;
if (!possuiAcoes && menuAberto.value === i) {
menuAberto.value = null;
}
const estaAberto = menuAberto.value === i;
const toggleId = `eli-tabela-acoes-toggle-${i}`;
const menuId = `eli-tabela-acoes-menu-${i}`;
const botaoToggle = h(
"button",
{
id: toggleId,
class: "eli-tabela__acoes-toggle",
type: "button",
disabled: !possuiAcoes,
onClick: (evento: MouseEvent) => {
evento.stopPropagation();
if (!possuiAcoes) {
return;
}
menuAberto.value = estaAberto ? null : i;
},
"aria-haspopup": "menu",
"aria-expanded": estaAberto ? "true" : "false",
"aria-controls": possuiAcoes ? menuId : undefined,
"aria-label": possuiAcoes
? "Ações da linha"
: "Nenhuma ação disponível",
title: possuiAcoes ? "Ações" : "Nenhuma ação disponível",
},
[
h(MoreVertical, {
class: "eli-tabela__acoes-toggle-icone",
size: 18,
strokeWidth: 2,
}),
]
);
const menu =
estaAberto && possuiAcoes
? h(
"ul",
{
id: menuId,
class: "eli-tabela__acoes-menu",
role: "menu",
"aria-labelledby": toggleId,
},
acoesDisponiveis.map(({ acao, indice }) =>
h(
"li",
{
key: `acao-${indice}`,
class: "eli-tabela__acoes-item",
role: "none",
},
h(
"button",
{
type: "button",
class: "eli-tabela__acoes-item-botao",
style: {
color: acao.cor,
},
onClick: (evento: MouseEvent) => {
evento.stopPropagation();
menuAberto.value = null;
acao.acao(linha as never);
},
role: "menuitem",
"aria-label": acao.rotulo,
title: acao.rotulo,
},
[
h(acao.icone, {
class: "eli-tabela__acoes-item-icone",
size: 16,
strokeWidth: 2,
}),
h(
"span",
{ class: "eli-tabela__acoes-item-texto" },
acao.rotulo
),
]
)
)
)
)
: null;
const classesContainer = ["eli-tabela__acoes-container"];
if (estaAberto) {
classesContainer.push("eli-tabela__acoes-container--aberto");
}
celulas.push(
h(
"td",
{
class: ["eli-tabela__td", "eli-tabela__td--acoes"],
key: `${i}-acoes`,
},
h(
"div",
{
class: classesContainer,
ref: criarRegistradorMenu(i),
},
[botaoToggle, menu]
)
)
);
}
return h(
"tr",
{
class: ["eli-tabela__tr", i % 2 === 1 ? "eli-tabela__tr--zebra" : undefined],
key: i,
},
celulas
);
})
),
])
);
if (totalPaginas.value > 1 && quantidade.value > 0) {
conteudoTabela.push(
h(EliTabelaPaginacao, {
pagina: paginaAtual.value,
totalPaginas: totalPaginas.value,
maximoBotoes: props.tabela.maximo_botoes_paginacao,
onAlterar: (pagina: number) => {
irParaPagina(pagina);
},
})
);
}
return h(
"div",
{
class: "eli-tabela",
},
conteudoTabela
);
// actions
alternarOrdenacao,
atualizarBusca,
irParaPagina,
registrarMenuElemento,
acoesDisponiveisPorLinha,
possuiAcoes,
toggleMenu,
};
},
});
@ -766,6 +620,15 @@ export default defineComponent({
border-spacing: 0;
border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 12px;
/*
IMPORTANTE:
O menu de ações de cada linha usa `position: absolute`. Caso a tabela (ou
algum ancestral) tenha `overflow: hidden`, o menu é recortado e some.
*/
overflow: visible;
}
.eli-tabela__tbody {
overflow: visible;
}
@ -906,6 +769,7 @@ export default defineComponent({
.eli-tabela__td--acoes {
white-space: nowrap;
overflow: visible;
}
.eli-tabela__acoes-container {