@topjoao/top-design-system 0.1.0-beta.2 → 0.1.0-beta.21

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 CHANGED
@@ -1,135 +1,770 @@
1
- # TopSolutions Design System
2
-
3
- Biblioteca de componentes Vue 3, estilos e tokens visuais compartilhados pela
4
- TopSolutions.
5
-
6
- ## Instalação
7
-
8
- ```bash
9
- npm install @topjoao/top-design-system@beta
10
- ```
11
-
12
- A aplicação consumidora deve possuir Vue 3, PrimeVue, PrimeIcons e
13
- `@primeuix/themes` em versões compatíveis com as `peerDependencies` do pacote.
14
-
15
- ## Uso com Nuxt
16
-
17
- Adicione o módulo uma única vez ao `nuxt.config.ts`:
18
-
19
- ```ts
20
- export default defineNuxtConfig({
21
- modules: [
22
- // outros módulos...
23
- '@topjoao/top-design-system/nuxt',
24
- ],
25
- })
26
- ```
27
-
28
- Depois de alterar a configuração, reinicie o servidor de desenvolvimento ou
29
- regenere os arquivos do Nuxt:
30
-
31
- ```bash
32
- npm run postinstall
33
- ```
34
-
35
- O módulo registra os componentes, gera suas tipagens e inclui o CSS da
36
- biblioteca. Não é necessário importar o componente manualmente:
37
-
38
- ```vue
39
- <template>
40
- <TopButton label="Salvar" icon="pi pi-save" />
41
- </template>
42
- ```
43
-
44
- ## Uso com Vue 3
45
-
46
- ### Importação por componente
47
-
48
- ```vue
49
- <script setup lang="ts">
50
- import { TopButton } from '@topjoao/top-design-system'
51
- import '@topjoao/top-design-system/style.css'
52
- </script>
53
-
54
- <template>
55
- <TopButton label="Salvar" icon="pi pi-save" />
56
- </template>
57
- ```
58
-
59
- ### Registro global
60
-
61
- Na inicialização da aplicação:
1
+ # TopSolutions Design System
2
+
3
+ Biblioteca de componentes Vue 3, estilos e tokens visuais compartilhados pela
4
+ TopSolutions.
5
+
6
+ ## Instalação
7
+
8
+ ```bash
9
+ npm install @topjoao/top-design-system@beta
10
+ ```
11
+
12
+ A aplicação consumidora deve possuir Vue 3, PrimeVue, PrimeIcons e
13
+ `@primeuix/themes` em versões compatíveis com as `peerDependencies` do pacote.
14
+
15
+ ## Tema TopSolutions
16
+
17
+ A biblioteca exporta `TopSolutionsPreset`, o preset PrimeVue baseado na paleta
18
+ oficial da TopSolutions. Configure-o uma vez na aplicação consumidora:
19
+
20
+ ```ts
21
+ import PrimeVue from 'primevue/config'
22
+ import { TopSolutionsPreset } from '@topjoao/top-design-system'
23
+
24
+ app.use(PrimeVue, {
25
+ theme: {
26
+ preset: TopSolutionsPreset,
27
+ options: { darkModeSelector: '.dark' },
28
+ },
29
+ })
30
+ ```
31
+
32
+ Para ativar o modo escuro, adicione ou remova `.dark` uma única vez no elemento
33
+ `<html>` da aplicação. Os componentes usam tokens semânticos de superfície, texto e
34
+ borda fornecidos por `style.css`; não é necessário passar classes `dark:` em cada
35
+ uso. A aplicação pode substituir esses tokens após importar o CSS da biblioteca.
36
+
37
+ Os tokens públicos de campos são `--top-field-background`,
38
+ `--top-field-background-readonly`, `--top-field-background-disabled`,
39
+ `--top-field-text`, `--top-field-label`, `--top-field-placeholder`,
40
+ `--top-field-border`, `--top-field-border-hover` e `--top-field-icon`.
41
+
42
+ O preset é independente dos ajustes de layout próprios do TopLicita; ele
43
+ contém apenas tokens semânticos compartilháveis, como cores primárias, neutras,
44
+ sucesso, alerta e erro.
45
+
46
+ `style.css` contém somente os estilos dos componentes públicos do Design System.
47
+ Regras que
48
+ alcançam o PrimeVue são encapsuladas pelas classes-raiz desses componentes;
49
+ componentes PrimeVue usados diretamente pela aplicação consumidora não são
50
+ sobrescritos pela biblioteca.
51
+
52
+ ## Uso com Nuxt
53
+
54
+ Adicione o módulo uma única vez ao `nuxt.config.ts`:
55
+
56
+ ```ts
57
+ export default defineNuxtConfig({
58
+ modules: [
59
+ // outros módulos...
60
+ '@topjoao/top-design-system/nuxt',
61
+ ],
62
+ })
63
+ ```
64
+
65
+ Depois de alterar a configuração, reinicie o servidor de desenvolvimento ou
66
+ regenere os arquivos do Nuxt:
67
+
68
+ ```bash
69
+ npm run postinstall
70
+ ```
71
+
72
+ O módulo registra os componentes, gera suas tipagens e inclui o CSS da
73
+ biblioteca. Não é necessário importar o componente manualmente:
74
+
75
+ ```vue
76
+ <template>
77
+ <TopButton label="Salvar" icon="pi pi-save" />
78
+ </template>
79
+ ```
80
+
81
+ ## Uso com Vue 3
82
+
83
+ ### Importação por componente
84
+
85
+ ```vue
86
+ <script setup lang="ts">
87
+ import { TopButton } from '@topjoao/top-design-system'
88
+ import '@topjoao/top-design-system/style.css'
89
+ </script>
90
+
91
+ <template>
92
+ <TopButton label="Salvar" icon="pi pi-save" />
93
+ </template>
94
+ ```
95
+
96
+ ### Registro global
97
+
98
+ Na inicialização da aplicação:
99
+
100
+ ```ts
101
+ import { createApp } from 'vue'
102
+ import { TopSolutionsDesignSystem } from '@topjoao/top-design-system/plugin'
103
+ import '@topjoao/top-design-system/style.css'
104
+ import App from './App.vue'
105
+
106
+ createApp(App).use(TopSolutionsDesignSystem).mount('#app')
107
+ ```
108
+
109
+ Após o registro, os componentes podem ser utilizados sem importação manual. A
110
+ entrada `/plugin` também fornece as declarações globais usadas pela IDE.
111
+
112
+ ## TopNavBar
113
+
114
+ Barra de navegação responsiva inspirada no AppSidebar do TopLicita. O
115
+ componente fornece apenas layout e interação: a aplicação consumidora continua
116
+ responsável por rotas, permissões, sessão, cliente/órgão, busca remota,
117
+ favoritos, Aia, suporte e integrações. Nenhuma dessas ações é executada pela
118
+ biblioteca; todas são comunicadas por eventos.
119
+
120
+ ```vue
121
+ <script setup lang="ts">
122
+ import { ref } from 'vue'
123
+ import {
124
+ TopNavBar,
125
+ type TopNavAction,
126
+ type TopNavItem,
127
+ type TopNavSection,
128
+ } from '@topjoao/top-design-system'
129
+
130
+ const clienteAtual = ref('prefeitura-a')
131
+ const favorito = ref(false)
132
+
133
+ const secoes: TopNavSection[] = [
134
+ {
135
+ id: 'planejamento',
136
+ label: 'Planejamento',
137
+ icon: 'pi pi-book',
138
+ children: [
139
+ {
140
+ id: 'programacao',
141
+ label: 'Programação',
142
+ children: [
143
+ { id: 'calendario', label: 'Calendário', to: '/programacao/calendario' },
144
+ ],
145
+ },
146
+ ],
147
+ },
148
+ ]
149
+
150
+ function navegar(item: TopNavItem) {
151
+ if (item.to) router.push(item.to)
152
+ }
153
+
154
+ function executarIntegracao(action: TopNavAction) {
155
+ // Abra a integração identificada por action.id.
156
+ }
157
+ </script>
158
+
159
+ <template>
160
+ <TopNavBar
161
+ :sections="secoes"
162
+ active-item-id="calendario"
163
+ searchable
164
+ show-aia
165
+ show-support
166
+ show-favorite-toggle
167
+ :favorite-active="favorito"
168
+ :actions="[
169
+ { id: 'integracoes', label: 'Integrações', icon: 'pi pi-th-large' },
170
+ ]"
171
+ :user="{ name: 'João Silva', subtitle: 'Administrador', initials: 'JS' }"
172
+ :user-menu-items="[
173
+ { id: 'perfil', label: 'Meu perfil', icon: 'pi pi-user' },
174
+ { id: 'sair', label: 'Sair', icon: 'pi pi-sign-out' },
175
+ ]"
176
+ @navigate="navegar"
177
+ @search="consultarMenusPermitidos"
178
+ @toggle-aia="alternarAia"
179
+ @open-support="abrirCentralSuporte"
180
+ @toggle-favorite="favorito = !favorito"
181
+ @action="executarIntegracao"
182
+ @user-action="executarAcaoDaSessao"
183
+ >
184
+ <template #brand="{ compact }">
185
+ <img src="/logo.svg" alt="Minha organização">
186
+ <span v-if="!compact">Sistema de Contratações</span>
187
+ </template>
188
+
189
+ <template #context="{ compact }">
190
+ <select v-model="clienteAtual" aria-label="Cliente atual">
191
+ <option value="prefeitura-a">Prefeitura A</option>
192
+ <option value="prefeitura-b">Prefeitura B</option>
193
+ </select>
194
+ <span v-if="!compact">Poder Executivo</span>
195
+ </template>
196
+ </TopNavBar>
197
+ </template>
198
+ ```
199
+
200
+ ### Menu e breadcrumbs
201
+
202
+ `sections` aceita uma árvore de profundidade arbitrária. Cada nó usa
203
+ `TopNavItem` (`id`, `label`, `to?`, `icon?`, `disabled?`, `children?`, `data?`).
204
+ Seções sem filhos são removidas e um `children: []` nunca cria um painel vazio.
205
+ No desktop, a primeira coluna do painel mestre–detalhe tem `16rem`; os itens de
206
+ cada nível são repartidos em colunas de `18rem`, no máximo dez por coluna.
207
+ Subníveis aparecem sempre à direita do nível de origem.
208
+
209
+ Breadcrumbs estão habilitados por padrão e começam por `Início / Navegação`.
210
+ Há duas formas de fornecer a trilha:
211
+
212
+ - informe `breadcrumbs` com itens `TopNavBreadcrumb`; `sectionId` liga o item a
213
+ uma seção e `menuId` liga a qualquer nó com filhos;
214
+ - omita `breadcrumbs` e informe `activeItemId`; a trilha é derivada da árvore.
215
+
216
+ Por exemplo, `calendario` dentro de `Programação` em `Planejamento` gera
217
+ `Início / Navegação / Planejamento / Programação / Calendário`. A rota ativa
218
+ não recebe fundo permanente nos menus desktop; somente hover e o ramo que está
219
+ sendo explorado recebem destaque.
220
+
221
+ ### Props
222
+
223
+ | Prop | Tipo / padrão | Finalidade |
224
+ |---|---|---|
225
+ | `sections` | `TopNavSection[]` / `[]` | Árvore de navegação já filtrada pela aplicação. |
226
+ | `breadcrumbs` | `TopNavBreadcrumb[]` / `[]` | Trilha explícita; vazia permite derivação por `activeItemId`. |
227
+ | `homeItem` | `TopNavItem` / `Início` | Item inicial emitido ao clicar em Início. |
228
+ | `navigationLabel` | `string` / `Navegação` | Rótulo do menu mestre. |
229
+ | `activeItemId` | `string` | Nó atual, usado no breadcrumb e no Drawer. |
230
+ | `searchable` | `boolean` / `false` | Habilita busca desktop e busca própria do Drawer. |
231
+ | `searchValue` | `string` | Valor opcionalmente controlado com `v-model:search-value`. |
232
+ | `searchResults` | `TopNavItem[]` | Resultados controlados; sem a prop, a árvore é filtrada localmente. |
233
+ | `searchPlaceholder` | `string` | Placeholder das duas buscas. |
234
+ | `showAia`, `showSupport` | `boolean` / `false` | Exibem as ações opcionais. |
235
+ | `aiaActive` | `boolean` / `false` | Estado visual do botão Aia. |
236
+ | `aiaLabel`, `supportLabel` | `string` | Textos acessíveis e rótulos do Drawer. |
237
+ | `actions` | `TopNavAction[]` / `[]` | Ações genéricas, como integrações, emitidas por `action`. |
238
+ | `user` | `TopNavUser` | Dados exclusivamente visuais do usuário. |
239
+ | `userMenuItems` | `TopNavUserMenuItem[]` | Opções emitidas por `user-action`; suporta separadores. |
240
+ | `favoriteItems` | `TopNavItem[]` / `[]` | Favoritos fornecidos pelo pai para dropdown e Drawer. |
241
+ | `showFavoriteToggle` | `boolean` / `false` | Exibe a estrela da página atual. |
242
+ | `favoriteActive` | `boolean` / `false` | Estado visual da estrela atual. |
243
+ | `mobileOpen` | `boolean` | Controle opcional com `v-model:mobile-open`. |
244
+ | `appearance` | `TopNavBarAppearance` | Tokens visuais locais descritos abaixo. |
245
+
246
+ ### Eventos
247
+
248
+ | Evento | Payload | Quando ocorre |
249
+ |---|---|---|
250
+ | `navigate` | `TopNavItem` | Início, menu, resultado ou favorito é selecionado. |
251
+ | `search` | `string` | A consulta muda no desktop ou no Drawer. |
252
+ | `update:searchValue` | `string` | Atualização de `v-model:search-value`. |
253
+ | `update:mobileOpen` | `boolean` | Atualização de `v-model:mobile-open`. |
254
+ | `toggle-aia` | — | A ação Aia é acionada. |
255
+ | `open-support` | — | A ação de suporte é acionada. |
256
+ | `toggle-favorite` | — | A estrela da página atual é acionada. |
257
+ | `action` | `TopNavAction` | Uma ação genérica/integração é acionada. |
258
+ | `user-action` | `TopNavUserMenuItem` | Uma opção de usuário é selecionada. |
259
+ | `user-click` | `TopNavUser \| undefined` | A área de usuário sem menu é acionada. |
260
+
261
+ `Ctrl+K` e `Cmd+K` abrem/focam a busca. Em telas menores que `1024px`, o
262
+ atalho abre primeiro o Drawer e foca a busca móvel. `Escape` fecha os painéis.
263
+
264
+ ### Slots
265
+
266
+ | Slot | Uso |
267
+ |---|---|
268
+ | `brand` | Marca; recebe `{ compact }`. |
269
+ | `context`, `scope` ou `client` | Contexto operacional; aliases com prioridade nessa ordem e `{ compact }`. |
270
+ | `context-compact` | Variante explícita usada no último estágio de overflow. |
271
+ | `actions` | Conteúdo adicional do cabeçalho; recebe `{ compact, close }`. |
272
+ | `aia-icon`, `support-icon` | Ícones customizados das ações quadradas. |
273
+ | `user` | Conteúdo do gatilho de usuário; recebe `{ user, compact }`. |
274
+ | `user-menu` | Painel de usuário; recebe `{ items, select }`. |
275
+ | `search-results` | Resultados customizados; recebe `{ items, select }`. |
276
+ | `breadcrumb-actions` | Ações adicionais no fim da segunda faixa. |
277
+ | `drawer-header` | Cabeçalho inteiro; recebe `{ close }`. |
278
+ | `drawer-brand` | Marca do Drawer; por padrão reutiliza `brand`. |
279
+ | `drawer-search` | Busca inteira; recebe `{ query, update, clear }`. |
280
+ | `drawer-actions` | Ações; recebe `{ actions, select, close }`. |
281
+ | `drawer-before-menu`, `drawer-after-menu` | Conteúdo antes/depois do trilho rolável. |
282
+ | `drawer-menu` | Substitui a árvore; recebe `{ sections, select, close }`. |
283
+ | `drawer-user` | Rodapé de usuário; recebe `{ user, open, toggle }`. |
284
+ | `drawer-footer` | Conteúdo final adicional; recebe `{ close }`. |
285
+
286
+ ### Faixa de favoritos
287
+
288
+ No desktop, `favoriteItems` aparece primeiro em uma faixa horizontal abaixo dos
289
+ breadcrumbs. A biblioteca mede uma cópia invisível da faixa com
290
+ `ResizeObserver`: se a largura real não couber, ela é substituída pelo dropdown
291
+ `Favoritos`. O mesmo dropdown é usado depois que a página passa de `120px` de
292
+ scroll e a faixa retorna apenas ao chegar a `24px` ou menos. Os valores evitam
293
+ oscilações perto do topo e reproduzem o comportamento do AppSidebar.
294
+
295
+ ### Aparência e responsividade
296
+
297
+ `appearance` é propositalmente pequeno e semântico. Use `colors` para
298
+ `navigation`, `text`, `mutedText`, `accent`, `border`, `menuText`,
299
+ `mobileActiveText` e `favoritesText`; `surfaces` para `breadcrumb`, `drawer`,
300
+ `drawerFooter`, `menu`, `menuHover`, `menuExplored`, `mobileActive`,
301
+ `favorites` e `currentPage`; `borders` para `menu` (a borda externa de 6px),
302
+ `menuOutline`, `mobileDivider`, `favorites` e `currentPage`; `shape` para
303
+ `radius`, `drawerRadius`, `shadow` e `favoritesShadow`; e `focus` para
304
+ `onDark` e `onLight`.
62
305
 
63
306
  ```ts
64
- import { createApp } from 'vue'
65
- import { TopSolutionsDesignSystem } from '@topjoao/top-design-system/plugin'
66
- import '@topjoao/top-design-system/style.css'
67
- import App from './App.vue'
68
-
69
- createApp(App).use(TopSolutionsDesignSystem).mount('#app')
70
- ```
71
-
72
- Após o registro, os componentes podem ser utilizados sem importação manual. A
73
- entrada `/plugin` também fornece as declarações globais usadas pela IDE.
74
-
75
- ## TopButton
76
-
77
- Exemplo
78
-
79
- ```vue
80
- <template>
81
- <TopButton label="Salvar" icon="pi pi-save" @click="salvar" />
82
- <TopButton label="Cancelar" secondary />
83
-
84
- <TopButton label="Consultar" outlined>
85
- <template #icon>
86
- <i class="pi pi-search" />
87
- </template>
88
- </TopButton>
89
- </template>
90
- ```
91
-
92
- | Prop | Tipo | Padrão |
93
- |---|---|---|
94
- | `tooltip` | `string` | `''` |
95
- | `tooltipClass` | `string` | `'text-xs'` |
96
- | `label` | `string` | `''` |
97
- | `icon` | `string` | `''` |
98
- | `loading` | `boolean` | `false` |
99
- | `class` | `string` | `''` |
100
- | `outlined` | `boolean` | `false` |
101
- | `secondary` | `boolean` | `false` |
102
- | `disabled` | `boolean` | `false` |
103
- | `unstyled` | `boolean` | `false` |
104
- | `type` | `'button' \| 'submit' \| 'reset'` | `'button'` |
105
-
106
- O componente emite `click` sem payload e oferece o slot nomeado `icon`.
107
-
108
- ## Desenvolvimento da biblioteca
109
-
110
- ```bash
111
- npm install
112
- npm run check
113
- ```
114
-
115
- O comando `check` executa verificação de tipos, testes e build.
116
-
117
- Para inspecionar o conteúdo que seria publicado:
118
-
119
- ```bash
120
- npm pack --dry-run
121
- ```
122
-
123
- Para gerar um pacote local instalável:
124
-
125
- ```bash
126
- npm pack
127
- ```
128
-
129
- Para publicar uma nova versão beta, primeiro altere a versão; versões já
130
- publicadas no npm não podem ser sobrescritas.
131
-
132
- ```bash
133
- npm version prerelease --preid=beta --no-git-tag-version
134
- npm publish --access public --tag beta
307
+ const appearance = {
308
+ colors: { navigation: '#09090b', text: '#f4f4f5', border: 'rgb(255 255 255 / 14%)' },
309
+ surfaces: { drawer: '#09090b', menu: '#18181b', menuHover: '#27272a' },
310
+ shape: { radius: '8px', shadow: '0 12px 30px rgb(0 0 0 / 25%)' },
311
+ }
135
312
  ```
313
+
314
+ A biblioteca não fixa tipografia inline. Por padrão, família, tamanho, peso e
315
+ altura de linha são herdados da aplicação consumidora. Além disso, `appearance`
316
+ só cria variáveis inline para propriedades que foram realmente informadas; os
317
+ valores fiéis ao AppSidebar existem apenas como *fallbacks* no CSS. Assim, um
318
+ tema global pode controlar o componente sem precisar usar `!important`:
319
+
320
+ ```css
321
+ :root {
322
+ --top-nav-font-family: var(--app-font-family);
323
+ --top-nav-font-size: var(--app-font-size);
324
+ --top-nav-strong-font-weight: 600;
325
+ --top-nav-action-size: 2.5rem;
326
+ --top-nav-header-padding: 0.625rem 1.5rem;
327
+ --top-nav-menu-item-padding: 0.5rem 0.75rem;
328
+ --top-nav-section-width: 16rem;
329
+ --top-nav-column-width: 18rem;
330
+ --top-nav-mobile-active-bg: #fff;
331
+ --top-nav-mobile-active-fg: #025a84;
332
+ --top-nav-mobile-divider: rgb(255 255 255 / 20%);
333
+ --top-nav-mobile-item-gap: 0.25rem;
334
+ --top-nav-mobile-submenu-padding: 0.5rem 0 0.375rem 0.35rem;
335
+ --top-nav-favorites-bg: #f8fafc;
336
+ --top-nav-favorites-fg: #0f172a;
337
+ --top-nav-favorites-border: #e2e8f0;
338
+ --top-nav-favorites-shadow: 0 1px 2px rgb(15 23 42 / 6%);
339
+ --top-nav-focus-ring: #dbeafe;
340
+ --top-nav-focus-ring-light: #1d4ed8;
341
+ --top-nav-current-bg: rgb(255 255 255 / 10%);
342
+ --top-nav-current-border: rgb(255 255 255 / 15%);
343
+ --top-nav-menu-scrollbar-thumb: #94a3b8;
344
+ }
345
+ ```
346
+
347
+ As variáveis globais também alcançam o Drawer teleportado. Use-as para ajustes
348
+ estruturais — tipografia, larguras, alturas, padding e espaçamentos do menu —
349
+ em vez de props no componente. Um valor passado por `appearance` tem precedência
350
+ local e não altera os demais tokens do tema.
351
+
352
+ O breakpoint móvel é `1024px`. No desktop, um `ResizeObserver` reaplica a mesma
353
+ sequência progressiva do AppSidebar: ações colapsam abaixo de `1440px`, busca
354
+ vira ícone abaixo de `1320px`, nome do usuário some abaixo de `1180px` e, se o
355
+ conteúdo ainda transbordar, o contexto recebe `compact: true`. O último estágio
356
+ também limita o contêiner do contexto a `3.25rem`; use `context-compact` quando
357
+ quiser controlar exatamente o que permanece visível.
358
+
359
+ Abaixo de `1024px`, a navegação desktop desaparece e o Drawer do PrimeVue assume.
360
+ Ele mantém cabeçalho, busca, ações, trilho translúcido rolável, árvore recursiva,
361
+ favoritos e usuário. A transição `menu-expand` existe somente dentro do Drawer;
362
+ os dropdowns desktop abrem sem animação e suas áreas de hover incluem o espaço
363
+ entre gatilho e painel. Nessa largura, os breadcrumbs deixam de rolar
364
+ horizontalmente: eles quebram em linhas e o bloco da página atual ocupa sua
365
+ própria linha para permanecer legível.
366
+
367
+ ## TopButton
368
+
369
+ Exemplo
370
+
371
+ ```vue
372
+ <template>
373
+ <TopButton label="Salvar" icon="pi pi-save" @click="salvar" />
374
+ <TopButton label="Cancelar" secondary />
375
+ <TopButton label="Excluir" severity="danger" />
376
+
377
+ <TopButton label="Consultar" outlined>
378
+ <template #icon>
379
+ <i class="pi pi-search" />
380
+ </template>
381
+ </TopButton>
382
+ </template>
383
+ ```
384
+
385
+ | Prop | Tipo | Padrão |
386
+ |---|---|---|
387
+ | `tooltip` | `string` | `''` |
388
+ | `tooltipClass` | `string` | `'text-xs'` |
389
+ | `label` | `string` | `''` |
390
+ | `icon` | `string` | `''` |
391
+ | `loading` | `boolean` | `false` |
392
+ | `class` | `string` | `''` |
393
+ | `outlined` | `boolean` | `false` |
394
+ | `severity` | `'primary' \| 'secondary' \| 'success' \| 'warn' \| 'danger'` | `'primary'` |
395
+ | `secondary` | `boolean` | `false` |
396
+ | `disabled` | `boolean` | `false` |
397
+ | `unstyled` | `boolean` | `false` |
398
+ | `type` | `'button' \| 'submit' \| 'reset'` | `'button'` |
399
+
400
+ O componente emite `click` sem payload e oferece o slot nomeado `icon`.
401
+ `secondary` permanece como atalho compatível para `severity="secondary"`.
402
+
403
+ ## TopConfirmDialog
404
+
405
+ Diálogo de confirmação baseado no `Dialog` do PrimeVue e nos botões do Design
406
+ System. A visibilidade é controlada por `v-model`; a aplicação consumidora
407
+ decide o que executar e quando encerrar após a confirmação.
408
+
409
+ ```vue
410
+ <script setup lang="ts">
411
+ import { ref } from 'vue'
412
+ import { TopConfirmDialog } from '@topjoao/top-design-system'
413
+
414
+ const showConfirm = ref(false)
415
+
416
+ function excluirRegistro() {
417
+ // Execute a ação e feche o diálogo quando apropriado.
418
+ showConfirm.value = false
419
+ }
420
+ </script>
421
+
422
+ <template>
423
+ <TopConfirmDialog
424
+ v-model="showConfirm"
425
+ title="Confirmar exclusão"
426
+ message="Deseja realmente excluir este registro?"
427
+ confirm-text="Excluir"
428
+ cancel-text="Cancelar"
429
+ @confirm="excluirRegistro"
430
+ />
431
+ </template>
432
+ ```
433
+
434
+ | Prop | Tipo | Padrão |
435
+ |---|---|---|
436
+ | `modelValue` | `boolean` | `false` |
437
+ | `title` | `string` | `'Confirmar'` |
438
+ | `message` | `string` | `''` |
439
+ | `confirmText` / `cancelText` | `string` | `'Confirmar'` / `'Cancelar'` |
440
+ | `loading` | `boolean` | `false` |
441
+ | `loadingText` | `string` | `'Processando...'` |
442
+ | `severity` | `'primary' \| 'danger'` | `'danger'` |
443
+ | `icon` | `string` | `'pi pi-exclamation-triangle'` |
444
+
445
+ Eventos: `update:modelValue`, `confirm`, `cancel` e `close`. O evento `confirm`
446
+ não fecha automaticamente o diálogo, permitindo que a aplicação aguarde uma
447
+ operação assíncrona. Os slots `message` e default permitem substituir a mensagem
448
+ textual.
449
+
450
+ ## TopDatePicker
451
+
452
+ Seletor de datas baseado no `DatePicker` do PrimeVue. O valor permanece como
453
+ `Date` (ou arrays de `Date` nos modos `multiple` e `range`); `dateFormat` altera
454
+ somente a apresentação no campo e não converte o `v-model` para texto.
455
+ No modo padrão (`single` com `dd/mm/yy`), a digitação recebe automaticamente a
456
+ máscara brasileira `dd/mm/aaaa`.
457
+
458
+ ```vue
459
+ <script setup lang="ts">
460
+ import { ref } from 'vue'
461
+ import { TopDatePicker } from '@topjoao/top-design-system'
462
+
463
+ const dataNascimento = ref<Date | null>(null)
464
+ </script>
465
+
466
+ <template>
467
+ <TopDatePicker
468
+ v-model="dataNascimento"
469
+ label="Data de nascimento"
470
+ placeholder="Selecione a data"
471
+ :max-date="new Date()"
472
+ required
473
+ error="Informe uma data válida."
474
+ @date-select="validarData"
475
+ />
476
+ </template>
477
+ ```
478
+
479
+ | Prop | Tipo | Padrão |
480
+ |---|---|---|
481
+ | `modelValue` | `Date \| Date[] \| (Date \| null)[] \| null` | `null` |
482
+ | `label` | `string` | `''` |
483
+ | `placeholder` | `string` | `'dd/mm/aaaa'` |
484
+ | `required` | `boolean` | `false` |
485
+ | `error` | `string` | `''` |
486
+ | `invalid` | `boolean` | `false` |
487
+ | `disabled` | `boolean` | `false` |
488
+ | `readonly` | `boolean` | `false` |
489
+ | `selectionMode` | `'single' \| 'multiple' \| 'range'` | `'single'` |
490
+ | `dateFormat` | `string` | `'dd/mm/yy'` |
491
+ | `minDate` / `maxDate` | `Date` | `undefined` |
492
+ | `showIcon` | `boolean` | `true` |
493
+ | `iconDisplay` | `'button' \| 'input'` | `'input'` |
494
+ | `manualInput` | `boolean` | `true` |
495
+ | `showButtonBar` | `boolean` | `false` |
496
+ | `appendTo` | `'body' \| 'self' \| HTMLElement` | `'body'` |
497
+
498
+ Eventos: `update:modelValue`, `input`, `change`, `date-select`, `show`, `hide`,
499
+ `today-click`, `clear-click`, `month-change`, `year-change`, `focus`, `blur` e
500
+ `keydown`. Os slots do `DatePicker` do PrimeVue são repassados pelo wrapper,
501
+ incluindo `date`, `header`, `footer`, `buttonbar`, `inputicon`, `dropdownicon`,
502
+ `previcon` e `nexticon`.
503
+
504
+ `required` adiciona o atributo nativo e o asterisco visual; a validação continua
505
+ sob responsabilidade da aplicação. `error` ativa o estado inválido, associa a
506
+ mensagem ao input com atributos ARIA e a exibe abaixo do campo. `readonly`
507
+ impede edição e seleção sem desabilitar o controle, enquanto `disabled` remove a
508
+ interação. No modo escuro, o campo usa os tokens públicos `--top-field-*`.
509
+
510
+ ## TopFileUpload
511
+
512
+ Seletor de arquivos baseado no `FileUpload` do PrimeVue. O componente valida e
513
+ apresenta os arquivos, mas não os envia: a aplicação consumidora controla o
514
+ upload por `v-model` e pelos eventos.
515
+
516
+ ```vue
517
+ <script setup lang="ts">
518
+ import { ref } from 'vue'
519
+ import { TopFileUpload } from '@topjoao/top-design-system'
520
+
521
+ const anexos = ref<File[]>([])
522
+
523
+ function enviarArquivos(files: File[]) {
524
+ // Envie os arquivos usando o serviço da aplicação.
525
+ }
526
+ </script>
527
+
528
+ <template>
529
+ <TopFileUpload
530
+ v-model="anexos"
531
+ label="Anexos"
532
+ accept=".pdf,image/*"
533
+ multiple
534
+ :max-file-size="5 * 1024 * 1024"
535
+ :max-files="5"
536
+ required
537
+ @select="enviarArquivos"
538
+ />
539
+ </template>
540
+ ```
541
+
542
+ | Prop | Tipo | Padrão |
543
+ |---|---|---|
544
+ | `modelValue` | `File \| File[] \| null` | `null` |
545
+ | `label` | `string` | `''` |
546
+ | `placeholder` | `string` | `'Arraste e solte o arquivo aqui'` |
547
+ | `required` | `boolean` | `false` |
548
+ | `disabled` | `boolean` | `false` |
549
+ | `accept` | `string` | `''` |
550
+ | `multiple` | `boolean` | `false` |
551
+ | `maxFileSize` | `number \| null` (bytes) | `null` |
552
+ | `maxFiles` | `number \| null` | `null` |
553
+ | `error` | `string` | `''` |
554
+ | `selectLabel` | `string` | `'Selecionar arquivo'` |
555
+ | `removeLabel` | `string` | `'Remover'` |
556
+ | `loading` | `boolean` | `false` |
557
+
558
+ Eventos: `update:modelValue`, `select`, `change`, `remove`, `clear` e `error`.
559
+ Erros de tipo, tamanho e quantidade possuem `code`, `message` e o `file`
560
+ relacionado. Os slots `empty` e `preview` permitem customizar a área vazia e a
561
+ pré-visualização. Os métodos `choose()` e `clear()` ficam disponíveis pela ref
562
+ do componente.
563
+
564
+ ## TopInputText
565
+
566
+ Campo textual baseado no `InputText` do PrimeVue. O `v-model` é sempre
567
+ `string`, inclusive para códigos, documentos e identificadores compostos
568
+ somente por dígitos; por exemplo, `"001234"` preserva os zeros à esquerda.
569
+
570
+ ```vue
571
+ <script setup lang="ts">
572
+ import { ref } from 'vue'
573
+ import { TopInputText } from '@topjoao/top-design-system'
574
+
575
+ const codigo = ref('001234')
576
+ </script>
577
+
578
+ <template>
579
+ <TopInputText
580
+ id="codigo"
581
+ v-model="codigo"
582
+ label="Código"
583
+ placeholder="Digite o código"
584
+ required
585
+ maxlength="10"
586
+ autocomplete="off"
587
+ />
588
+ </template>
589
+ ```
590
+
591
+ | Prop | Tipo | Padrão |
592
+ |---|---|---|
593
+ | `modelValue` | `string` | `''` |
594
+ | `label` | `string` | `''` |
595
+ | `placeholder` | `string` | `''` |
596
+ | `required` | `boolean` | `false` |
597
+ | `error` | `string` | `''` |
598
+ | `disabled` | `boolean` | `false` |
599
+ | `readonly` | `boolean` | `false` |
600
+
601
+ Atributos e eventos nativos adicionais, como `name`, `maxlength`,
602
+ `autocomplete`, `inputmode`, `pattern`, `aria-*`, `data-*`, `focus` e `blur`,
603
+ são repassados ao elemento `input` interno.
604
+
605
+ ## TopSelect
606
+
607
+ Seletor pesquisável baseado no `AutoComplete` do PrimeVue. Ele é genérico: a
608
+ aplicação fornece os itens, executa a busca e decide qualquer apresentação de
609
+ domínio por slots.
610
+
611
+ ```vue
612
+ <script setup lang="ts">
613
+ import { ref } from 'vue'
614
+ import { TopSelect } from '@topjoao/top-design-system'
615
+
616
+ const selectedCustomer = ref(null)
617
+ const customers = ref([])
618
+
619
+ function searchCustomers({ query }: { query: string }) {
620
+ // Atualize customers com o resultado da sua fonte de dados.
621
+ }
622
+ </script>
623
+
624
+ <template>
625
+ <TopSelect
626
+ v-model="selectedCustomer"
627
+ :options="customers"
628
+ option-label="name"
629
+ option-key="id"
630
+ option-prefix="code"
631
+ show-option-prefix
632
+ :loading="false"
633
+ @search="searchCustomers"
634
+ >
635
+ <template #icon="{ loading }">
636
+ <i :class="loading ? 'pi pi-spin pi-spinner' : 'pi pi-users'" />
637
+ </template>
638
+ <template #footer>
639
+ <button type="button">Criar cliente</button>
640
+ </template>
641
+ </TopSelect>
642
+ </template>
643
+ ```
644
+
645
+ | Prop | Tipo | Padrão |
646
+ |---|---|---|
647
+ | `options` | `array` | `[]` |
648
+ | `optionLabel` | `string \| function` | `'label'` |
649
+ | `optionKey` | `string` | `'id'` |
650
+ | `optionPrefix` | `string` | `''` |
651
+ | `showOptionPrefix` | `boolean` | `false` |
652
+ | `showSelectedPrefix` | `boolean` | `false` |
653
+ | `loading` / `disabled` / `invalid` | `boolean` | `false` |
654
+ | `placeholder` | `string` | `'Search...'` |
655
+ | `minQueryLength` | `number` | `1` |
656
+ | `multiple` / `forceSelection` | `boolean` | `false` / `true` |
657
+ | `panelWidth` / `scrollHeight` | `string` | `null` / `'250px'` |
658
+ | `emptyMessage` / `loadingMessage` | `string` | mensagens padrão em inglês |
659
+ | `closeOnSelect` | `boolean` | `false` |
660
+
661
+ Eventos: `update:modelValue`, `search`, `loadMore`, `clear`, `select` e
662
+ `change`.
663
+
664
+ Slots: `icon`, `option`, `selected-item`, `chip`, `empty`, `option-group` e
665
+ `footer`. O slot `icon` recebe `loading`; sem ele, o componente mostra uma lupa
666
+ ou um indicador de carregamento. Quando um slot não é informado, o componente usa sua apresentação padrão.
667
+ O texto das opções é limitado visualmente pela largura disponível do painel,
668
+ sem corte por quantidade fixa de caracteres; o tooltip padrão exibe o valor
669
+ completo quando `showTooltip` está ativo.
670
+
671
+ ## TopTabs
672
+
673
+ Navegação em abas baseada em `Tabs`, `TabList`, `Tab`, `TabPanels` e `TabPanel`
674
+ do PrimeVue, com estrutura simplificada e estilos dos temas claro e escuro do
675
+ Design System.
676
+
677
+ ```vue
678
+ <script setup lang="ts">
679
+ import { ref } from 'vue'
680
+ import {
681
+ TopTabs,
682
+ type TopTabItem,
683
+ type TopTabValue,
684
+ } from '@topjoao/top-design-system'
685
+
686
+ const activeTab = ref<TopTabValue>('dados')
687
+ const tabs: TopTabItem[] = [
688
+ { value: 'dados', label: 'Dados' },
689
+ { value: 'documentos', label: 'Documentos' },
690
+ { value: 'auditoria', label: 'Auditoria', disabled: true },
691
+ ]
692
+ </script>
693
+
694
+ <template>
695
+ <TopTabs v-model="activeTab" :tabs="tabs">
696
+ <template #dados>
697
+ Dados gerais do processo
698
+ </template>
699
+
700
+ <template #documentos>
701
+ Documentos anexados
702
+ </template>
703
+
704
+ <template #auditoria>
705
+ Histórico de auditoria
706
+ </template>
707
+ </TopTabs>
708
+ </template>
709
+ ```
710
+
711
+ | Prop | Tipo | Padrão |
712
+ |---|---|---|
713
+ | `modelValue` | `string \| number` | obrigatório |
714
+ | `tabs` | `TopTabItem[]` | obrigatório |
715
+ | `lazy` | `boolean` | `false` |
716
+ | `scrollable` | `boolean` | `true` |
717
+
718
+ Cada `TopTabItem` possui `value`, `label` e `disabled?`. O `value` deve ser
719
+ único e identifica tanto a seleção quanto o slot do painel; por exemplo,
720
+ `value: 'documentos'` utiliza `#documentos`. Cada slot recebe `tab` e `active`.
721
+ O componente emite somente `update:modelValue`. Atributos adicionais, incluindo
722
+ as opções de passthrough do PrimeVue, são repassados ao componente `Tabs`.
723
+
724
+ ## Desenvolvimento da biblioteca
725
+
726
+ ```bash
727
+ npm install
728
+ npm run check
729
+ ```
730
+
731
+ O comando `check` executa verificação de tipos, testes e build.
732
+
733
+ Para inspecionar o conteúdo que seria publicado:
734
+
735
+ ```bash
736
+ npm pack --dry-run
737
+ ```
738
+
739
+ ## Playground visual
740
+
741
+ O Storybook permite testar os componentes isoladamente e consultar seus
742
+ exemplos. Ele é uma dependência de desenvolvimento e não é incluído no pacote
743
+ publicado.
744
+
745
+ ```bash
746
+ npm run storybook
747
+ ```
748
+
749
+ Abra `http://localhost:6006` para acessar as histórias de `TopButton`,
750
+ `TopConfirmDialog`, `TopDatePicker`, `TopInputText`, `TopSelect` e `TopTabs`. Use o botão de
751
+ contraste na barra superior para alternar o preview entre tema claro e escuro.
752
+ Para gerar a versão estática da documentação, execute:
753
+
754
+ ```bash
755
+ npm run build-storybook
756
+ ```
757
+
758
+ Para gerar um pacote local instalável:
759
+
760
+ ```bash
761
+ npm pack
762
+ ```
763
+
764
+ Para publicar uma nova versão beta, primeiro altere a versão; versões já
765
+ publicadas no npm não podem ser sobrescritas.
766
+
767
+ ```bash
768
+ npm version prerelease --preid=beta --no-git-tag-version
769
+ npm publish --access public --tag beta
770
+ ```