@jsweb/ui 1.2.8 → 1.3.1
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.
- package/.agents/skills/update-documentation/SKILL.md +135 -0
- package/.prettierignore +3 -0
- package/.prettierrc +7 -0
- package/CHANGELOG.md +137 -0
- package/LICENSE +21 -21
- package/PROJECT.md +107 -0
- package/README.md +202 -18
- package/SKILL.md +609 -0
- package/dist/CHANGELOG.md +137 -0
- package/dist/LICENSE +21 -0
- package/dist/README.md +292 -0
- package/dist/SKILL.md +609 -0
- package/dist/index.es.js +2 -0
- package/dist/index.es.js.map +1 -0
- package/dist/index.umd.js +2 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/package.json +34 -0
- package/dist/src/index.d.ts +4 -0
- package/{src → dist/src}/parser.d.ts +2 -1
- package/dist/src/reactivity.d.ts +48 -0
- package/index.html +196 -0
- package/package.json +24 -8
- package/publish.js +36 -0
- package/src/evaluator.ts +29 -0
- package/src/index.ts +11 -0
- package/src/parser.ts +490 -0
- package/src/reactivity.ts +227 -0
- package/tsconfig.json +23 -0
- package/vite.config.ts +16 -0
- package/index.es.js +0 -2
- package/index.es.js.map +0 -1
- package/index.umd.js +0 -2
- package/index.umd.js.map +0 -1
- package/src/index.d.ts +0 -3
- package/src/reactivity.d.ts +0 -24
- /package/{index.d.ts → dist/index.d.ts} +0 -0
- /package/{src → dist/src}/evaluator.d.ts +0 -0
- /package/{vite.config.d.ts → dist/vite.config.d.ts} +0 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Todas as alterações notáveis neste projeto serão documentadas neste arquivo.
|
|
4
|
+
|
|
5
|
+
O formato é baseado em [Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0/) e este projeto adere ao [Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## [Unreleased]
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## [1.3.1] - 2026-09-24
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **Tipagem Contextual `ThisType<T & ScopeContext>`**: Inclusão de `ThisType` na assinatura de `reactive` e `createScope`, fornecendo autocomplete e tipagem estrita de `this` em métodos e _getters_ de objetos literais, com acesso direto a `$refs`, `$emit` e `$el` como propriedades não-opcionais.
|
|
18
|
+
- **Classe Base `Scope` e Interface `ScopeContext`**: Exportação da classe utilitária `Scope` e da interface `ScopeContext` para suporte nativo e tipado a arquiteturas orientadas a objetos (`class MyScope extends Scope`). Na classe `Scope`, `$el` e `$refs` são propriedades somente leitura (com `$refs` mantendo sua instância original de Map viva) e `$emit` como método `protected` com disparo nativo em `$el`, permitindo sobrescrita (`override`) nas subclasses.
|
|
19
|
+
- **Tipagem Genérica em `watch<T>`**: Aprimoramento da assinatura de `watch` para inferência de tipos em `newValue` e `oldValue`.
|
|
20
|
+
- **Skill Aberta para Agentes de IA (`SKILL.md`)**: Arquivo [`SKILL.md`](./SKILL.md) na raiz do projeto para integração direta com agentes de IA via `npx skills add @jsweb/ui`.
|
|
21
|
+
- **Skill de Workspace (`update-documentation`)**: Skill em [`.agents/skills/update-documentation/SKILL.md`](./.agents/skills/update-documentation/SKILL.md) para auditoria e sincronização contínua de documentação.
|
|
22
|
+
- **Histórico Semântico de Versões (`CHANGELOG.md`)**: Arquivo [`CHANGELOG.md`](./CHANGELOG.md) baseado no padrão Keep a Changelog.
|
|
23
|
+
- **Automação de Build e Metadados**: Atualização do script [`publish.js`](./publish.js) para sincronizar `SKILL.md` e `CHANGELOG.md` na pasta `dist/` durante o build do pacote NPM.
|
|
24
|
+
- **Script de Publicação**: Adicionado script `npm run push` no `package.json` para automatizar o envio de tags git e publicação no NPM.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## [1.3.0] - 2026-09-18
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- **Diretiva `:ref` / `ui:ref`**: Indexação reativa de elementos DOM no helper contextual `$refs` (`Map`).
|
|
33
|
+
- **Refs Aninhadas em Listas**: Em loops `:for` com `:key`, `$refs.get(name)` retorna um `Map` aninhado indexado pela chave do item (`$key`).
|
|
34
|
+
- **Cleanup Automático de Refs**: Remoção de referências do `$refs` quando nós são destruídos por `:if` ou `:for`.
|
|
35
|
+
- **Modificador de Evento `.outside`**: Captura cliques e eventos fora do elemento (ideal para modais e dropdowns) com remoção automática do listener no `document` ao desconectar o nó.
|
|
36
|
+
- **Reconciliação e Reciclagem de Nós DOM em Listas**: Rastreamento de nós pelo `:key` em `:for`, reaproveitando instâncias existentes e evitando reflows desnecessários.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## [1.2.8] - 2026-05-20
|
|
41
|
+
|
|
42
|
+
### Refactored
|
|
43
|
+
|
|
44
|
+
- Limpeza de imports e remoção de código não utilizado no parser e no core.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## [1.2.7] - 2026-05-20
|
|
49
|
+
|
|
50
|
+
### Added
|
|
51
|
+
|
|
52
|
+
- Suporte aprimorado à função `watch` com observação profunda (_deep traverse_) e suporte a `{ immediate: true }`.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## [1.2.6] - 2026-05-20
|
|
57
|
+
|
|
58
|
+
### Added
|
|
59
|
+
|
|
60
|
+
- **Diretiva `:class` / `ui:class`**: Bind reativo de classes CSS via objeto booleano, array ou string, preservando classes estáticas.
|
|
61
|
+
- **Diretiva `:style` / `ui:style`**: Bind reativo para estilos inline com limpeza automática de propriedades removidas.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## [1.2.5] - 2026-05-18
|
|
66
|
+
|
|
67
|
+
### Fixed
|
|
68
|
+
|
|
69
|
+
- Tratamento de erros nas funções de avaliação (`evaluate` e `evaluateEvent`).
|
|
70
|
+
- Ajuste no contexto `this` para getters em propriedades computadas.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## [1.2.4] - 2026-05-18
|
|
75
|
+
|
|
76
|
+
### Added
|
|
77
|
+
|
|
78
|
+
- Suporte automático a propriedades computadas via _getters_ nativos (`get prop() { ... }`) em objetos envolvidos por `reactive()`.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## [1.2.3] - 2026-05-18
|
|
83
|
+
|
|
84
|
+
### Added
|
|
85
|
+
|
|
86
|
+
- Script de automação `preversion` no `package.json` para executar o build antes de gerar versões.
|
|
87
|
+
|
|
88
|
+
### Refactored
|
|
89
|
+
|
|
90
|
+
- Remoção da função legada `hasDirective`.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## [1.2.2] - 2026-05-18
|
|
95
|
+
|
|
96
|
+
### Refactored
|
|
97
|
+
|
|
98
|
+
- Simplificação do logging de avisos no motor de avaliação.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## [1.2.1] - 2026-05-17
|
|
103
|
+
|
|
104
|
+
### Added
|
|
105
|
+
|
|
106
|
+
- Script `publish.js` para geração de pacote mínimo em `dist/` e automação de publicação NPM.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## [1.2.0] - 2026-05-09
|
|
111
|
+
|
|
112
|
+
### Added
|
|
113
|
+
|
|
114
|
+
- Exportação da API `watch(source, callback, options?)` para observação de estados reativos.
|
|
115
|
+
- Reorganização dos exports principais (`createScope`, `reactive`, `watch`).
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## [1.1.0] - 2026-05-09
|
|
120
|
+
|
|
121
|
+
### Added
|
|
122
|
+
|
|
123
|
+
- Injeção do helper contextual `$emit(eventName, detail?)` para disparo de `CustomEvent` nativos (`bubbles: true`, `composed: true`).
|
|
124
|
+
- Padronização do nome do método de montagem para `createScope`.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## [1.0.0] - 2026-05-07
|
|
129
|
+
|
|
130
|
+
### Added
|
|
131
|
+
|
|
132
|
+
- Primeiro lançamento estável do `@jsweb/ui`.
|
|
133
|
+
- Motor de reatividade de grão fino baseado em `Proxy` e `ReactiveEffect` (sem Virtual DOM).
|
|
134
|
+
- Avaliador de expressões dinâmicas em sandbox.
|
|
135
|
+
- Diretivas fundamentais: `ui:scope`, `ui:text`, `ui:bind`, `ui:if`, `ui:for`, `ui:[attr]` e `ui@[event]`.
|
|
136
|
+
- Modificadores de evento `.prevent`, `.stop` e `.self`.
|
|
137
|
+
- Distribuição dual: ESM para bundlers e UMD/Standalone para uso direto via CDN.
|
package/dist/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 jsweb
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/README.md
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# @jsweb/ui
|
|
2
|
+
|
|
3
|
+
## Introdução
|
|
4
|
+
|
|
5
|
+
O `@jsweb/ui` é um micro-framework frontend escrito em TypeScript, projetado para ser uma ferramenta leve, rápida e flexível para o desenvolvimento de interfaces de usuário. Ele combina a reatividade moderna de frameworks como Vue 3 (Composition API) com a simplicidade de uso direto no HTML, semelhante ao Alpine.js.
|
|
6
|
+
|
|
7
|
+
### Pilares Arquiteturais
|
|
8
|
+
|
|
9
|
+
- **Sem Virtual DOM**: Utiliza reatividade de grão fino (Fine-grained reactivity) via `Proxy` para atualizações diretas no DOM real.
|
|
10
|
+
- **Distribuição Dupla**:
|
|
11
|
+
- **Standalone**: Arquivo único (IIFE/UMD) para inclusão via `<script src="...">`.
|
|
12
|
+
- **Module**: Pacote ESM com exports nomeados para suporte a Tree-Shaking.
|
|
13
|
+
- **Contexto Híbrido**: Suporta definição de estado via Objetos Literais (POJOs) ou Classes TypeScript.
|
|
14
|
+
- **Template Engine**: Baseado em atributos customizados no HTML (`ui:*` para diretivas e `ui@*` para eventos, com shorthands `@`, `:`).
|
|
15
|
+
- Suporte completo a **modificadores de eventos** encadeados (`.prevent`, `.stop`, `.self`, `.outside`).
|
|
16
|
+
|
|
17
|
+
## Instalação
|
|
18
|
+
|
|
19
|
+
### NPM
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm i @jsweb/ui
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### CDN
|
|
26
|
+
|
|
27
|
+
```html
|
|
28
|
+
<script src="https://unpkg.com/@jsweb/ui"></script>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Diretivas Disponíveis
|
|
32
|
+
|
|
33
|
+
O framework utiliza um sistema de atributos customizados para declaratividade no HTML, suportando tanto o prefixo completo (`ui:`, `ui@`) quanto a sintaxe simplificada (`:`, `@`).
|
|
34
|
+
|
|
35
|
+
| Diretiva | Atalho | Descrição | Exemplo |
|
|
36
|
+
| :----------- | :--------- | :-------------------------------------------------------------------------------- | :----------------------------------------- |
|
|
37
|
+
| `ui:scope` | `:scope` | Define o objeto de estado/contexto para o elemento e seus filhos. | `<div :scope="{ count: 0 }">` |
|
|
38
|
+
| `ui:text` | `:text` | Sincroniza o `textContent` com uma variável ou expressão. | `<span :text="count"></span>` |
|
|
39
|
+
| `ui:bind` | `:bind` | Two-way data binding para inputs, checkboxes, radios, selects e textarea. | `<input :bind="name">` |
|
|
40
|
+
| `ui:if` | `:if` | Renderização condicional no DOM (via Comment Node placeholder). | `<div :if="count > 0">` |
|
|
41
|
+
| `ui:for` | `:for` | Renderiza listas com suporte a `in` e `of`, expondo `$index`. | `<li :for="item of items">` |
|
|
42
|
+
| `ui:key` | `:key` | Identificador único para reconciliação e reciclagem eficiente de nós DOM. | `<li :for="item of items" :key="item.id">` |
|
|
43
|
+
| `ui:class` | `:class` | Bind reativo para classes CSS (suporta String, Array ou Objeto booleano). | `<div :class="{ active: isActive }">` |
|
|
44
|
+
| `ui:style` | `:style` | Bind reativo para estilos inline (recebe objeto chave/valor de propriedades CSS). | `<div :style="{ color: textColor }">` |
|
|
45
|
+
| `ui:ref` | `:ref` | Referencia elementos HTML indexados em um Map acessível via `$refs`. | `<input :ref="myInput">` |
|
|
46
|
+
| `ui:[attr]` | `:[attr]` | Bind de atributos HTML nativos (remove se falsy, ativa se booleano `true`). | `<button :disabled="count > 10">` |
|
|
47
|
+
| `ui@[event]` | `@[event]` | Escuta eventos DOM nativos ou customizados (com suporte a modificadores). | `<button @click.prevent="save">` |
|
|
48
|
+
|
|
49
|
+
### Modificadores de Eventos
|
|
50
|
+
|
|
51
|
+
É possível encadear modificadores diretamente na sintaxe do evento (`@event.modificador` ou `ui@event.modificador`):
|
|
52
|
+
|
|
53
|
+
- **`.prevent`**: Executa `$event.preventDefault()`.
|
|
54
|
+
- **`.stop`**: Executa `$event.stopPropagation()`.
|
|
55
|
+
- **`.self`**: Dispara o manipulador apenas quando o evento se originou exatamente no próprio elemento (`$event.target === el`).
|
|
56
|
+
- **`.outside`**: Dispara quando o evento ocorre fora do elemento (ideal para fechar menus, modais e dropdowns). Gerencia o ouvinte no `document` com remoção e limpeza automáticas quando o elemento for desconectado.
|
|
57
|
+
|
|
58
|
+
### Variáveis e Helpers de Contexto
|
|
59
|
+
|
|
60
|
+
Dentro das expressões declaradas no HTML, o framework disponibiliza variáveis e métodos contextuais:
|
|
61
|
+
|
|
62
|
+
- **`$refs`**: Objeto `Map` nativo contendo as referências registradas via `ui:ref` / `:ref`. Em elementos simples, retorna diretamente o elemento (`this.$refs.get('name')`). Em elementos dentro de loops `ui:for` com `:key`, retorna um `Map` aninhado indexado pela chave (`this.$refs.get('name').get($key)`).
|
|
63
|
+
- **`$emit(eventName, detail?)`**: Função injetada em todos os escopos para disparar `CustomEvent` nativos (`bubbles: true`, `composed: true`), facilitando a comunicação com elementos ancestrais (`@custom-event="handle"`).
|
|
64
|
+
- **`$event`**: Objeto nativo do evento disparado, disponível nas expressões de manipuladores (`@click="handle($event)"`). Se você referenciar apenas a função (`@click="handle"`), ela receberá `$event` automaticamente como primeiro argumento.
|
|
65
|
+
- **`$index`**: Índice numérico (base 0) da iteração atual, disponível dentro do escopo de um `ui:for` / `:for`.
|
|
66
|
+
|
|
67
|
+
### Detalhes de Comportamento
|
|
68
|
+
|
|
69
|
+
#### Two-Way Data Binding (`ui:bind` / `:bind`)
|
|
70
|
+
|
|
71
|
+
Detecta e trata o elemento de acordo com seu tipo:
|
|
72
|
+
|
|
73
|
+
- **`input[type="checkbox"]`**: Sincroniza a propriedade booleana `checked` e atualiza no evento `change`.
|
|
74
|
+
- **`input[type="radio"]`**: Marca como selecionado caso `el.value === String(valor)` e atualiza no evento `change`.
|
|
75
|
+
- **`<select>`**: Sincroniza o `value` selecionado e escuta o evento `change`.
|
|
76
|
+
- **`<input>` (texto, número, cor, data, etc.) e `<textarea>`**: Sincroniza `value` e atualiza em tempo real no evento `input`.
|
|
77
|
+
|
|
78
|
+
#### Classes Dinâmicas (`ui:class` / `:class`)
|
|
79
|
+
|
|
80
|
+
Atualiza classes reativas preservando classes estáticas já presentes no elemento:
|
|
81
|
+
|
|
82
|
+
- **Objeto**: `:class="{ active: isActive, 'has-error': error }"`
|
|
83
|
+
- **Array**: `:class="['badge', isActive && 'badge-success']"`
|
|
84
|
+
- **String**: `:class="currentClass"`
|
|
85
|
+
|
|
86
|
+
#### Reconciliação Inteligente em Listas (`ui:for` / `:for` e `ui:key` / `:key`)
|
|
87
|
+
|
|
88
|
+
```html
|
|
89
|
+
<ul>
|
|
90
|
+
<li :for="item of items" :key="item.id">
|
|
91
|
+
<span :text="$index"></span>: <strong :text="item.title"></strong>
|
|
92
|
+
</li>
|
|
93
|
+
</ul>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Ao atualizar arrays reativos, o motor rastreia os nós pelo `:key` (ou índice por padrão) e reaproveita as instâncias existentes no DOM, evitando reflows desnecessários e mantendo estados de foco/interação.
|
|
97
|
+
|
|
98
|
+
#### Referências a Elementos (`ui:ref` / `:ref` e `$refs`)
|
|
99
|
+
|
|
100
|
+
O atributo `ui:ref` ou `:ref` indexa o elemento diretamente em um objeto `Map` acessível via `this.$refs` em métodos ou `$refs` em templates:
|
|
101
|
+
|
|
102
|
+
1. **Uso Simples (Elemento Único)**:
|
|
103
|
+
|
|
104
|
+
```html
|
|
105
|
+
<input type="text" :ref="searchBox" />
|
|
106
|
+
<button @click="$refs.get('searchBox').focus()">Focar</button>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
No código do componente:
|
|
110
|
+
|
|
111
|
+
```javascript
|
|
112
|
+
this.$refs.get('searchBox').focus()
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
2. **Uso em Listas (`ui:for` com `:key`)**:
|
|
116
|
+
Quando utilizado dentro de um loop com `:key`, o framework cria automaticamente um `Map` aninhado mapeando cada elemento pela chave do item:
|
|
117
|
+
|
|
118
|
+
```html
|
|
119
|
+
<ul>
|
|
120
|
+
<li :for="user of users" :key="user.id">
|
|
121
|
+
<input type="text" :value="user.name" :ref="userInput" />
|
|
122
|
+
</li>
|
|
123
|
+
</ul>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Para resgatar o elemento de um item específico:
|
|
127
|
+
|
|
128
|
+
```javascript
|
|
129
|
+
const input = this.$refs.get('userInput').get(user.id)
|
|
130
|
+
input?.focus()
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
3. **Limpeza Automática**:
|
|
134
|
+
Quando um elemento referenciado sai do DOM (seja por remoção do item em lista ou por `ui:if`), ele é desregistrado automaticamente do `Map`, evitando vazamentos de memória e referências a nós órfãos.
|
|
135
|
+
|
|
136
|
+
## Exemplo de Uso
|
|
137
|
+
|
|
138
|
+
### HTML
|
|
139
|
+
|
|
140
|
+
```html
|
|
141
|
+
<!DOCTYPE html>
|
|
142
|
+
<html lang="en">
|
|
143
|
+
<head>
|
|
144
|
+
<meta charset="UTF-8" />
|
|
145
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
146
|
+
<title>JS Web UI</title>
|
|
147
|
+
<script src="https://unpkg.com/@jsweb/ui"></script>
|
|
148
|
+
<script>
|
|
149
|
+
const scope = {
|
|
150
|
+
count: 0,
|
|
151
|
+
inc: 'Incremento',
|
|
152
|
+
dec: 'Decremento',
|
|
153
|
+
increment() {
|
|
154
|
+
this.count++
|
|
155
|
+
},
|
|
156
|
+
decrement() {
|
|
157
|
+
this.count--
|
|
158
|
+
},
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
jsweb.ui.createScope('body', { scope })
|
|
162
|
+
</script>
|
|
163
|
+
</head>
|
|
164
|
+
<body>
|
|
165
|
+
<div ui:scope="scope">
|
|
166
|
+
<h1>JS Web UI</h1>
|
|
167
|
+
<p>Contador: <span ui:text="count"></span></p>
|
|
168
|
+
<button ui:text="inc" @click="increment()"></button>
|
|
169
|
+
<button ui:text="dec" @click="decrement()"></button>
|
|
170
|
+
</div>
|
|
171
|
+
</body>
|
|
172
|
+
</html>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### TypeScript / ESM
|
|
176
|
+
|
|
177
|
+
#### Abordagem com Objeto Literal (`ThisType`)
|
|
178
|
+
|
|
179
|
+
Graças ao utilitário `ThisType<T & ScopeContext>`, dentro dos métodos e _getters_ do objeto literal você tem autocomplete e tipagem estrita de `this`, incluindo as propriedades do objeto e os helpers `$refs` e `$emit`:
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
import { createScope, reactive, watch } from '@jsweb/ui'
|
|
183
|
+
|
|
184
|
+
const scope = reactive({
|
|
185
|
+
count: 0,
|
|
186
|
+
inc: 'Incremento',
|
|
187
|
+
dec: 'Decremento',
|
|
188
|
+
|
|
189
|
+
get double() {
|
|
190
|
+
return this.count * 2
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
increment() {
|
|
194
|
+
this.count++
|
|
195
|
+
this.$emit('changed', this.count)
|
|
196
|
+
this.$refs.get('meuInput')?.focus()
|
|
197
|
+
},
|
|
198
|
+
decrement() {
|
|
199
|
+
this.count--
|
|
200
|
+
},
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
// Observa mudanças com suporte a tipagem genérica, oldValue/newValue e disparo imediato
|
|
204
|
+
const unwatch = watch(
|
|
205
|
+
() => scope.count,
|
|
206
|
+
(newVal, oldVal) => {
|
|
207
|
+
console.log(`Contador mudou de ${oldVal} para ${newVal}`)
|
|
208
|
+
},
|
|
209
|
+
{ immediate: true },
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
createScope('#container', { scope })
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
#### Abordagem Orientada a Objetos com a Classe `Scope`
|
|
216
|
+
|
|
217
|
+
Você também pode utilizar classes TypeScript estendendo a classe base `Scope` fornecida pelo framework:
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
import { createScope, reactive, Scope } from '@jsweb/ui'
|
|
221
|
+
|
|
222
|
+
class ContadorScope extends Scope {
|
|
223
|
+
count = 0
|
|
224
|
+
inc = 'Incremento'
|
|
225
|
+
dec = 'Decremento'
|
|
226
|
+
|
|
227
|
+
get double() {
|
|
228
|
+
return this.count * 2
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
increment() {
|
|
232
|
+
this.count++
|
|
233
|
+
this.$emit('changed', this.count)
|
|
234
|
+
this.$refs.get('meuInput')?.focus()
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
decrement() {
|
|
238
|
+
this.count--
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// É possível sobrescrever o método $emit se desejar lógica customizada:
|
|
242
|
+
protected override $emit(event: string, detail?: any) {
|
|
243
|
+
console.log(`[Contador] Evento disparado: ${event}`, detail)
|
|
244
|
+
super.$emit(event, detail)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const scope = reactive(new ContadorScope())
|
|
249
|
+
createScope('#container', { scope })
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## API JavaScript / TypeScript
|
|
253
|
+
|
|
254
|
+
### `createScope<T>(selectorOrElement, context?)`
|
|
255
|
+
|
|
256
|
+
Inicializa e amarra a reatividade ao elemento DOM ou seletor especificado.
|
|
257
|
+
|
|
258
|
+
- **`selectorOrElement`**: Seletor CSS (ex: `'#app'`, `'body'`) ou instância de `HTMLElement`.
|
|
259
|
+
- **`context`**: Objeto inicial de contexto/estado compartilhado (opcional), tipado contextualmente com `ThisType<T & ScopeContext>`. Injeta automaticamente os helpers `$emit` e `$refs`.
|
|
260
|
+
|
|
261
|
+
### `reactive(target)`
|
|
262
|
+
|
|
263
|
+
Envolve um objeto ou array em um `Proxy` de reatividade de grão fino (_fine-grained_).
|
|
264
|
+
|
|
265
|
+
- **Objeto Literal**: Tipado com `ThisType<T & ScopeContext>` para inferência e autocomplete de `this` (incluindo computeds via _getters_ e helpers contextuais `$refs`, `$emit` e `$el` prontos para uso sem necessidade de `?.`).
|
|
266
|
+
- **Instâncias de Classes**: Suporta instâncias que estendem `Scope` ou classes POJO personalizadas.
|
|
267
|
+
- **Arrays**: Intercepta métodos de mutação (`push`, `pop`, `splice`, etc.) e gerencia dependências de tamanho (`length`).
|
|
268
|
+
- **Suporta reatividade profunda** (_deep reactivity_).
|
|
269
|
+
|
|
270
|
+
### `Scope`
|
|
271
|
+
|
|
272
|
+
Classe base utilitária para definição de escopos orientados a objetos em TypeScript. Já possui `$el` e `$refs` implementados como somente leitura e `$emit` com implementação padrão como método `protected`, permitindo sobrescrita (`override`) nas subclasses.
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
export class Scope {
|
|
276
|
+
readonly $el: HTMLElement
|
|
277
|
+
protected readonly $refs: Map<string, any>
|
|
278
|
+
protected $emit(event: string, detail?: any): void
|
|
279
|
+
declare $index?: number
|
|
280
|
+
declare $key?: any
|
|
281
|
+
constructor(init?: Record<string, any>)
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### `watch<T>(source, callback, options?)`
|
|
286
|
+
|
|
287
|
+
Observa alterações reativas e executa uma função de callback quando o valor mudar.
|
|
288
|
+
|
|
289
|
+
- **`source`**: Objeto reativo completo ou função getter que retorna o valor a ser observado (ex: `() => state.count`).
|
|
290
|
+
- **`callback`**: `(newValue: T, oldValue: T | undefined) => void`.
|
|
291
|
+
- **`options`**: `{ immediate?: boolean }` para acionar a callback imediatamente na primeira execução.
|
|
292
|
+
- **Retorno**: Função de cancelamento `stop()` que encerra a observação e limpa dependências.
|