@seniorsistemas/angular-components-mcp 1.0.0-beta.3 → 1.0.0-beta.4

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.
Files changed (42) hide show
  1. package/dist/data/component-rules/button.md +37 -0
  2. package/dist/data/component-rules/checkbox.md +30 -0
  3. package/dist/data/component-rules/index.json +3 -0
  4. package/dist/data/component-technical/confirm-dialog.md +117 -0
  5. package/dist/data/component-technical/dialog.md +202 -0
  6. package/dist/data/component-technical/dynamic-form.md +292 -0
  7. package/dist/data/component-technical/index.json +4 -0
  8. package/dist/data/component-technical/kanban.md +116 -0
  9. package/dist/data/component-technical/loading-state.md +96 -0
  10. package/dist/data/component-technical/sidebar.md +82 -0
  11. package/dist/data/component-technical/table.md +19 -0
  12. package/dist/data/component-technical/toast.md +63 -0
  13. package/dist/data/components-metadata.json +3467 -0
  14. package/dist/data/dynamic-form-metadata.json +2272 -0
  15. package/dist/data/ux-rules.md +45 -0
  16. package/dist/handlers/handleDetectRequiredProviders.d.ts +12 -0
  17. package/dist/handlers/handleDetectRequiredProviders.d.ts.map +1 -0
  18. package/dist/handlers/handleGetComponentDetails.d.ts +13 -0
  19. package/dist/handlers/handleGetComponentDetails.d.ts.map +1 -0
  20. package/dist/handlers/handleGetComponentRules.d.ts +13 -0
  21. package/dist/handlers/handleGetComponentRules.d.ts.map +1 -0
  22. package/dist/handlers/handleGetComponentRulesIndex.d.ts +11 -0
  23. package/dist/handlers/handleGetComponentRulesIndex.d.ts.map +1 -0
  24. package/dist/handlers/handleGetComponentTechnicalGuide.d.ts +13 -0
  25. package/dist/handlers/handleGetComponentTechnicalGuide.d.ts.map +1 -0
  26. package/dist/handlers/handleGetComponentTechnicalIndex.d.ts +11 -0
  27. package/dist/handlers/handleGetComponentTechnicalIndex.d.ts.map +1 -0
  28. package/dist/handlers/handleGetComponents.d.ts +14 -0
  29. package/dist/handlers/handleGetComponents.d.ts.map +1 -0
  30. package/dist/handlers/handleGetDynamicFormTypes.d.ts +13 -0
  31. package/dist/handlers/handleGetDynamicFormTypes.d.ts.map +1 -0
  32. package/dist/handlers/handleGetUxRules.d.ts +10 -0
  33. package/dist/handlers/handleGetUxRules.d.ts.map +1 -0
  34. package/dist/handlers.d.ts +112 -0
  35. package/dist/handlers.d.ts.map +1 -0
  36. package/dist/index.cjs +16703 -0
  37. package/dist/index.cjs.map +1 -0
  38. package/dist/index.d.ts +3 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.mjs +16700 -0
  41. package/dist/index.mjs.map +1 -0
  42. package/package.json +1 -1
@@ -0,0 +1,37 @@
1
+ # Button — Regras de UX
2
+
3
+ **Seletor:** `s-button`
4
+ **Package:** `@seniorsistemas/angular-components/button`
5
+
6
+ ---
7
+
8
+ ## Variantes
9
+
10
+ | Variante | Quando usar |
11
+ | ----------- | ---------------------------------------------------------------------------- |
12
+ | `primary` | Ação principal da página — máximo 1 por página/diálogo |
13
+ | `secondary` | Ações complementares à ação principal |
14
+ | `link` | Navegação secundária ou ações de baixa importância visual |
15
+ | `select` | Quando houver menu de ações — obrigatório usar ícone de chevron pré-definido |
16
+
17
+ ---
18
+
19
+ ## Regras
20
+
21
+ ### ⛔ Hierarquia (error)
22
+
23
+ Use no máximo 1 botão `primary` por página/diálogo. Os demais devem ser `secondary` ou `link`.
24
+
25
+ ### ⚠️ Ícones (warning)
26
+
27
+ Use no máximo 1 ícone por botão (esquerda ou direita). Nunca 2 ícones no mesmo botão.
28
+
29
+ ### ⛔ Tamanho `small` (error)
30
+
31
+ Permitido **somente** em células de tabela e section title.
32
+ Proibido em formulários, navegação primária ou qualquer outro contexto.
33
+
34
+ ### ⛔ Labels
35
+
36
+ Use labels acionáveis e específicos. Prefira `"Salvar Cadastro"` a `"OK"`.
37
+
@@ -0,0 +1,30 @@
1
+ # Checkbox — Regras de UX
2
+
3
+ ## Variantes
4
+
5
+ | Variante | Quando usar |
6
+ | -------------- | --------------------------------------------------------------- |
7
+ | **Horizontal** | Quando todos os itens possuem textos curtos ou palavras únicas. |
8
+ | **Vertical** | Quando um ou mais itens possuem textos longos. |
9
+
10
+ ---
11
+
12
+ ## Regras de uso
13
+
14
+ - **SEMPRE** use alinhamento vertical em telas pequenas.
15
+ - Cada checkbox deve possuir um label descrevendo a sua ação.
16
+ - Coloque opções importantes no topo.
17
+
18
+ ## ⛔ Regras de erro
19
+
20
+ - Não use checkbox para escolhas mutuamente exclusivas. Nesses casos use Radio Button.
21
+ - Não use checkbox para ações que se aplicam imediatamente e alteram a interface em tempo real — isso é papel do Switch.
22
+ - Não apresente checkbox horizontalmente em dispositivos móveis.
23
+ - Não utilize checkbox sem um label claro e explícito.
24
+ - Não agrupe muitas opções horizontalmente. Neste caso use alinhamento vertical.
25
+
26
+ ---
27
+
28
+ ## ⚠️ Regras de recomendação
29
+
30
+ - Prefira usar checkbox em formulários ou contextos de escolha múltipla de forma persistente.
@@ -0,0 +1,3 @@
1
+ {
2
+ "components": ["button", "checkbox"]
3
+ }
@@ -0,0 +1,117 @@
1
+ # ConfirmDialog — Guia Técnico
2
+
3
+ **Seletor:** `s-confirm-dialog`
4
+ **Package:** `@seniorsistemas/angular-components/confirm-dialog`
5
+
6
+ ## Exports e Imports
7
+
8
+ | Tipo | Símbolo |
9
+ | ------- | ---------------------- |
10
+ | Service | `ConfirmDialogService` |
11
+ | Type | `ConfirmDialog` |
12
+
13
+ ```typescript
14
+ import { ConfirmDialogService } from '@seniorsistemas/angular-components/confirm-dialog';
15
+ import type { ConfirmDialog } from '@seniorsistemas/angular-components/confirm-dialog';
16
+ ```
17
+
18
+ > **Erro comum:** `Module has no exported member 'ConfirmDialogModule'` — use `ConfirmDialogService` diretamente; não existe `ConfirmDialogModule` público nesta versão.
19
+
20
+ ---
21
+
22
+ O ConfirmDialog é **SEMPRE** utilizado via `ConfirmDialogService`. O componente `s-confirm-dialog` **não deve ser instanciado diretamente** no template — ele é renderizado internamente pelo serviço.
23
+
24
+ ---
25
+
26
+ ## Padrão único: Via `ConfirmDialogService` ✅ ÚNICO PADRÃO SUPORTADO
27
+
28
+ Abre um dialog de confirmação com mensagem, título e botões configuráveis. Retorna uma `Promise<boolean>` que resolve com `true` se o usuário confirmar e `false` se cancelar.
29
+
30
+ ### Regras obrigatórias
31
+
32
+ - **NUNCA** adicionar `<s-confirm-dialog>` diretamente no template — o serviço gerencia isso automaticamente
33
+ - Importar `ConfirmDialogModule` no módulo ou componente raiz para registrar o componente
34
+
35
+ ### Exemplo
36
+
37
+ ```typescript
38
+ import { ConfirmDialogModule, ConfirmDialogService } from '@seniorsistemas/angular-components/confirm-dialog';
39
+
40
+ @Component({
41
+ imports: [ConfirmDialogModule],
42
+ template: `
43
+ <s-button
44
+ label="Deletar"
45
+ (clicked)="delete()"
46
+ />
47
+ `,
48
+ })
49
+ export class MyComponent {
50
+ private readonly confirmDialog = inject(ConfirmDialogService);
51
+
52
+ protected async delete(): Promise<void> {
53
+ const confirmed = await this.confirmDialog.open({
54
+ header: 'Confirmar exclusão',
55
+ message: 'Tem certeza que deseja excluir este registro? Esta ação não pode ser desfeita.',
56
+ acceptLabel: 'Excluir',
57
+ rejectLabel: 'Cancelar',
58
+ });
59
+
60
+ if (confirmed) {
61
+ // prosseguir com a exclusão
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ ---
68
+
69
+ ## Notas de integração
70
+
71
+ - `ConfirmDialogService.open()` retorna `Promise<boolean>`: use `async/await` ou `.then()` para aguardar a resposta do usuário.
72
+ - Personalize os labels dos botões via `acceptLabel` e `rejectLabel` para dar clareza à ação (ex: `'Excluir'` ao invés de `'Ok'`).
73
+
74
+ ---
75
+
76
+ ## Erros Comuns
77
+
78
+ ### `Property 'open' does not exist on type 'ConfirmDialogService'`
79
+
80
+ **Tipo:** method
81
+ **Solução:** Use `confirm()` em vez de `open()`.
82
+
83
+ ```typescript
84
+ // ❌ Incorreto
85
+ await this.confirmDialogService.open({ ... })
86
+
87
+ // ✅ Correto
88
+ this.confirmDialogService.confirm({ ... })
89
+ ```
90
+
91
+ > `ConfirmDialogService` desta versão expõe o método `confirm(confirmDialog)`.
92
+
93
+ ---
94
+
95
+ ### `Type 'Promise<boolean>' is not assignable...`
96
+
97
+ **Tipo:** return-type
98
+ **Solução:** Não use `await` no retorno de `confirm()`; o fluxo ocorre via callbacks `accept`/`reject`.
99
+
100
+ ```typescript
101
+ // ❌ Incorreto
102
+ const ok = await confirmDialogService.confirm(config);
103
+
104
+ // ✅ Correto
105
+ confirmDialogService.confirm({
106
+ ...config,
107
+ accept: () => {
108
+ /* ação confirmada */
109
+ },
110
+ reject: () => {
111
+ /* ação cancelada */
112
+ },
113
+ });
114
+ ```
115
+
116
+ > O método `confirm()` não retorna `Promise` nesta implementação.
117
+
@@ -0,0 +1,202 @@
1
+ # Dialog — Guia Técnico
2
+
3
+ **Seletor:** `s-dialog`
4
+ **Package:** `@seniorsistemas/angular-components/dialog`
5
+
6
+ ## Exports e Imports
7
+
8
+ | Tipo | Símbolo |
9
+ | --------- | ---------------------------- |
10
+ | Component | `DialogComponent` |
11
+ | Service | `DialogService` |
12
+ | Class | `ActiveDialog` |
13
+ | Types | `DialogOptions`, `DialogRef` |
14
+
15
+ ```typescript
16
+ import { DialogComponent } from '@seniorsistemas/angular-components/dialog';
17
+ import { DialogService } from '@seniorsistemas/angular-components/dialog';
18
+ import { ActiveDialog } from '@seniorsistemas/angular-components/dialog';
19
+ import type { DialogOptions, DialogRef } from '@seniorsistemas/angular-components/dialog';
20
+ ```
21
+
22
+ > **Erros comuns:**
23
+ >
24
+ > - `Module has no exported member 'DialogModule'` → use `DialogComponent` standalone
25
+ > - `Module has no exported member 'DynamicDialogRef'` → use `DialogRef` e `ActiveDialog`
26
+ > - `Module has no exported member 'DIALOG_DATA'` → use padrão `ActiveDialog`/`DialogRef`
27
+
28
+ ---
29
+
30
+ O Dialog pode ser controlado de forma declarativa via binding de propriedade ou de forma programática via `DialogService`. A forma de uso deve ser escolhida conforme o contexto de onde o dialog será aberto.
31
+
32
+ ---
33
+
34
+ ## Padrão 1: Declarativo via `[(visible)]`
35
+
36
+ **Preferido:** Não (use apenas para casos simples)
37
+
38
+ O estado de abertura é controlado por uma propriedade booleana no componente pai, vinculada via two-way binding `[(visible)]`.
39
+
40
+ ### Exemplo
41
+
42
+ ```typescript
43
+ import { DialogModule } from '@seniorsistemas/angular-components/dialog';
44
+
45
+ @Component({
46
+ imports: [DialogModule],
47
+ template: `
48
+ <s-button
49
+ label="Abrir"
50
+ (clicked)="isOpen = true"
51
+ />
52
+ <s-dialog
53
+ [(visible)]="isOpen"
54
+ header="Título"
55
+ >
56
+ <ng-template sTemplate="body"> Conteúdo do dialog </ng-template>
57
+ <ng-template sTemplate="footer">
58
+ <s-button
59
+ label="Fechar"
60
+ (clicked)="isOpen = false"
61
+ />
62
+ </ng-template>
63
+ </s-dialog>
64
+ `,
65
+ })
66
+ export class MyComponent {
67
+ protected isOpen = false;
68
+ }
69
+ ```
70
+
71
+ ---
72
+
73
+ ## Padrão 2: Programático via `DialogService` ✅ PREFERIDO
74
+
75
+ O dialog é aberto injetando o `DialogService`, que cria e renderiza o componente dinamicamente. Permite abrir dialogs de qualquer lugar da aplicação, inclusive de serviços.
76
+
77
+ ### Exemplo
78
+
79
+ ```typescript
80
+ import { DialogComponent, DialogService } from '@seniorsistemas/angular-components/dialog';
81
+
82
+
83
+ // No componente ou serviço que abre o dialog
84
+ @Component({ ... })
85
+ export class MyComponent {
86
+ private readonly dialogService = inject(DialogService);
87
+
88
+ protected openDialog(): void {
89
+ this.dialogService.open(MyDialogContentComponent, {
90
+ header: 'Título do Dialog',
91
+ data: { id: 1 }
92
+ });
93
+ }
94
+ }
95
+
96
+ // Componente de conteúdo do dialog
97
+ @Component({
98
+ imports: [DialogComponent, TemplateModule]
99
+ template: `
100
+ <s-dialog header="Dialog Header">
101
+ <!-- TODO: Campo opcional, utilizar quando [header] não for provido, caso queira HTML Customizado para o header. -->
102
+ <ng-template sTemplate="header"></ng-template>
103
+
104
+ <p>Id recebido: {{ data.id }}</p>
105
+ <!-- TODO: Campo opcional. -->
106
+ <ng-template
107
+ sTemplate="footer"
108
+ let-activeDialog="activeDialog">
109
+ <div class="grid grid-cols-12">
110
+ <div class="col-span-12 flex justify-around">
111
+ <s-button
112
+ label="Ação terciária"
113
+ priority="link"
114
+ />
115
+ <s-button
116
+ (clicked)="activeDialog.dismiss('Dismissed dialog demo footer')"
117
+ label="Ação secundária"
118
+ />
119
+ <s-button
120
+ label="Ação primária"
121
+ (clicked)="activeDialog.close('Closed dialog demo footer')"
122
+ priority="primary"
123
+ />
124
+ </div>
125
+ </div>
126
+ </ng-template>
127
+
128
+ </s-dialog>
129
+
130
+ `
131
+ })
132
+ export class MyDialogContentComponent {
133
+ readonly activeDialog = inject(ActiveDialog);
134
+
135
+ // Função opcional, caso utilize let-activeDialog no ng-template nao precisa fechar por aqui, ambos possuem o mesmo valor de activeDialog
136
+ protected close(): void {
137
+ this.activeDialog.close('resultado opcional');
138
+ }
139
+ }
140
+ ```
141
+
142
+ ---
143
+
144
+ ## Notas de integração
145
+
146
+ - O `<s-dialog>` deve existir no template para que o `DialogService` possa renderizar conteúdo nele. Coloque-o em um componente de layout de nível superior.
147
+ - Para receber dados no componente de conteúdo, use `inject(DIALOG_DATA)` importado de `@seniorsistemas/angular-components/dialog`.
148
+ - Para fechar o dialog de dentro do componente de conteúdo, injete `DynamicDialogRef` e chame `ref.close(resultado)`.
149
+
150
+ ---
151
+
152
+ ## Erros Comuns
153
+
154
+ ### `Module has no exported member 'DialogModule'`
155
+
156
+ **Tipo:** import
157
+ **Solução:** Use standalone import com `DialogComponent`.
158
+
159
+ ```typescript
160
+ // ❌ Incorreto
161
+ imports: [DialogModule];
162
+
163
+ // ✅ Correto
164
+ imports: [DialogComponent];
165
+ ```
166
+
167
+ > Nesta versão, o public API expõe `DialogComponent` para uso declarativo.
168
+
169
+ ---
170
+
171
+ ### `Property 'onClose' does not exist on type 'DialogRef'`
172
+
173
+ **Tipo:** property
174
+ **Solução:** Use o observable `closed`.
175
+
176
+ ```typescript
177
+ // ❌ Incorreto
178
+ ref.onClose.subscribe(...)
179
+
180
+ // ✅ Correto
181
+ ref.closed.subscribe(...)
182
+ ```
183
+
184
+ > `DialogRef` expõe `closed` e `dismissed`.
185
+
186
+ ---
187
+
188
+ ### `Object literal may only specify known properties, and 'header' does not exist in type 'DialogOptions'`
189
+
190
+ **Tipo:** parameter
191
+ **Solução:** Use apenas as `DialogOptions` suportadas (`destroyClickOutside`, `escapeOnEsc`, `size`).
192
+
193
+ ```typescript
194
+ // ❌ Incorreto
195
+ dialogService.open(MyDialogComponent, { header: 'Título' });
196
+
197
+ // ✅ Correto
198
+ dialogService.open(MyDialogComponent, { size: 'md' });
199
+ ```
200
+
201
+ > `DialogOptions` nesta versão não inclui `header`/`data` no tipo público.
202
+
@@ -0,0 +1,292 @@
1
+ # DynamicForm — Guia Técnico
2
+
3
+ **Seletor:** `s-dynamic-form`
4
+ **Package:** `@seniorsistemas/angular-components/dynamic-form`
5
+
6
+ ## Exports e Imports
7
+
8
+ | Tipo | Símbolo |
9
+ | ---------- | ---------------------------------------------------------------------------------------- |
10
+ | Components | `DynamicFormComponent`, `DynamicFormDirective`, `FieldLabelComponent`, `LookupComponent` |
11
+ | Classes | `DynamicFormRegistry` |
12
+ | Types | `DynamicStructure`, `FieldConfig`, `DynamicType`, `FieldType`, `FieldTypeMap` |
13
+
14
+ ```typescript
15
+ import { DynamicFormComponent } from '@seniorsistemas/angular-components/dynamic-form';
16
+ import { DynamicFormModule } from '@seniorsistemas/angular-components/dynamic-form';
17
+ import type { DynamicStructure } from '@seniorsistemas/angular-components/dynamic-form';
18
+ ```
19
+
20
+ ---
21
+
22
+ ## Regra de estruturas
23
+
24
+ - Use `row` quando os campos não precisam de título visual agrupando-os. É o tipo padrão.
25
+ - **TODOS** os campos do formulário devem ir na **mesma `row`**. Só crie uma estrutura adicional se for necessário separar campos em um agrupamento com título próprio.
26
+ - `section` **OBRIGATORIAMENTE** deve ter `header`. Nunca use `section` sem `header`.
27
+ - Dentro de `section` **SEMPRE** deve haver outra `DynamicStructure` (`row` ou `fieldset`). Campos diretos em `section` não são permitidos.
28
+ - **NUNCA** aninhe `section` dentro de `section`.
29
+ - **Antes de agrupar campos**, pergunte ao usuário qual estrutura ele quer usar para organizar o formulário: `row` (sem título), `section` (nova seção com título), ou `fieldset` (agrupamento semântico com título).
30
+
31
+ ### Exemplo comum (sem seção)
32
+
33
+ ```typescript
34
+ protected configs: DynamicStructure[] = [
35
+ {
36
+ type: 'row',
37
+ fields: [
38
+ { name: 'name', type: 'string', label: 'Nome', required: () => true, size: { xl: 6 } },
39
+ { name: 'email', type: 'string', label: 'E-mail', required: () => true, size: { xl: 6 } },
40
+ ],
41
+ },
42
+ ];
43
+ ```
44
+
45
+ > **OBRIGATÓRIO:** Para cada campo em `fields`, o `FormGroup` **deve ter um `FormControl` declarado explicitamente com o mesmo valor de `name`**. O DynamicForm usa esse nome internamente como `formControlName` para fazer o vínculo — ele NÃO cria os controles automaticamente.
46
+ > No exemplo acima, o `FormGroup` deve ser criado com: `this.fb.group({ name: [], email: [] })`.
47
+
48
+ ### Exemplo com seção (quando há nova seção)
49
+
50
+ ```typescript
51
+ protected configs: DynamicStructure[] = [
52
+ {
53
+ type: 'section',
54
+ header: 'Dados Pessoais',
55
+ configs: [
56
+ {
57
+ type: 'row',
58
+ fields: [
59
+ { name: 'name', type: 'string', label: 'Nome', required: () => true, size: { xl: 6 } },
60
+ { name: 'email', type: 'string', label: 'E-mail', required: () => true, size: { xl: 6 } },
61
+ ],
62
+ },
63
+ ],
64
+ },
65
+ ];
66
+ ```
67
+
68
+ ### Exemplo com `fieldset` para agrupamento
69
+
70
+ ```typescript
71
+ protected configs: DynamicStructure[] = [
72
+ {
73
+ type: 'row',
74
+ fields: [
75
+ { name: 'fullName', type: 'string', label: 'Nome completo', size: { xl: 12 } },
76
+ ],
77
+ },
78
+ {
79
+ type: 'fieldset',
80
+ header: 'Endereço',
81
+ configs: [
82
+ {
83
+ type: 'row',
84
+ fields: [
85
+ { name: 'zipCode', type: 'string', label: 'CEP', size: { xl: 4 }, mask: '000000-000' },
86
+ { name: 'street', type: 'string', label: 'Rua', size: { xl: 8 } },
87
+ ],
88
+ },
89
+ ],
90
+ },
91
+ ];
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Padrão 1: Com `FormGroup` externo ✅ PADRÃO OBRIGATÓRIO
97
+
98
+ O DynamicForm **NÃO cria nem gerencia seu próprio FormGroup**. O `FormGroup` deve ser criado externamente no componente e passado via `[form]`. **Para cada campo declarado em `configs`, deve existir um `FormControl` no `FormGroup` com o mesmo valor de `name`** — o DynamicForm usa esse nome como `formControlName` internamente.
99
+
100
+ ### Exemplo
101
+
102
+ ```typescript
103
+ import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
104
+ import { DynamicFormModule, DynamicStructure } from '@seniorsistemas/angular-components/dynamic-form';
105
+
106
+ @Component({
107
+ imports: [DynamicFormModule, ReactiveFormsModule],
108
+ template: `
109
+ <s-dynamic-form
110
+ [form]="form"
111
+ [configs]="configs"
112
+ />
113
+ <s-button
114
+ label="Salvar"
115
+ priority="primary"
116
+ [disabled]="form.invalid"
117
+ (clicked)="save()"
118
+ />
119
+ `,
120
+ })
121
+ export class MyComponent implements OnInit {
122
+ private readonly fb = inject(FormBuilder);
123
+
124
+ protected form!: FormGroup;
125
+ protected configs: DynamicStructure[] = [
126
+ {
127
+ type: 'row',
128
+ fields: [
129
+ { name: 'name', type: 'string', label: 'Nome', required: () => true, size: { xl: 6 } },
130
+ { name: 'email', type: 'string', label: 'E-mail', required: () => true, size: { xl: 6 } },
131
+ { name: 'birthDate', type: 'date', label: 'Data de Nascimento', size: { xl: 4 } },
132
+ ],
133
+ },
134
+ ];
135
+
136
+ public ngOnInit(): void {
137
+ // Cada FormControl deve ter o mesmo nome que o `name` do campo em configs
138
+ this.form = this.fb.group({
139
+ name: [],
140
+ email: [],
141
+ birthDate: [],
142
+ });
143
+ }
144
+
145
+ protected save(): void {
146
+ if (this.form.valid) {
147
+ console.log(this.form.value);
148
+ }
149
+ }
150
+ }
151
+ ```
152
+
153
+ ---
154
+
155
+ ## Notas de integração
156
+
157
+ - O `FormGroup` **DEVE** ser criado antes do DynamicForm renderizar, com um `FormControl` para cada campo. Exemplo: `this.fb.group({ fieldName: [], otherField: [] })`.
158
+ - Cada campo em `fields` **DEVE** ter `name` definido. Para cada `name`, **declare explicitamente um `FormControl` no `FormGroup` com o mesmo nome** — o DynamicForm usa esse nome como `formControlName` internamente e não cria controles automaticamente.
159
+ - Exemplo: campos com `name: 'name'` e `name: 'email'` exigem `this.fb.group({ name: [], email: [] })`.
160
+ - No array `fields`, a propriedade `required` deve ser uma função que retorna boolean (`() => boolean`).
161
+ - Para campos opcionais com providers específicos (ex: `editor`), use a tool `detect_required_providers` para identificar o provider necessário e onde adicioná-lo.
162
+ - Use `size` com breakpoints customizados (`xs`, `sm`, `md`, `lg`, `xl`, `xxl`, `big`) — **NÃO use breakpoints padrão do Tailwind**.
163
+ - **NUNCA** use `s-select`, `s-checkbox`, `s-text-area` ou outros campos individualmente — sempre prefira `s-dynamic-form`.
164
+
165
+ ---
166
+
167
+ ## Criando um tipo de campo customizado
168
+
169
+ > ⚠️ **ATENÇÃO: uso pouco frequente.** Criar um tipo custom do zero é trabalhoso e só deve ser feito quando não existe nenhum tipo nativo que atenda à necessidade e nenhuma lib externa com provider já disponível. **Antes de criar, pergunte ao usuário:**
170
+ >
171
+ > 1. Já existe algum tipo custom implementado no projeto (ex: em `optional-fields/` ou equivalente)?
172
+ > 2. Existe alguma biblioteca/pacote que o projeto já usa que fornece um provider pronto para o DynamicForm?
173
+
174
+ ### Passo 1 — Criar a interface de configuração e o provider
175
+
176
+ Crie um arquivo `<nome>-field.ts` no diretório do seu campo customizado.
177
+ A interface deve estender `FieldConfig`, fazer o module augmentation registrando o novo tipo em `FieldTypeMap`, e exportar uma função `provide<Nome>Field()` que registra o componente no `DynamicFormRegistry`.
178
+
179
+ ```typescript
180
+ // my-custom-field.ts
181
+ import { APP_INITIALIZER, EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';
182
+ import { FieldConfig, DynamicFormRegistry } from '@seniorsistemas/angular-components/dynamic-form';
183
+ import { MyCustomFieldComponent } from './my-custom-field/my-custom-field.component';
184
+
185
+ /** Configuração do campo customizado. */
186
+ export interface MyCustomFieldConfig extends FieldConfig {
187
+ type: 'myCustom';
188
+ // adicione aqui as propriedades específicas do seu campo
189
+ someOption?: string;
190
+ }
191
+
192
+ // Augmentation: registra o novo tipo no mapa do DynamicForm
193
+ declare module '@seniorsistemas/angular-components/dynamic-form' {
194
+ export interface FieldTypeMap {
195
+ myCustom: MyCustomFieldConfig;
196
+ }
197
+ }
198
+
199
+ /** Registra o componente do campo customizado no DynamicForm. */
200
+ export function provideMyCustomField(): EnvironmentProviders {
201
+ return makeEnvironmentProviders([
202
+ {
203
+ provide: APP_INITIALIZER,
204
+ useFactory: () => () => {
205
+ DynamicFormRegistry.registerField(MyCustomFieldComponent, 'myCustom');
206
+ },
207
+ multi: true,
208
+ },
209
+ ]);
210
+ }
211
+ ```
212
+
213
+ ### Passo 2 — Criar o componente Angular
214
+
215
+ Crie um componente standalone que implementa `BaseFieldComponentConfig`.
216
+ Requisitos obrigatórios:
217
+
218
+ - Ser `standalone: true`
219
+ - Implementar `BaseFieldComponentConfig`
220
+ - Ter o `input` de `field` (tipado com a interface criada no passo 1)
221
+ - Ter o `input` de `formControl` (necessário quando o campo modifica o `FormGroup`, que é quase sempre)
222
+ - Importar e **renderizar `FieldLabelComponent` como primeira tag no template** — responsável por exibir o label, tooltip e infoSign do campo
223
+
224
+ ```typescript
225
+ // my-custom-field/my-custom-field.component.ts
226
+ import { Component, input } from '@angular/core';
227
+ import { FormControl, ReactiveFormsModule } from '@angular/forms';
228
+ import { BaseFieldComponentConfig, FieldLabelComponent } from '@seniorsistemas/angular-components/dynamic-form';
229
+ import { MyCustomFieldConfig } from '../my-custom-field';
230
+
231
+ @Component({
232
+ standalone: true,
233
+ imports: [ReactiveFormsModule, FieldLabelComponent /* + outros imports necessários */],
234
+ templateUrl: './my-custom-field.component.html',
235
+ })
236
+ export class MyCustomFieldComponent implements BaseFieldComponentConfig {
237
+ /** Config do campo injetada pelo DynamicForm. */
238
+ field = input.required<MyCustomFieldConfig>();
239
+
240
+ /** FormControl associado ao campo no FormGroup pai. */
241
+ formControl = input.required<FormControl>();
242
+ }
243
+ ```
244
+
245
+ ```html
246
+ <!-- my-custom-field/my-custom-field.component.html -->
247
+ @let _field = field();
248
+
249
+ <!-- OBRIGATÓRIO: sempre a primeira tag do template -->
250
+ <s-field-label [field]="_field"></s-field-label>
251
+
252
+ <!-- Implemente aqui o input/controle visual do campo -->
253
+ <input
254
+ [formControl]="formControl()"
255
+ [placeholder]="_field.placeholder ?? ''"
256
+ />
257
+ ```
258
+
259
+ ### Passo 3 — Registrar o provider no AppConfig ou AppModule
260
+
261
+ O provider gerado no Passo 1 deve ser adicionado ao `providers` do `app.config.ts` (projetos standalone) ou ao `providers` do `AppModule` (projetos com módulos).
262
+
263
+ ```typescript
264
+ // app.config.ts (projetos standalone)
265
+ import { provideMyCustomField } from './my-custom-field/my-custom-field';
266
+
267
+ export const appConfig: ApplicationConfig = {
268
+ providers: [
269
+ // ... outros providers
270
+ provideMyCustomField(),
271
+ ],
272
+ };
273
+ ```
274
+
275
+ Após registrar o provider, o tipo `'myCustom'` estará disponível na configuração do DynamicForm:
276
+
277
+ ```typescript
278
+ configs: DynamicStructure[] = [
279
+ {
280
+ type: 'section',
281
+ configs: [
282
+ {
283
+ type: 'row',
284
+ fields: [
285
+ { type: 'myCustom', name: 'meuCampo', label: 'Meu Campo', someOption: 'valor' }
286
+ ]
287
+ }
288
+ ]
289
+ }
290
+ ];
291
+ ```
292
+
@@ -0,0 +1,4 @@
1
+ {
2
+ "components": ["dialog", "confirm-dialog", "toast", "sidebar", "loading-state", "dynamic-form", "kanban", "table"]
3
+ }
4
+