ajustes na tabela de consulta

This commit is contained in:
Luiz Silva 2026-06-01 13:13:32 -03:00
parent aea3da6585
commit 1446d6225b
78 changed files with 17921 additions and 17580 deletions

View file

@ -1,3 +1,5 @@
@import "./EliTabelaAcoes.css";
@import "./EliTabelaSelecao.css";
.eli-tabela {
width: 100%;
}
@ -132,7 +134,6 @@
background: rgba(0, 0, 0, 0.03);
}
.eli-tabela--erro {
display: flex;
align-items: flex-start;
@ -244,284 +245,6 @@
gap: 12px;
margin-bottom: 12px;
flex-wrap: wrap;
/* Altura padrão para controles do cabeçalho (busca e botões)
* - Mantém alinhamento visual entre input de busca e ações
* - Pode ser sobrescrita via CSS no consumidor, se necessário
*/
--eli-tabela-cabecalho-controle-altura: 34px;
/* Borda dos controles do cabeçalho: menos "pílula", mais quadrado.
* Ajuste fino via CSS no consumidor, se necessário.
*/
--eli-tabela-cabecalho-controle-radius: 8px;
}
.eli-tabela__acoes-cabecalho {
display: inline-flex;
gap: 8px;
flex-wrap: wrap;
}
.eli-tabela__acoes-cabecalho-botao {
display: inline-flex;
align-items: center;
gap: 6px;
height: var(--eli-tabela-cabecalho-controle-altura);
padding: 0 14px;
border-radius: var(--eli-tabela-cabecalho-controle-radius);
border: none;
background: var(--cor-eli-primaria-bg);
color: var(--cor-eli-primaria-escura);
font-size: 0.875rem;
font-weight: 500;
line-height: 1;
cursor: pointer;
transition: background-color 0.2s ease, color 0.2s ease;
}
.eli-tabela__acoes-cabecalho-botao:hover,
.eli-tabela__acoes-cabecalho-botao:focus-visible {
background: var(--cor-eli-primaria-clara);
}
.eli-tabela__acoes-cabecalho-botao:focus-visible {
outline: 2px solid var(--cor-eli-primaria-clara);
outline-offset: 2px;
}
.eli-tabela__acoes-cabecalho-icone {
display: inline-block;
}
.eli-tabela__acoes-cabecalho-rotulo {
line-height: 1;
}
.eli-tabela__acoes-toggle {
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border-radius: 9999px;
border: none;
background: transparent;
color: rgba(15, 23, 42, 0.72);
cursor: pointer;
transition: background-color 0.2s ease, color 0.2s ease;
}
.eli-tabela__acoes-toggle-icone {
display: block;
}
.eli-tabela__acoes-toggle:hover,
.eli-tabela__acoes-toggle:focus-visible {
background-color: rgba(15, 23, 42, 0.08);
color: rgba(15, 23, 42, 0.95);
}
.eli-tabela__acoes-toggle:focus-visible {
outline: 2px solid var(--cor-eli-primaria-clara);
outline-offset: 2px;
}
.eli-tabela__acoes-toggle:disabled {
cursor: default;
color: rgba(148, 163, 184, 0.8);
background: transparent;
}
.eli-tabela__acoes-menu {
min-width: 180px;
padding: 6px 0;
margin: 0;
list-style: none;
background: var(--cor-eli-superficie);
border: 1px solid rgba(15, 23, 42, 0.08);
border-radius: 10px;
box-shadow: 0 12px 30px rgba(15, 23, 42, 0.18);
z-index: 1000;
}
.eli-tabela__acoes-item {
margin: 0;
}
.eli-tabela__acoes-item-botao {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
padding: 8px 12px;
border: none;
background: transparent;
font-size: 0.9rem;
cursor: pointer;
transition: background-color 0.2s ease;
}
.eli-tabela__acoes-item-botao:hover,
.eli-tabela__acoes-item-botao:focus-visible {
background-color: rgba(15, 23, 42, 0.06);
}
.eli-tabela__acoes-item-botao:focus-visible {
outline: 2px solid currentColor;
outline-offset: -2px;
}
.eli-tabela__acoes-item-icone {
flex-shrink: 0;
}
.eli-tabela__acoes-item-texto {
flex: 1;
text-align: left;
}
/* =========================
* Expander (colunas invisíveis)
* ========================= */
.eli-tabela__th--expander,
.eli-tabela__td--expander {
width: 42px;
padding: 6px 8px;
text-align: center;
vertical-align: middle;
}
.eli-tabela__expander-botao {
display: inline-flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
border-radius: 9999px;
border: none;
background: transparent;
color: rgba(15, 23, 42, 0.72);
cursor: pointer;
transition: background-color 0.2s ease, color 0.2s ease;
}
.eli-tabela__expander-botao:hover,
.eli-tabela__expander-botao:focus-visible {
background-color: rgba(15, 23, 42, 0.08);
color: rgba(15, 23, 42, 0.95);
}
.eli-tabela__expander-botao:focus-visible {
outline: 2px solid var(--cor-eli-primaria-clara);
outline-offset: 2px;
}
.eli-tabela__expander-botao--ativo {
background-color: rgba(15, 23, 42, 0.06);
color: rgba(15, 23, 42, 0.95);
}
.eli-tabela__td--detalhes {
padding: 12px;
}
.eli-tabela__tr--detalhes .eli-tabela__td {
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
}
.eli-tabela__detalhes {
display: grid;
gap: 10px;
padding-left: 4px;
}
.eli-tabela__detalhe {
display: grid;
grid-template-columns: 180px 1fr;
gap: 10px;
align-items: start;
}
.eli-tabela__detalhe-rotulo {
font-weight: 600;
color: rgba(15, 23, 42, 0.85);
}
.eli-tabela__detalhe-valor {
min-width: 0;
}
@media (max-width: 720px) {
.eli-tabela__detalhe {
grid-template-columns: 1fr;
}
}
/* =========================================
* Seleção (Checkboxes)
* ========================================= */
.eli-tabela__th--selecao,
.eli-tabela__td--selecao {
width: 48px;
min-width: 48px;
max-width: 48px;
text-align: center;
padding: 0 8px;
vertical-align: middle;
}
.eli-tabela__checkbox {
appearance: none;
-webkit-appearance: none;
width: 15px;
height: 15px;
cursor: pointer;
border: 2px solid rgba(0, 0, 0, 0.54);
border-radius: 2px;
margin: 0;
vertical-align: middle;
position: relative;
transition: all 0.2s ease-in-out;
background-color: transparent;
outline: none;
}
.eli-tabela__checkbox:hover {
border-color: rgba(0, 0, 0, 0.87);
}
.eli-tabela__checkbox:checked,
.eli-tabela__checkbox:indeterminate {
background-color: var(--cor-eli-primaria);
border-color: var(--cor-eli-primaria);
}
.eli-tabela__checkbox:checked::after {
content: '';
position: absolute;
top: 0px;
left: 4px;
width: 4px;
height: 9px;
border: solid white;
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
.eli-tabela__checkbox:indeterminate::after {
content: '';
position: absolute;
top: 5px;
left: 2px;
width: 8px;
height: 2px;
background-color: white;
border-radius: 1px;
}
.eli-tabela__checkbox:focus-visible {
box-shadow: 0 0 0 2px var(--cor-eli-primaria-clara);
}

View file

@ -12,6 +12,7 @@
v-if="exibirBusca || temAcoesSuperiores"
:exibirBusca="exibirBusca"
:exibirBotaoFiltroAvancado="exibirFiltroAvancado"
:filtrosAtivosResumo="filtrosAtivosResumo"
:valorBusca="valorBusca"
:acoesCabecalho="acoesSuperiores"
:parametrosConsulta="parametrosConsultaAtuais"

View file

@ -0,0 +1,207 @@
/* =========================
* EliTabela — Ações de linha, Menu e Seleção (Checkboxes)
* Extraído de EliTabela.css para manter cada arquivo CSS abaixo de 250 linhas.
* ========================= */
.eli-tabela__acoes-cabecalho {
display: inline-flex;
gap: 8px;
flex-wrap: wrap;
}
.eli-tabela__acoes-cabecalho-botao {
display: inline-flex;
align-items: center;
gap: 6px;
height: var(--eli-tabela-cabecalho-controle-altura);
padding: 0 14px;
border-radius: var(--eli-tabela-cabecalho-controle-radius);
border: none;
background: var(--cor-eli-primaria-bg);
color: var(--cor-eli-primaria-escura);
font-size: 0.875rem;
font-weight: 500;
line-height: 1;
cursor: pointer;
transition: background-color 0.2s ease, color 0.2s ease;
}
.eli-tabela__acoes-cabecalho-botao:hover,
.eli-tabela__acoes-cabecalho-botao:focus-visible {
background: var(--cor-eli-primaria-clara);
}
.eli-tabela__acoes-cabecalho-botao:focus-visible {
outline: 2px solid var(--cor-eli-primaria-clara);
outline-offset: 2px;
}
.eli-tabela__acoes-cabecalho-icone {
display: inline-block;
}
.eli-tabela__acoes-cabecalho-rotulo {
line-height: 1;
}
.eli-tabela__acoes-toggle {
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border-radius: 9999px;
border: none;
background: transparent;
color: rgba(15, 23, 42, 0.72);
cursor: pointer;
transition: background-color 0.2s ease, color 0.2s ease;
}
.eli-tabela__acoes-toggle-icone {
display: block;
}
.eli-tabela__acoes-toggle:hover,
.eli-tabela__acoes-toggle:focus-visible {
background-color: rgba(15, 23, 42, 0.08);
color: rgba(15, 23, 42, 0.95);
}
.eli-tabela__acoes-toggle:focus-visible {
outline: 2px solid var(--cor-eli-primaria-clara);
outline-offset: 2px;
}
.eli-tabela__acoes-toggle:disabled {
cursor: default;
color: rgba(148, 163, 184, 0.8);
background: transparent;
}
.eli-tabela__acoes-menu {
min-width: 180px;
padding: 6px 0;
margin: 0;
list-style: none;
background: var(--cor-eli-superficie);
border: 1px solid rgba(15, 23, 42, 0.08);
border-radius: 10px;
box-shadow: 0 12px 30px rgba(15, 23, 42, 0.18);
z-index: 1000;
}
.eli-tabela__acoes-item {
margin: 0;
}
.eli-tabela__acoes-item-botao {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
padding: 8px 12px;
border: none;
background: transparent;
font-size: 0.9rem;
cursor: pointer;
transition: background-color 0.2s ease;
}
.eli-tabela__acoes-item-botao:hover,
.eli-tabela__acoes-item-botao:focus-visible {
background-color: rgba(15, 23, 42, 0.06);
}
.eli-tabela__acoes-item-botao:focus-visible {
outline: 2px solid currentColor;
outline-offset: -2px;
}
.eli-tabela__acoes-item-icone {
flex-shrink: 0;
}
.eli-tabela__acoes-item-texto {
flex: 1;
text-align: left;
}
/* =========================
* Expander (colunas invisíveis)
* ========================= */
.eli-tabela__th--expander,
.eli-tabela__td--expander {
width: 42px;
padding: 6px 8px;
text-align: center;
vertical-align: middle;
}
.eli-tabela__expander-botao {
display: inline-flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
border-radius: 9999px;
border: none;
background: transparent;
color: rgba(15, 23, 42, 0.72);
cursor: pointer;
transition: background-color 0.2s ease, color 0.2s ease;
}
.eli-tabela__expander-botao:hover,
.eli-tabela__expander-botao:focus-visible {
background-color: rgba(15, 23, 42, 0.08);
color: rgba(15, 23, 42, 0.95);
}
.eli-tabela__expander-botao:focus-visible {
outline: 2px solid var(--cor-eli-primaria-clara);
outline-offset: 2px;
}
.eli-tabela__expander-botao--ativo {
background-color: rgba(15, 23, 42, 0.06);
color: rgba(15, 23, 42, 0.95);
}
.eli-tabela__td--detalhes {
padding: 12px;
}
.eli-tabela__tr--detalhes .eli-tabela__td {
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
}
.eli-tabela__detalhes {
display: grid;
gap: 10px;
padding-left: 4px;
}
.eli-tabela__detalhe {
display: grid;
grid-template-columns: 180px 1fr;
gap: 10px;
align-items: start;
}
.eli-tabela__detalhe-rotulo {
font-weight: 600;
color: rgba(15, 23, 42, 0.85);
}
.eli-tabela__detalhe-valor {
min-width: 0;
}
@media (max-width: 720px) {
.eli-tabela__detalhe {
grid-template-columns: 1fr;
}
}

View file

@ -16,9 +16,16 @@
v-if="exibirBotaoFiltroAvancado"
type="button"
class="eli-tabela__acoes-cabecalho-botao eli-tabela__acoes-cabecalho-botao--filtro"
:title="tooltipFiltro"
@click="emitFiltroAvancado"
>
<Filter :size="15" :stroke-width="2" />
<span class="eli-tabela__filtro-icone-container">
<Filter :size="15" :stroke-width="2" />
<span
v-if="filtrosAtivosResumo.quantidade > 0"
class="eli-tabela__filtro-badge"
>{{ filtrosAtivosResumo.quantidade }}</span>
</span>
Filtro
</button>
<EliTabelaCaixaDeBusca :modelo="valorBusca" @buscar="emitBuscar" />
@ -70,6 +77,11 @@ export default defineComponent({
required: false,
default: false,
},
filtrosAtivosResumo: {
type: Object as PropType<{ quantidade: number; rotulos: string[] }>,
required: false,
default: () => ({ quantidade: 0, rotulos: [] }),
},
valorBusca: {
type: String,
required: true,
@ -107,6 +119,12 @@ export default defineComponent({
setup(props, { emit }) {
const temAcoesCabecalho = computed(() => props.acoesCabecalho.length > 0)
const tooltipFiltro = computed(() => {
const { quantidade, rotulos } = props.filtrosAtivosResumo
if (quantidade === 0) return "Filtro avançado"
return `Filtros ativos (${quantidade}):\n${rotulos.map((r) => `• ${r}`).join("\n")}`
})
function emitBuscar(texto: string) {
emit("buscar", texto)
}
@ -119,7 +137,7 @@ export default defineComponent({
emit("filtroAvancado")
}
return { temAcoesCabecalho, emitBuscar, emitColunas, emitFiltroAvancado }
return { temAcoesCabecalho, tooltipFiltro, emitBuscar, emitColunas, emitFiltroAvancado }
},
})
</script>
@ -131,4 +149,28 @@ export default defineComponent({
gap: 8px;
flex-wrap: wrap;
}
.eli-tabela__filtro-icone-container {
position: relative;
display: inline-flex;
align-items: center;
}
.eli-tabela__filtro-badge {
position: absolute;
top: -7px;
right: -9px;
min-width: 16px;
height: 16px;
padding: 0 4px;
border-radius: 9999px;
background: var(--cor-eli-erro, #dc3545);
color: #fff;
font-size: 0.65rem;
font-weight: 700;
line-height: 16px;
text-align: center;
pointer-events: none;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}
</style>

View file

@ -1,3 +1,5 @@
@import "./EliTabelaModalColunasItens.css";
.eli-tabela-modal-colunas__overlay {
position: fixed;
inset: 0;
@ -162,181 +164,3 @@
.eli-tabela-modal-colunas__lista--invisiveis {
background: var(--cor-eli-fundo);
}
/* ── Item ── */
.eli-tabela-modal-colunas__item {
display: flex;
align-items: center;
gap: 8px;
padding: 9px 10px;
border-radius: 8px;
border: 1px solid var(--cor-eli-borda);
background: var(--cor-eli-superficie);
cursor: grab;
user-select: none;
transition: box-shadow 0.15s ease, opacity 0.15s ease, border-color 0.15s ease;
}
.eli-tabela-modal-colunas__item + .eli-tabela-modal-colunas__item {
margin-top: 6px;
}
.eli-tabela-modal-colunas__item:hover {
border-color: var(--cor-eli-borda-forte);
box-shadow: 0 2px 8px rgba(15, 23, 42, 0.08);
}
.eli-tabela-modal-colunas__item:active {
cursor: grabbing;
}
.eli-tabela-modal-colunas__item--invisivel {
opacity: 0.72;
}
.eli-tabela-modal-colunas__item--arrastando {
opacity: 0.15;
border-style: dashed;
border-color: var(--cor-eli-primaria-media);
}
/* ── Partes do item ── */
.eli-tabela-modal-colunas__item-handle {
color: var(--cor-eli-borda-forte);
flex-shrink: 0;
transition: color 0.15s ease;
}
.eli-tabela-modal-colunas__item:hover .eli-tabela-modal-colunas__item-handle {
color: var(--cor-eli-neutro);
}
.eli-tabela-modal-colunas__item-texto {
flex: 1;
min-width: 0;
font-size: 0.875rem;
color: var(--cor-eli-neutro-escuro);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.eli-tabela-modal-colunas__item-badge-icone {
flex-shrink: 0;
}
.eli-tabela-modal-colunas__item-badge-icone--visivel {
color: var(--cor-eli-primaria-acao);
}
.eli-tabela-modal-colunas__item-badge-icone--invisivel {
color: var(--cor-eli-borda-forte);
}
/* ── Estado vazio ── */
.eli-tabela-modal-colunas__vazio {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8px;
padding: 24px 12px;
text-align: center;
font-size: 0.8rem;
color: var(--cor-eli-neutro);
line-height: 1.5;
}
.eli-tabela-modal-colunas__vazio-icone {
color: var(--cor-eli-borda-forte);
}
.eli-tabela-modal-colunas__vazio-icone--ok {
color: var(--cor-eli-primaria-acao);
}
/* ── Animações TransitionGroup ── */
.list-move,
.list-enter-active,
.list-leave-active {
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: scale(0.95);
}
.list-leave-active {
position: absolute;
width: calc(100% - 16px);
}
/* ── Footer ── */
.eli-tabela-modal-colunas__footer {
display: flex;
justify-content: flex-end;
gap: 8px;
padding: 12px 16px;
border-top: 1px solid var(--cor-eli-borda);
background: var(--cor-eli-fundo);
}
.eli-tabela-modal-colunas__botao {
display: inline-flex;
align-items: center;
gap: 6px;
height: 34px;
padding: 0 16px;
border-radius: 8px;
border: 1px solid var(--cor-eli-borda);
background: var(--cor-eli-superficie);
cursor: pointer;
font-size: 0.875rem;
font-weight: 500;
transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}
.eli-tabela-modal-colunas__botao--sec {
color: var(--cor-eli-neutro-escuro);
}
.eli-tabela-modal-colunas__botao--sec:hover,
.eli-tabela-modal-colunas__botao--sec:focus-visible {
background: var(--cor-eli-fundo);
border-color: var(--cor-eli-borda-forte);
}
.eli-tabela-modal-colunas__botao--sec:focus-visible {
outline: 2px solid var(--cor-eli-primaria-clara);
outline-offset: 2px;
}
.eli-tabela-modal-colunas__botao--prim {
border: none;
background: var(--cor-eli-primaria);
color: #fff;
}
.eli-tabela-modal-colunas__botao--prim:hover,
.eli-tabela-modal-colunas__botao--prim:focus-visible {
background: var(--cor-eli-primaria-escura);
}
.eli-tabela-modal-colunas__botao--prim:focus-visible {
outline: 2px solid var(--cor-eli-primaria-clara);
outline-offset: 2px;
}
/* ── Responsivo ── */
@media (max-width: 720px) {
.eli-tabela-modal-colunas__conteudo {
grid-template-columns: 1fr;
}
.eli-tabela-modal-colunas__coluna + .eli-tabela-modal-colunas__coluna {
border-left: none;
border-top: 1px solid var(--cor-eli-borda);
}
}

View file

@ -0,0 +1,182 @@
/* =========================
* EliTabelaModalColunas — Itens, estados e footer
* Extraído de EliTabelaModalColunas.css para manter cada arquivo abaixo de 250 linhas.
* ========================= */
/* ── Item ── */
.eli-tabela-modal-colunas__item {
display: flex;
align-items: center;
gap: 8px;
padding: 9px 10px;
border-radius: 8px;
border: 1px solid var(--cor-eli-borda);
background: var(--cor-eli-superficie);
cursor: grab;
user-select: none;
transition: box-shadow 0.15s ease, opacity 0.15s ease, border-color 0.15s ease;
}
.eli-tabela-modal-colunas__item + .eli-tabela-modal-colunas__item {
margin-top: 6px;
}
.eli-tabela-modal-colunas__item:hover {
border-color: var(--cor-eli-borda-forte);
box-shadow: 0 2px 8px rgba(15, 23, 42, 0.08);
}
.eli-tabela-modal-colunas__item:active {
cursor: grabbing;
}
.eli-tabela-modal-colunas__item--invisivel {
opacity: 0.72;
}
.eli-tabela-modal-colunas__item--arrastando {
opacity: 0.15;
border-style: dashed;
border-color: var(--cor-eli-primaria-media);
}
/* ── Partes do item ── */
.eli-tabela-modal-colunas__item-handle {
color: var(--cor-eli-borda-forte);
flex-shrink: 0;
transition: color 0.15s ease;
}
.eli-tabela-modal-colunas__item:hover .eli-tabela-modal-colunas__item-handle {
color: var(--cor-eli-neutro);
}
.eli-tabela-modal-colunas__item-texto {
flex: 1;
min-width: 0;
font-size: 0.875rem;
color: var(--cor-eli-neutro-escuro);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.eli-tabela-modal-colunas__item-badge-icone {
flex-shrink: 0;
}
.eli-tabela-modal-colunas__item-badge-icone--visivel {
color: var(--cor-eli-primaria-acao);
}
.eli-tabela-modal-colunas__item-badge-icone--invisivel {
color: var(--cor-eli-borda-forte);
}
/* ── Estado vazio ── */
.eli-tabela-modal-colunas__vazio {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8px;
padding: 24px 12px;
text-align: center;
font-size: 0.8rem;
color: var(--cor-eli-neutro);
line-height: 1.5;
}
.eli-tabela-modal-colunas__vazio-icone {
color: var(--cor-eli-borda-forte);
}
.eli-tabela-modal-colunas__vazio-icone--ok {
color: var(--cor-eli-primaria-acao);
}
/* ── Animações TransitionGroup ── */
.list-move,
.list-enter-active,
.list-leave-active {
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: scale(0.95);
}
.list-leave-active {
position: absolute;
width: calc(100% - 16px);
}
/* ── Footer ── */
.eli-tabela-modal-colunas__footer {
display: flex;
justify-content: flex-end;
gap: 8px;
padding: 12px 16px;
border-top: 1px solid var(--cor-eli-borda);
background: var(--cor-eli-fundo);
}
.eli-tabela-modal-colunas__botao {
display: inline-flex;
align-items: center;
gap: 6px;
height: 34px;
padding: 0 16px;
border-radius: 8px;
border: 1px solid var(--cor-eli-borda);
background: var(--cor-eli-superficie);
cursor: pointer;
font-size: 0.875rem;
font-weight: 500;
transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}
.eli-tabela-modal-colunas__botao--sec {
color: var(--cor-eli-neutro-escuro);
}
.eli-tabela-modal-colunas__botao--sec:hover,
.eli-tabela-modal-colunas__botao--sec:focus-visible {
background: var(--cor-eli-fundo);
border-color: var(--cor-eli-borda-forte);
}
.eli-tabela-modal-colunas__botao--sec:focus-visible {
outline: 2px solid var(--cor-eli-primaria-clara);
outline-offset: 2px;
}
.eli-tabela-modal-colunas__botao--prim {
border: none;
background: var(--cor-eli-primaria);
color: #fff;
}
.eli-tabela-modal-colunas__botao--prim:hover,
.eli-tabela-modal-colunas__botao--prim:focus-visible {
background: var(--cor-eli-primaria-escura);
}
.eli-tabela-modal-colunas__botao--prim:focus-visible {
outline: 2px solid var(--cor-eli-primaria-clara);
outline-offset: 2px;
}
/* ── Responsivo ── */
@media (max-width: 720px) {
.eli-tabela-modal-colunas__conteudo {
grid-template-columns: 1fr;
}
.eli-tabela-modal-colunas__coluna + .eli-tabela-modal-colunas__coluna {
border-left: none;
border-top: 1px solid var(--cor-eli-borda);
}
}

View file

@ -1,3 +1,5 @@
@import "./EliTabelaModalFiltroFormula.css";
.eli-tabela-modal-filtro__overlay {
position: fixed;
inset: 0;
@ -217,227 +219,3 @@
outline: none;
border-color: var(--cor-eli-primaria);
}
/* ── Fórmula de combinação ───────────────────── */
.eli-tabela-modal-filtro__formula-container {
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
margin-left: auto;
}
.eli-tabela-modal-filtro__formula-label {
font-size: 0.82rem;
font-weight: 500;
color: rgba(15, 23, 42, 0.55);
white-space: nowrap;
flex-shrink: 0;
}
.eli-tabela-modal-filtro__formula-ajuda {
position: relative;
display: inline-flex;
align-items: center;
color: rgba(15, 23, 42, 0.35);
cursor: help;
transition: color 0.15s;
}
.eli-tabela-modal-filtro__formula-ajuda:hover,
.eli-tabela-modal-filtro__formula-ajuda:focus-visible {
color: var(--cor-eli-primaria);
outline: none;
}
.eli-tabela-modal-filtro__formula-ajuda:hover .eli-tabela-modal-filtro__formula-tooltip,
.eli-tabela-modal-filtro__formula-ajuda:focus-visible .eli-tabela-modal-filtro__formula-tooltip {
opacity: 1;
pointer-events: auto;
transform: translateY(0);
}
.eli-tabela-modal-filtro__formula-tooltip {
position: absolute;
bottom: calc(100% + 10px);
right: 0;
width: 280px;
background: #0f172a;
color: #e2e8f0;
border-radius: 10px;
padding: 14px 16px;
font-size: 0.8rem;
line-height: 1.55;
z-index: 100;
opacity: 0;
pointer-events: none;
transform: translateY(4px);
transition: opacity 0.18s ease, transform 0.18s ease;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.35);
}
.eli-tabela-modal-filtro__formula-tooltip::after {
content: '';
position: absolute;
top: 100%;
right: 6px;
border: 6px solid transparent;
border-top-color: #0f172a;
}
.eli-tabela-modal-filtro__formula-tooltip strong {
font-size: 0.82rem;
color: var(--cor-eli-fundo);
display: block;
margin-bottom: 6px;
}
.eli-tabela-modal-filtro__formula-tooltip p {
margin: 6px 0 4px;
color: #94a3b8;
}
.eli-tabela-modal-filtro__formula-tooltip ul {
margin: 4px 0 8px 14px;
padding: 0;
}
.eli-tabela-modal-filtro__formula-tooltip li {
margin-bottom: 3px;
color: #cbd5e1;
}
.eli-tabela-modal-filtro__formula-tooltip code {
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
color: #7dd3fc;
font-size: 0.78rem;
background: rgba(255, 255, 255, 0.07);
border-radius: 4px;
padding: 1px 4px;
}
.eli-tabela-modal-filtro__formula-input {
width: 220px;
height: 36px;
border-radius: 10px;
border: 1px solid rgba(15, 23, 42, 0.12);
padding: 0 10px;
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
font-size: 0.85rem;
background: var(--cor-eli-fundo);
transition: border-color 0.15s, background 0.15s;
box-sizing: border-box;
}
.eli-tabela-modal-filtro__formula-input:focus {
outline: none;
border-color: var(--cor-eli-primaria);
background: #fff;
}
.eli-tabela-modal-filtro__formula-tooltip-nota {
margin-top: 8px;
opacity: 0.75;
}
/* ── Footer ──────────────────────────────────── */
.eli-tabela-modal-filtro__footer {
display: flex;
align-items: center;
gap: 8px;
padding: 14px 20px;
border-top: 1px solid rgba(15, 23, 42, 0.08);
}
.eli-tabela-modal-filtro__footer-spacer {
flex: 1;
}
/* ── Botões ──────────────────────────────────── */
.eli-tabela-modal-filtro__botao {
display: inline-flex;
align-items: center;
gap: 5px;
height: 36px;
padding: 0 16px;
border-radius: 10px;
border: 1px solid rgba(15, 23, 42, 0.12);
background: #fff;
cursor: pointer;
font-size: 0.875rem;
font-weight: 500;
transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.eli-tabela-modal-filtro__botao:disabled {
opacity: 0.45;
cursor: not-allowed;
}
.eli-tabela-modal-filtro__botao--sec:hover:not(:disabled),
.eli-tabela-modal-filtro__botao--sec:focus-visible {
background: rgba(15, 23, 42, 0.05);
}
.eli-tabela-modal-filtro__botao--prim {
border: none;
background: var(--cor-eli-primaria);
color: #fff;
}
.eli-tabela-modal-filtro__botao--prim:hover:not(:disabled),
.eli-tabela-modal-filtro__botao--prim:focus-visible {
background: var(--cor-eli-primaria-escura);
}
.eli-tabela-modal-filtro__botao--perigo {
color: rgba(220, 38, 38, 0.85);
border-color: rgba(220, 38, 38, 0.18);
background: #fff;
}
.eli-tabela-modal-filtro__botao--perigo:hover:not(:disabled),
.eli-tabela-modal-filtro__botao--perigo:focus-visible {
background: rgba(239, 68, 68, 0.06);
border-color: rgba(220, 38, 38, 0.3);
color: rgba(220, 38, 38, 1);
}
/* ── Responsivo ──────────────────────────────── */
@media (max-width: 600px) {
.eli-tabela-modal-filtro__lista {
grid-template-columns: 1fr;
}
.eli-tabela-modal-filtro__rodape-filtros {
flex-direction: column;
align-items: stretch;
}
.eli-tabela-modal-filtro__acoes {
flex-wrap: wrap;
}
}
/* ── Transição de abertura/fechamento ────────── */
.eli-tabela-modal-filtro-enter-active,
.eli-tabela-modal-filtro-leave-active {
transition: opacity 0.2s ease;
}
.eli-tabela-modal-filtro-enter-active .eli-tabela-modal-filtro__modal,
.eli-tabela-modal-filtro-leave-active .eli-tabela-modal-filtro__modal {
transition: transform 0.2s ease, opacity 0.2s ease;
}
.eli-tabela-modal-filtro-enter-from,
.eli-tabela-modal-filtro-leave-to {
opacity: 0;
}
.eli-tabela-modal-filtro-enter-from .eli-tabela-modal-filtro__modal,
.eli-tabela-modal-filtro-leave-to .eli-tabela-modal-filtro__modal {
transform: scale(0.96);
opacity: 0;
}

View file

@ -0,0 +1,225 @@
/* =========================
* EliTabelaModalFiltroAvancado — Fórmula, tooltip, botões e transições
* Extraído de EliTabelaModalFiltroAvancado.css para manter cada arquivo abaixo de 250 linhas.
* ========================= */
/* ── Fórmula de combinação ───────────────────── */
.eli-tabela-modal-filtro__formula-container {
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
margin-left: auto;
}
.eli-tabela-modal-filtro__formula-label {
font-size: 0.82rem;
font-weight: 500;
color: rgba(15, 23, 42, 0.55);
white-space: nowrap;
flex-shrink: 0;
}
.eli-tabela-modal-filtro__formula-ajuda {
position: relative;
display: inline-flex;
align-items: center;
color: rgba(15, 23, 42, 0.35);
cursor: help;
transition: color 0.15s;
}
.eli-tabela-modal-filtro__formula-ajuda:hover,
.eli-tabela-modal-filtro__formula-ajuda:focus-visible {
color: var(--cor-eli-primaria);
outline: none;
}
.eli-tabela-modal-filtro__formula-ajuda:hover .eli-tabela-modal-filtro__formula-tooltip,
.eli-tabela-modal-filtro__formula-ajuda:focus-visible .eli-tabela-modal-filtro__formula-tooltip {
opacity: 1;
pointer-events: auto;
transform: translateY(0);
}
.eli-tabela-modal-filtro__formula-tooltip {
position: absolute;
bottom: calc(100% + 10px);
right: 0;
width: 280px;
background: #0f172a;
color: #e2e8f0;
border-radius: 10px;
padding: 14px 16px;
font-size: 0.8rem;
line-height: 1.55;
z-index: 100;
opacity: 0;
pointer-events: none;
transform: translateY(4px);
transition: opacity 0.18s ease, transform 0.18s ease;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.35);
}
.eli-tabela-modal-filtro__formula-tooltip::after {
content: '';
position: absolute;
top: 100%;
right: 6px;
border: 6px solid transparent;
border-top-color: #0f172a;
}
.eli-tabela-modal-filtro__formula-tooltip strong {
font-size: 0.82rem;
color: var(--cor-eli-fundo);
display: block;
margin-bottom: 6px;
}
.eli-tabela-modal-filtro__formula-tooltip p {
margin: 6px 0 4px;
color: #94a3b8;
}
.eli-tabela-modal-filtro__formula-tooltip ul {
margin: 4px 0 8px 14px;
padding: 0;
}
.eli-tabela-modal-filtro__formula-tooltip li {
margin-bottom: 3px;
color: #cbd5e1;
}
.eli-tabela-modal-filtro__formula-tooltip code {
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
color: #7dd3fc;
font-size: 0.78rem;
background: rgba(255, 255, 255, 0.07);
border-radius: 4px;
padding: 1px 4px;
}
.eli-tabela-modal-filtro__formula-input {
width: 220px;
height: 36px;
border-radius: 10px;
border: 1px solid rgba(15, 23, 42, 0.12);
padding: 0 10px;
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
font-size: 0.85rem;
background: var(--cor-eli-fundo);
transition: border-color 0.15s, background 0.15s;
box-sizing: border-box;
}
.eli-tabela-modal-filtro__formula-input:focus {
outline: none;
border-color: var(--cor-eli-primaria);
background: #fff;
}
.eli-tabela-modal-filtro__formula-tooltip-nota {
margin-top: 8px;
opacity: 0.75;
}
/* ── Footer ──────────────────────────────────── */
.eli-tabela-modal-filtro__footer {
display: flex;
align-items: center;
gap: 8px;
padding: 14px 20px;
border-top: 1px solid rgba(15, 23, 42, 0.08);
}
.eli-tabela-modal-filtro__footer-spacer {
flex: 1;
}
/* ── Botões ──────────────────────────────────── */
.eli-tabela-modal-filtro__botao {
display: inline-flex;
align-items: center;
gap: 5px;
height: 36px;
padding: 0 16px;
border-radius: 10px;
border: 1px solid rgba(15, 23, 42, 0.12);
background: #fff;
cursor: pointer;
font-size: 0.875rem;
font-weight: 500;
transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.eli-tabela-modal-filtro__botao:disabled {
opacity: 0.45;
cursor: not-allowed;
}
.eli-tabela-modal-filtro__botao--sec:hover:not(:disabled),
.eli-tabela-modal-filtro__botao--sec:focus-visible {
background: rgba(15, 23, 42, 0.05);
}
.eli-tabela-modal-filtro__botao--prim {
border: none;
background: var(--cor-eli-primaria);
color: #fff;
}
.eli-tabela-modal-filtro__botao--prim:hover:not(:disabled),
.eli-tabela-modal-filtro__botao--prim:focus-visible {
background: var(--cor-eli-primaria-escura);
}
.eli-tabela-modal-filtro__botao--perigo {
color: rgba(220, 38, 38, 0.85);
border-color: rgba(220, 38, 38, 0.18);
background: #fff;
}
.eli-tabela-modal-filtro__botao--perigo:hover:not(:disabled),
.eli-tabela-modal-filtro__botao--perigo:focus-visible {
background: rgba(239, 68, 68, 0.06);
border-color: rgba(220, 38, 38, 0.3);
color: rgba(220, 38, 38, 1);
}
/* ── Responsivo ──────────────────────────────── */
@media (max-width: 600px) {
.eli-tabela-modal-filtro__lista {
grid-template-columns: 1fr;
}
.eli-tabela-modal-filtro__rodape-filtros {
flex-direction: column;
align-items: stretch;
}
.eli-tabela-modal-filtro__acoes {
flex-wrap: wrap;
}
}
/* ── Transição de abertura/fechamento ────────── */
.eli-tabela-modal-filtro-enter-active,
.eli-tabela-modal-filtro-leave-active {
transition: opacity 0.2s ease;
}
.eli-tabela-modal-filtro-enter-active .eli-tabela-modal-filtro__modal,
.eli-tabela-modal-filtro-leave-active .eli-tabela-modal-filtro__modal {
transition: transform 0.2s ease, opacity 0.2s ease;
}
.eli-tabela-modal-filtro-enter-from,
.eli-tabela-modal-filtro-leave-to {
opacity: 0;
}
.eli-tabela-modal-filtro-enter-from .eli-tabela-modal-filtro__modal,
.eli-tabela-modal-filtro-leave-to .eli-tabela-modal-filtro__modal {
transform: scale(0.96);
opacity: 0;
}

View file

@ -0,0 +1,66 @@
/* =========================================
* Seleção (Checkboxes)
* Extraído de EliTabela.css para manter cada arquivo abaixo de 250 linhas.
* ========================================= */
.eli-tabela__th--selecao,
.eli-tabela__td--selecao {
width: 48px;
min-width: 48px;
max-width: 48px;
text-align: center;
padding: 0 8px;
vertical-align: middle;
}
.eli-tabela__checkbox {
appearance: none;
-webkit-appearance: none;
width: 15px;
height: 15px;
cursor: pointer;
border: 2px solid rgba(0, 0, 0, 0.54);
border-radius: 2px;
margin: 0;
vertical-align: middle;
position: relative;
transition: all 0.2s ease-in-out;
background-color: transparent;
outline: none;
}
.eli-tabela__checkbox:hover {
border-color: rgba(0, 0, 0, 0.87);
}
.eli-tabela__checkbox:checked,
.eli-tabela__checkbox:indeterminate {
background-color: var(--cor-eli-primaria);
border-color: var(--cor-eli-primaria);
}
.eli-tabela__checkbox:checked::after {
content: '';
position: absolute;
top: 0px;
left: 4px;
width: 4px;
height: 9px;
border: solid white;
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
.eli-tabela__checkbox:indeterminate::after {
content: '';
position: absolute;
top: 5px;
left: 2px;
width: 8px;
height: 2px;
background-color: white;
border-radius: 1px;
}
.eli-tabela__checkbox:focus-visible {
box-shadow: 0 0 0 2px var(--cor-eli-primaria-clara);
}

View file

@ -1,5 +1,5 @@
import type { LucideIcon } from "@lucide/vue"
import type { tipoFiltroDeltaColunaTipada } from "p-comuns"
import type { tipoCondicaoFiltroDeltaTipado, tipoFiltroDeltaColunaTipada } from "p-comuns"
import type { tipoResposta } from "p-respostas"
import type { ComponenteEntrada } from "../EliEntrada/tiposEntradas"
@ -160,9 +160,17 @@ export type tipoEliTabelaConsulta<T> = {
chave: string
/** Rótulo exibido no select de adicionar filtro */
rotulo: string
/** vai gerar filtro e mescla com os filtros já existentes */
filtro: (valor: unknown) => tipoFiltroDeltaColunaTipada<Extract<keyof T, string>> | undefined
/** vai gerar filtro e mescla com os filtros já existentes , id é gerado de forma altomatica para esse local*/
filtro: (
valor: unknown,
) =>
| Pick<
tipoCondicaoFiltroDeltaTipado<Extract<keyof T, string>>,
"coluna" | "operador" | "valor"
>
| undefined
/** componente de entrada que vai receber o valor para o filtro */
entrada: ComponenteEntrada
}[]
}
;("")

View file

@ -49,12 +49,13 @@ export function useEliTabelaFiltrosAvancados(
.map((f) => {
const b = base.find((x) => String(x.chave) === String(f.chave))
if (!b) return null
return { chave: f.chave, filtro: b.filtro(f.valor) }
const condicao = b.filtro(f.valor)
if (!condicao) return null
return { chave: f.chave, condicao }
})
.filter(
(f): f is { chave: string; filtro: tipoFiltroDeltaColunaTipada<string> } =>
f !== null && f.filtro !== undefined && f.filtro !== null,
)
.filter((f): f is { chave: string; condicao: tipoCondicaoFiltroDelta } => f !== null)
if (filtrosIndividuais.length === 0) return []
if (filtrosFormula.value && filtrosIndividuais.length > 1) {
const condicoesFinais: tipoCondicaoFiltroDelta[] = []
@ -62,25 +63,22 @@ export function useEliTabelaFiltrosAvancados(
filtrosIndividuais.forEach(({ chave }, i) => {
formulaFinal = formulaFinal.replace(new RegExp(`\\{${chave}\\}`, "g"), `{c${i + 1}}`)
})
let globalIdx = 1
for (let i = 0; i < filtrosIndividuais.length; i++) {
const { filtro } = filtrosIndividuais[i]
const idMap: Record<string, string> = {}
for (const cond of filtro.condicoes) {
const novoId = `{c${globalIdx++}}`
idMap[cond.id] = novoId
condicoesFinais.push({ ...cond, id: novoId })
}
let formulaLocal = filtro.formula
for (const [oldId, newId] of Object.entries(idMap)) {
formulaLocal = formulaLocal.replace(new RegExp(`\\{${oldId.slice(1, -1)}\\}`, "g"), newId)
}
const subst = filtro.condicoes.length > 1 ? `(${formulaLocal})` : formulaLocal
formulaFinal = formulaFinal.replace(new RegExp(`\\{c${i + 1}\\}`, "g"), subst)
}
filtrosIndividuais.forEach(({ condicao }, i) => {
const novoId = `{c${i + 1}}`
condicoesFinais.push({ ...condicao, id: novoId })
})
return [{ condicoes: condicoesFinais, formula: formulaFinal }]
}
return filtrosIndividuais.map((f) => f.filtro)
/** Sem fórmula customizada: monta condições com IDs sequenciais e fórmula "E" */
const condicoesFinais: tipoCondicaoFiltroDelta[] = filtrosIndividuais.map(
({ condicao }, i) => ({
...condicao,
id: `{c${i + 1}}`,
}),
)
const formula = condicoesFinais.map((c) => c.id).join(" E ")
return [{ condicoes: condicoesFinais, formula }]
})
function abrirModalFiltro() {
@ -114,12 +112,82 @@ export function useEliTabelaFiltrosAvancados(
else void carregar()
}
const nomesOperadores: Record<string, string> = {
"=": "igual a",
"!=": "diferente de",
">": "maior que",
"<": "menor que",
">=": "maior ou igual a",
"<=": "menor ou igual a",
like: "contém",
in: "em",
"∩": "possui",
}
function formatarFiltroAtivo(entrada: unknown, valor: unknown): string {
const [tipo, opcoes] = Array.isArray(entrada)
? (entrada as [string, Record<string, unknown>])
: [entrada as string, {} as Record<string, unknown>]
if (tipo === "confirmacao") {
const opt = opcoes || {}
if (valor === true) return (opt.rotuloAtivo as string) || "Sim"
if (valor === false) return (opt.rotuloInativo as string) || "Não"
return String(valor)
}
if ((tipo === "selecao" || tipo === "selecaoMultipla") && typeof opcoes?.itens === "function") {
try {
const itensResult = opcoes.itens()
if (Array.isArray(itensResult)) {
const findLabel = (key: string) => {
const found = (itensResult as { chave: string; rotulo: string }[]).find(
(item) => String(item.chave) === String(key),
)
return found ? found.rotulo : key
}
if (Array.isArray(valor)) {
return valor.map((v) => findLabel(String(v))).join(", ")
}
return findLabel(String(valor))
}
} catch {
// Fallback
}
}
if (Array.isArray(valor)) {
return valor.map(String).join(", ")
}
return String(valor)
}
/** Resumo dos filtros ativos para badge e tooltip do cabecalho */
const filtrosAtivosResumo = computed(() => {
const base = props.tabela.filtroAvancado ?? []
const ativos = (filtrosUi.value ?? []).filter((f) => f && f.chave !== undefined)
const rotulos: string[] = []
for (const f of ativos) {
const b = base.find((x) => String(x.chave) === String(f.chave))
if (b) {
const condicao = b.filtro(f.valor)
if (condicao) {
const opStr = nomesOperadores[condicao.operador] || condicao.operador
const valStr = formatarFiltroAtivo(b.entrada, condicao.valor)
rotulos.push(`${b.rotulo} ${opStr} "${valStr}"`)
}
}
}
return { quantidade: rotulos.length, rotulos }
})
return {
filtrosUi,
filtrosFormula,
modalFiltroAberto,
exibirFiltroAvancado,
filtrosAvancadosAtivos,
filtrosAtivosResumo,
abrirModalFiltro,
fecharModalFiltro,
limparFiltrosAvancados,

View file

@ -10,8 +10,16 @@
*
* Extraído de `EliTabelaModalFiltroAvancado.vue` para manter o componente abaixo de 250 linhas.
*/
import { computed, type Ref, ref, watch } from "vue"
import { EliEntradaDataHora, EliEntradaNumero, EliEntradaTexto } from "../EliEntrada"
import { type Component, computed, type Ref, ref, watch } from "vue"
import {
EliEntradaConfirmacao,
EliEntradaDataHora,
EliEntradaNumero,
EliEntradaParagrafo,
EliEntradaSelecao,
EliEntradaSelecaoMultipla,
EliEntradaTexto,
} from "../EliEntrada"
import type { ComponenteEntrada } from "../EliEntrada/tiposEntradas"
import type { tipoEliTabelaConsulta } from "./types-eli-tabela"
@ -97,10 +105,14 @@ export function useFiltroAvancado({
return (filtrosBase.value ?? []).filter((b) => !usadas.has(String(b.chave)))
})
function componenteEntrada(entrada: ComponenteEntrada) {
function componenteEntrada(entrada: ComponenteEntrada): Component {
const tipo = entrada?.[0]
if (tipo === "numero") return EliEntradaNumero
if (tipo === "dataHora") return EliEntradaDataHora
if (tipo === "selecao") return EliEntradaSelecao
if (tipo === "selecaoMultipla") return EliEntradaSelecaoMultipla
if (tipo === "paragrafo") return EliEntradaParagrafo
if (tipo === "confirmacao") return EliEntradaConfirmacao
return EliEntradaTexto
}
@ -114,6 +126,9 @@ export function useFiltroAvancado({
function valorInicialPorEntrada(entrada: ComponenteEntrada) {
if (entrada?.[0] === "numero") return null
if (entrada?.[0] === "selecao") return null
if (entrada?.[0] === "selecaoMultipla") return []
if (entrada?.[0] === "confirmacao") return false
return ""
}