vex-ui-kit 1.0.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.
- package/README.md +830 -0
- package/dist/adapters/react.cjs.js +17 -0
- package/dist/adapters/react.cjs.js.map +1 -0
- package/dist/adapters/react.d.mts +37 -0
- package/dist/adapters/react.d.ts +37 -0
- package/dist/adapters/react.esm.js +17 -0
- package/dist/adapters/react.esm.js.map +1 -0
- package/dist/adapters/react.umd.js +63 -0
- package/dist/adapters/react.umd.js.map +1 -0
- package/dist/adapters/svelte.cjs.js +17 -0
- package/dist/adapters/svelte.cjs.js.map +1 -0
- package/dist/adapters/svelte.d.mts +21 -0
- package/dist/adapters/svelte.d.ts +21 -0
- package/dist/adapters/svelte.esm.js +17 -0
- package/dist/adapters/svelte.esm.js.map +1 -0
- package/dist/adapters/svelte.umd.js +44 -0
- package/dist/adapters/svelte.umd.js.map +1 -0
- package/dist/adapters/vue.cjs.js +17 -0
- package/dist/adapters/vue.cjs.js.map +1 -0
- package/dist/adapters/vue.d.mts +35 -0
- package/dist/adapters/vue.d.ts +35 -0
- package/dist/adapters/vue.esm.js +17 -0
- package/dist/adapters/vue.esm.js.map +1 -0
- package/dist/adapters/vue.umd.js +147 -0
- package/dist/adapters/vue.umd.js.map +1 -0
- package/dist/index-CpFq7Lxe.d.mts +213 -0
- package/dist/index-CpFq7Lxe.d.ts +213 -0
- package/dist/themes/flat.css +47 -0
- package/dist/themes/glass.css +22 -0
- package/dist/themes/minimal.css +35 -0
- package/dist/themes/neon.css +45 -0
- package/dist/themes/outline.css +18 -0
- package/dist/themes/pastel.css +21 -0
- package/dist/themes/tokens.css +329 -0
- package/dist/vex-ui-kit.css +329 -0
- package/dist/vexui.cjs.js +17 -0
- package/dist/vexui.cjs.js.map +1 -0
- package/dist/vexui.d.mts +1 -0
- package/dist/vexui.d.ts +1 -0
- package/dist/vexui.esm.js +17 -0
- package/dist/vexui.esm.js.map +1 -0
- package/dist/vexui.umd.js +17 -0
- package/dist/vexui.umd.js.map +1 -0
- package/package.json +63 -0
package/README.md
ADDED
|
@@ -0,0 +1,830 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# VexUI
|
|
4
|
+
|
|
5
|
+
**Sistema completo de feedback visual para a web**
|
|
6
|
+
|
|
7
|
+
Alerts · Modals · Drawers · Step Flows · Inline Prompts
|
|
8
|
+
|
|
9
|
+
[](https://npmjs.com/package/vexui)
|
|
10
|
+
[](./LICENSE)
|
|
11
|
+
[](#)
|
|
12
|
+
[](#)
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install vex-ui-kit
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Índice
|
|
23
|
+
|
|
24
|
+
- [Visão Geral](#visão-geral)
|
|
25
|
+
- [Por que VexUI?](#por-que-vexui)
|
|
26
|
+
- [Tecnologias](#tecnologias)
|
|
27
|
+
- [Instalação](#instalação)
|
|
28
|
+
- [Estrutura de Arquivos](#estrutura-de-arquivos)
|
|
29
|
+
- [Alerts](#alerts)
|
|
30
|
+
- [Modals](#modals)
|
|
31
|
+
- [Funcionalidades Exclusivas](#funcionalidades-exclusivas)
|
|
32
|
+
- [Sistema de Temas](#sistema-de-temas)
|
|
33
|
+
- [Acessibilidade](#acessibilidade)
|
|
34
|
+
- [Integrações](#integrações)
|
|
35
|
+
- [API Completa](#api-completa)
|
|
36
|
+
- [Roadmap](#roadmap)
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Visão Geral
|
|
41
|
+
|
|
42
|
+
VexUI é uma biblioteca JavaScript de **feedback visual** que substitui completamente soluções fragmentadas — sem precisar de uma lib para toast, outra para modal e outra para drawer. Com uma única instalação e API unificada, você tem acesso a um sistema coeso de comunicação com o usuário.
|
|
43
|
+
|
|
44
|
+
Os componentes se comportam como **banners visuais ricos** — inspirados nos Alerts do Bootstrap, mas com glassmorphism, animações polidas, suporte a ações, filas de prioridade e muito mais.
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
import vex from 'vexui'
|
|
48
|
+
|
|
49
|
+
// Simples assim
|
|
50
|
+
vex.alert.success('Arquivo salvo com sucesso!')
|
|
51
|
+
|
|
52
|
+
// Ou com async/await
|
|
53
|
+
const ok = await vex.modal.confirm({ title: 'Excluir projeto?', type: 'danger' })
|
|
54
|
+
if (ok) await deleteProject(id)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Por que VexUI?
|
|
60
|
+
|
|
61
|
+
| Funcionalidade | VexUI | Concorrentes |
|
|
62
|
+
|---|---|---|
|
|
63
|
+
| Alerts inline + fixed | ✅ Ambos, API única | Geralmente só um |
|
|
64
|
+
| Modals Promise-based | ✅ `await` nativo | Callbacks / eventos |
|
|
65
|
+
| Step Flow (wizard) | ✅ Built-in | ❌ Não existe |
|
|
66
|
+
| Drawer / Side Panel | ✅ Built-in | ❌ Não existe |
|
|
67
|
+
| Inline Prompt | ✅ Built-in | ❌ Não existe |
|
|
68
|
+
| Alert ancorado a elemento | ✅ Built-in | ❌ Não existe |
|
|
69
|
+
| Alert atualizável em tempo real | ✅ Built-in | ❌ Não existe |
|
|
70
|
+
| Fila de prioridade | ✅ Built-in | ❌ Não existe |
|
|
71
|
+
| Histórico de notificações | ✅ Built-in | ❌ Não existe |
|
|
72
|
+
| Glassmorphism nativo | ✅ Padrão | ❌ Estilo genérico |
|
|
73
|
+
| Dark / Light automático | ✅ `prefers-color-scheme` | ⚠️ Manual / limitado |
|
|
74
|
+
| Theming por CSS tokens | ✅ 100% tokens | ⚠️ Parcial |
|
|
75
|
+
| ARIA / Acessibilidade | ✅ Completo | ⚠️ Básico / ausente |
|
|
76
|
+
| Zero dependências | ✅ Pure JS | ⚠️ Variado |
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Tecnologias
|
|
81
|
+
|
|
82
|
+
### Core
|
|
83
|
+
|
|
84
|
+
| Tecnologia | Versão | Uso |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| **JavaScript (ES2020+)** | — | Engine principal da biblioteca |
|
|
87
|
+
| **CSS Custom Properties** | — | Sistema de tokens e temas |
|
|
88
|
+
| **Web Animations API** | — | Animações performáticas |
|
|
89
|
+
| **CSS `backdrop-filter`** | — | Efeito glassmorphism |
|
|
90
|
+
| **`prefers-color-scheme`** | — | Dark/light mode automático |
|
|
91
|
+
| **`prefers-reduced-motion`** | — | Respeito às preferências de acessibilidade |
|
|
92
|
+
| **ARIA / WAI-ARIA 1.2** | — | Acessibilidade completa |
|
|
93
|
+
|
|
94
|
+
### Build & Tooling
|
|
95
|
+
|
|
96
|
+
| Ferramenta | Uso |
|
|
97
|
+
|---|---|
|
|
98
|
+
| **tsup** | Build e bundling (ESM, CJS, UMD) |
|
|
99
|
+
| **TypeScript** | Tipos e declarações `.d.ts` |
|
|
100
|
+
| **Vitest** | Testes unitários |
|
|
101
|
+
| **Playwright** | Testes de integração / E2E |
|
|
102
|
+
| **Changesets** | Versionamento semântico |
|
|
103
|
+
| **np** | Publicação no npm |
|
|
104
|
+
|
|
105
|
+
### Adapters (opcionais)
|
|
106
|
+
|
|
107
|
+
| Adapter | Tecnologia |
|
|
108
|
+
|---|---|
|
|
109
|
+
| `vexui/react` | React 18+ (hook) |
|
|
110
|
+
| `vex-ui-kit/vue` | Vue 3 (plugin + composable) |
|
|
111
|
+
| `vex-ui-kit/svelte` | Svelte (store) |
|
|
112
|
+
|
|
113
|
+
> **Zero dependências no core.** Os adapters para frameworks são opcionais e separados.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Instalação
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
# npm
|
|
121
|
+
npm install vexui
|
|
122
|
+
|
|
123
|
+
# yarn
|
|
124
|
+
yarn add vexui
|
|
125
|
+
|
|
126
|
+
# pnpm
|
|
127
|
+
pnpm add vexui
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Via CDN (sem instalação)
|
|
131
|
+
|
|
132
|
+
```html
|
|
133
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vexui/dist/vexui.css">
|
|
134
|
+
<script src="https://cdn.jsdelivr.net/npm/vexui/dist/vexui.umd.js"></script>
|
|
135
|
+
|
|
136
|
+
<script>
|
|
137
|
+
VexUI.alert.success('Funciona em qualquer projeto!')
|
|
138
|
+
</script>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Importação no projeto
|
|
142
|
+
|
|
143
|
+
```js
|
|
144
|
+
// ES Modules (recomendado)
|
|
145
|
+
import vex from 'vexui'
|
|
146
|
+
import 'vexui/dist/vexui.css'
|
|
147
|
+
|
|
148
|
+
// CommonJS
|
|
149
|
+
const vex = require('vexui')
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Estrutura de Arquivos
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
vexui/
|
|
158
|
+
│
|
|
159
|
+
├── src/ # Código-fonte
|
|
160
|
+
│ │
|
|
161
|
+
│ ├── core/ # Engine principal (zero deps)
|
|
162
|
+
│ │ ├── alert.js # Sistema de alerts
|
|
163
|
+
│ │ ├── modal.js # Sistema de modals
|
|
164
|
+
│ │ ├── drawer.js # Drawer / painel lateral
|
|
165
|
+
│ │ ├── flow.js # Step Flow (wizard)
|
|
166
|
+
│ │ ├── queue.js # Fila de prioridade
|
|
167
|
+
│ │ ├── history.js # Histórico de notificações
|
|
168
|
+
│ │ ├── theme.js # Gerenciador de temas
|
|
169
|
+
│ │ ├── a11y.js # Utilitários de acessibilidade
|
|
170
|
+
│ │ └── index.js # Entry point — exporta `vex`
|
|
171
|
+
│ │
|
|
172
|
+
│ ├── themes/ # Temas prontos (CSS)
|
|
173
|
+
│ │ ├── tokens.css # CSS custom properties base
|
|
174
|
+
│ │ ├── glass.css # Glassmorphism (padrão)
|
|
175
|
+
│ │ ├── flat.css # Flat design
|
|
176
|
+
│ │ ├── outline.css # Apenas bordas
|
|
177
|
+
│ │ ├── minimal.css # Ultra-discreto
|
|
178
|
+
│ │ ├── neon.css # Neon / cyberpunk
|
|
179
|
+
│ │ └── pastel.css # Soft / tons pastel
|
|
180
|
+
│ │
|
|
181
|
+
│ └── adapters/ # Integrações com frameworks
|
|
182
|
+
│ ├── react.js # useVex() hook
|
|
183
|
+
│ ├── vue.js # $vex plugin + useVex()
|
|
184
|
+
│ └── svelte.js # vex store
|
|
185
|
+
│
|
|
186
|
+
├── dist/ # Build gerado (não editar)
|
|
187
|
+
│ ├── vexui.esm.js # ES Modules
|
|
188
|
+
│ ├── vexui.cjs.js # CommonJS
|
|
189
|
+
│ ├── vexui.umd.js # CDN / UMD (global VexUI)
|
|
190
|
+
│ └── vexui.css # Estilos compilados
|
|
191
|
+
│
|
|
192
|
+
├── types/
|
|
193
|
+
│ └── index.d.ts # TypeScript declarations
|
|
194
|
+
│
|
|
195
|
+
├── tests/
|
|
196
|
+
│ ├── unit/ # Testes unitários (Vitest)
|
|
197
|
+
│ └── e2e/ # Testes E2E (Playwright)
|
|
198
|
+
│
|
|
199
|
+
├── docs/ # Documentação
|
|
200
|
+
│ └── README.md
|
|
201
|
+
│
|
|
202
|
+
├── package.json
|
|
203
|
+
├── tsup.config.ts # Config de build
|
|
204
|
+
├── tsconfig.json
|
|
205
|
+
└── LICENSE
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Alerts
|
|
211
|
+
|
|
212
|
+
O sistema de alerts é o núcleo da VexUI. Diferente de toasts que aparecem em cantos flutuantes, os alerts da VexUI são **banners ricos** — inline no fluxo da página ou fixos na viewport.
|
|
213
|
+
|
|
214
|
+
### Tipos disponíveis
|
|
215
|
+
|
|
216
|
+
```js
|
|
217
|
+
vex.alert.success('Salvo com sucesso!')
|
|
218
|
+
vex.alert.error('Falha ao conectar com o servidor')
|
|
219
|
+
vex.alert.warning('Sessão expirando em 5 minutos')
|
|
220
|
+
vex.alert.info('Nova atualização disponível')
|
|
221
|
+
vex.alert.neutral('Rascunho salvo automaticamente')
|
|
222
|
+
vex.alert.loading('Enviando arquivo...') // com spinner animado
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Modos de posicionamento
|
|
226
|
+
|
|
227
|
+
#### Inline — renderizado no DOM
|
|
228
|
+
|
|
229
|
+
```js
|
|
230
|
+
// Dentro de um container específico
|
|
231
|
+
vex.alert.inline('#meu-formulario', {
|
|
232
|
+
type: 'error',
|
|
233
|
+
title: 'Erro de validação',
|
|
234
|
+
message: 'Preencha todos os campos obrigatórios.',
|
|
235
|
+
})
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
#### Fixed — flutua na viewport
|
|
239
|
+
|
|
240
|
+
```js
|
|
241
|
+
// Topo da tela
|
|
242
|
+
vex.alert.fixed({ position: 'top', type: 'success', message: 'Deploy finalizado!' })
|
|
243
|
+
|
|
244
|
+
// Rodapé da tela
|
|
245
|
+
vex.alert.fixed({ position: 'bottom', type: 'info', message: '2 novas mensagens.' })
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
#### Attached — ancorado a um elemento *(exclusivo)*
|
|
249
|
+
|
|
250
|
+
```js
|
|
251
|
+
// Aparece colado ao elemento, como um popover rico
|
|
252
|
+
vex.alert.attach('#campo-senha', {
|
|
253
|
+
type: 'warning',
|
|
254
|
+
message: 'Senha fraca: use pelo menos 8 caracteres.',
|
|
255
|
+
placement: 'bottom', // top | bottom | left | right | auto
|
|
256
|
+
duration: 0,
|
|
257
|
+
})
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Alerts com botões de ação
|
|
261
|
+
|
|
262
|
+
```js
|
|
263
|
+
vex.alert.warning('Você tem alterações não salvas.', {
|
|
264
|
+
duration: 0,
|
|
265
|
+
actions: [
|
|
266
|
+
{
|
|
267
|
+
label: 'Salvar agora',
|
|
268
|
+
variant: 'primary',
|
|
269
|
+
onClick: (close) => { save(); close() }
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
label: 'Descartar',
|
|
273
|
+
variant: 'danger',
|
|
274
|
+
onClick: (close) => { discard(); close() }
|
|
275
|
+
},
|
|
276
|
+
]
|
|
277
|
+
})
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Alert de Loading atualizável *(exclusivo)*
|
|
281
|
+
|
|
282
|
+
```js
|
|
283
|
+
// Cria o alert e recebe um ID
|
|
284
|
+
const id = vex.alert.loading('Enviando arquivo...')
|
|
285
|
+
|
|
286
|
+
// Atualiza em tempo real conforme o processo avança
|
|
287
|
+
vex.update(id, { message: 'Processando (45%)...' })
|
|
288
|
+
vex.update(id, { message: 'Processando (90%)...' })
|
|
289
|
+
|
|
290
|
+
// Transforma em sucesso ao finalizar
|
|
291
|
+
vex.update(id, { type: 'success', message: 'Arquivo enviado com sucesso!' })
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Alert de Upload com progresso *(exclusivo)*
|
|
295
|
+
|
|
296
|
+
```js
|
|
297
|
+
const id = vex.alert.upload('Enviando documento.pdf', { total: file.size })
|
|
298
|
+
|
|
299
|
+
upload.onprogress = (e) => {
|
|
300
|
+
vex.progress(id, { loaded: e.loaded, total: e.total })
|
|
301
|
+
// Exibe: "Enviando... 4.2 MB / 10 MB · ~8s restantes"
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
upload.onload = () => {
|
|
305
|
+
vex.update(id, { type: 'success', message: 'Upload concluído!' })
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Fila de prioridade *(exclusivo)*
|
|
310
|
+
|
|
311
|
+
```js
|
|
312
|
+
// Alerts críticos entram imediatamente, deslocando os demais
|
|
313
|
+
vex.alert.error('Servidor fora do ar', { priority: 'critical' })
|
|
314
|
+
|
|
315
|
+
// Prioridade alta — entra na frente da fila
|
|
316
|
+
vex.alert.warning('Sessão expirando', { priority: 'high' })
|
|
317
|
+
|
|
318
|
+
// Prioridade baixa — só aparece quando a fila esvazia
|
|
319
|
+
vex.alert.info('Dica do dia', { priority: 'low' })
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Todas as opções de Alert
|
|
323
|
+
|
|
324
|
+
| Opção | Tipo | Padrão | Descrição |
|
|
325
|
+
|---|---|---|---|
|
|
326
|
+
| `type` | `string` | `'info'` | `success` · `error` · `warning` · `info` · `neutral` · `loading` |
|
|
327
|
+
| `message` | `string` | — | Texto principal do alert |
|
|
328
|
+
| `title` | `string` | `null` | Título em negrito acima da mensagem |
|
|
329
|
+
| `duration` | `number` | `6000` | Tempo em ms. `0` = permanente |
|
|
330
|
+
| `dismissible` | `boolean` | `true` | Botão × para fechar manualmente |
|
|
331
|
+
| `icon` | `string \| null` | `auto` | Emoji, SVG string ou `null` para remover |
|
|
332
|
+
| `progress` | `boolean` | `true` | Barra de progresso animada do tempo restante |
|
|
333
|
+
| `actions` | `Action[]` | `[]` | Botões de ação dentro do alert |
|
|
334
|
+
| `priority` | `string` | `'normal'` | `critical` · `high` · `normal` · `low` |
|
|
335
|
+
| `position` | `string` | `'top'` | `top` · `bottom` — apenas para fixed |
|
|
336
|
+
| `align` | `string` | `'center'` | `center` · `left` · `right` |
|
|
337
|
+
| `animate` | `string` | `'slide'` | `slide` · `fade` · `scale` · `bounce` |
|
|
338
|
+
| `maxStack` | `number` | `5` | Máximo de alerts simultâneos |
|
|
339
|
+
| `theme` | `object` | preset | Sobrescreve tokens visuais desta instância |
|
|
340
|
+
| `onClose` | `function` | `null` | Callback chamado ao fechar |
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
## Modals
|
|
345
|
+
|
|
346
|
+
Todos os modals são **Promise-based** — retornam dados diretamente via `await`, eliminando callbacks aninhados.
|
|
347
|
+
|
|
348
|
+
### Modal de Confirmação
|
|
349
|
+
|
|
350
|
+
```js
|
|
351
|
+
const ok = await vex.modal.confirm({
|
|
352
|
+
type: 'danger', // danger | warning | info
|
|
353
|
+
title: 'Excluir projeto?',
|
|
354
|
+
message: 'Esta ação não pode ser desfeita. Todos os dados serão removidos permanentemente.',
|
|
355
|
+
confirmLabel: 'Sim, excluir',
|
|
356
|
+
cancelLabel: 'Cancelar',
|
|
357
|
+
confirmDelay: 3000, // botão bloqueado por 3s (segurança extra)
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
if (ok) await deleteProject(id)
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Modal de Aviso / Informação
|
|
364
|
+
|
|
365
|
+
```js
|
|
366
|
+
await vex.modal.alert({
|
|
367
|
+
type: 'info',
|
|
368
|
+
title: 'Nova política de privacidade',
|
|
369
|
+
message: 'Atualizamos nossos termos em Janeiro de 2025...',
|
|
370
|
+
confirmLabel: 'Entendido',
|
|
371
|
+
})
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Modal de Formulário
|
|
375
|
+
|
|
376
|
+
```js
|
|
377
|
+
const data = await vex.modal.form({
|
|
378
|
+
title: 'Convidar membro',
|
|
379
|
+
submitLabel: 'Enviar convite',
|
|
380
|
+
fields: [
|
|
381
|
+
{ name: 'name', label: 'Nome completo', type: 'text', required: true },
|
|
382
|
+
{ name: 'email', label: 'E-mail', type: 'email', required: true },
|
|
383
|
+
{ name: 'role', label: 'Função', type: 'select', options: ['Admin', 'Editor', 'Viewer'] },
|
|
384
|
+
{ name: 'msg', label: 'Mensagem', type: 'textarea', optional: true },
|
|
385
|
+
],
|
|
386
|
+
validate: (data) => {
|
|
387
|
+
if (!data.email.includes('@empresa.com'))
|
|
388
|
+
return { email: 'Use seu e-mail corporativo' }
|
|
389
|
+
}
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
if (data) await inviteMember(data)
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
**Tipos de campo suportados:**
|
|
396
|
+
|
|
397
|
+
| Tipo | Descrição |
|
|
398
|
+
|---|---|
|
|
399
|
+
| `text`, `email`, `password`, `number`, `url`, `tel` | Inputs nativos |
|
|
400
|
+
| `textarea` | Texto multilinha |
|
|
401
|
+
| `select` | Dropdown |
|
|
402
|
+
| `radio` | Grupo de opções exclusivas |
|
|
403
|
+
| `checkbox` | Múltipla seleção |
|
|
404
|
+
| `date`, `time`, `datetime-local` | Seletores de data/hora |
|
|
405
|
+
| `file` | Upload com preview |
|
|
406
|
+
| `range` | Slider numérico com label de valor |
|
|
407
|
+
| `color` | Seletor de cor nativo |
|
|
408
|
+
|
|
409
|
+
### Modal de Prompt
|
|
410
|
+
|
|
411
|
+
```js
|
|
412
|
+
const nome = await vex.modal.prompt({
|
|
413
|
+
title: 'Renomear arquivo',
|
|
414
|
+
placeholder: 'Ex: relatorio-final.pdf',
|
|
415
|
+
defaultValue: 'relatorio.pdf',
|
|
416
|
+
validate: (v) => v.trim().length < 3 ? 'Nome muito curto' : null
|
|
417
|
+
})
|
|
418
|
+
|
|
419
|
+
if (nome) await renameFile(nome)
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### Modal de Preview de Mídia
|
|
423
|
+
|
|
424
|
+
```js
|
|
425
|
+
vex.modal.preview({
|
|
426
|
+
src: '/uploads/foto.jpg',
|
|
427
|
+
title: 'Foto do Produto',
|
|
428
|
+
meta: ['PNG · 4K', '3.8 MB', 'Jan 2025'],
|
|
429
|
+
zoom: true,
|
|
430
|
+
gallery: ['/img1.jpg', '/img2.jpg', '/img3.jpg'], // navegação entre imagens
|
|
431
|
+
actions: [
|
|
432
|
+
{ label: '⬇ Download', onClick: () => downloadFile() }
|
|
433
|
+
],
|
|
434
|
+
})
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### Modal Customizado
|
|
438
|
+
|
|
439
|
+
```js
|
|
440
|
+
// Conteúdo completamente livre
|
|
441
|
+
const { close } = vex.modal.custom({
|
|
442
|
+
title: 'Editor avançado',
|
|
443
|
+
content: document.getElementById('meu-editor'), // elemento DOM ou HTML string
|
|
444
|
+
width: 800,
|
|
445
|
+
footer: false,
|
|
446
|
+
})
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### Opções comuns a todos os Modals
|
|
450
|
+
|
|
451
|
+
| Opção | Tipo | Padrão | Descrição |
|
|
452
|
+
|---|---|---|---|
|
|
453
|
+
| `title` | `string` | — | Título do modal |
|
|
454
|
+
| `type` | `string` | `'info'` | `danger` · `warning` · `info` · `success` |
|
|
455
|
+
| `width` | `number` | `480` | Largura máxima em pixels |
|
|
456
|
+
| `closeOnBackdrop` | `boolean` | `true` | Fecha ao clicar no backdrop |
|
|
457
|
+
| `closeOnEscape` | `boolean` | `true` | Fecha ao pressionar Escape |
|
|
458
|
+
| `showClose` | `boolean` | `true` | Botão × no canto superior direito |
|
|
459
|
+
| `animate` | `string` | `'scale'` | `scale` · `slide` · `fade` |
|
|
460
|
+
| `scrollLock` | `boolean` | `true` | Bloqueia scroll do body enquanto aberto |
|
|
461
|
+
| `stackable` | `boolean` | `true` | Permite múltiplos modals sobrepostos |
|
|
462
|
+
| `zIndex` | `number` | `9500` | z-index do backdrop |
|
|
463
|
+
| `onOpen` | `function` | `null` | Callback ao abrir |
|
|
464
|
+
| `onClose` | `function` | `null` | Callback ao fechar |
|
|
465
|
+
|
|
466
|
+
---
|
|
467
|
+
|
|
468
|
+
## Funcionalidades Exclusivas
|
|
469
|
+
|
|
470
|
+
### Step Flow — Wizard multi-etapa *(exclusivo)*
|
|
471
|
+
|
|
472
|
+
Fluxo guiado de múltiplas etapas dentro de um único modal. Cada etapa pode ter formulário próprio, validação e lógica condicional. Retorna todos os dados ao final.
|
|
473
|
+
|
|
474
|
+
```js
|
|
475
|
+
const result = await vex.flow([
|
|
476
|
+
{
|
|
477
|
+
title: 'Bem-vindo!',
|
|
478
|
+
type: 'info',
|
|
479
|
+
description: 'Vamos configurar sua conta em 3 passos simples.',
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
title: 'Seus dados',
|
|
483
|
+
fields: [
|
|
484
|
+
{ name: 'name', label: 'Nome', type: 'text', required: true },
|
|
485
|
+
{ name: 'email', label: 'E-mail', type: 'email', required: true },
|
|
486
|
+
],
|
|
487
|
+
// Valida antes de avançar
|
|
488
|
+
beforeNext: async (data) => {
|
|
489
|
+
const exists = await checkEmail(data.email)
|
|
490
|
+
if (exists) return { email: 'E-mail já cadastrado' }
|
|
491
|
+
}
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
title: 'Escolha seu plano',
|
|
495
|
+
fields: [
|
|
496
|
+
{
|
|
497
|
+
name: 'plan',
|
|
498
|
+
type: 'radio',
|
|
499
|
+
options: ['Starter (grátis)', 'Pro (R$49/mês)', 'Enterprise']
|
|
500
|
+
}
|
|
501
|
+
]
|
|
502
|
+
},
|
|
503
|
+
// Etapa condicional — só aparece se escolheu Enterprise
|
|
504
|
+
{
|
|
505
|
+
title: 'Dados da empresa',
|
|
506
|
+
condition: (data) => data.plan === 'Enterprise',
|
|
507
|
+
fields: [
|
|
508
|
+
{ name: 'company', label: 'Nome da empresa', type: 'text', required: true },
|
|
509
|
+
{ name: 'cnpj', label: 'CNPJ', type: 'text' },
|
|
510
|
+
]
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
title: 'Tudo pronto! 🎉',
|
|
514
|
+
type: 'success',
|
|
515
|
+
description: 'Sua conta foi configurada com sucesso.',
|
|
516
|
+
},
|
|
517
|
+
])
|
|
518
|
+
|
|
519
|
+
// result = { name, email, plan, company?, cnpj? }
|
|
520
|
+
if (result) await createAccount(result)
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
**Funcionalidades do Step Flow:**
|
|
524
|
+
- Barra de progresso visual com número da etapa atual
|
|
525
|
+
- Navegação para frente e para trás livremente
|
|
526
|
+
- Validação por etapa — não avança com erros
|
|
527
|
+
- Dados acumulados e disponíveis em todas as etapas
|
|
528
|
+
- Etapas condicionais baseadas em dados anteriores
|
|
529
|
+
- `beforeNext()` para lógica assíncrona por etapa
|
|
530
|
+
|
|
531
|
+
---
|
|
532
|
+
|
|
533
|
+
### Drawer — Painel lateral *(exclusivo)*
|
|
534
|
+
|
|
535
|
+
Painel deslizante que emerge da lateral sem bloquear a página. Ideal para filtros, formulários longos, detalhes de item e configurações.
|
|
536
|
+
|
|
537
|
+
```js
|
|
538
|
+
const { close } = vex.drawer({
|
|
539
|
+
title: 'Filtros avançados',
|
|
540
|
+
position: 'right', // right | left | top | bottom
|
|
541
|
+
width: 420, // para right/left (height para top/bottom)
|
|
542
|
+
content: '#filtros-form', // seletor CSS ou elemento DOM
|
|
543
|
+
persistent: false, // fecha ao clicar fora
|
|
544
|
+
footer: [
|
|
545
|
+
{ label: 'Aplicar filtros', variant: 'primary', onClick: () => { apply(); close() } },
|
|
546
|
+
{ label: 'Limpar tudo', onClick: () => clearAll() },
|
|
547
|
+
],
|
|
548
|
+
})
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
---
|
|
552
|
+
|
|
553
|
+
### Inline Prompt — Edição contextual *(exclusivo)*
|
|
554
|
+
|
|
555
|
+
Substitui um elemento de texto por um input diretamente no DOM, sem abrir modal. Perfeito para edição inline de títulos e valores.
|
|
556
|
+
|
|
557
|
+
```js
|
|
558
|
+
// O elemento #titulo vira um <input> na hora
|
|
559
|
+
const novoNome = await vex.prompt.inline('#titulo-projeto', {
|
|
560
|
+
placeholder: 'Nome do projeto',
|
|
561
|
+
validate: (v) => v.trim().length > 0 ? null : 'Nome obrigatório'
|
|
562
|
+
})
|
|
563
|
+
|
|
564
|
+
if (novoNome) await renameProject(novoNome)
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
---
|
|
568
|
+
|
|
569
|
+
### Histórico de Notificações *(exclusivo)*
|
|
570
|
+
|
|
571
|
+
A VexUI mantém um registro interno de todos os alerts. Pode ser exibido como um painel de "sino de notificações".
|
|
572
|
+
|
|
573
|
+
```js
|
|
574
|
+
// Acesso programático
|
|
575
|
+
const historico = vex.history.get()
|
|
576
|
+
// [{ id, type, title, message, timestamp, read }, ...]
|
|
577
|
+
|
|
578
|
+
vex.history.markAllRead()
|
|
579
|
+
vex.history.clear()
|
|
580
|
+
|
|
581
|
+
// Painel visual ancorado a um ícone
|
|
582
|
+
vex.history.open('#icone-notificacoes')
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
---
|
|
586
|
+
|
|
587
|
+
### Controle global
|
|
588
|
+
|
|
589
|
+
```js
|
|
590
|
+
// Remove tudo
|
|
591
|
+
vex.dismissAll()
|
|
592
|
+
|
|
593
|
+
// Com filtros
|
|
594
|
+
vex.dismissAll({ type: 'error' })
|
|
595
|
+
vex.dismissAll({ position: 'fixed' })
|
|
596
|
+
|
|
597
|
+
// Fecha um alert/modal específico pelo ID retornado
|
|
598
|
+
const id = vex.alert.loading('Processando...')
|
|
599
|
+
vex.dismiss(id)
|
|
600
|
+
|
|
601
|
+
// Configuração global padrão
|
|
602
|
+
vex.configure({
|
|
603
|
+
duration: 5000,
|
|
604
|
+
position: 'top',
|
|
605
|
+
maxStack: 3,
|
|
606
|
+
animate: 'slide',
|
|
607
|
+
})
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
---
|
|
611
|
+
|
|
612
|
+
## Sistema de Temas
|
|
613
|
+
|
|
614
|
+
### CSS Tokens (personalizável)
|
|
615
|
+
|
|
616
|
+
```css
|
|
617
|
+
/* Cole no seu CSS e sobrescreva o que precisar */
|
|
618
|
+
:root {
|
|
619
|
+
/* Aparência geral */
|
|
620
|
+
--vex-radius: 14px;
|
|
621
|
+
--vex-blur: 20px;
|
|
622
|
+
--vex-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
|
623
|
+
--vex-border-width: 1px;
|
|
624
|
+
--vex-font: inherit;
|
|
625
|
+
--vex-progress-h: 3px;
|
|
626
|
+
|
|
627
|
+
/* Cores por tipo */
|
|
628
|
+
--vex-success-bg: rgba(5, 46, 22, 0.85);
|
|
629
|
+
--vex-success-border: rgba(34, 197, 94, 0.35);
|
|
630
|
+
--vex-success-text: #4ade80;
|
|
631
|
+
--vex-success-accent: #22c55e;
|
|
632
|
+
|
|
633
|
+
--vex-error-bg: rgba(40, 5, 5, 0.85);
|
|
634
|
+
--vex-error-border: rgba(239, 68, 68, 0.35);
|
|
635
|
+
--vex-error-text: #f87171;
|
|
636
|
+
--vex-error-accent: #ef4444;
|
|
637
|
+
|
|
638
|
+
/* ... warning, info, neutral ... */
|
|
639
|
+
}
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
### Temas prontos
|
|
643
|
+
|
|
644
|
+
```js
|
|
645
|
+
// Importar no seu CSS/JS
|
|
646
|
+
import 'vexui/themes/glass' // Glassmorphism (padrão)
|
|
647
|
+
import 'vexui/themes/flat' // Flat design, sem blur
|
|
648
|
+
import 'vexui/themes/outline' // Apenas bordas
|
|
649
|
+
import 'vexui/themes/minimal' // Ultra-discreto
|
|
650
|
+
import 'vexui/themes/neon' // Neon / cyberpunk
|
|
651
|
+
import 'vexui/themes/pastel' // Tons suaves
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
### Alternar Dark / Light
|
|
655
|
+
|
|
656
|
+
```js
|
|
657
|
+
vex.setTheme('dark') // força dark
|
|
658
|
+
vex.setTheme('light') // força light
|
|
659
|
+
vex.setTheme('auto') // respeita o sistema (padrão)
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
### Tema por instância
|
|
663
|
+
|
|
664
|
+
```js
|
|
665
|
+
// Sobrescreve o visual apenas desta notificação
|
|
666
|
+
vex.alert.success('Deploy OK!', {
|
|
667
|
+
theme: {
|
|
668
|
+
background: 'rgba(5, 46, 22, 0.95)',
|
|
669
|
+
border: 'rgba(34, 197, 94, 0.6)',
|
|
670
|
+
progressColor: '#4ade80',
|
|
671
|
+
borderRadius: '20px',
|
|
672
|
+
}
|
|
673
|
+
})
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
---
|
|
677
|
+
|
|
678
|
+
## Acessibilidade
|
|
679
|
+
|
|
680
|
+
Todos os componentes são construídos seguindo **WCAG 2.1 AA** e **WAI-ARIA 1.2**.
|
|
681
|
+
|
|
682
|
+
| Recurso | Implementação |
|
|
683
|
+
|---|---|
|
|
684
|
+
| `role="alert"` | Alerts anunciados imediatamente por screen readers |
|
|
685
|
+
| `role="dialog"` | Modals e Drawers com `aria-modal="true"` |
|
|
686
|
+
| `aria-live` | Região de alerts com `polite` ou `assertive` por prioridade |
|
|
687
|
+
| `aria-labelledby` | Título do modal vinculado corretamente |
|
|
688
|
+
| `aria-describedby` | Mensagem vinculada ao elemento correto |
|
|
689
|
+
| Focus trap | Foco preso dentro do modal; restaurado ao fechar |
|
|
690
|
+
| Navegação por teclado | `Tab` / `Shift+Tab` entre ações; `Enter` confirma; `Esc` cancela |
|
|
691
|
+
| Contraste | Mínimo de 4.5:1 em todos os temas (WCAG AA) |
|
|
692
|
+
| `prefers-reduced-motion` | Animações desativadas; transições simples de opacidade |
|
|
693
|
+
|
|
694
|
+
---
|
|
695
|
+
|
|
696
|
+
## Integrações
|
|
697
|
+
|
|
698
|
+
### Vanilla JavaScript
|
|
699
|
+
|
|
700
|
+
```js
|
|
701
|
+
import vex from 'vexui'
|
|
702
|
+
import 'vexui/dist/vexui.css'
|
|
703
|
+
|
|
704
|
+
vex.alert.success('Pronto!')
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
### React
|
|
708
|
+
|
|
709
|
+
```jsx
|
|
710
|
+
import { useVex } from 'vexui/react'
|
|
711
|
+
|
|
712
|
+
function MeuComponente() {
|
|
713
|
+
const vex = useVex()
|
|
714
|
+
|
|
715
|
+
const handleDelete = async () => {
|
|
716
|
+
const ok = await vex.modal.confirm({
|
|
717
|
+
title: 'Excluir item?',
|
|
718
|
+
type: 'danger',
|
|
719
|
+
})
|
|
720
|
+
if (ok) deleteItem()
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
return <button onClick={handleDelete}>Excluir</button>
|
|
724
|
+
}
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
### Vue 3
|
|
728
|
+
|
|
729
|
+
```js
|
|
730
|
+
// main.js
|
|
731
|
+
import VexUI from 'vexui/vue'
|
|
732
|
+
app.use(VexUI)
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
```vue
|
|
736
|
+
<script setup>
|
|
737
|
+
import { useVex } from 'vexui/vue'
|
|
738
|
+
const vex = useVex()
|
|
739
|
+
|
|
740
|
+
const confirmar = async () => {
|
|
741
|
+
const ok = await vex.modal.confirm({ title: 'Tem certeza?' })
|
|
742
|
+
if (ok) executar()
|
|
743
|
+
}
|
|
744
|
+
</script>
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
### CDN / Script tag
|
|
748
|
+
|
|
749
|
+
```html
|
|
750
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vexui/dist/vexui.css">
|
|
751
|
+
<script src="https://cdn.jsdelivr.net/npm/vexui/dist/vexui.umd.js"></script>
|
|
752
|
+
|
|
753
|
+
<script>
|
|
754
|
+
VexUI.alert.success('Funciona em qualquer projeto!')
|
|
755
|
+
</script>
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
---
|
|
759
|
+
|
|
760
|
+
## API Completa
|
|
761
|
+
|
|
762
|
+
### `vex.alert.*`
|
|
763
|
+
|
|
764
|
+
| Método | Retorno | Descrição |
|
|
765
|
+
|---|---|---|
|
|
766
|
+
| `vex.alert.success(msg, opts?)` | `string` (id) | Alert de sucesso |
|
|
767
|
+
| `vex.alert.error(msg, opts?)` | `string` (id) | Alert de erro |
|
|
768
|
+
| `vex.alert.warning(msg, opts?)` | `string` (id) | Alert de aviso |
|
|
769
|
+
| `vex.alert.info(msg, opts?)` | `string` (id) | Alert informativo |
|
|
770
|
+
| `vex.alert.neutral(msg, opts?)` | `string` (id) | Alert neutro |
|
|
771
|
+
| `vex.alert.loading(msg, opts?)` | `string` (id) | Alert com spinner; atualizável via `vex.update(id)` |
|
|
772
|
+
| `vex.alert.upload(msg, opts?)` | `string` (id) | Alert com barra de progresso de upload |
|
|
773
|
+
| `vex.alert.inline(selector, opts)` | `string` (id) | Renderiza dentro de um container |
|
|
774
|
+
| `vex.alert.fixed(opts)` | `string` (id) | Renderiza fixo na viewport |
|
|
775
|
+
| `vex.alert.attach(selector, opts)` | `string` (id) | Ancora a um elemento do DOM |
|
|
776
|
+
| `vex.alert.show(opts)` | `string` (id) | Método base — aceita qualquer configuração |
|
|
777
|
+
|
|
778
|
+
### `vex.modal.*`
|
|
779
|
+
|
|
780
|
+
| Método | Retorno | Descrição |
|
|
781
|
+
|---|---|---|
|
|
782
|
+
| `vex.modal.confirm(opts)` | `Promise<boolean>` | Sim ou não |
|
|
783
|
+
| `vex.modal.alert(opts)` | `Promise<void>` | Apenas fechar |
|
|
784
|
+
| `vex.modal.form(opts)` | `Promise<object \| null>` | Dados do formulário |
|
|
785
|
+
| `vex.modal.prompt(opts)` | `Promise<string \| null>` | Texto digitado |
|
|
786
|
+
| `vex.modal.preview(opts)` | `void` | Preview de imagem / mídia |
|
|
787
|
+
| `vex.modal.custom(opts)` | `{ close }` | Modal com HTML/DOM livre |
|
|
788
|
+
|
|
789
|
+
### `vex.*` — Métodos globais
|
|
790
|
+
|
|
791
|
+
| Método | Descrição |
|
|
792
|
+
|---|---|
|
|
793
|
+
| `vex.flow(steps[])` | Step Flow (wizard) multi-etapa |
|
|
794
|
+
| `vex.drawer(opts)` | Abre painel lateral deslizante |
|
|
795
|
+
| `vex.prompt.inline(selector, opts)` | Input contextual inline no DOM |
|
|
796
|
+
| `vex.update(id, opts)` | Atualiza alert ativo em tempo real |
|
|
797
|
+
| `vex.progress(id, { loaded, total })` | Atualiza progresso de upload |
|
|
798
|
+
| `vex.dismiss(id)` | Fecha alert ou modal específico |
|
|
799
|
+
| `vex.dismissAll(filter?)` | Fecha tudo (com filtro opcional) |
|
|
800
|
+
| `vex.setTheme(theme)` | `'dark'` · `'light'` · `'auto'` |
|
|
801
|
+
| `vex.configure(opts)` | Configura opções padrão globais |
|
|
802
|
+
| `vex.history.get()` | Retorna histórico de notificações |
|
|
803
|
+
| `vex.history.clear()` | Limpa o histórico |
|
|
804
|
+
| `vex.history.markAllRead()` | Marca todas como lidas |
|
|
805
|
+
| `vex.history.open(anchor)` | Abre painel visual de histórico |
|
|
806
|
+
|
|
807
|
+
---
|
|
808
|
+
|
|
809
|
+
## Roadmap
|
|
810
|
+
|
|
811
|
+
| Versão | Nome | Entregas |
|
|
812
|
+
|---|---|---|
|
|
813
|
+
| **v1.0** | Core | Alerts (6 tipos) · Fixed & Inline · Modals (confirm, alert, form, prompt, preview, custom) · Glassmorphism · Progress bar · Queue · Dark/Light auto |
|
|
814
|
+
| **v1.1** | Adapters | React hook · Vue 3 plugin · CDN build · Svelte store |
|
|
815
|
+
| **v1.2** | Power Features | Step Flow · Drawer · Inline Prompt · Attached Alert · Upload progress · Histórico |
|
|
816
|
+
| **v2.0** | Enterprise | 6 temas prontos · Painel de notificações built-in · i18n · TypeScript types completo · Storybook oficial |
|
|
817
|
+
|
|
818
|
+
---
|
|
819
|
+
|
|
820
|
+
## Licença
|
|
821
|
+
|
|
822
|
+
MIT © VexUI
|
|
823
|
+
|
|
824
|
+
---
|
|
825
|
+
|
|
826
|
+
<div align="center">
|
|
827
|
+
|
|
828
|
+
Feito com ❤️ — **[npm](https://npmjs.com/package/vexui)** · **[Documentação](https://vexui.dev)** · **[Changelog](./CHANGELOG.md)**
|
|
829
|
+
|
|
830
|
+
</div>
|