bkp
This commit is contained in:
parent
50a971ccaf
commit
64535c51a3
12 changed files with 1016 additions and 774 deletions
2
.agent
2
.agent
|
|
@ -23,7 +23,7 @@ Construir um Design System de componentes em **Vue 3** para reutilização em m
|
|||
- **defineComponent** (obrigatório)
|
||||
- Sem TSX (padrão: `<template>` + `<script lang="ts">`)
|
||||
- Estilo: preferir CSS scoped por componente (se aplicável)
|
||||
- Ícones: se usar, definir um padrão único do repositório (não inventar por componente)
|
||||
- Ícones: caso seja necessário o uso de ícones, usar **lucide** (biblioteca `lucide-vue-next`) como padrão do repositório.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
2
dist/eli-vue.css
vendored
2
dist/eli-vue.css
vendored
File diff suppressed because one or more lines are too long
1531
dist/eli-vue.es.js
vendored
1531
dist/eli-vue.es.js
vendored
File diff suppressed because it is too large
Load diff
29
dist/eli-vue.umd.js
vendored
29
dist/eli-vue.umd.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -19,6 +19,8 @@ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractP
|
|||
onBuscar?: ((valor: string) => any) | undefined;
|
||||
}>, {
|
||||
modelo: string;
|
||||
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||
}, {}, {
|
||||
Search: import("vue").FunctionalComponent<import("lucide-vue-next").LucideProps, {}, any, {}>;
|
||||
}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
||||
declare const _default: typeof __VLS_export;
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import type { tipoResposta } from "p-respostas";
|
|||
import type { LucideIcon } from "lucide-vue-next";
|
||||
import type { VNodeChild } from "vue";
|
||||
export type ComponenteCelula = VNodeChild;
|
||||
export type EliAlinhamentoColuna = "esquerda" | "centro" | "direita";
|
||||
export type EliColuna<T> = {
|
||||
/** Texto exibido no cabeçalho da coluna. */
|
||||
rotulo: string;
|
||||
|
|
@ -9,6 +10,19 @@ export type EliColuna<T> = {
|
|||
celula: (linha: T) => ComponenteCelula;
|
||||
/** Ação opcional disparada ao clicar na célula. */
|
||||
acao?: () => void;
|
||||
/** Alinhamento do conteúdo da coluna (cabeçalho e células). */
|
||||
alinhamento?: EliAlinhamentoColuna;
|
||||
/**
|
||||
* Quando `true`, tenta truncar (ellipsis) conteúdos textuais longos.
|
||||
* Observação: o tooltip automático só é aplicado quando o conteúdo renderizado
|
||||
* da célula é um `string`/`number`.
|
||||
*/
|
||||
truncar?: boolean;
|
||||
/**
|
||||
* Largura máxima usada quando `truncar` estiver ativo.
|
||||
* Exemplos: `240` (px), `"18rem"`, `"30ch"`.
|
||||
*/
|
||||
largura_maxima?: number | string;
|
||||
/**
|
||||
* Campo de ordenação associado à coluna. Caso informado, a coluna passa a
|
||||
* exibir controles de ordenação e utiliza o valor como chave para o backend.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "eli-vue",
|
||||
"version": "0.1.23",
|
||||
"version": "0.1.33",
|
||||
"private": false,
|
||||
"main": "./dist/eli-vue.umd.js",
|
||||
"module": "./dist/eli-vue.es.js",
|
||||
|
|
|
|||
|
|
@ -140,6 +140,53 @@ export default defineComponent({
|
|||
return filhos as never;
|
||||
}
|
||||
|
||||
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 renderConteudoCelula(valor: unknown, coluna: (typeof props.tabela.colunas)[number]) {
|
||||
const filhos = normalizarFilhos(valor);
|
||||
const truncar = Boolean(coluna.truncar);
|
||||
|
||||
if (!truncar) {
|
||||
return filhos;
|
||||
}
|
||||
|
||||
// Só truncamos de forma segura quando o conteúdo é textual.
|
||||
if (typeof filhos !== "string" && typeof filhos !== "number") {
|
||||
return filhos;
|
||||
}
|
||||
|
||||
const tooltip = String(filhos);
|
||||
|
||||
return h(
|
||||
"span",
|
||||
{
|
||||
class: "eli-tabela__celula-conteudo",
|
||||
style: coluna.largura_maxima
|
||||
? {
|
||||
maxWidth: obterMaxWidth(coluna.largura_maxima),
|
||||
}
|
||||
: undefined,
|
||||
title: tooltip,
|
||||
},
|
||||
tooltip
|
||||
);
|
||||
}
|
||||
|
||||
function renderErro(mensagem: string) {
|
||||
return h(
|
||||
"div",
|
||||
|
|
@ -428,6 +475,7 @@ export default defineComponent({
|
|||
class: [
|
||||
"eli-tabela__th",
|
||||
ordenavel ? "eli-tabela__th--ordenavel" : undefined,
|
||||
obterClasseAlinhamento(coluna.alinhamento),
|
||||
],
|
||||
scope: "col",
|
||||
},
|
||||
|
|
@ -515,11 +563,12 @@ export default defineComponent({
|
|||
class: [
|
||||
"eli-tabela__td",
|
||||
coluna.acao ? "eli-tabela__td--clicavel" : undefined,
|
||||
obterClasseAlinhamento(coluna.alinhamento),
|
||||
],
|
||||
key: `${i}-${j}`,
|
||||
onClick: coluna.acao ? () => coluna.acao?.() : undefined,
|
||||
},
|
||||
normalizarFilhos(coluna.celula(linha as never))
|
||||
renderConteudoCelula(coluna.celula(linha as never), coluna)
|
||||
)
|
||||
);
|
||||
|
||||
|
|
@ -663,7 +712,10 @@ export default defineComponent({
|
|||
|
||||
return h(
|
||||
"tr",
|
||||
{ class: "eli-tabela__tr", key: i },
|
||||
{
|
||||
class: ["eli-tabela__tr", i % 2 === 1 ? "eli-tabela__tr--zebra" : undefined],
|
||||
key: i,
|
||||
},
|
||||
celulas
|
||||
);
|
||||
})
|
||||
|
|
@ -710,6 +762,10 @@ export default defineComponent({
|
|||
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;
|
||||
|
|
@ -786,6 +842,27 @@ export default defineComponent({
|
|||
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;
|
||||
|
|
@ -836,10 +913,11 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
.eli-tabela__cabecalho {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
/* Ações ficam imediatamente à direita da busca (em vez de ir para a extrema direita) */
|
||||
justify-content: flex-start;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
flex-wrap: wrap;
|
||||
|
|
|
|||
|
|
@ -12,9 +12,11 @@
|
|||
<button
|
||||
type="button"
|
||||
class="eli-tabela__busca-botao"
|
||||
aria-label="Buscar"
|
||||
title="Buscar"
|
||||
@click="emitirBusca"
|
||||
>
|
||||
Buscar
|
||||
<Search class="eli-tabela__busca-botao-icone" :size="16" :stroke-width="2" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -22,9 +24,11 @@
|
|||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, watch } from "vue";
|
||||
import { Search } from "lucide-vue-next";
|
||||
|
||||
export default defineComponent({
|
||||
name: "EliTabelaCaixaDeBusca",
|
||||
components: { Search },
|
||||
props: {
|
||||
modelo: {
|
||||
type: String,
|
||||
|
|
@ -65,10 +69,9 @@ export default defineComponent({
|
|||
<style scoped>
|
||||
.eli-tabela__busca {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
|
|
@ -95,6 +98,9 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
.eli-tabela__busca-botao {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
background: rgba(37, 99, 235, 0.12);
|
||||
color: rgba(37, 99, 235, 0.95);
|
||||
|
|
@ -103,6 +109,10 @@ export default defineComponent({
|
|||
transition: background-color 0.2s ease, color 0.2s ease;
|
||||
}
|
||||
|
||||
.eli-tabela__busca-botao-icone {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.eli-tabela__busca-botao:hover,
|
||||
.eli-tabela__busca-botao:focus-visible {
|
||||
background: rgba(37, 99, 235, 0.2);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import type { VNodeChild } from "vue";
|
|||
|
||||
export type ComponenteCelula = VNodeChild;
|
||||
|
||||
export type EliAlinhamentoColuna = "esquerda" | "centro" | "direita";
|
||||
|
||||
export type EliColuna<T> = {
|
||||
/** Texto exibido no cabeçalho da coluna. */
|
||||
rotulo: string;
|
||||
|
|
@ -11,6 +13,19 @@ export type EliColuna<T> = {
|
|||
celula: (linha: T) => ComponenteCelula;
|
||||
/** Ação opcional disparada ao clicar na célula. */
|
||||
acao?: () => void;
|
||||
/** Alinhamento do conteúdo da coluna (cabeçalho e células). */
|
||||
alinhamento?: EliAlinhamentoColuna;
|
||||
/**
|
||||
* Quando `true`, tenta truncar (ellipsis) conteúdos textuais longos.
|
||||
* Observação: o tooltip automático só é aplicado quando o conteúdo renderizado
|
||||
* da célula é um `string`/`number`.
|
||||
*/
|
||||
truncar?: boolean;
|
||||
/**
|
||||
* Largura máxima usada quando `truncar` estiver ativo.
|
||||
* Exemplos: `240` (px), `"18rem"`, `"30ch"`.
|
||||
*/
|
||||
largura_maxima?: number | string;
|
||||
/**
|
||||
* Campo de ordenação associado à coluna. Caso informado, a coluna passa a
|
||||
* exibir controles de ordenação e utiliza o valor como chave para o backend.
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import { defineComponent } from "vue";
|
||||
import BotaoPlayground from "./botao.playground.vue";
|
||||
import IndicadorPlayground from "./indicador.playground.vue";
|
||||
import CartaoPlayground from "./cartao.playground.vue";
|
||||
|
|
@ -34,8 +34,37 @@ import DataHoraPlayground from "./data_hora.playground.vue";
|
|||
import TabelaPlayground from "./tabela.playground.vue";
|
||||
import OlaMundoPlayground from "./ola_mundo.playground.vue";
|
||||
|
||||
type AbaPlayground =
|
||||
| "botao"
|
||||
| "indicador"
|
||||
| "cartao"
|
||||
| "campo"
|
||||
| "data_hora"
|
||||
| "tabela"
|
||||
| "ola_mundo";
|
||||
|
||||
const mapaHashParaAba: Record<string, AbaPlayground> = {
|
||||
botao: "botao",
|
||||
indicador: "indicador",
|
||||
cartao: "cartao",
|
||||
campo: "campo",
|
||||
"data-hora": "data_hora",
|
||||
tabela: "tabela",
|
||||
demo: "ola_mundo",
|
||||
};
|
||||
|
||||
const mapaAbaParaHash: Record<AbaPlayground, string> = {
|
||||
botao: "botao",
|
||||
indicador: "indicador",
|
||||
cartao: "cartao",
|
||||
campo: "campo",
|
||||
data_hora: "data-hora",
|
||||
tabela: "tabela",
|
||||
ola_mundo: "demo",
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'App',
|
||||
name: "App",
|
||||
components: {
|
||||
BotaoPlayground,
|
||||
IndicadorPlayground,
|
||||
|
|
@ -47,15 +76,45 @@ export default defineComponent({
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
aba: "botao" as
|
||||
| "botao"
|
||||
| "indicador"
|
||||
| "cartao"
|
||||
| "campo"
|
||||
| "data_hora"
|
||||
| "tabela"
|
||||
| "ola_mundo",
|
||||
aba: "botao" as AbaPlayground,
|
||||
};
|
||||
}
|
||||
})
|
||||
},
|
||||
mounted() {
|
||||
this.sincronizarAbaComHash();
|
||||
window.addEventListener("hashchange", this.sincronizarAbaComHash);
|
||||
},
|
||||
beforeUnmount() {
|
||||
window.removeEventListener("hashchange", this.sincronizarAbaComHash);
|
||||
},
|
||||
watch: {
|
||||
aba() {
|
||||
this.sincronizarHashComAba();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
normalizarHash() {
|
||||
const valor = window.location.hash.replace(/^#\/?/, "").trim();
|
||||
return valor;
|
||||
},
|
||||
sincronizarAbaComHash() {
|
||||
const hash = this.normalizarHash();
|
||||
|
||||
const aba = mapaHashParaAba[hash] ?? "botao";
|
||||
|
||||
if (this.aba !== aba) {
|
||||
this.aba = aba;
|
||||
}
|
||||
|
||||
// Se a URL estiver vazia/fora do padrão, normalizamos.
|
||||
this.sincronizarHashComAba();
|
||||
},
|
||||
sincronizarHashComAba() {
|
||||
const destino = `#/${mapaAbaParaHash[this.aba]}`;
|
||||
|
||||
if (window.location.hash !== destino) {
|
||||
window.location.hash = destino;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ export default defineComponent({
|
|||
empreendedor: "Maria Silva",
|
||||
empreendimento: "Doces da Maria",
|
||||
documento: "12.345.678/0001-90",
|
||||
email: "contato@docesdamaria.com",
|
||||
email: "contato.comercial.super.longo@doces-da-maria-exemplo-muito-grande.com.br",
|
||||
telefone: "(11) 91234-5678",
|
||||
},
|
||||
{
|
||||
|
|
@ -298,11 +298,14 @@ export default defineComponent({
|
|||
rotulo: "Documento",
|
||||
celula: (l) => l.documento,
|
||||
coluna_ordem: "documento",
|
||||
alinhamento: "direita",
|
||||
},
|
||||
{
|
||||
rotulo: "E-mail",
|
||||
celula: (l) => l.email,
|
||||
coluna_ordem: "email",
|
||||
truncar: true,
|
||||
largura_maxima: 260,
|
||||
acao: () => {
|
||||
// Exemplo de ação: poderia abrir detalhes
|
||||
console.log("Clicou na coluna e-mail");
|
||||
|
|
@ -312,6 +315,7 @@ export default defineComponent({
|
|||
rotulo: "Telefone",
|
||||
celula: (l) => l.telefone,
|
||||
coluna_ordem: "telefone",
|
||||
alinhamento: "direita",
|
||||
},
|
||||
],
|
||||
acoesLinha: acoesLinha,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue