bkp
This commit is contained in:
parent
4cc8bb736d
commit
d737400bad
19 changed files with 2820 additions and 1577 deletions
2
dist/eli-vue.css
vendored
2
dist/eli-vue.css
vendored
File diff suppressed because one or more lines are too long
2139
dist/eli-vue.es.js
vendored
2139
dist/eli-vue.es.js
vendored
File diff suppressed because it is too large
Load diff
28
dist/eli-vue.umd.js
vendored
28
dist/eli-vue.umd.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -1,11 +1,19 @@
|
||||||
|
/**
|
||||||
|
* EliTabela
|
||||||
|
* Componente de tabela consultável com busca, paginação, ordenação e ações por linha.
|
||||||
|
*/
|
||||||
|
/** Dependências do Vue (Composition API) */
|
||||||
import { PropType } from "vue";
|
import { PropType } from "vue";
|
||||||
|
/** Tipos da configuração/contrato da tabela */
|
||||||
import type { EliTabelaConsulta } from "./types-eli-tabela";
|
import type { EliTabelaConsulta } from "./types-eli-tabela";
|
||||||
declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||||
|
/** Configuração principal da tabela (colunas, consulta e ações) */
|
||||||
tabela: {
|
tabela: {
|
||||||
type: PropType<EliTabelaConsulta<any>>;
|
type: PropType<EliTabelaConsulta<any>>;
|
||||||
required: true;
|
required: true;
|
||||||
};
|
};
|
||||||
}>, {
|
}>, {
|
||||||
|
isDev: boolean;
|
||||||
tabela: import("vue").ComputedRef<EliTabelaConsulta<any>>;
|
tabela: import("vue").ComputedRef<EliTabelaConsulta<any>>;
|
||||||
carregando: import("vue").Ref<boolean, boolean>;
|
carregando: import("vue").Ref<boolean, boolean>;
|
||||||
erro: import("vue").Ref<string | null, string | null>;
|
erro: import("vue").Ref<string | null, string | null>;
|
||||||
|
|
@ -26,30 +34,114 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
||||||
}[]>;
|
}[]>;
|
||||||
temAcoesCabecalho: import("vue").ComputedRef<boolean>;
|
temAcoesCabecalho: import("vue").ComputedRef<boolean>;
|
||||||
temAcoes: import("vue").ComputedRef<boolean>;
|
temAcoes: import("vue").ComputedRef<boolean>;
|
||||||
ArrowUp: import("vue").FunctionalComponent<import("lucide-vue-next").LucideProps, {}, any, {}>;
|
|
||||||
ArrowDown: import("vue").FunctionalComponent<import("lucide-vue-next").LucideProps, {}, any, {}>;
|
|
||||||
MoreVertical: import("vue").FunctionalComponent<import("lucide-vue-next").LucideProps, {}, any, {}>;
|
|
||||||
isOrdenavel: (coluna: any) => boolean;
|
|
||||||
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;
|
|
||||||
alternarOrdenacao: (chave?: string) => void;
|
alternarOrdenacao: (chave?: string) => void;
|
||||||
atualizarBusca: (texto: string) => void;
|
atualizarBusca: (texto: string) => void;
|
||||||
irParaPagina: (pagina: number) => void;
|
irParaPagina: (pagina: number) => void;
|
||||||
registrarMenuElemento: (indice: number, elemento: HTMLElement | null) => void;
|
|
||||||
acoesDisponiveisPorLinha: (i: number) => {
|
acoesDisponiveisPorLinha: (i: number) => {
|
||||||
acao: import("./types-eli-tabela").EliTabelaAcao<any>;
|
acao: import("./types-eli-tabela").EliTabelaAcao<any>;
|
||||||
indice: number;
|
indice: number;
|
||||||
visivel: boolean;
|
visivel: boolean;
|
||||||
}[];
|
}[];
|
||||||
possuiAcoes: (i: number) => boolean;
|
possuiAcoes: (i: number) => boolean;
|
||||||
toggleMenu: (i: number) => void;
|
toggleMenu: (i: number, evento?: MouseEvent) => void;
|
||||||
|
menuPopup: import("vue").Ref<{
|
||||||
|
menuEl: {
|
||||||
|
value: HTMLElement | null;
|
||||||
|
};
|
||||||
|
} | null, {
|
||||||
|
menuEl: {
|
||||||
|
value: HTMLElement | null;
|
||||||
|
};
|
||||||
|
} | {
|
||||||
|
menuEl: {
|
||||||
|
value: HTMLElement | null;
|
||||||
|
};
|
||||||
|
} | null>;
|
||||||
|
menuPopupPos: import("vue").Ref<{
|
||||||
|
top: number;
|
||||||
|
left: number;
|
||||||
|
}, {
|
||||||
|
top: number;
|
||||||
|
left: number;
|
||||||
|
} | {
|
||||||
|
top: number;
|
||||||
|
left: number;
|
||||||
|
}>;
|
||||||
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||||
|
/** Configuração principal da tabela (colunas, consulta e ações) */
|
||||||
tabela: {
|
tabela: {
|
||||||
type: PropType<EliTabelaConsulta<any>>;
|
type: PropType<EliTabelaConsulta<any>>;
|
||||||
required: true;
|
required: true;
|
||||||
};
|
};
|
||||||
}>> & Readonly<{}>, {}, {}, {
|
}>> & Readonly<{}>, {}, {}, {
|
||||||
|
EliTabelaCabecalho: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||||
|
exibirBusca: {
|
||||||
|
type: BooleanConstructor;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
valorBusca: {
|
||||||
|
type: StringConstructor;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
acoesCabecalho: {
|
||||||
|
type: PropType<Array<{
|
||||||
|
icone
|
||||||
|
/**
|
||||||
|
* EliTabela
|
||||||
|
* Componente de tabela consultável com busca, paginação, ordenação e ações por linha.
|
||||||
|
*/
|
||||||
|
/** Dependências do Vue (Composition API) */
|
||||||
|
?: any;
|
||||||
|
cor
|
||||||
|
/**
|
||||||
|
* EliTabela
|
||||||
|
* Componente de tabela consultável com busca, paginação, ordenação e ações por linha.
|
||||||
|
*/
|
||||||
|
/** Dependências do Vue (Composition API) */
|
||||||
|
?: string;
|
||||||
|
rotulo: string;
|
||||||
|
acao: () => void;
|
||||||
|
}>>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>, {
|
||||||
|
temAcoesCabecalho: import("vue").ComputedRef<boolean>;
|
||||||
|
emitBuscar: (texto: string) => void;
|
||||||
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
||||||
|
buscar(valor: string): boolean;
|
||||||
|
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||||
|
exibirBusca: {
|
||||||
|
type: BooleanConstructor;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
valorBusca: {
|
||||||
|
type: StringConstructor;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
acoesCabecalho: {
|
||||||
|
type: PropType<Array<{
|
||||||
|
icone
|
||||||
|
/**
|
||||||
|
* EliTabela
|
||||||
|
* Componente de tabela consultável com busca, paginação, ordenação e ações por linha.
|
||||||
|
*/
|
||||||
|
/** Dependências do Vue (Composition API) */
|
||||||
|
?: any;
|
||||||
|
cor
|
||||||
|
/**
|
||||||
|
* EliTabela
|
||||||
|
* Componente de tabela consultável com busca, paginação, ordenação e ações por linha.
|
||||||
|
*/
|
||||||
|
/** Dependências do Vue (Composition API) */
|
||||||
|
?: string;
|
||||||
|
rotulo: string;
|
||||||
|
acao: () => void;
|
||||||
|
}>>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>> & Readonly<{
|
||||||
|
onBuscar?: ((valor: string) => any) | undefined;
|
||||||
|
}>, {}, {}, {
|
||||||
EliTabelaCaixaDeBusca: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
EliTabelaCaixaDeBusca: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||||
modelo: {
|
modelo: {
|
||||||
type: StringConstructor;
|
type: StringConstructor;
|
||||||
|
|
@ -74,6 +166,264 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
||||||
}, {}, {
|
}, {}, {
|
||||||
Search: import("vue").FunctionalComponent<import("lucide-vue-next").LucideProps, {}, any, {}>;
|
Search: import("vue").FunctionalComponent<import("lucide-vue-next").LucideProps, {}, any, {}>;
|
||||||
}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||||
|
}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||||
|
EliTabelaEstados: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||||
|
carregando: {
|
||||||
|
type: BooleanConstructor;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
erro: {
|
||||||
|
type: PropType<string | null>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
mensagemVazio: {
|
||||||
|
type: PropType<string | undefined>;
|
||||||
|
required: false;
|
||||||
|
default: undefined;
|
||||||
|
};
|
||||||
|
}>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||||
|
carregando: {
|
||||||
|
type: BooleanConstructor;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
erro: {
|
||||||
|
type: PropType<string | null>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
mensagemVazio: {
|
||||||
|
type: PropType<string | undefined>;
|
||||||
|
required: false;
|
||||||
|
default: undefined;
|
||||||
|
};
|
||||||
|
}>> & Readonly<{}>, {
|
||||||
|
mensagemVazio: string | undefined;
|
||||||
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||||
|
EliTabelaDebug: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||||
|
isDev: {
|
||||||
|
type: BooleanConstructor;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
menuAberto: {
|
||||||
|
type: PropType<number | null>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
menuPopupPos: {
|
||||||
|
type: PropType<{
|
||||||
|
top: number;
|
||||||
|
left: number;
|
||||||
|
}>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||||
|
isDev: {
|
||||||
|
type: BooleanConstructor;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
menuAberto: {
|
||||||
|
type: PropType<number | null>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
menuPopupPos: {
|
||||||
|
type: PropType<{
|
||||||
|
top: number;
|
||||||
|
left: number;
|
||||||
|
}>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||||
|
EliTabelaHead: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||||
|
colunas: {
|
||||||
|
type: PropType<Array<import("./types-eli-tabela").EliColuna<any>>>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
temAcoes: {
|
||||||
|
type: BooleanConstructor;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
colunaOrdenacao: {
|
||||||
|
type: PropType<string | null>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
direcaoOrdenacao: {
|
||||||
|
type: PropType<"asc" | "desc">;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>, {
|
||||||
|
ArrowUp: import("vue").FunctionalComponent<import("lucide-vue-next").LucideProps, {}, any, {}>;
|
||||||
|
ArrowDown: import("vue").FunctionalComponent<import("lucide-vue-next").LucideProps, {}, any, {}>;
|
||||||
|
isOrdenavel: (coluna: any) => boolean;
|
||||||
|
obterClasseAlinhamento: (alinhamento?: string) => "eli-tabela__celula--direita" | "eli-tabela__celula--centro" | "eli-tabela__celula--esquerda";
|
||||||
|
emitAlternarOrdenacao: (chave: string) => void;
|
||||||
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
||||||
|
alternarOrdenacao(chave: string): boolean;
|
||||||
|
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||||
|
colunas: {
|
||||||
|
type: PropType<Array<import("./types-eli-tabela").EliColuna<any>>>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
temAcoes: {
|
||||||
|
type: BooleanConstructor;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
colunaOrdenacao: {
|
||||||
|
type: PropType<string | null>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
direcaoOrdenacao: {
|
||||||
|
type: PropType<"asc" | "desc">;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>> & Readonly<{
|
||||||
|
onAlternarOrdenacao?: ((chave: string) => any) | undefined;
|
||||||
|
}>, {}, {}, {
|
||||||
|
ArrowUp: import("vue").FunctionalComponent<import("lucide-vue-next").LucideProps, {}, any, {}>;
|
||||||
|
ArrowDown: import("vue").FunctionalComponent<import("lucide-vue-next").LucideProps, {}, any, {}>;
|
||||||
|
}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||||
|
EliTabelaBody: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||||
|
colunas: {
|
||||||
|
type: PropType<Array<import("./types-eli-tabela").EliColuna<any>>>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
linhas: {
|
||||||
|
type: PropType<Array<unknown>>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
temAcoes: {
|
||||||
|
type: BooleanConstructor;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
menuAberto: {
|
||||||
|
type: PropType<number | null>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
possuiAcoes: {
|
||||||
|
type: PropType<(i: number) => boolean>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>, {
|
||||||
|
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;
|
||||||
|
emitToggleMenu: (indice: number, evento: MouseEvent) => void;
|
||||||
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
||||||
|
toggleMenu(payload: {
|
||||||
|
indice: number;
|
||||||
|
evento: MouseEvent;
|
||||||
|
}): boolean;
|
||||||
|
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||||
|
colunas: {
|
||||||
|
type: PropType<Array<import("./types-eli-tabela").EliColuna<any>>>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
linhas: {
|
||||||
|
type: PropType<Array<unknown>>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
temAcoes: {
|
||||||
|
type: BooleanConstructor;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
menuAberto: {
|
||||||
|
type: PropType<number | null>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
possuiAcoes: {
|
||||||
|
type: PropType<(i: number) => boolean>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>> & Readonly<{
|
||||||
|
onToggleMenu?: ((payload: {
|
||||||
|
indice: number;
|
||||||
|
evento: MouseEvent;
|
||||||
|
}) => any) | undefined;
|
||||||
|
}>, {}, {}, {
|
||||||
|
EliTabelaCelula: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||||
|
celula: {
|
||||||
|
type: PropType<import("./types-eli-tabela").ComponenteCelula>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>, {
|
||||||
|
Componente: import("vue").ComputedRef<import("vue").Component>;
|
||||||
|
dadosParaComponente: import("vue").ComputedRef<{
|
||||||
|
texto: string;
|
||||||
|
acao?: () => void;
|
||||||
|
} | {
|
||||||
|
numero: number;
|
||||||
|
acao?: () => void;
|
||||||
|
}>;
|
||||||
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||||
|
celula: {
|
||||||
|
type: PropType<import("./types-eli-tabela").ComponenteCelula>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||||
|
MoreVertical: import("vue").FunctionalComponent<import("lucide-vue-next").LucideProps, {}, any, {}>;
|
||||||
|
}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||||
|
EliTabelaMenuAcoes: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||||
|
menuAberto: {
|
||||||
|
type: PropType<number | null>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
posicao: {
|
||||||
|
type: PropType<{
|
||||||
|
top: number;
|
||||||
|
left: number;
|
||||||
|
}>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
acoes: {
|
||||||
|
type: PropType<Array<{
|
||||||
|
acao: import("./types-eli-tabela").EliTabelaAcao<any>;
|
||||||
|
indice: number;
|
||||||
|
visivel: boolean;
|
||||||
|
}>>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
linha: {
|
||||||
|
type: PropType<unknown | null>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>, {
|
||||||
|
menuEl: import("vue").Ref<HTMLElement | null, HTMLElement | null>;
|
||||||
|
possuiAcoes: import("vue").ComputedRef<boolean>;
|
||||||
|
emitExecutar: (item: {
|
||||||
|
acao: import("./types-eli-tabela").EliTabelaAcao<any>;
|
||||||
|
}) => void;
|
||||||
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
||||||
|
executar(payload: {
|
||||||
|
acao: import("./types-eli-tabela").EliTabelaAcao<any>;
|
||||||
|
linha: unknown;
|
||||||
|
}): boolean;
|
||||||
|
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||||
|
menuAberto: {
|
||||||
|
type: PropType<number | null>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
posicao: {
|
||||||
|
type: PropType<{
|
||||||
|
top: number;
|
||||||
|
left: number;
|
||||||
|
}>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
acoes: {
|
||||||
|
type: PropType<Array<{
|
||||||
|
acao: import("./types-eli-tabela").EliTabelaAcao<any>;
|
||||||
|
indice: number;
|
||||||
|
visivel: boolean;
|
||||||
|
}>>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
linha: {
|
||||||
|
type: PropType<unknown | null>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>> & Readonly<{
|
||||||
|
onExecutar?: ((payload: {
|
||||||
|
acao: import("./types-eli-tabela").EliTabelaAcao<any>;
|
||||||
|
linha: unknown;
|
||||||
|
}) => any) | undefined;
|
||||||
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||||
EliTabelaPaginacao: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
EliTabelaPaginacao: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||||
pagina: {
|
pagina: {
|
||||||
type: NumberConstructor;
|
type: NumberConstructor;
|
||||||
|
|
@ -117,29 +467,6 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
||||||
}>> & Readonly<{
|
}>> & Readonly<{
|
||||||
onAlterar?: ((pagina: number) => any) | undefined;
|
onAlterar?: ((pagina: number) => any) | undefined;
|
||||||
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||||
EliTabelaCelula: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
||||||
celula: {
|
|
||||||
type: PropType<import("./types-eli-tabela").ComponenteCelula>;
|
|
||||||
required: true;
|
|
||||||
};
|
|
||||||
}>, {
|
|
||||||
Componente: import("vue").ComputedRef<import("vue").Component>;
|
|
||||||
dadosParaComponente: import("vue").ComputedRef<{
|
|
||||||
texto: string;
|
|
||||||
acao?: () => void;
|
|
||||||
} | {
|
|
||||||
numero: number;
|
|
||||||
acao?: () => void;
|
|
||||||
}>;
|
|
||||||
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
||||||
celula: {
|
|
||||||
type: PropType<import("./types-eli-tabela").ComponenteCelula>;
|
|
||||||
required: true;
|
|
||||||
};
|
|
||||||
}>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
||||||
ArrowUp: import("vue").FunctionalComponent<import("lucide-vue-next").LucideProps, {}, any, {}>;
|
|
||||||
ArrowDown: import("vue").FunctionalComponent<import("lucide-vue-next").LucideProps, {}, any, {}>;
|
|
||||||
MoreVertical: import("vue").FunctionalComponent<import("lucide-vue-next").LucideProps, {}, any, {}>;
|
|
||||||
}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||||
declare const _default: typeof __VLS_export;
|
declare const _default: typeof __VLS_export;
|
||||||
export default _default;
|
export default _default;
|
||||||
|
|
|
||||||
84
dist/types/components/eli/EliTabela/EliTabelaBody.vue.d.ts
vendored
Normal file
84
dist/types/components/eli/EliTabela/EliTabelaBody.vue.d.ts
vendored
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
import { PropType } from "vue";
|
||||||
|
import type { EliColuna } from "./types-eli-tabela";
|
||||||
|
declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||||
|
colunas: {
|
||||||
|
type: PropType<Array<EliColuna<any>>>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
linhas: {
|
||||||
|
type: PropType<Array<unknown>>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
temAcoes: {
|
||||||
|
type: BooleanConstructor;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
menuAberto: {
|
||||||
|
type: PropType<number | null>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
possuiAcoes: {
|
||||||
|
type: PropType<(i: number) => boolean>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>, {
|
||||||
|
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;
|
||||||
|
emitToggleMenu: (indice: number, evento: MouseEvent) => void;
|
||||||
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
||||||
|
toggleMenu(payload: {
|
||||||
|
indice: number;
|
||||||
|
evento: MouseEvent;
|
||||||
|
}): boolean;
|
||||||
|
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||||
|
colunas: {
|
||||||
|
type: PropType<Array<EliColuna<any>>>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
linhas: {
|
||||||
|
type: PropType<Array<unknown>>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
temAcoes: {
|
||||||
|
type: BooleanConstructor;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
menuAberto: {
|
||||||
|
type: PropType<number | null>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
possuiAcoes: {
|
||||||
|
type: PropType<(i: number) => boolean>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>> & Readonly<{
|
||||||
|
onToggleMenu?: ((payload: {
|
||||||
|
indice: number;
|
||||||
|
evento: MouseEvent;
|
||||||
|
}) => any) | undefined;
|
||||||
|
}>, {}, {}, {
|
||||||
|
EliTabelaCelula: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||||
|
celula: {
|
||||||
|
type: PropType<import("./types-eli-tabela").ComponenteCelula>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>, {
|
||||||
|
Componente: import("vue").ComputedRef<import("vue").Component>;
|
||||||
|
dadosParaComponente: import("vue").ComputedRef<{
|
||||||
|
texto: string;
|
||||||
|
acao?: () => void;
|
||||||
|
} | {
|
||||||
|
numero: number;
|
||||||
|
acao?: () => void;
|
||||||
|
}>;
|
||||||
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||||
|
celula: {
|
||||||
|
type: PropType<import("./types-eli-tabela").ComponenteCelula>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||||
|
MoreVertical: import("vue").FunctionalComponent<import("lucide-vue-next").LucideProps, {}, any, {}>;
|
||||||
|
}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||||
|
declare const _default: typeof __VLS_export;
|
||||||
|
export default _default;
|
||||||
72
dist/types/components/eli/EliTabela/EliTabelaCabecalho.vue.d.ts
vendored
Normal file
72
dist/types/components/eli/EliTabela/EliTabelaCabecalho.vue.d.ts
vendored
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
import { PropType } from "vue";
|
||||||
|
declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||||
|
exibirBusca: {
|
||||||
|
type: BooleanConstructor;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
valorBusca: {
|
||||||
|
type: StringConstructor;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
acoesCabecalho: {
|
||||||
|
type: PropType<Array<{
|
||||||
|
icone?: any;
|
||||||
|
cor?: string;
|
||||||
|
rotulo: string;
|
||||||
|
acao: () => void;
|
||||||
|
}>>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>, {
|
||||||
|
temAcoesCabecalho: import("vue").ComputedRef<boolean>;
|
||||||
|
emitBuscar: (texto: string) => void;
|
||||||
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
||||||
|
buscar(valor: string): boolean;
|
||||||
|
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||||
|
exibirBusca: {
|
||||||
|
type: BooleanConstructor;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
valorBusca: {
|
||||||
|
type: StringConstructor;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
acoesCabecalho: {
|
||||||
|
type: PropType<Array<{
|
||||||
|
icone?: any;
|
||||||
|
cor?: string;
|
||||||
|
rotulo: string;
|
||||||
|
acao: () => void;
|
||||||
|
}>>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>> & Readonly<{
|
||||||
|
onBuscar?: ((valor: string) => any) | undefined;
|
||||||
|
}>, {}, {}, {
|
||||||
|
EliTabelaCaixaDeBusca: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||||
|
modelo: {
|
||||||
|
type: StringConstructor;
|
||||||
|
required: false;
|
||||||
|
default: string;
|
||||||
|
};
|
||||||
|
}>, {
|
||||||
|
texto: import("vue").Ref<string, string>;
|
||||||
|
emitirBusca: () => void;
|
||||||
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
||||||
|
buscar(valor: string): boolean;
|
||||||
|
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||||
|
modelo: {
|
||||||
|
type: StringConstructor;
|
||||||
|
required: false;
|
||||||
|
default: string;
|
||||||
|
};
|
||||||
|
}>> & Readonly<{
|
||||||
|
onBuscar?: ((valor: string) => any) | undefined;
|
||||||
|
}>, {
|
||||||
|
modelo: string;
|
||||||
|
}, {}, {
|
||||||
|
Search: import("vue").FunctionalComponent<import("lucide-vue-next").LucideProps, {}, any, {}>;
|
||||||
|
}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||||
|
}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||||
|
declare const _default: typeof __VLS_export;
|
||||||
|
export default _default;
|
||||||
36
dist/types/components/eli/EliTabela/EliTabelaDebug.vue.d.ts
vendored
Normal file
36
dist/types/components/eli/EliTabela/EliTabelaDebug.vue.d.ts
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { PropType } from "vue";
|
||||||
|
declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||||
|
isDev: {
|
||||||
|
type: BooleanConstructor;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
menuAberto: {
|
||||||
|
type: PropType<number | null>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
menuPopupPos: {
|
||||||
|
type: PropType<{
|
||||||
|
top: number;
|
||||||
|
left: number;
|
||||||
|
}>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||||
|
isDev: {
|
||||||
|
type: BooleanConstructor;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
menuAberto: {
|
||||||
|
type: PropType<number | null>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
menuPopupPos: {
|
||||||
|
type: PropType<{
|
||||||
|
top: number;
|
||||||
|
left: number;
|
||||||
|
}>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||||
|
declare const _default: typeof __VLS_export;
|
||||||
|
export default _default;
|
||||||
34
dist/types/components/eli/EliTabela/EliTabelaEstados.vue.d.ts
vendored
Normal file
34
dist/types/components/eli/EliTabela/EliTabelaEstados.vue.d.ts
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { PropType } from "vue";
|
||||||
|
declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||||
|
carregando: {
|
||||||
|
type: BooleanConstructor;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
erro: {
|
||||||
|
type: PropType<string | null>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
mensagemVazio: {
|
||||||
|
type: PropType<string | undefined>;
|
||||||
|
required: false;
|
||||||
|
default: undefined;
|
||||||
|
};
|
||||||
|
}>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||||
|
carregando: {
|
||||||
|
type: BooleanConstructor;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
erro: {
|
||||||
|
type: PropType<string | null>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
mensagemVazio: {
|
||||||
|
type: PropType<string | undefined>;
|
||||||
|
required: false;
|
||||||
|
default: undefined;
|
||||||
|
};
|
||||||
|
}>> & Readonly<{}>, {
|
||||||
|
mensagemVazio: string | undefined;
|
||||||
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||||
|
declare const _default: typeof __VLS_export;
|
||||||
|
export default _default;
|
||||||
52
dist/types/components/eli/EliTabela/EliTabelaHead.vue.d.ts
vendored
Normal file
52
dist/types/components/eli/EliTabela/EliTabelaHead.vue.d.ts
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
import { PropType } from "vue";
|
||||||
|
import type { EliColuna } from "./types-eli-tabela";
|
||||||
|
declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||||
|
colunas: {
|
||||||
|
type: PropType<Array<EliColuna<any>>>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
temAcoes: {
|
||||||
|
type: BooleanConstructor;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
colunaOrdenacao: {
|
||||||
|
type: PropType<string | null>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
direcaoOrdenacao: {
|
||||||
|
type: PropType<"asc" | "desc">;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>, {
|
||||||
|
ArrowUp: import("vue").FunctionalComponent<import("lucide-vue-next").LucideProps, {}, any, {}>;
|
||||||
|
ArrowDown: import("vue").FunctionalComponent<import("lucide-vue-next").LucideProps, {}, any, {}>;
|
||||||
|
isOrdenavel: (coluna: any) => boolean;
|
||||||
|
obterClasseAlinhamento: (alinhamento?: string) => "eli-tabela__celula--direita" | "eli-tabela__celula--centro" | "eli-tabela__celula--esquerda";
|
||||||
|
emitAlternarOrdenacao: (chave: string) => void;
|
||||||
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
||||||
|
alternarOrdenacao(chave: string): boolean;
|
||||||
|
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||||
|
colunas: {
|
||||||
|
type: PropType<Array<EliColuna<any>>>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
temAcoes: {
|
||||||
|
type: BooleanConstructor;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
colunaOrdenacao: {
|
||||||
|
type: PropType<string | null>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
direcaoOrdenacao: {
|
||||||
|
type: PropType<"asc" | "desc">;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>> & Readonly<{
|
||||||
|
onAlternarOrdenacao?: ((chave: string) => any) | undefined;
|
||||||
|
}>, {}, {}, {
|
||||||
|
ArrowUp: import("vue").FunctionalComponent<import("lucide-vue-next").LucideProps, {}, any, {}>;
|
||||||
|
ArrowDown: import("vue").FunctionalComponent<import("lucide-vue-next").LucideProps, {}, any, {}>;
|
||||||
|
}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||||
|
declare const _default: typeof __VLS_export;
|
||||||
|
export default _default;
|
||||||
66
dist/types/components/eli/EliTabela/EliTabelaMenuAcoes.vue.d.ts
vendored
Normal file
66
dist/types/components/eli/EliTabela/EliTabelaMenuAcoes.vue.d.ts
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
import { PropType } from "vue";
|
||||||
|
import type { EliTabelaAcao } from "./types-eli-tabela";
|
||||||
|
type ItemAcao<T> = {
|
||||||
|
acao: EliTabelaAcao<T>;
|
||||||
|
indice: number;
|
||||||
|
visivel: boolean;
|
||||||
|
};
|
||||||
|
declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||||
|
menuAberto: {
|
||||||
|
type: PropType<number | null>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
posicao: {
|
||||||
|
type: PropType<{
|
||||||
|
top: number;
|
||||||
|
left: number;
|
||||||
|
}>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
acoes: {
|
||||||
|
type: PropType<Array<ItemAcao<any>>>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
linha: {
|
||||||
|
type: PropType<unknown | null>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>, {
|
||||||
|
menuEl: import("vue").Ref<HTMLElement | null, HTMLElement | null>;
|
||||||
|
possuiAcoes: import("vue").ComputedRef<boolean>;
|
||||||
|
emitExecutar: (item: {
|
||||||
|
acao: EliTabelaAcao<any>;
|
||||||
|
}) => void;
|
||||||
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
||||||
|
executar(payload: {
|
||||||
|
acao: EliTabelaAcao<any>;
|
||||||
|
linha: unknown;
|
||||||
|
}): boolean;
|
||||||
|
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||||
|
menuAberto: {
|
||||||
|
type: PropType<number | null>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
posicao: {
|
||||||
|
type: PropType<{
|
||||||
|
top: number;
|
||||||
|
left: number;
|
||||||
|
}>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
acoes: {
|
||||||
|
type: PropType<Array<ItemAcao<any>>>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
linha: {
|
||||||
|
type: PropType<unknown | null>;
|
||||||
|
required: true;
|
||||||
|
};
|
||||||
|
}>> & Readonly<{
|
||||||
|
onExecutar?: ((payload: {
|
||||||
|
acao: EliTabelaAcao<any>;
|
||||||
|
linha: unknown;
|
||||||
|
}) => any) | undefined;
|
||||||
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||||
|
declare const _default: typeof __VLS_export;
|
||||||
|
export default _default;
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "eli-vue",
|
"name": "eli-vue",
|
||||||
"version": "0.1.39",
|
"version": "0.1.40",
|
||||||
"private": false,
|
"private": false,
|
||||||
"main": "./dist/eli-vue.umd.js",
|
"main": "./dist/eli-vue.umd.js",
|
||||||
"module": "./dist/eli-vue.es.js",
|
"module": "./dist/eli-vue.es.js",
|
||||||
|
|
|
||||||
300
src/components/eli/EliTabela/EliTabela.css
Normal file
300
src/components/eli/EliTabela/EliTabela.css
Normal file
|
|
@ -0,0 +1,300 @@
|
||||||
|
.eli-tabela {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: separate;
|
||||||
|
border-spacing: 0;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__tbody {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__tbody .eli-tabela__tr--zebra .eli-tabela__td {
|
||||||
|
background: rgba(15, 23, 42, 0.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__th,
|
||||||
|
.eli-tabela__td {
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__th {
|
||||||
|
text-align: left;
|
||||||
|
font-weight: 600;
|
||||||
|
background: rgba(0, 0, 0, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__th--ordenavel {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__th--ordenavel .eli-tabela__th-botao {
|
||||||
|
padding: 10px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__th-botao {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
width: 100%;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
font: inherit;
|
||||||
|
color: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: left;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__th-botao:hover,
|
||||||
|
.eli-tabela__th-botao:focus-visible {
|
||||||
|
color: rgba(15, 23, 42, 0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__th-botao:focus-visible {
|
||||||
|
outline: 2px solid rgba(37, 99, 235, 0.45);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__th-botao--ativo {
|
||||||
|
color: rgba(37, 99, 235, 0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__th-texto {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__th-icone {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__th-icone--oculto {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__tr:last-child .eli-tabela__td {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__td--clicavel {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__td--clicavel:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__celula--esquerda {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__celula--centro {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__celula--direita {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__celula-conteudo {
|
||||||
|
display: inline-block;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela--erro {
|
||||||
|
border: 1px solid rgba(220, 53, 69, 0.35);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela--carregando {
|
||||||
|
border: 1px dashed rgba(0, 0, 0, 0.25);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 12px;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__erro-titulo {
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__erro-mensagem {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela--vazio {
|
||||||
|
border: 1px dashed rgba(0, 0, 0, 0.25);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 12px;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__th--acoes {
|
||||||
|
text-align: right;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__td--acoes {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__acoes-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__acoes-container--aberto {
|
||||||
|
z-index: 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__cabecalho {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
padding: 6px 14px;
|
||||||
|
border-radius: 9999px;
|
||||||
|
border: none;
|
||||||
|
background: rgba(37, 99, 235, 0.12);
|
||||||
|
color: rgba(37, 99, 235, 0.95);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
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: rgba(37, 99, 235, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.eli-tabela__acoes-cabecalho-botao:focus-visible {
|
||||||
|
outline: 2px solid rgba(37, 99, 235, 0.35);
|
||||||
|
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 rgba(37, 99, 235, 0.45);
|
||||||
|
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: #ffffff;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
@ -1,222 +1,50 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="eli-tabela">
|
<div class="eli-tabela">
|
||||||
<div
|
<EliTabelaDebug :isDev="isDev" :menuAberto="menuAberto" :menuPopupPos="menuPopupPos" />
|
||||||
v-if="isDev"
|
|
||||||
style="position: fixed; left: 8px; bottom: 8px; z-index: 999999; background: rgba(185,28,28,0.9); color: #fff; padding: 6px 10px; border-radius: 8px; font-size: 12px; max-width: 500px;"
|
|
||||||
>
|
|
||||||
<div><b>EliTabela debug</b></div>
|
|
||||||
<div>menuAberto: {{ menuAberto }}</div>
|
|
||||||
<div>menuPos: top={{ menuPopupPos.top }}, left={{ menuPopupPos.left }}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="carregando" class="eli-tabela eli-tabela--carregando" aria-busy="true">
|
<EliTabelaEstados
|
||||||
Carregando...
|
v-if="carregando || Boolean(erro) || !linhas.length"
|
||||||
</div>
|
:carregando="carregando"
|
||||||
|
:erro="erro"
|
||||||
<div v-else-if="erro" class="eli-tabela eli-tabela--erro" role="alert">
|
:mensagemVazio="tabela.mensagemVazio"
|
||||||
<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>
|
<template v-else>
|
||||||
<div v-if="exibirBusca || temAcoesCabecalho" class="eli-tabela__cabecalho">
|
<EliTabelaCabecalho
|
||||||
<EliTabelaCaixaDeBusca
|
v-if="exibirBusca || temAcoesCabecalho"
|
||||||
v-if="exibirBusca"
|
:exibirBusca="exibirBusca"
|
||||||
:modelo="valorBusca"
|
:valorBusca="valorBusca"
|
||||||
|
:acoesCabecalho="acoesCabecalho"
|
||||||
@buscar="atualizarBusca"
|
@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">
|
<table class="eli-tabela__table">
|
||||||
<thead class="eli-tabela__thead">
|
<EliTabelaHead
|
||||||
<tr class="eli-tabela__tr eli-tabela__tr--header">
|
:colunas="tabela.colunas"
|
||||||
<th
|
:temAcoes="temAcoes"
|
||||||
v-for="(coluna, idx) in tabela.colunas"
|
:colunaOrdenacao="colunaOrdenacao"
|
||||||
:key="`th-${idx}`"
|
:direcaoOrdenacao="direcaoOrdenacao"
|
||||||
class="eli-tabela__th"
|
@alternarOrdenacao="alternarOrdenacao"
|
||||||
: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
|
<EliTabelaBody
|
||||||
class="eli-tabela__th-icone eli-tabela__th-icone--oculto"
|
:colunas="tabela.colunas"
|
||||||
:size="16"
|
:linhas="linhas"
|
||||||
:stroke-width="2"
|
:temAcoes="temAcoes"
|
||||||
aria-hidden="true"
|
:menuAberto="menuAberto"
|
||||||
|
:possuiAcoes="possuiAcoes"
|
||||||
|
@toggleMenu="({ indice, evento }) => toggleMenu(indice, evento)"
|
||||||
/>
|
/>
|
||||||
</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, $event)"
|
|
||||||
>
|
|
||||||
<MoreVertical class="eli-tabela__acoes-toggle-icone" :size="18" :stroke-width="2" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<!-- Menu teleportado para o body para evitar limitações de z-index/stacking do <table> -->
|
<EliTabelaMenuAcoes
|
||||||
<Teleport to="body">
|
|
||||||
<ul
|
|
||||||
v-if="menuAberto !== null && possuiAcoes(menuAberto)"
|
|
||||||
:id="`eli-tabela-acoes-menu-${menuAberto}`"
|
|
||||||
ref="menuPopup"
|
ref="menuPopup"
|
||||||
class="eli-tabela__acoes-menu"
|
:menuAberto="menuAberto"
|
||||||
role="menu"
|
:posicao="menuPopupPos"
|
||||||
:aria-labelledby="`eli-tabela-acoes-toggle-${menuAberto}`"
|
:acoes="menuAberto === null ? [] : acoesDisponiveisPorLinha(menuAberto)"
|
||||||
:style="{
|
:linha="menuAberto === null ? null : linhas[menuAberto]"
|
||||||
position: 'fixed',
|
@executar="({ acao, linha }) => { menuAberto = null; acao.acao(linha as never); }"
|
||||||
top: `${menuPopupPos.top}px`,
|
|
||||||
left: `${menuPopupPos.left}px`,
|
|
||||||
zIndex: 999999,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<li
|
|
||||||
v-for="item in acoesDisponiveisPorLinha(menuAberto)"
|
|
||||||
:key="`acao-${menuAberto}-${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="
|
|
||||||
() => {
|
|
||||||
const indice = menuAberto;
|
|
||||||
const linha = indice === null ? null : linhas[indice];
|
|
||||||
menuAberto = null;
|
|
||||||
if (linha !== null && linha !== undefined) {
|
|
||||||
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>
|
|
||||||
</Teleport>
|
|
||||||
|
|
||||||
<EliTabelaPaginacao
|
<EliTabelaPaginacao
|
||||||
v-if="totalPaginas > 1 && quantidade > 0"
|
v-if="totalPaginas > 1 && quantidade > 0"
|
||||||
|
|
@ -230,6 +58,12 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
/**
|
||||||
|
* EliTabela
|
||||||
|
* Componente de tabela consultável com busca, paginação, ordenação e ações por linha.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Dependências do Vue (Composition API) */
|
||||||
import {
|
import {
|
||||||
computed,
|
computed,
|
||||||
defineComponent,
|
defineComponent,
|
||||||
|
|
@ -239,53 +73,70 @@ import {
|
||||||
ref,
|
ref,
|
||||||
watch,
|
watch,
|
||||||
} from "vue";
|
} from "vue";
|
||||||
import { ArrowDown, ArrowUp, MoreVertical } from "lucide-vue-next";
|
/** Enum de códigos de resposta utilizado na consulta */
|
||||||
import { codigosResposta } from "p-respostas";
|
import { codigosResposta } from "p-respostas";
|
||||||
import EliTabelaCaixaDeBusca from "./EliTabelaCaixaDeBusca.vue";
|
/** Componentes auxiliares */
|
||||||
|
import EliTabelaCabecalho from "./EliTabelaCabecalho.vue";
|
||||||
|
import EliTabelaEstados from "./EliTabelaEstados.vue";
|
||||||
|
import EliTabelaDebug from "./EliTabelaDebug.vue";
|
||||||
|
import EliTabelaHead from "./EliTabelaHead.vue";
|
||||||
|
import EliTabelaBody from "./EliTabelaBody.vue";
|
||||||
|
import EliTabelaMenuAcoes from "./EliTabelaMenuAcoes.vue";
|
||||||
import EliTabelaPaginacao from "./EliTabelaPaginacao.vue";
|
import EliTabelaPaginacao from "./EliTabelaPaginacao.vue";
|
||||||
import EliTabelaCelula from "./celulas/EliTabelaCelula.vue";
|
/** Tipos da configuração/contrato da tabela */
|
||||||
import type { EliTabelaConsulta } from "./types-eli-tabela";
|
import type { EliTabelaConsulta } from "./types-eli-tabela";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "EliTabela",
|
name: "EliTabela",
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
components: {
|
components: {
|
||||||
EliTabelaCaixaDeBusca,
|
EliTabelaCabecalho,
|
||||||
|
EliTabelaEstados,
|
||||||
|
EliTabelaDebug,
|
||||||
|
EliTabelaHead,
|
||||||
|
EliTabelaBody,
|
||||||
|
EliTabelaMenuAcoes,
|
||||||
EliTabelaPaginacao,
|
EliTabelaPaginacao,
|
||||||
EliTabelaCelula,
|
|
||||||
ArrowUp,
|
|
||||||
ArrowDown,
|
|
||||||
MoreVertical,
|
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
/** Configuração principal da tabela (colunas, consulta e ações) */
|
||||||
tabela: {
|
tabela: {
|
||||||
type: Object as PropType<EliTabelaConsulta<any>>,
|
type: Object as PropType<EliTabelaConsulta<any>>,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
|
/** Flag para habilitar elementos de debug */
|
||||||
const isDev = import.meta.env.DEV;
|
const isDev = import.meta.env.DEV;
|
||||||
|
/** Estados de carregamento/erro e dados retornados */
|
||||||
const carregando = ref(false);
|
const carregando = ref(false);
|
||||||
const erro = ref<string | null>(null);
|
const erro = ref<string | null>(null);
|
||||||
const linhas = ref<unknown[]>([]);
|
const linhas = ref<unknown[]>([]);
|
||||||
const quantidade = ref<number>(0);
|
const quantidade = ref<number>(0);
|
||||||
|
|
||||||
|
/** Controle de visibilidade das ações por linha */
|
||||||
const acoesVisiveis = ref<boolean[][]>([]);
|
const acoesVisiveis = ref<boolean[][]>([]);
|
||||||
|
/** Estado do menu de ações (aberto, elemento e posição) */
|
||||||
const menuAberto = ref<number | null>(null);
|
const menuAberto = ref<number | null>(null);
|
||||||
const menuPopup = ref<HTMLElement | null>(null);
|
// O componente EliTabelaMenuAcoes expõe `menuEl` (ref do elemento <ul>)
|
||||||
|
const menuPopup = ref<{ menuEl: { value: HTMLElement | null } } | null>(null);
|
||||||
const menuPopupPos = ref({ top: 0, left: 0 });
|
const menuPopupPos = ref({ top: 0, left: 0 });
|
||||||
|
|
||||||
|
/** Filtros e ordenação */
|
||||||
const valorBusca = ref<string>("");
|
const valorBusca = ref<string>("");
|
||||||
const paginaAtual = ref(1);
|
const paginaAtual = ref(1);
|
||||||
const colunaOrdenacao = ref<string | null>(null);
|
const colunaOrdenacao = ref<string | null>(null);
|
||||||
const direcaoOrdenacao = ref<"asc" | "desc">("asc");
|
const direcaoOrdenacao = ref<"asc" | "desc">("asc");
|
||||||
|
|
||||||
|
/** Alias reativo da prop tabela */
|
||||||
const tabela = computed(() => props.tabela);
|
const tabela = computed(() => props.tabela);
|
||||||
|
|
||||||
|
/** Exibição da busca e ações do cabeçalho */
|
||||||
const exibirBusca = computed(() => Boolean(props.tabela.mostrarCaixaDeBusca));
|
const exibirBusca = computed(() => Boolean(props.tabela.mostrarCaixaDeBusca));
|
||||||
const acoesCabecalho = computed(() => props.tabela.acoesTabela ?? []);
|
const acoesCabecalho = computed(() => props.tabela.acoesTabela ?? []);
|
||||||
const temAcoesCabecalho = computed(() => acoesCabecalho.value.length > 0);
|
const temAcoesCabecalho = computed(() => acoesCabecalho.value.length > 0);
|
||||||
|
|
||||||
|
/** Registros por consulta (normaliza para inteiro positivo) */
|
||||||
const registrosPorConsulta = computed(() => {
|
const registrosPorConsulta = computed(() => {
|
||||||
const valor = props.tabela.registros_por_consulta;
|
const valor = props.tabela.registros_por_consulta;
|
||||||
if (typeof valor === "number" && valor > 0) {
|
if (typeof valor === "number" && valor > 0) {
|
||||||
|
|
@ -294,6 +145,7 @@ export default defineComponent({
|
||||||
return 10;
|
return 10;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** Total de páginas calculado com base na quantidade */
|
||||||
const totalPaginas = computed(() => {
|
const totalPaginas = computed(() => {
|
||||||
const limite = registrosPorConsulta.value;
|
const limite = registrosPorConsulta.value;
|
||||||
if (!limite || limite <= 0) return 1;
|
if (!limite || limite <= 0) return 1;
|
||||||
|
|
@ -304,18 +156,21 @@ export default defineComponent({
|
||||||
return Math.max(1, Math.ceil(total / limite));
|
return Math.max(1, Math.ceil(total / limite));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** Indica se existem ações por linha */
|
||||||
const temAcoes = computed(() => (props.tabela.acoesLinha ?? []).length > 0);
|
const temAcoes = computed(() => (props.tabela.acoesLinha ?? []).length > 0);
|
||||||
|
|
||||||
|
/** Sequencial para evitar race conditions entre consultas */
|
||||||
let carregamentoSequencial = 0;
|
let carregamentoSequencial = 0;
|
||||||
|
|
||||||
|
/** Calcula a posição do menu de ações na viewport */
|
||||||
function atualizarPosicaoMenu(anchor: HTMLElement) {
|
function atualizarPosicaoMenu(anchor: HTMLElement) {
|
||||||
const rect = anchor.getBoundingClientRect();
|
const rect = anchor.getBoundingClientRect();
|
||||||
const gap = 8;
|
const gap = 8;
|
||||||
|
|
||||||
// Alinha no canto inferior direito do botão.
|
// Alinha no canto inferior direito do botão.
|
||||||
// Se estourar a tela para baixo, abre para cima.
|
// Se estourar a tela para baixo, abre para cima.
|
||||||
const alturaMenu = menuPopup.value?.offsetHeight ?? 0;
|
const alturaMenu = menuPopup.value?.menuEl?.value?.offsetHeight ?? 0;
|
||||||
const larguraMenu = menuPopup.value?.offsetWidth ?? 180;
|
const larguraMenu = menuPopup.value?.menuEl?.value?.offsetWidth ?? 180;
|
||||||
|
|
||||||
let top = rect.bottom + gap;
|
let top = rect.bottom + gap;
|
||||||
const left = rect.right - larguraMenu;
|
const left = rect.right - larguraMenu;
|
||||||
|
|
@ -330,11 +185,12 @@ export default defineComponent({
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Fecha o menu quando ocorre clique fora */
|
||||||
function handleClickFora(evento: MouseEvent) {
|
function handleClickFora(evento: MouseEvent) {
|
||||||
if (menuAberto.value === null) return;
|
if (menuAberto.value === null) return;
|
||||||
|
|
||||||
const alvo = evento.target as Node;
|
const alvo = evento.target as Node;
|
||||||
if (menuPopup.value && menuPopup.value.contains(alvo)) return;
|
if (menuPopup.value?.menuEl?.value && menuPopup.value.menuEl.value.contains(alvo)) return;
|
||||||
|
|
||||||
if (import.meta.env.DEV) {
|
if (import.meta.env.DEV) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
|
|
@ -344,10 +200,7 @@ export default defineComponent({
|
||||||
menuAberto.value = null;
|
menuAberto.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isOrdenavel(coluna: any) {
|
/** Alterna ordenação e recarrega os dados */
|
||||||
return coluna?.coluna_ordem !== undefined && coluna?.coluna_ordem !== null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function alternarOrdenacao(chave?: string) {
|
function alternarOrdenacao(chave?: string) {
|
||||||
if (!chave) return;
|
if (!chave) return;
|
||||||
|
|
||||||
|
|
@ -366,6 +219,7 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Atualiza a busca e reinicia paginação, se necessário */
|
||||||
function atualizarBusca(texto: string) {
|
function atualizarBusca(texto: string) {
|
||||||
if (valorBusca.value === texto) return;
|
if (valorBusca.value === texto) return;
|
||||||
|
|
||||||
|
|
@ -377,6 +231,7 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Navega para a página solicitada com limites */
|
||||||
function irParaPagina(pagina: number) {
|
function irParaPagina(pagina: number) {
|
||||||
const alvo = Math.min(Math.max(1, pagina), totalPaginas.value);
|
const alvo = Math.min(Math.max(1, pagina), totalPaginas.value);
|
||||||
if (alvo !== paginaAtual.value) {
|
if (alvo !== paginaAtual.value) {
|
||||||
|
|
@ -384,34 +239,9 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function obterClasseAlinhamento(alinhamento?: string) {
|
/**
|
||||||
if (alinhamento === "direita") return "eli-tabela__celula--direita";
|
* Lista ações visíveis por linha, respeitando regras sync/async de `exibir`.
|
||||||
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;
|
|
||||||
return typeof largura === "number" ? `${largura}px` : String(largura);
|
|
||||||
}
|
|
||||||
|
|
||||||
function obterTooltipCelula(celula: unknown) {
|
|
||||||
if (!Array.isArray(celula)) return undefined;
|
|
||||||
|
|
||||||
const tipo = celula[0];
|
|
||||||
const dados = celula[1] as any;
|
|
||||||
|
|
||||||
if (tipo === "textoSimples") {
|
|
||||||
return typeof dados?.texto === "string" ? dados.texto : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tipo === "numero") {
|
|
||||||
return typeof dados?.numero === "number" ? String(dados.numero) : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function acoesDisponiveisPorLinha(i: number) {
|
function acoesDisponiveisPorLinha(i: number) {
|
||||||
const acoesLinha = props.tabela.acoesLinha ?? [];
|
const acoesLinha = props.tabela.acoesLinha ?? [];
|
||||||
const visibilidade = acoesVisiveis.value[i] ?? [];
|
const visibilidade = acoesVisiveis.value[i] ?? [];
|
||||||
|
|
@ -434,10 +264,12 @@ export default defineComponent({
|
||||||
.filter((item) => item.visivel);
|
.filter((item) => item.visivel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Informa se a linha possui ações disponíveis */
|
||||||
function possuiAcoes(i: number) {
|
function possuiAcoes(i: number) {
|
||||||
return acoesDisponiveisPorLinha(i).length > 0;
|
return acoesDisponiveisPorLinha(i).length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Abre/fecha o menu de ações da linha e posiciona o popup */
|
||||||
function toggleMenu(i: number, evento?: MouseEvent) {
|
function toggleMenu(i: number, evento?: MouseEvent) {
|
||||||
if (!possuiAcoes(i)) return;
|
if (!possuiAcoes(i)) return;
|
||||||
|
|
||||||
|
|
@ -473,6 +305,10 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executa consulta, aplica filtros e resolve visibilidade de ações.
|
||||||
|
* Usa o contador sequencial para ignorar respostas antigas.
|
||||||
|
*/
|
||||||
async function carregar() {
|
async function carregar() {
|
||||||
const idCarregamento = ++carregamentoSequencial;
|
const idCarregamento = ++carregamentoSequencial;
|
||||||
carregando.value = true;
|
carregando.value = true;
|
||||||
|
|
@ -578,15 +414,18 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Ciclo de vida: registra listener global e carrega dados iniciais */
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
document.addEventListener("click", handleClickFora);
|
document.addEventListener("click", handleClickFora);
|
||||||
void carregar();
|
void carregar();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** Ciclo de vida: remove listener global */
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
document.removeEventListener("click", handleClickFora);
|
document.removeEventListener("click", handleClickFora);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** Watch: ao desabilitar busca, limpa termo e recarrega */
|
||||||
watch(
|
watch(
|
||||||
() => props.tabela.mostrarCaixaDeBusca,
|
() => props.tabela.mostrarCaixaDeBusca,
|
||||||
(mostrar) => {
|
(mostrar) => {
|
||||||
|
|
@ -601,10 +440,12 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/** Watch: mudança de página dispara nova consulta */
|
||||||
watch(paginaAtual, (nova, antiga) => {
|
watch(paginaAtual, (nova, antiga) => {
|
||||||
if (nova !== antiga) void carregar();
|
if (nova !== antiga) void carregar();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** Watch: troca de configuração reseta estados e recarrega */
|
||||||
watch(
|
watch(
|
||||||
() => props.tabela,
|
() => props.tabela,
|
||||||
() => {
|
() => {
|
||||||
|
|
@ -620,6 +461,7 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/** Watch: alteração do limite de registros reinicia paginação */
|
||||||
watch(
|
watch(
|
||||||
() => props.tabela.registros_por_consulta,
|
() => props.tabela.registros_por_consulta,
|
||||||
() => {
|
() => {
|
||||||
|
|
@ -631,10 +473,12 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/** Watch: mudança nas linhas fecha o menu aberto */
|
||||||
watch(linhas, () => {
|
watch(linhas, () => {
|
||||||
menuAberto.value = null;
|
menuAberto.value = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** Exposição para o template (state, computed, helpers e actions) */
|
||||||
return {
|
return {
|
||||||
// state
|
// state
|
||||||
isDev,
|
isDev,
|
||||||
|
|
@ -656,17 +500,6 @@ export default defineComponent({
|
||||||
temAcoesCabecalho,
|
temAcoesCabecalho,
|
||||||
temAcoes,
|
temAcoes,
|
||||||
|
|
||||||
// icons
|
|
||||||
ArrowUp,
|
|
||||||
ArrowDown,
|
|
||||||
MoreVertical,
|
|
||||||
|
|
||||||
// helpers
|
|
||||||
isOrdenavel,
|
|
||||||
obterClasseAlinhamento,
|
|
||||||
obterMaxWidth,
|
|
||||||
obterTooltipCelula,
|
|
||||||
|
|
||||||
// actions
|
// actions
|
||||||
alternarOrdenacao,
|
alternarOrdenacao,
|
||||||
atualizarBusca,
|
atualizarBusca,
|
||||||
|
|
@ -683,315 +516,4 @@ export default defineComponent({
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style src="./EliTabela.css"></style>
|
||||||
.eli-tabela {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__table {
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: separate;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__tbody .eli-tabela__tr--zebra .eli-tabela__td {
|
|
||||||
background: rgba(15, 23, 42, 0.02);
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__th,
|
|
||||||
.eli-tabela__td {
|
|
||||||
padding: 10px 12px;
|
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__th {
|
|
||||||
text-align: left;
|
|
||||||
font-weight: 600;
|
|
||||||
background: rgba(0, 0, 0, 0.03);
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__th--ordenavel {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__th--ordenavel .eli-tabela__th-botao {
|
|
||||||
padding: 10px 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__th-botao {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-start;
|
|
||||||
gap: 8px;
|
|
||||||
width: 100%;
|
|
||||||
background: transparent;
|
|
||||||
border: none;
|
|
||||||
font: inherit;
|
|
||||||
color: inherit;
|
|
||||||
cursor: pointer;
|
|
||||||
text-align: left;
|
|
||||||
transition: color 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__th-botao:hover,
|
|
||||||
.eli-tabela__th-botao:focus-visible {
|
|
||||||
color: rgba(15, 23, 42, 0.85);
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__th-botao:focus-visible {
|
|
||||||
outline: 2px solid rgba(37, 99, 235, 0.45);
|
|
||||||
outline-offset: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__th-botao--ativo {
|
|
||||||
color: rgba(37, 99, 235, 0.95);
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__th-texto {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__th-icone {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__th-icone--oculto {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__tr:last-child .eli-tabela__td {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__td--clicavel {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__td--clicavel:hover {
|
|
||||||
background: rgba(0, 0, 0, 0.03);
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__celula--esquerda {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__celula--centro {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__celula--direita {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__celula-conteudo {
|
|
||||||
display: inline-block;
|
|
||||||
max-width: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela--erro {
|
|
||||||
border: 1px solid rgba(220, 53, 69, 0.35);
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela--carregando {
|
|
||||||
border: 1px dashed rgba(0, 0, 0, 0.25);
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 12px;
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__erro-titulo {
|
|
||||||
font-weight: 700;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__erro-mensagem {
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela--vazio {
|
|
||||||
border: 1px dashed rgba(0, 0, 0, 0.25);
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 12px;
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__th--acoes {
|
|
||||||
text-align: right;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__td--acoes {
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__acoes-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__acoes-container--aberto {
|
|
||||||
z-index: 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__cabecalho {
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-end;
|
|
||||||
gap: 12px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
padding: 6px 14px;
|
|
||||||
border-radius: 9999px;
|
|
||||||
border: none;
|
|
||||||
background: rgba(37, 99, 235, 0.12);
|
|
||||||
color: rgba(37, 99, 235, 0.95);
|
|
||||||
font-size: 0.875rem;
|
|
||||||
font-weight: 500;
|
|
||||||
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: rgba(37, 99, 235, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__acoes-cabecalho-botao:focus-visible {
|
|
||||||
outline: 2px solid rgba(37, 99, 235, 0.35);
|
|
||||||
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 rgba(37, 99, 235, 0.45);
|
|
||||||
outline-offset: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__acoes-toggle:disabled {
|
|
||||||
cursor: default;
|
|
||||||
color: rgba(148, 163, 184, 0.8);
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eli-tabela__acoes-menu {
|
|
||||||
/*
|
|
||||||
O menu é renderizado via Teleport e posicionado com `position: fixed`
|
|
||||||
via style inline, para não sofrer com as limitações de stacking/z-index
|
|
||||||
de elementos dentro de <table>.
|
|
||||||
*/
|
|
||||||
min-width: 180px;
|
|
||||||
padding: 6px 0;
|
|
||||||
margin: 0;
|
|
||||||
list-style: none;
|
|
||||||
background: #ffffff;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
||||||
136
src/components/eli/EliTabela/EliTabelaBody.vue
Normal file
136
src/components/eli/EliTabela/EliTabelaBody.vue
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
<template>
|
||||||
|
<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 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="emitToggleMenu(i, $event)"
|
||||||
|
>
|
||||||
|
<MoreVertical class="eli-tabela__acoes-toggle-icone" :size="18" :stroke-width="2" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, PropType } from "vue";
|
||||||
|
import { MoreVertical } from "lucide-vue-next";
|
||||||
|
import EliTabelaCelula from "./celulas/EliTabelaCelula.vue";
|
||||||
|
import type { EliColuna } from "./types-eli-tabela";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "EliTabelaBody",
|
||||||
|
components: {
|
||||||
|
EliTabelaCelula,
|
||||||
|
MoreVertical,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
colunas: {
|
||||||
|
type: Array as PropType<Array<EliColuna<any>>>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
linhas: {
|
||||||
|
type: Array as PropType<Array<unknown>>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
temAcoes: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
menuAberto: {
|
||||||
|
type: Number as PropType<number | null>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
possuiAcoes: {
|
||||||
|
type: Function as PropType<(i: number) => boolean>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: {
|
||||||
|
toggleMenu(payload: { indice: number; evento: MouseEvent }) {
|
||||||
|
return payload && typeof payload.indice === "number";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(_props, { emit }) {
|
||||||
|
function obterClasseAlinhamento(alinhamento?: string) {
|
||||||
|
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;
|
||||||
|
return typeof largura === "number" ? `${largura}px` : String(largura);
|
||||||
|
}
|
||||||
|
|
||||||
|
function obterTooltipCelula(celula: unknown) {
|
||||||
|
if (!Array.isArray(celula)) return undefined;
|
||||||
|
|
||||||
|
const tipo = celula[0];
|
||||||
|
const dados = celula[1] as any;
|
||||||
|
|
||||||
|
if (tipo === "textoSimples") {
|
||||||
|
return typeof dados?.texto === "string" ? dados.texto : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tipo === "numero") {
|
||||||
|
return typeof dados?.numero === "number" ? String(dados.numero) : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function emitToggleMenu(indice: number, evento: MouseEvent) {
|
||||||
|
emit("toggleMenu", { indice, evento });
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
obterClasseAlinhamento,
|
||||||
|
obterMaxWidth,
|
||||||
|
obterTooltipCelula,
|
||||||
|
emitToggleMenu,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
72
src/components/eli/EliTabela/EliTabelaCabecalho.vue
Normal file
72
src/components/eli/EliTabela/EliTabelaCabecalho.vue
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
<template>
|
||||||
|
<div class="eli-tabela__cabecalho">
|
||||||
|
<!-- Caixa de busca: emite @buscar com o termo digitado -->
|
||||||
|
<EliTabelaCaixaDeBusca v-if="exibirBusca" :modelo="valorBusca" @buscar="emitBuscar" />
|
||||||
|
|
||||||
|
<!-- Ações do cabeçalho: ações globais da tabela -->
|
||||||
|
<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>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, PropType } from "vue";
|
||||||
|
import EliTabelaCaixaDeBusca from "./EliTabelaCaixaDeBusca.vue";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "EliTabelaCabecalho",
|
||||||
|
components: { EliTabelaCaixaDeBusca },
|
||||||
|
props: {
|
||||||
|
exibirBusca: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
valorBusca: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
acoesCabecalho: {
|
||||||
|
type: Array as PropType<
|
||||||
|
Array<{
|
||||||
|
icone?: any;
|
||||||
|
cor?: string;
|
||||||
|
rotulo: string;
|
||||||
|
acao: () => void;
|
||||||
|
}>
|
||||||
|
>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: {
|
||||||
|
buscar(valor: string) {
|
||||||
|
return typeof valor === "string";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const temAcoesCabecalho = computed(() => props.acoesCabecalho.length > 0);
|
||||||
|
|
||||||
|
function emitBuscar(texto: string) {
|
||||||
|
emit("buscar", texto);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { temAcoesCabecalho, emitBuscar };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
33
src/components/eli/EliTabela/EliTabelaDebug.vue
Normal file
33
src/components/eli/EliTabela/EliTabelaDebug.vue
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
<template>
|
||||||
|
<!-- Debug visual (somente DEV): expõe estados internos úteis para testes e diagnóstico -->
|
||||||
|
<div
|
||||||
|
v-if="isDev"
|
||||||
|
style="position: fixed; left: 8px; bottom: 8px; z-index: 999999; background: rgba(185,28,28,0.9); color: #fff; padding: 6px 10px; border-radius: 8px; font-size: 12px; max-width: 500px;"
|
||||||
|
>
|
||||||
|
<div><b>EliTabela debug</b></div>
|
||||||
|
<div>menuAberto: {{ menuAberto }}</div>
|
||||||
|
<div>menuPos: top={{ menuPopupPos.top }}, left={{ menuPopupPos.left }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, PropType } from "vue";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "EliTabelaDebug",
|
||||||
|
props: {
|
||||||
|
isDev: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
menuAberto: {
|
||||||
|
type: Number as PropType<number | null>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
menuPopupPos: {
|
||||||
|
type: Object as PropType<{ top: number; left: number }>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
40
src/components/eli/EliTabela/EliTabelaEstados.vue
Normal file
40
src/components/eli/EliTabela/EliTabelaEstados.vue
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
<template>
|
||||||
|
<!-- Estado de carregamento: consulta em andamento -->
|
||||||
|
<div v-if="carregando" class="eli-tabela eli-tabela--carregando" aria-busy="true">
|
||||||
|
Carregando...
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Estado de erro: mostra mensagem retornada pela consulta -->
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<!-- Estado vazio: consulta sem registros -->
|
||||||
|
<div v-else class="eli-tabela eli-tabela--vazio">
|
||||||
|
{{ mensagemVazio ?? "Nenhum registro encontrado." }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, PropType } from "vue";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "EliTabelaEstados",
|
||||||
|
props: {
|
||||||
|
carregando: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
erro: {
|
||||||
|
type: String as PropType<string | null>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
mensagemVazio: {
|
||||||
|
type: String as PropType<string | undefined>,
|
||||||
|
required: false,
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
107
src/components/eli/EliTabela/EliTabelaHead.vue
Normal file
107
src/components/eli/EliTabela/EliTabelaHead.vue
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
<template>
|
||||||
|
<thead class="eli-tabela__thead">
|
||||||
|
<tr class="eli-tabela__tr eli-tabela__tr--header">
|
||||||
|
<th
|
||||||
|
v-for="(coluna, idx) in 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="emitAlternarOrdenacao(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>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, PropType } from "vue";
|
||||||
|
import { ArrowDown, ArrowUp } from "lucide-vue-next";
|
||||||
|
import type { EliColuna } from "./types-eli-tabela";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "EliTabelaHead",
|
||||||
|
components: { ArrowUp, ArrowDown },
|
||||||
|
props: {
|
||||||
|
colunas: {
|
||||||
|
type: Array as PropType<Array<EliColuna<any>>>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
temAcoes: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
colunaOrdenacao: {
|
||||||
|
type: String as PropType<string | null>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
direcaoOrdenacao: {
|
||||||
|
type: String as PropType<"asc" | "desc">,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: {
|
||||||
|
alternarOrdenacao(chave: string) {
|
||||||
|
return typeof chave === "string" && chave.length > 0;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(_props, { emit }) {
|
||||||
|
function isOrdenavel(coluna: any) {
|
||||||
|
return coluna?.coluna_ordem !== undefined && coluna?.coluna_ordem !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function obterClasseAlinhamento(alinhamento?: string) {
|
||||||
|
if (alinhamento === "direita") return "eli-tabela__celula--direita";
|
||||||
|
if (alinhamento === "centro") return "eli-tabela__celula--centro";
|
||||||
|
return "eli-tabela__celula--esquerda";
|
||||||
|
}
|
||||||
|
|
||||||
|
function emitAlternarOrdenacao(chave: string) {
|
||||||
|
emit("alternarOrdenacao", chave);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
ArrowUp,
|
||||||
|
ArrowDown,
|
||||||
|
isOrdenavel,
|
||||||
|
obterClasseAlinhamento,
|
||||||
|
emitAlternarOrdenacao,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
95
src/components/eli/EliTabela/EliTabelaMenuAcoes.vue
Normal file
95
src/components/eli/EliTabela/EliTabelaMenuAcoes.vue
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
<template>
|
||||||
|
<Teleport to="body">
|
||||||
|
<ul
|
||||||
|
v-if="menuAberto !== null && possuiAcoes"
|
||||||
|
:id="`eli-tabela-acoes-menu-${menuAberto}`"
|
||||||
|
ref="menuEl"
|
||||||
|
class="eli-tabela__acoes-menu"
|
||||||
|
role="menu"
|
||||||
|
:aria-labelledby="`eli-tabela-acoes-toggle-${menuAberto}`"
|
||||||
|
:style="{
|
||||||
|
position: 'fixed',
|
||||||
|
top: `${posicao.top}px`,
|
||||||
|
left: `${posicao.left}px`,
|
||||||
|
zIndex: 999999,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
v-for="item in acoes"
|
||||||
|
:key="`acao-${menuAberto}-${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="emitExecutar(item)"
|
||||||
|
>
|
||||||
|
<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>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, PropType, ref } from "vue";
|
||||||
|
import type { EliTabelaAcao } from "./types-eli-tabela";
|
||||||
|
|
||||||
|
type ItemAcao<T> = {
|
||||||
|
acao: EliTabelaAcao<T>;
|
||||||
|
indice: number;
|
||||||
|
visivel: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "EliTabelaMenuAcoes",
|
||||||
|
props: {
|
||||||
|
menuAberto: {
|
||||||
|
type: Number as PropType<number | null>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
posicao: {
|
||||||
|
type: Object as PropType<{ top: number; left: number }>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
acoes: {
|
||||||
|
type: Array as PropType<Array<ItemAcao<any>>>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
linha: {
|
||||||
|
// Aceita qualquer tipo de linha (objeto, string, etc.) sem validação runtime.
|
||||||
|
type: null as unknown as PropType<unknown | null>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: {
|
||||||
|
executar(payload: { acao: EliTabelaAcao<any>; linha: unknown }) {
|
||||||
|
return payload !== null && typeof payload === "object";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props, { emit, expose }) {
|
||||||
|
const menuEl = ref<HTMLElement | null>(null);
|
||||||
|
expose({ menuEl });
|
||||||
|
|
||||||
|
const possuiAcoes = computed(() => props.acoes.length > 0);
|
||||||
|
|
||||||
|
function emitExecutar(item: { acao: EliTabelaAcao<any> }) {
|
||||||
|
if (!props.linha) return;
|
||||||
|
emit("executar", { acao: item.acao, linha: props.linha });
|
||||||
|
}
|
||||||
|
|
||||||
|
return { menuEl, possuiAcoes, emitExecutar };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue