@praxisui/manual-form 1.0.0-beta.7 → 2.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,62 @@
1
+ ---
2
+ title: "Manual Form"
3
+ slug: "manual-form-overview"
4
+ description: "Visao geral do @praxisui/manual-form com formularios declarativos, autodeteccao de campos, autosave, persistencia e hot update de metadata."
5
+ doc_type: "reference"
6
+ document_kind: "component-overview"
7
+ component: "manual-form"
8
+ category: "components"
9
+ audience:
10
+ - "frontend"
11
+ - "host"
12
+ - "architect"
13
+ level: "intermediate"
14
+ status: "active"
15
+ owner: "praxis-ui"
16
+ tags:
17
+ - "manual-form"
18
+ - "forms"
19
+ - "autosave"
20
+ - "metadata-editor"
21
+ - "dynamic-fields"
22
+ order: 38
23
+ icon: "file-pen"
24
+ toc: true
25
+ sidebar: true
26
+ search_boost: 0.95
27
+ reading_time: 14
28
+ estimated_setup_time: 20
29
+ version: "1.0"
30
+ related_docs:
31
+ - "manual-form-api-reference"
32
+ - "host-manual-form-integration"
33
+ - "manual-form-toolbar-execution"
34
+ - "consumer-integration-quickstart"
35
+ - "host-integration-guide"
36
+ keywords:
37
+ - "praxis manual form"
38
+ - "manual form toolkit"
39
+ - "form autosave"
40
+ - "field metadata bridge"
41
+ last_updated: "2026-03-07"
42
+ ---
43
+
1
44
  # Manual Form Toolkit
2
45
 
46
+ Biblioteca voltada a formularios declarativos baseados em componentes `pdx-*`, com suporte a persistencia, autodeteccao de campos e ponte com editores de metadata.
47
+
48
+ ## Documentation
49
+
50
+ - Documentação oficial: https://praxisui.dev
51
+ - Aplicação de referência: https://github.com/codexrodrigues/praxis-ui-quickstart
52
+ - Indicado para: equipes que precisam montar formularios Praxis manualmente sem perder consistencia, autosave e governanca
53
+
54
+ ## When to use
55
+
56
+ - Declarar formularios manualmente com componentes `pdx-*` sem abrir mao de infraestrutura
57
+ - Adicionar autosave, persistencia e hot update de metadata ao fluxo do host
58
+ - Reduzir o atrito entre formularios manuais e o restante do ecossistema Praxis UI
59
+
3
60
  ## Instalação
4
61
 
5
62
  ```bash
@@ -35,6 +92,74 @@ O exemplo `/cargos/manual-form` demonstra uso declarativo:
35
92
  </praxis-manual-form>
36
93
  ```
37
94
 
95
+ ### Padrão didático para exemplos renderizados
96
+
97
+ Para páginas de documentação que precisam mostrar o componente antes de blocos longos de texto,
98
+ use o wrapper `praxis-manual-form-doc-example`. Ele já entrega:
99
+
100
+ - Banner curto com foco no exemplo live.
101
+ - Tabs para alternar entre `Live`, `Template`, `TS` e `Config`.
102
+ - Toggle de modo de customização (liga/desliga) com evento.
103
+ - Botão de cópia do snippet ativo.
104
+ - Callouts opcionais de `IMPORTANTE`, `NOTA` e `DICA`.
105
+
106
+ ```html
107
+ <praxis-manual-form-doc-example
108
+ title="Cadastro de cargo"
109
+ subtitle="Preview primeiro, explicações depois."
110
+ level="Intermediario"
111
+ bannerTitle="Veja o componente em ação"
112
+ bannerDescription="Explore o live e abra código apenas quando necessário."
113
+ [templateCode]="templateSnippet"
114
+ [tsCode]="tsSnippet"
115
+ [configCode]="configSnippet"
116
+ [customizationEnabled]="editMode()"
117
+ (customizationEnabledChange)="editMode.set($event)"
118
+ important="Persista o draft no submit."
119
+ note="A aba Live abre por padrão."
120
+ tip="Use a aba Config para explicar contratos JSON."
121
+ >
122
+ <div exampleLive>
123
+ <praxis-manual-form
124
+ [formGroup]="form"
125
+ formId="cargo-manual-form"
126
+ [enableCustomization]="editMode()"
127
+ >
128
+ <pdx-text-input formControlName="nome" label="Nome"></pdx-text-input>
129
+ </praxis-manual-form>
130
+ </div>
131
+
132
+ <div exampleDiagram>
133
+ <!-- Mermaid, SVG ou fluxo simplificado -->
134
+ </div>
135
+ </praxis-manual-form-doc-example>
136
+ ```
137
+
138
+ Também existe um showcase pronto para consumo direto:
139
+
140
+ ```html
141
+ <praxis-manual-form-doc-example-showcase></praxis-manual-form-doc-example-showcase>
142
+ ```
143
+
144
+ Esse showcase já inclui providers locais de `API_URL` e `ASYNC_CONFIG_STORAGE`, evitando erro de injeção em páginas de documentação.
145
+
146
+ ### Diagrama de funcionamento (alto nível)
147
+
148
+ ```mermaid
149
+ flowchart LR
150
+ Host[Template com <pdx-*>] --> ManualForm[praxis-manual-form]
151
+ ManualForm --> Detect[Detecta campos]
152
+ Detect --> Meta[Infere FieldMetadata]
153
+ Meta --> Group[Cria/Adota FormGroup]
154
+ Group --> Render[Renderiza campos + validações]
155
+ ManualForm --> Instance[ManualFormInstance]
156
+ Instance --> Storage[ConfigStorage]
157
+ ManualForm -->|editMode| Editor[Metadata Editor]
158
+ Editor --> Patch[Aplica patch]
159
+ Patch --> Instance
160
+ Instance --> Persist[saveDraft / resetToSeed]
161
+ ```
162
+
38
163
  ### Editor de metadados integrado
39
164
 
40
165
  Use o `ManualFieldMetadataBridgeService` para abrir o editor visual a partir de qualquer campo detectado pelo container. O serviço carrega o módulo sob demanda, aplica o patch retornado e chama `saveDraft()` para persistir as alterações no runtime.
@@ -58,6 +183,24 @@ export class CargoManualFormComponent {
58
183
  }
59
184
  ```
60
185
 
186
+ ### Toolbar de campo (modo de edicao)
187
+
188
+ Quando `enableCustomization` estiver ativo, o manual-form exibe uma toolbar flutuante ao clicar
189
+ no campo. Ela permite alternar `required`, `readOnly`, `hidden` e `disabled`, e abrir o editor
190
+ completo de metadados.
191
+
192
+ Atalhos de teclado (quando o campo esta focado):
193
+ - `F2` ou `Alt+F10` abre a toolbar e move o foco para o primeiro botao.
194
+ - `ESC` fecha a toolbar.
195
+
196
+ Observacao: se um componente de campo interromper o clique (stopPropagation), a toolbar nao
197
+ abrira via mouse. Use os atalhos de teclado ou evite bloquear o click no host do campo.
198
+
199
+ Token de z-index:
200
+ - `--pdx-manual-toolbar-z` (default: `2001`).
201
+ Classe de overlay (para temas corporativos):
202
+ - `.pdx-manual-toolbar-panel` (aplicada no CDK Overlay para customizacoes).
203
+
61
204
  ## Motivações
62
205
 
63
206
  - DX coerente para formulários manuais (sem JSON).
@@ -93,7 +236,7 @@ Essa base permite que hosts manualmente criem formulários com componentes Praxi
93
236
  ## Padrão corporativo recomendado
94
237
 
95
238
  - Host tipado: o host deve criar um `FormGroup` tipado e passá‑lo via `[formGroup]`. O container adota esse grupo, aplica metadados/validators e evita interceptações internas. Isso habilita o Angular Language Service a validar `formControlName` e melhora a DX.
96
- - Modo dinâmico: se nenhum `[formGroup]` for informado, o container cria e gerencia um `FormGroup` interno (útil para PoCs), sem garantia de validação estática de nomes.
239
+ - Modo dinâmico: opção para PoCs e exemplos rápidos; veja os detalhes na seção “Guia rápido”.
97
240
 
98
241
  ## Configurações avançadas
99
242
 
@@ -104,37 +247,44 @@ Essa base permite que hosts manualmente criem formulários com componentes Praxi
104
247
  - Autosave
105
248
  - `@Input() enableAutoSave: boolean` (padrão: `true`) e `@Input() autoSaveDebounceMs: number` (padrão: `800`).
106
249
 
107
- ## SSR (ConfigStorage sem localStorage)
250
+ ### API pública adicional
108
251
 
109
- Em SSR, `localStorage` não existe. Como `praxis-manual-form` apenas depende do token `CONFIG_STORAGE`, você pode prover uma implementação segura no servidor (ex.: in‑memory) e manter `LocalStorageConfigService` no browser.
252
+ - `ManualFormInstance.metadataChanges()`
253
+ - Emite sempre que o mapa de `FieldMetadata` é atualizado no runtime.
254
+ - Timing: após `patchFieldMetadata()`, `replaceConfig()`, `resetToSeed()` e cargas de config persistida.
255
+
256
+ ## SSR (AsyncConfigStorage sem localStorage)
257
+
258
+ Em SSR, `localStorage` não existe. Como `praxis-manual-form` depende do token `ASYNC_CONFIG_STORAGE`, você pode prover uma implementação segura no servidor (ex.: in‑memory) e manter `LocalStorageAsyncAdapter` no browser.
110
259
 
111
260
  Exemplo (Angular com `app.config.server.ts`):
112
261
 
113
262
  ```ts
114
263
  // app.config.server.ts
115
264
  import { ApplicationConfig, PLATFORM_ID, inject, isPlatformServer } from '@angular/core';
116
- import { CONFIG_STORAGE, LocalStorageConfigService, type ConfigStorage } from '@praxisui/core';
265
+ import { ASYNC_CONFIG_STORAGE, LocalStorageAsyncAdapter, type AsyncConfigStorage } from '@praxisui/core';
266
+ import { EMPTY, of } from 'rxjs';
117
267
 
118
- class MemoryConfigStorage implements ConfigStorage {
268
+ class MemoryAsyncStorage implements AsyncConfigStorage {
119
269
  private readonly map = new Map<string, any>();
120
- loadConfig<T>(key: string): T | null { return (this.map.get(key) as T) ?? null; }
121
- saveConfig<T>(key: string, config: T): void { this.map.set(key, config); }
122
- clearConfig(key: string): void { this.map.delete(key); }
270
+ loadConfig<T>(key: string) { return of((this.map.get(key) as T) ?? null); }
271
+ saveConfig<T>(key: string, config: T) { this.map.set(key, config); return EMPTY; }
272
+ clearConfig(key: string) { this.map.delete(key); return EMPTY; }
123
273
  }
124
274
 
125
275
  export const appConfig: ApplicationConfig = {
126
276
  providers: [
127
277
  {
128
- provide: CONFIG_STORAGE,
278
+ provide: ASYNC_CONFIG_STORAGE,
129
279
  useFactory: () => isPlatformServer(inject(PLATFORM_ID))
130
- ? new MemoryConfigStorage()
131
- : inject(LocalStorageConfigService),
280
+ ? new MemoryAsyncStorage()
281
+ : inject(LocalStorageAsyncAdapter),
132
282
  },
133
283
  ],
134
284
  };
135
285
  ```
136
286
 
137
- Alternativas no servidor: usar cookies, cache distribuído (ex.: Redis) ou persistir por sessão do usuário. Basta implementar a interface `ConfigStorage`.
287
+ Alternativas no servidor: usar cookies, cache distribuído (ex.: Redis) ou persistir por sessão do usuário. Basta implementar a interface `AsyncConfigStorage`.
138
288
 
139
289
  ### Exemplo (nested form)
140
290
 
@@ -192,6 +342,14 @@ export class MinhaPagina {
192
342
  </praxis-manual-form>
193
343
  ```
194
344
 
345
+ Observações importantes:
346
+ - Aplique `[formGroup]` diretamente no seletor `<praxis-manual-form>` (no host).
347
+ - Evite envolver o componente com um `<form [formGroup]>` externo; não há necessidade e pode causar forms aninhados.
348
+
349
+ Modo dinâmico (sem host tipado):
350
+ - Quando nenhum `[formGroup]` é informado no host, o container renderiza internamente um `<form [formGroup]="formGroup">` para atender o Angular Forms e evitar o erro NG01050.
351
+ - Isso dispensa qualquer “shim” externo para os exemplos dinâmicos.
352
+
195
353
  ## API do ManualFormComponent (resumo)
196
354
 
197
355
  - Inputs (sinais):
@@ -200,7 +358,7 @@ export class MinhaPagina {
200
358
  - `actions?: FormActionsLayout | null`
201
359
  - `showHeader = true`, `showActions = true`
202
360
  - `enableAutoSave = true`, `autoSaveDebounceMs = 800`
203
- - `editModeEnabled = false` (habilita ações de customização — ex.: abrir editor por duplo clique)
361
+ - `enableCustomization = false` (habilita ações de customização — ex.: abrir editor por duplo clique)
204
362
  - `persistenceOptions?: { namespace?, tenantId?, profileId?, locale? }`
205
363
  - `usePathNames = false` (usa `FormControlName.path` como nome do campo)
206
364
  - Outputs (sinais):
@@ -208,12 +366,12 @@ export class MinhaPagina {
208
366
  - `metadataChange(FormConfig)`
209
367
 
210
368
  - Métodos públicos:
211
- - `tryOpenFieldEditor(fieldName: string)`: tenta abrir o editor do campo, respeitando `editModeEnabled` (no‑op quando `false`).
369
+ - `tryOpenFieldEditor(fieldName: string)`: tenta abrir o editor do campo, respeitando `enableCustomization` (no‑op quando `false`).
212
370
 
213
371
  ## Header e Actions
214
372
 
215
373
  - `<praxis-manual-form-header>`: recebe `instance`, `title`, `description`, `saveLabel`, `resetLabel` e emite `save/reset`.
216
- - Quando `editModeEnabled` está ativo no container, o header exibe um botão “Editar formulário” que emite `editForm`; o container trata esse evento chamando `openFormEditor()`.
374
+ - Quando `enableCustomization` está ativo no container, o header exibe um botão “Editar formulário” que emite `editForm`; o container trata esse evento chamando `openFormEditor()`.
217
375
  - `<praxis-manual-form-actions>`: recebe `actions: FormActionsLayout`, emite `actionClick` e aceita `trackByFn` opcional para listas grandes.
218
376
 
219
377
  ## Editor de Metadados (visual)
@@ -227,34 +385,48 @@ openEditor(fieldName: string) {
227
385
  }
228
386
  ```
229
387
 
230
- Integração com Settings Panel: o `ManualFormComponent` utiliza `SettingsPanelService` para abrir o editor do formulário (lista de campos e flags) quando `editModeEnabled` está ativo. É possível abrir programaticamente via `manualForm.openFormEditor()`.
388
+ Integração com Settings Panel: o `ManualFormComponent` utiliza `SettingsPanelService` para abrir o editor do formulário (lista de campos e flags) quando `enableCustomization` está ativo. É possível abrir programaticamente via `manualForm.openFormEditor()`.
231
389
 
232
390
  ### Duplo clique com modo de edição
233
391
 
234
- Para espelhar o comportamento das demais libs (permitir edição apenas quando o modo de edição está ativo), use o input `editModeEnabled` no container e a diretiva `pdxManualEdit` nos campos:
392
+ Para espelhar o comportamento das demais libs (permitir edição apenas quando o modo de edição está ativo), use o input `enableCustomization` no container e a diretiva `pdxManualEdit` nos campos:
235
393
 
236
394
  ```html
237
- <praxis-manual-form formId="cargo" [editModeEnabled]="custom.enabled()">
395
+ <praxis-manual-form formId="cargo" [enableCustomization]="custom.enabled()">
238
396
  <pdx-text-input formControlName="nome" label="Nome" pdxManualEdit="nome"></pdx-text-input>
239
397
  <pdx-material-currency formControlName="salario" label="Salário" pdxManualEdit="salario"></pdx-material-currency>
240
398
  </praxis-manual-form>
241
399
  ```
242
400
 
243
- A diretiva chama `manualForm.tryOpenFieldEditor(fieldName)` e respeita `editModeEnabled`, de modo que o duplo clique só abre o editor quando a edição está ligada.
401
+ A diretiva chama `manualForm.tryOpenFieldEditor(fieldName)` e respeita `enableCustomization`, de modo que o duplo clique só abre o editor quando a edição está ligada.
244
402
 
245
403
  ### Editor do Formulário (lista de campos)
246
404
 
247
- O `ManualFormComponent` expõe `openFormEditor()` para abrir um editor simples do formulário que lista todos os campos e permite alternar: visibilidade (mostrar/ocultar), obrigatório, somente leitura e desabilitado — facilitando encontrar campos ocultos e reexibi‑los, além de ajustes rápidos. O editor respeita `editModeEnabled` e usa o `SettingsPanelService`.
405
+ O `ManualFormComponent` expõe `openFormEditor()` para abrir um editor simples do formulário que lista todos os campos e permite alternar: visibilidade (mostrar/ocultar), obrigatório, somente leitura e desabilitado — facilitando encontrar campos ocultos e reexibi‑los, além de ajustes rápidos. O editor respeita `enableCustomization` e usa o `SettingsPanelService`.
248
406
 
249
407
  Exemplo de uso no host:
250
408
 
251
409
  ```html
252
410
  <button *ngIf="custom.enabled()" type="button" (click)="manualForm?.openFormEditor()">Editar formulário</button>
253
- <praxis-manual-form #manualForm [formGroup]="form" formId="'cargo'" [editModeEnabled]="custom.enabled()">
411
+ <praxis-manual-form #manualForm [formGroup]="form" formId="'cargo'" [enableCustomization]="custom.enabled()">
254
412
  <!-- campos -->
255
413
  </praxis-manual-form>
256
414
  ```
257
415
 
416
+ #### O que o editor cobre (tabs)
417
+
418
+ O editor do formulario (Settings Panel) expõe:
419
+ - **Campos**: visibilidade, obrigatorio, somente leitura, desabilitado.
420
+ - **Acoes**: layout da barra, botoes padrao e acoes customizadas.
421
+ - **Mensagens**: feedbacks de sucesso/erro, loading e confirmacoes.
422
+ - **Comportamento**: flags de UX (ex.: focusFirstError, scrollToErrors).
423
+ - **Dicas**: textos auxiliares para modos de tela (i18n/UX).
424
+ - **Hooks**: JSON com hooks de lifecycle (antes/depois de init/submit).
425
+ - **Regras**: regras de layout/visibilidade (JSON).
426
+ - **Cascatas**: dependencias entre campos via metadados.
427
+
428
+ Obs.: a execucao de hooks/regras depende do host registrar os providers correspondentes.
429
+
258
430
  ## Extensibilidade por DI
259
431
 
260
432
  - Customize a inferência de `FieldControlType` via tokens:
@@ -271,11 +443,11 @@ Exemplo de uso no host:
271
443
 
272
444
  ## ManualFieldEditorOnDblclickDirective (`pdxManualEdit`)
273
445
 
274
- - Usa o duplo clique para abrir o editor do campo, respeitando `editModeEnabled` do `ManualFormComponent`.
446
+ - Usa o duplo clique para abrir o editor do campo, respeitando `enableCustomization` do `ManualFormComponent`.
275
447
  - Apenas funciona quando aplicada a elementos dentro de `<praxis-manual-form>`.
276
448
 
277
449
  ```html
278
- <praxis-manual-form formId="cargo" [editModeEnabled]="custom.enabled()">
450
+ <praxis-manual-form formId="cargo" [enableCustomization]="custom.enabled()">
279
451
  <pdx-text-input formControlName="nome" label="Nome" pdxManualEdit="nome"></pdx-text-input>
280
452
  </praxis-manual-form>
281
453
  ```
@@ -285,7 +457,7 @@ Exemplo de uso no host:
285
457
  - Preferir host tipado e `usePathNames=true` para nested forms.
286
458
  - Habilitar autosave com debounce adequado.
287
459
  - Evitar lógica pesada em templates; delegar inferências ao container.
288
- - No SSR, sempre prover `CONFIG_STORAGE` compatível (sem localStorage).
460
+ - No SSR, sempre prover `ASYNC_CONFIG_STORAGE` compatível (sem localStorage).
289
461
 
290
462
  ## Licença
291
463
 
@@ -297,6 +469,11 @@ Apache-2.0 — consulte `LICENSE` neste pacote ou no repositório raiz.
297
469
  - Layout é responsabilidade do host; não há reordenação automática por metadados.
298
470
  - Modo dinâmico sem `[formGroup]` não habilita validação estática de `formControlName` no IDE.
299
471
 
472
+ ## Notas adicionais
473
+
474
+ - Quando `enableCustomization` estiver ativo, o container renderiza o assistente AI (Praxis AI) no header do formulario.
475
+ - `componentInstanceId` ajuda a compor a chave de persistencia (com `formId` e `persistenceOptions`), evitando conflito entre instancias da mesma tela.
476
+
300
477
  ## Comparativo — Manual vs Dinâmico (JSON)
301
478
 
302
479
  - Manual: controle máximo do HTML; evolui por metadados incrementais; ótimo para telas estáveis com design específico.