bkp
This commit is contained in:
parent
8bb5aea15e
commit
24c07da6f8
17 changed files with 1458 additions and 371 deletions
2
dist/eli-vue.css
vendored
2
dist/eli-vue.css
vendored
|
|
@ -1 +1 @@
|
|||
[data-v-de2fbf2f] .v-badge__badge,[data-v-de2fbf2f] .v-badge__content{border-radius:var(--eli-badge-radius)!important}.eli-input[data-v-756cb549]{width:100%}.checkbox-group[data-v-756cb549]{display:flex;gap:8px;flex-wrap:wrap}.cursor-pointer[data-v-756cb549]{cursor:pointer}.eli-cartao[data-v-6c492bd9]{border-radius:12px}.eli-cartao__titulo[data-v-6c492bd9]{display:flex;align-items:center;justify-content:space-between;gap:12px}.eli-cartao__titulo-texto[data-v-6c492bd9]{min-width:0}.eli-cartao__conteudo[data-v-6c492bd9]{padding-top:8px}.eli-cartao__acoes[data-v-6c492bd9]{padding-top:0}.eli-cartao--cancelado[data-v-6c492bd9]{opacity:.85}.eli-data-hora[data-v-71afabb6]{width:100%}
|
||||
[data-v-de2fbf2f] .v-badge__badge,[data-v-de2fbf2f] .v-badge__content{border-radius:var(--eli-badge-radius)!important}.eli-input[data-v-756cb549]{width:100%}.checkbox-group[data-v-756cb549]{display:flex;gap:8px;flex-wrap:wrap}.cursor-pointer[data-v-756cb549]{cursor:pointer}.eli-cartao[data-v-6c492bd9]{border-radius:12px}.eli-cartao__titulo[data-v-6c492bd9]{display:flex;align-items:center;justify-content:space-between;gap:12px}.eli-cartao__titulo-texto[data-v-6c492bd9]{min-width:0}.eli-cartao__conteudo[data-v-6c492bd9]{padding-top:8px}.eli-cartao__acoes[data-v-6c492bd9]{padding-top:0}.eli-cartao--cancelado[data-v-6c492bd9]{opacity:.85}.eli-data-hora[data-v-71afabb6],.eli-tabela[data-v-cc67b529]{width:100%}.eli-tabela__table[data-v-cc67b529]{width:100%;border-collapse:separate;border-spacing:0;border:1px solid rgba(0,0,0,.12);border-radius:12px;overflow:hidden}.eli-tabela__th[data-v-cc67b529],.eli-tabela__td[data-v-cc67b529]{padding:10px 12px;border-bottom:1px solid rgba(0,0,0,.08);vertical-align:top}.eli-tabela__th[data-v-cc67b529]{text-align:left;font-weight:600;background:#00000008}.eli-tabela__tr:last-child .eli-tabela__td[data-v-cc67b529]{border-bottom:none}.eli-tabela__td--clicavel[data-v-cc67b529]{cursor:pointer}.eli-tabela__td--clicavel[data-v-cc67b529]:hover{background:#00000008}.eli-tabela--erro[data-v-cc67b529]{border:1px solid rgba(220,53,69,.35);border-radius:12px;padding:12px}.eli-tabela--carregando[data-v-cc67b529]{border:1px dashed rgba(0,0,0,.25);border-radius:12px;padding:12px;opacity:.8}.eli-tabela__erro-titulo[data-v-cc67b529]{font-weight:700;margin-bottom:4px}.eli-tabela__erro-mensagem[data-v-cc67b529]{opacity:.9}.eli-tabela--vazio[data-v-cc67b529]{border:1px dashed rgba(0,0,0,.25);border-radius:12px;padding:12px;opacity:.8}
|
||||
|
|
|
|||
867
dist/eli-vue.es.js
vendored
867
dist/eli-vue.es.js
vendored
File diff suppressed because it is too large
Load diff
2
dist/eli-vue.umd.js
vendored
2
dist/eli-vue.umd.js
vendored
File diff suppressed because one or more lines are too long
17
dist/types/components/eli/EliTabela/EliTabela.vue.d.ts
vendored
Normal file
17
dist/types/components/eli/EliTabela/EliTabela.vue.d.ts
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { PropType } from "vue";
|
||||
import type { EliTabelaConsulta } from "./types";
|
||||
declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
||||
tabela: {
|
||||
type: PropType<EliTabelaConsulta<any>>;
|
||||
required: true;
|
||||
};
|
||||
}>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
||||
[key: string]: any;
|
||||
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
tabela: {
|
||||
type: PropType<EliTabelaConsulta<any>>;
|
||||
required: true;
|
||||
};
|
||||
}>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||
declare const _default: typeof __VLS_export;
|
||||
export default _default;
|
||||
6
dist/types/components/eli/EliTabela/celulas/EliCelulaTextoSimples.d.ts
vendored
Normal file
6
dist/types/components/eli/EliTabela/celulas/EliCelulaTextoSimples.d.ts
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import type { VNodeChild } from "vue";
|
||||
export type EliCelulaTextoSimples = {
|
||||
tipo: "texto-simples";
|
||||
texto: string;
|
||||
};
|
||||
export declare const renderEliCelulaTextoSimples: (celula: EliCelulaTextoSimples) => VNodeChild;
|
||||
3
dist/types/components/eli/EliTabela/index.d.ts
vendored
Normal file
3
dist/types/components/eli/EliTabela/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export { default as EliTabela } from "./EliTabela.vue";
|
||||
export * from "./types";
|
||||
export * from "./celulas/EliCelulaTextoSimples";
|
||||
24
dist/types/components/eli/EliTabela/types.d.ts
vendored
Normal file
24
dist/types/components/eli/EliTabela/types.d.ts
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import type { tipoResposta } from "p-respostas";
|
||||
import type { VNodeChild } from "vue";
|
||||
export type ComponenteCelula = VNodeChild;
|
||||
export type EliColuna<T> = {
|
||||
rotulo: string;
|
||||
celula: (linha: T) => ComponenteCelula;
|
||||
acao?: () => void;
|
||||
};
|
||||
export type EliConsultaPaginada<T> = {
|
||||
valores: T[];
|
||||
quantidade: number;
|
||||
};
|
||||
/**
|
||||
* Estrutura de dados para uma tabela alimentada por uma consulta.
|
||||
*
|
||||
* - `colunas`: definição de colunas e como renderizar cada célula
|
||||
* - `resposta`: função assíncrona que retorna uma resposta padronizada
|
||||
*/
|
||||
export type EliTabelaConsulta<T> = {
|
||||
colunas: EliColuna<T>[];
|
||||
resposta: () => Promise<tipoResposta<EliConsultaPaginada<T>>>;
|
||||
/** Mensagem exibida quando a consulta retorna ok porém sem dados. */
|
||||
mensagemVazio?: string;
|
||||
};
|
||||
2
dist/types/index.d.ts
vendored
2
dist/types/index.d.ts
vendored
|
|
@ -5,11 +5,13 @@ import { EliBadge } from "./componentes/indicador";
|
|||
import { EliInput } from "./componentes/campo";
|
||||
import { EliCartao } from "./componentes/cartao";
|
||||
import { EliDataHora } from "./componentes/data_hora";
|
||||
import { EliTabela } from "./components/eli/EliTabela";
|
||||
export { EliOlaMundo };
|
||||
export { EliBotao };
|
||||
export { EliBadge };
|
||||
export { EliInput };
|
||||
export { EliCartao };
|
||||
export { EliDataHora };
|
||||
export { EliTabela };
|
||||
declare const EliVue: Plugin;
|
||||
export default EliVue;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "eli-vue",
|
||||
"version": "0.1.19",
|
||||
"version": "0.1.22",
|
||||
"private": false,
|
||||
"main": "./dist/eli-vue.umd.js",
|
||||
"module": "./dist/eli-vue.es.js",
|
||||
|
|
@ -35,6 +35,8 @@
|
|||
"vuetify": "^3.11.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"dayjs": "^1.11.19"
|
||||
"dayjs": "^1.11.19",
|
||||
"lucide-vue-next": "^0.563.0",
|
||||
"p-respostas": "git+https://git2.idz.one/publico/_respostas.git"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
96
pnpm-lock.yaml
generated
96
pnpm-lock.yaml
generated
|
|
@ -11,6 +11,12 @@ importers:
|
|||
dayjs:
|
||||
specifier: ^1.11.19
|
||||
version: 1.11.19
|
||||
lucide-vue-next:
|
||||
specifier: ^0.563.0
|
||||
version: 0.563.0(vue@3.5.25(typescript@5.9.3))
|
||||
p-respostas:
|
||||
specifier: git+https://git2.idz.one/publico/_respostas.git
|
||||
version: git+https://git2.idz.one/publico/_respostas.git#8c24d790ace7255404745dcbdf12c5396e8b9843(cross-fetch@4.1.0)(dayjs@1.11.19)(uuid@11.1.0)
|
||||
devDependencies:
|
||||
'@mdi/font':
|
||||
specifier: ^7.4.47
|
||||
|
|
@ -489,6 +495,9 @@ packages:
|
|||
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
|
||||
engines: {node: '>= 14.16.0'}
|
||||
|
||||
cross-fetch@4.1.0:
|
||||
resolution: {integrity: sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==}
|
||||
|
||||
csstype@3.2.3:
|
||||
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
|
||||
|
||||
|
|
@ -554,6 +563,11 @@ packages:
|
|||
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
||||
engines: {node: '>=0.12.0'}
|
||||
|
||||
lucide-vue-next@0.563.0:
|
||||
resolution: {integrity: sha512-zsE/lCKtmaa7bGfhSpN84br1K9YoQ5pCN+2oKWjQQG3Lo6ufUUKBuHSjNFI6RvUevxaajNXb8XwFUKeTXG3sIA==}
|
||||
peerDependencies:
|
||||
vue: '>=3.0.1'
|
||||
|
||||
magic-string@0.30.21:
|
||||
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
|
||||
|
||||
|
|
@ -575,6 +589,29 @@ packages:
|
|||
node-addon-api@7.1.1:
|
||||
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
|
||||
|
||||
node-fetch@2.7.0:
|
||||
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
|
||||
engines: {node: 4.x || >=6.0.0}
|
||||
peerDependencies:
|
||||
encoding: ^0.1.0
|
||||
peerDependenciesMeta:
|
||||
encoding:
|
||||
optional: true
|
||||
|
||||
p-comuns@git+https://git2.idz.one/publico/_comuns.git#d783fa12940a5b1bcafa5038bd1c49c3f5f9b7fc:
|
||||
resolution: {commit: d783fa12940a5b1bcafa5038bd1c49c3f5f9b7fc, repo: https://git2.idz.one/publico/_comuns.git, type: git}
|
||||
version: 0.298.0
|
||||
peerDependencies:
|
||||
cross-fetch: 4.1.0
|
||||
dayjs: ^1.11.18
|
||||
uuid: ^11.1.0
|
||||
zod: 4.1.4
|
||||
|
||||
p-respostas@git+https://git2.idz.one/publico/_respostas.git#8c24d790ace7255404745dcbdf12c5396e8b9843:
|
||||
resolution: {commit: 8c24d790ace7255404745dcbdf12c5396e8b9843, repo: https://git2.idz.one/publico/_respostas.git, type: git}
|
||||
version: 0.56.0
|
||||
engines: {node: '>=20'}
|
||||
|
||||
path-browserify@1.0.1:
|
||||
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
|
||||
|
||||
|
|
@ -619,6 +656,9 @@ packages:
|
|||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||
engines: {node: '>=8.0'}
|
||||
|
||||
tr46@0.0.3:
|
||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||
|
||||
typescript@5.9.3:
|
||||
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
|
||||
engines: {node: '>=14.17'}
|
||||
|
|
@ -631,6 +671,10 @@ packages:
|
|||
resolution: {integrity: sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
uuid@11.1.0:
|
||||
resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==}
|
||||
hasBin: true
|
||||
|
||||
vite-plugin-vuetify@2.1.2:
|
||||
resolution: {integrity: sha512-I/wd6QS+DO6lHmuGoi1UTyvvBTQ2KDzQZ9oowJQEJ6OcjWfJnscYXx2ptm6S7fJSASuZT8jGRBL3LV4oS3LpaA==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
|
|
@ -711,6 +755,15 @@ packages:
|
|||
webpack-plugin-vuetify:
|
||||
optional: true
|
||||
|
||||
webidl-conversions@3.0.1:
|
||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||
|
||||
whatwg-url@5.0.0:
|
||||
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
||||
|
||||
zod@4.1.4:
|
||||
resolution: {integrity: sha512-2YqJuWkU6IIK9qcE4k1lLLhyZ6zFw7XVRdQGpV97jEIZwTrscUw+DY31Xczd8nwaoksyJUIxCojZXwckJovWxA==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@babel/helper-string-parser@7.27.1': {}
|
||||
|
|
@ -1042,6 +1095,12 @@ snapshots:
|
|||
dependencies:
|
||||
readdirp: 4.1.2
|
||||
|
||||
cross-fetch@4.1.0:
|
||||
dependencies:
|
||||
node-fetch: 2.7.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
|
||||
csstype@3.2.3: {}
|
||||
|
||||
dayjs@1.11.19: {}
|
||||
|
|
@ -1111,6 +1170,10 @@ snapshots:
|
|||
is-number@7.0.0:
|
||||
optional: true
|
||||
|
||||
lucide-vue-next@0.563.0(vue@3.5.25(typescript@5.9.3)):
|
||||
dependencies:
|
||||
vue: 3.5.25(typescript@5.9.3)
|
||||
|
||||
magic-string@0.30.21:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
|
@ -1130,6 +1193,26 @@ snapshots:
|
|||
node-addon-api@7.1.1:
|
||||
optional: true
|
||||
|
||||
node-fetch@2.7.0:
|
||||
dependencies:
|
||||
whatwg-url: 5.0.0
|
||||
|
||||
p-comuns@git+https://git2.idz.one/publico/_comuns.git#d783fa12940a5b1bcafa5038bd1c49c3f5f9b7fc(cross-fetch@4.1.0)(dayjs@1.11.19)(uuid@11.1.0)(zod@4.1.4):
|
||||
dependencies:
|
||||
cross-fetch: 4.1.0
|
||||
dayjs: 1.11.19
|
||||
uuid: 11.1.0
|
||||
zod: 4.1.4
|
||||
|
||||
p-respostas@git+https://git2.idz.one/publico/_respostas.git#8c24d790ace7255404745dcbdf12c5396e8b9843(cross-fetch@4.1.0)(dayjs@1.11.19)(uuid@11.1.0):
|
||||
dependencies:
|
||||
p-comuns: git+https://git2.idz.one/publico/_comuns.git#d783fa12940a5b1bcafa5038bd1c49c3f5f9b7fc(cross-fetch@4.1.0)(dayjs@1.11.19)(uuid@11.1.0)(zod@4.1.4)
|
||||
zod: 4.1.4
|
||||
transitivePeerDependencies:
|
||||
- cross-fetch
|
||||
- dayjs
|
||||
- uuid
|
||||
|
||||
path-browserify@1.0.1: {}
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
|
@ -1195,6 +1278,8 @@ snapshots:
|
|||
is-number: 7.0.0
|
||||
optional: true
|
||||
|
||||
tr46@0.0.3: {}
|
||||
|
||||
typescript@5.9.3: {}
|
||||
|
||||
undici-types@7.16.0:
|
||||
|
|
@ -1202,6 +1287,8 @@ snapshots:
|
|||
|
||||
upath@2.0.1: {}
|
||||
|
||||
uuid@11.1.0: {}
|
||||
|
||||
vite-plugin-vuetify@2.1.2(vite@6.4.1(@types/node@24.10.1)(sass@1.94.2))(vue@3.5.25(typescript@5.9.3))(vuetify@3.11.2):
|
||||
dependencies:
|
||||
'@vuetify/loader-shared': 2.1.1(vue@3.5.25(typescript@5.9.3))(vuetify@3.11.2)
|
||||
|
|
@ -1250,3 +1337,12 @@ snapshots:
|
|||
optionalDependencies:
|
||||
typescript: 5.9.3
|
||||
vite-plugin-vuetify: 2.1.2(vite@6.4.1(@types/node@24.10.1)(sass@1.94.2))(vue@3.5.25(typescript@5.9.3))(vuetify@3.11.2)
|
||||
|
||||
webidl-conversions@3.0.1: {}
|
||||
|
||||
whatwg-url@5.0.0:
|
||||
dependencies:
|
||||
tr46: 0.0.3
|
||||
webidl-conversions: 3.0.1
|
||||
|
||||
zod@4.1.4: {}
|
||||
|
|
|
|||
616
src/components/eli/EliTabela/EliTabela.vue
Normal file
616
src/components/eli/EliTabela/EliTabela.vue
Normal file
|
|
@ -0,0 +1,616 @@
|
|||
<template>
|
||||
<!-- Render é feito no script via função render para suportar VNodeChild em células -->
|
||||
<div />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, h, onBeforeUnmount, onMounted, PropType, ref, watch } from "vue";
|
||||
import type { ComponentPublicInstance } from "vue";
|
||||
import { MoreVertical } from "lucide-vue-next";
|
||||
import { codigosResposta } from "p-respostas";
|
||||
import type { EliTabelaConsulta } from "./types";
|
||||
|
||||
export default defineComponent({
|
||||
name: "EliTabela",
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
tabela: {
|
||||
// Observação: este componente é “generic-friendly”.
|
||||
// Usamos `any` aqui para permitir passar `EliTabelaConsulta<T>` de qualquer T
|
||||
// sem brigar com invariância do TS (por causa do callback `celula(linha: T)`).
|
||||
type: Object as PropType<EliTabelaConsulta<any>>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const carregando = ref(false);
|
||||
const erro = ref<string | null>(null);
|
||||
const linhas = ref<unknown[]>([]);
|
||||
const quantidade = ref<number>(0);
|
||||
const acoesVisiveis = ref<boolean[][]>([]);
|
||||
const menuAberto = ref<number | null>(null);
|
||||
const menuElementos = new Map<number, HTMLElement>();
|
||||
let carregamentoSequencial = 0;
|
||||
|
||||
function registrarMenuElemento(indice: number, elemento: HTMLElement | null) {
|
||||
if (elemento) {
|
||||
menuElementos.set(indice, elemento);
|
||||
} else {
|
||||
menuElementos.delete(indice);
|
||||
}
|
||||
}
|
||||
|
||||
function criarRegistradorMenu(indice: number) {
|
||||
return (elemento: Element | ComponentPublicInstance | null) => {
|
||||
if (elemento instanceof HTMLElement) {
|
||||
registrarMenuElemento(indice, elemento);
|
||||
} else {
|
||||
registrarMenuElemento(indice, null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function handleClickFora(evento: MouseEvent) {
|
||||
if (menuAberto.value === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const container = menuElementos.get(menuAberto.value);
|
||||
if (container && container.contains(evento.target as Node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
menuAberto.value = null;
|
||||
}
|
||||
|
||||
function normalizarFilhos(filhos: unknown) {
|
||||
// `VNodeChild` pode ser null/undefined/boolean.
|
||||
// Para a assinatura de `h()`, normalizamos para string vazia.
|
||||
if (filhos === null || filhos === undefined || filhos === false) {
|
||||
return "";
|
||||
}
|
||||
return filhos as never;
|
||||
}
|
||||
|
||||
function renderErro(mensagem: string) {
|
||||
return h(
|
||||
"div",
|
||||
{
|
||||
class: "eli-tabela eli-tabela--erro",
|
||||
role: "alert",
|
||||
},
|
||||
[
|
||||
h("div", { class: "eli-tabela__erro-titulo" }, "Erro"),
|
||||
h("div", { class: "eli-tabela__erro-mensagem" }, mensagem),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
function renderVazio(mensagem?: string) {
|
||||
return h(
|
||||
"div",
|
||||
{
|
||||
class: "eli-tabela eli-tabela--vazio",
|
||||
},
|
||||
mensagem ?? "Nenhum registro encontrado."
|
||||
);
|
||||
}
|
||||
|
||||
function renderCarregando() {
|
||||
return h(
|
||||
"div",
|
||||
{
|
||||
class: "eli-tabela eli-tabela--carregando",
|
||||
"aria-busy": "true",
|
||||
},
|
||||
"Carregando..."
|
||||
);
|
||||
}
|
||||
|
||||
async function carregar() {
|
||||
const idCarregamento = ++carregamentoSequencial;
|
||||
carregando.value = true;
|
||||
erro.value = null;
|
||||
acoesVisiveis.value = [];
|
||||
menuAberto.value = null;
|
||||
menuElementos.clear();
|
||||
|
||||
try {
|
||||
const tabelaConfig = props.tabela;
|
||||
const res = await tabelaConfig.resposta();
|
||||
|
||||
if (idCarregamento !== carregamentoSequencial) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.cod !== codigosResposta.sucesso) {
|
||||
linhas.value = [];
|
||||
quantidade.value = 0;
|
||||
erro.value = res.mensagem;
|
||||
return;
|
||||
}
|
||||
|
||||
const valores = res.valor?.valores ?? [];
|
||||
const total = res.valor?.quantidade ?? valores.length;
|
||||
|
||||
linhas.value = valores;
|
||||
quantidade.value = total;
|
||||
|
||||
const acoes = tabelaConfig.acoes ?? [];
|
||||
|
||||
if (!acoes.length) {
|
||||
acoesVisiveis.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
const preResultado = valores.map(() =>
|
||||
acoes.map((acao) => {
|
||||
if (typeof acao.exibir === "boolean") {
|
||||
return acao.exibir;
|
||||
}
|
||||
return acao.exibir ? false : true;
|
||||
})
|
||||
);
|
||||
|
||||
acoesVisiveis.value = preResultado;
|
||||
|
||||
const visibilidade = await Promise.all(
|
||||
valores.map(async (linha) =>
|
||||
Promise.all(
|
||||
acoes.map(async (acao) => {
|
||||
if (acao.exibir === undefined) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeof acao.exibir === "boolean") {
|
||||
return acao.exibir;
|
||||
}
|
||||
|
||||
try {
|
||||
const resultado = acao.exibir(linha as never);
|
||||
return Boolean(await Promise.resolve(resultado));
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if (idCarregamento === carregamentoSequencial) {
|
||||
acoesVisiveis.value = visibilidade;
|
||||
}
|
||||
} catch (e) {
|
||||
if (idCarregamento !== carregamentoSequencial) {
|
||||
return;
|
||||
}
|
||||
|
||||
linhas.value = [];
|
||||
quantidade.value = 0;
|
||||
erro.value = e instanceof Error ? e.message : "Erro ao carregar dados.";
|
||||
} finally {
|
||||
if (idCarregamento === carregamentoSequencial) {
|
||||
carregando.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener("click", handleClickFora);
|
||||
void carregar();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener("click", handleClickFora);
|
||||
menuElementos.clear();
|
||||
});
|
||||
watch(
|
||||
() => props.tabela,
|
||||
() => {
|
||||
menuAberto.value = null;
|
||||
menuElementos.clear();
|
||||
// Caso a definição de tabela/consulta mude
|
||||
void carregar();
|
||||
}
|
||||
);
|
||||
|
||||
watch(linhas, () => {
|
||||
menuAberto.value = null;
|
||||
menuElementos.clear();
|
||||
});
|
||||
|
||||
return () => {
|
||||
const tabela = props.tabela;
|
||||
|
||||
if (carregando.value) {
|
||||
return renderCarregando();
|
||||
}
|
||||
|
||||
if (erro.value) {
|
||||
return renderErro(erro.value);
|
||||
}
|
||||
|
||||
const colunas = tabela.colunas;
|
||||
const acoes = tabela.acoes ?? [];
|
||||
const temAcoes = acoes.length > 0;
|
||||
|
||||
if (!linhas.value.length) {
|
||||
return renderVazio(tabela.mensagemVazio);
|
||||
}
|
||||
|
||||
const cabecalho = colunas.map((coluna) =>
|
||||
h("th", { class: "eli-tabela__th", scope: "col" }, coluna.rotulo)
|
||||
);
|
||||
|
||||
if (temAcoes) {
|
||||
cabecalho.push(
|
||||
h(
|
||||
"th",
|
||||
{ class: "eli-tabela__th eli-tabela__th--acoes", scope: "col" },
|
||||
"Ações"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return h(
|
||||
"div",
|
||||
{
|
||||
class: "eli-tabela",
|
||||
},
|
||||
[
|
||||
h("table", { class: "eli-tabela__table" }, [
|
||||
h(
|
||||
"thead",
|
||||
{ class: "eli-tabela__thead" },
|
||||
h(
|
||||
"tr",
|
||||
{ class: "eli-tabela__tr eli-tabela__tr--header" },
|
||||
cabecalho
|
||||
)
|
||||
),
|
||||
h(
|
||||
"tbody",
|
||||
{ class: "eli-tabela__tbody" },
|
||||
linhas.value.map((linha, i) => {
|
||||
const celulas = colunas.map((coluna, j) =>
|
||||
h(
|
||||
"td",
|
||||
{
|
||||
class: [
|
||||
"eli-tabela__td",
|
||||
coluna.acao ? "eli-tabela__td--clicavel" : undefined,
|
||||
],
|
||||
key: `${i}-${j}`,
|
||||
onClick: coluna.acao ? () => coluna.acao?.() : undefined,
|
||||
},
|
||||
normalizarFilhos(coluna.celula(linha as never))
|
||||
)
|
||||
);
|
||||
|
||||
if (temAcoes) {
|
||||
const visibilidade = acoesVisiveis.value[i] ?? [];
|
||||
const acoesDisponiveis = acoes
|
||||
.map((acao, indice) => ({
|
||||
acao,
|
||||
indice,
|
||||
visivel:
|
||||
visibilidade[indice] ??
|
||||
(typeof acao.exibir === "boolean"
|
||||
? acao.exibir
|
||||
: acao.exibir
|
||||
? false
|
||||
: true),
|
||||
}))
|
||||
.filter((item) => item.visivel);
|
||||
|
||||
const possuiAcoes = acoesDisponiveis.length > 0;
|
||||
|
||||
if (!possuiAcoes && menuAberto.value === i) {
|
||||
menuAberto.value = null;
|
||||
}
|
||||
|
||||
const estaAberto = menuAberto.value === i;
|
||||
const toggleId = `eli-tabela-acoes-toggle-${i}`;
|
||||
const menuId = `eli-tabela-acoes-menu-${i}`;
|
||||
|
||||
const botaoToggle = h(
|
||||
"button",
|
||||
{
|
||||
id: toggleId,
|
||||
class: "eli-tabela__acoes-toggle",
|
||||
type: "button",
|
||||
disabled: !possuiAcoes,
|
||||
onClick: (evento: MouseEvent) => {
|
||||
evento.stopPropagation();
|
||||
if (!possuiAcoes) {
|
||||
return;
|
||||
}
|
||||
menuAberto.value = estaAberto ? null : i;
|
||||
},
|
||||
"aria-haspopup": "menu",
|
||||
"aria-expanded": estaAberto ? "true" : "false",
|
||||
"aria-controls": possuiAcoes ? menuId : undefined,
|
||||
"aria-label": possuiAcoes
|
||||
? "Ações da linha"
|
||||
: "Nenhuma ação disponível",
|
||||
title: possuiAcoes ? "Ações" : "Nenhuma ação disponível",
|
||||
},
|
||||
[
|
||||
h(MoreVertical, {
|
||||
class: "eli-tabela__acoes-toggle-icone",
|
||||
size: 18,
|
||||
strokeWidth: 2,
|
||||
}),
|
||||
]
|
||||
);
|
||||
|
||||
const menu =
|
||||
estaAberto && possuiAcoes
|
||||
? h(
|
||||
"ul",
|
||||
{
|
||||
id: menuId,
|
||||
class: "eli-tabela__acoes-menu",
|
||||
role: "menu",
|
||||
"aria-labelledby": toggleId,
|
||||
},
|
||||
acoesDisponiveis.map(({ acao, indice }) =>
|
||||
h(
|
||||
"li",
|
||||
{
|
||||
key: `acao-${indice}`,
|
||||
class: "eli-tabela__acoes-item",
|
||||
role: "none",
|
||||
},
|
||||
h(
|
||||
"button",
|
||||
{
|
||||
type: "button",
|
||||
class: "eli-tabela__acoes-item-botao",
|
||||
style: {
|
||||
color: acao.cor,
|
||||
},
|
||||
onClick: (evento: MouseEvent) => {
|
||||
evento.stopPropagation();
|
||||
menuAberto.value = null;
|
||||
acao.acao(linha as never);
|
||||
},
|
||||
role: "menuitem",
|
||||
"aria-label": acao.rotulo,
|
||||
title: acao.rotulo,
|
||||
},
|
||||
[
|
||||
h(acao.icone, {
|
||||
class: "eli-tabela__acoes-item-icone",
|
||||
size: 16,
|
||||
strokeWidth: 2,
|
||||
}),
|
||||
h(
|
||||
"span",
|
||||
{ class: "eli-tabela__acoes-item-texto" },
|
||||
acao.rotulo
|
||||
),
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
: null;
|
||||
|
||||
const classesContainer = [
|
||||
"eli-tabela__acoes-container",
|
||||
];
|
||||
|
||||
if (estaAberto) {
|
||||
classesContainer.push("eli-tabela__acoes-container--aberto");
|
||||
}
|
||||
|
||||
celulas.push(
|
||||
h(
|
||||
"td",
|
||||
{
|
||||
class: ["eli-tabela__td", "eli-tabela__td--acoes"],
|
||||
key: `${i}-acoes`,
|
||||
},
|
||||
h(
|
||||
"div",
|
||||
{
|
||||
class: classesContainer,
|
||||
ref: criarRegistradorMenu(i),
|
||||
},
|
||||
[botaoToggle, menu]
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return h(
|
||||
"tr",
|
||||
{ class: "eli-tabela__tr", key: i },
|
||||
celulas
|
||||
);
|
||||
})
|
||||
),
|
||||
]),
|
||||
]
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.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__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__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--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;
|
||||
}
|
||||
|
||||
.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__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 {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
margin-top: 8px;
|
||||
right: 0;
|
||||
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>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import type { VNodeChild } from "vue";
|
||||
|
||||
export type EliCelulaTextoSimples = { tipo: "texto-simples"; texto: string };
|
||||
|
||||
export const renderEliCelulaTextoSimples = (
|
||||
celula: EliCelulaTextoSimples
|
||||
): VNodeChild => celula.texto;
|
||||
4
src/components/eli/EliTabela/index.ts
Normal file
4
src/components/eli/EliTabela/index.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export { default as EliTabela } from "./EliTabela.vue";
|
||||
|
||||
export * from "./types";
|
||||
export * from "./celulas/EliCelulaTextoSimples";
|
||||
38
src/components/eli/EliTabela/types.ts
Normal file
38
src/components/eli/EliTabela/types.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import type { tipoResposta } from "p-respostas";
|
||||
import type { LucideIcon } from "lucide-vue-next";
|
||||
import type { VNodeChild } from "vue";
|
||||
|
||||
export type ComponenteCelula = VNodeChild;
|
||||
|
||||
export type EliColuna<T> = {
|
||||
rotulo: string;
|
||||
celula: (linha: T) => ComponenteCelula;
|
||||
acao?: () => void;
|
||||
};
|
||||
|
||||
export type EliConsultaPaginada<T> = {
|
||||
valores: T[];
|
||||
quantidade: number;
|
||||
};
|
||||
|
||||
export type EliTabelaAcao<T> = {
|
||||
icone: LucideIcon;
|
||||
cor: string;
|
||||
rotulo: string;
|
||||
acao: (linha: T) => void;
|
||||
exibir?: boolean | ((linha: T) => Promise<boolean> | boolean);
|
||||
};
|
||||
|
||||
/**
|
||||
* Estrutura de dados para uma tabela alimentada por uma consulta.
|
||||
*
|
||||
* - `colunas`: definição de colunas e como renderizar cada célula
|
||||
* - `resposta`: função assíncrona que retorna uma resposta padronizada
|
||||
*/
|
||||
export type EliTabelaConsulta<T> = {
|
||||
colunas: EliColuna<T>[];
|
||||
resposta: () => Promise<tipoResposta<EliConsultaPaginada<T>>>;
|
||||
/** Mensagem exibida quando a consulta retorna ok porém sem dados. */
|
||||
mensagemVazio?: string;
|
||||
acoes?: EliTabelaAcao<T>[];
|
||||
};
|
||||
|
|
@ -5,6 +5,7 @@ import { EliBadge } from "./componentes/indicador";
|
|||
import { EliInput } from "./componentes/campo";
|
||||
import { EliCartao } from "./componentes/cartao";
|
||||
import { EliDataHora } from "./componentes/data_hora";
|
||||
import { EliTabela } from "./components/eli/EliTabela";
|
||||
|
||||
export { EliOlaMundo };
|
||||
export { EliBotao };
|
||||
|
|
@ -12,6 +13,7 @@ export { EliBadge };
|
|||
export { EliInput };
|
||||
export { EliCartao };
|
||||
export { EliDataHora };
|
||||
export { EliTabela };
|
||||
|
||||
const EliVue: Plugin = {
|
||||
install(app: App) {
|
||||
|
|
@ -21,6 +23,7 @@ const EliVue: Plugin = {
|
|||
app.component("EliInput", EliInput);
|
||||
app.component("EliCartao", EliCartao);
|
||||
app.component("EliDataHora", EliDataHora);
|
||||
app.component("EliTabela", EliTabela);
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
<v-tab value="cartao">Cartão</v-tab>
|
||||
<v-tab value="campo">Campo</v-tab>
|
||||
<v-tab value="data_hora">Data e hora</v-tab>
|
||||
<v-tab value="tabela">Tabela</v-tab>
|
||||
<v-tab value="ola_mundo">Demo</v-tab>
|
||||
</v-tabs>
|
||||
|
||||
|
|
@ -18,6 +19,7 @@
|
|||
<CartaoPlayground v-else-if="aba === 'cartao'" />
|
||||
<CampoPlayground v-else-if="aba === 'campo'" />
|
||||
<DataHoraPlayground v-else-if="aba === 'data_hora'" />
|
||||
<TabelaPlayground v-else-if="aba === 'tabela'" />
|
||||
<OlaMundoPlayground v-else />
|
||||
</v-container>
|
||||
</template>
|
||||
|
|
@ -29,6 +31,7 @@ import IndicadorPlayground from "./indicador.playground.vue";
|
|||
import CartaoPlayground from "./cartao.playground.vue";
|
||||
import CampoPlayground from "./campo.playground.vue";
|
||||
import DataHoraPlayground from "./data_hora.playground.vue";
|
||||
import TabelaPlayground from "./tabela.playground.vue";
|
||||
import OlaMundoPlayground from "./ola_mundo.playground.vue";
|
||||
|
||||
export default defineComponent({
|
||||
|
|
@ -39,6 +42,7 @@ export default defineComponent({
|
|||
CartaoPlayground,
|
||||
CampoPlayground,
|
||||
DataHoraPlayground,
|
||||
TabelaPlayground,
|
||||
OlaMundoPlayground,
|
||||
},
|
||||
data() {
|
||||
|
|
@ -49,6 +53,7 @@ export default defineComponent({
|
|||
| "cartao"
|
||||
| "campo"
|
||||
| "data_hora"
|
||||
| "tabela"
|
||||
| "ola_mundo",
|
||||
};
|
||||
}
|
||||
|
|
|
|||
131
src/playground/tabela.playground.vue
Normal file
131
src/playground/tabela.playground.vue
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
<template>
|
||||
<section class="stack">
|
||||
<h2>EliTabela</h2>
|
||||
|
||||
<EliTabela :tabela="tabelaOk" />
|
||||
|
||||
<EliTabela :tabela="tabelaVazia" />
|
||||
|
||||
<EliTabela :tabela="tabelaErro" />
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import { codigosResposta } from "p-respostas";
|
||||
import { Eye, Trash2 } from "lucide-vue-next";
|
||||
import { EliTabela } from "@/components/eli/EliTabela";
|
||||
import type { EliTabelaConsulta } from "@/components/eli/EliTabela";
|
||||
|
||||
type Linha = {
|
||||
nome: string;
|
||||
email: string;
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: "TabelaPlayground",
|
||||
components: { EliTabela },
|
||||
setup() {
|
||||
const acoesTabela: EliTabelaConsulta<Linha>["acoes"] = [
|
||||
{
|
||||
icone: Eye,
|
||||
cor: "#2563eb",
|
||||
rotulo: "Detalhes",
|
||||
acao: (linha) => {
|
||||
console.log("Visualizar detalhes de", linha.nome);
|
||||
},
|
||||
},
|
||||
{
|
||||
icone: Trash2,
|
||||
cor: "#dc2626",
|
||||
rotulo: "Remover",
|
||||
acao: (linha) => {
|
||||
console.log("Remover registro de", linha.nome);
|
||||
},
|
||||
exibir: async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 250));
|
||||
return true;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const tabelaOk: EliTabelaConsulta<Linha> = {
|
||||
colunas: [
|
||||
{
|
||||
rotulo: "Nome",
|
||||
celula: (l) => l.nome,
|
||||
},
|
||||
{
|
||||
rotulo: "E-mail",
|
||||
celula: (l) => l.email,
|
||||
acao: () => {
|
||||
// Exemplo de ação: poderia abrir detalhes
|
||||
console.log("clicou na coluna e-mail");
|
||||
},
|
||||
},
|
||||
],
|
||||
acoes: acoesTabela,
|
||||
resposta: async () => {
|
||||
return {
|
||||
cod: codigosResposta.sucesso,
|
||||
eCerto: true,
|
||||
eErro: false,
|
||||
mensagem: undefined,
|
||||
valor: {
|
||||
quantidade: 2,
|
||||
valores: [
|
||||
{ nome: "Ana", email: "ana@eli.com" },
|
||||
{ nome: "Bruno", email: "bruno@eli.com" },
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const tabelaVazia: EliTabelaConsulta<Linha> = {
|
||||
colunas: tabelaOk.colunas,
|
||||
resposta: async () => {
|
||||
return {
|
||||
cod: codigosResposta.sucesso,
|
||||
eCerto: true,
|
||||
eErro: false,
|
||||
mensagem: undefined,
|
||||
valor: {
|
||||
quantidade: 0,
|
||||
valores: [],
|
||||
},
|
||||
};
|
||||
},
|
||||
mensagemVazio: "Nada para mostrar aqui.",
|
||||
acoes: acoesTabela,
|
||||
};
|
||||
|
||||
const tabelaErro: EliTabelaConsulta<Linha> = {
|
||||
colunas: tabelaOk.colunas,
|
||||
acoes: acoesTabela,
|
||||
resposta: async () => {
|
||||
return {
|
||||
cod: codigosResposta.erroConhecido,
|
||||
eCerto: false,
|
||||
eErro: true,
|
||||
mensagem: "Falha ao buscar dados",
|
||||
valor: undefined,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
return { tabelaOk, tabelaVazia, tabelaErro };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.stack {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.stack :deep(.eli-tabela) {
|
||||
max-width: 900px;
|
||||
}
|
||||
</style>
|
||||
Loading…
Add table
Add a link
Reference in a new issue