From 5c587c92322993f517ff2ddb935f1af9033fbac1 Mon Sep 17 00:00:00 2001 From: Luiz Silva Date: Wed, 28 Jan 2026 19:28:34 -0300 Subject: [PATCH] adicionado detalhes --- .../eli/EliTabela/EliTabela.vue.d.ts | 97 +++++++++++- .../eli/EliTabela/EliTabelaBody.vue.d.ts | 76 +++++++++ .../EliTabela/EliTabelaDetalhesLinha.vue.d.ts | 44 ++++++ .../eli/EliTabela/EliTabelaHead.vue.d.ts | 8 + src/components/eli/EliTabela/EliTabela.css | 78 ++++++++++ src/components/eli/EliTabela/EliTabela.vue | 55 +++++++ .../eli/EliTabela/EliTabelaBody.vue | 147 ++++++++++++------ .../eli/EliTabela/EliTabelaDetalhesLinha.vue | 35 +++++ .../eli/EliTabela/EliTabelaHead.vue | 6 + 9 files changed, 496 insertions(+), 50 deletions(-) create mode 100644 dist/types/components/eli/EliTabela/EliTabelaDetalhesLinha.vue.d.ts create mode 100644 src/components/eli/EliTabela/EliTabelaDetalhesLinha.vue diff --git a/dist/types/components/eli/EliTabela/EliTabela.vue.d.ts b/dist/types/components/eli/EliTabela/EliTabela.vue.d.ts index 28aef6f..ac7f3ad 100644 --- a/dist/types/components/eli/EliTabela/EliTabela.vue.d.ts +++ b/dist/types/components/eli/EliTabela/EliTabela.vue.d.ts @@ -4,6 +4,7 @@ */ /** Dependências do Vue (Composition API) */ import { PropType } from "vue"; +import type { EliColuna } from "./types-eli-tabela"; /** Tipos da configuração/contrato da tabela */ import type { EliTabelaConsulta } from "./types-eli-tabela"; import { type EliTabelaColunasConfig } from "./colunasStorage"; @@ -45,9 +46,13 @@ declare const __VLS_export: import("vue").DefineComponent; + temColunasInvisiveis: import("vue").ComputedRef; + colunasInvisiveisEfetivas: import("vue").ComputedRef[]>; + linhasExpandidas: import("vue").Ref, Record>; abrirModalColunas: () => void; fecharModalColunas: () => void; salvarModalColunas: (cfg: EliTabelaColunasConfig) => void; + alternarLinhaExpandida: (indice: number) => void; alternarOrdenacao: (chave?: string) => void; atualizarBusca: (texto: string) => void; irParaPagina: (pagina: number) => void; @@ -262,13 +267,17 @@ declare const __VLS_export: import("vue").DefineComponent> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>; EliTabelaHead: import("vue").DefineComponent>>; + type: PropType>>; required: true; }; temAcoes: { type: BooleanConstructor; required: true; }; + temColunasInvisiveis: { + type: BooleanConstructor; + required: true; + }; colunaOrdenacao: { type: PropType; required: true; @@ -287,13 +296,17 @@ declare const __VLS_export: import("vue").DefineComponent>>; + type: PropType>>; required: true; }; temAcoes: { type: BooleanConstructor; required: true; }; + temColunasInvisiveis: { + type: BooleanConstructor; + required: true; + }; colunaOrdenacao: { type: PropType; required: true; @@ -310,7 +323,19 @@ declare const __VLS_export: import("vue").DefineComponent; EliTabelaBody: import("vue").DefineComponent>>; + type: PropType>>; + required: true; + }; + colunasInvisiveis: { + type: PropType>>; + required: true; + }; + temColunasInvisiveis: { + type: BooleanConstructor; + required: true; + }; + linhasExpandidas: { + type: PropType>; required: true; }; linhas: { @@ -333,13 +358,31 @@ declare const __VLS_export: import("vue").DefineComponent void>; required: true; }; + alternarLinhaExpandida: { + type: PropType<(indice: number) => void>; + required: true; + }; }>, { + ChevronRight: import("vue").FunctionalComponent; + ChevronDown: import("vue").FunctionalComponent; obterClasseAlinhamento: (alinhamento?: string) => "eli-tabela__celula--direita" | "eli-tabela__celula--centro" | "eli-tabela__celula--esquerda"; obterMaxWidth: (largura?: number | string) => string | undefined; obterTooltipCelula: (celula: unknown) => any; }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly>>; + type: PropType>>; + required: true; + }; + colunasInvisiveis: { + type: PropType>>; + required: true; + }; + temColunasInvisiveis: { + type: BooleanConstructor; + required: true; + }; + linhasExpandidas: { + type: PropType>; required: true; }; linhas: { @@ -362,6 +405,10 @@ declare const __VLS_export: import("vue").DefineComponent void>; required: true; }; + alternarLinhaExpandida: { + type: PropType<(indice: number) => void>; + required: true; + }; }>> & Readonly<{}>, {}, {}, { EliTabelaCelula: import("vue").DefineComponent> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>; + EliTabelaDetalhesLinha: import("vue").DefineComponent; + required: true; + }; + colunasInvisiveis: { + type: PropType>>; + required: true; + }; + }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly; + required: true; + }; + colunasInvisiveis: { + type: PropType>>; + required: true; + }; + }>> & Readonly<{}>, {}, {}, { + EliTabelaCelula: import("vue").DefineComponent; + required: true; + }; + }>, { + Componente: import("vue").ComputedRef; + dadosParaComponente: import("vue").ComputedRef<{ + texto: string; + acao?: () => void; + } | { + numero: number; + acao?: () => void; + }>; + }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly; + required: true; + }; + }>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>; + }, {}, string, import("vue").ComponentProvideOptions, true, {}, any>; MoreVertical: import("vue").FunctionalComponent; + ChevronRight: import("vue").FunctionalComponent; + ChevronDown: import("vue").FunctionalComponent; }, {}, string, import("vue").ComponentProvideOptions, true, {}, any>; EliTabelaMenuAcoes: import("vue").DefineComponent>>; required: true; }; + colunasInvisiveis: { + type: PropType>>; + required: true; + }; + temColunasInvisiveis: { + type: BooleanConstructor; + required: true; + }; + linhasExpandidas: { + type: PropType>; + required: true; + }; linhas: { type: PropType>; required: true; @@ -25,7 +37,13 @@ declare const __VLS_export: import("vue").DefineComponent void>; required: true; }; + alternarLinhaExpandida: { + type: PropType<(indice: number) => void>; + required: true; + }; }>, { + ChevronRight: import("vue").FunctionalComponent; + ChevronDown: import("vue").FunctionalComponent; obterClasseAlinhamento: (alinhamento?: string) => "eli-tabela__celula--direita" | "eli-tabela__celula--centro" | "eli-tabela__celula--esquerda"; obterMaxWidth: (largura?: number | string) => string | undefined; obterTooltipCelula: (celula: unknown) => any; @@ -34,6 +52,18 @@ declare const __VLS_export: import("vue").DefineComponent>>; required: true; }; + colunasInvisiveis: { + type: PropType>>; + required: true; + }; + temColunasInvisiveis: { + type: BooleanConstructor; + required: true; + }; + linhasExpandidas: { + type: PropType>; + required: true; + }; linhas: { type: PropType>; required: true; @@ -54,6 +84,10 @@ declare const __VLS_export: import("vue").DefineComponent void>; required: true; }; + alternarLinhaExpandida: { + type: PropType<(indice: number) => void>; + required: true; + }; }>> & Readonly<{}>, {}, {}, { EliTabelaCelula: import("vue").DefineComponent> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>; + EliTabelaDetalhesLinha: import("vue").DefineComponent; + required: true; + }; + colunasInvisiveis: { + type: PropType>>; + required: true; + }; + }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly; + required: true; + }; + colunasInvisiveis: { + type: PropType>>; + required: true; + }; + }>> & Readonly<{}>, {}, {}, { + EliTabelaCelula: import("vue").DefineComponent; + required: true; + }; + }>, { + Componente: import("vue").ComputedRef; + dadosParaComponente: import("vue").ComputedRef<{ + texto: string; + acao?: () => void; + } | { + numero: number; + acao?: () => void; + }>; + }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly; + required: true; + }; + }>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>; + }, {}, string, import("vue").ComponentProvideOptions, true, {}, any>; MoreVertical: import("vue").FunctionalComponent; + ChevronRight: import("vue").FunctionalComponent; + ChevronDown: import("vue").FunctionalComponent; }, {}, string, import("vue").ComponentProvideOptions, true, {}, any>; declare const _default: typeof __VLS_export; export default _default; diff --git a/dist/types/components/eli/EliTabela/EliTabelaDetalhesLinha.vue.d.ts b/dist/types/components/eli/EliTabela/EliTabelaDetalhesLinha.vue.d.ts new file mode 100644 index 0000000..4abd012 --- /dev/null +++ b/dist/types/components/eli/EliTabela/EliTabelaDetalhesLinha.vue.d.ts @@ -0,0 +1,44 @@ +import { PropType } from "vue"; +import type { EliColuna } from "./types-eli-tabela"; +declare const __VLS_export: import("vue").DefineComponent; + required: true; + }; + colunasInvisiveis: { + type: PropType>>; + required: true; + }; +}>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly; + required: true; + }; + colunasInvisiveis: { + type: PropType>>; + required: true; + }; +}>> & Readonly<{}>, {}, {}, { + EliTabelaCelula: import("vue").DefineComponent; + required: true; + }; + }>, { + Componente: import("vue").ComputedRef; + dadosParaComponente: import("vue").ComputedRef<{ + texto: string; + acao?: () => void; + } | { + numero: number; + acao?: () => void; + }>; + }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly; + required: true; + }; + }>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>; +}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>; +declare const _default: typeof __VLS_export; +export default _default; diff --git a/dist/types/components/eli/EliTabela/EliTabelaHead.vue.d.ts b/dist/types/components/eli/EliTabela/EliTabelaHead.vue.d.ts index cf8bcb2..db9effe 100644 --- a/dist/types/components/eli/EliTabela/EliTabelaHead.vue.d.ts +++ b/dist/types/components/eli/EliTabela/EliTabelaHead.vue.d.ts @@ -9,6 +9,10 @@ declare const __VLS_export: import("vue").DefineComponent; required: true; @@ -34,6 +38,10 @@ declare const __VLS_export: import("vue").DefineComponent; required: true; diff --git a/src/components/eli/EliTabela/EliTabela.css b/src/components/eli/EliTabela/EliTabela.css index 4b6e6bc..9d2563a 100644 --- a/src/components/eli/EliTabela/EliTabela.css +++ b/src/components/eli/EliTabela/EliTabela.css @@ -311,3 +311,81 @@ 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 rgba(37, 99, 235, 0.45); + 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; + } +} diff --git a/src/components/eli/EliTabela/EliTabela.vue b/src/components/eli/EliTabela/EliTabela.vue index 7dd3f25..f12f586 100644 --- a/src/components/eli/EliTabela/EliTabela.vue +++ b/src/components/eli/EliTabela/EliTabela.vue @@ -31,6 +31,7 @@ @@ -93,6 +98,7 @@ import EliTabelaBody from "./EliTabelaBody.vue"; import EliTabelaMenuAcoes from "./EliTabelaMenuAcoes.vue"; import EliTabelaPaginacao from "./EliTabelaPaginacao.vue"; import EliTabelaModalColunas from "./EliTabelaModalColunas.vue"; +import type { EliColuna } from "./types-eli-tabela"; /** Tipos da configuração/contrato da tabela */ import type { EliTabelaConsulta } from "./types-eli-tabela"; import { @@ -158,8 +164,37 @@ export default defineComponent({ carregarConfigColunas(props.tabela.nome) ); + /** Linhas expandidas (para exibir colunas invisíveis) */ + const linhasExpandidas = ref>({}); + const rotulosColunas = computed(() => props.tabela.colunas.map((c) => c.rotulo)); + const colunasInvisiveisEfetivas = computed(() => { + const colunas = props.tabela.colunas as Array>; + const invisiveisSet = new Set(configColunas.value.invisiveis ?? []); + const base = colunas.filter((c) => invisiveisSet.has(c.rotulo)); + + // ordenação: usa a lista salva de invisíveis (se existir), senão segue ordem original + const ordemSalva = configColunas.value.invisiveis ?? []; + const mapa = new Map>(); + for (const c of base) { + if (!mapa.has(c.rotulo)) mapa.set(c.rotulo, c); + } + + const ordenadas: Array> = []; + for (const r of ordemSalva) { + const c = mapa.get(r); + if (c) ordenadas.push(c); + } + for (const c of base) { + if (!ordenadas.includes(c)) ordenadas.push(c); + } + + return ordenadas; + }); + + const temColunasInvisiveis = computed(() => colunasInvisiveisEfetivas.value.length > 0); + const colunasEfetivas = computed(() => { const colunas = props.tabela.colunas; const todosRotulos = rotulosColunas.value; @@ -202,6 +237,17 @@ export default defineComponent({ configColunas.value = cfg; salvarConfigColunas(props.tabela.nome, cfg); modalColunasAberto.value = false; + + // ao mudar colunas, fecha detalhes expandidos + linhasExpandidas.value = {}; + } + + function alternarLinhaExpandida(indice: number) { + const atual = Boolean(linhasExpandidas.value[indice]); + linhasExpandidas.value = { + ...linhasExpandidas.value, + [indice]: !atual, + }; } /** Registros por consulta (normaliza para inteiro positivo) */ @@ -383,6 +429,7 @@ export default defineComponent({ erro.value = null; acoesVisiveis.value = []; menuAberto.value = null; + linhasExpandidas.value = {}; const limite = Math.max(1, registrosPorConsulta.value); const offset = (paginaAtual.value - 1) * limite; @@ -523,6 +570,7 @@ export default defineComponent({ valorBusca.value = ""; modalColunasAberto.value = false; configColunas.value = carregarConfigColunas(props.tabela.nome); + linhasExpandidas.value = {}; if (paginaAtual.value !== 1) { paginaAtual.value = 1; } else { @@ -546,6 +594,7 @@ export default defineComponent({ /** Watch: mudança nas linhas fecha o menu aberto */ watch(linhas, () => { menuAberto.value = null; + linhasExpandidas.value = {}; }); /** Exposição para o template (state, computed, helpers e actions) */ @@ -574,10 +623,16 @@ export default defineComponent({ modalColunasAberto, configColunas, + temColunasInvisiveis, + colunasInvisiveisEfetivas, + linhasExpandidas, + abrirModalColunas, fecharModalColunas, salvarModalColunas, + alternarLinhaExpandida, + // actions alternarOrdenacao, atualizarBusca, diff --git a/src/components/eli/EliTabela/EliTabelaBody.vue b/src/components/eli/EliTabela/EliTabelaBody.vue index 6ee7d2b..adadd9f 100644 --- a/src/components/eli/EliTabela/EliTabelaBody.vue +++ b/src/components/eli/EliTabela/EliTabelaBody.vue @@ -1,75 +1,124 @@ + + \ No newline at end of file diff --git a/src/components/eli/EliTabela/EliTabelaHead.vue b/src/components/eli/EliTabela/EliTabelaHead.vue index 91003dc..0cd741b 100644 --- a/src/components/eli/EliTabela/EliTabelaHead.vue +++ b/src/components/eli/EliTabela/EliTabelaHead.vue @@ -1,6 +1,8 @@