@praxisui/dynamic-form 9.0.0-beta.0 → 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 +135 -812
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -1,127 +1,65 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: "Dynamic Form"
|
|
3
|
-
slug: "dynamic-form-overview"
|
|
4
|
-
description: "Visao geral do @praxisui/dynamic-form com schema-driven UI, editor embutido, layout runtime e integracao orientada a contrato."
|
|
5
|
-
doc_type: "reference"
|
|
6
|
-
document_kind: "component-overview"
|
|
7
|
-
component: "dynamic-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
|
-
- "dynamic-form"
|
|
18
|
-
- "schema-driven"
|
|
19
|
-
- "runtime"
|
|
20
|
-
- "layout"
|
|
21
|
-
- "config-editor"
|
|
22
|
-
order: 30
|
|
23
|
-
icon: "dynamic_form"
|
|
24
|
-
toc: true
|
|
25
|
-
sidebar: true
|
|
26
|
-
search_boost: 1.0
|
|
27
|
-
reading_time: 12
|
|
28
|
-
estimated_setup_time: 20
|
|
29
|
-
version: "1.0"
|
|
30
|
-
related_docs:
|
|
31
|
-
- "dynamic-form-hot-metadata-updates"
|
|
32
|
-
- "praxis-dynamic-form-json-api"
|
|
33
|
-
- "host-integration-guide"
|
|
34
|
-
- "consumer-integration-quickstart"
|
|
35
|
-
keywords:
|
|
36
|
-
- "FormConfig"
|
|
37
|
-
- "schema-driven"
|
|
38
|
-
- "layout editor"
|
|
39
|
-
- "settings integration"
|
|
40
|
-
last_updated: "2026-04-12"
|
|
41
|
-
---
|
|
42
|
-
|
|
43
1
|
# @praxisui/dynamic-form
|
|
44
2
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
## Documentation
|
|
48
|
-
|
|
49
|
-
- Official documentation: https://praxisui.dev
|
|
50
|
-
- Quickstart reference app: https://github.com/codexrodrigues/praxis-ui-quickstart
|
|
51
|
-
- Recommended for: schema-driven screens that need runtime forms, settings integration and contract-first metadata
|
|
3
|
+
Schema-driven and metadata-driven form runtime for Praxis UI.
|
|
52
4
|
|
|
53
|
-
|
|
5
|
+
Use this package to render `FormConfig` and `FieldMetadata`, connect forms to backend schema/submit endpoints, host visual configuration, and keep form layout, rules, actions, and submit payloads governed by shared Praxis contracts.
|
|
54
6
|
|
|
55
|
-
|
|
56
|
-
- Keep runtime customization and layout governance inside the host application
|
|
57
|
-
- Integrate with other `@praxisui/*` packages through shared contracts and settings flows
|
|
7
|
+
## Official Links
|
|
58
8
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
9
|
+
- Documentation: https://praxisui.dev/docs/components
|
|
10
|
+
- Live demo: https://praxis-ui-4e602.web.app
|
|
11
|
+
- Quickstart app: https://github.com/codexrodrigues/praxis-ui-quickstart
|
|
12
|
+
- API quickstart: https://github.com/codexrodrigues/praxis-api-quickstart-public
|
|
13
|
+
- Source: https://github.com/codexrodrigues/praxis-ui-angular/tree/main/projects/praxis-dynamic-form
|
|
14
|
+
- Issues: https://github.com/codexrodrigues/praxis-ui-angular/issues
|
|
62
15
|
|
|
63
16
|
## Install
|
|
64
17
|
|
|
65
18
|
```bash
|
|
66
|
-
npm i @praxisui/dynamic-form@
|
|
19
|
+
npm i @praxisui/dynamic-form@latest
|
|
67
20
|
```
|
|
68
21
|
|
|
69
|
-
Peer dependencies
|
|
70
|
-
|
|
71
|
-
- `@angular/common` `^
|
|
72
|
-
- `@
|
|
73
|
-
-
|
|
74
|
-
- `@praxisui/rich-content` `^9.0.0-beta.0`
|
|
75
|
-
- `@praxisui/visual-builder` `^9.0.0-beta.0`
|
|
76
|
-
- `@praxisui/settings-panel` `^9.0.0-beta.0`
|
|
77
|
-
- `@praxisui/cron-builder` `^9.0.0-beta.0`
|
|
22
|
+
Peer dependencies:
|
|
23
|
+
|
|
24
|
+
- `@angular/common`, `@angular/core`, `@angular/forms`, `@angular/cdk`, `@angular/material`, `@angular/router` `^21.0.0`
|
|
25
|
+
- `@praxisui/ai`, `@praxisui/core`, `@praxisui/dynamic-fields`, `@praxisui/metadata-editor`, `@praxisui/rich-content`, `@praxisui/settings-panel`, `@praxisui/visual-builder` `^9.0.0-beta.1`
|
|
26
|
+
- `rxjs` `^7.8.0`
|
|
78
27
|
|
|
79
|
-
##
|
|
28
|
+
## Minimum Local Runtime
|
|
80
29
|
|
|
81
|
-
|
|
30
|
+
Use a local `FormConfig` when the host already knows the sections and fields.
|
|
82
31
|
|
|
83
32
|
```ts
|
|
84
33
|
import { Component } from '@angular/core';
|
|
34
|
+
import { FormConfig } from '@praxisui/core';
|
|
85
35
|
import { PraxisDynamicForm } from '@praxisui/dynamic-form';
|
|
86
|
-
import type { FormConfig } from '@praxisui/core';
|
|
87
36
|
|
|
88
37
|
@Component({
|
|
89
|
-
selector: 'app-form-demo',
|
|
90
38
|
standalone: true,
|
|
39
|
+
selector: 'app-local-form',
|
|
91
40
|
imports: [PraxisDynamicForm],
|
|
92
41
|
template: `
|
|
93
42
|
<praxis-dynamic-form
|
|
43
|
+
formId="employee-local-form"
|
|
44
|
+
mode="create"
|
|
94
45
|
[config]="config"
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
(formSubmit)="onSubmit($event)"
|
|
98
|
-
></praxis-dynamic-form>
|
|
46
|
+
(formSubmit)="save($event.formData)">
|
|
47
|
+
</praxis-dynamic-form>
|
|
99
48
|
`,
|
|
100
49
|
})
|
|
101
|
-
export class
|
|
50
|
+
export class LocalFormComponent {
|
|
102
51
|
config: FormConfig = {
|
|
103
52
|
sections: [
|
|
104
53
|
{
|
|
105
54
|
id: 'main',
|
|
106
55
|
title: 'Employee',
|
|
107
|
-
sectionHeader: {
|
|
108
|
-
mode: 'auto',
|
|
109
|
-
sourceField: 'employeePhoto',
|
|
110
|
-
initialsSourceField: 'fullName',
|
|
111
|
-
altField: 'fullName',
|
|
112
|
-
},
|
|
113
56
|
rows: [
|
|
114
57
|
{
|
|
115
58
|
columns: [
|
|
116
59
|
{
|
|
117
|
-
id: '
|
|
118
|
-
items: [{ kind: 'field', id: 'field
|
|
119
|
-
fields: ['
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
id: 'col-email',
|
|
123
|
-
items: [{ kind: 'field', id: 'field-email', fieldName: 'email' }],
|
|
124
|
-
fields: ['email'],
|
|
60
|
+
id: 'name-column',
|
|
61
|
+
items: [{ kind: 'field', id: 'name-field', fieldName: 'name' }],
|
|
62
|
+
fields: ['name'],
|
|
125
63
|
},
|
|
126
64
|
],
|
|
127
65
|
},
|
|
@@ -129,775 +67,160 @@ export class FormDemoComponent {
|
|
|
129
67
|
},
|
|
130
68
|
],
|
|
131
69
|
fieldMetadata: [
|
|
132
|
-
{ name: '
|
|
133
|
-
{ name: 'fullName', label: 'Full Name', controlType: 'input', required: true } as any,
|
|
134
|
-
{ name: 'email', label: 'E-mail', controlType: 'email' } as any,
|
|
70
|
+
{ name: 'name', label: 'Name', controlType: 'input', required: true },
|
|
135
71
|
],
|
|
136
72
|
};
|
|
137
73
|
|
|
138
|
-
|
|
139
|
-
console.log('Submitted:', evt);
|
|
140
|
-
}
|
|
74
|
+
save(payload: unknown): void {}
|
|
141
75
|
}
|
|
142
76
|
```
|
|
143
77
|
|
|
144
|
-
|
|
145
|
-
For metadata-driven surfaces published by the backend, prefer passing `schemaUrl`, `submitUrl` and `submitMethod` explicitly. In dialog/drawer hosts detached from the route injector, pass `apiUrlEntry` in addition to `apiEndpointKey` so relative runtime URLs still resolve against the remote backend instead of the local shell origin.
|
|
146
|
-
When a late `config` hydration rebuilds the surface, the runtime preserves the current form values already mounted in memory only when the logical context remains the same (`resourcePath`, `resourceId`, and mode). If the form was preloaded from `resourceId`, that entity hydration remains visible after the rebuild only for the same entity context; entity switches and create-mode transitions do not restore the previous record snapshot.
|
|
147
|
-
|
|
148
|
-
## Local fields, transient state, and submit payloads
|
|
149
|
-
|
|
150
|
-
`praxis-dynamic-form` supports host-owned fields that participate in the form runtime without becoming part of the backend DTO. This is the right model for confirmation controls, wizard state, client-side calculated previews, host notes, and other UI context that should be available to rules and hooks but should not be persisted by default.
|
|
151
|
-
|
|
152
|
-
The submit contract exposes two values:
|
|
153
|
-
|
|
154
|
-
| Property | Meaning | Typical consumer |
|
|
155
|
-
| --- | --- | --- |
|
|
156
|
-
| `formSubmit.formData` | filtered persistence payload after submit policy is applied | HTTP submit, persistence hooks, backend DTO integration |
|
|
157
|
-
| `formSubmit.rawFormData` | full form value before filtering | host hooks, UI audit context, diagnostics, calculations |
|
|
158
|
-
|
|
159
|
-
Default behavior:
|
|
160
|
-
|
|
161
|
-
- schema-backed fields are included in `formData`.
|
|
162
|
-
- fields with `source: 'local'` are omitted from `formData` and kept in `rawFormData`.
|
|
163
|
-
- fields with `transient: true` are omitted from `formData` and kept in `rawFormData`.
|
|
164
|
-
- `submitPolicy` has priority over `source` and `transient`.
|
|
165
|
-
|
|
166
|
-
| `submitPolicy` | Effect |
|
|
167
|
-
| --- | --- |
|
|
168
|
-
| `include` | always include the field in `formData` |
|
|
169
|
-
| `omit` | always omit the field from `formData` |
|
|
170
|
-
| `includeWhenDirty` | include the field in `formData` only when the control is dirty |
|
|
171
|
-
|
|
172
|
-
Example:
|
|
173
|
-
|
|
174
|
-
```ts
|
|
175
|
-
fieldMetadata: [
|
|
176
|
-
{ name: 'fullName', label: 'Full name', controlType: 'input' },
|
|
177
|
-
{
|
|
178
|
-
name: 'approvalComment',
|
|
179
|
-
label: 'Approval comment',
|
|
180
|
-
controlType: 'textarea',
|
|
181
|
-
source: 'local',
|
|
182
|
-
submitPolicy: 'omit',
|
|
183
|
-
},
|
|
184
|
-
{
|
|
185
|
-
name: 'calculatedRiskScore',
|
|
186
|
-
label: 'Risk score',
|
|
187
|
-
controlType: 'numericTextBox',
|
|
188
|
-
transient: true,
|
|
189
|
-
readOnly: true,
|
|
190
|
-
},
|
|
191
|
-
{
|
|
192
|
-
name: 'manualOverrideReason',
|
|
193
|
-
label: 'Manual override reason',
|
|
194
|
-
controlType: 'textarea',
|
|
195
|
-
source: 'local',
|
|
196
|
-
submitPolicy: 'includeWhenDirty',
|
|
197
|
-
},
|
|
198
|
-
];
|
|
199
|
-
```
|
|
78
|
+
## Minimum Backend Runtime
|
|
200
79
|
|
|
201
|
-
For
|
|
80
|
+
For schema-driven backend surfaces, prefer explicit `schemaUrl`, `submitUrl`, and `submitMethod`.
|
|
202
81
|
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
"
|
|
206
|
-
"
|
|
207
|
-
"
|
|
208
|
-
"
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
Presentation/read-only note:
|
|
217
|
-
- `praxis-dynamic-form` hospeda `FieldMetadata`, mas não é o resolvedor direto de `valuePresentation`.
|
|
218
|
-
- quando campos entram em modo view/presentation, a semântica de apresentação vem do metadata do campo e é resolvida por `@praxisui/dynamic-fields`.
|
|
219
|
-
|
|
220
|
-
## Canonical editorial resolution for field editors
|
|
221
|
-
|
|
222
|
-
Para nome, descricao e icone do tipo de campo no configurador/layout editor e no titulo do metadata editor:
|
|
223
|
-
|
|
224
|
-
- `@praxisui/dynamic-form` nao mantem mapa editorial local por `controlType`;
|
|
225
|
-
- a resolucao canonica vem de `ComponentMetadataRegistry.resolveEditorial(..., { namespace: 'dynamicFields' })`;
|
|
226
|
-
- aliases e normalizacao de `controlType` sao apenas ponte tecnica de lookup, nao fonte de copy;
|
|
227
|
-
- quando nao existe descriptor canonico para aquele tipo, o fallback aceito e tecnico/neutro.
|
|
228
|
-
|
|
229
|
-
Fronteira operacional:
|
|
230
|
-
|
|
231
|
-
- package-owned fields de `@praxisui/dynamic-fields`: consomem descriptor editorial canonico da lib;
|
|
232
|
-
- `ComponentDocMeta` desses fields e derivado, nao a origem semantica;
|
|
233
|
-
- catalogo/showcase continua derivado;
|
|
234
|
-
- custom types do host seguem suportados via `ComponentMetadataRegistry.register(ComponentDocMeta)`, sem exigir mapa local no form.
|
|
235
|
-
|
|
236
|
-
### Canonical DI contract for hosts
|
|
237
|
-
|
|
238
|
-
`praxis-dynamic-form` does not register `GenericCrudService` internally.
|
|
239
|
-
When the runtime uses `resourcePath`, `resourceId`, schema fetch, CRUD submit, or custom endpoints, the host must provide the service in the effective host scope.
|
|
240
|
-
|
|
241
|
-
Canonical rule:
|
|
242
|
-
- `praxis-table` is self-hosted for `GenericCrudService`.
|
|
243
|
-
- `praxis-dynamic-form` is host-driven for `GenericCrudService`.
|
|
244
|
-
- If the host needs a specific `endpointKey` or pre-configuration, it must provide and configure the same `GenericCrudService` instance before rendering the form.
|
|
245
|
-
|
|
246
|
-
Minimal host example:
|
|
247
|
-
|
|
248
|
-
```ts
|
|
249
|
-
import { Component } from '@angular/core';
|
|
250
|
-
import { GenericCrudService } from '@praxisui/core';
|
|
251
|
-
import { PraxisDynamicForm } from '@praxisui/dynamic-form';
|
|
252
|
-
|
|
253
|
-
@Component({
|
|
254
|
-
selector: 'app-form-host',
|
|
255
|
-
standalone: true,
|
|
256
|
-
imports: [PraxisDynamicForm],
|
|
257
|
-
providers: [GenericCrudService],
|
|
258
|
-
template: `
|
|
259
|
-
<praxis-dynamic-form
|
|
260
|
-
[formId]="'employees-form'"
|
|
261
|
-
[resourcePath]="'employees'"
|
|
262
|
-
[mode]="'edit'"
|
|
263
|
-
></praxis-dynamic-form>
|
|
264
|
-
`,
|
|
265
|
-
})
|
|
266
|
-
export class FormHostComponent {}
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
If the host must force a non-default API endpoint, configure the service in the same host scope:
|
|
270
|
-
|
|
271
|
-
```ts
|
|
272
|
-
import { Component } from '@angular/core';
|
|
273
|
-
import { ApiEndpoint, GenericCrudService } from '@praxisui/core';
|
|
274
|
-
import { PraxisDynamicForm } from '@praxisui/dynamic-form';
|
|
275
|
-
|
|
276
|
-
@Component({
|
|
277
|
-
selector: 'app-form-host',
|
|
278
|
-
standalone: true,
|
|
279
|
-
imports: [PraxisDynamicForm],
|
|
280
|
-
providers: [GenericCrudService],
|
|
281
|
-
template: `
|
|
282
|
-
<praxis-dynamic-form
|
|
283
|
-
[formId]="'employees-form'"
|
|
284
|
-
[resourcePath]="'human-resources/employees'"
|
|
285
|
-
[mode]="'edit'"
|
|
286
|
-
></praxis-dynamic-form>
|
|
287
|
-
`,
|
|
288
|
-
})
|
|
289
|
-
export class FormHostComponent {
|
|
290
|
-
constructor(private readonly crud: GenericCrudService<any>) {
|
|
291
|
-
this.crud.configure('human-resources/employees', ApiEndpoint.HumanResources);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
Without that provider, a standalone host can fail with `NG0201: No provider found for _GenericCrudService`.
|
|
297
|
-
|
|
298
|
-
### 2) Config Editor component
|
|
299
|
-
|
|
300
|
-
Use the standalone editor directly to edit and reconcile the form configuration.
|
|
301
|
-
|
|
302
|
-
```ts
|
|
303
|
-
import { Component } from '@angular/core';
|
|
304
|
-
import { MatDialog } from '@angular/material/dialog';
|
|
305
|
-
import { PraxisDynamicFormConfigEditor } from '@praxisui/dynamic-form';
|
|
306
|
-
import type { FormConfig } from '@praxisui/core';
|
|
307
|
-
|
|
308
|
-
@Component({
|
|
309
|
-
selector: 'app-form-editor-launcher',
|
|
310
|
-
standalone: true,
|
|
311
|
-
template: `<button (click)="openEditor()">Open Config Editor</button>`,
|
|
312
|
-
})
|
|
313
|
-
export class FormEditorLauncherComponent {
|
|
314
|
-
constructor(private dialog: MatDialog) {}
|
|
315
|
-
|
|
316
|
-
openEditor() {
|
|
317
|
-
const initial: FormConfig = { sections: [] } as any;
|
|
318
|
-
this.dialog.open(PraxisDynamicFormConfigEditor, {
|
|
319
|
-
width: '1024px',
|
|
320
|
-
data: { formConfig: initial, formId: 'employees-form', mode: 'edit' },
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
}
|
|
82
|
+
```html
|
|
83
|
+
<praxis-dynamic-form
|
|
84
|
+
formId="employee-edit"
|
|
85
|
+
mode="edit"
|
|
86
|
+
resourcePath="/api/employees"
|
|
87
|
+
[resourceId]="employeeId"
|
|
88
|
+
schemaUrl="/api/employees/schemas/edit"
|
|
89
|
+
readUrl="/api/employees/42"
|
|
90
|
+
submitUrl="/api/employees/42"
|
|
91
|
+
submitMethod="PUT"
|
|
92
|
+
(formSubmit)="onSubmitted($event)">
|
|
93
|
+
</praxis-dynamic-form>
|
|
324
94
|
```
|
|
325
95
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
## Agentic Authoring & Manifest
|
|
96
|
+
`resourcePath` can connect the form to backend discovery, but it is not the only or always preferred minimum. Detached dialog/drawer hosts may also need `apiEndpointKey` or `apiUrlEntry` so relative runtime URLs resolve against the intended backend.
|
|
329
97
|
|
|
330
|
-
|
|
98
|
+
When using resource, schema, read, or submit flows, the host must provide the effective API/CRUD service wiring for that scope.
|
|
331
99
|
|
|
332
|
-
|
|
333
|
-
- **Editable targets:** `field`, `section`, `row`, `column`, `visualBlock`, `formAction`, `formRule`, `message`, `layoutPlacement`, `localField`, `schemaBackedField`.
|
|
334
|
-
- **Operations:** Covers field property updates, local field lifecycle, layout item manipulation, visual block authoring, rule management, and action configuration declared in `PRAXIS_DYNAMIC_FORM_AUTHORING_MANIFEST`.
|
|
335
|
-
- **Validation:** Deterministic validators cover ID uniqueness, layout integrity, rich content document validity, local/schema-backed separation, destructive confirmation, and editor round-trip preservation.
|
|
336
|
-
- **Examples/evals:** Fixtures cover local transient fields, schema-backed relabeling, layout moves, visual block add/move, visibility rules, unknown target rejection, destructive confirmation, and save/reopen without drift.
|
|
337
|
-
- **Round-trip:** Preserves editor state and ensures that AI-generated patches are compatible with the visual editor.
|
|
100
|
+
## Runtime Inputs And Outputs
|
|
338
101
|
|
|
339
|
-
|
|
102
|
+
Common inputs:
|
|
340
103
|
|
|
341
|
-
|
|
104
|
+
- `config`, `formId`, `componentInstanceId`
|
|
105
|
+
- `mode`: `create | edit | view`
|
|
106
|
+
- `resourcePath`, `resourceId`, `initialValue`
|
|
107
|
+
- `schemaUrl`, `readUrl`, `submitUrl`, `submitMethod`, `responseSchemaUrl`
|
|
108
|
+
- `apiEndpointKey`, `apiUrlEntry`
|
|
109
|
+
- `actions`, `layout`, `backConfig`, `hooks`
|
|
110
|
+
- `enableCustomization`
|
|
111
|
+
- `readonlyModeGlobal`, `disabledModeGlobal`, `presentationModeGlobal`, `visibleGlobal`
|
|
112
|
+
- `notifyIfOutdated`, `snoozeMs`, `autoOpenSettingsOnOutdated`
|
|
113
|
+
- `domainRules`
|
|
114
|
+
- `editorialContext`
|
|
342
115
|
|
|
343
|
-
|
|
344
|
-
- Services: `FormConfigService`, `FormLayoutService`, `DynamicFormLayoutService`, `FormContextService`
|
|
345
|
-
- Utilities: form rule converters, normalize date arrays, **FormRulesService** (aplica regras de propriedades)
|
|
346
|
-
- Metadata helpers: `providePraxisDynamicFormMetadata`
|
|
116
|
+
Common outputs:
|
|
347
117
|
|
|
348
|
-
|
|
118
|
+
- `formSubmit`, `formCancel`, `formReset`
|
|
119
|
+
- `formReady`, `valueChange`
|
|
120
|
+
- `configChange`, `configPatchChange`
|
|
121
|
+
- `syncCompleted`, `schemaStatusChange`
|
|
122
|
+
- `loadingStateChange`, `initializationError`
|
|
123
|
+
- `customAction`, `actionConfirmation`
|
|
124
|
+
- `fieldRenderError`, `ruleDiagnosticsChange`
|
|
349
125
|
|
|
350
|
-
|
|
126
|
+
## Submit Contract
|
|
351
127
|
|
|
352
|
-
|
|
353
|
-
- `config.formBlocksBefore?: RichContentDocument | null`
|
|
354
|
-
- `config.formBlocksBeforeActions?: RichContentDocument | null`
|
|
355
|
-
- `config.formBlocksAfter?: RichContentDocument | null`
|
|
356
|
-
- `config.editorialContext?: Record<string, unknown>`
|
|
357
|
-
- `[editorialContext]?: Record<string, unknown>`
|
|
128
|
+
`formSubmit` exposes both persistence data and full UI state:
|
|
358
129
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
2. `config.editorialContext`
|
|
362
|
-
3. input host `[editorialContext]`
|
|
130
|
+
- `formData`: filtered backend payload after submit policies are applied
|
|
131
|
+
- `rawFormData`: complete form value bag, including UI-only values
|
|
363
132
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
Exemplo minimo:
|
|
133
|
+
Host-owned fields that should participate in UI state but not be persisted by default belong in `FormConfig.fieldMetadata` with `source: 'local'` or `transient: true`.
|
|
367
134
|
|
|
368
135
|
```ts
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
accountName: 'Helena Costa',
|
|
374
|
-
accountRole: 'Gestora financeira',
|
|
375
|
-
accountContext: {
|
|
376
|
-
user: {
|
|
377
|
-
name: 'Helena Costa',
|
|
378
|
-
email: 'helena.costa@praxis.demo',
|
|
379
|
-
role: 'Gestora financeira',
|
|
380
|
-
},
|
|
381
|
-
tenant: {
|
|
382
|
-
name: 'Praxis Holding',
|
|
383
|
-
},
|
|
384
|
-
},
|
|
385
|
-
},
|
|
386
|
-
formBlocksBefore: {
|
|
387
|
-
kind: 'praxis.rich-content',
|
|
388
|
-
version: '1.0.0',
|
|
389
|
-
nodes: [
|
|
390
|
-
{
|
|
391
|
-
type: 'mediaBlock',
|
|
392
|
-
title: 'Praxis Summit 2026',
|
|
393
|
-
subtitle: 'Inscricao institucional',
|
|
394
|
-
description:
|
|
395
|
-
'Experiencia institucional com composicao editorial hospedada antes do formulario.',
|
|
396
|
-
icon: 'event',
|
|
397
|
-
},
|
|
398
|
-
{
|
|
399
|
-
type: 'card',
|
|
400
|
-
content: [
|
|
401
|
-
{ type: 'text', text: 'Antes de comecar', variant: 'title' },
|
|
402
|
-
{
|
|
403
|
-
type: 'text',
|
|
404
|
-
text: 'Confirme os dados abaixo antes do envio e consulte a documentacao se precisar de suporte.',
|
|
405
|
-
},
|
|
406
|
-
],
|
|
407
|
-
},
|
|
408
|
-
],
|
|
409
|
-
},
|
|
410
|
-
formBlocksBeforeActions: {
|
|
411
|
-
kind: 'praxis.rich-content',
|
|
412
|
-
version: '1.0.0',
|
|
413
|
-
nodes: [
|
|
414
|
-
{
|
|
415
|
-
type: 'card',
|
|
416
|
-
content: [
|
|
417
|
-
{ type: 'text', text: 'Conta atual', variant: 'title' },
|
|
418
|
-
{ type: 'text', textExpr: '${accountContext.user.name}' },
|
|
419
|
-
{ type: 'text', textExpr: '${accountContext.user.email}' },
|
|
420
|
-
],
|
|
421
|
-
},
|
|
422
|
-
],
|
|
423
|
-
},
|
|
424
|
-
formBlocksAfter: {
|
|
425
|
-
kind: 'praxis.rich-content',
|
|
426
|
-
version: '1.0.0',
|
|
427
|
-
nodes: [
|
|
428
|
-
{
|
|
429
|
-
type: 'card',
|
|
430
|
-
content: [
|
|
431
|
-
{ type: 'text', text: 'Praxis', variant: 'title' },
|
|
432
|
-
{ type: 'text', text: 'Todos os direitos reservados.' },
|
|
433
|
-
],
|
|
434
|
-
},
|
|
435
|
-
],
|
|
436
|
-
},
|
|
437
|
-
sections: [
|
|
136
|
+
const config: FormConfig = {
|
|
137
|
+
sections: [],
|
|
138
|
+
fieldMetadata: [
|
|
139
|
+
{ name: 'fullName', label: 'Full name', controlType: 'input' },
|
|
438
140
|
{
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
{
|
|
445
|
-
id: 'registration-full-name',
|
|
446
|
-
items: [{ kind: 'field', id: 'field-fullName', fieldName: 'fullName' }],
|
|
447
|
-
fields: ['fullName'],
|
|
448
|
-
},
|
|
449
|
-
{
|
|
450
|
-
id: 'registration-work-email',
|
|
451
|
-
items: [{ kind: 'field', id: 'field-workEmail', fieldName: 'workEmail' }],
|
|
452
|
-
fields: ['workEmail'],
|
|
453
|
-
},
|
|
454
|
-
],
|
|
455
|
-
},
|
|
456
|
-
],
|
|
141
|
+
name: 'approvalComment',
|
|
142
|
+
label: 'Approval comment',
|
|
143
|
+
controlType: 'textarea',
|
|
144
|
+
source: 'local',
|
|
145
|
+
submitPolicy: 'omit',
|
|
457
146
|
},
|
|
458
|
-
],
|
|
459
|
-
fieldMetadata: [
|
|
460
|
-
{ name: 'fullName', label: 'Nome completo', controlType: 'input', required: true },
|
|
461
|
-
{ name: 'workEmail', label: 'E-mail corporativo', controlType: 'email', required: true },
|
|
462
|
-
],
|
|
463
|
-
};
|
|
464
|
-
```
|
|
465
|
-
|
|
466
|
-
Uso no host:
|
|
467
|
-
|
|
468
|
-
```html
|
|
469
|
-
<praxis-dynamic-form
|
|
470
|
-
[config]="formConfig"
|
|
471
|
-
[editorialContext]="{ accountName: 'Helena Costa', accountRole: 'Host override' }"
|
|
472
|
-
></praxis-dynamic-form>
|
|
473
|
-
```
|
|
474
|
-
|
|
475
|
-
Notas:
|
|
476
|
-
- `formBlocksBefore`, `formBlocksBeforeActions` e `formBlocksAfter` nao entram em `formData`.
|
|
477
|
-
- `editorialContext` deve ser tratado como imutavel; mutacoes in-place nao invalidam o cache do host.
|
|
478
|
-
- A surface editorial agora usa `RichContentDocument`, delegando os nodes para `@praxisui/rich-content`.
|
|
479
|
-
- `formBlocksBeforeActions` renderiza depois das secoes e antes da area principal de acoes, sendo o slot recomendado para blocos contextuais como `form:user-context-summary`.
|
|
480
|
-
- `formBlocksAfter` permanece como slot de fechamento e continua renderizando depois do formulario inteiro, incluindo os CTAs.
|
|
481
|
-
- secoes agora suportam `appearance` (`card|plain|step`) e `stepLabel` para cabecalhos mais estruturados.
|
|
482
|
-
|
|
483
|
-
See public exports: `projects/praxis-dynamic-form/src/public-api.ts`.
|
|
484
|
-
|
|
485
|
-
## Blocos visuais em colunas
|
|
486
|
-
|
|
487
|
-
O layout de coluna usa `sections[].rows[].columns[].items[]` como contrato
|
|
488
|
-
canônico ordenado. Itens `kind: 'field'` referenciam
|
|
489
|
-
`fieldMetadata[].name`; itens `kind: 'richContent'` hospedam um
|
|
490
|
-
`RichContentDocument` dentro da coluna e não entram em `fieldMetadata`,
|
|
491
|
-
`formData` ou payload HTTP.
|
|
492
|
-
|
|
493
|
-
`sections[].rows[].columns[].fields[]` permanece aceito como entrada de
|
|
494
|
-
migração e como projeção derivada dos itens `kind: 'field'`, mas não deve ser
|
|
495
|
-
usado para criar blocos visuais.
|
|
496
|
-
|
|
497
|
-
No editor visual, **Campos da API** lista apenas campos existentes em
|
|
498
|
-
`fieldMetadata[]` que ainda não aparecem em `columns[].items[]` como
|
|
499
|
-
`kind: 'field'`. Ao adicionar um campo, o editor reinsere o item em `items[]`
|
|
500
|
-
e mantém `fields[]` sincronizado como projeção; `fieldMetadata[]` não é
|
|
501
|
-
alterado.
|
|
502
|
-
|
|
503
|
-
**Adicionar bloco visual** oferece presets internos como texto, aviso,
|
|
504
|
-
separador, card informativo, `callout`, `keyValueList`, `recordSummary`,
|
|
505
|
-
`disclosure` e `emptyState`. Esses presets são apenas atalhos de authoring que
|
|
506
|
-
geram documentos `praxis.rich-content` válidos para itens `kind: 'richContent'`;
|
|
507
|
-
eles não criam novo `kind`, não criam `FieldMetadata` e não participam do
|
|
508
|
-
payload de submit.
|
|
509
|
-
|
|
510
|
-
O runtime do formulário também passa `hostCapabilities` canônicos para
|
|
511
|
-
`@praxisui/rich-content`, permitindo:
|
|
512
|
-
- `actionButton` despachar ações via a mesma trilha de `customAction`/`globalAction`
|
|
513
|
-
já usada pelo formulário;
|
|
514
|
-
- `requiresCapabilities` resolver capacidades como `form.mode.edit`,
|
|
515
|
-
`form.mode.view`, `form.customization.enabled`, `form.resource.connected` e
|
|
516
|
-
`form.actions.submit`.
|
|
517
|
-
|
|
518
|
-
Exemplo mínimo de uma coluna com campo, bloco visual e projeção `fields[]`:
|
|
519
|
-
|
|
520
|
-
```json
|
|
521
|
-
{
|
|
522
|
-
"items": [
|
|
523
147
|
{
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
{
|
|
530
|
-
"type": "paragraph",
|
|
531
|
-
"text": "Revise os dados principais antes de salvar."
|
|
532
|
-
}
|
|
533
|
-
]
|
|
534
|
-
}
|
|
148
|
+
name: 'manualOverrideReason',
|
|
149
|
+
label: 'Manual override reason',
|
|
150
|
+
controlType: 'textarea',
|
|
151
|
+
source: 'local',
|
|
152
|
+
submitPolicy: 'includeWhenDirty',
|
|
535
153
|
},
|
|
536
|
-
{ "kind": "field", "id": "field-nome", "fieldName": "nome" },
|
|
537
|
-
{ "kind": "field", "id": "field-email", "fieldName": "email" }
|
|
538
154
|
],
|
|
539
|
-
"fields": ["nome", "email"]
|
|
540
|
-
}
|
|
541
|
-
```
|
|
542
|
-
|
|
543
|
-
Guia completo: `projects/praxis-dynamic-form/docs/layout-items-visual-blocks.md`.
|
|
544
|
-
|
|
545
|
-
## Authoring e Persistência em Hosts
|
|
546
|
-
|
|
547
|
-
- `configChange` preserva o contrato legado e continua emitindo `FormConfig` para consumidores diretos.
|
|
548
|
-
- `configPatchChange` é o output recomendado para hosts genéricos, como Page Builder e páginas dinâmicas, persistirem autoria assistida: o payload segue `{ inputPatch: { config: FormConfig } }`.
|
|
549
|
-
- Fluxos de IA e editores devem materializar alterações no `FormConfig` canônico do componente; o host decide onde gravar `inputs.config`.
|
|
550
|
-
|
|
551
|
-
## Documentacao Tecnica da Lib
|
|
552
|
-
|
|
553
|
-
- `projects/praxis-dynamic-form/src/lib/praxis-dynamic-form.json-api.md`
|
|
554
|
-
- `projects/praxis-dynamic-form/docs/hot-metadata-updates.md`
|
|
555
|
-
- `projects/praxis-dynamic-form/docs/layout-items-visual-blocks.md`
|
|
556
|
-
|
|
557
|
-
## Header de seção com avatar dinâmico
|
|
558
|
-
|
|
559
|
-
- `FormSection.icon` continua suportado como identidade visual estática do header.
|
|
560
|
-
- `FormSection.sectionHeader` amplia o contrato para permitir avatar resolvido a partir do `formData`, sem perder compatibilidade com `icon`.
|
|
561
|
-
- `FormSection.headerActions` permite ações iconográficas no canto direito do header da seção, reutilizando o mesmo output `customAction` com `source: 'section-header'` e `sectionId`.
|
|
562
|
-
- Modos suportados:
|
|
563
|
-
- `icon`: usa o ícone estático da seção e, se ele estiver vazio, pode reutilizar `fallbackIcon` como identidade visual explícita do header.
|
|
564
|
-
- `avatar-image`: tenta resolver uma imagem a partir de `sourceField`.
|
|
565
|
-
- `avatar-initials`: deriva iniciais a partir de `initialsSourceField` e, na ausência dele, usa `altField`.
|
|
566
|
-
- `auto`: tenta imagem, depois iniciais e por fim aplica o fallback configurado.
|
|
567
|
-
- Campos principais de `sectionHeader`:
|
|
568
|
-
- `sourceField`: field cujo valor atual representa foto/avatar.
|
|
569
|
-
- `initialsSourceField`: field textual usado para derivar iniciais.
|
|
570
|
-
- `altField`: field usado como texto acessível do avatar e fallback textual para iniciais.
|
|
571
|
-
- `fallbackIcon`: ícone usado quando a fonte dinâmica estiver vazia ou inválida, e também como fallback explícito do modo `icon` quando `FormSection.icon` não estiver definido.
|
|
572
|
-
- `emptyState`: `fallback-icon`, `placeholder-avatar` ou `none`, útil sobretudo em fluxos `create`; o padrão efetivo é `placeholder-avatar`, neutro para qualquer domínio. Quando `fallback-icon` é usado, o ícone permanece dentro da moldura/avatar e continua obedecendo `size`.
|
|
573
|
-
- `size`: tamanho semântico do avatar (`sm`, `md` ou `lg`), materializado pelo runtime por meio dos tokens `--pfx-form-section-avatar-size*`.
|
|
574
|
-
- `initialsMaxLength`: máximo de letras no avatar textual, normalizado para `1..4`.
|
|
575
|
-
- Campos principais de `headerActions[]`:
|
|
576
|
-
- `id`, `label`, `icon`
|
|
577
|
-
- `action` (opcional; se vazio, usa `id`; quando informado, ele vira o nome lógico emitido e a base de rules/mensagens)
|
|
578
|
-
- `tooltip` (opcional; se vazio, usa `label`)
|
|
579
|
-
- `color`, `visible`, `disabled`, `loading`
|
|
580
|
-
- `className` (classe CSS opcional aplicada ao botão do header)
|
|
581
|
-
- `style` (objeto de estilo inline opcional; no editor é informado como JSON)
|
|
582
|
-
- Formatos de imagem aceitos pelo runtime:
|
|
583
|
-
- URL
|
|
584
|
-
- data URL base64
|
|
585
|
-
- `File`/`Blob`
|
|
586
|
-
- objetos com `url`, `src`, `dataUrl`, `base64` ou `bytes`
|
|
587
|
-
- Comportamento típico em cadastro:
|
|
588
|
-
- formulário vazio: mostra placeholder neutro por padrão, ou `fallbackIcon` se `emptyState` pedir isso
|
|
589
|
-
- nome preenchido, sem foto: `auto` pode mostrar iniciais
|
|
590
|
-
- foto preenchida: mostra a imagem
|
|
591
|
-
|
|
592
|
-
## IA — catálogo de capacidades (composição)
|
|
593
|
-
|
|
594
|
-
O copiloto semântico do formulário usa o shell global de `@praxisui/ai` e registra uma sessão segura no `PraxisAssistantSessionRegistryService`. O botão local apenas abre o contexto do formulário; minimizar/reabrir deve preservar a sessão no cockpit global, sem tratar o formulário como fonte primária de regra de negócio.
|
|
595
|
-
|
|
596
|
-
O assistente usa um catálogo agregado de capabilities para gerar patches seguros:
|
|
597
|
-
|
|
598
|
-
- **Macro (FormConfig)**: layout, regras, ações, hooks e mensagens (`form-ai-capabilities`).
|
|
599
|
-
- **Base (FieldMetadata)**: propriedades comuns de campos (`field-metadata-ai-capabilities`).
|
|
600
|
-
- **Micro (por controlType)**: overlay específico de cada input (`@praxisui/dynamic-fields`).
|
|
601
|
-
|
|
602
|
-
Os paths micro são normalizados para `fieldMetadata[].<prop>` para garantir que os patches apontem para a raiz correta do formulário.
|
|
603
|
-
|
|
604
|
-
O fluxo agentic-authoring do formulário exige `componentEditPlan` validado pelo `PRAXIS_DYNAMIC_FORM_AUTHORING_MANIFEST` antes de aplicar patch local. Prompts de regra de negócio, política, elegibilidade, compliance, LGPD, aprovação/publicação ou decisão compartilhada devem seguir para `domain-rules`/`shared_rule_authoring`; em `view` sem customização ou readonly, o assistente opera apenas como ajuda/diagnóstico e não aplica mutação local.
|
|
605
|
-
|
|
606
|
-
## Regras de formulário (novo contrato)
|
|
607
|
-
|
|
608
|
-
- Formato: cada regra tem `targetType` (`field | section | action | row | column | visualBlock`), `targets: string[]` (IDs canônicos do alvo), e `effect` com `condition` (`JsonLogicExpression | null`), `properties` e `propertiesWhenFalse`.
|
|
609
|
-
- Regra de negócio, política, elegibilidade, validação, compliance ou decisão compartilhada deve ser authorada no fluxo governado de `domain-rules`/`shared_rule_authoring`. `visualBlockGuidance` fica restrito a projeção visual opcional: quando uma decisão já governada precisar explicar impacto na UI, a materialização `form_config` pode gerar `type: "visualBlockGuidance"`, `targetType: "visualBlock"` e `metadata.origin: "llm"` com `metadata.reviewStatus: "pending"`; o editor aceita a projeção, marca como `accepted` e materializa `formRulesState` internamente para round-trip visual.
|
|
610
|
-
- Compatibilidade: regras antigas (`context/targetField`) são migradas para `properties/targets` automaticamente; prefixes legados `section:/action:/row:/column:` continuam sendo normalizados quando representarem alvos tradicionais. Para header actions de seção, o ID canônico é preservado como `section:<sectionId>:header-action:<actionLogicalId>`.
|
|
611
|
-
- Semântica de limpeza: valores `null` em `properties/propertiesWhenFalse` removem o override e retornam ao valor base do layout; ausência mantém o valor base.
|
|
612
|
-
- Whitelist por tipo (somente propriedades a seguir são aplicadas; demais são descartadas e logadas em dev):
|
|
613
|
-
- `field`: `visible`, `required`, `readonly`, `disabled`, `className`, `style`, `label`, `description`, `placeholder`, `hint`, `tooltip`, `prefixIcon`, `suffixIcon`, `prefixText`, `suffixText`, `defaultValue`, `value`, `options` (array `{label,value,disabled?}`), `appearance` (`fill|outline`), `color` (`primary|accent|warn`), `floatLabel` (`auto|always|never`), `hintPosition` (`start|end`), `validators` (primitivos por chave).
|
|
614
|
-
- `section`: `visible`, `title`, `description`, `icon`, `sectionHeader` (objeto rico), `headerActions` (ações contextuais do cabeçalho) e tambem subpropriedades tipadas como `sectionHeader.mode`, `sectionHeader.sourceField`, `sectionHeader.initialsSourceField`, `sectionHeader.altField`, `sectionHeader.fallbackIcon`, `sectionHeader.emptyState`, `sectionHeader.size`, `sectionHeader.initialsMaxLength`, `className`, `style`, `collapsible`, `collapsed`, `headerTooltip`, `headerAlign` (`start|center`), `appearance` (`card|plain|step`), `stepLabel`, gaps (`gapBottom`, `titleGapBottom`, `descriptionGapBottom`), cores/tipografia (`titleColor`, `descriptionColor`, `titleStyle`, `descriptionStyle`).
|
|
615
|
-
- `action`: `visible`, `disabled`, `loading`, `label`, `icon`, `tooltip`, `color` (`primary|accent|warn|basic`), `variant` (`raised|stroked|flat|fab`), `size` (`small|medium|large`), `className`, `style`.
|
|
616
|
-
- `row`: `visible`, `gap`, `rowGap`, `className`, `style`.
|
|
617
|
-
- `column`: `visible`, `span`, `offset`, `order`, `hidden`, `align` (`start|center|end|stretch`), `padding`, `className`, `style`.
|
|
618
|
-
- `visualBlock`: `visible`, `hidden`, `layout`, `className`, `rootClassName`, `style`, `text`, `title`, `message`; para documentos compostos, use `textNodeId`, `titleNodeId` ou `messageNodeId` para apontar o node textual seguro.
|
|
619
|
-
- IDs canônicos de regras para actions do header de seção usam o formato `section:<sectionId>:header-action:<actionLogicalId>`, evitando colisão com botões globais e com ações repetidas em seções diferentes.
|
|
620
|
-
- Se a seção ainda não tiver `id`, o runtime aceita o fallback `header-action:<actionLogicalId>` por compatibilidade; para contratos persistidos, prefira sempre materializar `section.id`.
|
|
621
|
-
- Valores calculados: regras de `targetType: "field"` podem escrever `effect.properties.value` ou `effect.propertiesWhenFalse.value`. Use envelope fechado e exclusivo: exatamente `{ "expression": <JsonLogic> }` para calcular ou exatamente `{ "literal": <valor> }` para valor estruturado. Primitivos, `null` e arrays continuam aceitos como literais; objetos literais devem usar `{ "literal": ... }` para não serem tratados como Json Logic legado. Envelopes com chaves extras ou com `literal` e `expression` juntos são inválidos.
|
|
622
|
-
- Ordem de regras: valores calculados são resolvidos em fase anterior, com iteração limitada até estabilizar, antes da aplicação das demais propriedades. Assim uma regra pode calcular `age` e outra regra pode depender de `age` sem depender da posição no array.
|
|
623
|
-
- Comandos condicionais: `formCommandRules` é a superfície aditiva para side effects governados, avaliada depois que `formRules` estabilizam valores calculados. O primeiro corte suporta `effects[].kind: "global-action"` com `globalAction: GlobalActionRef`, policy operacional (`trigger`, `distinct`, `distinctBy`, `debounceMs`, `errorPolicy`, `runOnInitialEvaluation`) e default `trigger: "on-condition-enter"`. Comandos não devem ser colocados em `formRules[].effect.properties`. No editor visual, `payloadExpr` permanece escape hatch de JSON avancado: ele e preservado quando ja existe e nenhum payload estruturado e definido, mas novos campos visuais authoram `payload` literal.
|
|
624
|
-
- Runtime (FormRulesService): filtra por whitelist, converte tipos (enum/number/boolean/string), saneia objetos (`options/validators/style`), aplica remoção de chaves com `null`, resolve `fieldValues` calculados e retorna mapas `fieldProps/sectionProps/actionProps/rowProps/columnProps/visualBlockProps`.
|
|
625
|
-
- Renderização: `PraxisDynamicForm` aplica overrides em campos/seções/ações/linhas/colunas/blocos visuais (visibilidade, gaps, padding, classes, estilos, labels e texto seguro, conforme o tipo); colunas respeitam `align/span/offset/order/hidden/padding` vindo das regras.
|
|
626
|
-
- Regras de `visualBlock` são visual-only: elas não criam `FieldMetadata`, `FormControl`, valor em `formData` ou payload HTTP. Use campo local com `submitPolicy` quando a experiência precisar de valor persistível ou leitura em `rawFormData`.
|
|
627
|
-
|
|
628
|
-
Politica de erro dos comandos condicionais: falhas de `GlobalActionService.executeRef(...)` geram diagnostico sem propagar excecao para o ciclo do form, e a memoria `distinct` evita repeticao enquanto a condicao segue verdadeira. Sem `GlobalActionService`, actions mutaveis (`api.post`, `api.put`, `api.patch`, `api.delete`) falham fechado e nao sao emitidas via `customAction`.
|
|
629
|
-
|
|
630
|
-
### Builder integrado
|
|
631
|
-
|
|
632
|
-
- No editor visual, use a aba “Propriedades” (integrada ao builder) para selecionar o alvo (`targetType` + autocomplete de IDs de campos/seções/ações/linhas/colunas/blocos visuais), escolher propriedades whitelisted e definir valores para `properties` (branch true) e `propertiesWhenFalse` (branch false). Botão “Limpar override” remove a propriedade (equivalente a `null`).
|
|
633
|
-
- Para `visualBlock`, o painel de propriedades exibe um seletor de node quando a propriedade é `text`, `title` ou `message`; o editor persiste `textNodeId`, `titleNodeId` ou `messageNodeId` automaticamente para evitar sobrescrever o node errado em documentos compostos.
|
|
634
|
-
- No round-trip do builder, `config.type` continua sendo reservado para o tipo visual do node; semântica de regra como `visualBlockGuidance` é preservada internamente em `config.ruleType` e volta para `formRules[].type`.
|
|
635
|
-
- A aba de Propriedades usa inputs tipados (enum/number/boolean/string/JSON) conforme o schema injetado; valores inválidos são ignorados.
|
|
636
|
-
- O config editor fornece `targetSchemas` (campos/seções/ações/linhas/colunas/blocos visuais) e `targetPropertySchemas` para o builder; `formRules` são salvas no formato canônico (sem `context/targetField`).
|
|
637
|
-
|
|
638
|
-
## Layout padrão (sem FormConfig)
|
|
639
|
-
|
|
640
|
-
Quando o componente não recebe uma `FormConfig` prévia (primeira execução), ele gera um layout padrão a partir do metadata do backend:
|
|
641
|
-
|
|
642
|
-
- Linhas/colunas: 2 campos por linha (padrão). Em telas pequenas (xs/sm) os campos empilham (1 por linha), e a partir de md ficam lado a lado.
|
|
643
|
-
- Responsividade do grid (12 colunas):
|
|
644
|
-
- xs: 12, sm: 12, md: 6, lg: 6, xl: 6 (para 2 por linha). Para outros valores, a regra é `base = floor(12 / fieldsPerRow)`.
|
|
645
|
-
- Largura dos campos: `mat-form-field { width: 100% }` e um **allow‑list** de tipos “input‑like” em `data-field-type` recebem `width: 100%`. Controles compactos (ex.: checkbox/radio/toggle/rating/slider) **não** são forçados a preencher a coluna.
|
|
646
|
-
- Editor de Configuração: ao abrir a aba “Layout”, o editor reflete esse layout padrão; ao aplicar/salvar, persiste a `FormConfig` no storage do host.
|
|
647
|
-
- Personalização: você pode ajustar o layout pelo Editor (arrastar/seções/linhas/colunas, alterar spans) ou fornecer uma `FormConfig` completa via `[config]`.
|
|
648
|
-
|
|
649
|
-
## Form Actions — Layout & Styling
|
|
650
|
-
|
|
651
|
-
A barra de ações (onde ficam "ENVIAR", "Cancelar", etc.) é configurável via `config.actions` e pelo Editor (aba "Ações").
|
|
652
|
-
|
|
653
|
-
Defaults
|
|
654
|
-
- Rótulo do botão principal: `ENVIAR`.
|
|
655
|
-
- Posição estrutural: `afterSections` (renderiza abaixo da última seção).
|
|
656
|
-
- Alinhamento: `right`.
|
|
657
|
-
- Orientação: `horizontal`.
|
|
658
|
-
- Espaçamento: `normal`.
|
|
659
|
-
- Background: sem cor por padrão (herda da superfície). Configure via `containerStyles` ou `containerClassName` se desejar uma superfície própria.
|
|
660
|
-
|
|
661
|
-
Estrutura (parcial)
|
|
662
|
-
```ts
|
|
663
|
-
interface FormActionsLayout {
|
|
664
|
-
submit: FormActionButton; // id, label, color, type, variant, shortcut, etc.
|
|
665
|
-
cancel: FormActionButton;
|
|
666
|
-
reset: FormActionButton;
|
|
667
|
-
custom?: FormActionButton[]; // botões extras
|
|
668
|
-
|
|
669
|
-
// Layout/posicionamento
|
|
670
|
-
placement?: 'afterSections' | 'insideLastSection' | 'top';
|
|
671
|
-
position?: 'left' | 'center' | 'right' | 'justified' | 'split';
|
|
672
|
-
orientation?: 'horizontal' | 'vertical';
|
|
673
|
-
spacing?: 'compact' | 'normal' | 'spacious';
|
|
674
|
-
sticky?: boolean; // fixa a barra (bottom)
|
|
675
|
-
|
|
676
|
-
// Estilização do container
|
|
677
|
-
containerClassName?: string; // adiciona classe ao container
|
|
678
|
-
containerStyles?: { [k: string]: any }; // estilos inline (camelCase)
|
|
679
|
-
|
|
680
|
-
// Mobile
|
|
681
|
-
mobile?: { position?: 'left'|'center'|'right'|'justified'; orientation?: 'horizontal'|'vertical'; collapseToMenu?: boolean };
|
|
682
|
-
}
|
|
683
|
-
```
|
|
684
|
-
|
|
685
|
-
Exemplo (config)
|
|
686
|
-
```ts
|
|
687
|
-
config.actions = {
|
|
688
|
-
submit: { visible: true, label: 'ENVIAR', type: 'submit', color: 'primary', variant: 'raised', shortcut: 'ctrl+s' },
|
|
689
|
-
cancel: { visible: true, label: 'Cancelar', type: 'button', color: 'basic' },
|
|
690
|
-
reset: { visible: false, label: 'Reset' },
|
|
691
|
-
placement: 'afterSections',
|
|
692
|
-
position: 'right',
|
|
693
|
-
orientation: 'horizontal',
|
|
694
|
-
spacing: 'normal',
|
|
695
|
-
sticky: false,
|
|
696
|
-
containerClassName: 'my-form-actions',
|
|
697
|
-
containerStyles: {
|
|
698
|
-
background: 'var(--md-sys-color-surface-container)',
|
|
699
|
-
border: '1px solid var(--md-sys-color-outline-variant)',
|
|
700
|
-
borderRadius: '12px',
|
|
701
|
-
padding: '12px 16px'
|
|
702
|
-
},
|
|
703
|
-
mobile: { collapseToMenu: true }
|
|
704
155
|
};
|
|
705
156
|
```
|
|
706
157
|
|
|
707
|
-
|
|
708
|
-
```scss
|
|
709
|
-
.my-form-actions {
|
|
710
|
-
background: var(--md-sys-color-surface-container);
|
|
711
|
-
border: 1px solid var(--md-sys-color-outline-variant);
|
|
712
|
-
border-radius: 12px;
|
|
713
|
-
padding: 12px 16px;
|
|
714
|
-
}
|
|
715
|
-
```
|
|
716
|
-
|
|
717
|
-
Dicas
|
|
718
|
-
- Cores do botão: use `color` = `primary|accent|warn|basic` (respeitam o tema Material).
|
|
719
|
-
- Tokens M3: prefira `--md-sys-*` para cores/superfícies.
|
|
720
|
-
- Mobile: ative `collapseToMenu` para colapsar botões extras em menu nas telas pequenas.
|
|
721
|
-
- A classe de tema é decisão do host (`.dark-theme` ou `.theme-dark`/`.theme-light`); mantenha tokens e componentes no mesmo escopo.
|
|
722
|
-
- Editor integrado:
|
|
723
|
-
- `Ações > Botões Customizados` permite editar `label`, `icon`, `color`, `variant`, `size`, `tooltip`, `shortcut`, `className`, `disabled`, `loading` e `style` (JSON).
|
|
724
|
-
- `Seção > Cabeçalho` permite editar `headerActions[]` sem JSON manual, incluindo adicionar, remover e reordenar ações do header.
|
|
725
|
-
- No campo `action`, o editor sugere o catálogo global canônico do core e mantém fallback para ação customizada livre; o valor salvo continua compatível com `action` textual legado.
|
|
726
|
-
- Ações globais no header seguem o mesmo princípio operacional do restante da plataforma: dependem de executor no app (`ActionResolver`) e validam parâmetro obrigatório quando o catálogo exigir.
|
|
727
|
-
- Para `surface.open`, o editor expõe o authoring especializado da surface em vez de reduzir tudo a texto cru.
|
|
728
|
-
- A mesma aba permite reordenar `actions.custom[]` pelos controles de mover para cima/baixo; a ordem salva é a mesma ordem renderizada no runtime, sempre depois de `submit/cancel/reset`.
|
|
729
|
-
|
|
730
|
-
### Tokens M3 obrigatórios (host)
|
|
731
|
-
|
|
732
|
-
Para que o builder e os editores respeitem o tema do app host:
|
|
733
|
-
|
|
734
|
-
- Superfícies: `--md-sys-color-surface`, `--md-sys-color-surface-variant`, `--md-sys-color-surface-container-*`
|
|
735
|
-
- Texto/contorno: `--md-sys-color-on-surface`, `--md-sys-color-on-surface-variant`, `--md-sys-color-outline`, `--md-sys-color-outline-variant`
|
|
736
|
-
- Semânticos: `--md-sys-color-primary`, `--md-sys-color-secondary`, `--md-sys-color-tertiary`, `--md-sys-color-error`
|
|
737
|
-
- Containers: `--md-sys-color-primary-container`, `--md-sys-color-secondary-container`, `--md-sys-color-tertiary-container`, `--md-sys-color-error-container`
|
|
738
|
-
- Elevação: `--md-sys-elevation-level1`–`--md-sys-elevation-level3`
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
## Section titles — espaçamento global
|
|
742
|
-
|
|
743
|
-
O título de seção usa por padrão `margin: 0 0 6px 0`. Você pode ajustar globalmente via CSS var:
|
|
744
|
-
|
|
745
|
-
```scss
|
|
746
|
-
/* Global (app host) */
|
|
747
|
-
:root { --pfx-section-title-mb: 10px; } // ex.: 10px abaixo do título
|
|
748
|
-
```
|
|
749
|
-
|
|
750
|
-
Ou por seção, via metadado `titleGapBottom` (em pixels), que aplica inline somente naquela seção.
|
|
158
|
+
Do not model backend DTO fields as local/transient fields. If a field belongs to the public backend schema, fix the canonical schema or metadata mapping.
|
|
751
159
|
|
|
752
|
-
##
|
|
160
|
+
## Layout, Rules, And Rich Content
|
|
753
161
|
|
|
754
|
-
|
|
755
|
-
- Module format: `ESM2022`
|
|
162
|
+
`FormConfig` owns sections, rows, columns, field placement, visual blocks, rules, hooks, and actions.
|
|
756
163
|
|
|
757
|
-
|
|
164
|
+
- `sections[].rows[].columns[].items[]` is the ordered layout contract.
|
|
165
|
+
- `kind: "field"` references `fieldMetadata[].name`.
|
|
166
|
+
- `kind: "richContent"` hosts a `RichContentDocument` and does not enter `fieldMetadata`, `formData`, or HTTP payloads.
|
|
167
|
+
- `formRules` govern visibility, required state, values, labels, styles, and other whitelisted runtime properties.
|
|
168
|
+
- `formCommandRules` are used for governed side effects such as global actions.
|
|
758
169
|
|
|
759
|
-
|
|
170
|
+
Presentation formatting of field values is resolved by `@praxisui/dynamic-fields` from field metadata such as `valuePresentation`.
|
|
760
171
|
|
|
761
|
-
|
|
762
|
-
- `projects/praxis-dynamic-form/test-dev/e2e/form-config-editor-smoke.playwright.spec.ts`
|
|
763
|
-
- `projects/praxis-dynamic-form/test-dev/e2e/form-config-editor-layout.playwright.spec.ts`
|
|
764
|
-
- `projects/praxis-dynamic-form/test-dev/e2e/form-config-editor-rules.playwright.spec.ts`
|
|
765
|
-
- `projects/praxis-dynamic-form/test-dev/e2e/form-config-editor-cascades.playwright.spec.ts`
|
|
766
|
-
- `projects/praxis-dynamic-form/test-dev/e2e/form-config-editor-json.playwright.spec.ts`
|
|
767
|
-
- `projects/praxis-dynamic-form/test-dev/e2e/form-config-editor-hooks.playwright.spec.ts`
|
|
768
|
-
- `projects/praxis-dynamic-form/test-dev/e2e/form-config-editor-actions.playwright.spec.ts`
|
|
769
|
-
- `projects/praxis-dynamic-form/test-dev/e2e/form-config-editor-actions-custom.playwright.spec.ts`
|
|
172
|
+
## Visual Editor
|
|
770
173
|
|
|
771
|
-
|
|
772
|
-
- smoke das tabs principais do editor integrado
|
|
773
|
-
- `Layout`
|
|
774
|
-
- `Regras`
|
|
775
|
-
- `Cascatas`
|
|
776
|
-
- `JSON` com edicao real e bloqueio de payload invalido
|
|
777
|
-
- `Hooks`
|
|
778
|
-
- `Acoes` (`Botoes Padrao` e `Layout`)
|
|
779
|
-
- `Acoes` customizadas profundas (`dialog.alert` com `globalAction` simples e estruturado)
|
|
780
|
-
- `Comportamento`
|
|
781
|
-
- `Mensagens`
|
|
782
|
-
- `Dicas e Tooltips`
|
|
174
|
+
Use `PraxisDynamicFormConfigEditor` directly or enable customization on the runtime.
|
|
783
175
|
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
- [actions-editor.component.ts](/mnt/d/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-dynamic-form/src/lib/actions-editor/actions-editor.component.ts) agora usa o catalogo global canonico do app para o editor de acoes customizadas.
|
|
787
|
-
- [actions-editor.component.ts](/mnt/d/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-dynamic-form/src/lib/actions-editor/actions-editor.component.ts) agora preserva draft local dos campos globais e usa `track` estavel em `actions.custom`.
|
|
788
|
-
|
|
789
|
-
Comando base usado para rerodar suites isoladas:
|
|
176
|
+
```ts
|
|
177
|
+
import { PraxisDynamicFormConfigEditor } from '@praxisui/dynamic-form';
|
|
790
178
|
|
|
791
|
-
|
|
792
|
-
|
|
179
|
+
dialog.open(PraxisDynamicFormConfigEditor, {
|
|
180
|
+
width: '1024px',
|
|
181
|
+
data: {
|
|
182
|
+
formConfig: config,
|
|
183
|
+
formId: 'employee-edit',
|
|
184
|
+
mode: 'edit',
|
|
185
|
+
},
|
|
186
|
+
});
|
|
793
187
|
```
|
|
794
188
|
|
|
795
|
-
|
|
796
|
-
1. garantir que o app esteja no ar
|
|
797
|
-
2. rerodar a ultima suite alterada
|
|
798
|
-
3. proximo alvo pendente: consolidar a bateria completa ou seguir para outra area residual do editor
|
|
189
|
+
The editor preserves the same `FormConfig` contract used by the runtime. Field type names, descriptions, and icons come from the dynamic-fields editorial metadata chain, not from a local map inside Dynamic Form.
|
|
799
190
|
|
|
800
|
-
##
|
|
191
|
+
## AI Authoring
|
|
801
192
|
|
|
802
|
-
|
|
193
|
+
`PRAXIS_DYNAMIC_FORM_AUTHORING_MANIFEST` declares executable operations for governed edits to:
|
|
803
194
|
|
|
804
|
-
|
|
195
|
+
- fields and local fields
|
|
196
|
+
- sections, rows, columns, and layout placement
|
|
197
|
+
- visual blocks
|
|
198
|
+
- form actions
|
|
199
|
+
- form rules and messages
|
|
200
|
+
- schema-backed field edits
|
|
805
201
|
|
|
806
|
-
|
|
202
|
+
The manifest is meant to keep AI-generated patches compatible with the visual editor and the runtime `FormConfig` contract.
|
|
807
203
|
|
|
808
|
-
|
|
809
|
-
- Declarative UI
|
|
810
|
-
- Schema-driven UI
|
|
204
|
+
## Public API
|
|
811
205
|
|
|
812
|
-
|
|
206
|
+
Main exports:
|
|
813
207
|
|
|
814
|
-
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
208
|
+
- `PraxisDynamicForm`
|
|
209
|
+
- `PraxisFilterForm`
|
|
210
|
+
- `PraxisDynamicFormConfigEditor`
|
|
211
|
+
- `JsonConfigEditorComponent`
|
|
212
|
+
- `LayoutEditorComponent`
|
|
213
|
+
- `PraxisFormActionsComponent`
|
|
214
|
+
- `FormConfigService`, `FormLayoutService`, `DynamicFormLayoutService`, `FormContextService`, `FormRulesService`, `DomainRuleFormRulesService`
|
|
215
|
+
- widget config editors for dynamic form and filter form
|
|
216
|
+
- metadata providers such as `providePraxisDynamicFormMetadata`
|
|
217
|
+
- rule utilities, date normalization, settings-panel providers
|
|
218
|
+
- `FORM_AI_CAPABILITIES`, `FORM_COMPONENT_AI_CAPABILITIES`
|
|
219
|
+
- `PRAXIS_DYNAMIC_FORM_AUTHORING_MANIFEST`
|
|
818
220
|
|
|
819
|
-
|
|
820
|
-
- Declarativa via metadata (`dependencyFields`, `resetOnDependentChange`, etc.).
|
|
821
|
-
- Observa `FormControl.valueChanges` dos campos dependentes, aplica `filterCriteria` e recarrega conforme estratégia (`loadOn`, `dependencyLoadOnChange`).
|
|
822
|
-
- Elimina código ad hoc na página.
|
|
823
|
-
|
|
824
|
-
## Convivência e fonte de verdade
|
|
825
|
-
|
|
826
|
-
- Se Connections atualizam `filterCriteria` de um campo, considere desativar a cascata nativa nesse campo (`enableDependencyCascade: false`) ou usar `dependencyLoadOnChange: 'manual'` (cascata só atualiza filtros e Connections disparam o load).
|
|
827
|
-
- Estratégia de merge recomendada: `dependencyMergeStrategy: 'merge'` para preservar chaves vindas de Connections.
|
|
828
|
-
|
|
829
|
-
## Links úteis
|
|
830
|
-
|
|
831
|
-
- Fluxo de Schema (ETag/304, schemaId, reconciliação): `projects/praxis-core/docs/schema-flow.md` (canônico) e `docs/schemas/fluxo-schema.md` (resumo operacional)
|
|
832
|
-
- Guia de implementação e metadados da cascata: `docs/CASCADE-NATIVA.md`
|
|
833
|
-
- Padrões de endpoints (Options vs Filter) para selects: `projects/praxis-dynamic-fields/docs/generic-crud-service.md` (canônico) e `docs/DEVS-GENERIC-CRUD-SERVICE.md` (resumo operacional)
|
|
834
|
-
- Recipe oficial de Entity Lookup corporativo com `RESOURCE_ENTITY`, `dependsOn`, `dependencyFilterMap`, reidratação por `by-ids` e política de seleção: `examples/ai-recipes/praxis-dynamic-form.entity-lookup-procurement.json`
|
|
835
|
-
- Recipe oficial de comandos condicionais governados com `formCommandRules`, `GlobalActionRef`, policy operacional, valor calculado e date range confirmado: `examples/ai-recipes/praxis-dynamic-form.command-rules.json`
|
|
836
|
-
|
|
837
|
-
## Verificação de Schema (ETag/If-None-Match)
|
|
838
|
-
|
|
839
|
-
- Inputs (opcionais; ativos apenas com `enableCustomization=true`):
|
|
840
|
-
- `notifyIfOutdated: 'inline' | 'snackbar' | 'both' | 'none' = 'both'`
|
|
841
|
-
- `snoozeMs: number = 86400000`
|
|
842
|
-
- `autoOpenSettingsOnOutdated: boolean = false`
|
|
843
|
-
- Output:
|
|
844
|
-
- `schemaStatusChange: { outdated: boolean; serverHash?: string; lastVerifiedAt?: string; formId?: string }`
|
|
845
|
-
- Persistência (ConfigStorage):
|
|
846
|
-
- `form-schema-meta:{formId}` → `{ serverHash?: string; lastVerifiedAt?: string }`
|
|
847
|
-
- `form-schema-prefs:{formId}` → `{ notifyIfOutdated?, snoozeMs?, autoOpenSettingsOnOutdated? }`
|
|
848
|
-
- Por hash (suppress): `schemaIgnore:{formId}:{hash}`, `schemaSnooze:{formId}:{hash}`, `schemaNotified:{formId}:{hash}`
|
|
849
|
-
- Comportamento:
|
|
850
|
-
- Quando existe uma config persistida para o `formId`, o componente normaliza a config local, baixa o schema estrutural canônico e reconcilia `fieldMetadata` antes de montar o formulário.
|
|
851
|
-
- Essa reconciliação preserva customizações locais intencionais, incluindo campos com `source: "local"`, `transient: true` e `submitPolicy`, mas atualiza semântica server-backed publicada pelo schema.
|
|
852
|
-
- Campos de select publicados com `x-ui.endpoint` em `/options/filter` preservam `endpoint` como operação concreta e `resourcePath` como recurso dono. Isso evita usar `/filter` ou consultar schema do endpoint de options por engano.
|
|
853
|
-
- 304 em verificações posteriores apenas atualiza `lastVerifiedAt` e emite `schemaStatusChange(outdated=false)`.
|
|
854
|
-
- 200 em verificações posteriores atualiza `serverHash/lastVerifiedAt`, define `schemaOutdated = enableCustomization && hadBase` e emite `schemaStatusChange`.
|
|
855
|
-
- Primeira vez (sem base): baixa o corpo do schema para gerar o layout; persiste `form-schema-meta:{formId}`.
|
|
856
|
-
- Notificações respeitam preferências e são one‑shot por hash; o banner/snackbar oferecem ações para Reconciliar, Lembrar depois (snooze) e Ignorar.
|
|
857
|
-
|
|
858
|
-
### URL da API (absoluto vs relativo)
|
|
859
|
-
|
|
860
|
-
- Quando `API_URL.default.baseUrl` for relativo (ex.: `'/api'`), a lib resolve a origem a partir de `location.origin` no browser. Isso cobre o cenário comum com proxy de dev (`/api`, `/schemas`).
|
|
861
|
-
- Em SSR (sem `location.origin`), configure `baseUrl` absoluto (ex.: `https://api.acme.com/api`) para evitar erros do tipo “Invalid URL” ao construir chamadas de `/schemas/filtered`.
|
|
862
|
-
- O `GenericCrudService.getSchemasFilteredBaseUrl()` retorna sempre uma URL absoluta; o `SchemaMetadataClient` também aceita `baseUrl` relativo quando há origin disponível.
|
|
863
|
-
|
|
864
|
-
## Regras de Domínio Compartilhadas
|
|
865
|
-
|
|
866
|
-
`praxis-dynamic-form` pode consumir regras materializadas pelo backend sem gravá-las dentro de `FormConfig`. O input `domainRules` é opt-in e combina as regras remotas com `config.formRules` no mesmo `FormRulesService`.
|
|
867
|
-
|
|
868
|
-
```html
|
|
869
|
-
<praxis-dynamic-form
|
|
870
|
-
formId="funcionarios-form-demo"
|
|
871
|
-
[config]="formConfig"
|
|
872
|
-
[domainRules]="{
|
|
873
|
-
enabled: true,
|
|
874
|
-
targetArtifactKey: 'funcionarios-form-demo',
|
|
875
|
-
targetLayer: 'form_config',
|
|
876
|
-
targetArtifactType: 'praxis-dynamic-form',
|
|
877
|
-
status: 'applied'
|
|
878
|
-
}">
|
|
879
|
-
</praxis-dynamic-form>
|
|
880
|
-
```
|
|
881
|
-
|
|
882
|
-
- `targetArtifactKey` usa `formId`/`componentInstanceId` quando omitido.
|
|
883
|
-
- `targetLayer` default: `form_config`.
|
|
884
|
-
- `targetArtifactType` default: `praxis-dynamic-form`.
|
|
885
|
-
- Para consumo runtime, prefira `status: 'applied'`; `pending_review` pertence a etapas de governança antes da aplicação.
|
|
886
|
-
- `materializedPayload` pode ser um `FormLayoutRule` direto ou uma operação reconhecida, como `rule.visualBlockGuidance.add`, desde que essa operação seja tratada como projeção visual derivada e não como fonte primária da regra de negócio.
|
|
887
|
-
- A rastreabilidade da regra compartilhada fica em `metadata.domainRule`.
|
|
888
|
-
- Quando o backend enviar `decisionDiagnostics`, o runtime preserva esse envelope em `metadata.domainRule.decisionDiagnostics`, mantendo a explicação canônica da decisão semântica governada junto da regra derivada.
|
|
889
|
-
|
|
890
|
-
E2E vivo com API real:
|
|
891
|
-
|
|
892
|
-
```bash
|
|
893
|
-
PAX_PROXY_TARGET=https://praxis-api-quickstart.onrender.com \
|
|
894
|
-
node scripts/run-playwright-with-dev-host.js \
|
|
895
|
-
--port 4003 \
|
|
896
|
-
--path /funcionarios-form-demo \
|
|
897
|
-
--spec projects/praxis-dynamic-form/test-dev/e2e/funcionarios-form-demo-domain-rules.playwright.spec.ts
|
|
898
|
-
```
|
|
221
|
+
## Notes
|
|
899
222
|
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
223
|
+
- Separate local-only, backend schema-driven, and authored/customizable usage before deciding the minimum host setup.
|
|
224
|
+
- Keep backend schema fields server-backed; use local/transient metadata only for UI-owned state.
|
|
225
|
+
- Prefer explicit `schemaUrl + submitUrl + submitMethod` for backend-published surfaces and actions.
|
|
226
|
+
- Use the official documentation for full recipes on cascades, schema reconciliation, domain rules, action layout, and visual editor workflows.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@praxisui/dynamic-form",
|
|
3
|
-
"version": "9.0.0-beta.
|
|
3
|
+
"version": "9.0.0-beta.2",
|
|
4
4
|
"description": "Angular dynamic form engine for Praxis UI: metadata-driven forms, hooks, and services integrating @praxisui/* packages.",
|
|
5
5
|
"peerDependencies": {
|
|
6
6
|
"@angular/common": "^21.0.0",
|
|
@@ -9,13 +9,13 @@
|
|
|
9
9
|
"@angular/forms": "^21.0.0",
|
|
10
10
|
"@angular/material": "^21.0.0",
|
|
11
11
|
"@angular/router": "^21.0.0",
|
|
12
|
-
"@praxisui/ai": "^9.0.0-beta.
|
|
13
|
-
"@praxisui/dynamic-fields": "^9.0.0-beta.
|
|
14
|
-
"@praxisui/metadata-editor": "^9.0.0-beta.
|
|
15
|
-
"@praxisui/rich-content": "^9.0.0-beta.
|
|
16
|
-
"@praxisui/settings-panel": "^9.0.0-beta.
|
|
17
|
-
"@praxisui/visual-builder": "^9.0.0-beta.
|
|
18
|
-
"@praxisui/core": "^9.0.0-beta.
|
|
12
|
+
"@praxisui/ai": "^9.0.0-beta.2",
|
|
13
|
+
"@praxisui/dynamic-fields": "^9.0.0-beta.2",
|
|
14
|
+
"@praxisui/metadata-editor": "^9.0.0-beta.2",
|
|
15
|
+
"@praxisui/rich-content": "^9.0.0-beta.2",
|
|
16
|
+
"@praxisui/settings-panel": "^9.0.0-beta.2",
|
|
17
|
+
"@praxisui/visual-builder": "^9.0.0-beta.2",
|
|
18
|
+
"@praxisui/core": "^9.0.0-beta.2",
|
|
19
19
|
"rxjs": "^7.8.0"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"repository": {
|
|
30
30
|
"type": "git",
|
|
31
|
-
"url": "https://github.com/codexrodrigues/praxis-ui-angular"
|
|
31
|
+
"url": "git+https://github.com/codexrodrigues/praxis-ui-angular.git"
|
|
32
32
|
},
|
|
33
33
|
"homepage": "https://praxisui.dev",
|
|
34
34
|
"bugs": {
|