@thibezer/ui-components-kit 2.7.0 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/CHANGELOG.md +265 -253
  2. package/README.md +815 -791
  3. package/dist/altura-controle-ukMRPckR.cjs +1 -0
  4. package/dist/altura-controle-vxQiXG4q.js +62 -0
  5. package/dist/botao.cjs +1 -1
  6. package/dist/botao.js +1 -1
  7. package/dist/campo-texto.cjs +1 -1
  8. package/dist/campo-texto.js +1 -1
  9. package/dist/components/ui-linha/index.d.ts +1 -0
  10. package/dist/components/ui-linha/ui-linha.d.ts +48 -0
  11. package/dist/core/altura-controle.d.ts +34 -0
  12. package/dist/core/index.d.ts +1 -0
  13. package/dist/core.cjs +1 -1
  14. package/dist/core.js +29 -20
  15. package/dist/custom-elements.json +13437 -12980
  16. package/dist/data.cjs +1 -1
  17. package/dist/data.js +1 -1
  18. package/dist/forms.cjs +1 -1
  19. package/dist/forms.js +2 -2
  20. package/dist/index.cjs +1 -1
  21. package/dist/index.d.ts +3 -0
  22. package/dist/index.js +179 -178
  23. package/dist/register.cjs +1 -1
  24. package/dist/register.js +5 -5
  25. package/dist/tipos-elementos.d.ts +25 -0
  26. package/dist/tipos-react.d.ts +2 -0
  27. package/dist/tipos-vue.d.ts +24 -0
  28. package/dist/{ui-badge-ChiiiO1r.js → ui-badge-BWhoGgo9.js} +4 -4
  29. package/dist/{ui-badge-DUCxyAG6.cjs → ui-badge-SIR81R9l.cjs} +3 -3
  30. package/dist/ui-botao-D24oXySA.cjs +12 -0
  31. package/dist/ui-botao-Dsj08jjw.js +165 -0
  32. package/dist/{ui-campo-texto-Uu2dJE_0.js → ui-campo-texto-B_OHVGiN.js} +51 -51
  33. package/dist/ui-campo-texto-DauJCde3.cjs +16 -0
  34. package/dist/ui-kit.css +1 -1
  35. package/dist/ui-linha-C6HBnsaw.cjs +19 -0
  36. package/dist/{ui-segmented-kbO1BLVs.js → ui-linha-nCDh2wPm.js} +162 -94
  37. package/dist/{ui-switch-BX6U5Ykm.cjs → ui-switch-D9a8XlVX.cjs} +9 -9
  38. package/dist/{ui-switch-DO5jR0hy.js → ui-switch-DO1R0Ce7.js} +55 -55
  39. package/package.json +2 -2
  40. package/dist/form-validacao-BIOXxt-1.js +0 -140
  41. package/dist/form-validacao-ytRYSF3E.cjs +0 -1
  42. package/dist/ui-badge-BxT-sDmV.cjs +0 -28
  43. package/dist/ui-badge-C0eKBI8E.js +0 -183
  44. package/dist/ui-botao-BOOnP8ds.cjs +0 -12
  45. package/dist/ui-botao-DfqSivHl.js +0 -165
  46. package/dist/ui-campo-texto-CHTfMO-H.cjs +0 -16
  47. package/dist/ui-campo-texto-Cnn2UAkS.cjs +0 -16
  48. package/dist/ui-campo-texto-gum_jDdI.js +0 -380
  49. package/dist/ui-card-B86lJXTP.cjs +0 -20
  50. package/dist/ui-card-C_o5_0cB.js +0 -111
  51. package/dist/ui-controle-cores-BS0WYPdi.js +0 -572
  52. package/dist/ui-controle-cores-Er6ZcQ7U.cjs +0 -111
  53. package/dist/ui-modal-CvGFGh1h.cjs +0 -18
  54. package/dist/ui-modal-DBOtSTbR.js +0 -238
  55. package/dist/ui-paleta-ferramentas-Ccmo-rB5.cjs +0 -21
  56. package/dist/ui-paleta-ferramentas-DNN5i6nL.js +0 -860
  57. package/dist/ui-segmented-BOaGRQw4.cjs +0 -19
  58. package/dist/ui-segmented-D6b6zxVs.cjs +0 -19
  59. package/dist/ui-segmented-wUraId2e.js +0 -412
  60. package/dist/ui-skeleton-BilwGdOm.js +0 -634
  61. package/dist/ui-skeleton-SPYqku8P.cjs +0 -52
  62. package/dist/ui-skeleton-W9uwIceA.js +0 -585
  63. package/dist/ui-skeleton-gZrJipCC.cjs +0 -52
  64. package/dist/ui-switch-BcOqOkiV.js +0 -856
  65. package/dist/ui-switch-D7iPKDj-.cjs +0 -53
  66. package/dist/ui-tabela-propriedades-BecPqC-e.cjs +0 -141
  67. package/dist/ui-tabela-propriedades-BokvDD3b.js +0 -2648
package/README.md CHANGED
@@ -1,791 +1,815 @@
1
- # 🎨 UI Components Kit - Web Components Nativos & Universais
2
-
3
- Biblioteca de **Web Components Nativos Universais e Agnósticos**, construída no padrão oficial **W3C Custom Elements + Shadow DOM**, com TypeScript, Design Tokens em CSS modular e Vite.
4
-
5
- Projetada para funcionar em **qualquer linguagem, framework ou sistema web** (HTML puro, React, Next.js, Vue, Angular, Svelte, PHP, Django, WordPress, Blazor, etc.) com **zero dependências externas** (exceto `<ui-mapa>` e `<ui-canvas-cad>`, que usam o Leaflet como dependência opcional).
6
-
7
- ---
8
-
9
- ## 📑 Índice
10
-
11
- 1. [Por que Web Components?](#-por-que-web-components)
12
- 2. [Como Importar na sua Aplicação](#-como-importar-na-sua-aplicação)
13
- - [Opção 1: CDN / Script Tag (HTML puro, PHP, Django)](#opção-1-cdn--script-tag-html-puro-php-django)
14
- - [Opção 2: NPM / Módulos ES (React, Vue, Next.js, Vite)](#opção-2-npm--módulos-es-react-vue-nextjs-vite)
15
- 3. [Como Controlar os Componentes (5 Pilares)](#-como-controlar-os-componentes-5-pilares)
16
- 4. [⚡ Recursos Inteligentes de Alta Produtividade](#-recursos-inteligentes-de-alta-produtividade)
17
- - [Ações Declarativas "Zero-JS" (Triggers Nativos)](#1-ações-declarativas-zero-js-triggers-nativos)
18
- - [Formulários Automáticos com FormData Nativo (W3C FACE)](#2-formulários-automáticos-com-formdata-nativo-w3c-face)
19
- - [Barramento Global de Orquestração (UIBus)](#3-barramento-global-de-orquestração-uibus)
20
- - [Tabelas Autônomas com Auto-Fetch Remoto](#4-tabelas-autônomas-com-auto-fetch-remoto)
21
- 5. [💻 Exemplos de Uso por Tecnologia](#-exemplos-de-uso-por-tecnologia)
22
- - [HTML Puro + JavaScript](#html-puro--javascript)
23
- - [React / Next.js](#react--nextjs)
24
- - [Vue.js](#vuejs)
25
- - [Iframes / Microfrontends](#iframes--microfrontends)
26
- 6. [🧩 Catálogo de Componentes & Referência](#-catálogo-de-componentes--referência)
27
- 7. [🛠️ Como Executar e Compilar Localmente](#️-como-executar-e-compilar-localmente)
28
-
29
- ---
30
-
31
- ## 🎯 Por que Web Components?
32
-
33
- * 🌐 **100% Universal e Agnóstico**: Funciona em qualquer stack sem necessidade de bibliotecas intermediárias.
34
- * 🛡️ **Encapsulamento Total (Shadow DOM)**: O CSS da sua aplicação nunca quebra o componente, e o CSS do componente nunca vaza para o seu site.
35
- * 📐 **Design Tokens Centralizados**: Variáveis CSS personalizáveis para cores, tipografia, raios e densidade.
36
- * 🚀 **Suporte a Altíssima Densidade**: Otimizado para interfaces densas e técnicas (GIS, CAD, Topografia de 15px a 20px de altura) até interfaces fluidas modernas.
37
-
38
- ---
39
-
40
- ## 📦 Como Importar na sua Aplicação
41
-
42
- ### Opção 1: CDN / Script Tag (HTML puro, PHP, Django)
43
-
44
- Adicione as tags no `<head>` ou antes do fechamento do `</body>`:
45
-
46
- ```html
47
- <!-- 1. Design Tokens e Estilos Globais -->
48
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@thibezer/ui-components-kit@1/dist/ui-kit.css">
49
-
50
- <!-- 2. Biblioteca dos Componentes (Módulo ES) -->
51
- <script type="module" src="https://cdn.jsdelivr.net/npm/@thibezer/ui-components-kit@1/dist/index.js"></script>
52
-
53
- <!-- 3. Uso imediato no HTML -->
54
- <ui-botao-primario variante="primary">Clique Aqui</ui-botao-primario>
55
- ```
56
-
57
- > **Mapa e CAD via CDN (`<ui-mapa>`, `<ui-canvas-cad>`)**: esses componentes dependem do [Leaflet](https://leafletjs.com/), importado como `leaflet` (especificador "bare"), que o navegador só resolve com um *import map*. Declare-o **antes** do script da biblioteca:
58
- >
59
- > ```html
60
- > <script type="importmap">
61
- > { "imports": { "leaflet": "https://cdn.jsdelivr.net/npm/leaflet@1.9.4/+esm" } }
62
- > </script>
63
- > ```
64
- >
65
- > Os demais componentes não precisam do Leaflet. Com npm, basta `npm install leaflet`.
66
-
67
- ### Opção 2: NPM / Módulos ES (React, Vue, Next.js, Vite)
68
-
69
- Instale pelo npm:
70
-
71
- ```bash
72
- npm install @thibezer/ui-components-kit
73
- ```
74
-
75
- > O pacote é publicado já compilado (`dist/`) e **não executa build na instalação**, então funciona em CI/CD e em servidores com `npm ci --omit=dev`. A antiga instalação por URL Git (`git+https://github.com/...`) não é mais suportada: o repositório não versiona `dist/`.
76
-
77
- No ponto de entrada da sua aplicação (`main.ts`, `index.js` ou `App.tsx`):
78
-
79
- ```typescript
80
- // 1. Estilos globais obrigatórios (tokens CSS)
81
- import '@thibezer/ui-components-kit/style.css';
82
-
83
- // 2. Biblioteca completa (ou escolha os submódulos abaixo)
84
- import '@thibezer/ui-components-kit';
85
- ```
86
-
87
- #### 🚀 Importação Modular e Otimizada (Sub-paths)
88
-
89
- Pague apenas pelo que utilizar. Importe por domínio de funcionalidade ou por componente específico:
90
-
91
- O export map expõe explicitamente os pontos de entrada canônicos (ESM em `.js`, CommonJS em `.cjs` e tipos `.d.ts`):
92
-
93
- | Subpath | Arquivo (ESM) |
94
- |---|---|
95
- | `@thibezer/ui-components-kit` | `dist/index.js` |
96
- | `@thibezer/ui-components-kit/camadas` | `dist/camadas.js` |
97
- | `@thibezer/ui-components-kit/forms` | `dist/forms.js` |
98
- | `@thibezer/ui-components-kit/feedback` | `dist/feedback.js` |
99
- | `@thibezer/ui-components-kit/tabela` | `dist/tabela.js` |
100
- | `@thibezer/ui-components-kit/style.css` | `dist/ui-kit.css` (100% autocontido, sem `@import`) |
101
-
102
- ##### 1. Por Domínio / Categoria:
103
- ```typescript
104
- import '@thibezer/ui-components-kit/forms'; // Campos, Select, Checkbox, Radio, Switch (~8 kB gzip)
105
- import '@thibezer/ui-components-kit/feedback'; // Modais, Drawers, Alertas, Toasts, Tooltips, Skeletons (~6 kB gzip)
106
- import '@thibezer/ui-components-kit/data'; // Tabela, Tabela-Propriedades, Stat KPI, Badge (~4 kB gzip)
107
- import '@thibezer/ui-components-kit/tools'; // Ribbon CAD/Office e Paleta de Ferramentas (~10 kB gzip)
108
- import '@thibezer/ui-components-kit/core'; // Apenas UIBus, Zero-JS e ListenerBag (< 1 kB gzip)
109
- import '@thibezer/ui-components-kit/canvas'; // Mesa CAD vetorial e motor GIS (~33 kB gzip)
110
- import '@thibezer/ui-components-kit/camadas'; // Painel de Camadas GIS/CAD redimensionável (~21 kB gzip)
111
- import '@thibezer/ui-components-kit/mapa'; // Mapa Geográfico Leaflet (~3 kB gzip)
112
- ```
113
-
114
- ##### 2. Granular por Componente (Ultra Leve):
115
- ```typescript
116
- import '@thibezer/ui-components-kit/botao'; // Apenas <ui-botao> e <ui-botao-primario> (~3.4 kB gzip)
117
- import '@thibezer/ui-components-kit/campo-texto'; // Apenas <ui-campo-texto> com FACE e anti-zoom (~3.9 kB gzip)
118
- import '@thibezer/ui-components-kit/modal'; // Apenas <ui-modal> com bottom-sheet mobile (~3.3 kB gzip)
119
- import '@thibezer/ui-components-kit/card'; // Apenas <ui-card> com elevação (~2.3 kB gzip)
120
- import '@thibezer/ui-components-kit/tabela'; // Apenas <ui-tabela> virtualizada
121
- ```
122
-
123
- > **Zero Overhead**: Uma tela que utilize apenas `<ui-botao>` e `<ui-campo-texto>` importa menos de **8 kB gzip**, sem carregar nada de Leaflet, CAD, GIS ou virtualização.
124
-
125
- ---
126
-
127
- ## 🎛️ Como Controlar os Componentes (5 Pilares)
128
-
129
- ```text
130
- ┌─────────────────────────────────────────────────────────────┐
131
- │ SISTEMA EXTERNO │
132
- └──────┬───────────────▲─────────────────┬─────────────────▲──┘
133
- │ │ │ │
134
- 1. Atributos 4. Eventos Custom 2. Propriedades 3. Métodos
135
- (HTML) (ui-*) (Objetos/Listas) (Imperativo)
136
- │ │ │ │
137
- ┌──────▼───────────────┴─────────────────▼─────────────────┴──┐
138
- │ WEB COMPONENT (<ui-*>) │
139
- └─────────────────────────────────────────────────────────────┘
140
- ```
141
-
142
- ### 1. Atributos HTML (Controle Declarativo)
143
- Configurações simples passadas diretamente nas tags:
144
- ```html
145
- <ui-campo-texto label="CPF" placeholder="000.000.000-00" obrigatorio></ui-campo-texto>
146
- <ui-modal id="meu-modal" titulo="Confirmar Exclusão"></ui-modal>
147
- ```
148
-
149
- ---
150
-
151
- ### 📏 Ajuste de Altura e Dimensionamento Inteligente (Zero Atrito)
152
-
153
- Os campos de texto (`<ui-campo-texto>`), listas flutuantes/selects (`<ui-lista-flutuante>`, `<ui-select>`) e botões (`<ui-botao-primario>`) possuem 4 formas flexíveis de ajuste de altura:
154
-
155
- #### 1. Via Atributo de Tamanho Predefinido (`tamanho` ou `densidade`):
156
- ```html
157
- <!-- Compacto (26px) - Ideal para Toolbars CAD/GIS, tabelas e alta densidade -->
158
- <ui-campo-texto tamanho="sm" placeholder="26px Compacto"></ui-campo-texto>
159
- <ui-lista-flutuante tamanho="sm" placeholder="Selecione..."></ui-lista-flutuante>
160
-
161
- <!-- Padrão (34px) - Padrão moderno e equilibrado para formulários e modais -->
162
- <ui-campo-texto tamanho="md" placeholder="34px Padrão"></ui-campo-texto>
163
- <ui-lista-flutuante tamanho="md" placeholder="Selecione..."></ui-lista-flutuante>
164
-
165
- <!-- Conforto / Touch (42px) - Otimizado para dispositivos móveis e telas sensíveis ao toque -->
166
- <ui-campo-texto tamanho="lg" placeholder="42px Conforto"></ui-campo-texto>
167
- <ui-lista-flutuante tamanho="lg" placeholder="Selecione..."></ui-lista-flutuante>
168
- ```
169
-
170
- #### 2. Via Atributo de Altura Explícita (`altura` ou `height`):
171
- ```html
172
- <!-- Define a altura exata em pixels diretamente no elemento -->
173
- <ui-campo-texto altura="38" placeholder="Altura de 38px"></ui-campo-texto>
174
- <ui-lista-flutuante altura="44px" placeholder="Altura de 44px"></ui-lista-flutuante>
175
- <ui-botao-primario altura="40">Salvar</ui-botao-primario>
176
- ```
177
-
178
- #### 3. Via Variável CSS Customizável (`--ui-campo-altura`):
179
- ```css
180
- /* No seu CSS global ou no container pai */
181
- .meu-formulario {
182
- --ui-campo-altura: 36px; /* Aplica instantaneamente em todos os inputs, selects e botões filhos */
183
- }
184
- ```
185
-
186
- #### 4. Via Barramento Global de Densidade (`UIBus`):
187
- ```javascript
188
- import { UIBus } from '@thibezer/ui-components-kit';
189
-
190
- // Altera a densidade de toda a aplicação em tempo real:
191
- UIBus.definirDensidade('compacta'); // 26px
192
- UIBus.definirDensidade('padrao'); // 34px
193
- UIBus.definirDensidade('relaxada'); // 42px
194
- UIBus.definirDensidade(38); // Valor numérico em px customizado
195
- ```
196
-
197
- ---
198
-
199
- ### 2. Propriedades JavaScript (Dados Complexos)
200
- Para enviar arrays e objetos complexos:
201
- ```javascript
202
- const tabela = document.querySelector('ui-tabela');
203
- tabela.colunas = [
204
- { id: 'ponto', rotulo: 'Ponto', ordenavel: true },
205
- { id: 'altitude', rotulo: 'Altitude (m)' }
206
- ];
207
- tabela.dados = [
208
- { ponto: 'P-01', altitude: '542,15' },
209
- { ponto: 'P-02', altitude: '545,30' }
210
- ];
211
- ```
212
-
213
- ### 3. Métodos Imperativos (Ações Diretas)
214
- Comandos diretos chamados na instância do elemento:
215
- ```javascript
216
- const modal = document.getElementById('meu-modal');
217
- modal.abrir();
218
- modal.fechar();
219
- ```
220
-
221
- ### 4. Eventos Customizados Nativos (`ui-*`)
222
- Escuta de eventos disparados pelas interações do usuário:
223
- ```javascript
224
- const campo = document.querySelector('ui-campo-texto');
225
- campo.addEventListener('ui-input', (e) => {
226
- console.log('Texto digitado:', e.detail.value);
227
- });
228
- ```
229
-
230
- > **Mutação de usuário vs. programática (`<ui-camadas>`)**: eventos saem do componente **somente** em resposta a interações físicas (clique, drag-and-drop, teclado). Atribuir `painel.layers = [...]`, alterar atributos (`camada-ativa`, `colapsado`, `mapa-base-ativo`...) ou chamar métodos públicos (`definirCamadaAtiva`, `selecionarFeicao`, `limparSelecao`, `colapsar`, `definirAltura`...) apenas atualiza o DOM interno — sem redisparar eventos. Isso elimina loops de sincronização em apps reativos (React, Vue, Svelte). Para forçar a emissão numa chamada programática, passe `emitirEvento = true` (ex.: `painel.definirCamadaAtiva('lotes', true)`).
231
-
232
- ### 5. Serviços Globais e Helpers
233
- Utilitários prontos para uso sem necessidade de instanciar elementos:
234
- ```javascript
235
- import { UIToast, UIBus } from '@thibezer/ui-components-kit';
236
-
237
- UIToast.notificar({
238
- tipo: 'sucesso',
239
- titulo: 'Registro Salvo!',
240
- mensagem: 'Os dados foram sincronizados.',
241
- duracao: 4000
242
- });
243
- ```
244
-
245
- ### 6. Escala de Elevação (z-index)
246
- Todos os componentes flutuantes consomem tokens de elevação previsíveis (com fallback), definidos em `ui-kit.css`. Sobrescreva-os no `:root` da aplicação para encaixar a pilha do kit entre os HUDs e modais do seu app:
247
- ```css
248
- :root {
249
- --ui-z-workspace-hud: 500; /* <ui-camadas flutuante>, <ui-seletor-mapa-base>, toolbars do canvas */
250
- --ui-z-docked-bar: 600; /* <ui-ribbon> recolhido, <ui-tabela-propriedades flutuante> */
251
- --ui-z-drawer: 700; /* <ui-drawer> e backdrop */
252
- --ui-z-modal: 1000; /* <ui-modal> e backdrop */
253
- --ui-z-popover: 1050; /* menus suspensos/contexto, <ui-select> (acima do modal, funciona dentro dele) */
254
- --ui-z-toast: 1100; /* UIToast */
255
- --ui-z-tooltip: 1200; /* <ui-tooltip> */
256
- }
257
- ```
258
-
259
- ### 7. Acessibilidade Nativa de Rótulos
260
- `<ui-input>` (alias de `<ui-campo-texto>`), `<ui-select>` e `<ui-switch>` geram IDs únicos internamente e associam o rótulo visível ao controle nativo do Shadow DOM — o projeto consumidor não precisa gerenciar IDs:
261
- ```html
262
- <ui-input label="Nome"></ui-input> <!-- <label for> ↔ <input> interno -->
263
- <ui-select label="Cidade">...</ui-select> <!-- aria-labelledby = rótulo + valor atual -->
264
- <ui-switch label="Modo escuro"></ui-switch> <!-- role="switch" aria-labelledby -->
265
-
266
- <!-- Sem o atributo label, o nome acessível vem de fora do Shadow DOM: -->
267
- <label>Telefone <ui-input></ui-input></label>
268
- <ui-switch aria-label="Camada visível"></ui-switch>
269
- ```
270
-
271
- ### 8. Temas Claro e Escuro
272
- Sem configuração, o kit **segue o tema do sistema operacional** (`prefers-color-scheme`). Para fixar um tema, use o atributo na raiz — ou em qualquer elemento, para fixar só aquele trecho:
273
- ```html
274
- <html data-tema="escuro"> <!-- ou "claro"; também aceitos: class="dark|light" e data-theme="dark|light" -->
275
- <aside data-tema="claro">…</aside> <!-- trecho claro dentro de uma página escura -->
276
- ```
277
- ```javascript
278
- UIBus.definirTema('auto'); // volta a seguir o SO
279
- UIBus.on('tema:alterado', ({ tema }) => { /* também dispara quando o SO muda, se o tema não estiver fixado */ });
280
- ```
281
- Os componentes recebem o tema **apenas pelos tokens de cor** (`--ui-cor-*`, `--ui-sombra-*`), que atravessam o Shadow DOM. Todo token de texto atinge contraste **WCAG AA (4,5:1)** nos dois temas, inclusive sobre fundos tingidos de status e no texto sobre a cor primária e o botão destrutivo — verificado em `tests/tema.test.ts`.
282
-
283
- | Token | Uso |
284
- | :--- | :--- |
285
- | `--ui-cor-texto-sucesso` / `-erro` / `-alerta` / `-info` | Texto e tons de status (alertas, badges, toasts) |
286
- | `--ui-cor-texto-sobre-status` | Texto sobre fundo sólido de status (badge sólido) |
287
- | `--ui-cor-fundo-recuado` | Faixas rebaixadas (rodapés de modal e drawer) |
288
- | `--ui-sombra-sm` / `-md` / `-lg` | Sombras: densas no escuro, suaves no claro |
289
-
290
- ### 9. Tipografia e Raios
291
- Todos os componentes usam apenas dois tokens de família de fonte e uma escala de raios derivada de `--ui-raio-borda`. Sobrescreva no `:root` (a escala é calculada lá):
292
- ```css
293
- :root {
294
- --ui-fonte-base: 'Roboto', sans-serif; /* textos da interface */
295
- --ui-fonte-codigo: 'JetBrains Mono', monospace; /* coordenadas, valores técnicos, código */
296
-
297
- --ui-raio-borda: 4px; /* base: botões, campos, menus */
298
- /* derivados automaticamente: --ui-raio-sm (2/3), --ui-raio-lg (5/3), --ui-raio-xl (8/3) */
299
- /* --ui-raio-borda: 0 → interface totalmente reta */
300
- }
301
- ```
302
-
303
- ### 10. Estilização Interna com `::part`
304
- O Shadow DOM isola o CSS, mas os elementos internos principais são expostos com `part`, com nomes consistentes entre componentes (uma regra serve para vários):
305
-
306
- | Componente | Parts |
307
- | :--- | :--- |
308
- | `<ui-botao>` / `<ui-botao-primario>` | `base`, `spinner` |
309
- | `<ui-campo-texto>` / `<ui-input>` | `rotulo`, `campo`, `input`, `icone`, `icone-esquerda`, `icone-direita`, `ajuda` |
310
- | `<ui-lista-flutuante>` / `<ui-select>` | `rotulo`, `campo`, `valor`, `seta`, `lista`, `opcao`, `opcao-selecionada` |
311
- | `<ui-checkbox>`, `<ui-radio>`, `<ui-switch>` | `base`, `controle`, `indicador`, `rotulo` |
312
- | `<ui-segmented>` | `base`, `indicador`, `opcao`, `opcao-selecionada` |
313
- | `<ui-modal>`, `<ui-drawer>` | `fundo`, `painel`, `cabecalho`, `titulo`, `descricao` (drawer), `fechar`, `corpo`, `rodape` |
314
- | `<ui-card>` | `base`, `midia`, `cabecalho`, `corpo`, `rodape` |
315
- | `<ui-badge>` / `<ui-chip>` | `base`, `rotulo`, `remover` |
316
- | `<ui-alerta>` / toasts | `base`, `icone`, `conteudo`, `titulo`, `mensagem`, `acao`, `fechar`, `progresso` |
317
- | `<ui-tooltip>` | `balao`, `texto`, `seta` |
318
- | `<ui-tabela>` | `base`, `tabela`, `celula-cabecalho`, `ordenar`, `linha`, `linha-selecionada`, `celula`, `vazio`, `carregando` |
319
-
320
- ```css
321
- ui-campo-texto::part(campo) { border-width: 2px; }
322
- ui-modal::part(painel) { max-width: 720px; }
323
- ui-tabela::part(linha-selecionada) { background: #0b3d2a; }
324
- :is(ui-checkbox, ui-radio, ui-switch)::part(rotulo) { font-weight: 600; }
325
- ```
326
-
327
- ---
328
-
329
- ## ⚡ Recursos Inteligentes de Alta Produtividade
330
-
331
- ### 1. Ações Declarativas "Zero-JS" (Triggers Nativos)
332
- Você pode acionar modais, fechar janelas, disparar notificações toast ou copiar textos **sem escrever nenhuma linha de JavaScript**, apenas adicionando atributos nas tags HTML:
333
-
334
- ```html
335
- <!-- Abrir Modal sem JS -->
336
- <ui-botao-primario target-modal="modal-exclusao" variante="destrutivo">
337
- Excluir Registro
338
- </ui-botao-primario>
339
-
340
- <!-- Modal com Fechamento e Toast automáticos -->
341
- <ui-modal id="modal-exclusao" titulo="Confirmação">
342
- Tem certeza que deseja excluir?
343
- <div slot="rodape">
344
- <ui-botao-primario dismiss-modal variante="secundario">Cancelar</ui-botao-primario>
345
- <ui-botao-primario dismiss-modal toast-sucesso="Excluído com sucesso!" variante="destrutivo">
346
- Confirmar
347
- </ui-botao-primario>
348
- </div>
349
- </ui-modal>
350
-
351
- <!-- Copiar Texto em 1 Clique -->
352
- <ui-campo-texto id="input-token" value="SIGEF-2026-X88"></ui-campo-texto>
353
- <ui-botao-primario copiar-texto="#input-token" copiar-mensagem="Token copiado!">
354
- Copiar Chave
355
- </ui-botao-primario>
356
- ```
357
-
358
- #### Tabela de Atributos Zero-JS Disponíveis:
359
- | Atributo | Descrição | Exemplo de Uso |
360
- | :--- | :--- | :--- |
361
- | `target-modal="id"` | Abre o modal com o ID especificado ao clicar. | `target-modal="modal-login"` |
362
- | `dismiss-modal` | Fecha o modal atual (ou o especificado por ID). | `dismiss-modal` |
363
- | `toast-sucesso="msg"` | Dispara uma notificação flutuante de sucesso. | `toast-sucesso="Salvo com sucesso!"` |
364
- | `toast-erro="msg"` | Dispara uma notificação flutuante de erro. | `toast-erro="Falha na conexão."` |
365
- | `toast-alerta="msg"` | Dispara uma notificação flutuante de aviso. | `toast-alerta="Preencha os campos obrigatórios."` |
366
- | `copiar-texto="texto\|#id"` | Copia o texto ou o valor do elemento referenciado. | `copiar-texto="#campo-pix"` |
367
- | `limpar-form="id"` | Reseta todos os campos do formulário indicado. | `limpar-form="form-cadastro"` |
368
- | `alternar-tema` | Alterna entre tema claro e escuro globalmente (a partir do tema em vigor, inclusive o do SO). | `alternar-tema` |
369
-
370
- ---
371
-
372
- ### 2. Formulários Automáticos com `FormData` Nativo (W3C FACE)
373
- Todos os campos (`<ui-campo-texto>`, `<ui-lista-flutuante>`, `<ui-checkbox>`, `<ui-radio>`, `<ui-switch>`) implementam a especificação oficial de **Form-Associated Custom Elements**.
374
-
375
- Isso significa que você extrai os dados de todos os campos de uma única vez, sem ler um por um:
376
-
377
- ```html
378
- <form id="form-imovel">
379
- <ui-campo-texto name="denominacao" label="Nome da Fazenda" value="Boa Vista"></ui-campo-texto>
380
- <ui-campo-texto name="municipio" label="Município" value="Uberlândia"></ui-campo-texto>
381
- <ui-checkbox name="georreferenciado" label="Georreferenciado" marcado value="sim"></ui-checkbox>
382
-
383
- <ui-botao-primario type="submit">Salvar Imóvel</ui-botao-primario>
384
- </form>
385
- ```
386
-
387
- ```javascript
388
- const form = document.getElementById('form-imovel');
389
-
390
- form.addEventListener('submit', (e) => {
391
- e.preventDefault();
392
-
393
- // Extrai TODOS os campos em 1 única linha nativa:
394
- const dados = Object.fromEntries(new FormData(form));
395
- console.log(dados);
396
- // Resultado: { denominacao: "Boa Vista", municipio: "Uberlândia", georreferenciado: "sim" }
397
- });
398
- ```
399
-
400
- Assim como nos campos nativos, **Enter** em um `<ui-campo-texto>` submete o formulário pelo botão de envio padrão (`<ui-botao-primario type="submit">` ou `<button>`), que também pode estar fora do `<form>` com `form="id-do-form"`. Para impedir, chame `preventDefault()` no `keydown`. O `name`/`value` do botão que submeteu chega como `e.submitter` (use `new FormData(form, e.submitter)`), e gatilhos Zero-JS em botões `disabled` ou `carregando` são ignorados.
401
-
402
- ---
403
-
404
- ### 3. Barramento Global de Orquestração (`UIBus`)
405
- O `UIBus` é uma central reativa que permite comandar qualquer componente do sistema ou ouvir eventos sem precisar de `querySelector`:
406
-
407
- ```javascript
408
- import { UIBus } from '@thibezer/ui-components-kit';
409
-
410
- // 1. Executar Ações Diretas:
411
- UIBus.abrirModal('modal-confirmacao');
412
- UIBus.fecharModal('modal-confirmacao');
413
- UIBus.notificar({ tipo: 'sucesso', mensagem: 'Operação concluída!' });
414
- UIBus.copiar('Texto para a área de transferência', 'Copiado!');
415
- UIBus.definirDensidade('compacta'); // 'compacta' | 'normal' | 'relaxada'
416
- UIBus.definirTema('escuro'); // 'claro' | 'escuro' | 'auto' (segue o SO); sem argumento, alterna
417
- UIBus.obterTema(); // tema em vigor: 'claro' | 'escuro'
418
-
419
- // 2. Comunicação Desacoplada (Pub/Sub):
420
- UIBus.on('vertice:selecionado', (dadosVertice) => {
421
- console.log('Vértice selecionado em qualquer tela:', dadosVertice);
422
- });
423
-
424
- UIBus.emit('vertice:selecionado', { id: 101, nome: 'M-01', altitude: 432.10 });
425
- ```
426
-
427
- ---
428
-
429
- ### 4. Tabelas Autônomas com Auto-Fetch Remoto
430
- A `<ui-tabela>` pode buscar dados de uma API remota de forma totalmente autônoma, exibindo indicador de loading, tratando erros e permitindo filtragem instantânea:
431
-
432
- ```html
433
- <ui-tabela
434
- id="tabela-vertices"
435
- src="/api/v1/geodesia/vertices"
436
- max-height="400px"
437
- densidade="compacta">
438
- </ui-tabela>
439
- ```
440
-
441
- ```javascript
442
- const tabela = document.getElementById('tabela-vertices');
443
-
444
- // Definir colunas da tabela:
445
- tabela.colunas = [
446
- { id: 'nome_vertice', rotulo: 'Vértice', ordenavel: true },
447
- { id: 'este', rotulo: 'Este (m)', ordenavel: true, alinhamento: 'direita' },
448
- { id: 'norte', rotulo: 'Norte (m)', ordenavel: true, alinhamento: 'direita' }
449
- ];
450
-
451
- // Métodos inteligentes disponíveis na tabela:
452
- tabela.filtrar('M-01'); // Filtra instantaneamente em todas as colunas
453
- tabela.recarregar(); // Recarrega os dados da API remota
454
- ```
455
-
456
- **Autenticação e opções do `fetch`** (o `src` sozinho faz um `GET` sem headers):
457
-
458
- ```javascript
459
- tabela.fetchOpcoes = { headers: { Authorization: `Bearer ${token}` }, credentials: 'include' };
460
- // ou troque a função de busca (renovação de token, mocks):
461
- tabela.fetcher = (url, init) => meuFetchAutenticado(url, init);
462
-
463
- const ok = await tabela.carregarDoEndpoint('/api/v1/vertices'); // true em sucesso; nunca rejeita
464
- ```
465
-
466
- **Ordenação no servidor**: `<ui-tabela ordenacao-remota>` (ou `tabela.ordenacaoRemota = true`) atualiza o indicador e emite `ui-sort`, mas não reordena; busque os dados já ordenados. Sem o atributo, chame `evento.preventDefault()` em `ui-sort` para recusar a ordenação.
467
-
468
- **Eventos de `<ui-tabela>`** (todos `bubbles` e `composed`; só `ui-sort` é cancelável):
469
-
470
- | Evento | `detail` | Quando |
471
- |---|---|---|
472
- | `ui-sort` (cancelável) | `{ idColuna, direcao }` | Clique no cabeçalho ordenável. `direcao: 'original'` com `idColuna: null` = ordenação removida |
473
- | `ui-column-resize` | `{ idColuna, largura }` (`"120px"` ou `"auto"`) | Fim do arraste, popover ou duplo clique; não dispara em clique sem arrastar |
474
- | `ui-linha-clique` | `{ item, indice }` | Clique/Enter/Espaço numa linha. `indice` é a posição **exibida** (após ordenar/filtrar): use `item` |
475
- | `ui-linha-selecionada` | `{ item, indice }` | `rolarPara(..., { selecionar: true })` |
476
- | `ui-selecao-removida` | `{ item }` | O item selecionado saiu dos `dados` |
477
- | `ui-selecao-atualizada` | `{ item, anterior }` | Os dados foram recarregados e o registro selecionado agora é outro objeto |
478
- | `ui-fetch-start` | `{ url }` | Início da requisição do `src` |
479
- | `ui-fetch-sucesso` | `{ url, total, dados }` | Dados recebidos |
480
- | `ui-fetch-erro` | `{ url, erro, origem, status? }` | Falha. `origem`: `rede`, `http` (com `status`), `json` ou `render` |
481
- | `ui-fetch-cancelado` | `{ url }` | Requisição abortada ou substituída (fecha o par do `ui-fetch-start`) |
482
-
483
- Boas práticas: trate `dados` como imutável (atribua sempre um array novo), mantenha `chave-id` única e não nula, memorize `colunas` (atribuí-las de novo refaz a tabela) e coloque controles interativos das células com `data-prevent-select` se não quiser que o clique selecione a linha. `render` devolvendo `string` vira texto (não HTML); devolva um `HTMLElement` novo por chamada.
484
-
485
- ---
486
-
487
- ## 💻 Exemplos de Uso por Tecnologia
488
-
489
- ### HTML Puro + JavaScript
490
- ```html
491
- <!DOCTYPE html>
492
- <html lang="pt-BR">
493
- <head>
494
- <link rel="stylesheet" href="dist/ui-kit.css">
495
- <script type="module" src="dist/index.js"></script>
496
- </head>
497
- <body>
498
- <ui-campo-texto id="meu-campo" label="Nome do Projeto"></ui-campo-texto>
499
- <ui-botao-primario target-modal="meu-modal">Abrir Detalhes</ui-botao-primario>
500
-
501
- <ui-modal id="meu-modal" titulo="Detalhes">
502
- <p>Conteúdo do modal...</p>
503
- <div slot="rodape">
504
- <ui-botao-primario dismiss-modal>Fechar</ui-botao-primario>
505
- </div>
506
- </ui-modal>
507
-
508
- <script>
509
- document.getElementById('meu-campo').addEventListener('ui-input', (e) => {
510
- console.log('Digitado:', e.detail.value);
511
- });
512
- </script>
513
- </body>
514
- </html>
515
- ```
516
-
517
- ---
518
-
519
- ### React / Next.js (SSR & Client Components)
520
-
521
- A biblioteca é **100% SSR-safe**: todas as classes herdam de `SafeHTMLElement` e o registro de tags utiliza verificações de segurança para ambientes sem DOM (Node.js). Isso garante que você pode importar tipos, classes e constantes tanto no servidor quanto no cliente sem encontrar `ReferenceError: HTMLElement is not defined` ou `customElements is not defined`.
522
-
523
- #### Tipos no TSX (autocompletar e checagem)
524
- O pacote gera tipos para todas as tags a partir do Custom Elements Manifest. Registre-os uma vez no `tsconfig.json`:
525
-
526
- ```json
527
- { "compilerOptions": { "types": ["@thibezer/ui-components-kit/tipos-react"] } }
528
- ```
529
-
530
- A partir daí o editor sugere atributos, propriedades e eventos de cada tag, e o TypeScript recusa valores inválidos:
531
-
532
- ```tsx
533
- <ui-tabela
534
- colunas={[{ id: 'ponto', rotulo: 'Ponto', ordenavel: true }]} // tipado como TabelaColuna[]
535
- densidade="compacta" // 'compacta' | 'normal' | 'relaxada'
536
- onui-sort={(e) => console.log(e.detail)} // eventos ui-* no formato do React 19
537
- />
538
- <ui-tabela densidade="enorme" /> // ❌ erro de tipo
539
- ```
540
-
541
- Outros JSX (Preact, Solid etc.) podem usar o mapa agnóstico `ElementosUI` de `@thibezer/ui-components-kit/tipos-elementos`.
542
-
543
- #### No Next.js (App Router):
544
-
545
- Crie um componente cliente provedor ou registre no topo do seu layout/página cliente:
546
-
547
- ```tsx
548
- // components/UIProvider.tsx
549
- 'use client';
550
-
551
- import { useEffect } from 'react';
552
- import '@thibezer/ui-components-kit/style.css';
553
- import '@thibezer/ui-components-kit/register'; // Registra todos os Custom Elements com segurança no cliente
554
-
555
- export function UIProvider({ children }: { children: React.ReactNode }) {
556
- return <>{children}</>;
557
- }
558
- ```
559
-
560
- Ou registre de forma modular apenas o que a tela precisa:
561
-
562
- ```tsx
563
- // app/dashboard/page.tsx
564
- 'use client';
565
-
566
- import React, { useEffect, useRef, useState } from 'react';
567
- import '@thibezer/ui-components-kit/style.css';
568
- import '@thibezer/ui-components-kit/forms';
569
- import '@thibezer/ui-components-kit/botao';
570
- import '@thibezer/ui-components-kit/tabela';
571
- import { UIBus } from '@thibezer/ui-components-kit/core';
572
- import type { UITabela } from '@thibezer/ui-components-kit/tabela';
573
-
574
- export default function DashboardPropriedades() {
575
- const tabelaRef = useRef<UITabela>(null);
576
- const [nome, setNome] = useState('');
577
-
578
- useEffect(() => {
579
- if (tabelaRef.current) {
580
- tabelaRef.current.colunas = [
581
- { id: 'id', rotulo: 'Código' },
582
- { id: 'nome', rotulo: 'Propriedade' }
583
- ];
584
- tabelaRef.current.dados = [
585
- { id: 1, nome: 'Fazenda Santa Luzia' }
586
- ];
587
- }
588
- }, []);
589
-
590
- return (
591
- <div style={{ padding: 24 }}>
592
- <ui-campo-texto
593
- label="Pesquisar Imóvel"
594
- value={nome}
595
- onInput={(e: any) => {
596
- // Os eventos nativos `input`/`change` não têm `detail`: leia o valor do próprio elemento
597
- const valor = e.target.value;
598
- setNome(valor);
599
- tabelaRef.current?.filtrar(valor);
600
- }}
601
- ></ui-campo-texto>
602
-
603
- <ui-tabela ref={tabelaRef} max-height="300px" densidade="compacta"></ui-tabela>
604
-
605
- <ui-botao-primario
606
- variante="primary"
607
- onClick={() => UIBus.notificar({ tipo: 'sucesso', mensagem: 'Dados sincronizados!' })}>
608
- Sincronizar
609
- </ui-botao-primario>
610
- </div>
611
- );
612
- }
613
- ```
614
-
615
- ---
616
-
617
- ### Vue.js
618
-
619
- Para autocompletar e checar props e eventos nos templates (Volar), registre os tipos no `tsconfig.json` e informe ao Vue que tags `ui-*` são custom elements:
620
-
621
- ```json
622
- { "compilerOptions": { "types": ["@thibezer/ui-components-kit/tipos-vue"] } }
623
- ```
624
-
625
- ```javascript
626
- // vite.config.js
627
- vue({ template: { compilerOptions: { isCustomElement: (tag) => tag.startsWith('ui-') } } })
628
- ```
629
-
630
- ```vue
631
- <template>
632
- <div class="container">
633
- <ui-campo-texto
634
- :value="termoBusca"
635
- label="Buscar Vértice"
636
- @ui-input="aoDigitar"
637
- />
638
-
639
- <ui-lista-flutuante
640
- .itens="opcoes"
641
- @ui-selecionar="aoSelecionar"
642
- />
643
-
644
- <ui-botao-primario target-modal="modal-confirmacao">
645
- Confirmar Operação
646
- </ui-botao-primario>
647
- </div>
648
- </template>
649
-
650
- <script setup>
651
- import { ref } from 'vue';
652
- import '@thibezer/ui-components-kit/style.css';
653
- import '@thibezer/ui-components-kit';
654
-
655
- const termoBusca = ref('');
656
- const opcoes = ref([
657
- { id: '1', label: 'Marco Geodésico M' },
658
- { id: '2', label: 'Ponto Topográfico P' }
659
- ]);
660
-
661
- function aoDigitar(e) {
662
- termoBusca.value = e.detail.value;
663
- }
664
-
665
- function aoSelecionar(e) {
666
- console.log('Opção selecionada:', e.detail);
667
- }
668
- </script>
669
- ```
670
-
671
- ---
672
-
673
- ### Iframes / Microfrontends
674
- Comunicação segura entre iframes e sistemas pais via eventos do `UIBus`:
675
-
676
- ```javascript
677
- // Dentro do Iframe (Filho):
678
- import { UIBus } from '@thibezer/ui-components-kit';
679
-
680
- UIBus.on('vertice:salvo', (dados) => {
681
- window.parent.postMessage({ tipo: 'VERTICE_SALVO', payload: dados }, '*');
682
- });
683
-
684
- // No Sistema Principal (Pai):
685
- window.addEventListener('message', (event) => {
686
- if (event.data?.tipo === 'VERTICE_SALVO') {
687
- console.log('Recebido do Iframe:', event.data.payload);
688
- }
689
- });
690
- ```
691
-
692
- ---
693
-
694
- ## 🧩 Catálogo de Componentes & Referência
695
-
696
- ### 🟢 Camada 1: Fundamentos
697
- | Tag | Descrição | Principais Atributos / Props | Eventos Emitidos |
698
- | :--- | :--- | :--- | :--- |
699
- | `<ui-botao-primario>` | Botão com estados de loading, ícone e variantes. Form-associated (`type="submit"`/`"reset"`, `name`/`value`, `form`). Só com ícone, informe `aria-label` (ou `title`). | `variante`, `carregando`, `disabled`, `type`, `aria-label`, `target-modal` | `ui-click` |
700
- | `<ui-campo-texto>` | Input de texto com Floating Label, validação e senha. | `label`, `value`, `tipo`, `obrigatorio`, `name` | `ui-input`, `ui-change` |
701
- | `<ui-lista-flutuante>` | Select/Dropdown com Bottom Sheet no mobile. | `itens`, `value`, `placeholder`, `name` | `ui-selecionar` |
702
- | `<ui-texto>` | Tipografia semântica (H1-H6, corpo, caption, código). | `variante`, `cor`, `peso` | - |
703
- | `<ui-icone>` | Wrapper para ícones vetoriais padronizados. | `nome`, `tamanho`, `cor` | - |
704
-
705
- ### 🔵 Camada 2: Controles & Seleção
706
- | Tag | Descrição | Principais Atributos / Props | Eventos Emitidos |
707
- | :--- | :--- | :--- | :--- |
708
- | `<ui-checkbox>` | Caixa de seleção múltipla com estado indeterminado. | `marcado`, `indeterminado`, `name`, `value` | `ui-change` |
709
- | `<ui-radio>` | Opção única com agrupamento automático por nome. | `marcado`, `name`, `value` | `ui-change` |
710
- | `<ui-switch>` | Chave de alternância liga/desliga física instantânea. | `ativo`, `name`, `value` | `ui-change` |
711
- | `<ui-segmented>` | Controle segmentado de opções (Pills/Tabs deslizantes). | `opcoes`, `value`, `name`, `tamanho` | `ui-change`, `ui-selecionar` |
712
- | `<ui-badge>` / `<ui-chip>` | Tags de status e chips com botão de remoção. | `variante`, `removivel`, `value` | `ui-remove` |
713
- | `<ui-avatar>` | Avatar com fotos, iniciais e status online. | `nome`, `src`, `status`, `tamanho` | - |
714
-
715
- ### 🟣 Camada 3: Contêineres, Overlays, Dados & GIS
716
- | Tag | Descrição | Principais Atributos / Props | Eventos Emitidos |
717
- | :--- | :--- | :--- | :--- |
718
- | `<ui-card>` | Cartão de conteúdo com slots nomeados e elevação. | `elevacao`, `clicavel` | `ui-click` |
719
- | `<ui-modal>` | Modal centralizado no PC e Bottom Sheet no Mobile. Abrir/fechar por `abrir()`/`fechar()` ou pelo atributo `aberto` tem o mesmo efeito (eventos, foco, `inert`). Foco inicial no `[autofocus]` ou no primeiro controle do conteúdo; ao fechar, volta ao gatilho. Modais empilhados: só o último aberto é interativo. | `aberto`, `titulo`, `bottom-sheet`, `bloquear-fechamento` (oculta o "✕" e ignora Esc/fundo) | `ui-abrir`, `ui-fechar` |
720
- | `<ui-drawer>` | Painel lateral deslizante (Sheet/Gaveta direita/esquerda). | `aberto`, `posicao` (`direita`, `esquerda`, `baixo`, `cima`), `titulo`, `largura` | `ui-abrir`, `ui-fechar` |
721
- | `<ui-alerta>` | Banner de notificação contextual fixo. | `variante`, `titulo`, `fechavel` | `ui-fechar` |
722
- | `<ui-toast>` | Notificações flutuantes inteligentes com pilha flex. | `UIToast.notificar({ tipo, mensagem })` | - |
723
- | `<ui-tooltip>` | Dica contextual inteligente (Hover PC / Toque Mobile). O texto vira `aria-description` do gatilho (lido pelo leitor de tela, também repassado pelo `<ui-botao>`); **Esc** fecha sem mover o foco; o balão não some ao levar o ponteiro até ele. | `texto`, `posicao`, `gatilho` | - |
724
- | `<ui-skeleton>` | Placeholder de carregamento animado com pulsos. | `tipo`, `largura`, `altura`, `animado` | - |
725
- | `<ui-stat>` | Cartão KPI de estatísticas, métricas e tendências. | `rotulo`, `valor`, `variacao`, `tendencia` (`alta`, `baixa`, `neutro`) | - |
726
- | `<ui-tabela>` | Tabela orientada a dados, virtualizada tipo Excel. **Teclado**: cabeçalhos ordenáveis são botões (Tab + Enter/Espaço, com `aria-sort`); nas linhas, ↑/↓, Home/End e PageUp/PageDown movem o foco e Enter/Espaço selecionam (`aria-current`). A seleção é mantida ao ordenar e filtrar. Nome acessível via `aria-label`. | `colunas`, `dados`, `src`, `densidade`, `aria-label` | `ui-sort`, `ui-column-resize`, `ui-linha-clique` (demais eventos: ver seção 4) |
727
- | `<ui-tabela-propriedades>` | Inspetor de propriedades técnicas padrão AutoCAD & Revit com cálculos matemáticos inline (+, -, *, /, ^, %), arraste de valor contínuo (scrubbing), splitter redimensionável e editores CAD. | `categorias`, `tipo-objeto`, `splitter-pos`, `modo-aplicar`, `filtro` | `ui-propriedade-alterada`, `ui-aplicar`, `ui-desfazer`, `ui-editar-tipo`, `ui-acao-clique`, `ui-quick-select`, `ui-calculadora` |
728
- | `<ui-ribbon>` | Barra de ferramentas em abas e grupos (estilo AutoCAD / Word / Excel): botões grandes e pequenos em colunas de 3, toggles, menus suspensos, abas contextuais, modo recolhido (duplo clique na aba) e navegação por teclado (roving tabindex). `aba-ativa` pode ser definido antes de `abas` (fica pendente); `aria-label` e `rotulo-abas` rotulam a toolbar e o tablist; `ui-ferramenta` é cancelável (`preventDefault()` reverte o toggle); `atalho` é só texto no ribbon. Use `id` únicos e `rotulo`/`rotuloAcessivel` em botões só de ícone. | `abas`, `aba-ativa`, `recolhido`, `compacto`, `aria-label`, `rotulo-abas` | `ui-ferramenta`, `ui-aba-change`, `ui-recolhido-change` |
729
- | `<ui-paleta-ferramentas>` | Paleta de ferramentas (estilo Illustrator / Photoshop): seleção exclusiva, grupos com flyout (clique longo, botão direito ou seta), 1 ou 2 colunas, vertical/horizontal e atalhos de teclado que percorrem o grupo. `valor` pode vir antes de `ferramentas` (pendente) e `valor = ''` / `limpar()` remove a seleção; `ui-ferramenta` dispara sempre e é cancelável (`preventDefault()` reverte), `ui-change`/`ui-selecionar` só quando a seleção muda (ouça um deles, não ambos). Atalhos: use `atalhos` em uma paleta por página. | `ferramentas`, `valor`, `orientacao`, `colunas`, `tamanho`, `atalhos`, `aria-label` | `ui-change`, `ui-selecionar`, `ui-ferramenta` |
730
- | `<ui-controle-cores>` | Par de cores de desenho (estilo Illustrator): preenchimento na frente, traço atrás, botões de inverter, "sem cor" e restaurar padrão, estado misto (`?`) e modo só traço. Clique no alvo ativo abre o `<ui-seletor-cor>`. Não registra atalhos: quem usa chama `alternarAlvo()`, `inverter()`, `semCor()`, `restaurarPadrao()` e pode exibir as teclas via `atalhos`. Tamanho ajustável com `--ui-controle-cores-amostra`. Entrada: `@thibezer/ui-components-kit/cores`. | `preenchimento`, `traco`, `opacidade`, `alvo`, `padrao-preenchimento`, `padrao-traco`, `titulo-seletor`, `so-traco`, `desabilitado`, `coresProjeto`, `definirCores()`, `definirMisto()` | `ui-cores-change` (`confirmado` false = pré-visualização), `ui-alvo-change` |
731
- | `<ui-seletor-cor>` | Painel de cor flutuante: abas Preenchimento/Traço (ou cor única), prévia antes × depois, hexadecimal, seletor do sistema, opacidade, cores do projeto, paleta e "sem cor". Abra com `abrirSeletorCor({ ancora, ... })`: um por vez, Esc/clique fora confirmam, Cancelar restaura, `X` alterna o alvo. | `abrirSeletorCor()`, `fecharSeletorCor()`, `configurar()`, `modo` (`duplo`, `simples`), `alvo`, `preenchimento`, `traco`, `cor`, `opacidade`, `mostrarOpacidade`, `misto`, `coresProjeto`, `paleta`, `focar` | `ui-cor-input`, `ui-cor-confirmar`, `ui-cor-cancelar`, `ui-alvo-change` |
732
- | `<ui-mapa>` | Mapa geográfico interativo com camadas OpenStreetMap. | `lat`, `lng`, `zoom`, `camadas` | - |
733
- | `<ui-camadas>` | Painel de camadas vetoriais CAD/GIS com hierarquia em árvore, barra de arraste vertical (`redimensionavel`), estabilidade de altura sem colapso, drag-and-drop, métricas geodésicas e persistência no LocalStorage. | `redimensionavel`, `altura`, `min-altura`, `max-altura`, `flutuante`, `colapsado`, `camada-ativa`, `persistir` | `ui-redimensionar-altura`, `ui-camada-selecionada`, `ui-camada-visibilidade`, `ui-camadas-reordenadas`, `ui-acao-massa` |
734
- | `<ui-canvas-cad>` | Mesa CAD/GIS para poligonais, vértices geodésicos e **feições genéricas** (ponto, linha, polígono, círculo, texto) que você cria: seleção por clique e caixa, popup, edição de vértices, snap, 15 ferramentas de desenho/medição, oficial × prévia, cursores remotos e culling/clusters para 100 mil feições. Linha multiparte, feição `oculta`, selo de texto e contorno de ponto. Nenhuma camada vem pronta. Guia: [`docs/guia-migracao-mapengine.md`](docs/guia-migracao-mapengine.md). | `pontos`, `segmentos`, `ferramentas`, `sem-camadas-padrao`, `sem-painel-camadas`, `sem-barra-rapida`, `sem-atalho-snap`, `aceitar-origem`, `inicializarAgora()`, `definirFeicoes()`, `aplicarLote()`, `definirFerramenta()`, `editarVertices()`, `definirMargensHud()`, `definirEstilosExternos()`, `fitBounds()` | `ui-ponto-selecionado`, `cad-selecao`, `cad-feicao-selecionada`, `cad-feicao-criada`, `cad-feicao-atualizada`, `cad-feicao-hover`, `cad-feicao-menu`, `cad-snap`, `cad-previas` e outros (ver `docs/canvas-cad-eventos.md`) |
735
-
736
- #### `<ui-canvas-cad>`: montar só com API pública (2.1)
737
-
738
- ```html
739
- <ui-canvas-cad id="mesa" sem-camadas-padrao sem-painel-camadas sem-barra-rapida sem-atalho-snap aceitar-origem></ui-canvas-cad>
740
- ```
741
- ```js
742
- const mapa = mesa.inicializarAgora(); // L.Map síncrono (idempotente; nunca cria um 2º mapa)
743
- mesa.definirEstilosExternos([...document.styleSheets]); // CSS do app dentro do shadow root (string ou folhas)
744
- mesa.definirMargensHud({ base: 56, direita: 320 }); // HUDs de desenho/edição fora de barras e painéis
745
- mesa.aplicarLote('glebas', { adicionar, atualizar, remover }); // uma regravação, ordem preservada
746
- mesa.atualizarFeicao('glebas', 'g1', { propriedades }, { substituirPropriedades: true });
747
- mesa.definirOpcoesPopup({ copiarParaAreaDeTransferencia: false });
748
- ```
749
-
750
- - Camadas de fábrica (satélite, SIGEF, homologados, perímetro, vizinhos, vértices, grade) só somem com `sem-camadas-padrao`; sem
751
- o atributo continuam como sempre.
752
- - `Feicao.oculta` (não desenha nem seleciona, mas existe) ≠ `Feicao.excluida`. Linha multiparte: `geometria.partes`.
753
- - Tudo, com exemplos: [`docs/guia-migracao-mapengine.md`](docs/guia-migracao-mapengine.md) · eventos e ordem das ferramentas:
754
- [`docs/canvas-cad-eventos.md`](docs/canvas-cad-eventos.md) · números de desempenho: [`docs/desempenho-feicoes.md`](docs/desempenho-feicoes.md).
755
-
756
-
757
- ---
758
-
759
- ### Custom Elements Manifest
760
- O pacote publica `custom-elements.json` (campo `customElements` do `package.json`), o formato padrão que IDEs, Storybook e geradores de documentação leem para listar tags, atributos, propriedades, eventos e `::part` de cada componente. Ele é gerado no `npm run build` pelo [`@custom-elements-manifest/analyzer`](https://custom-elements-manifest.open-wc.org/), e os tipos React/Vue são derivados dele — por isso nunca ficam desatualizados em relação ao código.
761
-
762
- ---
763
-
764
- ## 🛠️ Como Executar e Compilar Localmente
765
-
766
- 1. **Instalar Dependências**:
767
- ```bash
768
- npm install
769
- ```
770
-
771
- 2. **Executar o Playground Sandbox em Desenvolvimento**:
772
- ```bash
773
- npm run dev
774
- ```
775
- Acesse: `http://localhost:5173/`
776
-
777
- 3. **Compilar Pacote de Produção (Library Mode)**:
778
- ```bash
779
- npm run build
780
- ```
781
- Gera na pasta `dist/`:
782
- * `dist/index.js` e `dist/<modulo>.js` (Módulos ES para Vite, Webpack, Rollup)
783
- * `dist/index.cjs` e `dist/<modulo>.cjs` (CommonJS para `require`)
784
- * `dist/*.d.ts` (Tipagens TypeScript)
785
- * `dist/ui-kit.css` (Design Tokens compilados, 100% autocontido — o build falha se restar qualquer `@import`)
786
- * `dist/custom-elements.json` (Custom Elements Manifest) e `dist/tipos-react.d.ts`, `dist/tipos-vue.d.ts`, `dist/tipos-elementos.d.ts`
787
-
788
- 4. **Verificar os tipos gerados** (depois do build):
789
- ```bash
790
- npm run test:tipos
791
- ```
1
+ # 🎨 UI Components Kit - Web Components Nativos & Universais
2
+
3
+ Biblioteca de **Web Components Nativos Universais e Agnósticos**, construída no padrão oficial **W3C Custom Elements + Shadow DOM**, com TypeScript, Design Tokens em CSS modular e Vite.
4
+
5
+ Projetada para funcionar em **qualquer linguagem, framework ou sistema web** (HTML puro, React, Next.js, Vue, Angular, Svelte, PHP, Django, WordPress, Blazor, etc.) com **zero dependências externas** (exceto `<ui-mapa>` e `<ui-canvas-cad>`, que usam o Leaflet como dependência opcional).
6
+
7
+ ---
8
+
9
+ ## 📑 Índice
10
+
11
+ 1. [Por que Web Components?](#-por-que-web-components)
12
+ 2. [Como Importar na sua Aplicação](#-como-importar-na-sua-aplicação)
13
+ - [Opção 1: CDN / Script Tag (HTML puro, PHP, Django)](#opção-1-cdn--script-tag-html-puro-php-django)
14
+ - [Opção 2: NPM / Módulos ES (React, Vue, Next.js, Vite)](#opção-2-npm--módulos-es-react-vue-nextjs-vite)
15
+ 3. [Como Controlar os Componentes (5 Pilares)](#-como-controlar-os-componentes-5-pilares)
16
+ 4. [⚡ Recursos Inteligentes de Alta Produtividade](#-recursos-inteligentes-de-alta-produtividade)
17
+ - [Ações Declarativas "Zero-JS" (Triggers Nativos)](#1-ações-declarativas-zero-js-triggers-nativos)
18
+ - [Formulários Automáticos com FormData Nativo (W3C FACE)](#2-formulários-automáticos-com-formdata-nativo-w3c-face)
19
+ - [Barramento Global de Orquestração (UIBus)](#3-barramento-global-de-orquestração-uibus)
20
+ - [Tabelas Autônomas com Auto-Fetch Remoto](#4-tabelas-autônomas-com-auto-fetch-remoto)
21
+ 5. [💻 Exemplos de Uso por Tecnologia](#-exemplos-de-uso-por-tecnologia)
22
+ - [HTML Puro + JavaScript](#html-puro--javascript)
23
+ - [React / Next.js](#react--nextjs)
24
+ - [Vue.js](#vuejs)
25
+ - [Iframes / Microfrontends](#iframes--microfrontends)
26
+ 6. [🧩 Catálogo de Componentes & Referência](#-catálogo-de-componentes--referência)
27
+ 7. [🛠️ Como Executar e Compilar Localmente](#️-como-executar-e-compilar-localmente)
28
+
29
+ ---
30
+
31
+ ## 🎯 Por que Web Components?
32
+
33
+ * 🌐 **100% Universal e Agnóstico**: Funciona em qualquer stack sem necessidade de bibliotecas intermediárias.
34
+ * 🛡️ **Encapsulamento Total (Shadow DOM)**: O CSS da sua aplicação nunca quebra o componente, e o CSS do componente nunca vaza para o seu site.
35
+ * 📐 **Design Tokens Centralizados**: Variáveis CSS personalizáveis para cores, tipografia, raios e densidade.
36
+ * 🚀 **Suporte a Altíssima Densidade**: Otimizado para interfaces densas e técnicas (GIS, CAD, Topografia de 15px a 20px de altura) até interfaces fluidas modernas.
37
+
38
+ ---
39
+
40
+ ## 📦 Como Importar na sua Aplicação
41
+
42
+ ### Opção 1: CDN / Script Tag (HTML puro, PHP, Django)
43
+
44
+ Adicione as tags no `<head>` ou antes do fechamento do `</body>`:
45
+
46
+ ```html
47
+ <!-- 1. Design Tokens e Estilos Globais -->
48
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@thibezer/ui-components-kit@1/dist/ui-kit.css">
49
+
50
+ <!-- 2. Biblioteca dos Componentes (Módulo ES) -->
51
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@thibezer/ui-components-kit@1/dist/index.js"></script>
52
+
53
+ <!-- 3. Uso imediato no HTML -->
54
+ <ui-botao-primario variante="primary">Clique Aqui</ui-botao-primario>
55
+ ```
56
+
57
+ > **Mapa e CAD via CDN (`<ui-mapa>`, `<ui-canvas-cad>`)**: esses componentes dependem do [Leaflet](https://leafletjs.com/), importado como `leaflet` (especificador "bare"), que o navegador só resolve com um *import map*. Declare-o **antes** do script da biblioteca:
58
+ >
59
+ > ```html
60
+ > <script type="importmap">
61
+ > { "imports": { "leaflet": "https://cdn.jsdelivr.net/npm/leaflet@1.9.4/+esm" } }
62
+ > </script>
63
+ > ```
64
+ >
65
+ > Os demais componentes não precisam do Leaflet. Com npm, basta `npm install leaflet`.
66
+
67
+ ### Opção 2: NPM / Módulos ES (React, Vue, Next.js, Vite)
68
+
69
+ Instale pelo npm:
70
+
71
+ ```bash
72
+ npm install @thibezer/ui-components-kit
73
+ ```
74
+
75
+ > O pacote é publicado já compilado (`dist/`) e **não executa build na instalação**, então funciona em CI/CD e em servidores com `npm ci --omit=dev`. A antiga instalação por URL Git (`git+https://github.com/...`) não é mais suportada: o repositório não versiona `dist/`.
76
+
77
+ No ponto de entrada da sua aplicação (`main.ts`, `index.js` ou `App.tsx`):
78
+
79
+ ```typescript
80
+ // 1. Estilos globais obrigatórios (tokens CSS)
81
+ import '@thibezer/ui-components-kit/style.css';
82
+
83
+ // 2. Biblioteca completa (ou escolha os submódulos abaixo)
84
+ import '@thibezer/ui-components-kit';
85
+ ```
86
+
87
+ #### 🚀 Importação Modular e Otimizada (Sub-paths)
88
+
89
+ Pague apenas pelo que utilizar. Importe por domínio de funcionalidade ou por componente específico:
90
+
91
+ O export map expõe explicitamente os pontos de entrada canônicos (ESM em `.js`, CommonJS em `.cjs` e tipos `.d.ts`):
92
+
93
+ | Subpath | Arquivo (ESM) |
94
+ |---|---|
95
+ | `@thibezer/ui-components-kit` | `dist/index.js` |
96
+ | `@thibezer/ui-components-kit/camadas` | `dist/camadas.js` |
97
+ | `@thibezer/ui-components-kit/forms` | `dist/forms.js` |
98
+ | `@thibezer/ui-components-kit/feedback` | `dist/feedback.js` |
99
+ | `@thibezer/ui-components-kit/tabela` | `dist/tabela.js` |
100
+ | `@thibezer/ui-components-kit/style.css` | `dist/ui-kit.css` (100% autocontido, sem `@import`) |
101
+
102
+ ##### 1. Por Domínio / Categoria:
103
+ ```typescript
104
+ import '@thibezer/ui-components-kit/forms'; // Campos, Select, Checkbox, Radio, Switch (~8 kB gzip)
105
+ import '@thibezer/ui-components-kit/feedback'; // Modais, Drawers, Alertas, Toasts, Tooltips, Skeletons (~6 kB gzip)
106
+ import '@thibezer/ui-components-kit/data'; // Tabela, Tabela-Propriedades, Stat KPI, Badge (~4 kB gzip)
107
+ import '@thibezer/ui-components-kit/tools'; // Ribbon CAD/Office e Paleta de Ferramentas (~10 kB gzip)
108
+ import '@thibezer/ui-components-kit/core'; // Apenas UIBus, Zero-JS e ListenerBag (< 1 kB gzip)
109
+ import '@thibezer/ui-components-kit/canvas'; // Mesa CAD vetorial e motor GIS (~33 kB gzip)
110
+ import '@thibezer/ui-components-kit/camadas'; // Painel de Camadas GIS/CAD redimensionável (~21 kB gzip)
111
+ import '@thibezer/ui-components-kit/mapa'; // Mapa Geográfico Leaflet (~3 kB gzip)
112
+ ```
113
+
114
+ ##### 2. Granular por Componente (Ultra Leve):
115
+ ```typescript
116
+ import '@thibezer/ui-components-kit/botao'; // Apenas <ui-botao> e <ui-botao-primario> (~3.4 kB gzip)
117
+ import '@thibezer/ui-components-kit/campo-texto'; // Apenas <ui-campo-texto> com FACE e anti-zoom (~3.9 kB gzip)
118
+ import '@thibezer/ui-components-kit/modal'; // Apenas <ui-modal> com bottom-sheet mobile (~3.3 kB gzip)
119
+ import '@thibezer/ui-components-kit/card'; // Apenas <ui-card> com elevação (~2.3 kB gzip)
120
+ import '@thibezer/ui-components-kit/tabela'; // Apenas <ui-tabela> virtualizada
121
+ ```
122
+
123
+ > **Zero Overhead**: Uma tela que utilize apenas `<ui-botao>` e `<ui-campo-texto>` importa menos de **8 kB gzip**, sem carregar nada de Leaflet, CAD, GIS ou virtualização.
124
+
125
+ ---
126
+
127
+ ## 🎛️ Como Controlar os Componentes (5 Pilares)
128
+
129
+ ```text
130
+ ┌─────────────────────────────────────────────────────────────┐
131
+ │ SISTEMA EXTERNO │
132
+ └──────┬───────────────▲─────────────────┬─────────────────▲──┘
133
+ │ │ │ │
134
+ 1. Atributos 4. Eventos Custom 2. Propriedades 3. Métodos
135
+ (HTML) (ui-*) (Objetos/Listas) (Imperativo)
136
+ │ │ │ │
137
+ ┌──────▼───────────────┴─────────────────▼─────────────────┴──┐
138
+ │ WEB COMPONENT (<ui-*>) │
139
+ └─────────────────────────────────────────────────────────────┘
140
+ ```
141
+
142
+ ### 1. Atributos HTML (Controle Declarativo)
143
+ Configurações simples passadas diretamente nas tags:
144
+ ```html
145
+ <ui-campo-texto label="CPF" placeholder="000.000.000-00" obrigatorio></ui-campo-texto>
146
+ <ui-modal id="meu-modal" titulo="Confirmar Exclusão"></ui-modal>
147
+ ```
148
+
149
+ ---
150
+
151
+ ### 📏 Ajuste de Altura e Dimensionamento Inteligente (Zero Atrito)
152
+
153
+ Os campos de texto (`<ui-campo-texto>`), listas flutuantes/selects (`<ui-lista-flutuante>`, `<ui-select>`), botões (`<ui-botao>`) e o controle segmentado (`<ui-segmented>`) leem **a mesma variável**, `--ui-campo-altura`, e por isso têm sempre a mesma altura lado a lado. Há 5 formas de ajustá-la:
154
+
155
+ #### 1. Via Atributo de Tamanho Predefinido (`tamanho` ou `densidade`):
156
+ ```html
157
+ <!-- Compacto (26px) - Ideal para Toolbars CAD/GIS, tabelas e alta densidade -->
158
+ <ui-campo-texto tamanho="sm" placeholder="26px Compacto"></ui-campo-texto>
159
+ <ui-lista-flutuante tamanho="sm" placeholder="Selecione..."></ui-lista-flutuante>
160
+
161
+ <!-- Padrão (34px) - Padrão moderno e equilibrado para formulários e modais -->
162
+ <ui-campo-texto tamanho="md" placeholder="34px Padrão"></ui-campo-texto>
163
+ <ui-lista-flutuante tamanho="md" placeholder="Selecione..."></ui-lista-flutuante>
164
+
165
+ <!-- Conforto / Touch (42px) - Otimizado para dispositivos móveis e telas sensíveis ao toque -->
166
+ <ui-campo-texto tamanho="lg" placeholder="42px Conforto"></ui-campo-texto>
167
+ <ui-lista-flutuante tamanho="lg" placeholder="Selecione..."></ui-lista-flutuante>
168
+ ```
169
+
170
+ #### 2. Via Atributo de Altura Explícita (`altura` ou `height`):
171
+ ```html
172
+ <!-- Define a altura exata em pixels diretamente no elemento -->
173
+ <ui-campo-texto altura="38" placeholder="Altura de 38px"></ui-campo-texto>
174
+ <ui-lista-flutuante altura="44px" placeholder="Altura de 44px"></ui-lista-flutuante>
175
+ <ui-botao-primario altura="40">Salvar</ui-botao-primario>
176
+ ```
177
+
178
+ #### 3. Via Variável CSS Customizável (`--ui-campo-altura`):
179
+ ```css
180
+ /* No seu CSS global ou no container pai */
181
+ .meu-formulario {
182
+ --ui-campo-altura: 36px; /* Aplica instantaneamente em todos os inputs, selects e botões filhos */
183
+ }
184
+ ```
185
+
186
+ #### 4. Via Barramento Global de Densidade (`UIBus`):
187
+ ```javascript
188
+ import { UIBus } from '@thibezer/ui-components-kit';
189
+
190
+ // Altera a densidade de toda a aplicação em tempo real:
191
+ UIBus.definirDensidade('compacta'); // 26px
192
+ UIBus.definirDensidade('padrao'); // 34px
193
+ UIBus.definirDensidade('relaxada'); // 42px
194
+ UIBus.definirDensidade(38); // Valor numérico em px customizado
195
+ ```
196
+
197
+ #### 5. Via Contêiner de Linha (`<ui-linha>`): a forma de menor esforço
198
+ Quando vários controles ficam na mesma linha, envolva-os em `<ui-linha>`: um único atributo define a altura de todos, e o contêiner cuida de alinhamento, espaço, quebra e largura (botão e campo deixam de ocupar 100%).
199
+
200
+ ```html
201
+ <ui-linha tamanho="sm" justificar="entre">
202
+ <ui-segmented valor="todos"><span valor="todos">Todos</span><span valor="abertos">Abertos</span></ui-segmented>
203
+ <ui-campo-texto placeholder="Buscar..."></ui-campo-texto> <!-- ocupa o espaço que sobrar -->
204
+ <ui-botao variante="primario">Adicionar</ui-botao>
205
+ <ui-badge controle>Carlos</ui-badge> <!-- [controle]: badge com a altura dos controles -->
206
+ </ui-linha>
207
+ ```
208
+
209
+ | Atributo (`ui-linha`) | Valores | Padrão |
210
+ | :--- | :--- | :--- |
211
+ | `tamanho` / `densidade` | `sm` (26px), `md` (34px), `lg` (42px); também `compacta`, `padrao`, `relaxada` | herda do token global |
212
+ | `altura` | `40`, `40px`, `2.5rem` (vence `tamanho`) | - |
213
+ | `alinhar` | `centro`, `inicio`, `fim`, `esticar` (use `fim` quando um campo tem rótulo acima) | `centro` |
214
+ | `justificar` | `inicio`, `centro`, `fim`, `entre`, `ao-redor` | `inicio` |
215
+ | `espaco` | `nenhum`, `xs`, `sm`, `md`, `lg` | `sm` |
216
+ | `sem-quebra` / `inline` | booleanos | a linha quebra |
217
+
218
+ Regras do contrato: o filho com `tamanho`/`altura` próprio vence só ele; `expandir` em qualquer filho faz ocupar o espaço restante; CSS do consumidor (`style`, classe) vence as regras do contêiner. Tokens: `--ui-altura-sm|md|lg` (escala), `--ui-campo-altura` (valor efetivo), `--ui-linha-espaco`, `--ui-linha-campo-base`. No mobile (≤ 640px) todos os controles sobem para no mínimo 38px (alvo de toque), sem exceção, para continuarem alinhados. Mais detalhes em [`docs/layout-linha.md`](docs/layout-linha.md).
219
+
220
+ ---
221
+
222
+ ### 2. Propriedades JavaScript (Dados Complexos)
223
+ Para enviar arrays e objetos complexos:
224
+ ```javascript
225
+ const tabela = document.querySelector('ui-tabela');
226
+ tabela.colunas = [
227
+ { id: 'ponto', rotulo: 'Ponto', ordenavel: true },
228
+ { id: 'altitude', rotulo: 'Altitude (m)' }
229
+ ];
230
+ tabela.dados = [
231
+ { ponto: 'P-01', altitude: '542,15' },
232
+ { ponto: 'P-02', altitude: '545,30' }
233
+ ];
234
+ ```
235
+
236
+ ### 3. Métodos Imperativos (Ações Diretas)
237
+ Comandos diretos chamados na instância do elemento:
238
+ ```javascript
239
+ const modal = document.getElementById('meu-modal');
240
+ modal.abrir();
241
+ modal.fechar();
242
+ ```
243
+
244
+ ### 4. Eventos Customizados Nativos (`ui-*`)
245
+ Escuta de eventos disparados pelas interações do usuário:
246
+ ```javascript
247
+ const campo = document.querySelector('ui-campo-texto');
248
+ campo.addEventListener('ui-input', (e) => {
249
+ console.log('Texto digitado:', e.detail.value);
250
+ });
251
+ ```
252
+
253
+ > **Mutação de usuário vs. programática (`<ui-camadas>`)**: eventos saem do componente **somente** em resposta a interações físicas (clique, drag-and-drop, teclado). Atribuir `painel.layers = [...]`, alterar atributos (`camada-ativa`, `colapsado`, `mapa-base-ativo`...) ou chamar métodos públicos (`definirCamadaAtiva`, `selecionarFeicao`, `limparSelecao`, `colapsar`, `definirAltura`...) apenas atualiza o DOM interno — sem redisparar eventos. Isso elimina loops de sincronização em apps reativos (React, Vue, Svelte). Para forçar a emissão numa chamada programática, passe `emitirEvento = true` (ex.: `painel.definirCamadaAtiva('lotes', true)`).
254
+
255
+ ### 5. Serviços Globais e Helpers
256
+ Utilitários prontos para uso sem necessidade de instanciar elementos:
257
+ ```javascript
258
+ import { UIToast, UIBus } from '@thibezer/ui-components-kit';
259
+
260
+ UIToast.notificar({
261
+ tipo: 'sucesso',
262
+ titulo: 'Registro Salvo!',
263
+ mensagem: 'Os dados foram sincronizados.',
264
+ duracao: 4000
265
+ });
266
+ ```
267
+
268
+ ### 6. Escala de Elevação (z-index)
269
+ Todos os componentes flutuantes consomem tokens de elevação previsíveis (com fallback), definidos em `ui-kit.css`. Sobrescreva-os no `:root` da aplicação para encaixar a pilha do kit entre os HUDs e modais do seu app:
270
+ ```css
271
+ :root {
272
+ --ui-z-workspace-hud: 500; /* <ui-camadas flutuante>, <ui-seletor-mapa-base>, toolbars do canvas */
273
+ --ui-z-docked-bar: 600; /* <ui-ribbon> recolhido, <ui-tabela-propriedades flutuante> */
274
+ --ui-z-drawer: 700; /* <ui-drawer> e backdrop */
275
+ --ui-z-modal: 1000; /* <ui-modal> e backdrop */
276
+ --ui-z-popover: 1050; /* menus suspensos/contexto, <ui-select> (acima do modal, funciona dentro dele) */
277
+ --ui-z-toast: 1100; /* UIToast */
278
+ --ui-z-tooltip: 1200; /* <ui-tooltip> */
279
+ }
280
+ ```
281
+
282
+ ### 7. Acessibilidade Nativa de Rótulos
283
+ `<ui-input>` (alias de `<ui-campo-texto>`), `<ui-select>` e `<ui-switch>` geram IDs únicos internamente e associam o rótulo visível ao controle nativo do Shadow DOM — o projeto consumidor não precisa gerenciar IDs:
284
+ ```html
285
+ <ui-input label="Nome"></ui-input> <!-- <label for> ↔ <input> interno -->
286
+ <ui-select label="Cidade">...</ui-select> <!-- aria-labelledby = rótulo + valor atual -->
287
+ <ui-switch label="Modo escuro"></ui-switch> <!-- role="switch" aria-labelledby -->
288
+
289
+ <!-- Sem o atributo label, o nome acessível vem de fora do Shadow DOM: -->
290
+ <label>Telefone <ui-input></ui-input></label>
291
+ <ui-switch aria-label="Camada visível"></ui-switch>
292
+ ```
293
+
294
+ ### 8. Temas Claro e Escuro
295
+ Sem configuração, o kit **segue o tema do sistema operacional** (`prefers-color-scheme`). Para fixar um tema, use o atributo na raiz — ou em qualquer elemento, para fixar só aquele trecho:
296
+ ```html
297
+ <html data-tema="escuro"> <!-- ou "claro"; também aceitos: class="dark|light" e data-theme="dark|light" -->
298
+ <aside data-tema="claro">…</aside> <!-- trecho claro dentro de uma página escura -->
299
+ ```
300
+ ```javascript
301
+ UIBus.definirTema('auto'); // volta a seguir o SO
302
+ UIBus.on('tema:alterado', ({ tema }) => { /* também dispara quando o SO muda, se o tema não estiver fixado */ });
303
+ ```
304
+ Os componentes recebem o tema **apenas pelos tokens de cor** (`--ui-cor-*`, `--ui-sombra-*`), que atravessam o Shadow DOM. Todo token de texto atinge contraste **WCAG AA (4,5:1)** nos dois temas, inclusive sobre fundos tingidos de status e no texto sobre a cor primária e o botão destrutivo — verificado em `tests/tema.test.ts`.
305
+
306
+ | Token | Uso |
307
+ | :--- | :--- |
308
+ | `--ui-cor-texto-sucesso` / `-erro` / `-alerta` / `-info` | Texto e tons de status (alertas, badges, toasts) |
309
+ | `--ui-cor-texto-sobre-status` | Texto sobre fundo sólido de status (badge sólido) |
310
+ | `--ui-cor-fundo-recuado` | Faixas rebaixadas (rodapés de modal e drawer) |
311
+ | `--ui-sombra-sm` / `-md` / `-lg` | Sombras: densas no escuro, suaves no claro |
312
+
313
+ ### 9. Tipografia e Raios
314
+ Todos os componentes usam apenas dois tokens de família de fonte e uma escala de raios derivada de `--ui-raio-borda`. Sobrescreva no `:root` (a escala é calculada lá):
315
+ ```css
316
+ :root {
317
+ --ui-fonte-base: 'Roboto', sans-serif; /* textos da interface */
318
+ --ui-fonte-codigo: 'JetBrains Mono', monospace; /* coordenadas, valores técnicos, código */
319
+
320
+ --ui-raio-borda: 4px; /* base: botões, campos, menus */
321
+ /* derivados automaticamente: --ui-raio-sm (2/3), --ui-raio-lg (5/3), --ui-raio-xl (8/3) */
322
+ /* --ui-raio-borda: 0 → interface totalmente reta */
323
+ }
324
+ ```
325
+
326
+ ### 10. Estilização Interna com `::part`
327
+ O Shadow DOM isola o CSS, mas os elementos internos principais são expostos com `part`, com nomes consistentes entre componentes (uma regra serve para vários):
328
+
329
+ | Componente | Parts |
330
+ | :--- | :--- |
331
+ | `<ui-botao>` / `<ui-botao-primario>` | `base`, `spinner` |
332
+ | `<ui-campo-texto>` / `<ui-input>` | `rotulo`, `campo`, `input`, `icone`, `icone-esquerda`, `icone-direita`, `ajuda` |
333
+ | `<ui-lista-flutuante>` / `<ui-select>` | `rotulo`, `campo`, `valor`, `seta`, `lista`, `opcao`, `opcao-selecionada` |
334
+ | `<ui-checkbox>`, `<ui-radio>`, `<ui-switch>` | `base`, `controle`, `indicador`, `rotulo` |
335
+ | `<ui-segmented>` | `base`, `indicador`, `opcao`, `opcao-selecionada` |
336
+ | `<ui-modal>`, `<ui-drawer>` | `fundo`, `painel`, `cabecalho`, `titulo`, `descricao` (drawer), `fechar`, `corpo`, `rodape` |
337
+ | `<ui-card>` | `base`, `midia`, `cabecalho`, `corpo`, `rodape` |
338
+ | `<ui-badge>` / `<ui-chip>` | `base`, `rotulo`, `remover` |
339
+ | `<ui-alerta>` / toasts | `base`, `icone`, `conteudo`, `titulo`, `mensagem`, `acao`, `fechar`, `progresso` |
340
+ | `<ui-tooltip>` | `balao`, `texto`, `seta` |
341
+ | `<ui-tabela>` | `base`, `tabela`, `celula-cabecalho`, `ordenar`, `linha`, `linha-selecionada`, `celula`, `vazio`, `carregando` |
342
+
343
+ ```css
344
+ ui-campo-texto::part(campo) { border-width: 2px; }
345
+ ui-modal::part(painel) { max-width: 720px; }
346
+ ui-tabela::part(linha-selecionada) { background: #0b3d2a; }
347
+ :is(ui-checkbox, ui-radio, ui-switch)::part(rotulo) { font-weight: 600; }
348
+ ```
349
+
350
+ ---
351
+
352
+ ## ⚡ Recursos Inteligentes de Alta Produtividade
353
+
354
+ ### 1. Ações Declarativas "Zero-JS" (Triggers Nativos)
355
+ Você pode acionar modais, fechar janelas, disparar notificações toast ou copiar textos **sem escrever nenhuma linha de JavaScript**, apenas adicionando atributos nas tags HTML:
356
+
357
+ ```html
358
+ <!-- Abrir Modal sem JS -->
359
+ <ui-botao-primario target-modal="modal-exclusao" variante="destrutivo">
360
+ Excluir Registro
361
+ </ui-botao-primario>
362
+
363
+ <!-- Modal com Fechamento e Toast automáticos -->
364
+ <ui-modal id="modal-exclusao" titulo="Confirmação">
365
+ Tem certeza que deseja excluir?
366
+ <div slot="rodape">
367
+ <ui-botao-primario dismiss-modal variante="secundario">Cancelar</ui-botao-primario>
368
+ <ui-botao-primario dismiss-modal toast-sucesso="Excluído com sucesso!" variante="destrutivo">
369
+ Confirmar
370
+ </ui-botao-primario>
371
+ </div>
372
+ </ui-modal>
373
+
374
+ <!-- Copiar Texto em 1 Clique -->
375
+ <ui-campo-texto id="input-token" value="SIGEF-2026-X88"></ui-campo-texto>
376
+ <ui-botao-primario copiar-texto="#input-token" copiar-mensagem="Token copiado!">
377
+ Copiar Chave
378
+ </ui-botao-primario>
379
+ ```
380
+
381
+ #### Tabela de Atributos Zero-JS Disponíveis:
382
+ | Atributo | Descrição | Exemplo de Uso |
383
+ | :--- | :--- | :--- |
384
+ | `target-modal="id"` | Abre o modal com o ID especificado ao clicar. | `target-modal="modal-login"` |
385
+ | `dismiss-modal` | Fecha o modal atual (ou o especificado por ID). | `dismiss-modal` |
386
+ | `toast-sucesso="msg"` | Dispara uma notificação flutuante de sucesso. | `toast-sucesso="Salvo com sucesso!"` |
387
+ | `toast-erro="msg"` | Dispara uma notificação flutuante de erro. | `toast-erro="Falha na conexão."` |
388
+ | `toast-alerta="msg"` | Dispara uma notificação flutuante de aviso. | `toast-alerta="Preencha os campos obrigatórios."` |
389
+ | `copiar-texto="texto\|#id"` | Copia o texto ou o valor do elemento referenciado. | `copiar-texto="#campo-pix"` |
390
+ | `limpar-form="id"` | Reseta todos os campos do formulário indicado. | `limpar-form="form-cadastro"` |
391
+ | `alternar-tema` | Alterna entre tema claro e escuro globalmente (a partir do tema em vigor, inclusive o do SO). | `alternar-tema` |
392
+
393
+ ---
394
+
395
+ ### 2. Formulários Automáticos com `FormData` Nativo (W3C FACE)
396
+ Todos os campos (`<ui-campo-texto>`, `<ui-lista-flutuante>`, `<ui-checkbox>`, `<ui-radio>`, `<ui-switch>`) implementam a especificação oficial de **Form-Associated Custom Elements**.
397
+
398
+ Isso significa que você extrai os dados de todos os campos de uma única vez, sem ler um por um:
399
+
400
+ ```html
401
+ <form id="form-imovel">
402
+ <ui-campo-texto name="denominacao" label="Nome da Fazenda" value="Boa Vista"></ui-campo-texto>
403
+ <ui-campo-texto name="municipio" label="Município" value="Uberlândia"></ui-campo-texto>
404
+ <ui-checkbox name="georreferenciado" label="Georreferenciado" marcado value="sim"></ui-checkbox>
405
+
406
+ <ui-botao-primario type="submit">Salvar Imóvel</ui-botao-primario>
407
+ </form>
408
+ ```
409
+
410
+ ```javascript
411
+ const form = document.getElementById('form-imovel');
412
+
413
+ form.addEventListener('submit', (e) => {
414
+ e.preventDefault();
415
+
416
+ // Extrai TODOS os campos em 1 única linha nativa:
417
+ const dados = Object.fromEntries(new FormData(form));
418
+ console.log(dados);
419
+ // Resultado: { denominacao: "Boa Vista", municipio: "Uberlândia", georreferenciado: "sim" }
420
+ });
421
+ ```
422
+
423
+ Assim como nos campos nativos, **Enter** em um `<ui-campo-texto>` submete o formulário pelo botão de envio padrão (`<ui-botao-primario type="submit">` ou `<button>`), que também pode estar fora do `<form>` com `form="id-do-form"`. Para impedir, chame `preventDefault()` no `keydown`. O `name`/`value` do botão que submeteu chega como `e.submitter` (use `new FormData(form, e.submitter)`), e gatilhos Zero-JS em botões `disabled` ou `carregando` são ignorados.
424
+
425
+ ---
426
+
427
+ ### 3. Barramento Global de Orquestração (`UIBus`)
428
+ O `UIBus` é uma central reativa que permite comandar qualquer componente do sistema ou ouvir eventos sem precisar de `querySelector`:
429
+
430
+ ```javascript
431
+ import { UIBus } from '@thibezer/ui-components-kit';
432
+
433
+ // 1. Executar Ações Diretas:
434
+ UIBus.abrirModal('modal-confirmacao');
435
+ UIBus.fecharModal('modal-confirmacao');
436
+ UIBus.notificar({ tipo: 'sucesso', mensagem: 'Operação concluída!' });
437
+ UIBus.copiar('Texto para a área de transferência', 'Copiado!');
438
+ UIBus.definirDensidade('compacta'); // 'compacta' | 'normal' | 'relaxada'
439
+ UIBus.definirTema('escuro'); // 'claro' | 'escuro' | 'auto' (segue o SO); sem argumento, alterna
440
+ UIBus.obterTema(); // tema em vigor: 'claro' | 'escuro'
441
+
442
+ // 2. Comunicação Desacoplada (Pub/Sub):
443
+ UIBus.on('vertice:selecionado', (dadosVertice) => {
444
+ console.log('Vértice selecionado em qualquer tela:', dadosVertice);
445
+ });
446
+
447
+ UIBus.emit('vertice:selecionado', { id: 101, nome: 'M-01', altitude: 432.10 });
448
+ ```
449
+
450
+ ---
451
+
452
+ ### 4. Tabelas Autônomas com Auto-Fetch Remoto
453
+ A `<ui-tabela>` pode buscar dados de uma API remota de forma totalmente autônoma, exibindo indicador de loading, tratando erros e permitindo filtragem instantânea:
454
+
455
+ ```html
456
+ <ui-tabela
457
+ id="tabela-vertices"
458
+ src="/api/v1/geodesia/vertices"
459
+ max-height="400px"
460
+ densidade="compacta">
461
+ </ui-tabela>
462
+ ```
463
+
464
+ ```javascript
465
+ const tabela = document.getElementById('tabela-vertices');
466
+
467
+ // Definir colunas da tabela:
468
+ tabela.colunas = [
469
+ { id: 'nome_vertice', rotulo: 'Vértice', ordenavel: true },
470
+ { id: 'este', rotulo: 'Este (m)', ordenavel: true, alinhamento: 'direita' },
471
+ { id: 'norte', rotulo: 'Norte (m)', ordenavel: true, alinhamento: 'direita' }
472
+ ];
473
+
474
+ // Métodos inteligentes disponíveis na tabela:
475
+ tabela.filtrar('M-01'); // Filtra instantaneamente em todas as colunas
476
+ tabela.recarregar(); // Recarrega os dados da API remota
477
+ ```
478
+
479
+ **Autenticação e opções do `fetch`** (o `src` sozinho faz um `GET` sem headers):
480
+
481
+ ```javascript
482
+ tabela.fetchOpcoes = { headers: { Authorization: `Bearer ${token}` }, credentials: 'include' };
483
+ // ou troque a função de busca (renovação de token, mocks):
484
+ tabela.fetcher = (url, init) => meuFetchAutenticado(url, init);
485
+
486
+ const ok = await tabela.carregarDoEndpoint('/api/v1/vertices'); // true em sucesso; nunca rejeita
487
+ ```
488
+
489
+ **Ordenação no servidor**: `<ui-tabela ordenacao-remota>` (ou `tabela.ordenacaoRemota = true`) atualiza o indicador e emite `ui-sort`, mas não reordena; busque os dados já ordenados. Sem o atributo, chame `evento.preventDefault()` em `ui-sort` para recusar a ordenação.
490
+
491
+ **Eventos de `<ui-tabela>`** (todos `bubbles` e `composed`; só `ui-sort` é cancelável):
492
+
493
+ | Evento | `detail` | Quando |
494
+ |---|---|---|
495
+ | `ui-sort` (cancelável) | `{ idColuna, direcao }` | Clique no cabeçalho ordenável. `direcao: 'original'` com `idColuna: null` = ordenação removida |
496
+ | `ui-column-resize` | `{ idColuna, largura }` (`"120px"` ou `"auto"`) | Fim do arraste, popover ou duplo clique; não dispara em clique sem arrastar |
497
+ | `ui-linha-clique` | `{ item, indice }` | Clique/Enter/Espaço numa linha. `indice` é a posição **exibida** (após ordenar/filtrar): use `item` |
498
+ | `ui-linha-selecionada` | `{ item, indice }` | `rolarPara(..., { selecionar: true })` |
499
+ | `ui-selecao-removida` | `{ item }` | O item selecionado saiu dos `dados` |
500
+ | `ui-selecao-atualizada` | `{ item, anterior }` | Os dados foram recarregados e o registro selecionado agora é outro objeto |
501
+ | `ui-fetch-start` | `{ url }` | Início da requisição do `src` |
502
+ | `ui-fetch-sucesso` | `{ url, total, dados }` | Dados recebidos |
503
+ | `ui-fetch-erro` | `{ url, erro, origem, status? }` | Falha. `origem`: `rede`, `http` (com `status`), `json` ou `render` |
504
+ | `ui-fetch-cancelado` | `{ url }` | Requisição abortada ou substituída (fecha o par do `ui-fetch-start`) |
505
+
506
+ Boas práticas: trate `dados` como imutável (atribua sempre um array novo), mantenha `chave-id` única e não nula, memorize `colunas` (atribuí-las de novo refaz a tabela) e coloque controles interativos das células com `data-prevent-select` se não quiser que o clique selecione a linha. `render` devolvendo `string` vira texto (não HTML); devolva um `HTMLElement` novo por chamada.
507
+
508
+ ---
509
+
510
+ ## 💻 Exemplos de Uso por Tecnologia
511
+
512
+ ### HTML Puro + JavaScript
513
+ ```html
514
+ <!DOCTYPE html>
515
+ <html lang="pt-BR">
516
+ <head>
517
+ <link rel="stylesheet" href="dist/ui-kit.css">
518
+ <script type="module" src="dist/index.js"></script>
519
+ </head>
520
+ <body>
521
+ <ui-campo-texto id="meu-campo" label="Nome do Projeto"></ui-campo-texto>
522
+ <ui-botao-primario target-modal="meu-modal">Abrir Detalhes</ui-botao-primario>
523
+
524
+ <ui-modal id="meu-modal" titulo="Detalhes">
525
+ <p>Conteúdo do modal...</p>
526
+ <div slot="rodape">
527
+ <ui-botao-primario dismiss-modal>Fechar</ui-botao-primario>
528
+ </div>
529
+ </ui-modal>
530
+
531
+ <script>
532
+ document.getElementById('meu-campo').addEventListener('ui-input', (e) => {
533
+ console.log('Digitado:', e.detail.value);
534
+ });
535
+ </script>
536
+ </body>
537
+ </html>
538
+ ```
539
+
540
+ ---
541
+
542
+ ### React / Next.js (SSR & Client Components)
543
+
544
+ A biblioteca é **100% SSR-safe**: todas as classes herdam de `SafeHTMLElement` e o registro de tags utiliza verificações de segurança para ambientes sem DOM (Node.js). Isso garante que você pode importar tipos, classes e constantes tanto no servidor quanto no cliente sem encontrar `ReferenceError: HTMLElement is not defined` ou `customElements is not defined`.
545
+
546
+ #### Tipos no TSX (autocompletar e checagem)
547
+ O pacote gera tipos para todas as tags a partir do Custom Elements Manifest. Registre-os uma vez no `tsconfig.json`:
548
+
549
+ ```json
550
+ { "compilerOptions": { "types": ["@thibezer/ui-components-kit/tipos-react"] } }
551
+ ```
552
+
553
+ A partir daí o editor sugere atributos, propriedades e eventos de cada tag, e o TypeScript recusa valores inválidos:
554
+
555
+ ```tsx
556
+ <ui-tabela
557
+ colunas={[{ id: 'ponto', rotulo: 'Ponto', ordenavel: true }]} // tipado como TabelaColuna[]
558
+ densidade="compacta" // 'compacta' | 'normal' | 'relaxada'
559
+ onui-sort={(e) => console.log(e.detail)} // eventos ui-* no formato do React 19
560
+ />
561
+ <ui-tabela densidade="enorme" /> // ❌ erro de tipo
562
+ ```
563
+
564
+ Outros JSX (Preact, Solid etc.) podem usar o mapa agnóstico `ElementosUI` de `@thibezer/ui-components-kit/tipos-elementos`.
565
+
566
+ #### No Next.js (App Router):
567
+
568
+ Crie um componente cliente provedor ou registre no topo do seu layout/página cliente:
569
+
570
+ ```tsx
571
+ // components/UIProvider.tsx
572
+ 'use client';
573
+
574
+ import { useEffect } from 'react';
575
+ import '@thibezer/ui-components-kit/style.css';
576
+ import '@thibezer/ui-components-kit/register'; // Registra todos os Custom Elements com segurança no cliente
577
+
578
+ export function UIProvider({ children }: { children: React.ReactNode }) {
579
+ return <>{children}</>;
580
+ }
581
+ ```
582
+
583
+ Ou registre de forma modular apenas o que a tela precisa:
584
+
585
+ ```tsx
586
+ // app/dashboard/page.tsx
587
+ 'use client';
588
+
589
+ import React, { useEffect, useRef, useState } from 'react';
590
+ import '@thibezer/ui-components-kit/style.css';
591
+ import '@thibezer/ui-components-kit/forms';
592
+ import '@thibezer/ui-components-kit/botao';
593
+ import '@thibezer/ui-components-kit/tabela';
594
+ import { UIBus } from '@thibezer/ui-components-kit/core';
595
+ import type { UITabela } from '@thibezer/ui-components-kit/tabela';
596
+
597
+ export default function DashboardPropriedades() {
598
+ const tabelaRef = useRef<UITabela>(null);
599
+ const [nome, setNome] = useState('');
600
+
601
+ useEffect(() => {
602
+ if (tabelaRef.current) {
603
+ tabelaRef.current.colunas = [
604
+ { id: 'id', rotulo: 'Código' },
605
+ { id: 'nome', rotulo: 'Propriedade' }
606
+ ];
607
+ tabelaRef.current.dados = [
608
+ { id: 1, nome: 'Fazenda Santa Luzia' }
609
+ ];
610
+ }
611
+ }, []);
612
+
613
+ return (
614
+ <div style={{ padding: 24 }}>
615
+ <ui-campo-texto
616
+ label="Pesquisar Imóvel"
617
+ value={nome}
618
+ onInput={(e: any) => {
619
+ // Os eventos nativos `input`/`change` não têm `detail`: leia o valor do próprio elemento
620
+ const valor = e.target.value;
621
+ setNome(valor);
622
+ tabelaRef.current?.filtrar(valor);
623
+ }}
624
+ ></ui-campo-texto>
625
+
626
+ <ui-tabela ref={tabelaRef} max-height="300px" densidade="compacta"></ui-tabela>
627
+
628
+ <ui-botao-primario
629
+ variante="primary"
630
+ onClick={() => UIBus.notificar({ tipo: 'sucesso', mensagem: 'Dados sincronizados!' })}>
631
+ Sincronizar
632
+ </ui-botao-primario>
633
+ </div>
634
+ );
635
+ }
636
+ ```
637
+
638
+ ---
639
+
640
+ ### Vue.js
641
+
642
+ Para autocompletar e checar props e eventos nos templates (Volar), registre os tipos no `tsconfig.json` e informe ao Vue que tags `ui-*` são custom elements:
643
+
644
+ ```json
645
+ { "compilerOptions": { "types": ["@thibezer/ui-components-kit/tipos-vue"] } }
646
+ ```
647
+
648
+ ```javascript
649
+ // vite.config.js
650
+ vue({ template: { compilerOptions: { isCustomElement: (tag) => tag.startsWith('ui-') } } })
651
+ ```
652
+
653
+ ```vue
654
+ <template>
655
+ <div class="container">
656
+ <ui-campo-texto
657
+ :value="termoBusca"
658
+ label="Buscar Vértice"
659
+ @ui-input="aoDigitar"
660
+ />
661
+
662
+ <ui-lista-flutuante
663
+ .itens="opcoes"
664
+ @ui-selecionar="aoSelecionar"
665
+ />
666
+
667
+ <ui-botao-primario target-modal="modal-confirmacao">
668
+ Confirmar Operação
669
+ </ui-botao-primario>
670
+ </div>
671
+ </template>
672
+
673
+ <script setup>
674
+ import { ref } from 'vue';
675
+ import '@thibezer/ui-components-kit/style.css';
676
+ import '@thibezer/ui-components-kit';
677
+
678
+ const termoBusca = ref('');
679
+ const opcoes = ref([
680
+ { id: '1', label: 'Marco Geodésico M' },
681
+ { id: '2', label: 'Ponto Topográfico P' }
682
+ ]);
683
+
684
+ function aoDigitar(e) {
685
+ termoBusca.value = e.detail.value;
686
+ }
687
+
688
+ function aoSelecionar(e) {
689
+ console.log('Opção selecionada:', e.detail);
690
+ }
691
+ </script>
692
+ ```
693
+
694
+ ---
695
+
696
+ ### Iframes / Microfrontends
697
+ Comunicação segura entre iframes e sistemas pais via eventos do `UIBus`:
698
+
699
+ ```javascript
700
+ // Dentro do Iframe (Filho):
701
+ import { UIBus } from '@thibezer/ui-components-kit';
702
+
703
+ UIBus.on('vertice:salvo', (dados) => {
704
+ window.parent.postMessage({ tipo: 'VERTICE_SALVO', payload: dados }, '*');
705
+ });
706
+
707
+ // No Sistema Principal (Pai):
708
+ window.addEventListener('message', (event) => {
709
+ if (event.data?.tipo === 'VERTICE_SALVO') {
710
+ console.log('Recebido do Iframe:', event.data.payload);
711
+ }
712
+ });
713
+ ```
714
+
715
+ ---
716
+
717
+ ## 🧩 Catálogo de Componentes & Referência
718
+
719
+ ### 🟢 Camada 1: Fundamentos
720
+ | Tag | Descrição | Principais Atributos / Props | Eventos Emitidos |
721
+ | :--- | :--- | :--- | :--- |
722
+ | `<ui-botao-primario>` | Botão com estados de loading, ícone e variantes. Form-associated (`type="submit"`/`"reset"`, `name`/`value`, `form`). Só com ícone, informe `aria-label` (ou `title`). | `variante`, `carregando`, `disabled`, `type`, `aria-label`, `target-modal` | `ui-click` |
723
+ | `<ui-campo-texto>` | Input de texto com Floating Label, validação e senha. | `label`, `value`, `tipo`, `obrigatorio`, `name` | `ui-input`, `ui-change` |
724
+ | `<ui-lista-flutuante>` | Select/Dropdown com Bottom Sheet no mobile. | `itens`, `value`, `placeholder`, `name` | `ui-selecionar` |
725
+ | `<ui-texto>` | Tipografia semântica (H1-H6, corpo, caption, código). | `variante`, `cor`, `peso` | - |
726
+ | `<ui-icone>` | Wrapper para ícones vetoriais padronizados. | `nome`, `tamanho`, `cor` | - |
727
+
728
+ ### 🔵 Camada 2: Controles & Seleção
729
+ | Tag | Descrição | Principais Atributos / Props | Eventos Emitidos |
730
+ | :--- | :--- | :--- | :--- |
731
+ | `<ui-checkbox>` | Caixa de seleção múltipla com estado indeterminado. | `marcado`, `indeterminado`, `name`, `value` | `ui-change` |
732
+ | `<ui-radio>` | Opção única com agrupamento automático por nome. | `marcado`, `name`, `value` | `ui-change` |
733
+ | `<ui-switch>` | Chave de alternância liga/desliga física instantânea. | `ativo`, `name`, `value` | `ui-change` |
734
+ | `<ui-segmented>` | Controle segmentado de opções (Pills/Tabs deslizantes). | `opcoes`, `value`, `name`, `tamanho`, `altura` | `ui-change`, `ui-selecionar` |
735
+ | `<ui-badge>` / `<ui-chip>` | Tags de status e chips com botão de remoção. | `variante`, `removivel`, `value`, `controle` | `ui-remove` |
736
+ | `<ui-linha>` | Contêiner que alinha controles na mesma linha (altura única, espaço, quebra). | `tamanho`, `altura`, `alinhar`, `justificar`, `espaco`, `sem-quebra` | - |
737
+ | `<ui-avatar>` | Avatar com fotos, iniciais e status online. | `nome`, `src`, `status`, `tamanho` | - |
738
+
739
+ ### 🟣 Camada 3: Contêineres, Overlays, Dados & GIS
740
+ | Tag | Descrição | Principais Atributos / Props | Eventos Emitidos |
741
+ | :--- | :--- | :--- | :--- |
742
+ | `<ui-card>` | Cartão de conteúdo com slots nomeados e elevação. | `elevacao`, `clicavel` | `ui-click` |
743
+ | `<ui-modal>` | Modal centralizado no PC e Bottom Sheet no Mobile. Abrir/fechar por `abrir()`/`fechar()` ou pelo atributo `aberto` tem o mesmo efeito (eventos, foco, `inert`). Foco inicial no `[autofocus]` ou no primeiro controle do conteúdo; ao fechar, volta ao gatilho. Modais empilhados: só o último aberto é interativo. | `aberto`, `titulo`, `bottom-sheet`, `bloquear-fechamento` (oculta o "✕" e ignora Esc/fundo) | `ui-abrir`, `ui-fechar` |
744
+ | `<ui-drawer>` | Painel lateral deslizante (Sheet/Gaveta direita/esquerda). | `aberto`, `posicao` (`direita`, `esquerda`, `baixo`, `cima`), `titulo`, `largura` | `ui-abrir`, `ui-fechar` |
745
+ | `<ui-alerta>` | Banner de notificação contextual fixo. | `variante`, `titulo`, `fechavel` | `ui-fechar` |
746
+ | `<ui-toast>` | Notificações flutuantes inteligentes com pilha flex. | `UIToast.notificar({ tipo, mensagem })` | - |
747
+ | `<ui-tooltip>` | Dica contextual inteligente (Hover PC / Toque Mobile). O texto vira `aria-description` do gatilho (lido pelo leitor de tela, também repassado pelo `<ui-botao>`); **Esc** fecha sem mover o foco; o balão não some ao levar o ponteiro até ele. | `texto`, `posicao`, `gatilho` | - |
748
+ | `<ui-skeleton>` | Placeholder de carregamento animado com pulsos. | `tipo`, `largura`, `altura`, `animado` | - |
749
+ | `<ui-stat>` | Cartão KPI de estatísticas, métricas e tendências. | `rotulo`, `valor`, `variacao`, `tendencia` (`alta`, `baixa`, `neutro`) | - |
750
+ | `<ui-tabela>` | Tabela orientada a dados, virtualizada tipo Excel. **Teclado**: cabeçalhos ordenáveis são botões (Tab + Enter/Espaço, com `aria-sort`); nas linhas, ↑/↓, Home/End e PageUp/PageDown movem o foco e Enter/Espaço selecionam (`aria-current`). A seleção é mantida ao ordenar e filtrar. Nome acessível via `aria-label`. | `colunas`, `dados`, `src`, `densidade`, `aria-label` | `ui-sort`, `ui-column-resize`, `ui-linha-clique` (demais eventos: ver seção 4) |
751
+ | `<ui-tabela-propriedades>` | Inspetor de propriedades técnicas padrão AutoCAD & Revit com cálculos matemáticos inline (+, -, *, /, ^, %), arraste de valor contínuo (scrubbing), splitter redimensionável e editores CAD. | `categorias`, `tipo-objeto`, `splitter-pos`, `modo-aplicar`, `filtro` | `ui-propriedade-alterada`, `ui-aplicar`, `ui-desfazer`, `ui-editar-tipo`, `ui-acao-clique`, `ui-quick-select`, `ui-calculadora` |
752
+ | `<ui-ribbon>` | Barra de ferramentas em abas e grupos (estilo AutoCAD / Word / Excel): botões grandes e pequenos em colunas de 3, toggles, menus suspensos, abas contextuais, modo recolhido (duplo clique na aba) e navegação por teclado (roving tabindex). `aba-ativa` pode ser definido antes de `abas` (fica pendente); `aria-label` e `rotulo-abas` rotulam a toolbar e o tablist; `ui-ferramenta` é cancelável (`preventDefault()` reverte o toggle); `atalho` é só texto no ribbon. Use `id` únicos e `rotulo`/`rotuloAcessivel` em botões só de ícone. | `abas`, `aba-ativa`, `recolhido`, `compacto`, `aria-label`, `rotulo-abas` | `ui-ferramenta`, `ui-aba-change`, `ui-recolhido-change` |
753
+ | `<ui-paleta-ferramentas>` | Paleta de ferramentas (estilo Illustrator / Photoshop): seleção exclusiva, grupos com flyout (clique longo, botão direito ou seta), 1 ou 2 colunas, vertical/horizontal e atalhos de teclado que percorrem o grupo. `valor` pode vir antes de `ferramentas` (pendente) e `valor = ''` / `limpar()` remove a seleção; `ui-ferramenta` dispara sempre e é cancelável (`preventDefault()` reverte), `ui-change`/`ui-selecionar` só quando a seleção muda (ouça um deles, não ambos). Atalhos: use `atalhos` em uma paleta por página. | `ferramentas`, `valor`, `orientacao`, `colunas`, `tamanho`, `atalhos`, `aria-label` | `ui-change`, `ui-selecionar`, `ui-ferramenta` |
754
+ | `<ui-controle-cores>` | Par de cores de desenho (estilo Illustrator): preenchimento na frente, traço atrás, botões de inverter, "sem cor" e restaurar padrão, estado misto (`?`) e modo só traço. Clique no alvo ativo abre o `<ui-seletor-cor>`. Não registra atalhos: quem usa chama `alternarAlvo()`, `inverter()`, `semCor()`, `restaurarPadrao()` e pode exibir as teclas via `atalhos`. Tamanho ajustável com `--ui-controle-cores-amostra`. Entrada: `@thibezer/ui-components-kit/cores`. | `preenchimento`, `traco`, `opacidade`, `alvo`, `padrao-preenchimento`, `padrao-traco`, `titulo-seletor`, `so-traco`, `desabilitado`, `coresProjeto`, `definirCores()`, `definirMisto()` | `ui-cores-change` (`confirmado` false = pré-visualização), `ui-alvo-change` |
755
+ | `<ui-seletor-cor>` | Painel de cor flutuante: abas Preenchimento/Traço (ou cor única), prévia antes × depois, hexadecimal, seletor do sistema, opacidade, cores do projeto, paleta e "sem cor". Abra com `abrirSeletorCor({ ancora, ... })`: um por vez, Esc/clique fora confirmam, Cancelar restaura, `X` alterna o alvo. | `abrirSeletorCor()`, `fecharSeletorCor()`, `configurar()`, `modo` (`duplo`, `simples`), `alvo`, `preenchimento`, `traco`, `cor`, `opacidade`, `mostrarOpacidade`, `misto`, `coresProjeto`, `paleta`, `focar` | `ui-cor-input`, `ui-cor-confirmar`, `ui-cor-cancelar`, `ui-alvo-change` |
756
+ | `<ui-mapa>` | Mapa geográfico interativo com camadas OpenStreetMap. | `lat`, `lng`, `zoom`, `camadas` | - |
757
+ | `<ui-camadas>` | Painel de camadas vetoriais CAD/GIS com hierarquia em árvore, barra de arraste vertical (`redimensionavel`), estabilidade de altura sem colapso, drag-and-drop, métricas geodésicas e persistência no LocalStorage. | `redimensionavel`, `altura`, `min-altura`, `max-altura`, `flutuante`, `colapsado`, `camada-ativa`, `persistir` | `ui-redimensionar-altura`, `ui-camada-selecionada`, `ui-camada-visibilidade`, `ui-camadas-reordenadas`, `ui-acao-massa` |
758
+ | `<ui-canvas-cad>` | Mesa CAD/GIS para poligonais, vértices geodésicos e **feições genéricas** (ponto, linha, polígono, círculo, texto) que você cria: seleção por clique e caixa, popup, edição de vértices, snap, 15 ferramentas de desenho/medição, oficial × prévia, cursores remotos e culling/clusters para 100 mil feições. Linha multiparte, feição `oculta`, selo de texto e contorno de ponto. Nenhuma camada vem pronta. Guia: [`docs/guia-migracao-mapengine.md`](docs/guia-migracao-mapengine.md). | `pontos`, `segmentos`, `ferramentas`, `sem-camadas-padrao`, `sem-painel-camadas`, `sem-barra-rapida`, `sem-atalho-snap`, `aceitar-origem`, `inicializarAgora()`, `definirFeicoes()`, `aplicarLote()`, `definirFerramenta()`, `editarVertices()`, `definirMargensHud()`, `definirEstilosExternos()`, `fitBounds()` | `ui-ponto-selecionado`, `cad-selecao`, `cad-feicao-selecionada`, `cad-feicao-criada`, `cad-feicao-atualizada`, `cad-feicao-hover`, `cad-feicao-menu`, `cad-snap`, `cad-previas` e outros (ver `docs/canvas-cad-eventos.md`) |
759
+
760
+ #### `<ui-canvas-cad>`: montar só com API pública (2.1)
761
+
762
+ ```html
763
+ <ui-canvas-cad id="mesa" sem-camadas-padrao sem-painel-camadas sem-barra-rapida sem-atalho-snap aceitar-origem></ui-canvas-cad>
764
+ ```
765
+ ```js
766
+ const mapa = mesa.inicializarAgora(); // L.Map síncrono (idempotente; nunca cria um 2º mapa)
767
+ mesa.definirEstilosExternos([...document.styleSheets]); // CSS do app dentro do shadow root (string ou folhas)
768
+ mesa.definirMargensHud({ base: 56, direita: 320 }); // HUDs de desenho/edição fora de barras e painéis
769
+ mesa.aplicarLote('glebas', { adicionar, atualizar, remover }); // uma regravação, ordem preservada
770
+ mesa.atualizarFeicao('glebas', 'g1', { propriedades }, { substituirPropriedades: true });
771
+ mesa.definirOpcoesPopup({ copiarParaAreaDeTransferencia: false });
772
+ ```
773
+
774
+ - Camadas de fábrica (satélite, SIGEF, homologados, perímetro, vizinhos, vértices, grade) só somem com `sem-camadas-padrao`; sem
775
+ o atributo continuam como sempre.
776
+ - `Feicao.oculta` (não desenha nem seleciona, mas existe) ≠ `Feicao.excluida`. Linha multiparte: `geometria.partes`.
777
+ - Tudo, com exemplos: [`docs/guia-migracao-mapengine.md`](docs/guia-migracao-mapengine.md) · eventos e ordem das ferramentas:
778
+ [`docs/canvas-cad-eventos.md`](docs/canvas-cad-eventos.md) · números de desempenho: [`docs/desempenho-feicoes.md`](docs/desempenho-feicoes.md).
779
+
780
+
781
+ ---
782
+
783
+ ### Custom Elements Manifest
784
+ O pacote publica `custom-elements.json` (campo `customElements` do `package.json`), o formato padrão que IDEs, Storybook e geradores de documentação leem para listar tags, atributos, propriedades, eventos e `::part` de cada componente. Ele é gerado no `npm run build` pelo [`@custom-elements-manifest/analyzer`](https://custom-elements-manifest.open-wc.org/), e os tipos React/Vue são derivados dele — por isso nunca ficam desatualizados em relação ao código.
785
+
786
+ ---
787
+
788
+ ## 🛠️ Como Executar e Compilar Localmente
789
+
790
+ 1. **Instalar Dependências**:
791
+ ```bash
792
+ npm install
793
+ ```
794
+
795
+ 2. **Executar o Playground Sandbox em Desenvolvimento**:
796
+ ```bash
797
+ npm run dev
798
+ ```
799
+ Acesse: `http://localhost:5173/`
800
+
801
+ 3. **Compilar Pacote de Produção (Library Mode)**:
802
+ ```bash
803
+ npm run build
804
+ ```
805
+ Gera na pasta `dist/`:
806
+ * `dist/index.js` e `dist/<modulo>.js` (Módulos ES para Vite, Webpack, Rollup)
807
+ * `dist/index.cjs` e `dist/<modulo>.cjs` (CommonJS para `require`)
808
+ * `dist/*.d.ts` (Tipagens TypeScript)
809
+ * `dist/ui-kit.css` (Design Tokens compilados, 100% autocontido — o build falha se restar qualquer `@import`)
810
+ * `dist/custom-elements.json` (Custom Elements Manifest) e `dist/tipos-react.d.ts`, `dist/tipos-vue.d.ts`, `dist/tipos-elementos.d.ts`
811
+
812
+ 4. **Verificar os tipos gerados** (depois do build):
813
+ ```bash
814
+ npm run test:tipos
815
+ ```