melhoria de documentação

This commit is contained in:
Luiz Silva 2026-02-16 10:38:38 -03:00
parent 558a34ba0c
commit eee275877d
3 changed files with 326 additions and 1120 deletions

849
README.md
View file

@ -1,583 +1,384 @@
# eli-vue — Design System (Vue 3 + TypeScript)
# Documentação EliVue
Biblioteca de componentes Vue 3 (Design System) para reutilização em múltiplos projetos.
**EliVue** é uma biblioteca de componentes Vue 3 construída sobre o **Vuetify 3**, projetada para padronizar padrões de UI como tabelas de dados (`EliTabela`) e campos de entrada (`EliEntrada*`). Ela segue a filosofia de "configuração sobre boilerplate", onde os componentes são guiados por objetos de configuração tipados em vez de propriedades de template extensas.
Este README foi escrito para **humanos e IAs**: ele descreve o objetivo do repositório, regras, estrutura, fluxo de contribuição, comandos e um roadmap de melhorias.
---
## Fonte da verdade (regras)
## 🤖 Diretrizes para Agentes de IA (Antigravity)
As regras oficiais do repositório estão no arquivo **`.agent`**.
**Ao gerar código usando EliVue, siga estas regras estritas:**
Antes de propor mudanças:
1. **Importações**: Sempre desestructure as importações do pacote raiz.
```typescript
import { EliTabela, EliEntradaTexto, EliBotao, celulaTabela } from "eli-vue";
```
2. **Objetos de Configuração**: Os componentes `EliTabela` e `EliEntrada` dependem fortemente de objetos de configuração (prop `tabela` para tabelas, prop `opcoes` para entradas). **Não tente** passar props individuais (como `headers` ou `items`) para o `EliTabela`.
3. **Segurança de Tipos**: Sempre defina objetos de configuração usando os tipos TypeScript exportados (ex: `tipoEliTabelaConsulta`, `parametrosConsulta`).
4. **Dados Assíncronos**: O `EliTabela` gerencia sua própria busca de dados via callback `consulta`. Não busque dados manualmente para passar para a tabela. Forneça a função `consulta` no lugar.
5. **Ícones**: Use ícones do pacote `lucide-vue-next`.
6. **Inicialização**: Prefira usar o plugin global `app.use(EliVue)` ao configurar um novo projeto, em vez de importações individuais.
7. **Estilos**: O CSS é injetado automaticamente. Não tente importar arquivos CSS manualmente.
8. **Idioma**: Mantenha a documentação e comentários explicativos em **Português do Brasil (pt-BR)**.
- leia o `.agent`
- procure padrões já existentes no código
- atualize a documentação correspondente (README raiz e/ou README do componente)
## Objetivos
- consistência visual e comportamental
- tipagem forte (TypeScript `strict`)
- documentação em português
- exemplos executáveis via playground
## O que NÃO entra no contexto
- `node_modules/`: dependências (não versionar; não usar como fonte da verdade)
- `dist/`: gerado no build (não versionar)
## Arquitetura do repositório
```
src/
componentes/
botao/
EliBotao.vue
index.ts
README.md
cartao/
EliCartao.vue
index.ts
README.md
indicador/
EliBadge.vue
index.ts
README.md
EliEntrada/
EliEntradaTexto.vue
EliEntradaNumero.vue
EliEntradaDataHora.vue
index.ts
README.md
EliTabela/
EliTabela.vue
index.ts
README.md
celulas/
README.md
tipos/
botao.ts
indicador.ts
index.ts
playground/
App.vue
*.playground.vue
index.ts
```
### Convenções (nomenclatura)
- Componentes usam **prefixo `Eli`** (ex.: `EliBotao`, `EliEntradaTexto`).
- Pastas preferem **português** (ex.: `src/componentes/botao`, `src/componentes/campo`).
- Tipos compartilhados ficam em `src/tipos/`.
- Sem TSX; padrão SFC: `<template>` + `<script lang="ts">` + `defineComponent`.
- `any` é proibido.
---
## Instalação
```bash
pnpm add eli-vue
# Dependências (Peer Dependencies)
pnpm add vue vuetify lucide-vue-next
```
> Observação: `vue` e `vuetify` são **peerDependencies**.
Certifique-se de que seu projeto esteja configurado com Vuetify 3.
### Instalação (projeto consumidor já usa Vuetify)
---
Se o seu projeto já está configurado com Vuetify 3, basta instalar este pacote:
## 📦 Referência de Componentes
```bash
pnpm add eli-vue
### 1. EliTabela (Tabela de Dados)
O componente `EliTabela` é uma tabela poderosa com paginação no servidor (server-side), busca integrada, ordenação e menus de ação.
#### API
- **Prop**: `tabela` (Obrigatório)
- **Tipo**: `tipoEliTabelaConsulta<T>`
#### Interface de Configuração (`tipoEliTabelaConsulta`)
```typescript
// De: eli-vue/src/componentes/EliTabela/types-eli-tabela.ts
export type tipoEliTabelaConsulta<T> = {
// Identificador único para a tabela (usado para persistência local de visibilidade das colunas)
nome: string;
// Função para buscar dados. DEVE retornar um objeto de resposta padrão.
consulta: (params: parametrosConsulta<T>) => Promise<tipoResposta<tipoEliConsultaPaginada<T>>>;
// Definições das colunas
colunas: tipoEliColuna<T>[];
// Opções de UI
mostrarCaixaDeBusca?: boolean;
registros_por_consulta?: number; // padrão: 10
maximo_botoes_paginacao?: number; // padrão: 7
mensagemVazio?: string;
// Ações
acoesLinha?: tipoEliTabelaAcao<T>[]; // Ações para cada linha (menu de contexto)
acoesTabela?: Array<{ // Ações globais (superior/inferior)
// Ações da Tabela (Botões Globais)
acoesTabela?: Array<{
/**
* Posição do botão:
* - "superior": Ao lado da caixa de busca (canto superior direito).
* - "inferior": No rodapé, alinhado à esquerda (canto inferior esquerdo).
*/
posicao: "superior" | "inferior";
/** Rótulo do botão */
rotulo: string;
/** Ícone (Lucide) */
icone?: LucideIcon;
/** Cor do botão (ex: "primary", "success", "error") */
cor?: string;
/**
* Função executada ao clicar.
* Recebe um objeto contendo:
* - Parâmetros atuais da consulta (offSet, limit, filtros, etc.)
* - Helper `atualizarConsulta()`: Promise<void> para recarregar a tabela.
*/
acao: (params: parametrosConsulta<T> & { atualizarConsulta: () => Promise<void> }) => void;
}>;
// Definição de Filtro Avançado
filtroAvancado?: Array<{
rotulo: string;
coluna: keyof T;
operador: string; // ex: "=", "like", ">="
entrada: any; // Definição do componente, ex: ["texto", { ... }]
}>;
};
```
> Dica: mantenha `vue` e `vuetify` atualizados dentro das faixas suportadas.
#### Definindo Colunas (helper `celulaTabela`)
## Uso
Use o helper `celulaTabela` para criar definições de células com segurança de tipo.
### 1) Registro global (plugin)
```typescript
// Tipos de células disponíveis e seus dados:
// "textoSimples": { texto: string; acao?: () => void }
// "textoTruncado": { texto: string; acao?: () => void }
// "numero": { numero: number; prefixo?: string; sufixo?: string; acao?: () => void }
// "tags": { opcoes: Array<{ rotulo: string; cor?: string; icone?: LucideIcon; acao?: () => void }> }
// "data": { valor: string; formato: "data" | "data_hora" | "relativo"; acao?: () => void }
// "badge": { texto: string; cor: string }
```ts
import { createApp } from "vue";
import EliVue from "eli-vue";
import App from "./App.vue";
createApp(App).use(EliVue).mount("#app");
{
rotulo: "Nome",
celula: (row) => celulaTabela("textoSimples", { texto: row.nome }),
visivel: true
}
```
#### Exemplo completo (main.ts) — com Vuetify 3
Este é o modelo recomendado para projetos consumidores (Vite + Vue 3) **que já usam Vuetify 3**:
```ts
import { createApp } from "vue";
import App from "./App.vue";
// Vuetify
import "vuetify/styles";
import { createVuetify } from "vuetify";
// eli-vue (Design System)
import EliVue from "eli-vue";
const vuetify = createVuetify({
// opcional: componentes/directives/tema do seu projeto
});
createApp(App)
.use(vuetify)
.use(EliVue)
.mount("#app");
```
> Nota: o `eli-vue` **não cria** nem configura Vuetify; ele assume que o consumidor já tem Vuetify.
### 2) Importação direta de componentes
```ts
import {
EliBotao,
EliBadge,
EliCartao,
EliTabela,
EliEntradaTexto,
EliEntradaNumero,
EliEntradaDataHora,
EliEntradaParagrafo,
EliEntradaSelecao,
} from "eli-vue";
```
## Exemplos rápidos de uso
### EliBotao
#### Exemplo de Uso com Filtros Avançados
```vue
<script setup lang="ts">
import { EliTabela, celulaTabela } from "eli-vue";
import type { tipoEliTabelaConsulta } from "eli-vue";
import { UsuarioService } from "@/services/UsuarioService";
import { BadgeCheck, Pencil, Plus } from "lucide-vue-next";
type Usuario = { id: number; nome: string; email: string; ativo: boolean; created_at: string };
const tabelaConfig: tipoEliTabelaConsulta<Usuario> = {
nome: "tabela-usuarios",
mostrarCaixaDeBusca: true,
registros_por_consulta: 10,
colunas: [
{
rotulo: "Nome",
celula: (usuario) => celulaTabela("textoSimples", { texto: usuario.nome }),
visivel: true,
coluna_ordem: "nome"
},
{
rotulo: "Status",
visivel: true,
celula: (usuario) => celulaTabela("tags", {
opcoes: [
usuario.ativo
? { rotulo: "Ativo", cor: "success", icone: BadgeCheck }
: { rotulo: "Inativo", cor: "error" }
]
})
},
{
rotulo: "Criado em",
celula: (usuario) => celulaTabela("data", { valor: usuario.created_at, formato: "data_hora" }),
visivel: true
}
],
// Configuração de filtros avançados
filtroAvancado: [
{
rotulo: "Nome",
coluna: "nome",
operador: "like",
// definição do input: ["tipo", opcoes]
entrada: ["texto", { rotulo: "Nome do usuário" }] as any
},
{
rotulo: "Ativo?",
coluna: "ativo",
operador: "=",
entrada: ["selecao", {
rotulo: "Status",
itens: () => [
{ chave: "true", rotulo: "Ativo" },
{ chave: "false", rotulo: "Inativo" }
]
}] as any
}
],
acoesTabela: [
{
rotulo: "Novo Usuário",
icone: Plus,
posicao: "superior",
cor: "primary",
// O callback recebe: offSet, limit, filtros, e o helper 'atualizarConsulta'
acao: async ({ atualizarConsulta }) => {
// Exemplo: Abrir modal de criação e depois atualizar a tabela
await openCreateUserModal();
await atualizarConsulta();
}
},
{
rotulo: "Exportar CSV",
icone: Download,
posicao: "inferior",
cor: "success",
acao: (params) => {
// 'params' contém os filtros atuais aplicados na tabela
UsuarioService.exportarCsv(params);
}
}
],
consulta: async (params) => {
// params contém: offSet, limit, texto_busca, coluna_ordem, direcao_ordem, filtros
return await UsuarioService.listar(params);
},
acoesLinha: [
{
rotulo: "Editar",
icone: Pencil,
cor: "primary",
acao: (usuario) => console.log("Editar", usuario)
}
]
};
</script>
<template>
<EliBotao @click="salvar">Salvar</EliBotao>
<EliTabela :tabela="tabelaConfig" />
</template>
```
### Entradas (EliEntrada*) com v-model
---
### 2. EliEntrada (Inputs)
Um conjunto de wrappers padronizados em torno dos campos do Vuetify.
#### Variantes
- `EliEntradaTexto`: Texto, Email, URL, CPF, CNPJ, CEP.
- `EliEntradaNumero`: Inteiros, Decimais, Moeda.
- `EliEntradaDataHora`: Data, DataHora.
- `EliEntradaSelecao`: Select/Dropdown (pode ser assíncrono).
- `EliEntradaParagrafo`: Textarea.
#### API Comum
Todas as entradas aceitam duas props principais:
1. `value`: O binding v-model.
2. `opcoes`: Um objeto de configuração específico para o tipo de entrada.
#### Objetos de Configuração (`tiposEntradas.ts`)
**EliEntradaTexto**
```typescript
opcoes: {
rotulo: string;
placeholder?: string;
limiteCaracteres?: number;
formato?: "texto" | "email" | "url" | "telefone" | "cpfCnpj" | "cep";
}
```
**EliEntradaNumero**
```typescript
opcoes: {
rotulo: string;
precisao?: number; // 0=inteiro (padrão se null), 0.1=1 decimal, 0.01=2 decimais. Se undefined/null, assume decimal livre.
prefixo?: string; // ex: "R$"
sufixo?: string; // ex: "kg"
}
```
**EliEntradaDataHora**
```typescript
opcoes: {
rotulo: string;
modo?: "data" | "dataHora"; // padrão: "dataHora"
min?: string; // String ISO
max?: string; // String ISO
}
```
**EliEntradaSelecao**
```typescript
opcoes: {
rotulo: string;
// Função que retorna itens ou promise de itens.
// IMPORTANTE: Itens devem ser objetos { chave: string, rotulo: string }
itens: () => Array<{ chave: string; rotulo: string }> | Promise<Array<{ chave: string; rotulo: string }>>;
limpavel?: boolean;
}
```
#### Exemplo de Uso
```vue
<script setup lang="ts">
import { ref } from "vue";
import { EliEntradaTexto, EliEntradaNumero, EliEntradaDataHora } from "eli-vue";
const nome = ref("");
const salario = ref<number | null>(null);
const nascimento = ref<string | null>(null); // ISO string
</script>
<template>
<EliEntradaTexto
v-model:value="nome"
:opcoes="{ rotulo: 'Nome', placeholder: 'Digite seu nome' }"
:value="nome"
@update:value="nome = $event"
:opcoes="{ rotulo: 'Nome Completo', formato: 'texto' }"
/>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
export default defineComponent({
setup() {
const nome = ref<string | null>("");
return { nome };
},
});
</script>
```
### EliEntradaNumero (exemplo)
```vue
<template>
<EliEntradaNumero
v-model:value="taxa"
:opcoes="{ rotulo: 'Taxa', placeholder: '0,00' }"
:value="salario"
@update:value="salario = $event"
:opcoes="{ rotulo: 'Salário', prefixo: 'R$', precisao: 0.01 }"
/>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
export default defineComponent({
setup() {
const taxa = ref<number | null>(null);
return { taxa };
},
});
</script>
```
### EliBadge
```vue
<template>
<EliBadge badge="3">
<v-icon>mdi-bell</v-icon>
</EliBadge>
</template>
```
### EliEntradaDataHora
> Entrada/saída sempre em **ISO 8601**.
> - Aceita UTC absoluto (`Z`) ou com offset.
> - Emite ISO com **offset local**.
```vue
<template>
<EliEntradaDataHora v-model:value="agendamento" :opcoes="{ rotulo: 'Agendamento' }" />
<!-- Exibe como data local, vincula como string ISO -->
<EliEntradaDataHora
v-model:value="nascimento"
:opcoes="{ rotulo: 'Nascimento', modo: 'data' }"
:value="nascimento"
@update:value="nascimento = $event"
:opcoes="{ rotulo: 'Data de Nascimento', modo: 'data' }"
/>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
import { EliEntradaDataHora } from "eli-vue";
export default defineComponent({
components: { EliEntradaDataHora },
setup() {
const agendamento = ref<string | null>("2026-01-09T16:15:00Z");
const nascimento = ref<string | null>("2026-01-09T00:00:00-03:00");
return { agendamento, nascimento };
},
});
</script>
```
### EliTabela — célula `tags`
---
Você pode renderizar múltiplas tags/chips numa célula usando `celulaTabela("tags", ...)`.
### 3. EliBotao
```ts
import { celulaTabela } from "eli-vue";
import { BadgeCheck, Pencil } from "lucide-vue-next";
Wrapper simples para `v-btn`.
celula: (l) =>
celulaTabela("tags", {
opcoes: [
{ rotulo: "Ativo", cor: "success", icone: BadgeCheck },
{ rotulo: "Editar", cor: "primary", icone: Pencil, acao: () => editar(l) },
],
})
```
### EliTabela — célula `numero` com prefixo/sufixo
Para representar **moeda** ou **unidade de medida**, a célula `numero` aceita `prefixo` e `sufixo`:
```ts
import { celulaTabela } from "eli-vue";
// moeda
celula: (l) => celulaTabela("numero", { numero: l.total, prefixo: "R$" })
// unidade
celula: (l) => celulaTabela("numero", { numero: l.peso, sufixo: "kg" })
```
### EliTabela — célula `data`
Para exibir datas (ISO 8601) com diferentes formatos, use a célula `data`:
```ts
import { celulaTabela } from "eli-vue";
// somente data
celula: (l) => celulaTabela("data", { valor: l.criado_em, formato: "data" })
// data e hora
celula: (l) => celulaTabela("data", { valor: l.criado_em, formato: "data_hora" })
// relativo (dayjs)
celula: (l) => celulaTabela("data", { valor: l.atualizado_em, formato: "relativo" })
```
## Troubleshooting (problemas comuns)
### 1) "Failed to resolve component" / componente não registrado
Você provavelmente esqueceu de:
- usar o plugin: `app.use(EliVue)`
- ou importar e registrar o componente localmente
### 2) Estilos não aplicam / layout estranho
Confirme que o consumidor importou:
- `vuetify/styles`
### 3) Ícones não aparecem
Alguns exemplos usam `<v-icon>` com Material Design Icons. O `eli-vue` **não instala ícones** no seu projeto.
- garanta que o seu projeto consumidor está configurado com o pacote de ícones desejado (ex.: MDI)
- ou substitua o conteúdo do slot por outro elemento
## Exemplo de composição (pipeline estilo Trello)
O caso mais comum no **gestor de oportunidades e propostas comerciais** é compor UI a partir de componentes básicos.
Exemplo: um **pipeline** em colunas (estilo Trello/Kanban) com cards de oportunidades.
> Este exemplo usa Vuetify para layout/containers e o `eli-vue` para inputs, botões e indicadores.
- **Props**: `color`, `variant` (elevated, flat, text, etc.), `size`, `loading`, `disabled`.
- **Slots**: slot padrão para o conteúdo do botão.
```vue
<template>
<v-container class="py-6">
<div class="toolbar">
<h2 class="text-h6">Pipeline de Oportunidades</h2>
<EliEntradaTexto
v-model:value="filtro"
:opcoes="{ rotulo: 'Buscar', placeholder: 'Cliente, proposta, valor...' }"
/>
<EliBotao @click="criarOportunidade">Nova oportunidade</EliBotao>
</div>
<div class="colunas">
<section
v-for="coluna in colunas"
:key="coluna.id"
class="coluna"
>
<header class="coluna-header">
<strong>{{ coluna.titulo }}</strong>
<EliBadge :badge="coluna.itens.length" radius="pill" />
</header>
<div class="cards">
<v-card
v-for="item in itensFiltrados(coluna.itens)"
:key="item.id"
class="card"
variant="outlined"
>
<v-card-title class="d-flex justify-space-between align-center">
<span class="text-subtitle-2">{{ item.titulo }}</span>
<EliBadge
v-if="item.alerta"
dot
color="error"
:visible="true"
>
<span />
</EliBadge>
</v-card-title>
<v-card-text class="text-body-2">
<div><strong>Cliente:</strong> {{ item.cliente }}</div>
<div><strong>Valor:</strong> {{ item.valor }}</div>
<div><strong>Vencimento:</strong> {{ item.vencimento }}</div>
</v-card-text>
<v-card-actions>
<EliBotao variant="text" @click="abrir(item)">Abrir</EliBotao>
<EliBotao variant="text" color="secondary" @click="editar(item)">
Editar
</EliBotao>
</v-card-actions>
</v-card>
</div>
</section>
</div>
</v-container>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
import { EliBadge, EliBotao, EliEntradaTexto } from "eli-vue";
type Oportunidade = {
id: string;
titulo: string;
cliente: string;
valor: string;
vencimento: string;
alerta?: boolean;
};
type Coluna = {
id: string;
titulo: string;
itens: Oportunidade[];
};
export default defineComponent({
name: "PipelineExemplo",
components: { EliBadge, EliBotao, EliEntradaTexto },
setup() {
const filtro = ref("");
const colunas = ref<Coluna[]>([
{
id: "novo",
titulo: "Novo",
itens: [
{
id: "1",
titulo: "Proposta #1024",
cliente: "ACME Ltda",
valor: "R$ 12.500,00",
vencimento: "10/01/2026",
alerta: true,
},
],
},
{
id: "negociacao",
titulo: "Negociação",
itens: [
{
id: "2",
titulo: "Oportunidade #204",
cliente: "Empresa X",
valor: "R$ 8.000,00",
vencimento: "15/01/2026",
},
],
},
{
id: "ganho",
titulo: "Ganho",
itens: [],
},
]);
function itensFiltrados(itens: Oportunidade[]) {
const q = filtro.value.trim().toLowerCase();
if (!q) return itens;
return itens.filter((i) =>
`${i.titulo} ${i.cliente} ${i.valor}`.toLowerCase().includes(q)
);
}
function criarOportunidade() {
// aqui você abriria um modal / rota de criação
}
function abrir(item: Oportunidade) {
// aqui você navega para detalhes da oportunidade
}
function editar(item: Oportunidade) {
// aqui você abre edição
}
return {
filtro,
colunas,
itensFiltrados,
criarOportunidade,
abrir,
editar,
};
},
});
</script>
<style scoped>
.toolbar {
display: grid;
grid-template-columns: 1fr minmax(260px, 420px) auto;
gap: 12px;
align-items: center;
margin-bottom: 16px;
}
.colunas {
display: grid;
gap: 16px;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
.coluna {
border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 12px;
padding: 12px;
background: rgba(0, 0, 0, 0.02);
}
.coluna-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.cards {
display: grid;
gap: 12px;
}
.card {
border-radius: 12px;
}
@media (max-width: 720px) {
.toolbar {
grid-template-columns: 1fr;
}
}
</style>
<EliBotao color="primary" @click="salvar">
Salvar
</EliBotao>
```
## Como rodar localmente
---
### Playground
### 4. EliCartao & EliBadge
```bash
pnpm dev
```
**EliCartao**
Cartão padronizado para itens de domínio.
- **Props**: `titulo`, `status` (tipo `CartaoStatus`: "novo" | "rascunho" | "vendido" | "cancelado"), `variant`.
- **Slots**: `default` (conteúdo), `acoes` (ações de rodapé).
O playground (`src/playground`) valida visualmente variações e interações.
**EliBadge**
Badge aprimorado com raios.
- **Props**: `badge` (conteúdo), `color`, `radius` ("suave" | "pill").
### Build da lib + geração de tipos
---
```bash
pnpm run build
```
## 🔧 Solução de Problemas e Dicas
Gera `dist/` (artefatos de build) e `dist/types` (declarações `.d.ts`).
### 1. "Failed to resolve component"
- Certifique-se de que `app.use(EliVue)` foi chamado no `main.ts`.
- Se importar diretamente, garanta importações padrão: `import { EliTabela } from 'eli-vue'`.
> Observação importante: este repositório roda `npm version patch --no-git-tag-version` no `prebuild`.
> Ou seja, ao rodar `pnpm run build` a versão do `package.json` é incrementada automaticamente.
### 2. Problemas de Estilo
- **Injeção de CSS**: O `eli-vue` injeta CSS automaticamente. Verifique sua CSP (Política de Segurança de Conteúdo) se os estilos estiverem bloqueados.
- **Vuetify**: Certifique-se de que o Vuetify 3 está corretamente instalado e os estilos (`vuetify/styles`) estão importados no seu arquivo de entrada principal.
## Guia rápido para IAs (antes de codar)
### 3. Erros de TypeScript no `celulaTabela`
- Garanta que o segundo argumento corresponda ao esquema específico do tipo de célula.
- Exemplo: `celulaTabela("textoSimples", { texto: ... })` funciona, mas `celulaTabela("textoSimples", { numero: ... })` falhará.
1) **Leia** `.agent` e este README.
2) **Busque padrões** antes de criar API nova (props/emits/slots).
3) **Não use `any`**.
4) **Centralize tipos** em `src/tipos/`.
5) Ao mudar API/comportamento:
- atualize o `README.md` do componente
- atualize ou crie um `*.playground.vue`
6) Rode `pnpm run build` antes de finalizar.
### 4. Ícones não aparecem
- Passe o **objeto do componente** (ex: importado de `lucide-vue-next`), não o nome em string.
- Exemplo: `icone: Pencil` (Correto) vs `icone: "pencil"` (Incorreto).
## Como criar/alterar um componente (checklist)
1. Criar pasta em `src/componentes/<nome_em_portugues>/`
2. Criar `EliNomeDoComponente.vue` com `defineComponent` + comentários úteis
3. Criar `index.ts` com re-export (ex.: `export { default as EliX } from './EliX.vue'`)
4. Criar `README.md` do componente (API, exemplos, casos de borda, A11y)
5. Criar playground em `src/playground/<nome>.playground.vue` (mín. 3 variações)
6. Exportar no `src/index.ts`
### Critérios de aceite (qualidade)
- `pnpm run build` passa (inclui `vue-tsc`)
- zero `any`
- `defineComponent` em todos os componentes
- README do componente atualizado quando API mudar
- playground demonstrando variações + interação
## Roadmap de melhorias (a aplicar)
### Curto prazo (qualidade / consistência)
- [ ] Padronizar comentários úteis em todos os componentes (explicar decisões e estados)
- [ ] Tipar `emits` com validação de payload (quando houver)
- [ ] Consolidar nomenclatura de props/eventos em português (avaliar breaking changes)
### Médio prazo (DX + segurança)
- [ ] Adicionar ESLint + Prettier e regras (incluindo proibição de `any`)
- [ ] Adicionar testes unitários com Vitest + @vue/test-utils
- [ ] Criar pipeline de CI rodando `pnpm run build`
### Longo prazo (docs e maturidade)
- [ ] Documentação visual (Storybook ou VitePress)
- [ ] Tokens de design (cores/spacing) e guideline de acessibilidade
### 5. Valores do `EliEntrada`
- `EliEntradaTexto` com `formato='cpfCnpj'` emite a string **formatada** (ex: "123.456.789-00").
- `EliEntradaNumero` emite um `number` ou `null`.
- `EliEntradaDataHora` trabalha com **Strings ISO** (ex: "2023-01-01T00:00:00Z"). A exibição é localizada, mas o valor do modelo é sempre ISO.