@praxisui/manual-form 9.0.0-beta.1 → 9.0.0-beta.2
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 +76 -445
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Manual Form"
|
|
3
3
|
slug: "manual-form-overview"
|
|
4
|
-
description: "
|
|
4
|
+
description: "Public npm documentation for @praxisui/manual-form: declarative HTML forms with Praxis field metadata, autosave, persistence and metadata editor bridge."
|
|
5
5
|
doc_type: "reference"
|
|
6
6
|
document_kind: "component-overview"
|
|
7
7
|
component: "manual-form"
|
|
@@ -24,7 +24,7 @@ icon: "file-pen"
|
|
|
24
24
|
toc: true
|
|
25
25
|
sidebar: true
|
|
26
26
|
search_boost: 0.95
|
|
27
|
-
reading_time:
|
|
27
|
+
reading_time: 5
|
|
28
28
|
estimated_setup_time: 20
|
|
29
29
|
version: "1.0"
|
|
30
30
|
related_docs:
|
|
@@ -38,492 +38,123 @@ keywords:
|
|
|
38
38
|
- "manual form toolkit"
|
|
39
39
|
- "form autosave"
|
|
40
40
|
- "field metadata bridge"
|
|
41
|
-
last_updated: "2026-
|
|
41
|
+
last_updated: "2026-06-16"
|
|
42
42
|
---
|
|
43
43
|
|
|
44
|
-
#
|
|
44
|
+
# @praxisui/manual-form
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
`@praxisui/manual-form` lets a host write forms in Angular templates with `pdx-*` fields while still using Praxis metadata, autosave, persisted drafts, runtime field editing and the metadata editor bridge.
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
Use it when the host must own the HTML layout. Prefer `@praxisui/dynamic-form` when the whole screen should be driven by JSON metadata.
|
|
49
49
|
|
|
50
|
-
|
|
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
|
|
50
|
+
## Install
|
|
53
51
|
|
|
54
|
-
|
|
52
|
+
```bash
|
|
53
|
+
npm i @praxisui/manual-form@latest
|
|
54
|
+
```
|
|
55
55
|
|
|
56
|
-
|
|
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
|
|
56
|
+
Peer dependencies:
|
|
59
57
|
|
|
60
|
-
|
|
58
|
+
- `@angular/common`, `@angular/core`, `@angular/forms`, `@angular/router`, `@angular/cdk`, `@angular/material` `^21.0.0`
|
|
59
|
+
- `@praxisui/core`, `@praxisui/dynamic-fields`, `@praxisui/settings-panel`, `@praxisui/metadata-editor`, `@praxisui/ai` `^9.0.0-beta.1`
|
|
60
|
+
- `rxjs` `~7.8.0`
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
npm install @praxisui/manual-form@latest
|
|
64
|
-
```
|
|
62
|
+
## Recommended Typed Host
|
|
65
63
|
|
|
66
|
-
|
|
64
|
+
Apply `[formGroup]` directly on `<praxis-manual-form>`. This lets Angular Language Service validate `formControlName` while the Praxis container detects fields and manages metadata.
|
|
67
65
|
|
|
68
66
|
```ts
|
|
69
|
-
import { Component } from '@angular/core';
|
|
70
|
-
import { ReactiveFormsModule } from '@angular/forms';
|
|
67
|
+
import { Component, inject } from '@angular/core';
|
|
68
|
+
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
|
|
71
69
|
import { ManualFormComponent } from '@praxisui/manual-form';
|
|
72
70
|
|
|
73
71
|
@Component({
|
|
74
|
-
selector: 'app-
|
|
72
|
+
selector: 'app-job-form',
|
|
75
73
|
standalone: true,
|
|
76
74
|
imports: [ReactiveFormsModule, ManualFormComponent],
|
|
77
75
|
template: `
|
|
78
|
-
<praxis-manual-form formId="cargo" formTitle="Cargo">
|
|
79
|
-
<!-- Declare pdx-* fields owned by @praxisui/dynamic-fields here. -->
|
|
80
|
-
</praxis-manual-form>
|
|
81
|
-
`,
|
|
82
|
-
})
|
|
83
|
-
export class ManualFormHostComponent {}
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
Peers necessários (instale no app host):
|
|
87
|
-
- `@angular/core` `^20.1.0`, `@angular/common` `^20.1.0`
|
|
88
|
-
- `@praxisui/core` (ConfigStorage, GenericCrudService, ícones)
|
|
89
|
-
- `@praxisui/dynamic-fields` (componentes de campo `pdx-*`)
|
|
90
|
-
- `@praxisui/settings-panel` (integração com painel de configurações)
|
|
91
|
-
- `@praxisui/metadata-editor` (edição visual de campos, opcional)
|
|
92
|
-
|
|
93
|
-
## Problema
|
|
94
|
-
|
|
95
|
-
Montar formulários Praxis manualmente (usando `<pdx-*>`) exigia montar `FormConfig`, `FormGroup`, persistência e metadados manualmente. Isso gerava atrito para hosts e dificultava a evolução.
|
|
96
|
-
|
|
97
|
-
## Solução
|
|
98
|
-
|
|
99
|
-
Criamos a biblioteca `@praxisui/manual-form` com:
|
|
100
|
-
|
|
101
|
-
- **ManualFormComponent**: container que detecta campos, infere `FieldMetadata`, cria o `FormGroup`, mantém hot update e persiste via `ManualFormInstance`. Aceita inputs `formId`, `formTitle`, `actions`, `enableAutoSave` etc.
|
|
102
|
-
- **ManualFormInstanceFactory**: runtime que cuida da persistência (`createManualFormSeed`, `saveDraft`, `resetToSeed`).
|
|
103
|
-
- **ManualFormHeader/Actions**: componentes auxiliares para título/descritivo e ações (`submit/cancel/reset/custom`).
|
|
104
|
-
- **ManualFieldMetadataBridgeService**: integra com o `@praxisui/metadata-editor`, aplica patches e garante persistência via `ManualFormInstance`.
|
|
105
|
-
|
|
106
|
-
O exemplo `/cargos/manual-form` demonstra uso declarativo:
|
|
107
|
-
|
|
108
|
-
```html
|
|
109
|
-
<praxis-manual-form formId="cargo">
|
|
110
|
-
<pdx-text-input formControlName="nome" label="Nome" required></pdx-text-input>
|
|
111
|
-
<pdx-material-currency formControlName="salario" label="Salário"></pdx-material-currency>
|
|
112
|
-
</praxis-manual-form>
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
### Padrão didático para exemplos renderizados
|
|
116
|
-
|
|
117
|
-
Para páginas de documentação que precisam mostrar o componente antes de blocos longos de texto,
|
|
118
|
-
use o wrapper `praxis-manual-form-doc-example`. Ele já entrega:
|
|
119
|
-
|
|
120
|
-
- Banner curto com foco no exemplo live.
|
|
121
|
-
- Tabs para alternar entre `Live`, `Template`, `TS` e `Config`.
|
|
122
|
-
- Toggle de modo de customização (liga/desliga) com evento.
|
|
123
|
-
- Botão de cópia do snippet ativo.
|
|
124
|
-
- Callouts opcionais de `IMPORTANTE`, `NOTA` e `DICA`.
|
|
125
|
-
|
|
126
|
-
```html
|
|
127
|
-
<praxis-manual-form-doc-example
|
|
128
|
-
title="Cadastro de cargo"
|
|
129
|
-
subtitle="Preview primeiro, explicações depois."
|
|
130
|
-
level="Intermediario"
|
|
131
|
-
bannerTitle="Veja o componente em ação"
|
|
132
|
-
bannerDescription="Explore o live e abra código apenas quando necessário."
|
|
133
|
-
[templateCode]="templateSnippet"
|
|
134
|
-
[tsCode]="tsSnippet"
|
|
135
|
-
[configCode]="configSnippet"
|
|
136
|
-
[customizationEnabled]="editMode()"
|
|
137
|
-
(customizationEnabledChange)="editMode.set($event)"
|
|
138
|
-
important="Persista o draft no submit."
|
|
139
|
-
note="A aba Live abre por padrão."
|
|
140
|
-
tip="Use a aba Config para explicar contratos JSON."
|
|
141
|
-
>
|
|
142
|
-
<div exampleLive>
|
|
143
76
|
<praxis-manual-form
|
|
144
77
|
[formGroup]="form"
|
|
145
|
-
formId="
|
|
146
|
-
|
|
78
|
+
formId="job-form"
|
|
79
|
+
formTitle="Job"
|
|
80
|
+
[usePathNames]="true"
|
|
81
|
+
[enableCustomization]="editMode"
|
|
82
|
+
(submitted)="onSubmit($event.value)"
|
|
147
83
|
>
|
|
148
|
-
<pdx-text-input formControlName="
|
|
84
|
+
<pdx-text-input formControlName="name" label="Name" pdxManualEdit="name" />
|
|
85
|
+
|
|
86
|
+
<div formGroupName="salary">
|
|
87
|
+
<pdx-material-currency
|
|
88
|
+
formControlName="amount"
|
|
89
|
+
label="Salary"
|
|
90
|
+
pdxManualEdit="salary.amount"
|
|
91
|
+
/>
|
|
92
|
+
</div>
|
|
149
93
|
</praxis-manual-form>
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
<div exampleDiagram>
|
|
153
|
-
<!-- Mermaid, SVG ou fluxo simplificado -->
|
|
154
|
-
</div>
|
|
155
|
-
</praxis-manual-form-doc-example>
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
Também existe um showcase pronto para consumo direto:
|
|
159
|
-
|
|
160
|
-
```html
|
|
161
|
-
<praxis-manual-form-doc-example-showcase></praxis-manual-form-doc-example-showcase>
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
Esse showcase já inclui providers locais de `API_URL` e `ASYNC_CONFIG_STORAGE`, evitando erro de injeção em páginas de documentação.
|
|
165
|
-
|
|
166
|
-
### Diagrama de funcionamento (alto nível)
|
|
167
|
-
|
|
168
|
-
```mermaid
|
|
169
|
-
flowchart LR
|
|
170
|
-
Host[Template com <pdx-*>] --> ManualForm[praxis-manual-form]
|
|
171
|
-
ManualForm --> Detect[Detecta campos]
|
|
172
|
-
Detect --> Meta[Infere FieldMetadata]
|
|
173
|
-
Meta --> Group[Cria/Adota FormGroup]
|
|
174
|
-
Group --> Render[Renderiza campos + validações]
|
|
175
|
-
ManualForm --> Instance[ManualFormInstance]
|
|
176
|
-
Instance --> Storage[ConfigStorage]
|
|
177
|
-
ManualForm -->|editMode| Editor[Metadata Editor]
|
|
178
|
-
Editor --> Patch[Aplica patch]
|
|
179
|
-
Patch --> Instance
|
|
180
|
-
Instance --> Persist[saveDraft / resetToSeed]
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
### Editor de metadados integrado
|
|
184
|
-
|
|
185
|
-
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.
|
|
186
|
-
|
|
187
|
-
```ts
|
|
188
|
-
@Component({
|
|
189
|
-
/* ... */
|
|
94
|
+
`,
|
|
190
95
|
})
|
|
191
|
-
export class
|
|
192
|
-
@ViewChild(ManualFormComponent) manualForm?: ManualFormComponent;
|
|
193
|
-
|
|
194
|
-
constructor(private readonly metadataBridge: ManualFieldMetadataBridgeService) {}
|
|
195
|
-
|
|
196
|
-
openFieldEditor(fieldName: string): void {
|
|
197
|
-
const instance = this.manualForm?.instance;
|
|
198
|
-
if (!instance) {
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
void this.metadataBridge.openEditor(instance, fieldName);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
### Toolbar de campo (modo de edicao)
|
|
207
|
-
|
|
208
|
-
Quando `enableCustomization` estiver ativo, o manual-form exibe uma toolbar flutuante ao clicar
|
|
209
|
-
no campo. Ela permite alternar `required`, `readOnly`, `hidden` e `disabled`, e abrir o editor
|
|
210
|
-
completo de metadados.
|
|
211
|
-
|
|
212
|
-
Atalhos de teclado (quando o campo esta focado):
|
|
213
|
-
- `F2` ou `Alt+F10` abre a toolbar e move o foco para o primeiro botao.
|
|
214
|
-
- `ESC` fecha a toolbar.
|
|
215
|
-
|
|
216
|
-
Observacao: se um componente de campo interromper o clique (stopPropagation), a toolbar nao
|
|
217
|
-
abrira via mouse. Use os atalhos de teclado ou evite bloquear o click no host do campo.
|
|
218
|
-
|
|
219
|
-
Token de z-index:
|
|
220
|
-
- `--pdx-manual-toolbar-z` (default: `2001`).
|
|
221
|
-
Classe de overlay (para temas corporativos):
|
|
222
|
-
- `.pdx-manual-toolbar-panel` (aplicada no CDK Overlay para customizacoes).
|
|
223
|
-
|
|
224
|
-
## Motivações
|
|
225
|
-
|
|
226
|
-
- DX coerente para formulários manuais (sem JSON).
|
|
227
|
-
- Compatibilidade com o ecossistema (metadados, persistência, metadata editor).
|
|
228
|
-
- Facilita adoção por hosts sem precisar do builder completo.
|
|
229
|
-
|
|
230
|
-
## Estado atual
|
|
231
|
-
|
|
232
|
-
- Container autodetecta campos (`selector`/`constructor → FieldControlType`), infere `label`, `validators.required`.
|
|
233
|
-
- Persistência automática (`ManualFormInstance`/`ConfigStorage`).
|
|
234
|
-
- Header/Ações padrões com `FormActionsLayout` (normalizado por `DEFAULT_ACTIONS`).
|
|
235
|
-
- Seeds usam `ensureIds`.
|
|
236
|
-
- Autosave opcional com debounce configurável (`enableAutoSave` + `autoSaveDebounceMs`).
|
|
237
|
-
|
|
238
|
-
## Próximos passos
|
|
239
|
-
|
|
240
|
-
1. Enriquecer inferência (opções, limites numéricos, etc.).
|
|
241
|
-
2. Permitir layout personalizado via `@Input() sections` ou `layoutConfig`.
|
|
242
|
-
3. Expandir testes unitários (detecção de campos, persistência, normalização de ações, nested paths).
|
|
243
|
-
4. Documentar API pública com exemplos (README + docs).
|
|
244
|
-
5. Adicionar feedbacks visuais pós-edição (notificações, toasts).
|
|
245
|
-
|
|
246
|
-
## Notas para evolução
|
|
247
|
-
|
|
248
|
-
- Continue a trabalhar em `projects/praxis-manual-form` e atualize demos (ex. `/src/app/features/cargo-manual-form`).
|
|
249
|
-
- Ao rodar `ng-packagr`, confirme que libs referenciadas (`@praxisui/core`, `@praxisui/dynamic-fields`) estão construídas ou apontam para `dist/`.
|
|
250
|
-
- Respeite contratos de `FieldMetadata` e use helpers (`ensureIds`, `deepClone`).
|
|
251
|
-
- Atenção a inputs obrigatórios (alguns componentes exigem `metadata`, `readonlyMode`, etc.).
|
|
252
|
-
- Prefira inline styles para evitar warnings de arquivo ausente.
|
|
253
|
-
|
|
254
|
-
Essa base permite que hosts manualmente criem formulários com componentes Praxis, mantendo compatibilidade com o restante do ecossistema (persistência, metadados e editor).
|
|
255
|
-
|
|
256
|
-
## Padrão corporativo recomendado
|
|
257
|
-
|
|
258
|
-
- 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.
|
|
259
|
-
- Modo dinâmico: opção para PoCs e exemplos rápidos; veja os detalhes na seção “Guia rápido”.
|
|
260
|
-
|
|
261
|
-
## Configurações avançadas
|
|
262
|
-
|
|
263
|
-
- `@Input() usePathNames: boolean` (padrão: `false`)
|
|
264
|
-
- Quando habilitado, usa `FormControlName.path` (segmentos unidos por `.`) como `FieldMetadata.name`. Requer suporte a grupos aninhados — já implementado em `DynamicFormService`.
|
|
265
|
-
- Mapeamentos de inferência via DI
|
|
266
|
-
- `MANUAL_FORM_SELECTOR_TO_CONTROL_TYPE` e `MANUAL_FORM_CONSTRUCTOR_TO_CONTROL_TYPE` permitem sobrescrever o mapa padrão de seletores/constructors → `FieldControlType`.
|
|
267
|
-
- Autosave
|
|
268
|
-
- `@Input() enableAutoSave: boolean` (padrão: `true`) e `@Input() autoSaveDebounceMs: number` (padrão: `800`).
|
|
269
|
-
|
|
270
|
-
### API pública adicional
|
|
271
|
-
|
|
272
|
-
- `ManualFormInstance.metadataChanges()`
|
|
273
|
-
- Emite sempre que o mapa de `FieldMetadata` é atualizado no runtime.
|
|
274
|
-
- Timing: após `patchFieldMetadata()`, `replaceConfig()`, `resetToSeed()` e cargas de config persistida.
|
|
275
|
-
|
|
276
|
-
## SSR (AsyncConfigStorage sem localStorage)
|
|
277
|
-
|
|
278
|
-
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.
|
|
279
|
-
|
|
280
|
-
Exemplo (Angular com `app.config.server.ts`):
|
|
281
|
-
|
|
282
|
-
```ts
|
|
283
|
-
// app.config.server.ts
|
|
284
|
-
import { ApplicationConfig, PLATFORM_ID, inject, isPlatformServer } from '@angular/core';
|
|
285
|
-
import { ASYNC_CONFIG_STORAGE, LocalStorageAsyncAdapter, type AsyncConfigStorage } from '@praxisui/core';
|
|
286
|
-
import { EMPTY, of } from 'rxjs';
|
|
287
|
-
|
|
288
|
-
class MemoryAsyncStorage implements AsyncConfigStorage {
|
|
289
|
-
private readonly map = new Map<string, any>();
|
|
290
|
-
loadConfig<T>(key: string) { return of((this.map.get(key) as T) ?? null); }
|
|
291
|
-
saveConfig<T>(key: string, config: T) { this.map.set(key, config); return EMPTY; }
|
|
292
|
-
clearConfig(key: string) { this.map.delete(key); return EMPTY; }
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
export const appConfig: ApplicationConfig = {
|
|
296
|
-
providers: [
|
|
297
|
-
{
|
|
298
|
-
provide: ASYNC_CONFIG_STORAGE,
|
|
299
|
-
useFactory: () => isPlatformServer(inject(PLATFORM_ID))
|
|
300
|
-
? new MemoryAsyncStorage()
|
|
301
|
-
: inject(LocalStorageAsyncAdapter),
|
|
302
|
-
},
|
|
303
|
-
],
|
|
304
|
-
};
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
Alternativas no servidor: usar cookies, cache distribuído (ex.: Redis) ou persistir por sessão do usuário. Basta implementar a interface `AsyncConfigStorage`.
|
|
308
|
-
|
|
309
|
-
### Exemplo (nested form)
|
|
310
|
-
|
|
311
|
-
```html
|
|
312
|
-
<form [formGroup]="form">
|
|
313
|
-
<praxis-manual-form formId="nested" [usePathNames]="true">
|
|
314
|
-
<div formGroupName="endereco">
|
|
315
|
-
<pdx-text-input formControlName="logradouro" label="Logradouro"></pdx-text-input>
|
|
316
|
-
<pdx-text-input formControlName="numero" label="Número"></pdx-text-input>
|
|
317
|
-
</div>
|
|
318
|
-
</praxis-manual-form>
|
|
319
|
-
</form>
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
Com `usePathNames=true`, os metadados serão gerados com nomes `endereco.logradouro` e `endereco.numero`, mantendo o `FormGroup` aninhado do host.
|
|
323
|
-
|
|
324
|
-
## Quando usar (e quando não)
|
|
325
|
-
|
|
326
|
-
Use esta biblioteca quando:
|
|
327
|
-
- Você quer controlar o layout via HTML, reaproveitando os componentes Praxis.
|
|
328
|
-
- Prefere começar simples e evoluir metadados gradualmente (inferência + editor visual opcional).
|
|
329
|
-
- Precisa integrar com persistência de rascunhos e editar propriedades de campos em runtime.
|
|
330
|
-
|
|
331
|
-
Prefira o formulário totalmente dinâmico (JSON com `@praxisui/dynamic-form`) quando:
|
|
332
|
-
- O layout deve ser 100% dirigido por metadados/JSON (sem HTML manual).
|
|
333
|
-
- É necessário trocar telas apenas alterando seeds/backends.
|
|
334
|
-
- Precisa de recursos avançados do motor dinâmico (regras, seções condicionais, layouts declarativos complexos).
|
|
335
|
-
|
|
336
|
-
## Guia rápido (host tipado)
|
|
337
|
-
|
|
338
|
-
```ts
|
|
339
|
-
// component.ts
|
|
340
|
-
@Component({ /* … */ })
|
|
341
|
-
export class MinhaPagina {
|
|
96
|
+
export class JobFormComponent {
|
|
342
97
|
private readonly fb = inject(FormBuilder);
|
|
98
|
+
editMode = false;
|
|
99
|
+
|
|
343
100
|
readonly form = this.fb.group({
|
|
344
|
-
|
|
345
|
-
|
|
101
|
+
name: this.fb.control('', { nonNullable: true, validators: [Validators.required] }),
|
|
102
|
+
salary: this.fb.group({
|
|
103
|
+
amount: this.fb.control<number | null>(null),
|
|
104
|
+
}),
|
|
346
105
|
});
|
|
347
|
-
}
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
```html
|
|
351
|
-
<praxis-manual-form
|
|
352
|
-
[formGroup]="form"
|
|
353
|
-
formId="meu-form"
|
|
354
|
-
[usePathNames]="true"
|
|
355
|
-
formTitle="Cadastro"
|
|
356
|
-
(submitted)="onSubmit($event)"
|
|
357
|
-
>
|
|
358
|
-
<pdx-text-input formControlName="nome" label="Nome"></pdx-text-input>
|
|
359
|
-
<div formGroupName="endereco">
|
|
360
|
-
<pdx-text-input formControlName="logradouro" label="Logradouro"></pdx-text-input>
|
|
361
|
-
</div>
|
|
362
|
-
</praxis-manual-form>
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
Observações importantes:
|
|
366
|
-
- Aplique `[formGroup]` diretamente no seletor `<praxis-manual-form>` (no host).
|
|
367
|
-
- Evite envolver o componente com um `<form [formGroup]>` externo; não há necessidade e pode causar forms aninhados.
|
|
368
|
-
|
|
369
|
-
Modo dinâmico (sem host tipado):
|
|
370
|
-
- Quando nenhum `[formGroup]` é informado no host, o container renderiza internamente um `<form [formGroup]="formGroup">` para atender o Angular Forms e evitar o erro NG01050.
|
|
371
|
-
- Isso dispensa qualquer “shim” externo para os exemplos dinâmicos.
|
|
372
|
-
|
|
373
|
-
## API do ManualFormComponent (resumo)
|
|
374
106
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
- `actions?: FormActionsLayout | null`
|
|
379
|
-
- `showHeader = true`, `showActions = true`
|
|
380
|
-
- `enableAutoSave = true`, `autoSaveDebounceMs = 800`
|
|
381
|
-
- `enableCustomization = false` (habilita ações de customização — ex.: abrir editor por duplo clique)
|
|
382
|
-
- `persistenceOptions?: { namespace?, tenantId?, profileId?, locale? }`
|
|
383
|
-
- `usePathNames = false` (usa `FormControlName.path` como nome do campo)
|
|
384
|
-
- Outputs (sinais):
|
|
385
|
-
- `submitted({ value, instance })`, `saved(instance)`, `reset(instance)`
|
|
386
|
-
- `metadataChange(FormConfig)`
|
|
387
|
-
|
|
388
|
-
- Métodos públicos:
|
|
389
|
-
- `tryOpenFieldEditor(fieldName: string)`: tenta abrir o editor do campo, respeitando `enableCustomization` (no‑op quando `false`).
|
|
390
|
-
|
|
391
|
-
## Contrato agentic de authoring
|
|
392
|
-
|
|
393
|
-
`PRAXIS_MANUAL_FORM_AUTHORING_MANIFEST` e o contrato AI executavel do pacote.
|
|
394
|
-
|
|
395
|
-
O manifesto governa a orquestracao do formulario manual:
|
|
396
|
-
|
|
397
|
-
- `manualField.add`
|
|
398
|
-
- `manualField.remove`
|
|
399
|
-
- `manualField.label.set`
|
|
400
|
-
- `layout.configure`
|
|
401
|
-
- `toolbar.configure`
|
|
402
|
-
- `autosave.enabled.set`
|
|
403
|
-
- `submitBehavior.set`
|
|
404
|
-
- `metadataBridge.configure`
|
|
405
|
-
|
|
406
|
-
Regras de fronteira:
|
|
407
|
-
|
|
408
|
-
- `ManualFormInstance` e a superficie runtime do pacote: config, mapa de metadata, `FormGroup` e persistencia devem permanecer coerentes
|
|
409
|
-
- as operacoes escrevem nos paths reais do runtime (`currentConfig`, `currentFieldMetadata`, `form`, inputs como `enableCustomization`, `enableAutoSave`, `autoSaveDebounceMs`, `persistenceOptions`, e outputs como `submitted`, `saved`, `resetEvent`)
|
|
410
|
-
- IDs de campo manual sao nomes estaveis de campo, nunca indice de array
|
|
411
|
-
- remocao de campo exige confirmacao explicita
|
|
412
|
-
- autosave deve declarar debounce seguro e chave de persistencia deterministica
|
|
413
|
-
- toolbar de campo e metadata bridge sao entry points de runtime controlados por `enableCustomization`; nao existe objeto persistido `toolbar.*` ou `metadataBridge.*`
|
|
414
|
-
- edicoes profundas de `FieldMetadata` sao delegadas ao `@praxisui/metadata-editor`
|
|
415
|
-
- authoring avancado de `FormConfig` pertence ao `@praxisui/dynamic-form`
|
|
416
|
-
|
|
417
|
-
## Header e Actions
|
|
418
|
-
|
|
419
|
-
- `<praxis-manual-form-header>`: recebe `instance`, `title`, `description`, `saveLabel`, `resetLabel` e emite `save/reset`.
|
|
420
|
-
- 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()`.
|
|
421
|
-
- `<praxis-manual-form-actions>`: recebe `actions: FormActionsLayout`, emite `actionClick` e aceita `trackByFn` opcional para listas grandes.
|
|
422
|
-
|
|
423
|
-
## Editor de Metadados (visual)
|
|
424
|
-
|
|
425
|
-
```ts
|
|
426
|
-
@ViewChild(ManualFormComponent) manual?: ManualFormComponent;
|
|
427
|
-
constructor(private readonly bridge: ManualFieldMetadataBridgeService) {}
|
|
428
|
-
openEditor(fieldName: string) {
|
|
429
|
-
const inst = this.manual?.instance; if (!inst) return;
|
|
430
|
-
this.bridge.openEditor(inst, fieldName).catch(console.error);
|
|
107
|
+
onSubmit(value: unknown): void {
|
|
108
|
+
console.log(value);
|
|
109
|
+
}
|
|
431
110
|
}
|
|
432
111
|
```
|
|
433
112
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
### Duplo clique com modo de edição
|
|
113
|
+
If no host `FormGroup` is provided, the container creates an internal dynamic group for quick examples and prototypes. Use typed host forms for production screens.
|
|
437
114
|
|
|
438
|
-
|
|
115
|
+
## Main Inputs And Outputs
|
|
439
116
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
117
|
+
- `formId: string`: required stable id for persistence.
|
|
118
|
+
- `formTitle`, `formDescription`: optional header copy.
|
|
119
|
+
- `actions?: FormActionsLayout`: submit/cancel/reset/custom action layout.
|
|
120
|
+
- `showHeader`, `showActions`: visibility flags.
|
|
121
|
+
- `enableAutoSave`, `autoSaveDebounceMs`: draft persistence controls.
|
|
122
|
+
- `componentInstanceId?: string`: disambiguates repeated instances.
|
|
123
|
+
- `enableCustomization: boolean`: enables toolbar, form editor and semantic assistant affordances.
|
|
124
|
+
- `persistenceOptions?: ManualFormPersistenceOptions`: namespace, tenant, profile and locale scope.
|
|
125
|
+
- `usePathNames: boolean`: uses `FormControlName.path` such as `address.street` as `FieldMetadata.name`.
|
|
126
|
+
- `submitted`, `saved`, `reset`, `metadataChange`: public lifecycle outputs.
|
|
446
127
|
|
|
447
|
-
|
|
128
|
+
## Runtime Model
|
|
448
129
|
|
|
449
|
-
|
|
130
|
+
`ManualFormComponent` detects projected `pdx-*` fields, infers minimal `FieldMetadata`, creates or adopts the `FormGroup`, and exposes a `ManualFormInstance`. The instance manages `createManualFormSeed`, `saveDraft`, `resetToSeed`, runtime metadata patches and `metadataChanges()`.
|
|
450
131
|
|
|
451
|
-
|
|
132
|
+
Autosave and metadata persistence use `ASYNC_CONFIG_STORAGE`. Provide a browser or server-safe implementation in the host; do not rely on `localStorage` during SSR.
|
|
452
133
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
```html
|
|
456
|
-
<button *ngIf="custom.enabled()" type="button" (click)="manualForm?.openFormEditor()">Editar formulário</button>
|
|
457
|
-
<praxis-manual-form #manualForm [formGroup]="form" formId="'cargo'" [enableCustomization]="custom.enabled()">
|
|
458
|
-
<!-- campos -->
|
|
459
|
-
</praxis-manual-form>
|
|
460
|
-
```
|
|
461
|
-
|
|
462
|
-
#### O que o editor cobre (tabs)
|
|
463
|
-
|
|
464
|
-
O editor do formulario (Settings Panel) expõe:
|
|
465
|
-
- **Campos**: visibilidade, obrigatorio, somente leitura, desabilitado.
|
|
466
|
-
- **Acoes**: layout da barra, botoes padrao e acoes customizadas.
|
|
467
|
-
- **Mensagens**: feedbacks de sucesso/erro, loading e confirmacoes.
|
|
468
|
-
- **Comportamento**: flags de UX (ex.: focusFirstError, scrollToErrors).
|
|
469
|
-
- **Dicas**: textos auxiliares para modos de tela (i18n/UX).
|
|
470
|
-
- **Hooks**: JSON com hooks de lifecycle (antes/depois de init/submit).
|
|
471
|
-
- **Regras**: regras de layout/visibilidade (JSON).
|
|
472
|
-
- **Cascatas**: dependencias entre campos via metadados.
|
|
473
|
-
|
|
474
|
-
Obs.: a execucao de hooks/regras depende do host registrar os providers correspondentes.
|
|
475
|
-
|
|
476
|
-
## Extensibilidade por DI
|
|
477
|
-
|
|
478
|
-
- Customize a inferência de `FieldControlType` via tokens:
|
|
479
|
-
- `MANUAL_FORM_SELECTOR_TO_CONTROL_TYPE`
|
|
480
|
-
- `MANUAL_FORM_CONSTRUCTOR_TO_CONTROL_TYPE`
|
|
481
|
-
|
|
482
|
-
## ManualFieldDirective
|
|
483
|
-
|
|
484
|
-
```html
|
|
485
|
-
<ng-container *praxisManualField="'nome'; praxisManualFieldInstance: manual.instance as ctx">
|
|
486
|
-
Label atual: {{ ctx?.label }}
|
|
487
|
-
</ng-container>
|
|
488
|
-
```
|
|
489
|
-
|
|
490
|
-
## ManualFieldEditorOnDblclickDirective (`pdxManualEdit`)
|
|
491
|
-
|
|
492
|
-
- Usa o duplo clique para abrir o editor do campo, respeitando `enableCustomization` do `ManualFormComponent`.
|
|
493
|
-
- Apenas funciona quando aplicada a elementos dentro de `<praxis-manual-form>`.
|
|
494
|
-
|
|
495
|
-
```html
|
|
496
|
-
<praxis-manual-form formId="cargo" [enableCustomization]="custom.enabled()">
|
|
497
|
-
<pdx-text-input formControlName="nome" label="Nome" pdxManualEdit="nome"></pdx-text-input>
|
|
498
|
-
</praxis-manual-form>
|
|
499
|
-
```
|
|
134
|
+
## Runtime Editing
|
|
500
135
|
|
|
501
|
-
|
|
136
|
+
When `enableCustomization` is true:
|
|
502
137
|
|
|
503
|
-
-
|
|
504
|
-
-
|
|
505
|
-
-
|
|
506
|
-
-
|
|
138
|
+
- the field toolbar can toggle required, read-only, hidden and disabled state;
|
|
139
|
+
- `pdxManualEdit="fieldName"` opens the field editor on double click;
|
|
140
|
+
- `tryOpenFieldEditor(fieldName)` and `openFormEditor()` can be called programmatically;
|
|
141
|
+
- `ManualFieldMetadataBridgeService` delegates deep field edits to `@praxisui/metadata-editor` and persists the resulting patch.
|
|
507
142
|
|
|
508
|
-
|
|
143
|
+
The toolbar and metadata bridge are runtime entry points. They are not separate persisted `toolbar.*` or `metadataBridge.*` objects.
|
|
509
144
|
|
|
510
|
-
|
|
145
|
+
## Authoring
|
|
511
146
|
|
|
512
|
-
|
|
147
|
+
`PRAXIS_MANUAL_FORM_AUTHORING_MANIFEST` governs AI/tooling operations for field add/remove, labels, layout, toolbar behavior, autosave, submit behavior and metadata bridge configuration. Advanced `FieldMetadata` editing belongs to `@praxisui/metadata-editor`; advanced `FormConfig` authoring belongs to `@praxisui/dynamic-form`.
|
|
513
148
|
|
|
514
|
-
|
|
515
|
-
- Layout é responsabilidade do host; não há reordenação automática por metadados.
|
|
516
|
-
- Modo dinâmico sem `[formGroup]` não habilita validação estática de `formControlName` no IDE.
|
|
149
|
+
Free JSON patches from AI flows are not the public authoring contract. Local apply must come from a manifest-backed `componentEditPlan`.
|
|
517
150
|
|
|
518
|
-
##
|
|
151
|
+
## Public API Snapshot
|
|
519
152
|
|
|
520
|
-
|
|
521
|
-
- O contexto do assistente e seguro e semantico: identidade do formulario, `formId`, contagem de campos/secoes/acoes, nomes de campos, referencia ao `PRAXIS_MANUAL_FORM_AUTHORING_MANIFEST` e hints de governanca.
|
|
522
|
-
- Prompts de regra, politica, compliance, publicacao, materializacao ou enforcement seguem para handoff governado `domain-rules/intake` e nao sao aplicados como patch local do formulario.
|
|
523
|
-
- Patches JSON livres vindos do backend sao rejeitados. O apply local permanece bloqueado ate existir `componentEditPlan` compilado e validado pelo manifesto canonico.
|
|
524
|
-
- `componentInstanceId` ajuda a compor a chave de persistencia (com `formId` e `persistenceOptions`), evitando conflito entre instancias da mesma tela.
|
|
153
|
+
Main exports include `ManualFormComponent`, `ManualFormHeaderComponent`, `ManualFormActionsComponent`, `ManualFormConfigEditorComponent`, `ManualFormInstance`, seed helpers, manual field directives, metadata bridge services, DI tokens, AI capabilities and `PRAXIS_MANUAL_FORM_AUTHORING_MANIFEST`.
|
|
525
154
|
|
|
526
|
-
##
|
|
155
|
+
## Official Links
|
|
527
156
|
|
|
528
|
-
-
|
|
529
|
-
-
|
|
157
|
+
- Documentation: https://praxisui.dev/components/manual-form
|
|
158
|
+
- Live demo: https://praxis-ui-4e602.web.app
|
|
159
|
+
- Quickstart repository: https://github.com/codexrodrigues/praxis-ui-quickstart
|
|
160
|
+
- Source and issues: https://github.com/codexrodrigues/praxis-ui-angular
|
package/package.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@praxisui/manual-form",
|
|
3
|
-
"version": "9.0.0-beta.
|
|
3
|
+
"version": "9.0.0-beta.2",
|
|
4
4
|
"description": "Manual form toolkit for Praxis UI: container, instance factory and editor bridge for @praxisui/* fields.",
|
|
5
5
|
"peerDependencies": {
|
|
6
6
|
"@angular/common": "^21.0.0",
|
|
7
7
|
"@angular/core": "^21.0.0",
|
|
8
|
-
"@praxisui/core": "^9.0.0-beta.
|
|
9
|
-
"@praxisui/dynamic-fields": "^9.0.0-beta.
|
|
10
|
-
"@praxisui/settings-panel": "^9.0.0-beta.
|
|
11
|
-
"@praxisui/metadata-editor": "^9.0.0-beta.
|
|
8
|
+
"@praxisui/core": "^9.0.0-beta.2",
|
|
9
|
+
"@praxisui/dynamic-fields": "^9.0.0-beta.2",
|
|
10
|
+
"@praxisui/settings-panel": "^9.0.0-beta.2",
|
|
11
|
+
"@praxisui/metadata-editor": "^9.0.0-beta.2",
|
|
12
12
|
"@angular/cdk": "^21.0.0",
|
|
13
13
|
"@angular/forms": "^21.0.0",
|
|
14
14
|
"@angular/material": "^21.0.0",
|
|
15
15
|
"@angular/router": "^21.0.0",
|
|
16
|
-
"@praxisui/ai": "^9.0.0-beta.
|
|
16
|
+
"@praxisui/ai": "^9.0.0-beta.2",
|
|
17
17
|
"rxjs": "~7.8.0"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"repository": {
|
|
27
27
|
"type": "git",
|
|
28
|
-
"url": "https://github.com/codexrodrigues/praxis-ui-angular"
|
|
28
|
+
"url": "git+https://github.com/codexrodrigues/praxis-ui-angular.git"
|
|
29
29
|
},
|
|
30
30
|
"homepage": "https://praxisui.dev",
|
|
31
31
|
"bugs": {
|