@wellingtonhlc/shared-ui 0.26.13 → 0.26.14
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 +405 -405
- package/dist/components/Banner.d.ts +13 -0
- package/dist/components/Banner.d.ts.map +1 -0
- package/dist/components/Banner.js +29 -0
- package/dist/components/FieldControl.js +20 -20
- package/dist/components/Loading.d.ts +17 -0
- package/dist/components/Loading.d.ts.map +1 -0
- package/dist/components/Loading.js +15 -0
- package/dist/components/LoadingOverlay.d.ts +11 -0
- package/dist/components/LoadingOverlay.d.ts.map +1 -0
- package/dist/components/LoadingOverlay.js +8 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/styles.css +135 -29
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,405 +1,405 @@
|
|
|
1
|
-
# Wellington Shared UI
|
|
2
|
-
|
|
3
|
-
Conjunto de componentes visuais padronizados, criado por Wellington Henrique para aplicacoes React.
|
|
4
|
-
|
|
5
|
-
O objetivo deste pacote e concentrar componentes base, tokens, presets e helpers de UI reutilizaveis, sem regras de negocio de dominio.
|
|
6
|
-
|
|
7
|
-
## Pacote
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
npm install @wellingtonhlc/shared-ui
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
Uso basico:
|
|
14
|
-
|
|
15
|
-
```tsx
|
|
16
|
-
import { Button, Card, Page, Table } from '@wellingtonhlc/shared-ui';
|
|
17
|
-
|
|
18
|
-
export function ExamplePage() {
|
|
19
|
-
return (
|
|
20
|
-
<Page.Root>
|
|
21
|
-
<Card.Root>
|
|
22
|
-
<Card.Header>
|
|
23
|
-
<Card.Title>Registros</Card.Title>
|
|
24
|
-
<Card.Description>Consulta de dados</Card.Description>
|
|
25
|
-
</Card.Header>
|
|
26
|
-
<Card.Body>
|
|
27
|
-
<Button>Salvar</Button>
|
|
28
|
-
</Card.Body>
|
|
29
|
-
</Card.Root>
|
|
30
|
-
</Page.Root>
|
|
31
|
-
);
|
|
32
|
-
}
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## Principios
|
|
36
|
-
|
|
37
|
-
Este pacote foi desenhado para oferecer infraestrutura visual generica:
|
|
38
|
-
|
|
39
|
-
- componentes base de interface;
|
|
40
|
-
- campos de formulario reutilizaveis;
|
|
41
|
-
- primitivas de acao;
|
|
42
|
-
- tokens, estilos e presets;
|
|
43
|
-
- helpers sem dependencia de dominio;
|
|
44
|
-
- composicao por slots quando o componente possui partes internas.
|
|
45
|
-
|
|
46
|
-
## Validacao de Page Actions
|
|
47
|
-
|
|
48
|
-
O pacote publica o CLI `shared-ui-check-page-actions` para validar o contrato de actions em uma aplicacao.
|
|
49
|
-
|
|
50
|
-
Contrato esperado:
|
|
51
|
-
|
|
52
|
-
- `AppLayout.actions` e somente o slot de layout.
|
|
53
|
-
- `Page.Actions` e o root visual da barra.
|
|
54
|
-
- Props como `helpContent`, `helpLabel`, `position`, `align` e `size` pertencem ao `Page.Actions`.
|
|
55
|
-
- Nao usar `ActionBar.*`, `Page.Root actions=...` dentro de `AppLayout`, Fragment, `div` ou `Page.ActionButton` solto como root de `AppLayout.actions`.
|
|
56
|
-
|
|
57
|
-
Uso recomendado:
|
|
58
|
-
|
|
59
|
-
```json
|
|
60
|
-
{
|
|
61
|
-
"scripts": {
|
|
62
|
-
"check:page-actions": "shared-ui-check-page-actions"
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
Tambem e possivel apontar um diretorio explicitamente:
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
shared-ui-check-page-actions ./src
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
## Page Actions
|
|
74
|
-
|
|
75
|
-
`Page.Actions` e a raiz visual das acoes de tela. O pacote suporta `left`, `top`, `right` e
|
|
76
|
-
`bottom`, com `left` como padrao desktop e `bottom` como fallback mobile quando permitido.
|
|
77
|
-
|
|
78
|
-
Persistencia de preferencia continua sendo responsabilidade do consumidor. Use
|
|
79
|
-
`Page.ActionsProvider` para fornecer a preferencia global sem acoplar o pacote a
|
|
80
|
-
`localStorage`:
|
|
81
|
-
|
|
82
|
-
```tsx
|
|
83
|
-
import { useCallback, useMemo, useState } from 'react';
|
|
84
|
-
import { Page, type PageActionsPosition, type PageActionsPreferences } from '@wellingtonhlc/shared-ui';
|
|
85
|
-
|
|
86
|
-
const storageKey = '@my-app/preference/page-actions-position';
|
|
87
|
-
const positions: PageActionsPosition[] = ['left', 'top', 'right', 'bottom'];
|
|
88
|
-
|
|
89
|
-
function readStoredPosition() {
|
|
90
|
-
const stored = window.localStorage.getItem(storageKey);
|
|
91
|
-
return positions.includes(stored as PageActionsPosition) ? (stored as PageActionsPosition) : undefined;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export function AppLayout({ children }: { children: React.ReactNode }) {
|
|
95
|
-
const [position, setPosition] = useState<PageActionsPosition | undefined>(readStoredPosition);
|
|
96
|
-
|
|
97
|
-
const handlePositionChange = useCallback(function handlePositionChange(nextPosition: PageActionsPosition) {
|
|
98
|
-
setPosition(nextPosition);
|
|
99
|
-
window.localStorage.setItem(storageKey, nextPosition);
|
|
100
|
-
}, []);
|
|
101
|
-
|
|
102
|
-
const preferences = useMemo<PageActionsPreferences>(
|
|
103
|
-
() => ({
|
|
104
|
-
mobilePosition: 'bottom',
|
|
105
|
-
movable: true,
|
|
106
|
-
onPositionChange: handlePositionChange,
|
|
107
|
-
position,
|
|
108
|
-
}),
|
|
109
|
-
[handlePositionChange, position],
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
return (
|
|
113
|
-
<Page.ActionsProvider preferences={preferences}>
|
|
114
|
-
<Page.ActionsSlot position="left" />
|
|
115
|
-
<main>{children}</main>
|
|
116
|
-
<Page.ActionsSlot position="right" />
|
|
117
|
-
<Page.ActionsSlot position="top" />
|
|
118
|
-
<Page.ActionsSlot position="bottom" />
|
|
119
|
-
</Page.ActionsProvider>
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
Acoes de uma tela devem continuar usando `Page.Actions`; o provider apenas controla onde elas serao exibidas:
|
|
125
|
-
|
|
126
|
-
```tsx
|
|
127
|
-
import { Page } from '@wellingtonhlc/shared-ui/components/Page';
|
|
128
|
-
import { Save } from 'lucide-react';
|
|
129
|
-
|
|
130
|
-
export function EditPageActions() {
|
|
131
|
-
return (
|
|
132
|
-
<Page.Actions helpLabel="Ajuda" helpContent="Revise os dados antes de salvar.">
|
|
133
|
-
<Page.ActionButton icon={<Save />} label="Salvar" />
|
|
134
|
-
</Page.Actions>
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
Telas podem limitar o contrato localmente:
|
|
140
|
-
|
|
141
|
-
```tsx
|
|
142
|
-
<Page.Actions allowedPositions={['top', 'bottom']} movable={false}>
|
|
143
|
-
<Page.ActionButton label="Salvar" />
|
|
144
|
-
</Page.Actions>
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
Acoes auxiliares podem ser passadas por `helpContent`, `metaActions` ou por componentes
|
|
148
|
-
marcados como meta. Em barras verticais, o grupo meta e renderizado em modo compacto
|
|
149
|
-
e alinhado na borda externa da barra para preservar leitura visual; as labels continuam
|
|
150
|
-
obrigatorias no contrato e sao usadas para acessibilidade e tooltip.
|
|
151
|
-
|
|
152
|
-
## Exportacoes
|
|
153
|
-
|
|
154
|
-
Entrada principal:
|
|
155
|
-
|
|
156
|
-
```tsx
|
|
157
|
-
import { Button, Card, Modal, Table, cn } from '@wellingtonhlc/shared-ui';
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
Subpaths recomendados para telas grandes ou rotas lazy-loaded:
|
|
161
|
-
|
|
162
|
-
```tsx
|
|
163
|
-
import { Button } from '@wellingtonhlc/shared-ui/components/Button';
|
|
164
|
-
import { Table } from '@wellingtonhlc/shared-ui/components/Table';
|
|
165
|
-
import { cn } from '@wellingtonhlc/shared-ui/utils/cn';
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
Estilos:
|
|
169
|
-
|
|
170
|
-
```tsx
|
|
171
|
-
import '@wellingtonhlc/shared-ui/styles.css';
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
Preset Tailwind:
|
|
175
|
-
|
|
176
|
-
```ts
|
|
177
|
-
import sharedUiPreset from '@wellingtonhlc/shared-ui/tailwind-preset';
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
## Componentes
|
|
181
|
-
|
|
182
|
-
Componentes disponiveis na API publica:
|
|
183
|
-
|
|
184
|
-
| Grupo | Componentes |
|
|
185
|
-
|-------|-------------|
|
|
186
|
-
| Layout | `AppShell`, `Page`, `Sidebar`, `Card`, `StatCard` |
|
|
187
|
-
| Acoes | `Button`, `ActionPrimitives`, `AppShellActions`, `ConfirmationDialog` |
|
|
188
|
-
| Feedback | `Badge`, `EmptyState`, `PageMessage`, `Tooltip`, `CopyableField` |
|
|
189
|
-
| Formularios | `FieldControl`, `FieldGroup`, `TextField`, `TextareaField`, `SelectField`, `SelectionField`, `DateField`, `DecimalField`, `MultiSelectField`, `PasswordInput`, `Switch`, `RadioGroup` |
|
|
190
|
-
| Consulta | `Filter`, `Workspace` |
|
|
191
|
-
| Dados | `Table`, `Pagination`, `TabsUnderlined` |
|
|
192
|
-
| Controle de renderizacao | `RenderIf`, `RenderCase` |
|
|
193
|
-
| Tema | `ThemePreferencesSelector` |
|
|
194
|
-
|
|
195
|
-
## DateField
|
|
196
|
-
|
|
197
|
-
`DateField` centraliza entrada de data, data e hora, ou apenas hora. Use `mode`
|
|
198
|
-
para declarar o formato esperado pelo campo:
|
|
199
|
-
|
|
200
|
-
```tsx
|
|
201
|
-
<DateField label="Data" value="2026-06-16" onChange={setDate} mode="date" />
|
|
202
|
-
<DateField label="Data e hora" value="2026-06-16T14:30" onChange={setDateTime} mode="dateTime" />
|
|
203
|
-
<DateField label="Hora" value="14:30" onChange={setTime} mode="time" />
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
`enableTime` permanece suportado como alias legado para `mode="dateTime"`.
|
|
207
|
-
|
|
208
|
-
## FieldGroup
|
|
209
|
-
|
|
210
|
-
`FieldGroup` renderiza um grupo visual de campos com legenda. A borda vem ativa por
|
|
211
|
-
padrao; use `bordered={false}` quando o consumidor precisar manter a semantica de
|
|
212
|
-
grupo sem adicionar contorno visual.
|
|
213
|
-
|
|
214
|
-
```tsx
|
|
215
|
-
import { FieldGroup, TextField } from '@wellingtonhlc/shared-ui';
|
|
216
|
-
|
|
217
|
-
<FieldGroup title="Endereco" bordered={false}>
|
|
218
|
-
<TextField label="CEP" />
|
|
219
|
-
<TextField label="Cidade" />
|
|
220
|
-
</FieldGroup>;
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
## RadioGroup
|
|
224
|
-
|
|
225
|
-
`RadioGroup` segue a mesma escala de altura dos campos base (`xs`, `sm`, `md`,
|
|
226
|
-
`lg`) para alinhar visualmente com `TextField`, `SelectField` e demais controles
|
|
227
|
-
na mesma linha de formulario.
|
|
228
|
-
|
|
229
|
-
O padrao `boxed` renderiza um `fieldset` com legenda, fazendo a borda contornar
|
|
230
|
-
o grupo a partir da label. Use `variant="plain"` quando precisar do grupo sem
|
|
231
|
-
borda.
|
|
232
|
-
|
|
233
|
-
```tsx
|
|
234
|
-
import { RadioGroup } from '@wellingtonhlc/shared-ui';
|
|
235
|
-
|
|
236
|
-
<RadioGroup label="Tipo Pessoa" value={1} onChange={setType}>
|
|
237
|
-
<RadioGroup.Item value={1} label="Fisica" />
|
|
238
|
-
<RadioGroup.Item value={2} label="Juridica" />
|
|
239
|
-
</RadioGroup>;
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
## AppShell.Topbar.Button
|
|
243
|
-
|
|
244
|
-
Use `AppShell.Topbar.Button` para acoes de icone na barra superior, como notificacoes,
|
|
245
|
-
configuracoes e atalhos globais. O componente reutiliza o mesmo contrato visual dos
|
|
246
|
-
primitivos de acao, mas aplica o tamanho esperado para a topbar.
|
|
247
|
-
|
|
248
|
-
```tsx
|
|
249
|
-
import { AppShell } from '@wellingtonhlc/shared-ui';
|
|
250
|
-
import { Bell, Settings } from 'lucide-react';
|
|
251
|
-
|
|
252
|
-
<AppShell.Actions>
|
|
253
|
-
<AppShell.Topbar.Button icon={<Bell />} tooltip="Notificacoes" />
|
|
254
|
-
<AppShell.Topbar.Button icon={<Settings />} tooltip="Configuracoes" />
|
|
255
|
-
</AppShell.Actions>
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
## TabsUnderlined
|
|
259
|
-
|
|
260
|
-
`TabsUnderlined` renderiza abas sublinhadas com conteudo controlado ou nao controlado.
|
|
261
|
-
As abas ja possuem tamanho minimo padrao e padding horizontal para comportar labels
|
|
262
|
-
medias, como `Configuracoes` e `Fornecedor`, sem ficarem apertadas.
|
|
263
|
-
|
|
264
|
-
Quando houver labels longas, use `tabWidth` e `truncateLabels` para manter abas
|
|
265
|
-
uniformes e aplicar ellipsis no texto que exceder o limite.
|
|
266
|
-
|
|
267
|
-
```tsx
|
|
268
|
-
import { TabsUnderlined } from '@wellingtonhlc/shared-ui';
|
|
269
|
-
|
|
270
|
-
<TabsUnderlined
|
|
271
|
-
items={items}
|
|
272
|
-
tabWidth="12rem"
|
|
273
|
-
truncateLabels
|
|
274
|
-
/>;
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
Quando a largura puder variar dentro de uma faixa, use `tabMinWidth` e `tabMaxWidth`.
|
|
278
|
-
`truncateLabels` nao altera o comportamento sozinho; defina tambem `tabWidth` ou
|
|
279
|
-
`tabMaxWidth` para que o navegador saiba quando abreviar o texto.
|
|
280
|
-
|
|
281
|
-
## ThemePreferencesSelector
|
|
282
|
-
|
|
283
|
-
`ThemePreferencesSelector` centraliza a escolha de cor e aparencia (`system`, `light`, `dark`) usada pelos consumidores.
|
|
284
|
-
|
|
285
|
-
Contrato minimo:
|
|
286
|
-
|
|
287
|
-
```tsx
|
|
288
|
-
import { ThemePreferencesSelector, type ThemePreferencesValue } from '@wellingtonhlc/shared-ui';
|
|
289
|
-
|
|
290
|
-
const value: ThemePreferencesValue = {
|
|
291
|
-
color: 'indigo',
|
|
292
|
-
appearance: 'system',
|
|
293
|
-
};
|
|
294
|
-
|
|
295
|
-
<ThemePreferencesSelector
|
|
296
|
-
value={value}
|
|
297
|
-
options={[
|
|
298
|
-
{ key: 'indigo', label: 'Indigo', primary: '#5b5cff' },
|
|
299
|
-
{ key: 'emerald', label: 'Emerald', primary: '#059669' },
|
|
300
|
-
]}
|
|
301
|
-
onChange={setValue}
|
|
302
|
-
/>;
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
Pontos de validacao:
|
|
306
|
-
|
|
307
|
-
- O botao de aparencia selecionado deve ficar perceptivelmente destacado sem depender de hover.
|
|
308
|
-
- Opcoes de cor bloqueadas usam `locked` e podem informar `requiredPlan`.
|
|
309
|
-
- Use `showColorSelector={false}` ou `showAppearanceSelector={false}` quando o consumidor precisar exibir apenas uma parte do controle.
|
|
310
|
-
- A story `AppearanceStates` mostra as tres aparencias ja selecionadas para validacao visual rapida.
|
|
311
|
-
|
|
312
|
-
## SelectionField
|
|
313
|
-
|
|
314
|
-
`SelectionField` padroniza campos de selecao que exibem um registro, abrem uma busca externa e permitem limpar o valor selecionado. O componente nao gerencia modal nem estado interno do registro; o consumidor controla `value`, `onClick` e `onClear`.
|
|
315
|
-
|
|
316
|
-
Contrato minimo:
|
|
317
|
-
|
|
318
|
-
```tsx
|
|
319
|
-
import { SelectionField, type SelectionDisplayValue } from '@wellingtonhlc/shared-ui';
|
|
320
|
-
import { UserCheck } from 'lucide-react';
|
|
321
|
-
|
|
322
|
-
const value: SelectionDisplayValue = {
|
|
323
|
-
title: 'Registro selecionado',
|
|
324
|
-
subtitle: 'Descricao complementar',
|
|
325
|
-
meta: 'COD-1024',
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
<SelectionField
|
|
329
|
-
label="Registro"
|
|
330
|
-
value={value}
|
|
331
|
-
icon={<UserCheck />}
|
|
332
|
-
clearable
|
|
333
|
-
size="sm"
|
|
334
|
-
selectTooltip="Selecionar registro"
|
|
335
|
-
clearTooltip="Limpar registro"
|
|
336
|
-
onClick={openSearch}
|
|
337
|
-
onClear={clearSelection}
|
|
338
|
-
/>;
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
Pontos de validacao:
|
|
342
|
-
|
|
343
|
-
- `size` segue a mesma escala visual dos campos base (`xs`, `sm`, `md`, `lg`).
|
|
344
|
-
- Use `triggerSlot` quando o consumidor precisar injetar um botao proprio para abrir a selecao.
|
|
345
|
-
- `clearable` exibe a acao de limpar somente quando ha `value` e `onClear`.
|
|
346
|
-
- `shortcutHint` pode exibir atalhos de teclado, como `F1`.
|
|
347
|
-
|
|
348
|
-
## Filter (contrato legado)
|
|
349
|
-
|
|
350
|
-
`Filter.Root` organiza filtros compactos, ocupando apenas o espaço necessário. Quando houver muitos filtros, somente o corpo do painel rola e as ações continuam visíveis.
|
|
351
|
-
|
|
352
|
-
```tsx
|
|
353
|
-
import { Button, Filter, TextField } from '@wellingtonhlc/shared-ui';
|
|
354
|
-
|
|
355
|
-
export function CustomersFilters() {
|
|
356
|
-
return (
|
|
357
|
-
<Filter.Root description="Refine os critérios antes de consultar os resultados.">
|
|
358
|
-
<TextField label="Buscar" size="sm" />
|
|
359
|
-
<Filter.Footer>
|
|
360
|
-
<Button type="submit" size="sm">
|
|
361
|
-
Pesquisar
|
|
362
|
-
</Button>
|
|
363
|
-
</Filter.Footer>
|
|
364
|
-
</Filter.Root>
|
|
365
|
-
);
|
|
366
|
-
}
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
Padrões do painel:
|
|
370
|
-
|
|
371
|
-
- Usa `h-fit` e `w-fit` por padrão, sem ocupar a altura inteira da tela.
|
|
372
|
-
- Limita a altura com `maxHeightClassName`; o default é `max-h-[calc(100vh-12rem)]`.
|
|
373
|
-
- Aplica rolagem apenas na área de filtros.
|
|
374
|
-
- Use `Filter.Footer` para manter botões sempre visíveis no rodapé do painel.
|
|
375
|
-
- Em interfaces densas, campos dentro do painel devem preferir `size="sm"` e nao devem usar formato `rounded-full`.
|
|
376
|
-
|
|
377
|
-
## Filter atual
|
|
378
|
-
|
|
379
|
-
`Filter.Root` usa descrição vazia por padrão e rola apenas o viewport dos campos. Informe `onSubmit` e `onClear` para obter as ações automáticas `Pesquisar` e `Redefinir`; o submit cria um único `<form>` interno. `submitLabel`, `clearLabel`, `isSubmitting`, `actionsDisabled` e `fieldsClassName` são opcionais. Um `Filter.Footer` explícito substitui as ações automáticas e continua indicado para formulários externos.
|
|
380
|
-
|
|
381
|
-
`Filter.Visibility` oferece estado controlado ou não controlado. `Filter.Trigger` aceita o botão padrão ou clona um `Page.ActionButton` via `triggerSlot`, preservando seu estilo e handler. Dentro de `Workspace.Root`, o estado fechado recolhe a coluna de filtros.
|
|
382
|
-
|
|
383
|
-
`Workspace.Root appearance="joined"` compõe filtros e resultados sem gap em uma moldura única. O padrão continua `separated`; `Table.Root` permanece responsável por viewport, moldura e paginação.
|
|
384
|
-
|
|
385
|
-
```tsx
|
|
386
|
-
<Filter.Root onSubmit={handleSearch} onClear={handleClear} isSubmitting={isLoading}>
|
|
387
|
-
<TextField label="Buscar" size="sm" />
|
|
388
|
-
</Filter.Root>
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
## Contrato publico 0.1.0
|
|
392
|
-
|
|
393
|
-
A API pública pré-v1 foi limpa para evitar compatibilidade artificial em produção.
|
|
394
|
-
|
|
395
|
-
Exports removidos:
|
|
396
|
-
|
|
397
|
-
- `ActionBar`: use `Page.Actions`, `Page.ActionButton` e `Page.ActionsSeparator`.
|
|
398
|
-
- `Dialog`: use `Modal`.
|
|
399
|
-
- `ChoiceGroup`: use `RadioGroup`.
|
|
400
|
-
- `ConditionalCase`: use `RenderCase`.
|
|
401
|
-
- `ConditionalRender`: use `RenderIf`.
|
|
402
|
-
- `ToggleSwitch`: use `Switch`.
|
|
403
|
-
- `SearchFilter`: use `Filter`.
|
|
404
|
-
- `SearchWorkspace`: componha layout no consumidor ou com componentes genéricos.
|
|
405
|
-
- `TabsUnderline` e `UnderlinedTabs`: use `TabsUnderlined`.
|
|
1
|
+
# Wellington Shared UI
|
|
2
|
+
|
|
3
|
+
Conjunto de componentes visuais padronizados, criado por Wellington Henrique para aplicacoes React.
|
|
4
|
+
|
|
5
|
+
O objetivo deste pacote e concentrar componentes base, tokens, presets e helpers de UI reutilizaveis, sem regras de negocio de dominio.
|
|
6
|
+
|
|
7
|
+
## Pacote
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @wellingtonhlc/shared-ui
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Uso basico:
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { Button, Card, Page, Table } from '@wellingtonhlc/shared-ui';
|
|
17
|
+
|
|
18
|
+
export function ExamplePage() {
|
|
19
|
+
return (
|
|
20
|
+
<Page.Root>
|
|
21
|
+
<Card.Root>
|
|
22
|
+
<Card.Header>
|
|
23
|
+
<Card.Title>Registros</Card.Title>
|
|
24
|
+
<Card.Description>Consulta de dados</Card.Description>
|
|
25
|
+
</Card.Header>
|
|
26
|
+
<Card.Body>
|
|
27
|
+
<Button>Salvar</Button>
|
|
28
|
+
</Card.Body>
|
|
29
|
+
</Card.Root>
|
|
30
|
+
</Page.Root>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Principios
|
|
36
|
+
|
|
37
|
+
Este pacote foi desenhado para oferecer infraestrutura visual generica:
|
|
38
|
+
|
|
39
|
+
- componentes base de interface;
|
|
40
|
+
- campos de formulario reutilizaveis;
|
|
41
|
+
- primitivas de acao;
|
|
42
|
+
- tokens, estilos e presets;
|
|
43
|
+
- helpers sem dependencia de dominio;
|
|
44
|
+
- composicao por slots quando o componente possui partes internas.
|
|
45
|
+
|
|
46
|
+
## Validacao de Page Actions
|
|
47
|
+
|
|
48
|
+
O pacote publica o CLI `shared-ui-check-page-actions` para validar o contrato de actions em uma aplicacao.
|
|
49
|
+
|
|
50
|
+
Contrato esperado:
|
|
51
|
+
|
|
52
|
+
- `AppLayout.actions` e somente o slot de layout.
|
|
53
|
+
- `Page.Actions` e o root visual da barra.
|
|
54
|
+
- Props como `helpContent`, `helpLabel`, `position`, `align` e `size` pertencem ao `Page.Actions`.
|
|
55
|
+
- Nao usar `ActionBar.*`, `Page.Root actions=...` dentro de `AppLayout`, Fragment, `div` ou `Page.ActionButton` solto como root de `AppLayout.actions`.
|
|
56
|
+
|
|
57
|
+
Uso recomendado:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"scripts": {
|
|
62
|
+
"check:page-actions": "shared-ui-check-page-actions"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Tambem e possivel apontar um diretorio explicitamente:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
shared-ui-check-page-actions ./src
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Page Actions
|
|
74
|
+
|
|
75
|
+
`Page.Actions` e a raiz visual das acoes de tela. O pacote suporta `left`, `top`, `right` e
|
|
76
|
+
`bottom`, com `left` como padrao desktop e `bottom` como fallback mobile quando permitido.
|
|
77
|
+
|
|
78
|
+
Persistencia de preferencia continua sendo responsabilidade do consumidor. Use
|
|
79
|
+
`Page.ActionsProvider` para fornecer a preferencia global sem acoplar o pacote a
|
|
80
|
+
`localStorage`:
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
import { useCallback, useMemo, useState } from 'react';
|
|
84
|
+
import { Page, type PageActionsPosition, type PageActionsPreferences } from '@wellingtonhlc/shared-ui';
|
|
85
|
+
|
|
86
|
+
const storageKey = '@my-app/preference/page-actions-position';
|
|
87
|
+
const positions: PageActionsPosition[] = ['left', 'top', 'right', 'bottom'];
|
|
88
|
+
|
|
89
|
+
function readStoredPosition() {
|
|
90
|
+
const stored = window.localStorage.getItem(storageKey);
|
|
91
|
+
return positions.includes(stored as PageActionsPosition) ? (stored as PageActionsPosition) : undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function AppLayout({ children }: { children: React.ReactNode }) {
|
|
95
|
+
const [position, setPosition] = useState<PageActionsPosition | undefined>(readStoredPosition);
|
|
96
|
+
|
|
97
|
+
const handlePositionChange = useCallback(function handlePositionChange(nextPosition: PageActionsPosition) {
|
|
98
|
+
setPosition(nextPosition);
|
|
99
|
+
window.localStorage.setItem(storageKey, nextPosition);
|
|
100
|
+
}, []);
|
|
101
|
+
|
|
102
|
+
const preferences = useMemo<PageActionsPreferences>(
|
|
103
|
+
() => ({
|
|
104
|
+
mobilePosition: 'bottom',
|
|
105
|
+
movable: true,
|
|
106
|
+
onPositionChange: handlePositionChange,
|
|
107
|
+
position,
|
|
108
|
+
}),
|
|
109
|
+
[handlePositionChange, position],
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<Page.ActionsProvider preferences={preferences}>
|
|
114
|
+
<Page.ActionsSlot position="left" />
|
|
115
|
+
<main>{children}</main>
|
|
116
|
+
<Page.ActionsSlot position="right" />
|
|
117
|
+
<Page.ActionsSlot position="top" />
|
|
118
|
+
<Page.ActionsSlot position="bottom" />
|
|
119
|
+
</Page.ActionsProvider>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Acoes de uma tela devem continuar usando `Page.Actions`; o provider apenas controla onde elas serao exibidas:
|
|
125
|
+
|
|
126
|
+
```tsx
|
|
127
|
+
import { Page } from '@wellingtonhlc/shared-ui/components/Page';
|
|
128
|
+
import { Save } from 'lucide-react';
|
|
129
|
+
|
|
130
|
+
export function EditPageActions() {
|
|
131
|
+
return (
|
|
132
|
+
<Page.Actions helpLabel="Ajuda" helpContent="Revise os dados antes de salvar.">
|
|
133
|
+
<Page.ActionButton icon={<Save />} label="Salvar" />
|
|
134
|
+
</Page.Actions>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Telas podem limitar o contrato localmente:
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
<Page.Actions allowedPositions={['top', 'bottom']} movable={false}>
|
|
143
|
+
<Page.ActionButton label="Salvar" />
|
|
144
|
+
</Page.Actions>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Acoes auxiliares podem ser passadas por `helpContent`, `metaActions` ou por componentes
|
|
148
|
+
marcados como meta. Em barras verticais, o grupo meta e renderizado em modo compacto
|
|
149
|
+
e alinhado na borda externa da barra para preservar leitura visual; as labels continuam
|
|
150
|
+
obrigatorias no contrato e sao usadas para acessibilidade e tooltip.
|
|
151
|
+
|
|
152
|
+
## Exportacoes
|
|
153
|
+
|
|
154
|
+
Entrada principal:
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
import { Button, Card, Modal, Table, cn } from '@wellingtonhlc/shared-ui';
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Subpaths recomendados para telas grandes ou rotas lazy-loaded:
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
import { Button } from '@wellingtonhlc/shared-ui/components/Button';
|
|
164
|
+
import { Table } from '@wellingtonhlc/shared-ui/components/Table';
|
|
165
|
+
import { cn } from '@wellingtonhlc/shared-ui/utils/cn';
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Estilos:
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
import '@wellingtonhlc/shared-ui/styles.css';
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Preset Tailwind:
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
import sharedUiPreset from '@wellingtonhlc/shared-ui/tailwind-preset';
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Componentes
|
|
181
|
+
|
|
182
|
+
Componentes disponiveis na API publica:
|
|
183
|
+
|
|
184
|
+
| Grupo | Componentes |
|
|
185
|
+
|-------|-------------|
|
|
186
|
+
| Layout | `AppShell`, `Page`, `Sidebar`, `Card`, `StatCard` |
|
|
187
|
+
| Acoes | `Button`, `ActionPrimitives`, `AppShellActions`, `ConfirmationDialog` |
|
|
188
|
+
| Feedback | `Badge`, `EmptyState`, `PageMessage`, `Tooltip`, `CopyableField` |
|
|
189
|
+
| Formularios | `FieldControl`, `FieldGroup`, `TextField`, `TextareaField`, `SelectField`, `SelectionField`, `DateField`, `DecimalField`, `MultiSelectField`, `PasswordInput`, `Switch`, `RadioGroup` |
|
|
190
|
+
| Consulta | `Filter`, `Workspace` |
|
|
191
|
+
| Dados | `Table`, `Pagination`, `TabsUnderlined` |
|
|
192
|
+
| Controle de renderizacao | `RenderIf`, `RenderCase` |
|
|
193
|
+
| Tema | `ThemePreferencesSelector` |
|
|
194
|
+
|
|
195
|
+
## DateField
|
|
196
|
+
|
|
197
|
+
`DateField` centraliza entrada de data, data e hora, ou apenas hora. Use `mode`
|
|
198
|
+
para declarar o formato esperado pelo campo:
|
|
199
|
+
|
|
200
|
+
```tsx
|
|
201
|
+
<DateField label="Data" value="2026-06-16" onChange={setDate} mode="date" />
|
|
202
|
+
<DateField label="Data e hora" value="2026-06-16T14:30" onChange={setDateTime} mode="dateTime" />
|
|
203
|
+
<DateField label="Hora" value="14:30" onChange={setTime} mode="time" />
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
`enableTime` permanece suportado como alias legado para `mode="dateTime"`.
|
|
207
|
+
|
|
208
|
+
## FieldGroup
|
|
209
|
+
|
|
210
|
+
`FieldGroup` renderiza um grupo visual de campos com legenda. A borda vem ativa por
|
|
211
|
+
padrao; use `bordered={false}` quando o consumidor precisar manter a semantica de
|
|
212
|
+
grupo sem adicionar contorno visual.
|
|
213
|
+
|
|
214
|
+
```tsx
|
|
215
|
+
import { FieldGroup, TextField } from '@wellingtonhlc/shared-ui';
|
|
216
|
+
|
|
217
|
+
<FieldGroup title="Endereco" bordered={false}>
|
|
218
|
+
<TextField label="CEP" />
|
|
219
|
+
<TextField label="Cidade" />
|
|
220
|
+
</FieldGroup>;
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## RadioGroup
|
|
224
|
+
|
|
225
|
+
`RadioGroup` segue a mesma escala de altura dos campos base (`xs`, `sm`, `md`,
|
|
226
|
+
`lg`) para alinhar visualmente com `TextField`, `SelectField` e demais controles
|
|
227
|
+
na mesma linha de formulario.
|
|
228
|
+
|
|
229
|
+
O padrao `boxed` renderiza um `fieldset` com legenda, fazendo a borda contornar
|
|
230
|
+
o grupo a partir da label. Use `variant="plain"` quando precisar do grupo sem
|
|
231
|
+
borda.
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
import { RadioGroup } from '@wellingtonhlc/shared-ui';
|
|
235
|
+
|
|
236
|
+
<RadioGroup label="Tipo Pessoa" value={1} onChange={setType}>
|
|
237
|
+
<RadioGroup.Item value={1} label="Fisica" />
|
|
238
|
+
<RadioGroup.Item value={2} label="Juridica" />
|
|
239
|
+
</RadioGroup>;
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## AppShell.Topbar.Button
|
|
243
|
+
|
|
244
|
+
Use `AppShell.Topbar.Button` para acoes de icone na barra superior, como notificacoes,
|
|
245
|
+
configuracoes e atalhos globais. O componente reutiliza o mesmo contrato visual dos
|
|
246
|
+
primitivos de acao, mas aplica o tamanho esperado para a topbar.
|
|
247
|
+
|
|
248
|
+
```tsx
|
|
249
|
+
import { AppShell } from '@wellingtonhlc/shared-ui';
|
|
250
|
+
import { Bell, Settings } from 'lucide-react';
|
|
251
|
+
|
|
252
|
+
<AppShell.Actions>
|
|
253
|
+
<AppShell.Topbar.Button icon={<Bell />} tooltip="Notificacoes" />
|
|
254
|
+
<AppShell.Topbar.Button icon={<Settings />} tooltip="Configuracoes" />
|
|
255
|
+
</AppShell.Actions>
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## TabsUnderlined
|
|
259
|
+
|
|
260
|
+
`TabsUnderlined` renderiza abas sublinhadas com conteudo controlado ou nao controlado.
|
|
261
|
+
As abas ja possuem tamanho minimo padrao e padding horizontal para comportar labels
|
|
262
|
+
medias, como `Configuracoes` e `Fornecedor`, sem ficarem apertadas.
|
|
263
|
+
|
|
264
|
+
Quando houver labels longas, use `tabWidth` e `truncateLabels` para manter abas
|
|
265
|
+
uniformes e aplicar ellipsis no texto que exceder o limite.
|
|
266
|
+
|
|
267
|
+
```tsx
|
|
268
|
+
import { TabsUnderlined } from '@wellingtonhlc/shared-ui';
|
|
269
|
+
|
|
270
|
+
<TabsUnderlined
|
|
271
|
+
items={items}
|
|
272
|
+
tabWidth="12rem"
|
|
273
|
+
truncateLabels
|
|
274
|
+
/>;
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Quando a largura puder variar dentro de uma faixa, use `tabMinWidth` e `tabMaxWidth`.
|
|
278
|
+
`truncateLabels` nao altera o comportamento sozinho; defina tambem `tabWidth` ou
|
|
279
|
+
`tabMaxWidth` para que o navegador saiba quando abreviar o texto.
|
|
280
|
+
|
|
281
|
+
## ThemePreferencesSelector
|
|
282
|
+
|
|
283
|
+
`ThemePreferencesSelector` centraliza a escolha de cor e aparencia (`system`, `light`, `dark`) usada pelos consumidores.
|
|
284
|
+
|
|
285
|
+
Contrato minimo:
|
|
286
|
+
|
|
287
|
+
```tsx
|
|
288
|
+
import { ThemePreferencesSelector, type ThemePreferencesValue } from '@wellingtonhlc/shared-ui';
|
|
289
|
+
|
|
290
|
+
const value: ThemePreferencesValue = {
|
|
291
|
+
color: 'indigo',
|
|
292
|
+
appearance: 'system',
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
<ThemePreferencesSelector
|
|
296
|
+
value={value}
|
|
297
|
+
options={[
|
|
298
|
+
{ key: 'indigo', label: 'Indigo', primary: '#5b5cff' },
|
|
299
|
+
{ key: 'emerald', label: 'Emerald', primary: '#059669' },
|
|
300
|
+
]}
|
|
301
|
+
onChange={setValue}
|
|
302
|
+
/>;
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Pontos de validacao:
|
|
306
|
+
|
|
307
|
+
- O botao de aparencia selecionado deve ficar perceptivelmente destacado sem depender de hover.
|
|
308
|
+
- Opcoes de cor bloqueadas usam `locked` e podem informar `requiredPlan`.
|
|
309
|
+
- Use `showColorSelector={false}` ou `showAppearanceSelector={false}` quando o consumidor precisar exibir apenas uma parte do controle.
|
|
310
|
+
- A story `AppearanceStates` mostra as tres aparencias ja selecionadas para validacao visual rapida.
|
|
311
|
+
|
|
312
|
+
## SelectionField
|
|
313
|
+
|
|
314
|
+
`SelectionField` padroniza campos de selecao que exibem um registro, abrem uma busca externa e permitem limpar o valor selecionado. O componente nao gerencia modal nem estado interno do registro; o consumidor controla `value`, `onClick` e `onClear`.
|
|
315
|
+
|
|
316
|
+
Contrato minimo:
|
|
317
|
+
|
|
318
|
+
```tsx
|
|
319
|
+
import { SelectionField, type SelectionDisplayValue } from '@wellingtonhlc/shared-ui';
|
|
320
|
+
import { UserCheck } from 'lucide-react';
|
|
321
|
+
|
|
322
|
+
const value: SelectionDisplayValue = {
|
|
323
|
+
title: 'Registro selecionado',
|
|
324
|
+
subtitle: 'Descricao complementar',
|
|
325
|
+
meta: 'COD-1024',
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
<SelectionField
|
|
329
|
+
label="Registro"
|
|
330
|
+
value={value}
|
|
331
|
+
icon={<UserCheck />}
|
|
332
|
+
clearable
|
|
333
|
+
size="sm"
|
|
334
|
+
selectTooltip="Selecionar registro"
|
|
335
|
+
clearTooltip="Limpar registro"
|
|
336
|
+
onClick={openSearch}
|
|
337
|
+
onClear={clearSelection}
|
|
338
|
+
/>;
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
Pontos de validacao:
|
|
342
|
+
|
|
343
|
+
- `size` segue a mesma escala visual dos campos base (`xs`, `sm`, `md`, `lg`).
|
|
344
|
+
- Use `triggerSlot` quando o consumidor precisar injetar um botao proprio para abrir a selecao.
|
|
345
|
+
- `clearable` exibe a acao de limpar somente quando ha `value` e `onClear`.
|
|
346
|
+
- `shortcutHint` pode exibir atalhos de teclado, como `F1`.
|
|
347
|
+
|
|
348
|
+
## Filter (contrato legado)
|
|
349
|
+
|
|
350
|
+
`Filter.Root` organiza filtros compactos, ocupando apenas o espaço necessário. Quando houver muitos filtros, somente o corpo do painel rola e as ações continuam visíveis.
|
|
351
|
+
|
|
352
|
+
```tsx
|
|
353
|
+
import { Button, Filter, TextField } from '@wellingtonhlc/shared-ui';
|
|
354
|
+
|
|
355
|
+
export function CustomersFilters() {
|
|
356
|
+
return (
|
|
357
|
+
<Filter.Root description="Refine os critérios antes de consultar os resultados.">
|
|
358
|
+
<TextField label="Buscar" size="sm" />
|
|
359
|
+
<Filter.Footer>
|
|
360
|
+
<Button type="submit" size="sm">
|
|
361
|
+
Pesquisar
|
|
362
|
+
</Button>
|
|
363
|
+
</Filter.Footer>
|
|
364
|
+
</Filter.Root>
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
Padrões do painel:
|
|
370
|
+
|
|
371
|
+
- Usa `h-fit` e `w-fit` por padrão, sem ocupar a altura inteira da tela.
|
|
372
|
+
- Limita a altura com `maxHeightClassName`; o default é `max-h-[calc(100vh-12rem)]`.
|
|
373
|
+
- Aplica rolagem apenas na área de filtros.
|
|
374
|
+
- Use `Filter.Footer` para manter botões sempre visíveis no rodapé do painel.
|
|
375
|
+
- Em interfaces densas, campos dentro do painel devem preferir `size="sm"` e nao devem usar formato `rounded-full`.
|
|
376
|
+
|
|
377
|
+
## Filter atual
|
|
378
|
+
|
|
379
|
+
`Filter.Root` usa descrição vazia por padrão e rola apenas o viewport dos campos. Informe `onSubmit` e `onClear` para obter as ações automáticas `Pesquisar` e `Redefinir`; o submit cria um único `<form>` interno. `submitLabel`, `clearLabel`, `isSubmitting`, `actionsDisabled` e `fieldsClassName` são opcionais. Um `Filter.Footer` explícito substitui as ações automáticas e continua indicado para formulários externos.
|
|
380
|
+
|
|
381
|
+
`Filter.Visibility` oferece estado controlado ou não controlado. `Filter.Trigger` aceita o botão padrão ou clona um `Page.ActionButton` via `triggerSlot`, preservando seu estilo e handler. Dentro de `Workspace.Root`, o estado fechado recolhe a coluna de filtros.
|
|
382
|
+
|
|
383
|
+
`Workspace.Root appearance="joined"` compõe filtros e resultados sem gap em uma moldura única. O padrão continua `separated`; `Table.Root` permanece responsável por viewport, moldura e paginação.
|
|
384
|
+
|
|
385
|
+
```tsx
|
|
386
|
+
<Filter.Root onSubmit={handleSearch} onClear={handleClear} isSubmitting={isLoading}>
|
|
387
|
+
<TextField label="Buscar" size="sm" />
|
|
388
|
+
</Filter.Root>
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
## Contrato publico 0.1.0
|
|
392
|
+
|
|
393
|
+
A API pública pré-v1 foi limpa para evitar compatibilidade artificial em produção.
|
|
394
|
+
|
|
395
|
+
Exports removidos:
|
|
396
|
+
|
|
397
|
+
- `ActionBar`: use `Page.Actions`, `Page.ActionButton` e `Page.ActionsSeparator`.
|
|
398
|
+
- `Dialog`: use `Modal`.
|
|
399
|
+
- `ChoiceGroup`: use `RadioGroup`.
|
|
400
|
+
- `ConditionalCase`: use `RenderCase`.
|
|
401
|
+
- `ConditionalRender`: use `RenderIf`.
|
|
402
|
+
- `ToggleSwitch`: use `Switch`.
|
|
403
|
+
- `SearchFilter`: use `Filter`.
|
|
404
|
+
- `SearchWorkspace`: componha layout no consumidor ou com componentes genéricos.
|
|
405
|
+
- `TabsUnderline` e `UnderlinedTabs`: use `TabsUnderlined`.
|