@xjur-ui/react 3.1.2 → 4.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/CHANGELOG.md +1472 -3
- package/dist/components/controls/pin-input/index.d.ts +4 -0
- package/dist/components/controls/pin-input/index.js +1 -0
- package/dist/components/controls/pin-input/pin-input-caption.d.ts +2 -0
- package/dist/components/controls/pin-input/pin-input-caption.js +1 -0
- package/dist/components/controls/pin-input/pin-input-control.d.ts +6 -0
- package/dist/components/controls/pin-input/pin-input-control.js +1 -0
- package/dist/components/controls/pin-input/pin-input-root.d.ts +29 -0
- package/dist/components/controls/pin-input/pin-input-root.js +1 -0
- package/dist/components/controls/pin-input/pin-input-trigger.d.ts +2 -0
- package/dist/components/controls/pin-input/pin-input-trigger.js +1 -0
- package/dist/components/message/index.d.ts +5 -0
- package/dist/components/message/index.js +1 -0
- package/dist/components/message/message-action.d.ts +3 -0
- package/dist/components/message/message-action.js +1 -0
- package/dist/components/message/message-close.d.ts +4 -0
- package/dist/components/message/message-close.js +1 -0
- package/dist/components/message/message-icon.d.ts +1 -0
- package/dist/components/message/message-icon.js +1 -0
- package/dist/components/message/message-root.d.ts +15 -0
- package/dist/components/message/message-root.js +1 -0
- package/dist/components/message/message-text.d.ts +3 -0
- package/dist/components/message/message-text.js +1 -0
- package/dist/components/selection-bar/index.d.ts +5 -58
- package/dist/components/selection-bar/index.js +1 -1
- package/dist/components/selection-bar/selection-bar-action-group.d.ts +2 -0
- package/dist/components/selection-bar/selection-bar-action-group.js +1 -0
- package/dist/components/selection-bar/selection-bar-action.d.ts +3 -0
- package/dist/components/selection-bar/selection-bar-action.js +1 -0
- package/dist/components/selection-bar/selection-bar-close.d.ts +3 -0
- package/dist/components/selection-bar/selection-bar-close.js +1 -0
- package/dist/components/selection-bar/selection-bar-root.d.ts +12 -0
- package/dist/components/selection-bar/selection-bar-root.js +1 -0
- package/dist/components/selection-bar/selection-bar-title.d.ts +4 -0
- package/dist/components/selection-bar/selection-bar-title.js +1 -0
- package/dist/components/toast/index.d.ts +1 -24
- package/dist/components/toast/index.js +1 -1
- package/dist/components/toast/toast-component.d.ts +10 -0
- package/dist/components/toast/toast-component.js +1 -0
- package/dist/components/toast/toast.d.ts +25 -0
- package/dist/components/toast/toast.js +1 -0
- package/dist/components/toast/toast.types.d.ts +6 -0
- package/dist/components/toast/toast.types.js +0 -0
- package/dist/index.css +1 -1
- package/package.json +8 -6
- package/dist/components/controls/otp-input.d.ts +0 -20
- package/dist/components/controls/otp-input.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,16 +1,1485 @@
|
|
|
1
1
|
# @xjur-ui/react
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 4.0.0
|
|
4
4
|
|
|
5
|
-
###
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 07e5bf1: # Migração de OTPInput para PinInput + Novo Componente Message
|
|
8
|
+
|
|
9
|
+
Esta release inclui duas mudanças importantes:
|
|
10
|
+
1. **BREAKING CHANGE**: Migração do componente OTPInput para PinInput com arquitetura composicional
|
|
11
|
+
2. **NEW FEATURE**: Adição do componente Message para feedback e notificações
|
|
12
|
+
|
|
13
|
+
***
|
|
14
|
+
|
|
15
|
+
## 1. Migração de OTPInput para PinInput com arquitetura composicional
|
|
16
|
+
|
|
17
|
+
## Breaking Changes
|
|
18
|
+
|
|
19
|
+
**BREAKING CHANGE**: O componente `OTPInput` foi completamente substituído pelo novo componente `PinInput` com arquitetura composicional moderna.
|
|
20
|
+
|
|
21
|
+
### Migração Necessária
|
|
22
|
+
|
|
23
|
+
#### Antes (OTPInput - Depreciado)
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import {
|
|
27
|
+
OTPInputRoot,
|
|
28
|
+
OTPInputTrigger,
|
|
29
|
+
OTPInputInput,
|
|
30
|
+
OTPInputCaption,
|
|
31
|
+
OTPInputHiddenInput
|
|
32
|
+
} from '@xjur-ui/react/otp-input'
|
|
33
|
+
|
|
34
|
+
;<OTPInputRoot state="normal" length={4} initial="1234">
|
|
35
|
+
<OTPInputTrigger>
|
|
36
|
+
<OTPInputInput index={0} />
|
|
37
|
+
<OTPInputInput index={1} />
|
|
38
|
+
<OTPInputInput index={2} />
|
|
39
|
+
<OTPInputInput index={3} />
|
|
40
|
+
</OTPInputTrigger>
|
|
41
|
+
<OTPInputCaption>Digite o código</OTPInputCaption>
|
|
42
|
+
<OTPInputHiddenInput name="code" />
|
|
43
|
+
</OTPInputRoot>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
#### Depois (PinInput - Novo)
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import {
|
|
50
|
+
PinInput,
|
|
51
|
+
PinInputControl,
|
|
52
|
+
PinInputTrigger,
|
|
53
|
+
PinInputCaption
|
|
54
|
+
} from '@xjur-ui/react/pin-input'
|
|
55
|
+
|
|
56
|
+
;<PinInput state="normal" length={4} defaultValue="1234" name="code">
|
|
57
|
+
<PinInputTrigger>
|
|
58
|
+
<PinInputControl index={0} />
|
|
59
|
+
<PinInputControl index={1} />
|
|
60
|
+
<PinInputControl index={2} />
|
|
61
|
+
<PinInputControl index={3} />
|
|
62
|
+
</PinInputTrigger>
|
|
63
|
+
<PinInputCaption>Digite o código</PinInputCaption>
|
|
64
|
+
</PinInput>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Principais Mudanças na API
|
|
68
|
+
|
|
69
|
+
| OTPInput (Antigo) | PinInput (Novo) | Notas |
|
|
70
|
+
| --------------------- | ---------------------------------------- | ------------------------------------------ |
|
|
71
|
+
| `OTPInputRoot` | `PinInput` | Renomeado para seguir padrão composicional |
|
|
72
|
+
| `OTPInputInput` | `PinInputControl` | Nome mais descritivo |
|
|
73
|
+
| `initial` prop | `defaultValue` prop | API mais consistente com React |
|
|
74
|
+
| `OTPInputHiddenInput` | Campo automático via prop `name` | Simplificado |
|
|
75
|
+
| Sem callbacks | `onChangeValue`, `onComplete`, `onError` | Maior controle |
|
|
76
|
+
| Sem auto-submit | `autoSubmit` prop | Nova funcionalidade |
|
|
77
|
+
| Sem placeholder | `placeholder` prop | Melhoria UX |
|
|
78
|
+
|
|
79
|
+
## Novas Funcionalidades
|
|
80
|
+
|
|
81
|
+
### 1. Auto-Submit de Formulários
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
<PinInput autoSubmit={true} name="code" onError={handleError}>
|
|
85
|
+
{/* Submete automaticamente quando completo */}
|
|
86
|
+
</PinInput>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 2. Placeholder Posicional
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
<PinInput placeholder="0000">{/* Mostra "0" em cada campo vazio */}</PinInput>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 3. Callbacks Detalhados
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
<PinInput
|
|
99
|
+
onChangeValue={(value) => console.log('Valor:', value)}
|
|
100
|
+
onComplete={(value) => console.log('Completo:', value)}
|
|
101
|
+
onError={(error) => console.error('Erro:', error)}
|
|
102
|
+
>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 4. Hidden Input Automático
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
<PinInput name="verification_code">
|
|
109
|
+
{/* Cria automaticamente <input type="hidden" name="verification_code" /> */}
|
|
110
|
+
</PinInput>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 5. Colagem Inteligente de Código
|
|
114
|
+
- Detecta quando usuário cola código completo
|
|
115
|
+
- Distribui automaticamente pelos campos
|
|
116
|
+
- Valida todos os caracteres contra padrão regex
|
|
117
|
+
|
|
118
|
+
### 6. Validação Customizada por Campo
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
<PinInputControl pattern="[A-Z0-9]" inputMode="text" />
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Melhorias de Performance
|
|
125
|
+
- **Context memoizado**: Reduz re-renders desnecessários
|
|
126
|
+
- **Callbacks estáveis**: `useCallback` para `setCodeAt` e `registerInput`
|
|
127
|
+
- **Refs otimizadas**: Mantém referências sem causar re-renders
|
|
128
|
+
- **Validação lazy**: Regex compilado apenas quando necessário
|
|
129
|
+
|
|
130
|
+
## Melhorias de Acessibilidade
|
|
131
|
+
- `autoComplete="one-time-code"`: Melhor experiência em dispositivos móveis
|
|
132
|
+
- `inputMode="numeric"`: Exibe teclado numérico automaticamente
|
|
133
|
+
- Navegação por teclado completa (setas, backspace, tab)
|
|
134
|
+
- Estados visuais claros (normal, error, disabled)
|
|
135
|
+
- Suporte completo a leitores de tela
|
|
136
|
+
|
|
137
|
+
## Melhorias de DX (Developer Experience)
|
|
138
|
+
- **TypeScript completo**: Tipos exportados e documentados
|
|
139
|
+
- **Hook de contexto**: `usePinInputContext()` para componentes customizados
|
|
140
|
+
- **Documentação**: README.md detalhado com exemplos práticos
|
|
141
|
+
- **Stories**: 9 exemplos no Storybook cobrindo todos os casos de uso
|
|
142
|
+
- **Tratamento de erros**: Callback `onError` para debugging
|
|
143
|
+
|
|
144
|
+
## Arquivos Criados
|
|
145
|
+
- `packages/react/src/components/controls/pin-input/pin-input-root.tsx`
|
|
146
|
+
- `packages/react/src/components/controls/pin-input/pin-input-control.tsx`
|
|
147
|
+
- `packages/react/src/components/controls/pin-input/pin-input-trigger.tsx`
|
|
148
|
+
- `packages/react/src/components/controls/pin-input/pin-input-caption.tsx`
|
|
149
|
+
- `packages/react/src/components/controls/pin-input/index.ts`
|
|
150
|
+
- `packages/react/src/components/controls/pin-input/README.md`
|
|
151
|
+
|
|
152
|
+
## Arquivos Removidos
|
|
153
|
+
- `packages/react/src/components/controls/otp-input.tsx`
|
|
154
|
+
- `apps/docs/src/components/otp-input.stories.tsx`
|
|
155
|
+
|
|
156
|
+
## Arquivos Criados/Atualizados (Documentação)
|
|
157
|
+
- `apps/docs/src/components/pin-input.stories.tsx` - 9 stories interativas:
|
|
158
|
+
- **Playground**: Exemplo básico com controles interativos
|
|
159
|
+
- **SixDigits**: OTP de 6 dígitos
|
|
160
|
+
- **Error**: Estado de erro com validação
|
|
161
|
+
- **Disabled**: Estado desabilitado
|
|
162
|
+
- **AutoSubmit**: Submissão automática de formulário
|
|
163
|
+
- **WithDefaultValue**: Valor inicial pré-preenchido
|
|
164
|
+
- **Alphanumeric**: Validação customizada alfanumérica
|
|
165
|
+
- **WithMonitoring**: Monitoramento em tempo real
|
|
166
|
+
- **TwoFactorAuth**: Fluxo completo de autenticação 2FA
|
|
167
|
+
|
|
168
|
+
## Casos de Uso Suportados
|
|
169
|
+
1. ✅ Verificação de Email/SMS (OTP de 6 dígitos)
|
|
170
|
+
2. ✅ Autenticação de Dois Fatores (2FA)
|
|
171
|
+
3. ✅ PIN de Segurança (4 dígitos)
|
|
172
|
+
4. ✅ Código de Ativação (alfanumérico)
|
|
173
|
+
5. ✅ Verificação de Pagamento
|
|
174
|
+
6. ✅ Reset de Senha
|
|
175
|
+
7. ✅ Confirmação de Transação
|
|
176
|
+
|
|
177
|
+
## Guia de Migração Detalhado
|
|
178
|
+
|
|
179
|
+
### Passo 1: Atualizar Imports
|
|
180
|
+
|
|
181
|
+
```diff
|
|
182
|
+
- import { OTPInputRoot, OTPInputTrigger, OTPInputInput, OTPInputCaption } from '@xjur-ui/react/otp-input'
|
|
183
|
+
+ import { PinInput, PinInputTrigger, PinInputControl, PinInputCaption } from '@xjur-ui/react/pin-input'
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Passo 2: Renomear Componentes
|
|
187
|
+
|
|
188
|
+
```diff
|
|
189
|
+
- <OTPInputRoot state="normal" length={4} initial="1234">
|
|
190
|
+
+ <PinInput state="normal" length={4} defaultValue="1234" name="code">
|
|
191
|
+
<OTPInputTrigger>
|
|
192
|
+
- <OTPInputInput index={0} />
|
|
193
|
+
+ <PinInputControl index={0} />
|
|
194
|
+
</OTPInputTrigger>
|
|
195
|
+
- <OTPInputHiddenInput name="code" />
|
|
196
|
+
</PinInput>
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Passo 3: Adicionar Callbacks (Opcional)
|
|
200
|
+
|
|
201
|
+
```tsx
|
|
202
|
+
<PinInput
|
|
203
|
+
onComplete={(code) => {
|
|
204
|
+
// Código completo digitado
|
|
205
|
+
verifyCode(code)
|
|
206
|
+
}}
|
|
207
|
+
onChangeValue={(value) => {
|
|
208
|
+
// Cada alteração
|
|
209
|
+
console.log('Valor:', value)
|
|
210
|
+
}}
|
|
211
|
+
>
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Passo 4: Habilitar Auto-Submit (Opcional)
|
|
215
|
+
|
|
216
|
+
```tsx
|
|
217
|
+
<form onSubmit={handleSubmit}>
|
|
218
|
+
<PinInput autoSubmit={true} name="code">
|
|
219
|
+
{/* Submete automaticamente ao completar */}
|
|
220
|
+
</PinInput>
|
|
221
|
+
</form>
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Compatibilidade
|
|
225
|
+
- ✅ React 19.1.1+
|
|
226
|
+
- ✅ TypeScript 5.x
|
|
227
|
+
- ✅ Tailwind CSS 4.x
|
|
228
|
+
- ✅ Navegadores modernos (Chrome, Firefox, Safari, Edge)
|
|
229
|
+
- ✅ Dispositivos móveis (iOS Safari, Chrome Mobile)
|
|
230
|
+
|
|
231
|
+
## Próximos Passos
|
|
232
|
+
|
|
233
|
+
Após esta atualização, recomendamos:
|
|
234
|
+
1. **Testar fluxos de autenticação**: Verifique todos os fluxos que usavam OTPInput
|
|
235
|
+
2. **Revisar validações**: Configure validações customizadas se necessário
|
|
236
|
+
3. **Implementar auto-submit**: Aproveite a nova funcionalidade para melhor UX
|
|
237
|
+
4. **Adicionar tratamento de erros**: Use o callback `onError` para debugging
|
|
238
|
+
|
|
239
|
+
## Suporte
|
|
240
|
+
|
|
241
|
+
Para dúvidas ou problemas com a migração, consulte:
|
|
242
|
+
- 📖 README.md completo em `packages/react/src/components/controls/pin-input/README.md`
|
|
243
|
+
- 📚 9 exemplos interativos no Storybook
|
|
244
|
+
- 🔍 TypeScript types para autocomplete e documentação inline
|
|
245
|
+
|
|
246
|
+
***
|
|
247
|
+
|
|
248
|
+
## 2. Novo Componente Message
|
|
249
|
+
|
|
250
|
+
### Visão Geral
|
|
251
|
+
|
|
252
|
+
O **Message** é um novo componente composicional para exibir mensagens e notificações contextuais aos usuários. Perfeito para feedbacks de ações, alertas, avisos e mensagens de sucesso.
|
|
253
|
+
|
|
254
|
+
### Características Principais
|
|
255
|
+
|
|
256
|
+
#### Arquitetura Composicional
|
|
257
|
+
- **MessageRoot**: Container principal com controle de variante e modo de exibição
|
|
258
|
+
- **MessageIcon**: Ícone automático baseado na variante (success, neutral, warning, error)
|
|
259
|
+
- **MessageText**: Componente de texto com truncate automático
|
|
260
|
+
- **MessageClose**: Botão de fechamento que remove o elemento do DOM
|
|
261
|
+
|
|
262
|
+
#### Variantes Visuais
|
|
263
|
+
|
|
264
|
+
| Variante | Ícone | Cor | Uso |
|
|
265
|
+
| -------- | ------- | -------- | ----------------------------------- |
|
|
266
|
+
| success | check | Verde | Confirmações de ações bem-sucedidas |
|
|
267
|
+
| neutral | info | Cinza | Informações gerais |
|
|
268
|
+
| warning | warning | Amarelo | Avisos que requerem atenção |
|
|
269
|
+
| error | error | Vermelho | Erros e problemas críticos |
|
|
270
|
+
|
|
271
|
+
#### Modos de Apresentação
|
|
272
|
+
|
|
273
|
+
**Full-Width (Padrão)**
|
|
274
|
+
- Largura total do container
|
|
275
|
+
- Ícone visível à esquerda
|
|
276
|
+
- Borda colorida de 4px à esquerda
|
|
277
|
+
- Padding generoso
|
|
278
|
+
- Ideal para mensagens de sistema
|
|
279
|
+
|
|
280
|
+
**Inline (Compacto)**
|
|
281
|
+
- Largura ajustada ao conteúdo (w-fit)
|
|
282
|
+
- Sem ícone
|
|
283
|
+
- Sem borda lateral
|
|
284
|
+
- Padding compacto
|
|
285
|
+
- Ideal para mensagens próximas a campos
|
|
286
|
+
|
|
287
|
+
### API do Componente
|
|
288
|
+
|
|
289
|
+
#### MessageRoot Props
|
|
290
|
+
|
|
291
|
+
```tsx
|
|
292
|
+
interface MessageRootProps {
|
|
293
|
+
variant?: 'success' | 'neutral' | 'warning' | 'error' // Padrão: 'neutral'
|
|
294
|
+
inline?: boolean // Padrão: false
|
|
295
|
+
children: React.ReactNode
|
|
296
|
+
// + todas as props de HTMLDivElement
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
#### MessageIcon
|
|
301
|
+
- Renderiza automaticamente o ícone apropriado
|
|
302
|
+
- Não é exibido quando `inline={true}`
|
|
303
|
+
- Sem props customizáveis
|
|
304
|
+
|
|
305
|
+
#### MessageText
|
|
306
|
+
- Wrapper do Typography com truncate automático
|
|
307
|
+
- Aceita todas as props do Typography
|
|
308
|
+
- Herda cor do contexto da mensagem
|
|
309
|
+
|
|
310
|
+
#### MessageClose
|
|
311
|
+
- Botão que remove a mensagem do DOM
|
|
312
|
+
- Renderiza ícone "close" por padrão
|
|
313
|
+
- Aceita children customizados
|
|
314
|
+
- Aceita todas as props de `<button>`
|
|
315
|
+
|
|
316
|
+
### Exemplos de Uso
|
|
317
|
+
|
|
318
|
+
#### Mensagem de Sucesso
|
|
319
|
+
|
|
320
|
+
```tsx
|
|
321
|
+
import {
|
|
322
|
+
MessageRoot,
|
|
323
|
+
MessageIcon,
|
|
324
|
+
MessageText,
|
|
325
|
+
MessageClose
|
|
326
|
+
} from '@xjur-ui/react/message'
|
|
327
|
+
|
|
328
|
+
;<MessageRoot variant="success">
|
|
329
|
+
<MessageIcon />
|
|
330
|
+
<MessageText>Documento salvo com sucesso!</MessageText>
|
|
331
|
+
<MessageClose />
|
|
332
|
+
</MessageRoot>
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
#### Mensagem de Erro em Formulário
|
|
336
|
+
|
|
337
|
+
```tsx
|
|
338
|
+
<MessageRoot variant="error">
|
|
339
|
+
<MessageIcon />
|
|
340
|
+
<MessageText>Por favor, preencha todos os campos obrigatórios.</MessageText>
|
|
341
|
+
</MessageRoot>
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
#### Aviso Importante
|
|
345
|
+
|
|
346
|
+
```tsx
|
|
347
|
+
<MessageRoot variant="warning">
|
|
348
|
+
<MessageIcon />
|
|
349
|
+
<MessageText>Esta ação não pode ser desfeita.</MessageText>
|
|
350
|
+
</MessageRoot>
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
#### Mensagem Inline
|
|
354
|
+
|
|
355
|
+
```tsx
|
|
356
|
+
<MessageRoot variant="neutral" inline>
|
|
357
|
+
<MessageText>Informação adicional</MessageText>
|
|
358
|
+
</MessageRoot>
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Funcionalidades
|
|
362
|
+
|
|
363
|
+
#### 1. Animação de Entrada
|
|
364
|
+
- Fade-in suave (250ms) para não assustar usuários
|
|
365
|
+
- Classes: `animate-in fade-in-0 duration-250`
|
|
366
|
+
|
|
367
|
+
#### 2. Fechamento Inteligente
|
|
368
|
+
- Remove o elemento do DOM ao clicar no botão de fechar
|
|
369
|
+
- Utiliza `ref.current?.remove()` para limpeza completa
|
|
370
|
+
- Callback `onClose` disponível via contexto
|
|
371
|
+
|
|
372
|
+
#### 3. Context API
|
|
373
|
+
|
|
374
|
+
```tsx
|
|
375
|
+
interface MessageRootContextValue {
|
|
376
|
+
variant: MessageRootVariant
|
|
377
|
+
inline: boolean
|
|
378
|
+
onClose?: () => void
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Hook para componentes customizados
|
|
382
|
+
const { variant, inline, onClose } = useMessageRootContext()
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
#### 4. Mapeamento Automático de Ícones
|
|
386
|
+
|
|
387
|
+
```tsx
|
|
388
|
+
const iconMap = new Map<MessageRootVariant, React.ReactNode>([
|
|
389
|
+
['success', <Icon name="check" className="text-success-idle" />],
|
|
390
|
+
['neutral', <Icon name="info" className="text-icon-neutral-3" />],
|
|
391
|
+
['warning', <Icon name="warning" className="text-warning-idle" />],
|
|
392
|
+
['error', <Icon name="error" className="text-danger-idle" />]
|
|
393
|
+
])
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### Acessibilidade
|
|
397
|
+
- ✅ Botão de fechamento com `type="button"` para evitar submissão acidental
|
|
398
|
+
- ✅ Estados visuais claros com cores contrastantes
|
|
399
|
+
- ✅ Animação suave de entrada
|
|
400
|
+
- ✅ Remoção do DOM ao fechar para limpar árvore de acessibilidade
|
|
401
|
+
- ✅ Texto com truncate para evitar quebras visuais
|
|
402
|
+
- ✅ Suporte completo a leitores de tela
|
|
403
|
+
|
|
404
|
+
### Performance
|
|
405
|
+
- **Context memoizado**: Reduz re-renders desnecessários
|
|
406
|
+
- **Callback estável**: `handleClose` com `useCallback`
|
|
407
|
+
- **Ícones pré-compilados**: Map de ícones criado uma única vez
|
|
408
|
+
- **Composição modular**: Componentes pequenos e focados
|
|
409
|
+
|
|
410
|
+
### Documentação no Storybook
|
|
411
|
+
|
|
412
|
+
10 stories interativas demonstrando todos os casos de uso:
|
|
413
|
+
1. **Playground**: Exemplo interativo com controles
|
|
414
|
+
2. **Success**: Mensagem de sucesso
|
|
415
|
+
3. **Neutral**: Mensagem neutra/informativa
|
|
416
|
+
4. **Warning**: Mensagem de aviso
|
|
417
|
+
5. **Error**: Mensagem de erro
|
|
418
|
+
6. **InlineSuccess**: Versão compacta de sucesso
|
|
419
|
+
7. **InlineError**: Versão compacta de erro
|
|
420
|
+
8. **WithoutClose**: Mensagem persistente sem botão fechar
|
|
421
|
+
9. **LongText**: Demonstração com texto longo
|
|
422
|
+
10. **MultipleMessages**: Showcase de todas as variantes
|
|
423
|
+
|
|
424
|
+
### Casos de Uso Suportados
|
|
425
|
+
1. ✅ Feedback de ações (salvar, deletar, enviar)
|
|
426
|
+
2. ✅ Validação de formulários
|
|
427
|
+
3. ✅ Avisos importantes
|
|
428
|
+
4. ✅ Mensagens de erro
|
|
429
|
+
5. ✅ Notificações de sistema
|
|
430
|
+
6. ✅ Confirmações de operações
|
|
431
|
+
7. ✅ Informações contextuais inline
|
|
432
|
+
8. ✅ Alertas temporários
|
|
433
|
+
9. ✅ Status de operações assíncronas
|
|
434
|
+
10. ✅ Mensagens de onboarding
|
|
435
|
+
|
|
436
|
+
### Arquivos Criados
|
|
437
|
+
- `packages/react/src/components/message/index.ts`
|
|
438
|
+
- `packages/react/src/components/message/message-root.tsx`
|
|
439
|
+
- `packages/react/src/components/message/message-icon.tsx`
|
|
440
|
+
- `packages/react/src/components/message/message-text.tsx`
|
|
441
|
+
- `packages/react/src/components/message/message-close.tsx`
|
|
442
|
+
- `apps/docs/src/components/message.stories.tsx`
|
|
443
|
+
|
|
444
|
+
### Integração com Design System
|
|
445
|
+
|
|
446
|
+
O componente Message utiliza:
|
|
447
|
+
- **Design tokens** do @xjur-ui/styles para cores e espaçamentos
|
|
448
|
+
- **Sistema de ícones** via componente Icon
|
|
449
|
+
- **Typography** para texto consistente
|
|
450
|
+
- **TailwindCSS 4.x** para estilização
|
|
451
|
+
- **Tailwind Variants** (tv) para variantes CSS
|
|
452
|
+
|
|
453
|
+
### TypeScript
|
|
454
|
+
|
|
455
|
+
Tipagem completa exportada:
|
|
456
|
+
|
|
457
|
+
```tsx
|
|
458
|
+
export type MessageRootVariant = 'success' | 'neutral' | 'warning' | 'error'
|
|
459
|
+
|
|
460
|
+
export interface MessageRootProps extends ComponentProps<'div'> {
|
|
461
|
+
variant?: MessageRootVariant
|
|
462
|
+
inline?: boolean
|
|
463
|
+
children: React.ReactNode
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
export function useMessageRootContext(): MessageRootContextValue
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
### Compatibilidade
|
|
470
|
+
- ✅ React 19.1.1+
|
|
471
|
+
- ✅ TypeScript 5.x
|
|
472
|
+
- ✅ Tailwind CSS 4.x
|
|
473
|
+
- ✅ Navegadores modernos (Chrome, Firefox, Safari, Edge)
|
|
474
|
+
- ✅ Dispositivos móveis (iOS Safari, Chrome Mobile)
|
|
475
|
+
|
|
476
|
+
### Roadmap Futuro
|
|
477
|
+
|
|
478
|
+
Possíveis melhorias para próximas versões:
|
|
479
|
+
- Auto-dismiss com timeout configurável
|
|
480
|
+
- Posicionamento fixo (toast-style)
|
|
481
|
+
- Animações de saída customizáveis
|
|
482
|
+
- Suporte a ações customizadas (além de close)
|
|
483
|
+
- Progress bar para mensagens temporárias
|
|
484
|
+
- Queue de mensagens
|
|
485
|
+
- Suporte a rich content (links, imagens)
|
|
486
|
+
|
|
487
|
+
***
|
|
488
|
+
|
|
489
|
+
## 3. Novo Componente Toast (toast-v2)
|
|
490
|
+
|
|
491
|
+
### Visão Geral
|
|
492
|
+
|
|
493
|
+
O **Toast** é um sistema completo de notificações temporárias não intrusivas, construído sobre a biblioteca Sonner e o componente Message. Oferece uma API imperativa simples e poderosa para exibir notificações toast com posicionamento, ações e callbacks.
|
|
494
|
+
|
|
495
|
+
### Características Principais
|
|
496
|
+
|
|
497
|
+
#### Baseado em Sonner
|
|
498
|
+
- **Library moderna e performática**: Sonner é a biblioteca toast mais popular e otimizada do ecossistema React
|
|
499
|
+
- **Empilhamento inteligente**: Gerenciamento automático de múltiplos toasts
|
|
500
|
+
- **Animações suaves**: CSS transforms otimizadas
|
|
501
|
+
- **Mobile-friendly**: Suporte a swipe gestures
|
|
502
|
+
- **Acessibilidade integrada**: ARIA live regions automáticas
|
|
503
|
+
|
|
504
|
+
#### Integração com Message
|
|
505
|
+
- Reutiliza componente Message para consistência visual
|
|
506
|
+
- Usa MessageIcon, MessageText, MessageAction e MessageClose
|
|
507
|
+
- Design tokens compartilhados
|
|
508
|
+
- Mesmos padrões de acessibilidade
|
|
509
|
+
|
|
510
|
+
#### Arquitetura
|
|
511
|
+
- **ToastWrapper**: Provider global baseado no Toaster do Sonner
|
|
512
|
+
- **toast()**: Função imperativa para disparar toasts
|
|
513
|
+
- **ToastComponent**: Componente interno que renderiza usando Message
|
|
6
514
|
|
|
7
|
-
|
|
515
|
+
### API do Componente
|
|
516
|
+
|
|
517
|
+
O objeto `toast` expõe 3 métodos principais:
|
|
518
|
+
|
|
519
|
+
#### 1. toast.dispatch(message, options)
|
|
520
|
+
|
|
521
|
+
Dispara um novo toast e retorna o ID do toast criado.
|
|
522
|
+
|
|
523
|
+
```tsx
|
|
524
|
+
function toast.dispatch(message: string, options?: {
|
|
525
|
+
variant?: 'primary' | 'destructive'
|
|
526
|
+
dismissible?: boolean
|
|
527
|
+
action?: { label: string; onClick: () => void }
|
|
528
|
+
position?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'
|
|
529
|
+
inline?: boolean
|
|
530
|
+
duration?: number // ms, default: 3000
|
|
531
|
+
toasterId?: string
|
|
532
|
+
onAutoClose?: (toast) => void
|
|
533
|
+
onDismiss?: (toast) => void
|
|
534
|
+
}): string // Retorna o ID do toast
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
#### 2. toast.dismiss(toastId)
|
|
538
|
+
|
|
539
|
+
Fecha um toast específico pelo ID.
|
|
540
|
+
|
|
541
|
+
```tsx
|
|
542
|
+
function toast.dismiss(toastId: string): void
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
**Exemplo**:
|
|
546
|
+
|
|
547
|
+
```tsx
|
|
548
|
+
const toastId = toast.dispatch('Processando...')
|
|
549
|
+
// Mais tarde...
|
|
550
|
+
toast.dismiss(toastId)
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
#### 3. toast.clear()
|
|
554
|
+
|
|
555
|
+
Fecha todos os toasts ativos.
|
|
556
|
+
|
|
557
|
+
```tsx
|
|
558
|
+
function toast.clear(): void
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
**Exemplo**:
|
|
562
|
+
|
|
563
|
+
```tsx
|
|
564
|
+
// Fechar todos os toasts ao navegar para outra página
|
|
565
|
+
toast.clear()
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
#### Opções Disponíveis
|
|
569
|
+
|
|
570
|
+
| Opção | Tipo | Padrão | Descrição |
|
|
571
|
+
| ----------- | ---------------------------------------- | -------------- | --------------------------------------------- | ----------------------------------------- | --------------- |
|
|
572
|
+
| variant | `'primary' | 'destructive'` | `'primary'` | Estilo visual (mapeia para success/error) |
|
|
573
|
+
| dismissible | `boolean` | `true` | Se pode ser fechado manualmente |
|
|
574
|
+
| action | `{ label: string; onClick: () => void }` | `undefined` | Botão de ação adicional |
|
|
575
|
+
| position | `'top-right' | 'top-left' | ...` | `undefined` | Posição na tela |
|
|
576
|
+
| inline | `boolean` | `false` | Modo compacto |
|
|
577
|
+
| duration | `number` | `3000` | Duração em ms (Infinity se dismissible=false) |
|
|
578
|
+
| toasterId | `string` | `undefined` | ID do toaster específico |
|
|
579
|
+
| onAutoClose | `(toast) => void` | `undefined` | Callback ao fechar automaticamente |
|
|
580
|
+
| onDismiss | `(toast) => void` | `undefined` | Callback ao fechar |
|
|
581
|
+
|
|
582
|
+
### Variantes
|
|
583
|
+
|
|
584
|
+
| Variante | Mapeia para Message | Visual | Uso |
|
|
585
|
+
| ----------- | ------------------- | ------------------------ | -------------------------------- |
|
|
586
|
+
| primary | success | Verde com ícone check | Confirmações e sucessos (padrão) |
|
|
587
|
+
| destructive | error | Vermelho com ícone error | Erros e ações destrutivas |
|
|
588
|
+
|
|
589
|
+
**Nota**: Para variantes `neutral` e `warning`, use o componente Message diretamente com posicionamento fixo.
|
|
590
|
+
|
|
591
|
+
### Posições Disponíveis
|
|
592
|
+
- **top-right**: Canto superior direito
|
|
593
|
+
- **top-left**: Canto superior esquerdo
|
|
594
|
+
- **bottom-right**: Canto inferior direito (padrão Sonner)
|
|
595
|
+
- **bottom-left**: Canto inferior esquerdo
|
|
596
|
+
|
|
597
|
+
### Exemplos de Uso
|
|
598
|
+
|
|
599
|
+
#### Setup Inicial
|
|
600
|
+
|
|
601
|
+
```tsx
|
|
602
|
+
import { ToastWrapper } from '@xjur-ui/react/toast-v2'
|
|
603
|
+
|
|
604
|
+
function App() {
|
|
605
|
+
return (
|
|
606
|
+
<>
|
|
607
|
+
<ToastWrapper />
|
|
608
|
+
{/* Resto da aplicação */}
|
|
609
|
+
</>
|
|
610
|
+
)
|
|
611
|
+
}
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
#### Toast Simples
|
|
615
|
+
|
|
616
|
+
```tsx
|
|
617
|
+
import { toast } from '@xjur-ui/react/toast'
|
|
618
|
+
|
|
619
|
+
// Sucesso
|
|
620
|
+
toast.dispatch('Documento salvo com sucesso!')
|
|
621
|
+
|
|
622
|
+
// Erro
|
|
623
|
+
toast.dispatch('Erro ao processar solicitação', {
|
|
624
|
+
variant: 'destructive'
|
|
625
|
+
})
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
#### Toast com Ação
|
|
629
|
+
|
|
630
|
+
```tsx
|
|
631
|
+
toast.dispatch('Email enviado com sucesso', {
|
|
632
|
+
action: {
|
|
633
|
+
label: 'Desfazer',
|
|
634
|
+
onClick: () => undoSendEmail()
|
|
635
|
+
}
|
|
636
|
+
})
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
#### Toast Persistente com Dismiss
|
|
640
|
+
|
|
641
|
+
```tsx
|
|
642
|
+
const toastId = toast.dispatch('Processando... Por favor aguarde', {
|
|
643
|
+
dismissible: false,
|
|
644
|
+
duration: Infinity
|
|
645
|
+
})
|
|
646
|
+
|
|
647
|
+
// Depois de completar a tarefa
|
|
648
|
+
setTimeout(() => {
|
|
649
|
+
toast.dismiss(toastId)
|
|
650
|
+
toast.dispatch('Concluído!', { variant: 'primary' })
|
|
651
|
+
}, 3000)
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
#### Fechar Todos os Toasts
|
|
655
|
+
|
|
656
|
+
```tsx
|
|
657
|
+
// Disparar múltiplos toasts
|
|
658
|
+
toast.dispatch('Primeira notificação')
|
|
659
|
+
toast.dispatch('Segunda notificação')
|
|
660
|
+
toast.dispatch('Terceira notificação')
|
|
661
|
+
|
|
662
|
+
// Fechar todos de uma vez
|
|
663
|
+
toast.clear()
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
#### Toast com Posição e Duração
|
|
667
|
+
|
|
668
|
+
```tsx
|
|
669
|
+
toast.dispatch('Notificação importante', {
|
|
670
|
+
position: 'top-right',
|
|
671
|
+
duration: 5000 // 5 segundos
|
|
672
|
+
})
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
#### Toast com Callbacks
|
|
676
|
+
|
|
677
|
+
```tsx
|
|
678
|
+
toast.dispatch('Upload iniciado', {
|
|
679
|
+
onAutoClose: t => {
|
|
680
|
+
console.log('Toast auto-fechado:', t)
|
|
681
|
+
logAnalytics('toast_auto_close')
|
|
682
|
+
},
|
|
683
|
+
onDismiss: t => {
|
|
684
|
+
console.log('Toast fechado:', t)
|
|
685
|
+
}
|
|
686
|
+
})
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
### Funcionalidades
|
|
690
|
+
|
|
691
|
+
#### 1. Empilhamento Automático
|
|
692
|
+
- Sonner gerencia automaticamente a queue de toasts
|
|
693
|
+
- Toasts são empilhados verticalmente
|
|
694
|
+
- Animações coordenadas ao adicionar/remover
|
|
695
|
+
|
|
696
|
+
#### 2. Swipe to Dismiss (Mobile)
|
|
697
|
+
- Gestos de swipe para fechar em mobile
|
|
698
|
+
- Feedback visual durante o swipe
|
|
699
|
+
- Configuração automática pelo Sonner
|
|
700
|
+
|
|
701
|
+
#### 3. Auto-dismiss Inteligente
|
|
702
|
+
- Duração padrão de 3 segundos
|
|
703
|
+
- Ajuste automático para Infinity se `dismissible=false`
|
|
704
|
+
- Pause no hover (comportamento Sonner)
|
|
705
|
+
|
|
706
|
+
#### 4. Callbacks de Lifecycle
|
|
707
|
+
- `onAutoClose`: Chamado quando fecha automaticamente
|
|
708
|
+
- `onDismiss`: Chamado em qualquer tipo de fechamento
|
|
709
|
+
- Recebe objeto toast com ID e metadata
|
|
710
|
+
|
|
711
|
+
#### 5. Múltiplos Toasters
|
|
712
|
+
- Suporte a múltiplos toasters com IDs diferentes
|
|
713
|
+
- Útil para diferentes áreas da aplicação
|
|
714
|
+
- Configuração via `toasterId`
|
|
715
|
+
|
|
716
|
+
#### 6. Gerenciamento de Ciclo de Vida
|
|
717
|
+
- **Disparar**: `toast.dispatch()` retorna o ID do toast
|
|
718
|
+
- **Fechar específico**: `toast.dismiss(id)` fecha um toast pelo ID
|
|
719
|
+
- **Fechar todos**: `toast.clear()` limpa todos os toasts ativos
|
|
720
|
+
- Permite padrões avançados:
|
|
721
|
+
- Substituir toast de loading por sucesso
|
|
722
|
+
- Limpar notificações ao mudar de página
|
|
723
|
+
- Gerenciar toasts relacionados
|
|
724
|
+
|
|
725
|
+
### Integração com Message
|
|
726
|
+
|
|
727
|
+
O Toast reutiliza completamente o componente Message:
|
|
728
|
+
|
|
729
|
+
```tsx
|
|
730
|
+
// toast-component.tsx
|
|
731
|
+
export function ToastComponent({
|
|
732
|
+
variant = 'primary',
|
|
733
|
+
inline = false,
|
|
734
|
+
dismissible = true,
|
|
735
|
+
message,
|
|
736
|
+
action,
|
|
737
|
+
onDismiss
|
|
738
|
+
}: ToastComponentProps) {
|
|
739
|
+
return (
|
|
740
|
+
<MessageRoot inline={inline} variant={variant}>
|
|
741
|
+
<MessageIcon />
|
|
742
|
+
<MessageText>{message}</MessageText>
|
|
743
|
+
|
|
744
|
+
{action && (
|
|
745
|
+
<MessageAction variant={variant} onClick={action.onClick}>
|
|
746
|
+
{action.label}
|
|
747
|
+
</MessageAction>
|
|
748
|
+
)}
|
|
749
|
+
|
|
750
|
+
{dismissible && <MessageClose onClick={onDismiss} />}
|
|
751
|
+
</MessageRoot>
|
|
752
|
+
)
|
|
753
|
+
}
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
Isso garante:
|
|
757
|
+
- ✅ Consistência visual total
|
|
758
|
+
- ✅ Mesmos design tokens
|
|
759
|
+
- ✅ Manutenção centralizada
|
|
760
|
+
- ✅ Reutilização de código
|
|
761
|
+
|
|
762
|
+
### Acessibilidade
|
|
763
|
+
- ✅ **ARIA live regions**: Sonner cria automaticamente
|
|
764
|
+
- ✅ **Screen readers**: Anúncios automáticos de toasts
|
|
765
|
+
- ✅ **Navegação por teclado**: Tab, Enter, Escape
|
|
766
|
+
- ✅ **Focus management**: Foco adequado em ações
|
|
767
|
+
- ✅ **Swipe gestures**: Mobile acessível
|
|
768
|
+
- ✅ **Prefers-reduced-motion**: Respeita preferências do usuário
|
|
769
|
+
- ✅ **Labels apropriadas**: Botões com texto claro
|
|
770
|
+
|
|
771
|
+
### Performance
|
|
772
|
+
- ✅ **Renderização otimizada**: Sonner usa virtualização
|
|
773
|
+
- ✅ **Animações com transforms**: GPU-accelerated
|
|
774
|
+
- ✅ **Limpeza automática**: Remove toasts do DOM após fechar
|
|
775
|
+
- ✅ **Memoização**: Context e callbacks memoizados
|
|
776
|
+
- ✅ **Bundle size pequeno**: ~10KB (Sonner) + componentes existentes
|
|
777
|
+
|
|
778
|
+
### Documentação no Storybook
|
|
779
|
+
|
|
780
|
+
13 stories interativas cobrindo todos os casos:
|
|
781
|
+
1. **Playground**: Exemplo básico interativo
|
|
782
|
+
2. **Primary**: Toast de sucesso
|
|
783
|
+
3. **Destructive**: Toast de erro
|
|
784
|
+
4. **WithAction**: Toast com botão de ação
|
|
785
|
+
5. **NotDismissible**: Toast persistente
|
|
786
|
+
6. **CustomDuration**: Durações customizadas (1s, 10s)
|
|
787
|
+
7. **Positions**: Todas as 4 posições
|
|
788
|
+
8. **InlineMode**: Modo compacto
|
|
789
|
+
9. **WithCallbacks**: Demonstração de callbacks
|
|
790
|
+
10. **MultipleToasts**: Empilhamento de 3 toasts
|
|
791
|
+
11. **DismissSpecific**: Fechar toast específico por ID
|
|
792
|
+
12. **ClearAll**: Fechar todos os toasts ativos
|
|
793
|
+
13. **RealWorldExample**: Exemplos CRUD práticos
|
|
794
|
+
|
|
795
|
+
### Casos de Uso Suportados
|
|
796
|
+
1. ✅ Confirmações de ações (salvar, deletar, enviar)
|
|
797
|
+
2. ✅ Notificações de erro
|
|
798
|
+
3. ✅ Ações com desfazer (undo)
|
|
799
|
+
4. ✅ Status de upload/download
|
|
800
|
+
5. ✅ Notificações de sistema
|
|
801
|
+
6. ✅ Feedback de formulários
|
|
802
|
+
7. ✅ Operações assíncronas
|
|
803
|
+
8. ✅ Alertas temporários
|
|
804
|
+
9. ✅ Confirmações destrutivas
|
|
805
|
+
10. ✅ Múltiplas notificações simultâneas
|
|
806
|
+
|
|
807
|
+
### Arquivos Criados/Substituídos
|
|
808
|
+
|
|
809
|
+
**Toast (substituição completa)**:
|
|
810
|
+
- `packages/react/src/components/toast/index.ts`
|
|
811
|
+
- `packages/react/src/components/toast/toast.tsx`
|
|
812
|
+
- `packages/react/src/components/toast/toast-component.tsx`
|
|
813
|
+
- `packages/react/src/components/toast/toast.types.ts`
|
|
814
|
+
|
|
815
|
+
**Message**:
|
|
816
|
+
- `packages/react/src/components/message/message-action.tsx` (novo)
|
|
817
|
+
|
|
818
|
+
**Documentação**:
|
|
819
|
+
- `apps/docs/src/components/toast.stories.tsx` (reescrito completamente)
|
|
820
|
+
|
|
821
|
+
**Observação**: O componente Toast foi completamente reescrito. O código antigo baseado em Radix foi substituído por uma nova implementação baseada em Sonner.
|
|
822
|
+
|
|
823
|
+
### Dependências
|
|
824
|
+
|
|
825
|
+
Nova dependência adicionada:
|
|
826
|
+
|
|
827
|
+
```json
|
|
828
|
+
{
|
|
829
|
+
"dependencies": {
|
|
830
|
+
"sonner": "^1.x"
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
**Sonner**: Library toast moderna, leve e performática
|
|
836
|
+
- Bundle: ~10KB gzipped
|
|
837
|
+
- Zero dependencies além de React
|
|
838
|
+
- Amplamente adotada pela comunidade
|
|
839
|
+
- Manutenção ativa
|
|
840
|
+
|
|
841
|
+
### Comparação: Toast vs Message
|
|
842
|
+
|
|
843
|
+
| Aspecto | Toast (toast-v2) | Message |
|
|
844
|
+
| ---------------- | ------------------------- | ------------------------- |
|
|
845
|
+
| **Posição** | Fixo na tela (flutuante) | Inline no layout |
|
|
846
|
+
| **Duração** | Temporário (auto-dismiss) | Persistente |
|
|
847
|
+
| **API** | Imperativa (`toast()`) | Declarativa (JSX) |
|
|
848
|
+
| **Uso** | Notificações globais | Feedback contextual |
|
|
849
|
+
| **Empilhamento** | Automático (Sonner queue) | Manual |
|
|
850
|
+
| **Biblioteca** | Sonner + Message | Nativo |
|
|
851
|
+
| **Callbacks** | onAutoClose, onDismiss | onClose via ref |
|
|
852
|
+
| **Posições** | 4 cantos da tela | Onde for renderizado |
|
|
853
|
+
| **Ações** | Suporte via MessageAction | Suporte via MessageAction |
|
|
854
|
+
|
|
855
|
+
### Boas Práticas
|
|
856
|
+
1. **Use Toast para feedback não crítico**: Confirmações rápidas, updates de status
|
|
857
|
+
2. **Use Message para feedback crítico inline**: Erros de formulário, avisos importantes
|
|
858
|
+
3. **Limite a duração**: 3-5s para leitura confortável
|
|
859
|
+
4. **Evite spam de toasts**: Não dispare múltiplos toasts em sequência rápida
|
|
860
|
+
5. **Forneça ações quando relevante**: "Desfazer", "Ver detalhes"
|
|
861
|
+
6. **Posicione adequadamente**: top-right para notificações, bottom-right para status
|
|
862
|
+
7. **Teste em mobile**: Verifique swipe gestures
|
|
863
|
+
8. **Use callbacks para analytics**: Track abertura e fechamento
|
|
864
|
+
9. **Gerencie o ciclo de vida**: Use `dismiss()` para substituir loading por sucesso
|
|
865
|
+
10. **Limpe ao navegar**: Use `clear()` ao mudar de página/contexto
|
|
866
|
+
11. **Armazene IDs quando necessário**: Salve o retorno de `dispatch()` para controle posterior
|
|
867
|
+
|
|
868
|
+
### Migração do Toast Antigo (Radix)
|
|
869
|
+
|
|
870
|
+
O toast antigo baseado em Radix UI será removido. Migração:
|
|
871
|
+
|
|
872
|
+
#### Antes (Toast Radix - Depreciado)
|
|
873
|
+
|
|
874
|
+
```tsx
|
|
875
|
+
import {
|
|
876
|
+
ToastProvider,
|
|
877
|
+
ToastComponent,
|
|
878
|
+
ToastViewport
|
|
879
|
+
} from '@xjur-ui/react/toast'
|
|
880
|
+
|
|
881
|
+
;<ToastProvider>
|
|
882
|
+
<ToastComponent variant="success" description="Salvo!" />
|
|
883
|
+
<ToastViewport />
|
|
884
|
+
</ToastProvider>
|
|
885
|
+
```
|
|
886
|
+
|
|
887
|
+
#### Depois (Toast Sonner - Novo)
|
|
888
|
+
|
|
889
|
+
```tsx
|
|
890
|
+
import { ToastWrapper, toast } from '@xjur-ui/react/toast'
|
|
891
|
+
|
|
892
|
+
// No root da app
|
|
893
|
+
;<ToastWrapper />
|
|
894
|
+
|
|
895
|
+
// Onde precisar
|
|
896
|
+
toast.dispatch('Salvo!', { variant: 'primary' })
|
|
897
|
+
|
|
898
|
+
// Com controle de ciclo de vida
|
|
899
|
+
const toastId = toast.dispatch('Processando...')
|
|
900
|
+
toast.dismiss(toastId) // Fechar específico
|
|
901
|
+
toast.clear() // Fechar todos
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
### Compatibilidade
|
|
905
|
+
- ✅ React 19.1.1+
|
|
906
|
+
- ✅ TypeScript 5.x
|
|
907
|
+
- ✅ Tailwind CSS 4.x
|
|
908
|
+
- ✅ Navegadores modernos (Chrome, Firefox, Safari, Edge)
|
|
909
|
+
- ✅ iOS Safari 12+
|
|
910
|
+
- ✅ Chrome Mobile
|
|
911
|
+
- ✅ Suporte SSR/SSG
|
|
912
|
+
|
|
913
|
+
### Roadmap Futuro
|
|
914
|
+
|
|
915
|
+
Melhorias planejadas:
|
|
916
|
+
- Mais variantes (neutral, warning) mapeando para Message
|
|
917
|
+
- Suporte a rich content (imagens, progress bars)
|
|
918
|
+
- Posições customizadas (não apenas cantos)
|
|
919
|
+
- Temas customizados
|
|
920
|
+
- Agrupamento de toasts relacionados
|
|
921
|
+
- Priorização de toasts
|
|
922
|
+
- Rate limiting automático
|
|
923
|
+
|
|
924
|
+
***
|
|
925
|
+
|
|
926
|
+
## 4. Novo Componente SelectionBar (selection-bar-v2)
|
|
927
|
+
|
|
928
|
+
### Visão Geral
|
|
929
|
+
|
|
930
|
+
O **SelectionBar** é um componente composicional que exibe uma barra de ações flutuante na parte inferior da tela quando itens são selecionados. Renderizado via Portal React, é ideal para operações em lote em listas, tabelas e grids, oferecendo uma experiência moderna e intuitiva.
|
|
931
|
+
|
|
932
|
+
### Características Principais
|
|
933
|
+
|
|
934
|
+
#### Arquitetura Composicional Moderna
|
|
935
|
+
- **SelectionBarRoot**: Container principal com gerenciamento de estado e portal
|
|
936
|
+
- **SelectionBarTitleRoot**: Container para título e botão de fechar
|
|
937
|
+
- **SelectionBarTitle**: Texto do título (wrapper do Typography)
|
|
938
|
+
- **SelectionBarClose**: Botão para fechar a barra
|
|
939
|
+
- **SelectionBarActionGroup**: Container de ações com suporte a overflow
|
|
940
|
+
- **SelectionBarAction**: Botão de ação (wrapper do Button)
|
|
941
|
+
|
|
942
|
+
#### Portal e Posicionamento Fixo
|
|
943
|
+
- Renderizado via `createPortal` React
|
|
944
|
+
- Alvo: elemento `#selection-bar-root` ou `document.body`
|
|
945
|
+
- Posição: `fixed bottom-[60px]` (acima de barras de navegação)
|
|
946
|
+
- Centralizado horizontalmente: `left-1/2 -translate-x-1/2`
|
|
947
|
+
- Z-index: 1000 (acima de outros elementos)
|
|
948
|
+
|
|
949
|
+
#### Animações e Transições
|
|
950
|
+
- **Entrada**: `slide-in-from-top` (200ms)
|
|
951
|
+
- **Saída**: `slide-out-to-bottom` (200ms)
|
|
952
|
+
- Auto-remoção do DOM após fechamento
|
|
953
|
+
- Estados visuais: `data-state="open|closed"`
|
|
954
|
+
|
|
955
|
+
### API do Componente
|
|
956
|
+
|
|
957
|
+
#### SelectionBarRoot
|
|
958
|
+
|
|
959
|
+
```tsx
|
|
960
|
+
interface SelectionBarRootProps {
|
|
961
|
+
open?: boolean // Padrão: true
|
|
962
|
+
onOpenChange?: (open: boolean) => void
|
|
963
|
+
className?: string
|
|
964
|
+
children: React.ReactNode
|
|
965
|
+
// + todas as props de HTMLDivElement
|
|
966
|
+
}
|
|
967
|
+
```
|
|
968
|
+
|
|
969
|
+
**Context API**:
|
|
970
|
+
|
|
971
|
+
```tsx
|
|
972
|
+
interface SelectionBarContextValue {
|
|
973
|
+
open: boolean
|
|
974
|
+
onClose: () => void
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// Hook
|
|
978
|
+
const { open, onClose } = useSelectionBarContext()
|
|
979
|
+
```
|
|
980
|
+
|
|
981
|
+
#### Outros Componentes
|
|
982
|
+
|
|
983
|
+
| Componente | Base | Props Especiais |
|
|
984
|
+
| ----------------------- | ---------- | ----------------------------- |
|
|
985
|
+
| SelectionBarTitleRoot | div | Container flexbox |
|
|
986
|
+
| SelectionBarTitle | Typography | weight="medium", cor neutra |
|
|
987
|
+
| SelectionBarClose | Button | size="icon_md", ícone close |
|
|
988
|
+
| SelectionBarActionGroup | div | Flexbox com overflow-x-hidden |
|
|
989
|
+
| SelectionBarAction | Button | className="shrink-0" |
|
|
990
|
+
|
|
991
|
+
### Exemplos de Uso
|
|
992
|
+
|
|
993
|
+
#### Seleção Básica em Tabela
|
|
994
|
+
|
|
995
|
+
```tsx
|
|
996
|
+
import {
|
|
997
|
+
SelectionBarRoot,
|
|
998
|
+
SelectionBarTitleRoot,
|
|
999
|
+
SelectionBarTitle,
|
|
1000
|
+
SelectionBarClose,
|
|
1001
|
+
SelectionBarActionGroup,
|
|
1002
|
+
SelectionBarAction
|
|
1003
|
+
} from '@xjur-ui/react/selection-bar'
|
|
1004
|
+
import { Icon } from '@xjur-ui/react/icon'
|
|
1005
|
+
|
|
1006
|
+
const [selectedIds, setSelectedIds] = useState<string[]>([])
|
|
1007
|
+
|
|
1008
|
+
{
|
|
1009
|
+
selectedIds.length > 0 && (
|
|
1010
|
+
<SelectionBarRoot>
|
|
1011
|
+
<SelectionBarTitleRoot>
|
|
1012
|
+
<SelectionBarClose />
|
|
1013
|
+
<SelectionBarTitle>
|
|
1014
|
+
{selectedIds.length} selecionados
|
|
1015
|
+
</SelectionBarTitle>
|
|
1016
|
+
</SelectionBarTitleRoot>
|
|
1017
|
+
|
|
1018
|
+
<SelectionBarActionGroup>
|
|
1019
|
+
<SelectionBarAction onClick={handleExport}>
|
|
1020
|
+
<Icon name="download" />
|
|
1021
|
+
Exportar
|
|
1022
|
+
</SelectionBarAction>
|
|
1023
|
+
<SelectionBarAction onClick={handleArchive}>
|
|
1024
|
+
Arquivar
|
|
1025
|
+
</SelectionBarAction>
|
|
1026
|
+
<SelectionBarAction variant="destructive" onClick={handleDelete}>
|
|
1027
|
+
<Icon name="delete" />
|
|
1028
|
+
Excluir
|
|
1029
|
+
</SelectionBarAction>
|
|
1030
|
+
</SelectionBarActionGroup>
|
|
1031
|
+
</SelectionBarRoot>
|
|
1032
|
+
)
|
|
1033
|
+
}
|
|
1034
|
+
```
|
|
1035
|
+
|
|
1036
|
+
#### Com Controle de Estado
|
|
1037
|
+
|
|
1038
|
+
```tsx
|
|
1039
|
+
const [open, setOpen] = useState(false)
|
|
1040
|
+
const [selectedItems, setSelectedItems] = useState([])
|
|
1041
|
+
|
|
1042
|
+
<SelectionBarRoot
|
|
1043
|
+
open={open}
|
|
1044
|
+
onOpenChange={(newOpen) => {
|
|
1045
|
+
setOpen(newOpen)
|
|
1046
|
+
if (!newOpen) {
|
|
1047
|
+
// Limpar seleções ao fechar
|
|
1048
|
+
setSelectedItems([])
|
|
1049
|
+
}
|
|
1050
|
+
}}
|
|
1051
|
+
>
|
|
1052
|
+
<SelectionBarTitleRoot>
|
|
1053
|
+
<SelectionBarClose />
|
|
1054
|
+
<SelectionBarTitle>
|
|
1055
|
+
{selectedItems.length} selecionados
|
|
1056
|
+
</SelectionBarTitle>
|
|
1057
|
+
</SelectionBarTitleRoot>
|
|
1058
|
+
|
|
1059
|
+
<SelectionBarActionGroup>
|
|
1060
|
+
<SelectionBarAction>Exportar PDF</SelectionBarAction>
|
|
1061
|
+
<SelectionBarAction>Compartilhar</SelectionBarAction>
|
|
1062
|
+
<SelectionBarAction variant="destructive">
|
|
1063
|
+
<Icon name="delete" />
|
|
1064
|
+
</SelectionBarAction>
|
|
1065
|
+
</SelectionBarActionGroup>
|
|
1066
|
+
</SelectionBarRoot>
|
|
1067
|
+
```
|
|
1068
|
+
|
|
1069
|
+
#### Operações em Lote
|
|
1070
|
+
|
|
1071
|
+
```tsx
|
|
1072
|
+
<SelectionBarRoot>
|
|
1073
|
+
<SelectionBarTitleRoot>
|
|
1074
|
+
<SelectionBarClose />
|
|
1075
|
+
<SelectionBarTitle>5 documentos selecionados</SelectionBarTitle>
|
|
1076
|
+
</SelectionBarTitleRoot>
|
|
1077
|
+
|
|
1078
|
+
<SelectionBarActionGroup>
|
|
1079
|
+
<SelectionBarAction>Exportar PDF</SelectionBarAction>
|
|
1080
|
+
<SelectionBarAction>Exportar Excel</SelectionBarAction>
|
|
1081
|
+
<SelectionBarAction>Compartilhar</SelectionBarAction>
|
|
1082
|
+
<SelectionBarAction>Mover para</SelectionBarAction>
|
|
1083
|
+
<SelectionBarAction>Arquivar</SelectionBarAction>
|
|
1084
|
+
|
|
1085
|
+
<SelectionBarAction size="icon_md" variant="destructive">
|
|
1086
|
+
<Icon name="delete" />
|
|
1087
|
+
</SelectionBarAction>
|
|
1088
|
+
</SelectionBarActionGroup>
|
|
1089
|
+
</SelectionBarRoot>
|
|
1090
|
+
```
|
|
1091
|
+
|
|
1092
|
+
### Funcionalidades
|
|
1093
|
+
|
|
1094
|
+
#### 1. Portal Rendering
|
|
1095
|
+
- Renderiza fora da hierarquia DOM do componente pai
|
|
1096
|
+
- Evita problemas de z-index e overflow
|
|
1097
|
+
- Permite posicionamento fixo global
|
|
1098
|
+
- Fallback automático para `document.body`
|
|
1099
|
+
|
|
1100
|
+
#### 2. Gerenciamento de Estado
|
|
1101
|
+
- Estado controlado via props `open` e `onOpenChange`
|
|
1102
|
+
- Estado interno quando não controlado
|
|
1103
|
+
- Callback ao fechar para sincronizar com estado externo
|
|
1104
|
+
- Auto-remoção do DOM após animação de saída
|
|
1105
|
+
|
|
1106
|
+
#### 3. Overflow Horizontal
|
|
1107
|
+
- Container de ações com `overflow-x-hidden`
|
|
1108
|
+
- Scroll horizontal automático em mobile
|
|
1109
|
+
- Ações com `shrink-0` para não comprimir
|
|
1110
|
+
- Suporta muitas ações sem quebrar layout
|
|
1111
|
+
|
|
1112
|
+
#### 4. Context API
|
|
1113
|
+
- Hook `useSelectionBarContext()` para componentes customizados
|
|
1114
|
+
- Acesso a `open` e `onClose` em qualquer nível
|
|
1115
|
+
- Permite criar componentes especializados
|
|
1116
|
+
|
|
1117
|
+
#### 5. Integração com Componentes Base
|
|
1118
|
+
- Usa Button para ações (todas as variantes disponíveis)
|
|
1119
|
+
- Usa Typography para título (todas as props disponíveis)
|
|
1120
|
+
- Usa Icon para ícones
|
|
1121
|
+
- Mantém consistência visual com design system
|
|
1122
|
+
|
|
1123
|
+
### Acessibilidade
|
|
1124
|
+
- ✅ **Navegação por teclado**: Todos os botões são focáveis
|
|
1125
|
+
- ✅ **Ícones descritivos**: Ações com ícones e/ou texto claro
|
|
1126
|
+
- ✅ **Botão de fechar**: Sempre visível e acessível
|
|
1127
|
+
- ✅ **Contraste adequado**: Fundo primário com texto legível
|
|
1128
|
+
- ✅ **Animações**: Respeitam `prefers-reduced-motion`
|
|
1129
|
+
- ✅ **Focus management**: Foco adequado em botões
|
|
1130
|
+
|
|
1131
|
+
### Performance
|
|
1132
|
+
- ✅ **Portal rendering**: Otimizado pelo React
|
|
1133
|
+
- ✅ **Animações CSS**: GPU-accelerated com transforms
|
|
1134
|
+
- ✅ **Auto-remoção**: Limpa DOM após fechamento
|
|
1135
|
+
- ✅ **Context memoizado**: Evita re-renders desnecessários
|
|
1136
|
+
- ✅ **Callbacks estáveis**: `useCallback` para `handleClose`
|
|
1137
|
+
- ✅ **Composição modular**: Componentes pequenos e focados
|
|
1138
|
+
|
|
1139
|
+
### Documentação no Storybook
|
|
1140
|
+
|
|
1141
|
+
8 stories interativas cobrindo todos os casos:
|
|
1142
|
+
1. **Playground**: Exemplo básico com 3 itens selecionados
|
|
1143
|
+
2. **WithManyActions**: Múltiplas ações com overflow
|
|
1144
|
+
3. **WithIconsOnly**: Apenas ícones (compacto)
|
|
1145
|
+
4. **WithMixedActions**: Mistura de ícones e texto
|
|
1146
|
+
5. **ControlledState**: Controle total do estado
|
|
1147
|
+
6. **TableIntegration**: Integração completa com tabela
|
|
1148
|
+
7. **MinimalActions**: Uma única ação
|
|
1149
|
+
8. **WithVariants**: Diferentes variantes de botão
|
|
1150
|
+
|
|
1151
|
+
### Casos de Uso Suportados
|
|
1152
|
+
1. ✅ Seleção em tabelas
|
|
1153
|
+
2. ✅ Operações em lote em listas
|
|
1154
|
+
3. ✅ Seleção múltipla em grids
|
|
1155
|
+
4. ✅ Ações de arquivamento em massa
|
|
1156
|
+
5. ✅ Exportação de múltiplos itens
|
|
1157
|
+
6. ✅ Compartilhamento em lote
|
|
1158
|
+
7. ✅ Exclusão de múltiplos registros
|
|
1159
|
+
8. ✅ Movimentação de arquivos
|
|
1160
|
+
9. ✅ Marcação de leitura/não lido
|
|
1161
|
+
10. ✅ Aprovação/rejeição em lote
|
|
1162
|
+
|
|
1163
|
+
### Arquivos Criados/Substituídos
|
|
1164
|
+
|
|
1165
|
+
**SelectionBar (substituição completa)**:
|
|
1166
|
+
- `packages/react/src/components/selection-bar/index.ts`
|
|
1167
|
+
- `packages/react/src/components/selection-bar/selection-bar-root.tsx`
|
|
1168
|
+
- `packages/react/src/components/selection-bar/selection-bar-title.tsx`
|
|
1169
|
+
- `packages/react/src/components/selection-bar/selection-bar-close.tsx`
|
|
1170
|
+
- `packages/react/src/components/selection-bar/selection-bar-action.tsx`
|
|
1171
|
+
- `packages/react/src/components/selection-bar/selection-bar-action-group.tsx`
|
|
1172
|
+
|
|
1173
|
+
**Documentação**:
|
|
1174
|
+
- `apps/docs/src/components/selection-bar.stories.tsx` (reescrito completamente)
|
|
1175
|
+
|
|
1176
|
+
**Observação**: O componente SelectionBar foi completamente reescrito. O código antigo foi substituído por uma nova implementação composicional com portal, estado gerenciado e animações.
|
|
1177
|
+
|
|
1178
|
+
### Comparação: Versão Antiga vs Nova
|
|
1179
|
+
|
|
1180
|
+
| Aspecto | Antiga | Nova (v2) |
|
|
1181
|
+
| ---------------- | ----------------------- | ---------------------------- |
|
|
1182
|
+
| **Arquitetura** | Monolítica | Composicional |
|
|
1183
|
+
| **Renderização** | Normal no DOM | Portal (flutuante) |
|
|
1184
|
+
| **Estado** | Não gerenciado | Controlado/não-controlado |
|
|
1185
|
+
| **Animações** | Básicas ou inexistentes | Slide-in/out suaves |
|
|
1186
|
+
| **Overflow** | Sem tratamento | Scroll horizontal automático |
|
|
1187
|
+
| **Fechamento** | Manual | Auto-remoção do DOM |
|
|
1188
|
+
| **Context** | Sem context | Context API completa |
|
|
1189
|
+
| **Callbacks** | Limitados | `onOpenChange` completo |
|
|
1190
|
+
| **Composição** | 1-2 componentes | 6 componentes especializados |
|
|
1191
|
+
|
|
1192
|
+
### Boas Práticas
|
|
1193
|
+
1. **Use para operações em lote**: Perfeito para ações em múltiplos itens
|
|
1194
|
+
2. **Limite o número de ações**: 3-5 ações principais
|
|
1195
|
+
3. **Priorize ações importantes**: Mais usadas primeiro
|
|
1196
|
+
4. **Indique quantidade selecionada**: Sempre mostre o número
|
|
1197
|
+
5. **Forneça feedback visual**: Ícones e cores apropriadas
|
|
1198
|
+
6. **Feche ao limpar seleção**: Use `onOpenChange` para sincronizar
|
|
1199
|
+
7. **Teste em mobile**: Verifique overflow horizontal
|
|
1200
|
+
8. **Confirme ações destrutivas**: Use diálogos para deletar
|
|
1201
|
+
9. **Crie portal root**: Adicione `<div id="selection-bar-root" />` no app
|
|
1202
|
+
10. **Sincronize estado**: Mantenha seleções e barra em sync
|
|
1203
|
+
|
|
1204
|
+
### Migração da Versão Antiga
|
|
1205
|
+
|
|
1206
|
+
#### Antes (SelectionBar Antigo - Depreciado)
|
|
1207
|
+
|
|
1208
|
+
```tsx
|
|
1209
|
+
import { SelectionBar } from '@xjur-ui/react/selection-bar'
|
|
1210
|
+
|
|
1211
|
+
;<SelectionBar
|
|
1212
|
+
title="3 selecionados"
|
|
1213
|
+
actions={[
|
|
1214
|
+
{ label: 'Exportar', onClick: handleExport },
|
|
1215
|
+
{ label: 'Excluir', onClick: handleDelete }
|
|
1216
|
+
]}
|
|
1217
|
+
/>
|
|
1218
|
+
```
|
|
1219
|
+
|
|
1220
|
+
#### Depois (SelectionBar Novo - Composicional)
|
|
1221
|
+
|
|
1222
|
+
```tsx
|
|
1223
|
+
import {
|
|
1224
|
+
SelectionBarRoot,
|
|
1225
|
+
SelectionBarTitleRoot,
|
|
1226
|
+
SelectionBarTitle,
|
|
1227
|
+
SelectionBarClose,
|
|
1228
|
+
SelectionBarActionGroup,
|
|
1229
|
+
SelectionBarAction
|
|
1230
|
+
} from '@xjur-ui/react/selection-bar'
|
|
1231
|
+
|
|
1232
|
+
;<SelectionBarRoot>
|
|
1233
|
+
<SelectionBarTitleRoot>
|
|
1234
|
+
<SelectionBarClose />
|
|
1235
|
+
<SelectionBarTitle>3 selecionados</SelectionBarTitle>
|
|
1236
|
+
</SelectionBarTitleRoot>
|
|
1237
|
+
|
|
1238
|
+
<SelectionBarActionGroup>
|
|
1239
|
+
<SelectionBarAction onClick={handleExport}>Exportar</SelectionBarAction>
|
|
1240
|
+
<SelectionBarAction variant="destructive" onClick={handleDelete}>
|
|
1241
|
+
Excluir
|
|
1242
|
+
</SelectionBarAction>
|
|
1243
|
+
</SelectionBarActionGroup>
|
|
1244
|
+
</SelectionBarRoot>
|
|
1245
|
+
```
|
|
1246
|
+
|
|
1247
|
+
### Compatibilidade
|
|
1248
|
+
- ✅ React 19.1.1+
|
|
1249
|
+
- ✅ TypeScript 5.x
|
|
1250
|
+
- ✅ Tailwind CSS 4.x
|
|
1251
|
+
- ✅ Navegadores modernos (Chrome, Firefox, Safari, Edge)
|
|
1252
|
+
- ✅ iOS Safari 12+
|
|
1253
|
+
- ✅ Chrome Mobile
|
|
1254
|
+
- ✅ Suporte SSR/SSG (com hydration de portals)
|
|
1255
|
+
|
|
1256
|
+
### Roadmap Futuro
|
|
1257
|
+
|
|
1258
|
+
Melhorias planejadas:
|
|
1259
|
+
- Indicador visual de scroll (mais ações disponíveis)
|
|
1260
|
+
- Posicionamento customizável (top/bottom)
|
|
1261
|
+
- Temas customizados
|
|
1262
|
+
- Suporte a ações agrupadas (dropdowns)
|
|
1263
|
+
- Contador animado ao mudar seleção
|
|
1264
|
+
- Undo/Redo de ações
|
|
1265
|
+
- Modo compacto para mobile
|
|
1266
|
+
|
|
1267
|
+
***
|
|
1268
|
+
|
|
1269
|
+
## Resumo da Release
|
|
1270
|
+
|
|
1271
|
+
### Breaking Changes
|
|
1272
|
+
- ❌ **OTPInput** removido e substituído por **PinInput**
|
|
1273
|
+
- ❌ **Toast (Radix)** removido e substituído por **Toast (Sonner)**
|
|
1274
|
+
- ❌ **SelectionBar** reescrito completamente com arquitetura composicional
|
|
1275
|
+
|
|
1276
|
+
### Novas Funcionalidades
|
|
1277
|
+
- ✅ **PinInput**: Componente moderno com auto-submit, callbacks e validação
|
|
1278
|
+
- ✅ **Message**: Sistema de mensagens contextuais com 4 variantes
|
|
1279
|
+
- ✅ **Toast**: Sistema de notificações baseado em Sonner com controle de ciclo de vida
|
|
1280
|
+
- ✅ **SelectionBar**: Barra de ações flutuante com portal e estado gerenciado
|
|
1281
|
+
- ✅ **MessageAction**: Componente de ação para Message e Toast
|
|
1282
|
+
|
|
1283
|
+
### Melhorias
|
|
1284
|
+
- 📖 Documentação completa no Storybook (40 stories no total)
|
|
1285
|
+
- 🎨 Design consistente com tokens do sistema
|
|
1286
|
+
- ♿ Acessibilidade aprimorada em todos os componentes
|
|
1287
|
+
- ⚡ Performance otimizada com memoização e portals
|
|
1288
|
+
- 📦 TypeScript completo com tipos exportados
|
|
1289
|
+
- 🔄 Reutilização de componentes (Toast usa Message internamente)
|
|
1290
|
+
- 📱 Mobile-friendly (swipe no Toast, overflow no SelectionBar)
|
|
1291
|
+
- 🎯 Controle total de ciclo de vida (toasts e selection bar)
|
|
1292
|
+
- 🎭 Animações suaves em todos os componentes
|
|
1293
|
+
|
|
1294
|
+
### Arquivos Modificados/Criados
|
|
1295
|
+
- 5 arquivos do componente PinInput
|
|
1296
|
+
- 6 arquivos do componente Message (incluindo MessageAction)
|
|
1297
|
+
- 4 arquivos do componente Toast
|
|
1298
|
+
- 6 arquivos do componente SelectionBar
|
|
1299
|
+
- 3 arquivos de stories no Storybook
|
|
1300
|
+
- 1 README do PinInput
|
|
1301
|
+
|
|
1302
|
+
### Arquivos Removidos
|
|
1303
|
+
- 1 arquivo do OTPInput antigo
|
|
1304
|
+
- 1 arquivo do Toast antigo (Radix)
|
|
1305
|
+
- 1 arquivo do SelectionBar antigo
|
|
1306
|
+
- 1 arquivo de stories do OTPInput
|
|
1307
|
+
|
|
1308
|
+
### Dependências Adicionadas
|
|
1309
|
+
- **sonner**: ^2.0.7 (~10KB) - Library moderna para toasts
|
|
1310
|
+
|
|
1311
|
+
### Estatísticas da Release
|
|
1312
|
+
- **Componentes novos/reescritos**: 4 (PinInput, Message, Toast, SelectionBar)
|
|
1313
|
+
- **Componentes removidos**: 3 (OTPInput, Toast Radix, SelectionBar antigo)
|
|
1314
|
+
- **Stories no Storybook**: 40 (9 PinInput + 10 Message + 13 Toast + 8 SelectionBar)
|
|
1315
|
+
- **Breaking changes**: 3 (OTPInput → PinInput, Toast Radix → Toast Sonner, SelectionBar reescrito)
|
|
1316
|
+
- **Linhas de código**: ~1800 novas linhas
|
|
1317
|
+
- **Cobertura de casos de uso**: 37+ casos diferentes
|
|
1318
|
+
- **Funcionalidades do Toast**: 3 métodos (dispatch, dismiss, clear)
|
|
1319
|
+
- **Componentes no SelectionBar**: 6 componentes especializados
|
|
1320
|
+
|
|
1321
|
+
### Próximos Passos Recomendados
|
|
1322
|
+
1. **Migrar de OTPInput para PinInput**
|
|
1323
|
+
- Atualizar imports: `@xjur-ui/react/pin-input`
|
|
1324
|
+
- Renomear componentes: `OTPInputRoot` → `PinInput`, etc.
|
|
1325
|
+
- Ajustar props: `initial` → `defaultValue`
|
|
1326
|
+
- Adicionar callbacks opcionais: `onComplete`, `onChangeValue`
|
|
1327
|
+
- Aproveitar auto-submit para melhor UX
|
|
1328
|
+
2. **Migrar de Toast Radix para Toast Sonner**
|
|
1329
|
+
- Atualizar imports: `@xjur-ui/react/toast`
|
|
1330
|
+
- Substituir `<ToastProvider>` por `<ToastWrapper />`
|
|
1331
|
+
- Converter de API declarativa para imperativa
|
|
1332
|
+
- Usar `toast.dispatch()` ao invés de componentes JSX
|
|
1333
|
+
- Aproveitar `toast.dismiss()` e `toast.clear()` para controle avançado
|
|
1334
|
+
- Armazenar IDs retornados por `dispatch()` quando necessário
|
|
1335
|
+
- Remover lógica de gerenciamento manual de toasts
|
|
1336
|
+
3. **Integrar Message no sistema**
|
|
1337
|
+
- Substituir alerts antigos por Message
|
|
1338
|
+
- Padronizar feedback de ações em formulários
|
|
1339
|
+
- Implementar em fluxos críticos
|
|
1340
|
+
- Usar MessageAction para ações contextuais
|
|
1341
|
+
4. **Migrar SelectionBar**
|
|
1342
|
+
- Atualizar imports para componentes composicionais
|
|
1343
|
+
- Converter props de array para JSX composicional
|
|
1344
|
+
- Adicionar `<div id="selection-bar-root" />` no root da aplicação (opcional)
|
|
1345
|
+
- Usar `onOpenChange` para sincronizar estado com seleções
|
|
1346
|
+
- Aproveitar overflow horizontal para muitas ações
|
|
1347
|
+
5. **Integrar Toast para notificações**
|
|
1348
|
+
- Adicionar `<ToastWrapper />` no root da aplicação
|
|
1349
|
+
- Substituir notificações antigas por `toast.dispatch()`
|
|
1350
|
+
- Usar `toast.dismiss()` para substituir loading por sucesso
|
|
1351
|
+
- Usar `toast.clear()` ao mudar de página/contexto
|
|
1352
|
+
- Implementar callbacks para analytics
|
|
1353
|
+
- Configurar posicionamento adequado
|
|
1354
|
+
- Armazenar IDs para controle de toasts relacionados
|
|
1355
|
+
6. **Integrar SelectionBar em tabelas e listas**
|
|
1356
|
+
- Adicionar ao componente de seleção múltipla
|
|
1357
|
+
- Sincronizar estado de seleções com `onOpenChange`
|
|
1358
|
+
- Implementar ações em lote (exportar, deletar, arquivar)
|
|
1359
|
+
- Configurar portal root para melhor controle
|
|
1360
|
+
- Testar overflow em mobile
|
|
1361
|
+
7. **Testar em produção**
|
|
1362
|
+
- Validar acessibilidade com leitores de tela
|
|
1363
|
+
- Verificar performance em dispositivos low-end
|
|
1364
|
+
- Testar gestures em mobile (swipe to dismiss, overflow horizontal)
|
|
1365
|
+
- Validar portals em SSR/SSG
|
|
1366
|
+
- Coletar feedback de usuários
|
|
1367
|
+
- Monitorar métricas de uso
|
|
1368
|
+
|
|
1369
|
+
### Impacto Estimado
|
|
1370
|
+
|
|
1371
|
+
**Positivo:**
|
|
1372
|
+
- ✅ Melhor DX com API simplificada
|
|
1373
|
+
- ✅ Componentes mais modernos e performáticos
|
|
1374
|
+
- ✅ Documentação muito mais completa
|
|
1375
|
+
- ✅ Consistência visual total entre Message e Toast
|
|
1376
|
+
- ✅ Redução de bundle size (Sonner vs Radix Toast)
|
|
1377
|
+
- ✅ Mais casos de uso suportados
|
|
1378
|
+
|
|
1379
|
+
**Atenção Necessária:**
|
|
1380
|
+
- ⚠️ Migração necessária em todos os usos de OTPInput
|
|
1381
|
+
- ⚠️ Migração necessária em todos os usos de Toast
|
|
1382
|
+
- ⚠️ Migração necessária em todos os usos de SelectionBar
|
|
1383
|
+
- ⚠️ Testes extensivos recomendados (especialmente portals em SSR)
|
|
1384
|
+
- ⚠️ Atualização de documentação interna da equipe
|
|
1385
|
+
- ⚠️ Validação de z-index em diferentes contextos
|
|
1386
|
+
|
|
1387
|
+
### Guias de Migração Rápida
|
|
1388
|
+
|
|
1389
|
+
#### OTPInput → PinInput (1 minuto)
|
|
1390
|
+
|
|
1391
|
+
```diff
|
|
1392
|
+
- import { OTPInputRoot, OTPInputInput, ... } from '@xjur-ui/react/otp-input'
|
|
1393
|
+
+ import { PinInput, PinInputControl, ... } from '@xjur-ui/react/pin-input'
|
|
1394
|
+
|
|
1395
|
+
- <OTPInputRoot initial="1234" length={4}>
|
|
1396
|
+
+ <PinInput defaultValue="1234" length={4} name="code">
|
|
1397
|
+
<PinInputTrigger>
|
|
1398
|
+
- <OTPInputInput index={0} />
|
|
1399
|
+
+ <PinInputControl index={0} />
|
|
1400
|
+
</PinInputTrigger>
|
|
1401
|
+
- <OTPInputHiddenInput name="code" />
|
|
1402
|
+
</PinInput>
|
|
1403
|
+
```
|
|
1404
|
+
|
|
1405
|
+
#### Toast Radix → Toast Sonner (30 segundos)
|
|
1406
|
+
|
|
1407
|
+
```diff
|
|
1408
|
+
- import { ToastProvider, ToastComponent } from '@xjur-ui/react/toast'
|
|
1409
|
+
+ import { ToastWrapper, toast } from '@xjur-ui/react/toast'
|
|
1410
|
+
|
|
1411
|
+
// No root
|
|
1412
|
+
- <ToastProvider>
|
|
1413
|
+
- <App />
|
|
1414
|
+
- </ToastProvider>
|
|
1415
|
+
+ <ToastWrapper />
|
|
1416
|
+
+ <App />
|
|
1417
|
+
|
|
1418
|
+
// Ao disparar
|
|
1419
|
+
- <ToastComponent variant="success" description="Salvo!" />
|
|
1420
|
+
+ toast.dispatch('Salvo!', { variant: 'primary' })
|
|
1421
|
+
|
|
1422
|
+
// Controle avançado
|
|
1423
|
+
+ const id = toast.dispatch('Processando...')
|
|
1424
|
+
+ toast.dismiss(id) // Fechar específico
|
|
1425
|
+
+ toast.clear() // Fechar todos
|
|
1426
|
+
```
|
|
1427
|
+
|
|
1428
|
+
#### SelectionBar Antigo → SelectionBar Composicional (2 minutos)
|
|
1429
|
+
|
|
1430
|
+
```diff
|
|
1431
|
+
- import { SelectionBar } from '@xjur-ui/react/selection-bar'
|
|
1432
|
+
+ import {
|
|
1433
|
+
+ SelectionBarRoot,
|
|
1434
|
+
+ SelectionBarTitleRoot,
|
|
1435
|
+
+ SelectionBarTitle,
|
|
1436
|
+
+ SelectionBarClose,
|
|
1437
|
+
+ SelectionBarActionGroup,
|
|
1438
|
+
+ SelectionBarAction
|
|
1439
|
+
+ } from '@xjur-ui/react/selection-bar'
|
|
1440
|
+
|
|
1441
|
+
- <SelectionBar
|
|
1442
|
+
- title="3 selecionados"
|
|
1443
|
+
- actions={[
|
|
1444
|
+
- { label: 'Exportar', onClick: handleExport },
|
|
1445
|
+
- { label: 'Excluir', onClick: handleDelete }
|
|
1446
|
+
- ]}
|
|
1447
|
+
- />
|
|
1448
|
+
+ <SelectionBarRoot>
|
|
1449
|
+
+ <SelectionBarTitleRoot>
|
|
1450
|
+
+ <SelectionBarClose />
|
|
1451
|
+
+ <SelectionBarTitle>3 selecionados</SelectionBarTitle>
|
|
1452
|
+
+ </SelectionBarTitleRoot>
|
|
1453
|
+
+
|
|
1454
|
+
+ <SelectionBarActionGroup>
|
|
1455
|
+
+ <SelectionBarAction onClick={handleExport}>
|
|
1456
|
+
+ Exportar
|
|
1457
|
+
+ </SelectionBarAction>
|
|
1458
|
+
+ <SelectionBarAction
|
|
1459
|
+
+ variant="destructive"
|
|
1460
|
+
+ onClick={handleDelete}
|
|
1461
|
+
+ >
|
|
1462
|
+
+ Excluir
|
|
1463
|
+
+ </SelectionBarAction>
|
|
1464
|
+
+ </SelectionBarActionGroup>
|
|
1465
|
+
+ </SelectionBarRoot>
|
|
1466
|
+
|
|
1467
|
+
// Com controle de estado
|
|
1468
|
+
+ <SelectionBarRoot
|
|
1469
|
+
+ open={open}
|
|
1470
|
+
+ onOpenChange={(newOpen) => {
|
|
1471
|
+
+ setOpen(newOpen)
|
|
1472
|
+
+ if (!newOpen) setSelectedItems([])
|
|
1473
|
+
+ }}
|
|
1474
|
+
+ >
|
|
1475
|
+
```
|
|
8
1476
|
|
|
9
1477
|
## 3.1.1
|
|
10
1478
|
|
|
11
1479
|
### Patch Changes
|
|
12
1480
|
|
|
13
1481
|
- 6ff314b: fix progress text view
|
|
1482
|
+
- ffe201e: Add error state and disabled state in otp input
|
|
14
1483
|
|
|
15
1484
|
## 3.1.0
|
|
16
1485
|
|