@praxisui/dynamic-form 9.0.68-rc.3 → 10.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "1.0.0",
|
|
3
|
-
"generatedAt": "2026-09-
|
|
3
|
+
"generatedAt": "2026-09-10T12:25:16.581Z",
|
|
4
4
|
"packageName": "@praxisui/dynamic-form",
|
|
5
|
-
"packageVersion": "
|
|
5
|
+
"packageVersion": "10.0.0-rc.0",
|
|
6
6
|
"sourceRegistry": "praxis-component-registry-ingestion",
|
|
7
7
|
"sourceRegistryVersion": "1.0.0",
|
|
8
8
|
"componentCount": 2,
|
|
@@ -4,7 +4,7 @@ doc_type: "adr"
|
|
|
4
4
|
component: "praxis-dynamic-form"
|
|
5
5
|
status: "accepted"
|
|
6
6
|
owner: "praxis-ui"
|
|
7
|
-
last_updated: "2026-
|
|
7
|
+
last_updated: "2026-09-10"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# Dynamic Form Authoring Document Semantics
|
|
@@ -50,6 +50,12 @@ Patches vindos de `@praxisui/metadata-editor` para `fieldMetadata` seguem semant
|
|
|
50
50
|
|
|
51
51
|
Para `entityLookup`, o bloco `fieldMetadata[].optionSource` e aninhado e deve ser mesclado em profundidade. O dynamic-form deve preservar campos existentes, como `key`, `resourcePath`, `valuePropertyPath` e `capabilities.byIds`, quando o editor alterar apenas partes do contrato, como `dependsOn`, `selectionPolicy`, `detail` ou `capabilities.auditSnapshot`.
|
|
52
52
|
|
|
53
|
+
No Layout Editor, cada evento aceito é um delta cumulativo relativo ao campo na
|
|
54
|
+
abertura. A aplicação usa esse baseline imutável: A → B → Aplicar → A → Concluir
|
|
55
|
+
restaura A mesmo quando o delta final está vazio. Alterações em outros campos do
|
|
56
|
+
rascunho pai são preservadas. Arrays são substituídos e `null` remove somente a
|
|
57
|
+
propriedade endereçada; a identidade `name` permanece estável.
|
|
58
|
+
|
|
53
59
|
Essa regra evita perda silenciosa do contrato canonico de Entity Lookup durante o fluxo: abrir editor -> aplicar/salvar -> reabrir editor -> runtime consumir.
|
|
54
60
|
|
|
55
61
|
## Consequences
|
|
@@ -24140,6 +24140,45 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
24140
24140
|
`, styles: [".destination-fields{display:grid;gap:var(--px-editor-section-gap, 1rem)}mat-form-field{width:100%;min-width:0}.destination-option{display:grid;white-space:normal;overflow-wrap:anywhere}.destination-summary{color:var(--mat-sys-on-surface-variant, var(--md-sys-color-on-surface-variant));font-size:var(--mat-sys-body-small-size, .75rem);line-height:var(--mat-sys-body-small-line-height, 1rem)}mat-hint{overflow-wrap:anywhere}\n"] }]
|
|
24141
24141
|
}] });
|
|
24142
24142
|
|
|
24143
|
+
const unsafeKeys = new Set(['__proto__', 'prototype', 'constructor']);
|
|
24144
|
+
function isRecord$1(value) {
|
|
24145
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
24146
|
+
return false;
|
|
24147
|
+
const prototype = Object.getPrototypeOf(value);
|
|
24148
|
+
return prototype === Object.prototype || prototype === null;
|
|
24149
|
+
}
|
|
24150
|
+
function cloneValue(value) {
|
|
24151
|
+
if (Array.isArray(value))
|
|
24152
|
+
return value.map(cloneValue);
|
|
24153
|
+
if (!isRecord$1(value))
|
|
24154
|
+
return value;
|
|
24155
|
+
return Object.fromEntries(Object.entries(value)
|
|
24156
|
+
.filter(([key]) => !unsafeKeys.has(key))
|
|
24157
|
+
.map(([key, entry]) => [key, cloneValue(entry)]));
|
|
24158
|
+
}
|
|
24159
|
+
/** Materializes the metadata editor's recursive delta without mutating its baseline. */
|
|
24160
|
+
function mergeFieldMetadataPatch(base, patch) {
|
|
24161
|
+
if (!isRecord$1(patch)) {
|
|
24162
|
+
return cloneValue(patch);
|
|
24163
|
+
}
|
|
24164
|
+
const result = {};
|
|
24165
|
+
if (isRecord$1(base)) {
|
|
24166
|
+
for (const [key, value] of Object.entries(base)) {
|
|
24167
|
+
if (!unsafeKeys.has(key))
|
|
24168
|
+
result[key] = cloneValue(value);
|
|
24169
|
+
}
|
|
24170
|
+
}
|
|
24171
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
24172
|
+
if (unsafeKeys.has(key))
|
|
24173
|
+
continue;
|
|
24174
|
+
if (value === null)
|
|
24175
|
+
delete result[key];
|
|
24176
|
+
else
|
|
24177
|
+
result[key] = mergeFieldMetadataPatch(result[key], value);
|
|
24178
|
+
}
|
|
24179
|
+
return result;
|
|
24180
|
+
}
|
|
24181
|
+
|
|
24143
24182
|
/**
|
|
24144
24183
|
* Editor component for configuring sections, rows and fields of a dynamic form.
|
|
24145
24184
|
* Provides a collapsible palette of available fields.
|
|
@@ -24592,10 +24631,12 @@ class LayoutEditorComponent {
|
|
|
24592
24631
|
}
|
|
24593
24632
|
openFieldMetadataEditorInternal(editorComponent, field) {
|
|
24594
24633
|
const fieldName = field.name;
|
|
24634
|
+
// Each emitted delta is cumulative relative to the field opened in the child.
|
|
24635
|
+
const baseline = mergeFieldMetadataPatch(field, {});
|
|
24595
24636
|
const handlePatch = (patch) => {
|
|
24596
24637
|
if (!patch)
|
|
24597
24638
|
return;
|
|
24598
|
-
this.applyFieldMetadataPatch(fieldName, patch);
|
|
24639
|
+
this.applyFieldMetadataPatch(fieldName, patch, baseline);
|
|
24599
24640
|
};
|
|
24600
24641
|
const title = this.t('field.editMetadataTitle', { field: field.label || field.name });
|
|
24601
24642
|
const ref = this.settingsPanel.openChild({
|
|
@@ -24614,13 +24655,12 @@ class LayoutEditorComponent {
|
|
|
24614
24655
|
ref?.applied$?.subscribe(handlePatch);
|
|
24615
24656
|
ref?.saved$?.subscribe(handlePatch);
|
|
24616
24657
|
}
|
|
24617
|
-
applyFieldMetadataPatch(fieldName, patch) {
|
|
24658
|
+
applyFieldMetadataPatch(fieldName, patch, baseline) {
|
|
24618
24659
|
const fieldMetadata = (this.config.fieldMetadata || []).map((field) => {
|
|
24619
24660
|
if (field.name !== fieldName)
|
|
24620
24661
|
return field;
|
|
24621
24662
|
return {
|
|
24622
|
-
...field,
|
|
24623
|
-
...patch,
|
|
24663
|
+
...mergeFieldMetadataPatch(baseline ?? field, patch),
|
|
24624
24664
|
name: field.name,
|
|
24625
24665
|
};
|
|
24626
24666
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@praxisui/dynamic-form",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.0.0-rc.0",
|
|
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": "^
|
|
13
|
-
"@praxisui/dynamic-fields": "^
|
|
14
|
-
"@praxisui/metadata-editor": "^
|
|
15
|
-
"@praxisui/rich-content": "^
|
|
16
|
-
"@praxisui/settings-panel": "^
|
|
17
|
-
"@praxisui/visual-builder": "^
|
|
18
|
-
"@praxisui/core": "^
|
|
12
|
+
"@praxisui/ai": "^10.0.0-rc.0",
|
|
13
|
+
"@praxisui/dynamic-fields": "^10.0.0-rc.0",
|
|
14
|
+
"@praxisui/metadata-editor": "^10.0.0-rc.0",
|
|
15
|
+
"@praxisui/rich-content": "^10.0.0-rc.0",
|
|
16
|
+
"@praxisui/settings-panel": "^10.0.0-rc.0",
|
|
17
|
+
"@praxisui/visual-builder": "^10.0.0-rc.0",
|
|
18
|
+
"@praxisui/core": "^10.0.0-rc.0",
|
|
19
19
|
"rxjs": "^7.8.0"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|