@thibezer/ui-components-kit
🎨 UI Components Kit - Web Components Nativos & Universais
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.
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).
📑 Índice
- Por que Web Components?
- Como Importar na sua Aplicação
- Como Controlar os Componentes (5 Pilares)
- ⚡ Recursos Inteligentes de Alta Produtividade
- 💻 Exemplos de Uso por Tecnologia
- 🧩 Catálogo de Componentes & Referência
- 🛠️ Como Executar e Compilar Localmente
🎯 Por que Web Components?
- 🌐 100% Universal e Agnóstico: Funciona em qualquer stack sem necessidade de bibliotecas intermediárias.
- 🛡️ 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.
- 📐 Design Tokens Centralizados: Variáveis CSS personalizáveis para cores, tipografia, raios e densidade.
- 🚀 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.
📦 Como Importar na sua Aplicação
Opção 1: CDN / Script Tag (HTML puro, PHP, Django)
Adicione as tags no <head> ou antes do fechamento do </body>:
<!-- 1. Design Tokens e Estilos Globais -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@thibezer/ui-components-kit@1/dist/ui-kit.css">
<!-- 2. Biblioteca dos Componentes (Módulo ES) -->
<script type="module" src="https://cdn.jsdelivr.net/npm/@thibezer/ui-components-kit@1/dist/index.js"></script>
<!-- 3. Uso imediato no HTML -->
<ui-botao-primario variante="primary">Clique Aqui</ui-botao-primario>
Mapa e CAD via CDN (
<ui-mapa>,<ui-canvas-cad>): esses componentes dependem do Leaflet, importado comoleaflet(especificador "bare"), que o navegador só resolve com um import map. Declare-o antes do script da biblioteca:<script type="importmap"> { "imports": { "leaflet": "https://cdn.jsdelivr.net/npm/leaflet@1.9.4/+esm" } } </script>Os demais componentes não precisam do Leaflet. Com npm, basta
npm install leaflet.
Opção 2: NPM / Módulos ES (React, Vue, Next.js, Vite)
Instale pelo npm:
npm install @thibezer/ui-components-kit
O pacote é publicado já compilado (
dist/) e não executa build na instalação, então funciona em CI/CD e em servidores comnpm ci --omit=dev. A antiga instalação por URL Git (git+https://github.com/...) não é mais suportada: o repositório não versionadist/.
No ponto de entrada da sua aplicação (main.ts, index.js ou App.tsx):
// 1. Estilos globais obrigatórios (tokens CSS)
import '@thibezer/ui-components-kit/style.css';
// 2. Biblioteca completa (ou escolha os submódulos abaixo)
import '@thibezer/ui-components-kit';
🚀 Importação Modular e Otimizada (Sub-paths)
Pague apenas pelo que utilizar. Importe por domínio de funcionalidade ou por componente específico:
O export map expõe explicitamente os pontos de entrada canônicos (ESM em .js, CommonJS em .cjs e tipos .d.ts):
| Subpath | Arquivo (ESM) |
|---|---|
@thibezer/ui-components-kit |
dist/index.js |
@thibezer/ui-components-kit/camadas |
dist/camadas.js |
@thibezer/ui-components-kit/forms |
dist/forms.js |
@thibezer/ui-components-kit/feedback |
dist/feedback.js |
@thibezer/ui-components-kit/tabela |
dist/tabela.js |
@thibezer/ui-components-kit/style.css |
dist/ui-kit.css (100% autocontido, sem @import) |
1. Por Domínio / Categoria:
import '@thibezer/ui-components-kit/forms'; // Campos, Select, Checkbox, Radio, Switch (~8 kB gzip)
import '@thibezer/ui-components-kit/feedback'; // Modais, Drawers, Alertas, Toasts, Tooltips, Skeletons (~6 kB gzip)
import '@thibezer/ui-components-kit/data'; // Tabela, Tabela-Propriedades, Stat KPI, Badge (~4 kB gzip)
import '@thibezer/ui-components-kit/tools'; // Ribbon CAD/Office e Paleta de Ferramentas (~10 kB gzip)
import '@thibezer/ui-components-kit/core'; // Apenas UIBus, Zero-JS e ListenerBag (< 1 kB gzip)
import '@thibezer/ui-components-kit/canvas'; // Mesa CAD vetorial e motor GIS (~33 kB gzip)
import '@thibezer/ui-components-kit/camadas'; // Painel de Camadas GIS/CAD redimensionável (~21 kB gzip)
import '@thibezer/ui-components-kit/mapa'; // Mapa Geográfico Leaflet (~3 kB gzip)
2. Granular por Componente (Ultra Leve):
import '@thibezer/ui-components-kit/botao'; // Apenas <ui-botao> e <ui-botao-primario> (~3.4 kB gzip)
import '@thibezer/ui-components-kit/campo-texto'; // Apenas <ui-campo-texto> com FACE e anti-zoom (~3.9 kB gzip)
import '@thibezer/ui-components-kit/modal'; // Apenas <ui-modal> com bottom-sheet mobile (~3.3 kB gzip)
import '@thibezer/ui-components-kit/card'; // Apenas <ui-card> com elevação (~2.3 kB gzip)
import '@thibezer/ui-components-kit/tabela'; // Apenas <ui-tabela> virtualizada
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.
🎛️ Como Controlar os Componentes (5 Pilares)
┌─────────────────────────────────────────────────────────────┐
│ SISTEMA EXTERNO │
└──────┬───────────────▲─────────────────┬─────────────────▲──┘
│ │ │ │
1. Atributos 4. Eventos Custom 2. Propriedades 3. Métodos
(HTML) (ui-*) (Objetos/Listas) (Imperativo)
│ │ │ │
┌──────▼───────────────┴─────────────────▼─────────────────┴──┐
│ WEB COMPONENT (<ui-*>) │
└─────────────────────────────────────────────────────────────┘
1. Atributos HTML (Controle Declarativo)
Configurações simples passadas diretamente nas tags:
<ui-campo-texto label="CPF" placeholder="000.000.000-00" obrigatorio></ui-campo-texto>
<ui-modal id="meu-modal" titulo="Confirmar Exclusão"></ui-modal>
📏 Ajuste de Altura e Dimensionamento Inteligente (Zero Atrito)
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:
1. Via Atributo de Tamanho Predefinido (tamanho ou densidade):
<!-- Compacto (26px) - Ideal para Toolbars CAD/GIS, tabelas e alta densidade -->
<ui-campo-texto tamanho="sm" placeholder="26px Compacto"></ui-campo-texto>
<ui-lista-flutuante tamanho="sm" placeholder="Selecione..."></ui-lista-flutuante>
<!-- Padrão (34px) - Padrão moderno e equilibrado para formulários e modais -->
<ui-campo-texto tamanho="md" placeholder="34px Padrão"></ui-campo-texto>
<ui-lista-flutuante tamanho="md" placeholder="Selecione..."></ui-lista-flutuante>
<!-- Conforto / Touch (42px) - Otimizado para dispositivos móveis e telas sensíveis ao toque -->
<ui-campo-texto tamanho="lg" placeholder="42px Conforto"></ui-campo-texto>
<ui-lista-flutuante tamanho="lg" placeholder="Selecione..."></ui-lista-flutuante>
2. Via Atributo de Altura Explícita (altura ou height):
<!-- Define a altura exata em pixels diretamente no elemento -->
<ui-campo-texto altura="38" placeholder="Altura de 38px"></ui-campo-texto>
<ui-lista-flutuante altura="44px" placeholder="Altura de 44px"></ui-lista-flutuante>
<ui-botao-primario altura="40">Salvar</ui-botao-primario>
3. Via Variável CSS Customizável (--ui-campo-altura):
/* No seu CSS global ou no container pai */
.meu-formulario {
--ui-campo-altura: 36px; /* Aplica instantaneamente em todos os inputs, selects e botões filhos */
}
4. Via Barramento Global de Densidade (UIBus):
import { UIBus } from '@thibezer/ui-components-kit';
// Altera a densidade de toda a aplicação em tempo real:
UIBus.definirDensidade('compacta'); // 26px
UIBus.definirDensidade('padrao'); // 34px
UIBus.definirDensidade('relaxada'); // 42px
UIBus.definirDensidade(38); // Valor numérico em px customizado
5. Via Contêiner de Linha (<ui-linha>): a forma de menor esforço
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%).
<ui-linha tamanho="sm" justificar="entre">
<ui-segmented valor="todos"><span valor="todos">Todos</span><span valor="abertos">Abertos</span></ui-segmented>
<ui-campo-texto placeholder="Buscar..."></ui-campo-texto> <!-- ocupa o espaço que sobrar -->
<ui-botao variante="primario">Adicionar</ui-botao>
<ui-badge controle>Carlos</ui-badge> <!-- [controle]: badge com a altura dos controles -->
</ui-linha>
Atributo (ui-linha) |
Valores | Padrão |
|---|---|---|
tamanho / densidade |
sm (26px), md (34px), lg (42px); também compacta, padrao, relaxada |
herda do token global |
altura |
40, 40px, 2.5rem (vence tamanho) |
- |
alinhar |
centro, inicio, fim, esticar (use fim quando um campo tem rótulo acima) |
centro |
justificar |
inicio, centro, fim, entre, ao-redor |
inicio |
espaco |
nenhum, xs, sm, md, lg |
sm |
sem-quebra / inline |
booleanos | a linha quebra |
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.
2. Propriedades JavaScript (Dados Complexos)
Para enviar arrays e objetos complexos:
const tabela = document.querySelector('ui-tabela');
tabela.colunas = [
{ id: 'ponto', rotulo: 'Ponto', ordenavel: true },
{ id: 'altitude', rotulo: 'Altitude (m)' }
];
tabela.dados = [
{ ponto: 'P-01', altitude: '542,15' },
{ ponto: 'P-02', altitude: '545,30' }
];
3. Métodos Imperativos (Ações Diretas)
Comandos diretos chamados na instância do elemento:
const modal = document.getElementById('meu-modal');
modal.abrir();
modal.fechar();
4. Eventos Customizados Nativos (ui-*)
Escuta de eventos disparados pelas interações do usuário:
const campo = document.querySelector('ui-campo-texto');
campo.addEventListener('ui-input', (e) => {
console.log('Texto digitado:', e.detail.value);
});
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). Atribuirpainel.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, passeemitirEvento = true(ex.:painel.definirCamadaAtiva('lotes', true)).
5. Serviços Globais e Helpers
Utilitários prontos para uso sem necessidade de instanciar elementos:
import { UIToast, UIBus } from '@thibezer/ui-components-kit';
UIToast.notificar({
tipo: 'sucesso',
titulo: 'Registro Salvo!',
mensagem: 'Os dados foram sincronizados.',
duracao: 4000
});
6. Escala de Elevação (z-index)
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:
:root {
--ui-z-workspace-hud: 500; /* <ui-camadas flutuante>, <ui-seletor-mapa-base>, toolbars do canvas */
--ui-z-docked-bar: 600; /* <ui-ribbon> recolhido, <ui-tabela-propriedades flutuante> */
--ui-z-drawer: 700; /* <ui-drawer> e backdrop */
--ui-z-modal: 1000; /* <ui-modal> e backdrop */
--ui-z-popover: 1050; /* menus suspensos/contexto, <ui-select> (acima do modal, funciona dentro dele) */
--ui-z-toast: 1100; /* UIToast */
--ui-z-tooltip: 1200; /* <ui-tooltip> */
}
7. Acessibilidade Nativa de Rótulos
<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:
<ui-input label="Nome"></ui-input> <!-- <label for> ↔ <input> interno -->
<ui-select label="Cidade">...</ui-select> <!-- aria-labelledby = rótulo + valor atual -->
<ui-switch label="Modo escuro"></ui-switch> <!-- role="switch" aria-labelledby -->
<!-- Sem o atributo label, o nome acessível vem de fora do Shadow DOM: -->
<label>Telefone <ui-input></ui-input></label>
<ui-switch aria-label="Camada visível"></ui-switch>
8. Temas Claro e Escuro
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:
<html data-tema="escuro"> <!-- ou "claro"; também aceitos: class="dark|light" e data-theme="dark|light" -->
<aside data-tema="claro">…</aside> <!-- trecho claro dentro de uma página escura -->
UIBus.definirTema('auto'); // volta a seguir o SO
UIBus.on('tema:alterado', ({ tema }) => { /* também dispara quando o SO muda, se o tema não estiver fixado */ });
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.
| Token | Uso |
|---|---|
--ui-cor-texto-sucesso / -erro / -alerta / -info |
Texto e tons de status (alertas, badges, toasts) |
--ui-cor-texto-sobre-status |
Texto sobre fundo sólido de status (badge sólido) |
--ui-cor-fundo-recuado |
Faixas rebaixadas (rodapés de modal e drawer) |
--ui-sombra-sm / -md / -lg |
Sombras: densas no escuro, suaves no claro |
9. Tipografia e Raios
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á):
:root {
--ui-fonte-base: 'Roboto', sans-serif; /* textos da interface */
--ui-fonte-codigo: 'JetBrains Mono', monospace; /* coordenadas, valores técnicos, código */
--ui-raio-borda: 4px; /* base: botões, campos, menus */
/* derivados automaticamente: --ui-raio-sm (2/3), --ui-raio-lg (5/3), --ui-raio-xl (8/3) */
/* --ui-raio-borda: 0 → interface totalmente reta */
}
10. Estilização Interna com ::part
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):
| Componente | Parts |
|---|---|
<ui-botao> / <ui-botao-primario> |
base, spinner |
<ui-campo-texto> / <ui-input> |
rotulo, campo, input, icone, icone-esquerda, icone-direita, ajuda |
<ui-area-texto> / <ui-textarea> |
rotulo, campo, input, rodape, ajuda, contador |
<ui-texto> |
base |
<ui-lista-flutuante> / <ui-select> |
rotulo, campo, valor, seta, lista, opcao, opcao-selecionada |
<ui-checkbox>, <ui-radio>, <ui-switch> |
base, controle, indicador, rotulo |
<ui-segmented> |
base, indicador, opcao, opcao-selecionada |
<ui-modal>, <ui-drawer> |
fundo, painel, cabecalho, titulo, descricao (drawer), fechar, corpo, rodape |
<ui-card> |
base, midia, cabecalho, corpo, rodape |
<ui-badge> / <ui-chip> |
base, rotulo, remover |
<ui-alerta> / toasts |
base, icone, conteudo, titulo, mensagem, acao, fechar, progresso |
<ui-tooltip> |
balao, texto, seta |
<ui-tabela> |
base, tabela, celula-cabecalho, ordenar, linha, linha-selecionada, celula, vazio, carregando |
ui-campo-texto::part(campo) { border-width: 2px; }
ui-modal::part(painel) { max-width: 720px; }
ui-tabela::part(linha-selecionada) { background: #0b3d2a; }
:is(ui-checkbox, ui-radio, ui-switch)::part(rotulo) { font-weight: 600; }
⚡ Recursos Inteligentes de Alta Produtividade
1. Ações Declarativas "Zero-JS" (Triggers Nativos)
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:
<!-- Abrir Modal sem JS -->
<ui-botao-primario target-modal="modal-exclusao" variante="destrutivo">
Excluir Registro
</ui-botao-primario>
<!-- Modal com Fechamento e Toast automáticos -->
<ui-modal id="modal-exclusao" titulo="Confirmação">
Tem certeza que deseja excluir?
<div slot="rodape">
<ui-botao-primario dismiss-modal variante="secundario">Cancelar</ui-botao-primario>
<ui-botao-primario dismiss-modal toast-sucesso="Excluído com sucesso!" variante="destrutivo">
Confirmar
</ui-botao-primario>
</div>
</ui-modal>
<!-- Copiar Texto em 1 Clique -->
<ui-campo-texto id="input-token" value="SIGEF-2026-X88"></ui-campo-texto>
<ui-botao-primario copiar-texto="#input-token" copiar-mensagem="Token copiado!">
Copiar Chave
</ui-botao-primario>
Tabela de Atributos Zero-JS Disponíveis:
| Atributo | Descrição | Exemplo de Uso |
|---|---|---|
target-modal="id" |
Abre o modal com o ID especificado ao clicar. | target-modal="modal-login" |
dismiss-modal |
Fecha o modal atual (ou o especificado por ID). | dismiss-modal |
toast-sucesso="msg" |
Dispara uma notificação flutuante de sucesso. | toast-sucesso="Salvo com sucesso!" |
toast-erro="msg" |
Dispara uma notificação flutuante de erro. | toast-erro="Falha na conexão." |
toast-alerta="msg" |
Dispara uma notificação flutuante de aviso. | toast-alerta="Preencha os campos obrigatórios." |
copiar-texto="texto|#id" |
Copia o texto ou o valor do elemento referenciado. | copiar-texto="#campo-pix" |
limpar-form="id" |
Reseta todos os campos do formulário indicado. | limpar-form="form-cadastro" |
alternar-tema |
Alterna entre tema claro e escuro globalmente (a partir do tema em vigor, inclusive o do SO). | alternar-tema |
2. Formulários Automáticos com FormData Nativo (W3C FACE)
Todos os campos (<ui-campo-texto>, <ui-area-texto>, <ui-lista-flutuante>, <ui-checkbox>, <ui-radio>, <ui-switch>) implementam a especificação oficial de Form-Associated Custom Elements.
Isso significa que você extrai os dados de todos os campos de uma única vez, sem ler um por um:
<form id="form-imovel">
<ui-campo-texto name="denominacao" label="Nome da Fazenda" value="Boa Vista"></ui-campo-texto>
<ui-campo-texto name="municipio" label="Município" value="Uberlândia"></ui-campo-texto>
<ui-checkbox name="georreferenciado" label="Georreferenciado" marcado value="sim"></ui-checkbox>
<ui-botao-primario type="submit">Salvar Imóvel</ui-botao-primario>
</form>
const form = document.getElementById('form-imovel');
form.addEventListener('submit', (e) => {
e.preventDefault();
// Extrai TODOS os campos em 1 única linha nativa:
const dados = Object.fromEntries(new FormData(form));
console.log(dados);
// Resultado: { denominacao: "Boa Vista", municipio: "Uberlândia", georreferenciado: "sim" }
});
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.
3. Barramento Global de Orquestração (UIBus)
O UIBus é uma central reativa que permite comandar qualquer componente do sistema ou ouvir eventos sem precisar de querySelector:
import { UIBus } from '@thibezer/ui-components-kit';
// 1. Executar Ações Diretas:
UIBus.abrirModal('modal-confirmacao');
UIBus.fecharModal('modal-confirmacao');
UIBus.notificar({ tipo: 'sucesso', mensagem: 'Operação concluída!' });
UIBus.copiar('Texto para a área de transferência', 'Copiado!');
UIBus.definirDensidade('compacta'); // 'compacta' | 'normal' | 'relaxada'
UIBus.definirTema('escuro'); // 'claro' | 'escuro' | 'auto' (segue o SO); sem argumento, alterna
UIBus.obterTema(); // tema em vigor: 'claro' | 'escuro'
// 2. Comunicação Desacoplada (Pub/Sub):
UIBus.on('vertice:selecionado', (dadosVertice) => {
console.log('Vértice selecionado em qualquer tela:', dadosVertice);
});
UIBus.emit('vertice:selecionado', { id: 101, nome: 'M-01', altitude: 432.10 });
4. Tabelas Autônomas com Auto-Fetch Remoto
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:
<ui-tabela
id="tabela-vertices"
src="/api/v1/geodesia/vertices"
max-height="400px"
densidade="compacta">
</ui-tabela>
const tabela = document.getElementById('tabela-vertices');
// Definir colunas da tabela:
tabela.colunas = [
{ id: 'nome_vertice', rotulo: 'Vértice', ordenavel: true },
{ id: 'este', rotulo: 'Este (m)', ordenavel: true, alinhamento: 'direita' },
{ id: 'norte', rotulo: 'Norte (m)', ordenavel: true, alinhamento: 'direita' }
];
// Métodos inteligentes disponíveis na tabela:
tabela.filtrar('M-01'); // Filtra instantaneamente em todas as colunas
tabela.recarregar(); // Recarrega os dados da API remota
Autenticação e opções do fetch (o src sozinho faz um GET sem headers):
tabela.fetchOpcoes = { headers: { Authorization: `Bearer ${token}` }, credentials: 'include' };
// ou troque a função de busca (renovação de token, mocks):
tabela.fetcher = (url, init) => meuFetchAutenticado(url, init);
const ok = await tabela.carregarDoEndpoint('/api/v1/vertices'); // true em sucesso; nunca rejeita
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.
Eventos de <ui-tabela> (todos bubbles e composed; só ui-sort é cancelável):
| Evento | detail |
Quando |
|---|---|---|
ui-sort (cancelável) |
{ idColuna, direcao } |
Clique no cabeçalho ordenável. direcao: 'original' com idColuna: null = ordenação removida |
ui-column-resize |
{ idColuna, largura } ("120px" ou "auto") |
Fim do arraste, popover ou duplo clique; não dispara em clique sem arrastar |
ui-linha-clique |
{ item, indice } |
Clique/Enter/Espaço numa linha. indice é a posição exibida (após ordenar/filtrar): use item |
ui-linha-selecionada |
{ item, indice } |
rolarPara(..., { selecionar: true }) |
ui-selecao-removida |
{ item } |
O item selecionado saiu dos dados |
ui-selecao-atualizada |
{ item, anterior } |
Os dados foram recarregados e o registro selecionado agora é outro objeto |
ui-fetch-start |
{ url } |
Início da requisição do src |
ui-fetch-sucesso |
{ url, total, dados } |
Dados recebidos |
ui-fetch-erro |
{ url, erro, origem, status? } |
Falha. origem: rede, http (com status), json ou render |
ui-fetch-cancelado |
{ url } |
Requisição abortada ou substituída (fecha o par do ui-fetch-start) |
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.
💻 Exemplos de Uso por Tecnologia
HTML Puro + JavaScript
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<link rel="stylesheet" href="dist/ui-kit.css">
<script type="module" src="dist/index.js"></script>
</head>
<body>
<ui-campo-texto id="meu-campo" label="Nome do Projeto"></ui-campo-texto>
<ui-botao-primario target-modal="meu-modal">Abrir Detalhes</ui-botao-primario>
<ui-modal id="meu-modal" titulo="Detalhes">
<p>Conteúdo do modal...</p>
<div slot="rodape">
<ui-botao-primario dismiss-modal>Fechar</ui-botao-primario>
</div>
</ui-modal>
<script>
document.getElementById('meu-campo').addEventListener('ui-input', (e) => {
console.log('Digitado:', e.detail.value);
});
</script>
</body>
</html>
React / Next.js (SSR & Client Components)
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.
Tipos no TSX (autocompletar e checagem)
O pacote gera tipos para todas as tags a partir do Custom Elements Manifest. Registre-os uma vez no tsconfig.json:
{ "compilerOptions": { "types": ["@thibezer/ui-components-kit/tipos-react"] } }
A partir daí o editor sugere atributos, propriedades e eventos de cada tag, e o TypeScript recusa valores inválidos:
<ui-tabela
colunas={[{ id: 'ponto', rotulo: 'Ponto', ordenavel: true }]} // tipado como TabelaColuna[]
densidade="compacta" // 'compacta' | 'normal' | 'relaxada'
onui-sort={(e) => console.log(e.detail)} // eventos ui-* no formato do React 19
/>
<ui-tabela densidade="enorme" /> // ❌ erro de tipo
Outros JSX (Preact, Solid etc.) podem usar o mapa agnóstico ElementosUI de @thibezer/ui-components-kit/tipos-elementos.
No Next.js (App Router):
Crie um componente cliente provedor ou registre no topo do seu layout/página cliente:
// components/UIProvider.tsx
'use client';
import { useEffect } from 'react';
import '@thibezer/ui-components-kit/style.css';
import '@thibezer/ui-components-kit/register'; // Registra todos os Custom Elements com segurança no cliente
export function UIProvider({ children }: { children: React.ReactNode }) {
return <>{children}</>;
}
Ou registre de forma modular apenas o que a tela precisa:
// app/dashboard/page.tsx
'use client';
import React, { useEffect, useRef, useState } from 'react';
import '@thibezer/ui-components-kit/style.css';
import '@thibezer/ui-components-kit/forms';
import '@thibezer/ui-components-kit/botao';
import '@thibezer/ui-components-kit/tabela';
import { UIBus } from '@thibezer/ui-components-kit/core';
import type { UITabela } from '@thibezer/ui-components-kit/tabela';
export default function DashboardPropriedades() {
const tabelaRef = useRef<UITabela>(null);
const [nome, setNome] = useState('');
useEffect(() => {
if (tabelaRef.current) {
tabelaRef.current.colunas = [
{ id: 'id', rotulo: 'Código' },
{ id: 'nome', rotulo: 'Propriedade' }
];
tabelaRef.current.dados = [
{ id: 1, nome: 'Fazenda Santa Luzia' }
];
}
}, []);
return (
<div style={{ padding: 24 }}>
<ui-campo-texto
label="Pesquisar Imóvel"
value={nome}
onInput={(e: any) => {
// Os eventos nativos `input`/`change` não têm `detail`: leia o valor do próprio elemento
const valor = e.target.value;
setNome(valor);
tabelaRef.current?.filtrar(valor);
}}
></ui-campo-texto>
<ui-tabela ref={tabelaRef} max-height="300px" densidade="compacta"></ui-tabela>
<ui-botao-primario
variante="primary"
onClick={() => UIBus.notificar({ tipo: 'sucesso', mensagem: 'Dados sincronizados!' })}>
Sincronizar
</ui-botao-primario>
</div>
);
}
Vue.js
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:
{ "compilerOptions": { "types": ["@thibezer/ui-components-kit/tipos-vue"] } }
// vite.config.js
vue({ template: { compilerOptions: { isCustomElement: (tag) => tag.startsWith('ui-') } } })
<template>
<div class="container">
<ui-campo-texto
:value="termoBusca"
label="Buscar Vértice"
@ui-input="aoDigitar"
/>
<ui-lista-flutuante
.itens="opcoes"
@ui-selecionar="aoSelecionar"
/>
<ui-botao-primario target-modal="modal-confirmacao">
Confirmar Operação
</ui-botao-primario>
</div>
</template>
<script setup>
import { ref } from 'vue';
import '@thibezer/ui-components-kit/style.css';
import '@thibezer/ui-components-kit';
const termoBusca = ref('');
const opcoes = ref([
{ id: '1', label: 'Marco Geodésico M' },
{ id: '2', label: 'Ponto Topográfico P' }
]);
function aoDigitar(e) {
termoBusca.value = e.detail.value;
}
function aoSelecionar(e) {
console.log('Opção selecionada:', e.detail);
}
</script>
Iframes / Microfrontends
Comunicação segura entre iframes e sistemas pais via eventos do UIBus:
// Dentro do Iframe (Filho):
import { UIBus } from '@thibezer/ui-components-kit';
UIBus.on('vertice:salvo', (dados) => {
window.parent.postMessage({ tipo: 'VERTICE_SALVO', payload: dados }, '*');
});
// No Sistema Principal (Pai):
window.addEventListener('message', (event) => {
if (event.data?.tipo === 'VERTICE_SALVO') {
console.log('Recebido do Iframe:', event.data.payload);
}
});
🧩 Catálogo de Componentes & Referência
🟢 Camada 1: Fundamentos
| Tag | Descrição | Principais Atributos / Props | Eventos Emitidos |
|---|---|---|---|
<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 |
<ui-campo-texto> |
Input de texto com Floating Label, validação e senha. | label, value, tipo, obrigatorio, name |
ui-input, ui-change |
<ui-area-texto> |
Texto longo de várias linhas (descrições, laudos): contador, altura automática, redimensionamento, form-associated. Enter quebra a linha; Ctrl/Cmd+Enter emite ui-enviar. |
label, value, linhas, auto-altura, linhas-max, maxlength, contador, obrigatorio, name |
ui-input, ui-change, ui-enviar |
<ui-lista-flutuante> |
Select/Dropdown com Bottom Sheet no mobile. | itens, value, placeholder, name |
ui-selecionar |
<ui-texto> |
Tipografia semântica (H1-H6, corpo, caption, código). Texto longo: linhas="3" limita a N linhas com reticências e palavras compridas quebram. |
variante, cor, peso, linhas, truncar |
- |
<ui-icone> |
Wrapper para ícones vetoriais padronizados. | nome, tamanho, cor |
- |
🔵 Camada 2: Controles & Seleção
| Tag | Descrição | Principais Atributos / Props | Eventos Emitidos |
|---|---|---|---|
<ui-checkbox> |
Caixa de seleção múltipla com estado indeterminado. | marcado, indeterminado, name, value |
ui-change |
<ui-radio> |
Opção única com agrupamento automático por nome. | marcado, name, value |
ui-change |
<ui-switch> |
Chave de alternância liga/desliga física instantânea. | ativo, name, value |
ui-change |
<ui-segmented> |
Controle segmentado de opções (Pills/Tabs deslizantes). | opcoes, value, name, tamanho, altura |
ui-change, ui-selecionar |
<ui-badge> / <ui-chip> |
Tags de status e chips com botão de remoção. | variante, removivel, value, controle |
ui-remove |
<ui-linha> |
Contêiner que alinha controles na mesma linha (altura única, espaço, quebra). | tamanho, altura, alinhar, justificar, espaco, sem-quebra |
- |
<ui-avatar> |
Avatar com fotos, iniciais e status online. | nome, src, status, tamanho |
- |
🟣 Camada 3: Contêineres, Overlays, Dados & GIS
| Tag | Descrição | Principais Atributos / Props | Eventos Emitidos |
|---|---|---|---|
<ui-card> |
Cartão de conteúdo com slots nomeados e elevação. | elevacao, clicavel |
ui-click |
<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 |
<ui-drawer> |
Painel lateral deslizante (Sheet/Gaveta direita/esquerda). | aberto, posicao (direita, esquerda, baixo, cima), titulo, largura |
ui-abrir, ui-fechar |
<ui-alerta> |
Banner de notificação contextual fixo. | variante, titulo, fechavel |
ui-fechar |
<ui-toast> |
Notificações flutuantes inteligentes com pilha flex. | UIToast.notificar({ tipo, mensagem }) |
- |
<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 |
- |
<ui-skeleton> |
Placeholder de carregamento animado com pulsos. | tipo, largura, altura, animado |
- |
<ui-stat> |
Cartão KPI de estatísticas, métricas e tendências. | rotulo, valor, variacao, tendencia (alta, baixa, neutro) |
- |
<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) |
<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. Seleção múltipla: propriedade com valores diferentes entre os objetos (VALOR_MISTO, misto: true ou combinarValores()) mostra "Varia" (texto via texto-misto); as iguais mostram o valor comum. |
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 |
<ui-ribbon> |
Barra de ferramentas em abas e grupos (estilo AutoCAD / Word / Excel): botões grandes e pequenos em colunas de 3, split buttons (item.dividido), toggles, exclusividade configurável (grupo.exclusivo), menus suspensos com submenus aninhados, lançador de diálogo (grupo.lancadorDialogo), colapso automático responsivo de grupos (ResizeObserver + grupo.prioridade), abas contextuais, ativação manual de abas (ativacao-manual), atalhos de teclado e KeyTips (keytips), modo controlado sem mutação (copiar-dados), tooltips estilizados 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). Use id únicos e rotulo/rotuloAcessivel em botões só de ícone. |
abas, aba-ativa, recolhido, compacto, ativacao-manual, copiar-dados, atalhos, keytips, aria-label, rotulo-abas |
ui-ferramenta, ui-aba-change, ui-recolhido-change, ui-grupo-dialogo |
<ui-paleta-ferramentas> |
Paleta de ferramentas gráficas (estilo Illustrator / Photoshop): seleção exclusiva, grupos com flyout expansível e submenus aninhados, navegação em grade bidimensional para colunas="2", suporte a atalhos compostos (Ctrl+Z, Shift+V, Esc) com isolamento multi-paleta, modo controlado sem mutação (copiar-dados), orientações vertical/horizontal e tooltips leves integrados no Shadow DOM. 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). |
ferramentas, valor, orientacao, colunas, tamanho, atalhos, copiar-dados, aria-label |
ui-change, ui-selecionar, ui-ferramenta |
<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 |
<ui-seletor-cor> |
Painel de cor flutuante: abas Preenchimento/Traço (ou cor única), prévia antes × depois, hexadecimal, seletor do sistema, campos numéricos de espessura (px) e opacidade (%) no estilo Illustrator (setas ▲▼, ↑/↓ e roda, Shift ×10) 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, espessuraTraco, mostrarEspessura, misto, focar |
ui-cor-input, ui-cor-confirmar, ui-cor-cancelar, ui-alvo-change |
<ui-mapa> |
Mapa geográfico interativo com camadas OpenStreetMap. | lat, lng, zoom, camadas |
- |
<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 |
<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 (Enter, botão Editar ou popup; desfazer, refazer e cancelar), 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. |
pontos, segmentos, ferramentas, sem-camadas-padrao, sem-painel-camadas, sem-barra-rapida, sem-atalho-snap, sem-avisos, aceitar-origem, popups, linha-* (linha padrão: tipo de linha CAD, escala, opacidade, pontas, cantos; ver docs/linha-padrao-cad.md), definirLinhaPadrao(), renderizador (leaflet | canvas: motor + Canvas 2D para dezenas de milhares de formas, ver docs/motor-formas.md), estatisticasFeicoes(), abrirPopupFeicao(), 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) |
<ui-canvas-cad>: montar só com API pública (2.1)
<ui-canvas-cad id="mesa" sem-camadas-padrao sem-painel-camadas sem-barra-rapida sem-atalho-snap aceitar-origem></ui-canvas-cad>
const mapa = mesa.inicializarAgora(); // L.Map síncrono (idempotente; nunca cria um 2º mapa)
mesa.definirEstilosExternos([...document.styleSheets]); // CSS do app dentro do shadow root (string ou folhas)
mesa.definirMargensHud({ base: 56, direita: 320 }); // HUDs de desenho/edição fora de barras e painéis
mesa.aplicarLote('glebas', { adicionar, atualizar, remover }); // uma regravação, ordem preservada
mesa.atualizarFeicao('glebas', 'g1', { propriedades }, { substituirPropriedades: true });
mesa.definirOpcoesPopup({ copiarParaAreaDeTransferencia: false });
- Camadas de fábrica (satélite, SIGEF, homologados, perímetro, vizinhos, vértices, grade) só somem com
sem-camadas-padrao; sem o atributo continuam como sempre. Feicao.oculta(não desenha nem seleciona, mas existe) ≠Feicao.excluida. Linha multiparte:geometria.partes.- Tudo, com exemplos:
docs/guia-migracao-mapengine.md· eventos e ordem das ferramentas:docs/canvas-cad-eventos.md· números de desempenho:docs/desempenho-feicoes.md.
Custom Elements Manifest
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, e os tipos React/Vue são derivados dele — por isso nunca ficam desatualizados em relação ao código.
🛠️ Como Executar e Compilar Localmente
-
Instalar Dependências:
npm install -
Executar o Playground Sandbox em Desenvolvimento:
npm run devAcesse:
http://localhost:5173/ -
Compilar Pacote de Produção (Library Mode):
npm run buildGera na pasta
dist/:dist/index.jsedist/<modulo>.js(Módulos ES para Vite, Webpack, Rollup)dist/index.cjsedist/<modulo>.cjs(CommonJS pararequire)dist/*.d.ts(Tipagens TypeScript)dist/ui-kit.css(Design Tokens compilados, 100% autocontido — o build falha se restar qualquer@import)dist/custom-elements.json(Custom Elements Manifest) edist/tipos-react.d.ts,dist/tipos-vue.d.ts,dist/tipos-elementos.d.ts
-
Verificar os tipos gerados (depois do build):
npm run test:tipos
Related Packages
Framework-agnostic custom elements for the Kanso Protocol design system — use co...
Open-source CLI for measuring design-system component usage across multi-framewo...
Scan React and Vue repos for design-system component usage, and upload the resul...
Version History
| Version | Released | License |
|---|---|---|
| 2.11.1 | MIT | |
| 2.11.0 | MIT | |
| 2.10.1 | MIT | |
| 2.10.0 | MIT | |
| 2.9.0 | MIT | |
| 2.8.2 | MIT | |
| 2.8.1 | MIT | |
| 2.8.0 | MIT | |
| 2.7.0 | MIT | |
| 2.6.1 | MIT | |
| 2.6.0 | MIT | |
| 2.5.0 | MIT | |
| 2.4.1 | MIT | |
| 2.4.0 | MIT | |
| 2.3.0 | MIT | |
| 2.2.2 | MIT | |
| 2.2.1 | MIT | |
| 2.2.0 | MIT | |
| 2.1.0 | MIT | |
| 2.0.0 | MIT | |
| 1.0.0 | MIT |