@praxisui/dynamic-form 9.0.5-rc.37 → 9.0.5-rc.39
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-08-
|
|
3
|
+
"generatedAt": "2026-08-19T11:10:58.413Z",
|
|
4
4
|
"packageName": "@praxisui/dynamic-form",
|
|
5
|
-
"packageVersion": "9.0.5-rc.
|
|
5
|
+
"packageVersion": "9.0.5-rc.39",
|
|
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-08-19"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# Dynamic Form Authoring Document Semantics
|
|
@@ -39,6 +39,9 @@ Essa regra evita perda silenciosa do contrato canonico de Entity Lookup durante
|
|
|
39
39
|
- `Apply` e `Save` vindos do editor operam em modo `replace-all`.
|
|
40
40
|
- `reset` do editor limpa todos os modos persistidos do artefato.
|
|
41
41
|
- adapters legados continuam suportados sem limpeza destrutiva de contexto ausente.
|
|
42
|
+
- editores subordinados de seção, linha e coluna devem aplicar alterações pela localização estrutural capturada (`section`, `row`, `column`), nunca por busca global do primeiro `id` coincidente;
|
|
43
|
+
- IDs de linha devem ser únicos dentro da seção e IDs de coluna devem ser únicos dentro da linha;
|
|
44
|
+
- o estado `dirty` desses editores é reversível: restaurar o snapshot inicial deve desabilitar novamente Apply/Save.
|
|
42
45
|
|
|
43
46
|
## Visual Blocks Inside Layout
|
|
44
47
|
|
|
@@ -17553,7 +17553,7 @@ class PraxisDynamicForm {
|
|
|
17553
17553
|
this.debugLog('[PDF] settingsPanel.open(FieldMetadataEditor)', { fieldName, composedTitle });
|
|
17554
17554
|
}
|
|
17555
17555
|
catch { }
|
|
17556
|
-
const ref = this.settingsPanel.
|
|
17556
|
+
const ref = this.settingsPanel.openChild({
|
|
17557
17557
|
id: `field-meta.${this.formId}.${fieldName}`,
|
|
17558
17558
|
title: composedTitle,
|
|
17559
17559
|
titleIcon,
|
|
@@ -17654,31 +17654,37 @@ class PraxisDynamicForm {
|
|
|
17654
17654
|
openRowEditor(row) {
|
|
17655
17655
|
if (!row)
|
|
17656
17656
|
return;
|
|
17657
|
+
const location = this.resolveLayoutEditorLocation('row', row);
|
|
17658
|
+
if (!location)
|
|
17659
|
+
return;
|
|
17657
17660
|
Promise.resolve().then(function () { return rowEditor_component; }).then((m) => {
|
|
17658
17661
|
const { RowEditorComponent } = m;
|
|
17659
|
-
const ref = this.settingsPanel.
|
|
17662
|
+
const ref = this.settingsPanel.openChild({
|
|
17660
17663
|
id: `row.${this.formId || 'form'}.${row.id}`,
|
|
17661
17664
|
title: 'Configurar Linha',
|
|
17662
17665
|
titleIcon: 'view_stream',
|
|
17663
17666
|
content: { component: RowEditorComponent, inputs: { row } },
|
|
17664
17667
|
});
|
|
17665
|
-
ref.applied$.pipe(takeUntil(this.destroy$)).subscribe(() => this.
|
|
17666
|
-
ref.saved$.pipe(takeUntil(this.destroy$)).subscribe(() => this.
|
|
17668
|
+
ref.applied$.pipe(takeUntil(this.destroy$)).subscribe((value) => this.applyRowEditorValue(location, value));
|
|
17669
|
+
ref.saved$.pipe(takeUntil(this.destroy$)).subscribe((value) => this.applyRowEditorValue(location, value));
|
|
17667
17670
|
});
|
|
17668
17671
|
}
|
|
17669
17672
|
openColumnEditor(column) {
|
|
17670
17673
|
if (!column)
|
|
17671
17674
|
return;
|
|
17675
|
+
const location = this.resolveLayoutEditorLocation('column', column);
|
|
17676
|
+
if (!location)
|
|
17677
|
+
return;
|
|
17672
17678
|
Promise.resolve().then(function () { return columnEditor_component; }).then((m) => {
|
|
17673
17679
|
const { ColumnEditorComponent } = m;
|
|
17674
|
-
const ref = this.settingsPanel.
|
|
17680
|
+
const ref = this.settingsPanel.openChild({
|
|
17675
17681
|
id: `column.${this.formId || 'form'}.${column.id}`,
|
|
17676
17682
|
title: 'Configurar Coluna',
|
|
17677
17683
|
titleIcon: 'view_column',
|
|
17678
17684
|
content: { component: ColumnEditorComponent, inputs: { column } },
|
|
17679
17685
|
});
|
|
17680
|
-
ref.applied$.pipe(takeUntil(this.destroy$)).subscribe(() => this.
|
|
17681
|
-
ref.saved$.pipe(takeUntil(this.destroy$)).subscribe(() => this.
|
|
17686
|
+
ref.applied$.pipe(takeUntil(this.destroy$)).subscribe((value) => this.applyColumnEditorValue(location, value));
|
|
17687
|
+
ref.saved$.pipe(takeUntil(this.destroy$)).subscribe((value) => this.applyColumnEditorValue(location, value));
|
|
17682
17688
|
});
|
|
17683
17689
|
}
|
|
17684
17690
|
openSectionEditor(section, focusTarget) {
|
|
@@ -17686,7 +17692,7 @@ class PraxisDynamicForm {
|
|
|
17686
17692
|
return;
|
|
17687
17693
|
Promise.resolve().then(function () { return sectionEditor_component; }).then((m) => {
|
|
17688
17694
|
const { SectionEditorComponent } = m;
|
|
17689
|
-
const ref = this.settingsPanel.
|
|
17695
|
+
const ref = this.settingsPanel.openChild({
|
|
17690
17696
|
id: `section.${this.formId || 'form'}.${section.id}`,
|
|
17691
17697
|
title: section.title || 'Configurar seção',
|
|
17692
17698
|
titleIcon: 'view_agenda',
|
|
@@ -17703,6 +17709,71 @@ class PraxisDynamicForm {
|
|
|
17703
17709
|
});
|
|
17704
17710
|
});
|
|
17705
17711
|
}
|
|
17712
|
+
applyRowEditorValue(location, value) {
|
|
17713
|
+
this.applyNestedLayoutEditorValue('row', location, value);
|
|
17714
|
+
}
|
|
17715
|
+
applyColumnEditorValue(location, value) {
|
|
17716
|
+
this.applyNestedLayoutEditorValue('column', location, value);
|
|
17717
|
+
}
|
|
17718
|
+
resolveLayoutEditorLocation(kind, target) {
|
|
17719
|
+
const sections = this.config?.sections || [];
|
|
17720
|
+
for (let sectionIndex = 0; sectionIndex < sections.length; sectionIndex++) {
|
|
17721
|
+
const section = sections[sectionIndex];
|
|
17722
|
+
for (let rowIndex = 0; rowIndex < (section.rows || []).length; rowIndex++) {
|
|
17723
|
+
const row = section.rows[rowIndex];
|
|
17724
|
+
if (kind === 'row' && row === target) {
|
|
17725
|
+
return { sectionIndex, rowIndex, sectionId: section.id, rowId: row.id };
|
|
17726
|
+
}
|
|
17727
|
+
if (kind === 'column') {
|
|
17728
|
+
const columnIndex = (row.columns || []).findIndex((candidate) => candidate === target);
|
|
17729
|
+
if (columnIndex >= 0) {
|
|
17730
|
+
return {
|
|
17731
|
+
sectionIndex,
|
|
17732
|
+
rowIndex,
|
|
17733
|
+
columnIndex,
|
|
17734
|
+
sectionId: section.id,
|
|
17735
|
+
rowId: row.id,
|
|
17736
|
+
columnId: row.columns[columnIndex]?.id,
|
|
17737
|
+
};
|
|
17738
|
+
}
|
|
17739
|
+
}
|
|
17740
|
+
}
|
|
17741
|
+
}
|
|
17742
|
+
return undefined;
|
|
17743
|
+
}
|
|
17744
|
+
applyNestedLayoutEditorValue(kind, location, value) {
|
|
17745
|
+
if (!value || typeof value !== 'object')
|
|
17746
|
+
return;
|
|
17747
|
+
const sourceSections = this.config?.sections || [];
|
|
17748
|
+
const targetSection = sourceSections[location.sectionIndex];
|
|
17749
|
+
const targetRow = targetSection?.rows?.[location.rowIndex];
|
|
17750
|
+
const targetColumn = targetRow?.columns?.[location.columnIndex ?? -1];
|
|
17751
|
+
if (!targetSection ||
|
|
17752
|
+
targetSection.id !== location.sectionId ||
|
|
17753
|
+
!targetRow ||
|
|
17754
|
+
targetRow.id !== location.rowId ||
|
|
17755
|
+
(kind === 'column' && (!targetColumn || targetColumn.id !== location.columnId)))
|
|
17756
|
+
return;
|
|
17757
|
+
const sections = structuredClone(sourceSections);
|
|
17758
|
+
const positionedRow = sections[location.sectionIndex].rows[location.rowIndex];
|
|
17759
|
+
if (kind === 'row') {
|
|
17760
|
+
sections[location.sectionIndex].rows[location.rowIndex] = {
|
|
17761
|
+
...positionedRow,
|
|
17762
|
+
...structuredClone(value),
|
|
17763
|
+
};
|
|
17764
|
+
}
|
|
17765
|
+
else {
|
|
17766
|
+
const columnIndex = location.columnIndex;
|
|
17767
|
+
positionedRow.columns[columnIndex] = {
|
|
17768
|
+
...positionedRow.columns[columnIndex],
|
|
17769
|
+
...structuredClone(value),
|
|
17770
|
+
};
|
|
17771
|
+
}
|
|
17772
|
+
this.config = { ...this.config, sections };
|
|
17773
|
+
this.configChange.emit(this.config);
|
|
17774
|
+
this.emitConfigPatchChange();
|
|
17775
|
+
this.cdr.detectChanges();
|
|
17776
|
+
}
|
|
17706
17777
|
applySectionEditorValue(section, value) {
|
|
17707
17778
|
if (!section || !value || typeof value !== 'object') {
|
|
17708
17779
|
this.cdr.detectChanges();
|
|
@@ -20532,6 +20603,30 @@ class FormConfigService {
|
|
|
20532
20603
|
else {
|
|
20533
20604
|
ids.add(section.id);
|
|
20534
20605
|
}
|
|
20606
|
+
const rowIds = new Set();
|
|
20607
|
+
for (const row of section.rows || []) {
|
|
20608
|
+
if (!row.id) {
|
|
20609
|
+
errors.push(`Row id is required in section: ${section.id}`);
|
|
20610
|
+
}
|
|
20611
|
+
else if (rowIds.has(row.id)) {
|
|
20612
|
+
errors.push(`Duplicate row id in section ${section.id}: ${row.id}`);
|
|
20613
|
+
}
|
|
20614
|
+
else {
|
|
20615
|
+
rowIds.add(row.id);
|
|
20616
|
+
}
|
|
20617
|
+
const columnIds = new Set();
|
|
20618
|
+
for (const column of row.columns || []) {
|
|
20619
|
+
if (!column.id) {
|
|
20620
|
+
errors.push(`Column id is required in row: ${row.id}`);
|
|
20621
|
+
}
|
|
20622
|
+
else if (columnIds.has(column.id)) {
|
|
20623
|
+
errors.push(`Duplicate column id in row ${row.id}: ${column.id}`);
|
|
20624
|
+
}
|
|
20625
|
+
else {
|
|
20626
|
+
columnIds.add(column.id);
|
|
20627
|
+
}
|
|
20628
|
+
}
|
|
20629
|
+
}
|
|
20535
20630
|
}
|
|
20536
20631
|
return errors;
|
|
20537
20632
|
}
|
|
@@ -22774,7 +22869,7 @@ class SectionConfiguratorComponent {
|
|
|
22774
22869
|
const SectionEditorComponent = m.SectionEditorComponent;
|
|
22775
22870
|
if (!SectionEditorComponent)
|
|
22776
22871
|
return;
|
|
22777
|
-
const ref = this.settingsPanel.
|
|
22872
|
+
const ref = this.settingsPanel.openChild({
|
|
22778
22873
|
id: `layout.section.${this.section.id || this.sectionIndex}`,
|
|
22779
22874
|
title: this.section.title || `Seção ${this.sectionIndex + 1}`,
|
|
22780
22875
|
titleIcon: 'view_agenda',
|
|
@@ -22783,8 +22878,8 @@ class SectionConfiguratorComponent {
|
|
|
22783
22878
|
inputs: { section: this.section, fieldMetadata: this.fieldMetadata },
|
|
22784
22879
|
},
|
|
22785
22880
|
});
|
|
22786
|
-
ref.applied$?.subscribe(() => this.onSectionUpdated());
|
|
22787
|
-
ref.saved$?.subscribe(() => this.onSectionUpdated());
|
|
22881
|
+
ref.applied$?.subscribe((value) => this.onSectionUpdated(value));
|
|
22882
|
+
ref.saved$?.subscribe((value) => this.onSectionUpdated(value));
|
|
22788
22883
|
});
|
|
22789
22884
|
}
|
|
22790
22885
|
getSectionHeaderPreview() {
|
|
@@ -23376,7 +23471,7 @@ class LayoutEditorComponent {
|
|
|
23376
23471
|
this.applyFieldMetadataPatch(fieldName, patch);
|
|
23377
23472
|
};
|
|
23378
23473
|
const title = this.t('field.editMetadataTitle').replace('{field}', field.label || field.name);
|
|
23379
|
-
const ref = this.settingsPanel.
|
|
23474
|
+
const ref = this.settingsPanel.openChild({
|
|
23380
23475
|
id: `layout.fieldMetadata.${fieldName}`,
|
|
23381
23476
|
title,
|
|
23382
23477
|
titleIcon: 'edit_note',
|
|
@@ -23426,7 +23521,7 @@ class LayoutEditorComponent {
|
|
|
23426
23521
|
if (!section?.id || !row?.id || !column?.id || item?.kind !== 'richContent') {
|
|
23427
23522
|
return;
|
|
23428
23523
|
}
|
|
23429
|
-
const ref = this.settingsPanel.
|
|
23524
|
+
const ref = this.settingsPanel.openChild({
|
|
23430
23525
|
id: `layout.richContent.${section.id}.${row.id}.${column.id}.${item.id}`,
|
|
23431
23526
|
title: this.t('visualBlock.editTitle'),
|
|
23432
23527
|
titleIcon: 'notes',
|
|
@@ -30953,24 +31048,45 @@ class PraxisDynamicFormConfigEditor {
|
|
|
30953
31048
|
onLayoutSelect(event) {
|
|
30954
31049
|
if (event && event.type === 'column' && event.openEditor) {
|
|
30955
31050
|
const column = withFormConfigSections(this.editedConfig).sections[event.sectionIndex]?.rows[event.rowIndex]?.columns[event.columnIndex];
|
|
30956
|
-
this.openColumnEditor(column
|
|
31051
|
+
this.openColumnEditor(column, {
|
|
31052
|
+
sectionIndex: event.sectionIndex,
|
|
31053
|
+
rowIndex: event.rowIndex,
|
|
31054
|
+
columnIndex: event.columnIndex,
|
|
31055
|
+
});
|
|
30957
31056
|
}
|
|
30958
31057
|
}
|
|
30959
|
-
openColumnEditor(column) {
|
|
31058
|
+
openColumnEditor(column, location) {
|
|
30960
31059
|
if (!column)
|
|
30961
31060
|
return;
|
|
30962
31061
|
Promise.resolve().then(function () { return columnEditor_component; }).then((m) => {
|
|
30963
31062
|
const { ColumnEditorComponent } = m;
|
|
30964
|
-
const ref = this.settingsPanel.
|
|
31063
|
+
const ref = this.settingsPanel.openChild({
|
|
30965
31064
|
id: `column.${this.formId || 'form'}.${column.id}`,
|
|
30966
31065
|
title: this.tx('config.layout.columnEditor.title', 'Configurar Coluna'),
|
|
30967
31066
|
titleIcon: 'view_column',
|
|
30968
31067
|
content: { component: ColumnEditorComponent, inputs: { column } },
|
|
30969
31068
|
});
|
|
30970
|
-
ref.applied$.pipe(takeUntil(this.destroy$)).subscribe(() => this.
|
|
30971
|
-
ref.saved$.pipe(takeUntil(this.destroy$)).subscribe(() => this.
|
|
31069
|
+
ref.applied$.pipe(takeUntil(this.destroy$)).subscribe((value) => this.applyColumnEditorValue(column, location, value));
|
|
31070
|
+
ref.saved$.pipe(takeUntil(this.destroy$)).subscribe((value) => this.applyColumnEditorValue(column, location, value));
|
|
30972
31071
|
});
|
|
30973
31072
|
}
|
|
31073
|
+
applyColumnEditorValue(column, location, value) {
|
|
31074
|
+
if (!column || !value || typeof value !== 'object')
|
|
31075
|
+
return;
|
|
31076
|
+
const sourceSections = this.editedConfig.sections || [];
|
|
31077
|
+
const targetColumn = sourceSections[location.sectionIndex]?.rows?.[location.rowIndex]
|
|
31078
|
+
?.columns?.[location.columnIndex];
|
|
31079
|
+
if (!targetColumn || targetColumn.id !== column.id)
|
|
31080
|
+
return;
|
|
31081
|
+
const sections = structuredClone(sourceSections);
|
|
31082
|
+
const row = sections[location.sectionIndex].rows[location.rowIndex];
|
|
31083
|
+
row.columns[location.columnIndex] = {
|
|
31084
|
+
...row.columns[location.columnIndex],
|
|
31085
|
+
...structuredClone(value),
|
|
31086
|
+
};
|
|
31087
|
+
this.onConfigChange({ ...this.editedConfig, sections });
|
|
31088
|
+
this.cdr.detectChanges();
|
|
31089
|
+
}
|
|
30974
31090
|
// Cascatas: aplicar patch granular aos campos
|
|
30975
31091
|
onCascadeApply(patch) {
|
|
30976
31092
|
const fields = this.editedConfig.fieldMetadata || [];
|
|
@@ -32962,13 +33078,14 @@ class SectionEditorComponent {
|
|
|
32962
33078
|
});
|
|
32963
33079
|
customActionValue = '__custom__';
|
|
32964
33080
|
destroy$ = new Subject();
|
|
33081
|
+
initialValueSig = '';
|
|
32965
33082
|
titleInput;
|
|
32966
33083
|
descriptionInput;
|
|
32967
33084
|
constructor(fb, data, iconPicker) {
|
|
32968
33085
|
this.fb = fb;
|
|
32969
33086
|
this.data = data;
|
|
32970
33087
|
this.iconPicker = iconPicker;
|
|
32971
|
-
this.section = this.data.section;
|
|
33088
|
+
this.section = structuredClone(this.data.section);
|
|
32972
33089
|
this.fieldMetadata = this.data.fieldMetadata || this.fieldMetadata;
|
|
32973
33090
|
}
|
|
32974
33091
|
ngOnInit() {
|
|
@@ -33017,9 +33134,11 @@ class SectionEditorComponent {
|
|
|
33017
33134
|
headerActions: this.fb.array((this.normalizeHeaderActions(this.section.headerActions) ?? []).map((action) => this.createHeaderActionGroup(action))),
|
|
33018
33135
|
});
|
|
33019
33136
|
this.syncCollapsedControlState(!!this.form.get('collapsible')?.value);
|
|
33137
|
+
this.initialValueSig = JSON.stringify(this.normalizeSectionFormValue(this.form.getRawValue()));
|
|
33020
33138
|
this.form.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(() => {
|
|
33021
33139
|
this.applyNormalizedFormValue();
|
|
33022
|
-
this.isDirty$.next(
|
|
33140
|
+
this.isDirty$.next(JSON.stringify(this.normalizeSectionFormValue(this.form.getRawValue())) !==
|
|
33141
|
+
this.initialValueSig);
|
|
33023
33142
|
this.isValid$.next(this.form.valid);
|
|
33024
33143
|
});
|
|
33025
33144
|
this.form.get('collapsible')?.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((canCollapse) => {
|
|
@@ -33031,6 +33150,9 @@ class SectionEditorComponent {
|
|
|
33031
33150
|
}
|
|
33032
33151
|
ngAfterViewInit() {
|
|
33033
33152
|
setTimeout(() => {
|
|
33153
|
+
if (!this.isDirty$.value) {
|
|
33154
|
+
this.initialValueSig = JSON.stringify(this.normalizeSectionFormValue(this.form.getRawValue()));
|
|
33155
|
+
}
|
|
33034
33156
|
if (this.focusTarget === 'title') {
|
|
33035
33157
|
this.titleInput?.nativeElement?.focus?.();
|
|
33036
33158
|
}
|
|
@@ -33346,17 +33468,14 @@ class SectionEditorComponent {
|
|
|
33346
33468
|
return;
|
|
33347
33469
|
this.form.patchValue({ titleColor: v, descriptionColor: v });
|
|
33348
33470
|
Object.assign(this.section, { titleColor: v, descriptionColor: v });
|
|
33349
|
-
this.isDirty$.next(true);
|
|
33350
33471
|
}
|
|
33351
33472
|
onTitleStyleChange(style) {
|
|
33352
33473
|
this.section.titleStyle = style;
|
|
33353
33474
|
this.form.get('titleStyle')?.setValue(style);
|
|
33354
|
-
this.isDirty$.next(true);
|
|
33355
33475
|
}
|
|
33356
33476
|
onDescriptionStyleChange(style) {
|
|
33357
33477
|
this.section.descriptionStyle = style;
|
|
33358
33478
|
this.form.get('descriptionStyle')?.setValue(style);
|
|
33359
|
-
this.isDirty$.next(true);
|
|
33360
33479
|
}
|
|
33361
33480
|
trackFieldOption(index, field) {
|
|
33362
33481
|
return field.value || String(index);
|
|
@@ -34925,7 +35044,7 @@ class RowEditorComponent {
|
|
|
34925
35044
|
constructor(fb, data) {
|
|
34926
35045
|
this.fb = fb;
|
|
34927
35046
|
this.data = data;
|
|
34928
|
-
this.row = data.row;
|
|
35047
|
+
this.row = structuredClone(data.row);
|
|
34929
35048
|
}
|
|
34930
35049
|
ngOnInit() {
|
|
34931
35050
|
const r = this.data.row;
|
|
@@ -34950,7 +35069,6 @@ class RowEditorComponent {
|
|
|
34950
35069
|
}
|
|
34951
35070
|
catch { }
|
|
34952
35071
|
this.form.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(() => {
|
|
34953
|
-
Object.assign(this.data.row, this.form.value);
|
|
34954
35072
|
let dirty = true;
|
|
34955
35073
|
try {
|
|
34956
35074
|
const nowSig = JSON.stringify(this.form.getRawValue());
|
|
@@ -34966,7 +35084,7 @@ class RowEditorComponent {
|
|
|
34966
35084
|
this.destroy$.complete();
|
|
34967
35085
|
}
|
|
34968
35086
|
getSettingsValue() {
|
|
34969
|
-
return this.form.
|
|
35087
|
+
return { ...this.row, ...this.form.getRawValue() };
|
|
34970
35088
|
}
|
|
34971
35089
|
displayValue(value, fallback) {
|
|
34972
35090
|
if (value === null || value === undefined)
|
|
@@ -35296,11 +35414,12 @@ class ColumnEditorComponent {
|
|
|
35296
35414
|
isBusy$ = new BehaviorSubject(false);
|
|
35297
35415
|
destroy$ = new Subject();
|
|
35298
35416
|
_previewTimer;
|
|
35417
|
+
initialValueSig = '';
|
|
35299
35418
|
i18n = inject(PraxisI18nService);
|
|
35300
35419
|
constructor(fb, data) {
|
|
35301
35420
|
this.fb = fb;
|
|
35302
35421
|
this.data = data;
|
|
35303
|
-
this.column = data.column;
|
|
35422
|
+
this.column = structuredClone(data.column);
|
|
35304
35423
|
}
|
|
35305
35424
|
ngOnInit() {
|
|
35306
35425
|
const col = this.data.column;
|
|
@@ -35338,6 +35457,7 @@ class ColumnEditorComponent {
|
|
|
35338
35457
|
className: [col.className ?? ''],
|
|
35339
35458
|
testId: [col.testId ?? ''],
|
|
35340
35459
|
});
|
|
35460
|
+
this.initialValueSig = JSON.stringify(this.form.getRawValue());
|
|
35341
35461
|
this.form.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(() => {
|
|
35342
35462
|
// Evita NG0100: agendar a mutação para o próximo macrotask,
|
|
35343
35463
|
// fora do ciclo de detecção atual, mantendo o preview ao vivo.
|
|
@@ -35346,7 +35466,7 @@ class ColumnEditorComponent {
|
|
|
35346
35466
|
}
|
|
35347
35467
|
this._previewTimer = setTimeout(() => {
|
|
35348
35468
|
this.applyChanges();
|
|
35349
|
-
this.isDirty$.next(
|
|
35469
|
+
this.isDirty$.next(JSON.stringify(this.form.getRawValue()) !== this.initialValueSig);
|
|
35350
35470
|
this.isValid$.next(this.form.valid);
|
|
35351
35471
|
this._previewTimer = null;
|
|
35352
35472
|
}, 0);
|
|
@@ -35361,10 +35481,10 @@ class ColumnEditorComponent {
|
|
|
35361
35481
|
this.destroy$.complete();
|
|
35362
35482
|
}
|
|
35363
35483
|
applyChanges() {
|
|
35364
|
-
|
|
35484
|
+
this.column = { ...this.column, ...this.form.getRawValue() };
|
|
35365
35485
|
}
|
|
35366
35486
|
getSettingsValue() {
|
|
35367
|
-
return this.form.
|
|
35487
|
+
return { ...this.column, ...this.form.getRawValue() };
|
|
35368
35488
|
}
|
|
35369
35489
|
onBreakpointChange(bp) {
|
|
35370
35490
|
this.previewBreakpoint = bp;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@praxisui/dynamic-form",
|
|
3
|
-
"version": "9.0.5-rc.
|
|
3
|
+
"version": "9.0.5-rc.39",
|
|
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.5-rc.
|
|
13
|
-
"@praxisui/dynamic-fields": "^9.0.5-rc.
|
|
14
|
-
"@praxisui/metadata-editor": "^9.0.5-rc.
|
|
15
|
-
"@praxisui/rich-content": "^9.0.5-rc.
|
|
16
|
-
"@praxisui/settings-panel": "^9.0.5-rc.
|
|
17
|
-
"@praxisui/visual-builder": "^9.0.5-rc.
|
|
18
|
-
"@praxisui/core": "^9.0.5-rc.
|
|
12
|
+
"@praxisui/ai": "^9.0.5-rc.39",
|
|
13
|
+
"@praxisui/dynamic-fields": "^9.0.5-rc.39",
|
|
14
|
+
"@praxisui/metadata-editor": "^9.0.5-rc.39",
|
|
15
|
+
"@praxisui/rich-content": "^9.0.5-rc.39",
|
|
16
|
+
"@praxisui/settings-panel": "^9.0.5-rc.39",
|
|
17
|
+
"@praxisui/visual-builder": "^9.0.5-rc.39",
|
|
18
|
+
"@praxisui/core": "^9.0.5-rc.39",
|
|
19
19
|
"rxjs": "^7.8.0"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
@@ -1358,6 +1358,10 @@ declare class PraxisDynamicForm implements OnInit, OnChanges, OnDestroy {
|
|
|
1358
1358
|
private openRowEditor;
|
|
1359
1359
|
private openColumnEditor;
|
|
1360
1360
|
openSectionEditor(section: any, focusTarget?: 'title' | 'description'): void;
|
|
1361
|
+
private applyRowEditorValue;
|
|
1362
|
+
private applyColumnEditorValue;
|
|
1363
|
+
private resolveLayoutEditorLocation;
|
|
1364
|
+
private applyNestedLayoutEditorValue;
|
|
1361
1365
|
private applySectionEditorValue;
|
|
1362
1366
|
private resolveControlTypeEditorial;
|
|
1363
1367
|
private getControlTypeIcon;
|
|
@@ -1789,6 +1793,7 @@ declare class PraxisDynamicFormConfigEditor implements SettingsValueProvider, On
|
|
|
1789
1793
|
ngOnDestroy(): void;
|
|
1790
1794
|
onLayoutSelect(event: any): void;
|
|
1791
1795
|
private openColumnEditor;
|
|
1796
|
+
private applyColumnEditorValue;
|
|
1792
1797
|
onCascadeApply(patch: Record<string, Partial<FieldDefinition>>): void;
|
|
1793
1798
|
private syncCascadeManagerFields;
|
|
1794
1799
|
private stripLegacy;
|
|
@@ -2470,6 +2475,7 @@ declare class SectionEditorComponent implements OnInit, AfterViewInit, OnDestroy
|
|
|
2470
2475
|
readonly globalActionCatalog: ActionCatalogOption[];
|
|
2471
2476
|
readonly customActionValue = "__custom__";
|
|
2472
2477
|
private destroy$;
|
|
2478
|
+
private initialValueSig;
|
|
2473
2479
|
titleInput?: ElementRef<HTMLInputElement>;
|
|
2474
2480
|
descriptionInput?: ElementRef<HTMLTextAreaElement>;
|
|
2475
2481
|
constructor(fb: FormBuilder, data: {
|
|
@@ -2593,6 +2599,7 @@ declare class ColumnEditorComponent implements OnInit, OnDestroy, SettingsValueP
|
|
|
2593
2599
|
isBusy$: BehaviorSubject<boolean>;
|
|
2594
2600
|
private destroy$;
|
|
2595
2601
|
private _previewTimer;
|
|
2602
|
+
private initialValueSig;
|
|
2596
2603
|
private readonly i18n;
|
|
2597
2604
|
constructor(fb: FormBuilder, data: {
|
|
2598
2605
|
column: FormColumn;
|