@praxisui/metadata-editor 8.0.0-beta.9 → 8.0.0-beta.91
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 +35 -0
- package/fesm2022/praxisui-metadata-editor.mjs +2727 -660
- package/package.json +15 -6
- package/{index.d.ts → types/praxisui-metadata-editor.d.ts} +14 -2
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { Injectable, inject, Component, Input, ViewEncapsulation, EventEmitter, DestroyRef, Output, signal, ENVIRONMENT_INITIALIZER } from '@angular/core';
|
|
3
|
-
import * as
|
|
4
|
-
import { FormControl, FormGroup, ReactiveFormsModule, FormsModule } from '@angular/forms';
|
|
5
|
-
import * as i3
|
|
3
|
+
import * as i1 from '@angular/forms';
|
|
4
|
+
import { FormControl, FormGroup, ReactiveFormsModule, FormArray, FormBuilder, FormsModule } from '@angular/forms';
|
|
5
|
+
import * as i3 from '@angular/common';
|
|
6
6
|
import { CommonModule } from '@angular/common';
|
|
7
7
|
import { DynamicFieldLoaderDirective } from '@praxisui/dynamic-fields';
|
|
8
8
|
import * as i2$1 from '@praxisui/core';
|
|
9
|
-
import { FieldControlType, PraxisIconDirective, INLINE_FILTER_CONTROL_TYPES, resolveInlineFilterControlType, normalizeControlTypeToken, ComponentMetadataRegistry } from '@praxisui/core';
|
|
9
|
+
import { FieldControlType, PraxisIconDirective, providePraxisI18n, PraxisI18nService, INLINE_FILTER_CONTROL_TYPES, resolveInlineFilterControlType, normalizeControlTypeToken, ComponentMetadataRegistry } from '@praxisui/core';
|
|
10
10
|
import * as i5 from '@angular/material/expansion';
|
|
11
11
|
import { MatExpansionModule } from '@angular/material/expansion';
|
|
12
12
|
import * as i6 from '@angular/material/icon';
|
|
13
13
|
import { MatIconModule } from '@angular/material/icon';
|
|
14
|
-
import * as
|
|
14
|
+
import * as i4 from '@angular/material/button';
|
|
15
15
|
import { MatButtonModule } from '@angular/material/button';
|
|
16
|
-
import * as i1 from '@angular/material/dialog';
|
|
16
|
+
import * as i1$1 from '@angular/material/dialog';
|
|
17
17
|
import { MatDialogRef, MatDialogModule } from '@angular/material/dialog';
|
|
18
18
|
import * as i8 from '@angular/material/tooltip';
|
|
19
19
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
@@ -22,19 +22,19 @@ import { MatListModule } from '@angular/material/list';
|
|
|
22
22
|
import { SETTINGS_PANEL_DATA } from '@praxisui/settings-panel';
|
|
23
23
|
import { BehaviorSubject } from 'rxjs';
|
|
24
24
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
25
|
-
import * as
|
|
25
|
+
import * as i6$1 from '@angular/material/input';
|
|
26
26
|
import { MatInputModule } from '@angular/material/input';
|
|
27
27
|
import { MatChipsModule } from '@angular/material/chips';
|
|
28
28
|
import { MatDividerModule } from '@angular/material/divider';
|
|
29
|
-
import * as
|
|
29
|
+
import * as i7 from '@angular/material/select';
|
|
30
30
|
import { MatSelectModule } from '@angular/material/select';
|
|
31
|
-
import * as
|
|
31
|
+
import * as i8$1 from '@angular/material/checkbox';
|
|
32
32
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
|
33
|
-
import * as
|
|
33
|
+
import * as i10 from '@angular/material/menu';
|
|
34
34
|
import { MatMenuModule } from '@angular/material/menu';
|
|
35
|
-
import * as
|
|
35
|
+
import * as i11 from '@angular/material/autocomplete';
|
|
36
36
|
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
|
37
|
-
import * as
|
|
37
|
+
import * as i12 from '@angular/cdk/scrolling';
|
|
38
38
|
import { ScrollingModule } from '@angular/cdk/scrolling';
|
|
39
39
|
|
|
40
40
|
class ConfigRegistryService {
|
|
@@ -51,10 +51,10 @@ class ConfigRegistryService {
|
|
|
51
51
|
count: props.length,
|
|
52
52
|
}));
|
|
53
53
|
}
|
|
54
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
55
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
54
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ConfigRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
55
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ConfigRegistryService, providedIn: 'root' });
|
|
56
56
|
}
|
|
57
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
57
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ConfigRegistryService, decorators: [{
|
|
58
58
|
type: Injectable,
|
|
59
59
|
args: [{ providedIn: 'root' }]
|
|
60
60
|
}] });
|
|
@@ -69,10 +69,10 @@ class ContextValidatorRegistryService {
|
|
|
69
69
|
get(controlType) {
|
|
70
70
|
return this.map.get(controlType) ?? [];
|
|
71
71
|
}
|
|
72
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
73
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
72
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ContextValidatorRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
73
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ContextValidatorRegistryService, providedIn: 'root' });
|
|
74
74
|
}
|
|
75
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
75
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ContextValidatorRegistryService, decorators: [{
|
|
76
76
|
type: Injectable,
|
|
77
77
|
args: [{ providedIn: 'root' }]
|
|
78
78
|
}] });
|
|
@@ -182,10 +182,10 @@ class SchemaNormalizerService {
|
|
|
182
182
|
getByPath(obj, path) {
|
|
183
183
|
return path.split('.').reduce((acc, key) => (acc == null ? acc : acc[key]), obj);
|
|
184
184
|
}
|
|
185
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
186
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
185
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: SchemaNormalizerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
186
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: SchemaNormalizerService, providedIn: 'root' });
|
|
187
187
|
}
|
|
188
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
188
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: SchemaNormalizerService, decorators: [{
|
|
189
189
|
type: Injectable,
|
|
190
190
|
args: [{ providedIn: 'root' }]
|
|
191
191
|
}] });
|
|
@@ -276,13 +276,13 @@ class DynamicFormFactoryService {
|
|
|
276
276
|
}
|
|
277
277
|
return result;
|
|
278
278
|
}
|
|
279
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
280
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
279
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicFormFactoryService, deps: [{ token: i1.FormBuilder }, { token: ContextValidatorRegistryService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
280
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicFormFactoryService, providedIn: 'root' });
|
|
281
281
|
}
|
|
282
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
282
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicFormFactoryService, decorators: [{
|
|
283
283
|
type: Injectable,
|
|
284
284
|
args: [{ providedIn: 'root' }]
|
|
285
|
-
}], ctorParameters: () => [{ type:
|
|
285
|
+
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: ContextValidatorRegistryService }] });
|
|
286
286
|
|
|
287
287
|
class EditorComponentRegistryService {
|
|
288
288
|
registry = new Map();
|
|
@@ -305,10 +305,10 @@ class EditorComponentRegistryService {
|
|
|
305
305
|
isRegistered(editorType) {
|
|
306
306
|
return this.registry.has(editorType);
|
|
307
307
|
}
|
|
308
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
309
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
308
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: EditorComponentRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
309
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: EditorComponentRegistryService, providedIn: 'root' });
|
|
310
310
|
}
|
|
311
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
311
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: EditorComponentRegistryService, decorators: [{
|
|
312
312
|
type: Injectable,
|
|
313
313
|
args: [{ providedIn: 'root' }]
|
|
314
314
|
}] });
|
|
@@ -349,8 +349,8 @@ class RegexHelpDialogComponent {
|
|
|
349
349
|
choose(pattern) {
|
|
350
350
|
this.dialogRef.close(pattern);
|
|
351
351
|
}
|
|
352
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
353
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
352
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: RegexHelpDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
353
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: RegexHelpDialogComponent, isStandalone: true, selector: "praxis-regex-help-dialog", ngImport: i0, template: `
|
|
354
354
|
<h2 mat-dialog-title>Modelos de Expressões Regulares</h2>
|
|
355
355
|
<div mat-dialog-content>
|
|
356
356
|
<p>Selecione um dos modelos abaixo para preencher o campo ou use como base:</p>
|
|
@@ -371,14 +371,14 @@ class RegexHelpDialogComponent {
|
|
|
371
371
|
<div mat-dialog-actions>
|
|
372
372
|
<button class="mat-mdc-button mdc-button" mat-dialog-close>Fechar</button>
|
|
373
373
|
</div>
|
|
374
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type:
|
|
374
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1$1.MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: i1$1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1$1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1$1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatListModule }, { kind: "component", type: i2.MatNavList, selector: "mat-nav-list", exportAs: ["matNavList"] }, { kind: "component", type: i2.MatListItem, selector: "mat-list-item, a[mat-list-item], button[mat-list-item]", inputs: ["activated"], exportAs: ["matListItem"] }, { kind: "directive", type: i2.MatListItemLine, selector: "[matListItemLine]" }, { kind: "directive", type: i2.MatListItemTitle, selector: "[matListItemTitle]" }, { kind: "ngmodule", type: MatButtonModule }] });
|
|
375
375
|
}
|
|
376
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
376
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: RegexHelpDialogComponent, decorators: [{
|
|
377
377
|
type: Component,
|
|
378
378
|
args: [{
|
|
379
379
|
selector: 'praxis-regex-help-dialog',
|
|
380
380
|
standalone: true,
|
|
381
|
-
imports: [
|
|
381
|
+
imports: [MatDialogModule, MatListModule, MatButtonModule],
|
|
382
382
|
template: `
|
|
383
383
|
<h2 mat-dialog-title>Modelos de Expressões Regulares</h2>
|
|
384
384
|
<div mat-dialog-content>
|
|
@@ -530,6 +530,7 @@ class DynamicEditorRendererComponent {
|
|
|
530
530
|
const order = [
|
|
531
531
|
'geral',
|
|
532
532
|
'apresent',
|
|
533
|
+
'display',
|
|
533
534
|
'valida',
|
|
534
535
|
'formato',
|
|
535
536
|
'comport',
|
|
@@ -594,7 +595,7 @@ class DynamicEditorRendererComponent {
|
|
|
594
595
|
.replace(/[ñ]/g, 'n');
|
|
595
596
|
if (normalized.includes('geral'))
|
|
596
597
|
return 'widgets';
|
|
597
|
-
if (normalized.includes('apresent'))
|
|
598
|
+
if (normalized.includes('apresent') || normalized.includes('display'))
|
|
598
599
|
return 'slideshow';
|
|
599
600
|
if (normalized.includes('dados') || normalized.includes('opcoes'))
|
|
600
601
|
return 'list_alt';
|
|
@@ -683,79 +684,87 @@ class DynamicEditorRendererComponent {
|
|
|
683
684
|
this.form?.markAsDirty();
|
|
684
685
|
}
|
|
685
686
|
}
|
|
686
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
687
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
687
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicEditorRendererComponent, deps: [{ token: i1$1.MatDialog }, { token: i2$1.IconPickerService }], target: i0.ɵɵFactoryTarget.Component });
|
|
688
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: DynamicEditorRendererComponent, isStandalone: true, selector: "praxis-dynamic-editor-renderer", inputs: { properties: "properties", form: "form" }, usesOnChanges: true, ngImport: i0, template: `
|
|
688
689
|
<ng-template #item let-field="field" let-index="index" let-content="content">
|
|
689
690
|
<div class="editor-item" [attr.data-control-type]="field.controlType">
|
|
690
691
|
<ng-container [ngTemplateOutlet]="content"></ng-container>
|
|
691
|
-
|
|
692
|
+
|
|
692
693
|
<div class="actions-container">
|
|
693
694
|
<!-- Botão de ajuda para regex -->
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
695
|
+
@if (field.name === 'pattern') {
|
|
696
|
+
<button
|
|
697
|
+
type="button"
|
|
698
|
+
mat-icon-button
|
|
699
|
+
class="regex-help-btn decorator-btn"
|
|
700
|
+
(click)="openRegexHelp()"
|
|
701
|
+
aria-label="Exemplos de regex"
|
|
702
|
+
matTooltip="Exemplos de regex"
|
|
703
|
+
>
|
|
704
|
+
<mat-icon [praxisIcon]="'search'"></mat-icon>
|
|
705
|
+
</button>
|
|
706
|
+
}
|
|
705
707
|
<!-- Botão para escolher ícone (prefix/suffix) -->
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
708
|
+
@if (isIconField(field.name)) {
|
|
709
|
+
<button
|
|
710
|
+
type="button"
|
|
711
|
+
mat-icon-button
|
|
712
|
+
class="icon-picker-btn decorator-btn"
|
|
713
|
+
(click)="openIconPicker(field.name)"
|
|
714
|
+
aria-label="Escolher ícone"
|
|
715
|
+
matTooltip="Escolher ícone"
|
|
716
|
+
>
|
|
717
|
+
<mat-icon [praxisIcon]="'apps'"></mat-icon>
|
|
718
|
+
</button>
|
|
719
|
+
}
|
|
717
720
|
<!-- Botão de ajuda (fallback quando suffix não é aplicado) -->
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
721
|
+
@if (field.hint && !useHelpSuffix(field)) {
|
|
722
|
+
<button
|
|
723
|
+
type="button"
|
|
724
|
+
mat-icon-button
|
|
725
|
+
class="hint-btn decorator-btn"
|
|
726
|
+
[class.has-icon-picker]="isIconField(field.name)"
|
|
727
|
+
[matTooltip]="field.hint"
|
|
728
|
+
aria-label="Ajuda"
|
|
729
|
+
>
|
|
730
|
+
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
731
|
+
</button>
|
|
732
|
+
}
|
|
729
733
|
</div>
|
|
730
734
|
</div>
|
|
731
735
|
</ng-template>
|
|
732
|
-
|
|
736
|
+
|
|
733
737
|
<div class="editor-surface">
|
|
734
738
|
<mat-accordion class="section-accordion" multi>
|
|
735
|
-
|
|
736
|
-
<mat-expansion-panel
|
|
737
|
-
<mat-panel-
|
|
738
|
-
<
|
|
739
|
-
<
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
739
|
+
@for (g of groupOrder; track g) {
|
|
740
|
+
<mat-expansion-panel class="section-card">
|
|
741
|
+
<mat-expansion-panel-header class="section-header">
|
|
742
|
+
<mat-panel-title>
|
|
743
|
+
<span class="section-title">
|
|
744
|
+
<mat-icon class="section-icon" [praxisIcon]="groupIcon(g)"></mat-icon>
|
|
745
|
+
{{ g }}
|
|
746
|
+
</span>
|
|
747
|
+
</mat-panel-title>
|
|
748
|
+
</mat-expansion-panel-header>
|
|
749
|
+
@for (row of groupRows[g]; track row) {
|
|
750
|
+
<div class="editor-row" [ngClass]="rowClass(row)">
|
|
751
|
+
@if (ready) {
|
|
752
|
+
<ng-container
|
|
753
|
+
dynamicFieldLoader
|
|
754
|
+
[fields]="row"
|
|
755
|
+
[formGroup]="form"
|
|
756
|
+
[itemTemplate]="item">
|
|
757
|
+
</ng-container>
|
|
758
|
+
}
|
|
759
|
+
</div>
|
|
760
|
+
}
|
|
761
|
+
</mat-expansion-panel>
|
|
762
|
+
}
|
|
754
763
|
</mat-accordion>
|
|
755
764
|
</div>
|
|
756
|
-
|
|
765
|
+
`, isInline: true, styles: [":host{display:block}.editor-surface{padding:8px 0 2px;border-radius:12px;background:transparent}.section-accordion{display:block}.section-card{margin:6px 0 8px;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);overflow:hidden}.section-card.mat-expanded{border-color:var(--md-sys-color-primary);box-shadow:0 2px 10px #0000000d}.section-card .mat-expansion-panel-body{background:var(--md-sys-color-surface-container-low)}.section-header{background:var(--md-sys-color-surface-container-low);border-bottom:1px solid var(--md-sys-color-outline-variant, var(--sicoob-stroke-medium));padding:2px 8px;min-height:32px;display:flex;align-items:center;color:var(--md-sys-color-on-surface)}.section-header .mat-expansion-panel-header-title,.section-header .mat-expansion-panel-header-description{display:flex;align-items:center;color:var(--md-sys-color-on-surface)}.section-title{display:inline-flex;align-items:center;gap:6px;font-weight:600;letter-spacing:.2px;color:var(--md-sys-color-on-surface);font-size:.9rem;line-height:1.2}.section-icon{color:var(--md-sys-color-primary, var(--md-sys-color-primary, #3f51b5));opacity:.9}.editor-row{display:flex;gap:12px;align-items:flex-start;flex-wrap:wrap;margin-bottom:8px}.editor-row>praxis-field-shell{flex:1 1 100%;min-width:280px}.editor-row praxis-field-shell .mat-mdc-form-field{width:100%;margin-bottom:8px}.editor-row.row-inline>praxis-field-shell{flex:0 0 auto}.editor-row.row-2>praxis-field-shell{flex:1 1 calc(50% - 12px);min-width:260px}.editor-row.row-3>praxis-field-shell{flex:1 1 calc(33.333% - 12px);min-width:220px}.editor-row>praxis-field-shell[data-field-name=controlType],.editor-row>praxis-field-shell[data-field-name=label],.editor-row>praxis-field-shell[data-field-name=placeholder]{flex:1 1 calc(50% - 12px);min-width:300px}praxis-field-shell[data-field-name=label] .mdc-floating-label,praxis-field-shell[data-field-name=placeholder] .mdc-floating-label{font-weight:600}.editor-row>praxis-field-shell[data-field-name=prefix],.editor-row>praxis-field-shell[data-field-name=suffix]{flex:0 0 200px;min-width:160px}.editor-row>praxis-field-shell[data-field-name=prefixIcon],.editor-row>praxis-field-shell[data-field-name=suffixIcon],.editor-row>praxis-field-shell[data-field-name=prefixIconColor],.editor-row>praxis-field-shell[data-field-name=suffixIconColor]{flex:1 1 auto;min-width:160px}.editor-item{display:flex;align-items:flex-start;gap:4px}.editor-item>ng-container,.editor-item>praxis-field-shell{flex:1;min-width:0}.actions-container{display:flex;align-items:center;gap:0;margin-top:12px;flex-shrink:0}.decorator-btn{position:static;color:var(--md-sys-color-primary, #3f51b5);opacity:.6;transform:scale(.8);width:32px;height:32px;line-height:32px;padding:0}.decorator-btn:hover{opacity:1}.icon-picker-btn,.hint-btn,.hint-btn.has-icon-picker{right:auto}.editor-row .mat-mdc-form-field-subscript-wrapper mat-hint{display:none!important}.editor-row .mat-mdc-form-field-subscript-wrapper mat-error{display:block!important}praxis-field-shell[data-field-name=showCharCount] .mdc-switch{transform:scale(1.05)}praxis-field-shell[data-field-name=showCharCount] .mat-mdc-slide-toggle{font-weight:600}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-hint{display:flex;align-items:center;gap:8px;background:var(--md-sys-color-tertiary-container);border:1px solid var(--md-sys-color-tertiary);color:var(--md-sys-color-on-tertiary-container);padding:8px 10px;border-radius:6px;line-height:1.3;white-space:normal}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-hint:before{content:\"\\26a0\\fe0f\";display:inline-block}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-subscript-wrapper{margin-top:6px;margin-bottom:10px;min-height:auto}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field{margin-bottom:16px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: DynamicFieldLoaderDirective, selector: "[dynamicFieldLoader]", inputs: ["fields", "formGroup", "enableExternalControlBinding", "itemTemplate", "debugTrace", "debugTraceLabel", "readonlyMode", "disabledMode", "presentationMode", "visible", "canvasMode"], outputs: ["componentsCreated", "fieldCreated", "fieldDestroyed", "renderError", "canvasMouseEnter", "canvasMouseLeave", "canvasClick", "canvasFocus"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "directive", type: i5.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i5.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i5.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i5.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i4.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
757
766
|
}
|
|
758
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
767
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicEditorRendererComponent, decorators: [{
|
|
759
768
|
type: Component,
|
|
760
769
|
args: [{ selector: 'praxis-dynamic-editor-renderer', standalone: true, imports: [
|
|
761
770
|
CommonModule,
|
|
@@ -771,78 +780,915 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
771
780
|
<ng-template #item let-field="field" let-index="index" let-content="content">
|
|
772
781
|
<div class="editor-item" [attr.data-control-type]="field.controlType">
|
|
773
782
|
<ng-container [ngTemplateOutlet]="content"></ng-container>
|
|
774
|
-
|
|
783
|
+
|
|
775
784
|
<div class="actions-container">
|
|
776
785
|
<!-- Botão de ajuda para regex -->
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
786
|
+
@if (field.name === 'pattern') {
|
|
787
|
+
<button
|
|
788
|
+
type="button"
|
|
789
|
+
mat-icon-button
|
|
790
|
+
class="regex-help-btn decorator-btn"
|
|
791
|
+
(click)="openRegexHelp()"
|
|
792
|
+
aria-label="Exemplos de regex"
|
|
793
|
+
matTooltip="Exemplos de regex"
|
|
794
|
+
>
|
|
795
|
+
<mat-icon [praxisIcon]="'search'"></mat-icon>
|
|
796
|
+
</button>
|
|
797
|
+
}
|
|
788
798
|
<!-- Botão para escolher ícone (prefix/suffix) -->
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
799
|
+
@if (isIconField(field.name)) {
|
|
800
|
+
<button
|
|
801
|
+
type="button"
|
|
802
|
+
mat-icon-button
|
|
803
|
+
class="icon-picker-btn decorator-btn"
|
|
804
|
+
(click)="openIconPicker(field.name)"
|
|
805
|
+
aria-label="Escolher ícone"
|
|
806
|
+
matTooltip="Escolher ícone"
|
|
807
|
+
>
|
|
808
|
+
<mat-icon [praxisIcon]="'apps'"></mat-icon>
|
|
809
|
+
</button>
|
|
810
|
+
}
|
|
800
811
|
<!-- Botão de ajuda (fallback quando suffix não é aplicado) -->
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
+
@if (field.hint && !useHelpSuffix(field)) {
|
|
813
|
+
<button
|
|
814
|
+
type="button"
|
|
815
|
+
mat-icon-button
|
|
816
|
+
class="hint-btn decorator-btn"
|
|
817
|
+
[class.has-icon-picker]="isIconField(field.name)"
|
|
818
|
+
[matTooltip]="field.hint"
|
|
819
|
+
aria-label="Ajuda"
|
|
820
|
+
>
|
|
821
|
+
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
822
|
+
</button>
|
|
823
|
+
}
|
|
812
824
|
</div>
|
|
813
825
|
</div>
|
|
814
826
|
</ng-template>
|
|
815
|
-
|
|
827
|
+
|
|
816
828
|
<div class="editor-surface">
|
|
817
829
|
<mat-accordion class="section-accordion" multi>
|
|
818
|
-
|
|
819
|
-
<mat-expansion-panel
|
|
820
|
-
<mat-panel-
|
|
821
|
-
<
|
|
822
|
-
<
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
830
|
+
@for (g of groupOrder; track g) {
|
|
831
|
+
<mat-expansion-panel class="section-card">
|
|
832
|
+
<mat-expansion-panel-header class="section-header">
|
|
833
|
+
<mat-panel-title>
|
|
834
|
+
<span class="section-title">
|
|
835
|
+
<mat-icon class="section-icon" [praxisIcon]="groupIcon(g)"></mat-icon>
|
|
836
|
+
{{ g }}
|
|
837
|
+
</span>
|
|
838
|
+
</mat-panel-title>
|
|
839
|
+
</mat-expansion-panel-header>
|
|
840
|
+
@for (row of groupRows[g]; track row) {
|
|
841
|
+
<div class="editor-row" [ngClass]="rowClass(row)">
|
|
842
|
+
@if (ready) {
|
|
843
|
+
<ng-container
|
|
844
|
+
dynamicFieldLoader
|
|
845
|
+
[fields]="row"
|
|
846
|
+
[formGroup]="form"
|
|
847
|
+
[itemTemplate]="item">
|
|
848
|
+
</ng-container>
|
|
849
|
+
}
|
|
850
|
+
</div>
|
|
851
|
+
}
|
|
852
|
+
</mat-expansion-panel>
|
|
853
|
+
}
|
|
837
854
|
</mat-accordion>
|
|
838
855
|
</div>
|
|
839
|
-
|
|
840
|
-
}], ctorParameters: () => [{ type: i1.MatDialog }, { type: i2$1.IconPickerService }], propDecorators: { properties: [{
|
|
856
|
+
`, styles: [":host{display:block}.editor-surface{padding:8px 0 2px;border-radius:12px;background:transparent}.section-accordion{display:block}.section-card{margin:6px 0 8px;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);overflow:hidden}.section-card.mat-expanded{border-color:var(--md-sys-color-primary);box-shadow:0 2px 10px #0000000d}.section-card .mat-expansion-panel-body{background:var(--md-sys-color-surface-container-low)}.section-header{background:var(--md-sys-color-surface-container-low);border-bottom:1px solid var(--md-sys-color-outline-variant, var(--sicoob-stroke-medium));padding:2px 8px;min-height:32px;display:flex;align-items:center;color:var(--md-sys-color-on-surface)}.section-header .mat-expansion-panel-header-title,.section-header .mat-expansion-panel-header-description{display:flex;align-items:center;color:var(--md-sys-color-on-surface)}.section-title{display:inline-flex;align-items:center;gap:6px;font-weight:600;letter-spacing:.2px;color:var(--md-sys-color-on-surface);font-size:.9rem;line-height:1.2}.section-icon{color:var(--md-sys-color-primary, var(--md-sys-color-primary, #3f51b5));opacity:.9}.editor-row{display:flex;gap:12px;align-items:flex-start;flex-wrap:wrap;margin-bottom:8px}.editor-row>praxis-field-shell{flex:1 1 100%;min-width:280px}.editor-row praxis-field-shell .mat-mdc-form-field{width:100%;margin-bottom:8px}.editor-row.row-inline>praxis-field-shell{flex:0 0 auto}.editor-row.row-2>praxis-field-shell{flex:1 1 calc(50% - 12px);min-width:260px}.editor-row.row-3>praxis-field-shell{flex:1 1 calc(33.333% - 12px);min-width:220px}.editor-row>praxis-field-shell[data-field-name=controlType],.editor-row>praxis-field-shell[data-field-name=label],.editor-row>praxis-field-shell[data-field-name=placeholder]{flex:1 1 calc(50% - 12px);min-width:300px}praxis-field-shell[data-field-name=label] .mdc-floating-label,praxis-field-shell[data-field-name=placeholder] .mdc-floating-label{font-weight:600}.editor-row>praxis-field-shell[data-field-name=prefix],.editor-row>praxis-field-shell[data-field-name=suffix]{flex:0 0 200px;min-width:160px}.editor-row>praxis-field-shell[data-field-name=prefixIcon],.editor-row>praxis-field-shell[data-field-name=suffixIcon],.editor-row>praxis-field-shell[data-field-name=prefixIconColor],.editor-row>praxis-field-shell[data-field-name=suffixIconColor]{flex:1 1 auto;min-width:160px}.editor-item{display:flex;align-items:flex-start;gap:4px}.editor-item>ng-container,.editor-item>praxis-field-shell{flex:1;min-width:0}.actions-container{display:flex;align-items:center;gap:0;margin-top:12px;flex-shrink:0}.decorator-btn{position:static;color:var(--md-sys-color-primary, #3f51b5);opacity:.6;transform:scale(.8);width:32px;height:32px;line-height:32px;padding:0}.decorator-btn:hover{opacity:1}.icon-picker-btn,.hint-btn,.hint-btn.has-icon-picker{right:auto}.editor-row .mat-mdc-form-field-subscript-wrapper mat-hint{display:none!important}.editor-row .mat-mdc-form-field-subscript-wrapper mat-error{display:block!important}praxis-field-shell[data-field-name=showCharCount] .mdc-switch{transform:scale(1.05)}praxis-field-shell[data-field-name=showCharCount] .mat-mdc-slide-toggle{font-weight:600}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-hint{display:flex;align-items:center;gap:8px;background:var(--md-sys-color-tertiary-container);border:1px solid var(--md-sys-color-tertiary);color:var(--md-sys-color-on-tertiary-container);padding:8px 10px;border-radius:6px;line-height:1.3;white-space:normal}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-hint:before{content:\"\\26a0\\fe0f\";display:inline-block}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field-subscript-wrapper{margin-top:6px;margin-bottom:10px;min-height:auto}praxis-field-shell[data-field-name=controlType] .mat-mdc-form-field{margin-bottom:16px}\n"] }]
|
|
857
|
+
}], ctorParameters: () => [{ type: i1$1.MatDialog }, { type: i2$1.IconPickerService }], propDecorators: { properties: [{
|
|
841
858
|
type: Input
|
|
842
859
|
}], form: [{
|
|
843
860
|
type: Input
|
|
844
861
|
}] } });
|
|
845
862
|
|
|
863
|
+
const METADATA_EDITOR_EN_US = {
|
|
864
|
+
'praxis.metadataEditor.arrayFields.section': 'Collection subfields',
|
|
865
|
+
'praxis.metadataEditor.arrayFields.title': 'Subfields',
|
|
866
|
+
'praxis.metadataEditor.arrayFields.add': 'Add subfield',
|
|
867
|
+
'praxis.metadataEditor.arrayFields.duplicateNames': 'There are duplicate names: {{names}}',
|
|
868
|
+
'praxis.metadataEditor.arrayFields.empty': 'No local subfields configured.',
|
|
869
|
+
'praxis.metadataEditor.arrayFields.name': 'Name',
|
|
870
|
+
'praxis.metadataEditor.arrayFields.nameRequired': 'Enter the subfield name.',
|
|
871
|
+
'praxis.metadataEditor.arrayFields.nameDuplicated': 'This name is already used in the collection.',
|
|
872
|
+
'praxis.metadataEditor.arrayFields.label': 'Label',
|
|
873
|
+
'praxis.metadataEditor.arrayFields.controlType': 'Control',
|
|
874
|
+
'praxis.metadataEditor.arrayFields.required': 'Required',
|
|
875
|
+
'praxis.metadataEditor.arrayFields.moveUp': 'Move up',
|
|
876
|
+
'praxis.metadataEditor.arrayFields.moveDown': 'Move down',
|
|
877
|
+
'praxis.metadataEditor.arrayFields.duplicate': 'Duplicate',
|
|
878
|
+
'praxis.metadataEditor.arrayFields.remove': 'Remove',
|
|
879
|
+
'praxis.metadataEditor.arrayFields.removeBlocked': 'Remove references before deleting: {{references}}',
|
|
880
|
+
'praxis.metadataEditor.arrayFields.refOnly': 'The item schema comes from itemSchemaRef; create local subfields only when you need an override.',
|
|
881
|
+
'praxis.metadataEditor.arrayFields.overrideLocal': 'There is an itemSchemaRef and local subfields; local subfields work as an explicit override.',
|
|
882
|
+
'praxis.metadataEditor.arrayFields.imported': 'Subfields imported. Review them before applying or saving.',
|
|
883
|
+
'praxis.metadataEditor.arrayFields.localOnly': 'Local subfields will be saved in array.itemSchema.fields.',
|
|
884
|
+
'praxis.metadataEditor.arrayFields.control.input': 'Text',
|
|
885
|
+
'praxis.metadataEditor.arrayFields.control.textarea': 'Long text',
|
|
886
|
+
'praxis.metadataEditor.arrayFields.control.numeric': 'Number',
|
|
887
|
+
'praxis.metadataEditor.arrayFields.control.currency': 'Currency',
|
|
888
|
+
'praxis.metadataEditor.arrayFields.control.email': 'Email',
|
|
889
|
+
'praxis.metadataEditor.arrayFields.control.phone': 'Phone',
|
|
890
|
+
'praxis.metadataEditor.arrayFields.control.select': 'Select',
|
|
891
|
+
'praxis.metadataEditor.arrayFields.control.entityLookup': 'Entity lookup',
|
|
892
|
+
'praxis.metadataEditor.arrayFields.control.toggle': 'Toggle',
|
|
893
|
+
'praxis.metadataEditor.arrayFields.control.date': 'Date',
|
|
894
|
+
};
|
|
895
|
+
|
|
896
|
+
const METADATA_EDITOR_PT_BR = {
|
|
897
|
+
'praxis.metadataEditor.arrayFields.section': 'Subcampos da cole\u00e7\u00e3o',
|
|
898
|
+
'praxis.metadataEditor.arrayFields.title': 'Subcampos',
|
|
899
|
+
'praxis.metadataEditor.arrayFields.add': 'Adicionar subcampo',
|
|
900
|
+
'praxis.metadataEditor.arrayFields.duplicateNames': 'Existem nomes duplicados: {{names}}',
|
|
901
|
+
'praxis.metadataEditor.arrayFields.empty': 'Nenhum subcampo local configurado.',
|
|
902
|
+
'praxis.metadataEditor.arrayFields.name': 'Nome',
|
|
903
|
+
'praxis.metadataEditor.arrayFields.nameRequired': 'Informe o nome do subcampo.',
|
|
904
|
+
'praxis.metadataEditor.arrayFields.nameDuplicated': 'Este nome j\u00e1 est\u00e1 em uso na cole\u00e7\u00e3o.',
|
|
905
|
+
'praxis.metadataEditor.arrayFields.label': 'Label',
|
|
906
|
+
'praxis.metadataEditor.arrayFields.controlType': 'Controle',
|
|
907
|
+
'praxis.metadataEditor.arrayFields.required': 'Obrigat\u00f3rio',
|
|
908
|
+
'praxis.metadataEditor.arrayFields.moveUp': 'Subir',
|
|
909
|
+
'praxis.metadataEditor.arrayFields.moveDown': 'Descer',
|
|
910
|
+
'praxis.metadataEditor.arrayFields.duplicate': 'Duplicar',
|
|
911
|
+
'praxis.metadataEditor.arrayFields.remove': 'Remover',
|
|
912
|
+
'praxis.metadataEditor.arrayFields.removeBlocked': 'Remova as refer\u00eancias antes de excluir: {{references}}',
|
|
913
|
+
'praxis.metadataEditor.arrayFields.refOnly': 'O schema do item vem do itemSchemaRef; crie subcampos locais apenas quando precisar de override.',
|
|
914
|
+
'praxis.metadataEditor.arrayFields.overrideLocal': 'H\u00e1 um itemSchemaRef e subcampos locais; os subcampos locais funcionam como override expl\u00edcito.',
|
|
915
|
+
'praxis.metadataEditor.arrayFields.imported': 'Subcampos importados. Revise antes de aplicar ou salvar.',
|
|
916
|
+
'praxis.metadataEditor.arrayFields.localOnly': 'Subcampos locais ser\u00e3o salvos em array.itemSchema.fields.',
|
|
917
|
+
'praxis.metadataEditor.arrayFields.control.input': 'Texto',
|
|
918
|
+
'praxis.metadataEditor.arrayFields.control.textarea': 'Texto longo',
|
|
919
|
+
'praxis.metadataEditor.arrayFields.control.numeric': 'N\u00famero',
|
|
920
|
+
'praxis.metadataEditor.arrayFields.control.currency': 'Moeda',
|
|
921
|
+
'praxis.metadataEditor.arrayFields.control.email': 'E-mail',
|
|
922
|
+
'praxis.metadataEditor.arrayFields.control.phone': 'Telefone',
|
|
923
|
+
'praxis.metadataEditor.arrayFields.control.select': 'Sele\u00e7\u00e3o',
|
|
924
|
+
'praxis.metadataEditor.arrayFields.control.entityLookup': 'Busca de entidade',
|
|
925
|
+
'praxis.metadataEditor.arrayFields.control.toggle': 'Liga/desliga',
|
|
926
|
+
'praxis.metadataEditor.arrayFields.control.date': 'Data',
|
|
927
|
+
};
|
|
928
|
+
|
|
929
|
+
function createPraxisMetadataEditorI18nConfig(options = {}) {
|
|
930
|
+
const dictionaries = {
|
|
931
|
+
'pt-BR': {
|
|
932
|
+
...METADATA_EDITOR_PT_BR,
|
|
933
|
+
...(options.dictionaries?.['pt-BR'] ?? {}),
|
|
934
|
+
},
|
|
935
|
+
'en-US': {
|
|
936
|
+
...METADATA_EDITOR_EN_US,
|
|
937
|
+
...(options.dictionaries?.['en-US'] ?? {}),
|
|
938
|
+
},
|
|
939
|
+
};
|
|
940
|
+
for (const [locale, dictionary] of Object.entries(options.dictionaries ?? {})) {
|
|
941
|
+
if (locale === 'pt-BR' || locale === 'en-US') {
|
|
942
|
+
continue;
|
|
943
|
+
}
|
|
944
|
+
dictionaries[locale] = {
|
|
945
|
+
...(dictionaries[locale] ?? {}),
|
|
946
|
+
...dictionary,
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
return {
|
|
950
|
+
locale: options.locale,
|
|
951
|
+
fallbackLocale: options.fallbackLocale ?? 'pt-BR',
|
|
952
|
+
dictionaries,
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
const METADATA_EDITOR_I18N_CONFIG = createPraxisMetadataEditorI18nConfig();
|
|
956
|
+
function providePraxisMetadataEditorI18n(options = {}) {
|
|
957
|
+
return providePraxisI18n(createPraxisMetadataEditorI18nConfig(options));
|
|
958
|
+
}
|
|
959
|
+
function resolvePraxisMetadataEditorText(i18n, key, fallback) {
|
|
960
|
+
const namespacedKey = key.startsWith('praxis.metadataEditor.')
|
|
961
|
+
? key
|
|
962
|
+
: `praxis.metadataEditor.${key}`;
|
|
963
|
+
return i18n.t(namespacedKey, undefined, fallback) || fallback;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
class ArrayItemSchemaFieldsEditorComponent {
|
|
967
|
+
static nextInstanceId = 0;
|
|
968
|
+
control = null;
|
|
969
|
+
arrayControl = null;
|
|
970
|
+
formArray = new FormArray([]);
|
|
971
|
+
form = new FormGroup({ fields: this.formArray });
|
|
972
|
+
controlTypeOptions = [
|
|
973
|
+
FieldControlType.INPUT,
|
|
974
|
+
FieldControlType.TEXTAREA,
|
|
975
|
+
FieldControlType.NUMERIC_TEXT_BOX,
|
|
976
|
+
FieldControlType.CURRENCY_INPUT,
|
|
977
|
+
FieldControlType.EMAIL_INPUT,
|
|
978
|
+
FieldControlType.PHONE,
|
|
979
|
+
FieldControlType.SELECT,
|
|
980
|
+
FieldControlType.ENTITY_LOOKUP,
|
|
981
|
+
FieldControlType.TOGGLE,
|
|
982
|
+
FieldControlType.DATE_INPUT,
|
|
983
|
+
];
|
|
984
|
+
schemaSourceState = 'local-only';
|
|
985
|
+
duplicateNames = [];
|
|
986
|
+
fb = inject(FormBuilder);
|
|
987
|
+
i18n = inject(PraxisI18nService);
|
|
988
|
+
instanceId = ++ArrayItemSchemaFieldsEditorComponent.nextInstanceId;
|
|
989
|
+
externalSubscription;
|
|
990
|
+
internalSubscription;
|
|
991
|
+
originalNames = [];
|
|
992
|
+
importedFieldsFingerprint = null;
|
|
993
|
+
hydrating = false;
|
|
994
|
+
get rows() {
|
|
995
|
+
return this.formArray.controls;
|
|
996
|
+
}
|
|
997
|
+
ngOnChanges(changes) {
|
|
998
|
+
if (changes['control']) {
|
|
999
|
+
this.externalSubscription?.unsubscribe();
|
|
1000
|
+
this.hydrateFromControl();
|
|
1001
|
+
this.externalSubscription = this.control?.valueChanges.subscribe(() => {
|
|
1002
|
+
if (!this.hydrating) {
|
|
1003
|
+
this.hydrateFromControl();
|
|
1004
|
+
}
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
if (changes['arrayControl'] || changes['control']) {
|
|
1008
|
+
this.updateSchemaSourceState();
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
ngOnDestroy() {
|
|
1012
|
+
this.externalSubscription?.unsubscribe();
|
|
1013
|
+
this.internalSubscription?.unsubscribe();
|
|
1014
|
+
}
|
|
1015
|
+
text(key, fallback, fallbackWithParams, params) {
|
|
1016
|
+
return this.i18n.t(key, params, fallbackWithParams ?? fallback);
|
|
1017
|
+
}
|
|
1018
|
+
controlTypeLabel(controlType) {
|
|
1019
|
+
const labels = {
|
|
1020
|
+
[FieldControlType.INPUT]: this.text('praxis.metadataEditor.arrayFields.control.input', 'Texto'),
|
|
1021
|
+
[FieldControlType.TEXTAREA]: this.text('praxis.metadataEditor.arrayFields.control.textarea', 'Texto longo'),
|
|
1022
|
+
[FieldControlType.NUMERIC_TEXT_BOX]: this.text('praxis.metadataEditor.arrayFields.control.numeric', 'N\u00famero'),
|
|
1023
|
+
[FieldControlType.CURRENCY_INPUT]: this.text('praxis.metadataEditor.arrayFields.control.currency', 'Moeda'),
|
|
1024
|
+
[FieldControlType.EMAIL_INPUT]: this.text('praxis.metadataEditor.arrayFields.control.email', 'E-mail'),
|
|
1025
|
+
[FieldControlType.PHONE]: this.text('praxis.metadataEditor.arrayFields.control.phone', 'Telefone'),
|
|
1026
|
+
[FieldControlType.SELECT]: this.text('praxis.metadataEditor.arrayFields.control.select', 'Sele\u00e7\u00e3o'),
|
|
1027
|
+
[FieldControlType.ENTITY_LOOKUP]: this.text('praxis.metadataEditor.arrayFields.control.entityLookup', 'Busca de entidade'),
|
|
1028
|
+
[FieldControlType.TOGGLE]: this.text('praxis.metadataEditor.arrayFields.control.toggle', 'Liga/desliga'),
|
|
1029
|
+
[FieldControlType.DATE_INPUT]: this.text('praxis.metadataEditor.arrayFields.control.date', 'Data'),
|
|
1030
|
+
};
|
|
1031
|
+
return labels[controlType] ?? controlType;
|
|
1032
|
+
}
|
|
1033
|
+
addField(seed = {}) {
|
|
1034
|
+
const fieldName = seed.name || this.nextAvailableName();
|
|
1035
|
+
this.formArray.push(this.createRow({
|
|
1036
|
+
...seed,
|
|
1037
|
+
name: fieldName,
|
|
1038
|
+
label: seed.label || fieldName,
|
|
1039
|
+
controlType: seed.controlType || FieldControlType.INPUT,
|
|
1040
|
+
required: seed.required ?? false,
|
|
1041
|
+
}));
|
|
1042
|
+
this.originalNames.push(fieldName);
|
|
1043
|
+
this.syncToControl();
|
|
1044
|
+
}
|
|
1045
|
+
duplicateField(index) {
|
|
1046
|
+
const current = this.rowValue(index);
|
|
1047
|
+
if (!current)
|
|
1048
|
+
return;
|
|
1049
|
+
const nextName = this.nextAvailableName(`${current.name || 'field'}Copy`);
|
|
1050
|
+
this.addField({
|
|
1051
|
+
...current,
|
|
1052
|
+
name: nextName,
|
|
1053
|
+
label: nextName,
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
1056
|
+
removeField(index) {
|
|
1057
|
+
const name = String(this.formArray.at(index)?.get('name')?.value || '').trim();
|
|
1058
|
+
if (this.isReferenced(name)) {
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
this.formArray.removeAt(index);
|
|
1062
|
+
this.originalNames.splice(index, 1);
|
|
1063
|
+
this.syncToControl();
|
|
1064
|
+
}
|
|
1065
|
+
moveField(index, delta) {
|
|
1066
|
+
const target = index + delta;
|
|
1067
|
+
if (target < 0 || target >= this.formArray.length)
|
|
1068
|
+
return;
|
|
1069
|
+
const row = this.formArray.at(index);
|
|
1070
|
+
this.formArray.removeAt(index);
|
|
1071
|
+
this.formArray.insert(target, row);
|
|
1072
|
+
const [originalName] = this.originalNames.splice(index, 1);
|
|
1073
|
+
this.originalNames.splice(target, 0, originalName);
|
|
1074
|
+
this.syncToControl();
|
|
1075
|
+
}
|
|
1076
|
+
onNameBlur(index) {
|
|
1077
|
+
const previous = this.originalNames[index];
|
|
1078
|
+
const next = String(this.formArray.at(index)?.get('name')?.value || '').trim();
|
|
1079
|
+
if (previous && !next && this.isReferenced(previous)) {
|
|
1080
|
+
this.formArray.at(index)?.get('name')?.setValue(previous, { emitEvent: false });
|
|
1081
|
+
this.syncToControl();
|
|
1082
|
+
return;
|
|
1083
|
+
}
|
|
1084
|
+
if (previous && next && previous !== next) {
|
|
1085
|
+
this.renameReferences(previous, next);
|
|
1086
|
+
this.originalNames[index] = next;
|
|
1087
|
+
}
|
|
1088
|
+
this.syncToControl();
|
|
1089
|
+
}
|
|
1090
|
+
importFieldsFromJson(raw) {
|
|
1091
|
+
try {
|
|
1092
|
+
const parsed = JSON.parse(String(raw || '').trim());
|
|
1093
|
+
if (!Array.isArray(parsed) ||
|
|
1094
|
+
parsed.some((entry) => !this.isImportableField(entry))) {
|
|
1095
|
+
return false;
|
|
1096
|
+
}
|
|
1097
|
+
this.replaceRows(parsed);
|
|
1098
|
+
this.schemaSourceState = 'imported';
|
|
1099
|
+
this.importedFieldsFingerprint = this.fieldsFingerprint();
|
|
1100
|
+
this.syncToControl();
|
|
1101
|
+
return true;
|
|
1102
|
+
}
|
|
1103
|
+
catch {
|
|
1104
|
+
return false;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
isImportableField(value) {
|
|
1108
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
1109
|
+
return false;
|
|
1110
|
+
}
|
|
1111
|
+
const raw = value;
|
|
1112
|
+
return typeof raw.name === 'string' && raw.name.trim().length > 0;
|
|
1113
|
+
}
|
|
1114
|
+
isNameDuplicated(index) {
|
|
1115
|
+
const name = String(this.formArray.at(index)?.get('name')?.value || '').trim();
|
|
1116
|
+
return !!name && this.duplicateNames.includes(name);
|
|
1117
|
+
}
|
|
1118
|
+
isNameInvalid(index) {
|
|
1119
|
+
const control = this.formArray.at(index)?.get('name');
|
|
1120
|
+
return !!control?.invalid;
|
|
1121
|
+
}
|
|
1122
|
+
nameErrorId(index) {
|
|
1123
|
+
return `praxis-array-field-name-error-${this.instanceId}-${index}`;
|
|
1124
|
+
}
|
|
1125
|
+
nameErrorDescribedBy(index) {
|
|
1126
|
+
return this.nameErrorMessage(index) ? this.nameErrorId(index) : null;
|
|
1127
|
+
}
|
|
1128
|
+
nameErrorMessage(index) {
|
|
1129
|
+
const control = this.formArray.at(index)?.get('name');
|
|
1130
|
+
if (!control?.errors) {
|
|
1131
|
+
return '';
|
|
1132
|
+
}
|
|
1133
|
+
if (control.errors['arrayFieldNameRequired']) {
|
|
1134
|
+
return this.text('praxis.metadataEditor.arrayFields.nameRequired', 'Informe o nome do subcampo.');
|
|
1135
|
+
}
|
|
1136
|
+
if (control.errors['arrayFieldNameDuplicated']) {
|
|
1137
|
+
return this.text('praxis.metadataEditor.arrayFields.nameDuplicated', 'Este nome j\u00e1 est\u00e1 em uso na cole\u00e7\u00e3o.');
|
|
1138
|
+
}
|
|
1139
|
+
return '';
|
|
1140
|
+
}
|
|
1141
|
+
isReferenced(fieldName) {
|
|
1142
|
+
return this.referencedBy(fieldName).length > 0;
|
|
1143
|
+
}
|
|
1144
|
+
removeTitle(fieldName) {
|
|
1145
|
+
const references = this.referencedBy(fieldName);
|
|
1146
|
+
if (!references.length) {
|
|
1147
|
+
return '';
|
|
1148
|
+
}
|
|
1149
|
+
return this.text('praxis.metadataEditor.arrayFields.removeBlocked', undefined, 'Remova as refer\u00eancias antes de excluir: {{references}}', { references: references.join(', ') });
|
|
1150
|
+
}
|
|
1151
|
+
schemaStateDescription() {
|
|
1152
|
+
switch (this.schemaSourceState) {
|
|
1153
|
+
case 'ref-only':
|
|
1154
|
+
return this.text('praxis.metadataEditor.arrayFields.refOnly', 'O schema do item vem do itemSchemaRef; crie subcampos locais apenas quando precisar de override.');
|
|
1155
|
+
case 'override-local':
|
|
1156
|
+
return this.text('praxis.metadataEditor.arrayFields.overrideLocal', 'H\u00e1 um itemSchemaRef e subcampos locais; os subcampos locais funcionam como override expl\u00edcito.');
|
|
1157
|
+
case 'imported':
|
|
1158
|
+
return this.text('praxis.metadataEditor.arrayFields.imported', 'Subcampos importados. Revise antes de aplicar ou salvar.');
|
|
1159
|
+
default:
|
|
1160
|
+
return this.text('praxis.metadataEditor.arrayFields.localOnly', 'Subcampos locais ser\u00e3o salvos em array.itemSchema.fields.');
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
hydrateFromControl() {
|
|
1164
|
+
const fields = this.normalizeFields(this.control?.value);
|
|
1165
|
+
this.replaceRows(fields, false);
|
|
1166
|
+
this.updateSchemaSourceState();
|
|
1167
|
+
}
|
|
1168
|
+
replaceRows(fields, emit = true) {
|
|
1169
|
+
this.hydrating = true;
|
|
1170
|
+
this.internalSubscription?.unsubscribe();
|
|
1171
|
+
while (this.formArray.length) {
|
|
1172
|
+
this.formArray.removeAt(0, { emitEvent: false });
|
|
1173
|
+
}
|
|
1174
|
+
this.originalNames = [];
|
|
1175
|
+
for (const field of fields) {
|
|
1176
|
+
const normalized = this.normalizeField(field);
|
|
1177
|
+
this.formArray.push(this.createRow(normalized), { emitEvent: false });
|
|
1178
|
+
this.originalNames.push(normalized.name);
|
|
1179
|
+
}
|
|
1180
|
+
this.internalSubscription = this.formArray.valueChanges.subscribe(() => this.syncToControl());
|
|
1181
|
+
this.hydrating = false;
|
|
1182
|
+
this.updateFieldListValidity();
|
|
1183
|
+
if (emit) {
|
|
1184
|
+
this.syncToControl();
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
createRow(field) {
|
|
1188
|
+
return this.fb.group({
|
|
1189
|
+
name: [field.name || ''],
|
|
1190
|
+
label: [field.label || field.name || ''],
|
|
1191
|
+
controlType: [field.controlType || FieldControlType.INPUT],
|
|
1192
|
+
required: [!!field.required],
|
|
1193
|
+
metadata: [this.clonePlainObject(field)],
|
|
1194
|
+
});
|
|
1195
|
+
}
|
|
1196
|
+
rowValue(index) {
|
|
1197
|
+
const row = this.formArray.at(index);
|
|
1198
|
+
return row ? this.normalizeField(row.getRawValue()) : null;
|
|
1199
|
+
}
|
|
1200
|
+
syncToControl() {
|
|
1201
|
+
if (this.hydrating || !this.control)
|
|
1202
|
+
return;
|
|
1203
|
+
const normalizedRows = this.formArray.controls.map((row) => this.normalizeField(row.getRawValue()));
|
|
1204
|
+
this.updateDuplicateNames(normalizedRows);
|
|
1205
|
+
const fields = normalizedRows.filter((field) => field.name);
|
|
1206
|
+
this.hydrating = true;
|
|
1207
|
+
this.control.setValue(fields, { emitEvent: true });
|
|
1208
|
+
this.control.markAsDirty();
|
|
1209
|
+
this.control.markAsTouched();
|
|
1210
|
+
this.hydrating = false;
|
|
1211
|
+
this.updateFieldListValidity(normalizedRows);
|
|
1212
|
+
this.updateSchemaSourceState();
|
|
1213
|
+
}
|
|
1214
|
+
normalizeFields(value) {
|
|
1215
|
+
if (Array.isArray(value)) {
|
|
1216
|
+
return value;
|
|
1217
|
+
}
|
|
1218
|
+
if (typeof value === 'string' && value.trim()) {
|
|
1219
|
+
try {
|
|
1220
|
+
const parsed = JSON.parse(value);
|
|
1221
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
1222
|
+
}
|
|
1223
|
+
catch {
|
|
1224
|
+
return [];
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
return [];
|
|
1228
|
+
}
|
|
1229
|
+
normalizeField(value) {
|
|
1230
|
+
const raw = (value && typeof value === 'object' ? value : {});
|
|
1231
|
+
const base = raw.metadata && typeof raw.metadata === 'object' && !Array.isArray(raw.metadata)
|
|
1232
|
+
? this.clonePlainObject(raw.metadata)
|
|
1233
|
+
: this.clonePlainObject(raw);
|
|
1234
|
+
const name = String(raw.name ?? base.name ?? '').trim();
|
|
1235
|
+
return {
|
|
1236
|
+
...base,
|
|
1237
|
+
name,
|
|
1238
|
+
label: String(raw.label ?? base.label ?? name ?? '').trim(),
|
|
1239
|
+
controlType: raw.controlType || base.controlType || FieldControlType.INPUT,
|
|
1240
|
+
required: raw.required === true,
|
|
1241
|
+
};
|
|
1242
|
+
}
|
|
1243
|
+
nextAvailableName(prefix = 'campo') {
|
|
1244
|
+
const existing = new Set(this.formArray.controls
|
|
1245
|
+
.map((row) => String(row.get('name')?.value || '').trim())
|
|
1246
|
+
.filter(Boolean));
|
|
1247
|
+
let index = 1;
|
|
1248
|
+
let candidate = prefix;
|
|
1249
|
+
while (existing.has(candidate)) {
|
|
1250
|
+
index += 1;
|
|
1251
|
+
candidate = `${prefix}${index}`;
|
|
1252
|
+
}
|
|
1253
|
+
return candidate;
|
|
1254
|
+
}
|
|
1255
|
+
updateDuplicateNames(fields) {
|
|
1256
|
+
const source = fields ?? this.formArray.controls.map((row) => this.normalizeField(row.getRawValue()));
|
|
1257
|
+
const seen = new Set();
|
|
1258
|
+
const duplicated = new Set();
|
|
1259
|
+
for (const field of source) {
|
|
1260
|
+
const name = String(field.name || '').trim();
|
|
1261
|
+
if (!name)
|
|
1262
|
+
continue;
|
|
1263
|
+
if (seen.has(name)) {
|
|
1264
|
+
duplicated.add(name);
|
|
1265
|
+
}
|
|
1266
|
+
seen.add(name);
|
|
1267
|
+
}
|
|
1268
|
+
this.duplicateNames = Array.from(duplicated);
|
|
1269
|
+
}
|
|
1270
|
+
updateFieldListValidity(fields = this.formArray.controls.map((row) => this.normalizeField(row.getRawValue()))) {
|
|
1271
|
+
this.updateDuplicateNames(fields);
|
|
1272
|
+
const duplicateSet = new Set(this.duplicateNames);
|
|
1273
|
+
const hasRequiredNameError = fields.some((field) => !String(field.name || '').trim());
|
|
1274
|
+
const hasDuplicateNameError = duplicateSet.size > 0;
|
|
1275
|
+
this.formArray.controls.forEach((row) => {
|
|
1276
|
+
const nameControl = row.get('name');
|
|
1277
|
+
const name = String(nameControl?.value || '').trim();
|
|
1278
|
+
this.setControlError(nameControl, 'arrayFieldNameRequired', !name);
|
|
1279
|
+
this.setControlError(nameControl, 'arrayFieldNameDuplicated', !!name && duplicateSet.has(name));
|
|
1280
|
+
});
|
|
1281
|
+
const errorPayload = hasDuplicateNameError ? { names: this.duplicateNames } : null;
|
|
1282
|
+
this.setControlError(this.control, 'arrayFieldNameRequired', hasRequiredNameError);
|
|
1283
|
+
this.setControlError(this.control, 'arrayFieldNameDuplicated', errorPayload);
|
|
1284
|
+
this.setControlError(this.formArray, 'arrayFieldNameRequired', hasRequiredNameError);
|
|
1285
|
+
this.setControlError(this.formArray, 'arrayFieldNameDuplicated', errorPayload);
|
|
1286
|
+
}
|
|
1287
|
+
setControlError(control, key, value) {
|
|
1288
|
+
if (!control)
|
|
1289
|
+
return;
|
|
1290
|
+
const errors = { ...(control.errors || {}) };
|
|
1291
|
+
const hasError = value !== null && value !== undefined && value !== false;
|
|
1292
|
+
if (hasError) {
|
|
1293
|
+
errors[key] = value === true ? true : value;
|
|
1294
|
+
}
|
|
1295
|
+
else {
|
|
1296
|
+
delete errors[key];
|
|
1297
|
+
}
|
|
1298
|
+
control.setErrors(Object.keys(errors).length ? errors : null);
|
|
1299
|
+
}
|
|
1300
|
+
updateSchemaSourceState() {
|
|
1301
|
+
const rawArray = this.arrayValue();
|
|
1302
|
+
const hasRef = typeof rawArray.itemSchemaRef === 'string' && rawArray.itemSchemaRef.trim().length > 0;
|
|
1303
|
+
const hasFields = this.formArray.length > 0;
|
|
1304
|
+
if (this.schemaSourceState === 'imported' &&
|
|
1305
|
+
hasFields &&
|
|
1306
|
+
this.importedFieldsFingerprint === this.fieldsFingerprint()) {
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
if (this.schemaSourceState === 'imported') {
|
|
1310
|
+
this.importedFieldsFingerprint = null;
|
|
1311
|
+
}
|
|
1312
|
+
this.schemaSourceState = hasRef && hasFields ? 'override-local' : hasRef ? 'ref-only' : 'local-only';
|
|
1313
|
+
}
|
|
1314
|
+
arrayValue() {
|
|
1315
|
+
const value = this.arrayControl?.value;
|
|
1316
|
+
return value && typeof value === 'object' ? value : {};
|
|
1317
|
+
}
|
|
1318
|
+
referencedBy(fieldName) {
|
|
1319
|
+
const name = String(fieldName || '').trim();
|
|
1320
|
+
if (!name)
|
|
1321
|
+
return [];
|
|
1322
|
+
const array = this.arrayValue();
|
|
1323
|
+
const references = [];
|
|
1324
|
+
if (array.itemIdentityField === name)
|
|
1325
|
+
references.push('itemIdentityField');
|
|
1326
|
+
if (this.uniqueByFields(array.collectionValidation?.uniqueBy).includes(name)) {
|
|
1327
|
+
references.push('collectionValidation.uniqueBy');
|
|
1328
|
+
}
|
|
1329
|
+
if (array.collectionValidation?.exactlyOne?.field === name) {
|
|
1330
|
+
references.push('collectionValidation.exactlyOne.field');
|
|
1331
|
+
}
|
|
1332
|
+
if (array.collectionValidation?.atLeastOne?.field === name) {
|
|
1333
|
+
references.push('collectionValidation.atLeastOne.field');
|
|
1334
|
+
}
|
|
1335
|
+
if (typeof array.itemTitleTemplate === 'string' && this.templateReferencesField(array.itemTitleTemplate, name)) {
|
|
1336
|
+
references.push('itemTitleTemplate');
|
|
1337
|
+
}
|
|
1338
|
+
return references;
|
|
1339
|
+
}
|
|
1340
|
+
renameReferences(previous, next) {
|
|
1341
|
+
if (!this.arrayControl || !previous || !next)
|
|
1342
|
+
return;
|
|
1343
|
+
const array = { ...this.arrayValue() };
|
|
1344
|
+
if (array.itemIdentityField === previous)
|
|
1345
|
+
array.itemIdentityField = next;
|
|
1346
|
+
const collectionValidation = array.collectionValidation && typeof array.collectionValidation === 'object'
|
|
1347
|
+
? { ...array.collectionValidation }
|
|
1348
|
+
: undefined;
|
|
1349
|
+
if (collectionValidation) {
|
|
1350
|
+
if (Array.isArray(collectionValidation.uniqueBy)) {
|
|
1351
|
+
collectionValidation.uniqueBy = collectionValidation.uniqueBy.map((entry) => entry === previous ? next : entry);
|
|
1352
|
+
}
|
|
1353
|
+
else if (typeof collectionValidation.uniqueBy === 'string') {
|
|
1354
|
+
collectionValidation.uniqueBy = this.replaceUniqueByField(collectionValidation.uniqueBy, previous, next);
|
|
1355
|
+
}
|
|
1356
|
+
if (collectionValidation.exactlyOne?.field === previous) {
|
|
1357
|
+
collectionValidation.exactlyOne = { ...collectionValidation.exactlyOne, field: next };
|
|
1358
|
+
}
|
|
1359
|
+
if (collectionValidation.atLeastOne?.field === previous) {
|
|
1360
|
+
collectionValidation.atLeastOne = { ...collectionValidation.atLeastOne, field: next };
|
|
1361
|
+
}
|
|
1362
|
+
array.collectionValidation = collectionValidation;
|
|
1363
|
+
}
|
|
1364
|
+
if (typeof array.itemTitleTemplate === 'string') {
|
|
1365
|
+
array.itemTitleTemplate = array.itemTitleTemplate.replace(new RegExp(`{{\\s*${this.escapeRegExp(previous)}\\s*}}`, 'g'), `{{${next}}}`);
|
|
1366
|
+
}
|
|
1367
|
+
this.arrayControl.patchValue(array);
|
|
1368
|
+
this.arrayControl.markAsDirty();
|
|
1369
|
+
}
|
|
1370
|
+
uniqueByFields(value) {
|
|
1371
|
+
if (Array.isArray(value)) {
|
|
1372
|
+
return value.map((entry) => String(entry || '').trim()).filter(Boolean);
|
|
1373
|
+
}
|
|
1374
|
+
if (typeof value !== 'string') {
|
|
1375
|
+
return [];
|
|
1376
|
+
}
|
|
1377
|
+
const trimmed = value.trim();
|
|
1378
|
+
if (!trimmed) {
|
|
1379
|
+
return [];
|
|
1380
|
+
}
|
|
1381
|
+
try {
|
|
1382
|
+
const parsed = JSON.parse(trimmed);
|
|
1383
|
+
if (Array.isArray(parsed)) {
|
|
1384
|
+
return parsed.map((entry) => String(entry || '').trim()).filter(Boolean);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
catch { }
|
|
1388
|
+
return trimmed
|
|
1389
|
+
.split(/\r?\n|,/)
|
|
1390
|
+
.map((entry) => entry.trim())
|
|
1391
|
+
.filter(Boolean);
|
|
1392
|
+
}
|
|
1393
|
+
replaceUniqueByField(value, previous, next) {
|
|
1394
|
+
const trimmed = value.trim();
|
|
1395
|
+
if (!trimmed) {
|
|
1396
|
+
return value;
|
|
1397
|
+
}
|
|
1398
|
+
try {
|
|
1399
|
+
const parsed = JSON.parse(trimmed);
|
|
1400
|
+
if (Array.isArray(parsed)) {
|
|
1401
|
+
return JSON.stringify(parsed.map((entry) => String(entry || '').trim() === previous ? next : entry));
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
catch { }
|
|
1405
|
+
return value
|
|
1406
|
+
.split(/(\r?\n|,)/)
|
|
1407
|
+
.map((entry) => entry.trim() === previous ? next : entry)
|
|
1408
|
+
.join('');
|
|
1409
|
+
}
|
|
1410
|
+
templateReferencesField(template, fieldName) {
|
|
1411
|
+
return new RegExp(`{{\\s*${this.escapeRegExp(fieldName)}\\s*}}`).test(template);
|
|
1412
|
+
}
|
|
1413
|
+
clonePlainObject(value) {
|
|
1414
|
+
return this.cloneMetadataValue(value, new WeakMap());
|
|
1415
|
+
}
|
|
1416
|
+
cloneMetadataValue(value, seen) {
|
|
1417
|
+
if (!value || typeof value !== 'object') {
|
|
1418
|
+
return value;
|
|
1419
|
+
}
|
|
1420
|
+
if (seen.has(value)) {
|
|
1421
|
+
return seen.get(value);
|
|
1422
|
+
}
|
|
1423
|
+
if (value instanceof Date) {
|
|
1424
|
+
return new Date(value.getTime());
|
|
1425
|
+
}
|
|
1426
|
+
if (value instanceof RegExp) {
|
|
1427
|
+
return new RegExp(value.source, value.flags);
|
|
1428
|
+
}
|
|
1429
|
+
if (Array.isArray(value)) {
|
|
1430
|
+
const clone = [];
|
|
1431
|
+
seen.set(value, clone);
|
|
1432
|
+
clone.push(...value.map((entry) => this.cloneMetadataValue(entry, seen)));
|
|
1433
|
+
return clone;
|
|
1434
|
+
}
|
|
1435
|
+
if (value instanceof Map) {
|
|
1436
|
+
const clone = new Map();
|
|
1437
|
+
seen.set(value, clone);
|
|
1438
|
+
value.forEach((entry, key) => {
|
|
1439
|
+
clone.set(this.cloneMetadataValue(key, seen), this.cloneMetadataValue(entry, seen));
|
|
1440
|
+
});
|
|
1441
|
+
return clone;
|
|
1442
|
+
}
|
|
1443
|
+
if (value instanceof Set) {
|
|
1444
|
+
const clone = new Set();
|
|
1445
|
+
seen.set(value, clone);
|
|
1446
|
+
value.forEach((entry) => clone.add(this.cloneMetadataValue(entry, seen)));
|
|
1447
|
+
return clone;
|
|
1448
|
+
}
|
|
1449
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1450
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
1451
|
+
return value;
|
|
1452
|
+
}
|
|
1453
|
+
const clone = {};
|
|
1454
|
+
seen.set(value, clone);
|
|
1455
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
1456
|
+
clone[key] = this.cloneMetadataValue(entry, seen);
|
|
1457
|
+
}
|
|
1458
|
+
return clone;
|
|
1459
|
+
}
|
|
1460
|
+
fieldsFingerprint() {
|
|
1461
|
+
return JSON.stringify(this.formArray.controls.map((row) => this.normalizeField(row.getRawValue())));
|
|
1462
|
+
}
|
|
1463
|
+
escapeRegExp(value) {
|
|
1464
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
1465
|
+
}
|
|
1466
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ArrayItemSchemaFieldsEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1467
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: ArrayItemSchemaFieldsEditorComponent, isStandalone: true, selector: "praxis-array-item-schema-fields-editor", inputs: { control: "control", arrayControl: "arrayControl" }, providers: [providePraxisMetadataEditorI18n()], usesOnChanges: true, ngImport: i0, template: `
|
|
1468
|
+
<section
|
|
1469
|
+
class="array-fields-editor"
|
|
1470
|
+
[attr.data-schema-source]="schemaSourceState"
|
|
1471
|
+
[attr.aria-label]="text('praxis.metadataEditor.arrayFields.section', 'Subcampos da cole\u00e7\u00e3o')"
|
|
1472
|
+
>
|
|
1473
|
+
<header class="array-fields-editor__header">
|
|
1474
|
+
<div>
|
|
1475
|
+
<h3>{{ text('praxis.metadataEditor.arrayFields.title', 'Subcampos') }}</h3>
|
|
1476
|
+
<p>
|
|
1477
|
+
{{ schemaStateDescription() }}
|
|
1478
|
+
</p>
|
|
1479
|
+
</div>
|
|
1480
|
+
<button type="button" class="array-fields-editor__primary" (click)="addField()">
|
|
1481
|
+
{{ text('praxis.metadataEditor.arrayFields.add', 'Adicionar subcampo') }}
|
|
1482
|
+
</button>
|
|
1483
|
+
</header>
|
|
1484
|
+
|
|
1485
|
+
@if (duplicateNames.length) {
|
|
1486
|
+
<div
|
|
1487
|
+
class="array-fields-editor__warning"
|
|
1488
|
+
role="alert"
|
|
1489
|
+
>
|
|
1490
|
+
{{
|
|
1491
|
+
text(
|
|
1492
|
+
'praxis.metadataEditor.arrayFields.duplicateNames',
|
|
1493
|
+
undefined,
|
|
1494
|
+
'Existem nomes duplicados: {{names}}',
|
|
1495
|
+
{ names: duplicateNames.join(', ') }
|
|
1496
|
+
)
|
|
1497
|
+
}}
|
|
1498
|
+
</div>
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
@if (formArray.length === 0) {
|
|
1502
|
+
<div
|
|
1503
|
+
class="array-fields-editor__empty"
|
|
1504
|
+
>
|
|
1505
|
+
{{ text('praxis.metadataEditor.arrayFields.empty', 'Nenhum subcampo local configurado.') }}
|
|
1506
|
+
</div>
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
<div class="array-fields-editor__list" [formGroup]="form">
|
|
1510
|
+
@for (row of rows; track row; let i = $index) {
|
|
1511
|
+
<div
|
|
1512
|
+
class="array-fields-editor__row"
|
|
1513
|
+
[formGroup]="row"
|
|
1514
|
+
>
|
|
1515
|
+
<label>
|
|
1516
|
+
<span>{{ text('praxis.metadataEditor.arrayFields.name', 'Nome') }}</span>
|
|
1517
|
+
<input
|
|
1518
|
+
type="text"
|
|
1519
|
+
formControlName="name"
|
|
1520
|
+
(blur)="onNameBlur(i)"
|
|
1521
|
+
[attr.aria-invalid]="isNameInvalid(i) ? 'true' : null"
|
|
1522
|
+
[attr.aria-describedby]="nameErrorDescribedBy(i)"
|
|
1523
|
+
/>
|
|
1524
|
+
@if (nameErrorMessage(i); as nameError) {
|
|
1525
|
+
<span
|
|
1526
|
+
class="array-fields-editor__field-error"
|
|
1527
|
+
[id]="nameErrorId(i)"
|
|
1528
|
+
>
|
|
1529
|
+
{{ nameError }}
|
|
1530
|
+
</span>
|
|
1531
|
+
}
|
|
1532
|
+
</label>
|
|
1533
|
+
<label>
|
|
1534
|
+
<span>{{ text('praxis.metadataEditor.arrayFields.label', 'Label') }}</span>
|
|
1535
|
+
<input type="text" formControlName="label" />
|
|
1536
|
+
</label>
|
|
1537
|
+
<label>
|
|
1538
|
+
<span>{{ text('praxis.metadataEditor.arrayFields.controlType', 'Controle') }}</span>
|
|
1539
|
+
<select formControlName="controlType">
|
|
1540
|
+
@for (option of controlTypeOptions; track option) {
|
|
1541
|
+
<option [value]="option">
|
|
1542
|
+
{{ controlTypeLabel(option) }}
|
|
1543
|
+
</option>
|
|
1544
|
+
}
|
|
1545
|
+
</select>
|
|
1546
|
+
</label>
|
|
1547
|
+
<label class="array-fields-editor__check">
|
|
1548
|
+
<input type="checkbox" formControlName="required" />
|
|
1549
|
+
<span>{{ text('praxis.metadataEditor.arrayFields.required', 'Obrigat\u00f3rio') }}</span>
|
|
1550
|
+
</label>
|
|
1551
|
+
<div class="array-fields-editor__actions">
|
|
1552
|
+
<button type="button" (click)="moveField(i, -1)" [disabled]="i === 0">
|
|
1553
|
+
{{ text('praxis.metadataEditor.arrayFields.moveUp', 'Subir') }}
|
|
1554
|
+
</button>
|
|
1555
|
+
<button type="button" (click)="moveField(i, 1)" [disabled]="i === formArray.length - 1">
|
|
1556
|
+
{{ text('praxis.metadataEditor.arrayFields.moveDown', 'Descer') }}
|
|
1557
|
+
</button>
|
|
1558
|
+
<button type="button" (click)="duplicateField(i)">
|
|
1559
|
+
{{ text('praxis.metadataEditor.arrayFields.duplicate', 'Duplicar') }}
|
|
1560
|
+
</button>
|
|
1561
|
+
<button
|
|
1562
|
+
type="button"
|
|
1563
|
+
(click)="removeField(i)"
|
|
1564
|
+
[disabled]="isReferenced(row.get('name')?.value)"
|
|
1565
|
+
[title]="removeTitle(row.get('name')?.value)"
|
|
1566
|
+
>
|
|
1567
|
+
{{ text('praxis.metadataEditor.arrayFields.remove', 'Remover') }}
|
|
1568
|
+
</button>
|
|
1569
|
+
</div>
|
|
1570
|
+
</div>
|
|
1571
|
+
}
|
|
1572
|
+
</div>
|
|
1573
|
+
</section>
|
|
1574
|
+
`, isInline: true, styles: [":host{display:block}.array-fields-editor{display:block;margin:10px 0 2px;padding:12px;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;background:var(--md-sys-color-surface-container-low);color:var(--md-sys-color-on-surface)}.array-fields-editor__header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:12px}.array-fields-editor__header h3{margin:0 0 4px;font-size:1rem;line-height:1.25}.array-fields-editor__header p,.array-fields-editor__empty{margin:0;color:var(--md-sys-color-on-surface-variant);font-size:.875rem;line-height:1.35}.array-fields-editor__primary,.array-fields-editor button{border:1px solid var(--md-sys-color-outline-variant);border-radius:6px;background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);cursor:pointer;min-height:32px;padding:5px 10px}.array-fields-editor__primary{border-color:var(--md-sys-color-primary);color:var(--md-sys-color-primary);font-weight:600;white-space:nowrap}.array-fields-editor button:disabled{cursor:not-allowed;opacity:.55}.array-fields-editor__warning{margin:8px 0 12px;padding:8px 10px;border:1px solid var(--md-sys-color-error);border-radius:6px;color:var(--md-sys-color-error);background:var(--md-sys-color-error-container)}.array-fields-editor__list{display:grid;gap:10px}.array-fields-editor__row{display:grid;grid-template-columns:minmax(140px,1fr) minmax(160px,1.2fr) minmax(140px,.9fr) auto;gap:10px;align-items:end;padding:10px;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;background:var(--md-sys-color-surface)}.array-fields-editor label{display:grid;gap:4px;font-size:.8rem;font-weight:600}.array-fields-editor input[type=text],.array-fields-editor select{box-sizing:border-box;width:100%;min-height:34px;border:1px solid var(--md-sys-color-outline-variant);border-radius:6px;background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);padding:5px 8px}.array-fields-editor input[aria-invalid=true]{border-color:var(--md-sys-color-error)}.array-fields-editor__field-error{color:var(--md-sys-color-error);font-size:.75rem;font-weight:500;line-height:1.3}.array-fields-editor__check{display:flex!important;align-items:center;gap:6px;min-height:34px}.array-fields-editor__actions{grid-column:1 / -1;display:flex;flex-wrap:wrap;gap:6px}@media(max-width:720px){.array-fields-editor__header,.array-fields-editor__row{display:grid;grid-template-columns:1fr}.array-fields-editor__primary{width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] });
|
|
1575
|
+
}
|
|
1576
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ArrayItemSchemaFieldsEditorComponent, decorators: [{
|
|
1577
|
+
type: Component,
|
|
1578
|
+
args: [{ selector: 'praxis-array-item-schema-fields-editor', standalone: true, imports: [ReactiveFormsModule], providers: [providePraxisMetadataEditorI18n()], template: `
|
|
1579
|
+
<section
|
|
1580
|
+
class="array-fields-editor"
|
|
1581
|
+
[attr.data-schema-source]="schemaSourceState"
|
|
1582
|
+
[attr.aria-label]="text('praxis.metadataEditor.arrayFields.section', 'Subcampos da cole\u00e7\u00e3o')"
|
|
1583
|
+
>
|
|
1584
|
+
<header class="array-fields-editor__header">
|
|
1585
|
+
<div>
|
|
1586
|
+
<h3>{{ text('praxis.metadataEditor.arrayFields.title', 'Subcampos') }}</h3>
|
|
1587
|
+
<p>
|
|
1588
|
+
{{ schemaStateDescription() }}
|
|
1589
|
+
</p>
|
|
1590
|
+
</div>
|
|
1591
|
+
<button type="button" class="array-fields-editor__primary" (click)="addField()">
|
|
1592
|
+
{{ text('praxis.metadataEditor.arrayFields.add', 'Adicionar subcampo') }}
|
|
1593
|
+
</button>
|
|
1594
|
+
</header>
|
|
1595
|
+
|
|
1596
|
+
@if (duplicateNames.length) {
|
|
1597
|
+
<div
|
|
1598
|
+
class="array-fields-editor__warning"
|
|
1599
|
+
role="alert"
|
|
1600
|
+
>
|
|
1601
|
+
{{
|
|
1602
|
+
text(
|
|
1603
|
+
'praxis.metadataEditor.arrayFields.duplicateNames',
|
|
1604
|
+
undefined,
|
|
1605
|
+
'Existem nomes duplicados: {{names}}',
|
|
1606
|
+
{ names: duplicateNames.join(', ') }
|
|
1607
|
+
)
|
|
1608
|
+
}}
|
|
1609
|
+
</div>
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
@if (formArray.length === 0) {
|
|
1613
|
+
<div
|
|
1614
|
+
class="array-fields-editor__empty"
|
|
1615
|
+
>
|
|
1616
|
+
{{ text('praxis.metadataEditor.arrayFields.empty', 'Nenhum subcampo local configurado.') }}
|
|
1617
|
+
</div>
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
<div class="array-fields-editor__list" [formGroup]="form">
|
|
1621
|
+
@for (row of rows; track row; let i = $index) {
|
|
1622
|
+
<div
|
|
1623
|
+
class="array-fields-editor__row"
|
|
1624
|
+
[formGroup]="row"
|
|
1625
|
+
>
|
|
1626
|
+
<label>
|
|
1627
|
+
<span>{{ text('praxis.metadataEditor.arrayFields.name', 'Nome') }}</span>
|
|
1628
|
+
<input
|
|
1629
|
+
type="text"
|
|
1630
|
+
formControlName="name"
|
|
1631
|
+
(blur)="onNameBlur(i)"
|
|
1632
|
+
[attr.aria-invalid]="isNameInvalid(i) ? 'true' : null"
|
|
1633
|
+
[attr.aria-describedby]="nameErrorDescribedBy(i)"
|
|
1634
|
+
/>
|
|
1635
|
+
@if (nameErrorMessage(i); as nameError) {
|
|
1636
|
+
<span
|
|
1637
|
+
class="array-fields-editor__field-error"
|
|
1638
|
+
[id]="nameErrorId(i)"
|
|
1639
|
+
>
|
|
1640
|
+
{{ nameError }}
|
|
1641
|
+
</span>
|
|
1642
|
+
}
|
|
1643
|
+
</label>
|
|
1644
|
+
<label>
|
|
1645
|
+
<span>{{ text('praxis.metadataEditor.arrayFields.label', 'Label') }}</span>
|
|
1646
|
+
<input type="text" formControlName="label" />
|
|
1647
|
+
</label>
|
|
1648
|
+
<label>
|
|
1649
|
+
<span>{{ text('praxis.metadataEditor.arrayFields.controlType', 'Controle') }}</span>
|
|
1650
|
+
<select formControlName="controlType">
|
|
1651
|
+
@for (option of controlTypeOptions; track option) {
|
|
1652
|
+
<option [value]="option">
|
|
1653
|
+
{{ controlTypeLabel(option) }}
|
|
1654
|
+
</option>
|
|
1655
|
+
}
|
|
1656
|
+
</select>
|
|
1657
|
+
</label>
|
|
1658
|
+
<label class="array-fields-editor__check">
|
|
1659
|
+
<input type="checkbox" formControlName="required" />
|
|
1660
|
+
<span>{{ text('praxis.metadataEditor.arrayFields.required', 'Obrigat\u00f3rio') }}</span>
|
|
1661
|
+
</label>
|
|
1662
|
+
<div class="array-fields-editor__actions">
|
|
1663
|
+
<button type="button" (click)="moveField(i, -1)" [disabled]="i === 0">
|
|
1664
|
+
{{ text('praxis.metadataEditor.arrayFields.moveUp', 'Subir') }}
|
|
1665
|
+
</button>
|
|
1666
|
+
<button type="button" (click)="moveField(i, 1)" [disabled]="i === formArray.length - 1">
|
|
1667
|
+
{{ text('praxis.metadataEditor.arrayFields.moveDown', 'Descer') }}
|
|
1668
|
+
</button>
|
|
1669
|
+
<button type="button" (click)="duplicateField(i)">
|
|
1670
|
+
{{ text('praxis.metadataEditor.arrayFields.duplicate', 'Duplicar') }}
|
|
1671
|
+
</button>
|
|
1672
|
+
<button
|
|
1673
|
+
type="button"
|
|
1674
|
+
(click)="removeField(i)"
|
|
1675
|
+
[disabled]="isReferenced(row.get('name')?.value)"
|
|
1676
|
+
[title]="removeTitle(row.get('name')?.value)"
|
|
1677
|
+
>
|
|
1678
|
+
{{ text('praxis.metadataEditor.arrayFields.remove', 'Remover') }}
|
|
1679
|
+
</button>
|
|
1680
|
+
</div>
|
|
1681
|
+
</div>
|
|
1682
|
+
}
|
|
1683
|
+
</div>
|
|
1684
|
+
</section>
|
|
1685
|
+
`, styles: [":host{display:block}.array-fields-editor{display:block;margin:10px 0 2px;padding:12px;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;background:var(--md-sys-color-surface-container-low);color:var(--md-sys-color-on-surface)}.array-fields-editor__header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:12px}.array-fields-editor__header h3{margin:0 0 4px;font-size:1rem;line-height:1.25}.array-fields-editor__header p,.array-fields-editor__empty{margin:0;color:var(--md-sys-color-on-surface-variant);font-size:.875rem;line-height:1.35}.array-fields-editor__primary,.array-fields-editor button{border:1px solid var(--md-sys-color-outline-variant);border-radius:6px;background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);cursor:pointer;min-height:32px;padding:5px 10px}.array-fields-editor__primary{border-color:var(--md-sys-color-primary);color:var(--md-sys-color-primary);font-weight:600;white-space:nowrap}.array-fields-editor button:disabled{cursor:not-allowed;opacity:.55}.array-fields-editor__warning{margin:8px 0 12px;padding:8px 10px;border:1px solid var(--md-sys-color-error);border-radius:6px;color:var(--md-sys-color-error);background:var(--md-sys-color-error-container)}.array-fields-editor__list{display:grid;gap:10px}.array-fields-editor__row{display:grid;grid-template-columns:minmax(140px,1fr) minmax(160px,1.2fr) minmax(140px,.9fr) auto;gap:10px;align-items:end;padding:10px;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;background:var(--md-sys-color-surface)}.array-fields-editor label{display:grid;gap:4px;font-size:.8rem;font-weight:600}.array-fields-editor input[type=text],.array-fields-editor select{box-sizing:border-box;width:100%;min-height:34px;border:1px solid var(--md-sys-color-outline-variant);border-radius:6px;background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);padding:5px 8px}.array-fields-editor input[aria-invalid=true]{border-color:var(--md-sys-color-error)}.array-fields-editor__field-error{color:var(--md-sys-color-error);font-size:.75rem;font-weight:500;line-height:1.3}.array-fields-editor__check{display:flex!important;align-items:center;gap:6px;min-height:34px}.array-fields-editor__actions{grid-column:1 / -1;display:flex;flex-wrap:wrap;gap:6px}@media(max-width:720px){.array-fields-editor__header,.array-fields-editor__row{display:grid;grid-template-columns:1fr}.array-fields-editor__primary{width:100%}}\n"] }]
|
|
1686
|
+
}], propDecorators: { control: [{
|
|
1687
|
+
type: Input
|
|
1688
|
+
}], arrayControl: [{
|
|
1689
|
+
type: Input
|
|
1690
|
+
}] } });
|
|
1691
|
+
|
|
846
1692
|
const inputProperties = [
|
|
847
1693
|
// Geral
|
|
848
1694
|
{ name: 'label', label: 'Label', editorType: 'text', placeholder: 'Rótulo do campo', group: 'Geral' },
|
|
@@ -1105,7 +1951,7 @@ const arrayProperties = [
|
|
|
1105
1951
|
{ name: 'array.collectionValidation.atLeastOne.field', label: 'Pelo menos um: campo', editorType: 'text', group: 'Validacao' },
|
|
1106
1952
|
{ name: 'array.collectionValidation.atLeastOne.value', label: 'Pelo menos um: valor tipado', editorType: 'text', group: 'Validacao', hint: 'Use true/false, numero ou texto. Ex.: true, 1, ACTIVE.' },
|
|
1107
1953
|
{ name: 'array.collectionValidation.atLeastOne.message', label: 'Mensagem: pelo menos um', editorType: 'text', group: 'Validacao' },
|
|
1108
|
-
{ name: 'array.itemSchema.fields', label: 'Subcampos
|
|
1954
|
+
{ name: 'array.itemSchema.fields', label: 'Subcampos', editorType: 'field-list', group: 'Subcampos', hint: 'Lista FieldMetadata[] usada quando nao houver itemSchemaRef resolvido.' },
|
|
1109
1955
|
{ name: 'required', label: 'Obrigatorio', editorType: 'checkbox', group: 'Validacao' },
|
|
1110
1956
|
];
|
|
1111
1957
|
|
|
@@ -1278,8 +2124,39 @@ function cloneProperty$7(prop) {
|
|
|
1278
2124
|
: prop.options,
|
|
1279
2125
|
};
|
|
1280
2126
|
}
|
|
2127
|
+
function cloneEntityLookupBaseProperty(prop) {
|
|
2128
|
+
const cloned = cloneProperty$7(prop);
|
|
2129
|
+
if (cloned.name === 'multiple') {
|
|
2130
|
+
return {
|
|
2131
|
+
...cloned,
|
|
2132
|
+
group: 'Selecao em colecao',
|
|
2133
|
+
hint: 'Ativa selecao governada de varias entidades no mesmo campo.',
|
|
2134
|
+
};
|
|
2135
|
+
}
|
|
2136
|
+
if (cloned.name === 'maxSelections') {
|
|
2137
|
+
return {
|
|
2138
|
+
...cloned,
|
|
2139
|
+
group: 'Selecao em colecao',
|
|
2140
|
+
hint: 'Limite maximo de entidades selecionadas quando multiple=true.',
|
|
2141
|
+
};
|
|
2142
|
+
}
|
|
2143
|
+
return cloned;
|
|
2144
|
+
}
|
|
1281
2145
|
const entityLookupProperties = [
|
|
1282
|
-
...selectProperties.map((prop) =>
|
|
2146
|
+
...selectProperties.map((prop) => cloneEntityLookupBaseProperty(prop)),
|
|
2147
|
+
{
|
|
2148
|
+
name: 'payloadMode',
|
|
2149
|
+
label: 'Modo do payload',
|
|
2150
|
+
editorType: 'select',
|
|
2151
|
+
group: 'Selecao em colecao',
|
|
2152
|
+
options: [
|
|
2153
|
+
{ value: 'id', text: 'id' },
|
|
2154
|
+
{ value: 'entityRef', text: 'entityRef' },
|
|
2155
|
+
{ value: 'ids', text: 'ids' },
|
|
2156
|
+
{ value: 'entityRefs', text: 'entityRefs' },
|
|
2157
|
+
],
|
|
2158
|
+
hint: 'Define o shape salvo no payload. O runtime resolve combinacoes invalidas conforme multiple.',
|
|
2159
|
+
},
|
|
1283
2160
|
{
|
|
1284
2161
|
name: 'optionSource.key',
|
|
1285
2162
|
label: 'Option source key',
|
|
@@ -1379,6 +2256,261 @@ const entityLookupProperties = [
|
|
|
1379
2256
|
group: 'Exibicao de entidade',
|
|
1380
2257
|
hint: 'Campo opcional com mensagem de bloqueio quando selectable=false.',
|
|
1381
2258
|
},
|
|
2259
|
+
{
|
|
2260
|
+
name: 'optionSource.display.preset',
|
|
2261
|
+
label: 'Preset canonico',
|
|
2262
|
+
editorType: 'select',
|
|
2263
|
+
group: 'Display canonico',
|
|
2264
|
+
options: [
|
|
2265
|
+
{ value: 'compact', text: 'compact' },
|
|
2266
|
+
{ value: 'rich', text: 'rich' },
|
|
2267
|
+
{ value: 'directory', text: 'directory' },
|
|
2268
|
+
{ value: 'status', text: 'status' },
|
|
2269
|
+
{ value: 'reference', text: 'reference' },
|
|
2270
|
+
{ value: 'hierarchical', text: 'hierarchical' },
|
|
2271
|
+
],
|
|
2272
|
+
hint: 'Intencao visual da entidade. Use directory para pessoas/equipes, reference para codigo + descricao e status quando o estado operacional for central.',
|
|
2273
|
+
},
|
|
2274
|
+
{
|
|
2275
|
+
name: 'optionSource.display.usage',
|
|
2276
|
+
label: 'Uso preferencial',
|
|
2277
|
+
editorType: 'select',
|
|
2278
|
+
group: 'Display canonico',
|
|
2279
|
+
options: [
|
|
2280
|
+
{ value: 'form', text: 'form' },
|
|
2281
|
+
{ value: 'filter', text: 'filter' },
|
|
2282
|
+
{ value: 'table-cell', text: 'table-cell' },
|
|
2283
|
+
{ value: 'dashboard', text: 'dashboard' },
|
|
2284
|
+
{ value: 'wizard', text: 'wizard' },
|
|
2285
|
+
{ value: 'review', text: 'review' },
|
|
2286
|
+
],
|
|
2287
|
+
hint: 'Contexto principal de uso desta option-source sem amarrar a regra a um componente especifico.',
|
|
2288
|
+
},
|
|
2289
|
+
{
|
|
2290
|
+
name: 'optionSource.display.density',
|
|
2291
|
+
label: 'Densidade',
|
|
2292
|
+
editorType: 'select',
|
|
2293
|
+
group: 'Display canonico',
|
|
2294
|
+
options: [
|
|
2295
|
+
{ value: 'compact', text: 'compact' },
|
|
2296
|
+
{ value: 'comfortable', text: 'comfortable' },
|
|
2297
|
+
{ value: 'rich', text: 'rich' },
|
|
2298
|
+
],
|
|
2299
|
+
hint: 'Densidade preferencial para runtimes. Filtros costumam usar compact; formularios, comfortable; revisoes, rich.',
|
|
2300
|
+
},
|
|
2301
|
+
{
|
|
2302
|
+
name: 'optionSource.display.selectedLayout',
|
|
2303
|
+
label: 'Layout selecionado canonico',
|
|
2304
|
+
editorType: 'select',
|
|
2305
|
+
group: 'Display canonico',
|
|
2306
|
+
options: [
|
|
2307
|
+
{ value: 'card', text: 'card' },
|
|
2308
|
+
{ value: 'inline', text: 'inline' },
|
|
2309
|
+
{ value: 'compact', text: 'compact' },
|
|
2310
|
+
{ value: 'token', text: 'token' },
|
|
2311
|
+
],
|
|
2312
|
+
hint: 'Layout preferencial do valor selecionado publicado pela option-source.',
|
|
2313
|
+
},
|
|
2314
|
+
{
|
|
2315
|
+
name: 'optionSource.display.resultLayout',
|
|
2316
|
+
label: 'Layout de resultados canonico',
|
|
2317
|
+
editorType: 'select',
|
|
2318
|
+
group: 'Display canonico',
|
|
2319
|
+
options: [
|
|
2320
|
+
{ value: 'list', text: 'list' },
|
|
2321
|
+
{ value: 'denseList', text: 'denseList' },
|
|
2322
|
+
{ value: 'table', text: 'table' },
|
|
2323
|
+
{ value: 'card', text: 'card' },
|
|
2324
|
+
],
|
|
2325
|
+
hint: 'Layout preferencial da lista de resultados publicado pela option-source.',
|
|
2326
|
+
},
|
|
2327
|
+
{
|
|
2328
|
+
name: 'optionSource.display.primaryPropertyPath',
|
|
2329
|
+
label: 'Campo primario',
|
|
2330
|
+
editorType: 'text',
|
|
2331
|
+
group: 'Display canonico',
|
|
2332
|
+
hint: 'Campo semantico usado como titulo principal quando diferente de labelPropertyPath.',
|
|
2333
|
+
},
|
|
2334
|
+
{
|
|
2335
|
+
name: 'optionSource.display.fields',
|
|
2336
|
+
label: 'Campos ricos',
|
|
2337
|
+
editorType: 'textarea',
|
|
2338
|
+
group: 'Display canonico',
|
|
2339
|
+
hint: 'JSON array de subinformacoes ricas. Ex.: [{ "key": "role", "propertyPath": "cargo.nome", "label": "Cargo", "icon": "work", "presentation": "text", "tone": "neutral" }, { "key": "department", "propertyPath": "departamento.nome", "label": "Departamento", "icon": "groups", "presentation": "chip", "tone": "info" }].',
|
|
2340
|
+
},
|
|
2341
|
+
{
|
|
2342
|
+
name: 'optionSource.display.secondaryPropertyPaths',
|
|
2343
|
+
label: 'Campos secundarios canonicos',
|
|
2344
|
+
editorType: 'textarea',
|
|
2345
|
+
group: 'Display canonico',
|
|
2346
|
+
hint: 'JSON array ou uma linha por campo usado como subtitulo. Ex.: cargo.nome, departamento.nome.',
|
|
2347
|
+
},
|
|
2348
|
+
{
|
|
2349
|
+
name: 'optionSource.display.badgePropertyPaths',
|
|
2350
|
+
label: 'Campos de badges canonicos',
|
|
2351
|
+
editorType: 'textarea',
|
|
2352
|
+
group: 'Display canonico',
|
|
2353
|
+
hint: 'JSON array ou uma linha por campo materializado como badge/chip.',
|
|
2354
|
+
},
|
|
2355
|
+
{
|
|
2356
|
+
name: 'optionSource.display.avatarPropertyPath',
|
|
2357
|
+
label: 'Campo de avatar',
|
|
2358
|
+
editorType: 'text',
|
|
2359
|
+
group: 'Display canonico',
|
|
2360
|
+
hint: 'Campo usado para avatar/imagem; quando ausente, o runtime pode usar iniciais.',
|
|
2361
|
+
},
|
|
2362
|
+
{
|
|
2363
|
+
name: 'optionSource.display.showAvatar',
|
|
2364
|
+
label: 'Display: avatar',
|
|
2365
|
+
editorType: 'checkbox',
|
|
2366
|
+
group: 'Display canonico',
|
|
2367
|
+
},
|
|
2368
|
+
{
|
|
2369
|
+
name: 'optionSource.display.showCode',
|
|
2370
|
+
label: 'Display: codigo',
|
|
2371
|
+
editorType: 'checkbox',
|
|
2372
|
+
group: 'Display canonico',
|
|
2373
|
+
},
|
|
2374
|
+
{
|
|
2375
|
+
name: 'optionSource.display.showDescription',
|
|
2376
|
+
label: 'Display: descricao',
|
|
2377
|
+
editorType: 'checkbox',
|
|
2378
|
+
group: 'Display canonico',
|
|
2379
|
+
},
|
|
2380
|
+
{
|
|
2381
|
+
name: 'optionSource.display.showStatus',
|
|
2382
|
+
label: 'Display: status',
|
|
2383
|
+
editorType: 'checkbox',
|
|
2384
|
+
group: 'Display canonico',
|
|
2385
|
+
},
|
|
2386
|
+
{
|
|
2387
|
+
name: 'optionSource.display.showBadges',
|
|
2388
|
+
label: 'Display: badges',
|
|
2389
|
+
editorType: 'checkbox',
|
|
2390
|
+
group: 'Display canonico',
|
|
2391
|
+
},
|
|
2392
|
+
{
|
|
2393
|
+
name: 'optionSource.display.showDisabledReason',
|
|
2394
|
+
label: 'Display: motivo bloqueado',
|
|
2395
|
+
editorType: 'checkbox',
|
|
2396
|
+
group: 'Display canonico',
|
|
2397
|
+
},
|
|
2398
|
+
{
|
|
2399
|
+
name: 'optionSource.display.showResultCount',
|
|
2400
|
+
label: 'Display: contador de resultados',
|
|
2401
|
+
editorType: 'checkbox',
|
|
2402
|
+
group: 'Display canonico',
|
|
2403
|
+
},
|
|
2404
|
+
{
|
|
2405
|
+
name: 'optionSource.display.statusToneMap',
|
|
2406
|
+
label: 'Mapa canonico de tons',
|
|
2407
|
+
editorType: 'textarea',
|
|
2408
|
+
group: 'Display canonico',
|
|
2409
|
+
hint: 'JSON object. Ex.: { "ACTIVE": "success", "BLOCKED": "danger" }.',
|
|
2410
|
+
},
|
|
2411
|
+
{
|
|
2412
|
+
name: 'optionSource.display.badgeKeys',
|
|
2413
|
+
label: 'Chaves canonicas de badges',
|
|
2414
|
+
editorType: 'textarea',
|
|
2415
|
+
group: 'Display canonico',
|
|
2416
|
+
hint: 'JSON array ou uma linha por chave complementar de badge.',
|
|
2417
|
+
},
|
|
2418
|
+
{
|
|
2419
|
+
name: 'optionSource.display.maxVisibleBadges',
|
|
2420
|
+
label: 'Maximo canonico de badges',
|
|
2421
|
+
editorType: 'number',
|
|
2422
|
+
group: 'Display canonico',
|
|
2423
|
+
hint: 'Limite preferencial de badges antes de overflow.',
|
|
2424
|
+
},
|
|
2425
|
+
{
|
|
2426
|
+
name: 'selectedLayout',
|
|
2427
|
+
label: 'Layout selecionado',
|
|
2428
|
+
editorType: 'select',
|
|
2429
|
+
group: 'Exibicao de entidade',
|
|
2430
|
+
options: [
|
|
2431
|
+
{ value: 'card', text: 'card' },
|
|
2432
|
+
{ value: 'inline', text: 'inline' },
|
|
2433
|
+
{ value: 'compact', text: 'compact' },
|
|
2434
|
+
{ value: 'token', text: 'token' },
|
|
2435
|
+
],
|
|
2436
|
+
hint: 'Layout do item selecionado no trigger ou card principal.',
|
|
2437
|
+
},
|
|
2438
|
+
{
|
|
2439
|
+
name: 'resultLayout',
|
|
2440
|
+
label: 'Layout dos resultados',
|
|
2441
|
+
editorType: 'select',
|
|
2442
|
+
group: 'Exibicao de entidade',
|
|
2443
|
+
options: [
|
|
2444
|
+
{ value: 'list', text: 'list' },
|
|
2445
|
+
{ value: 'denseList', text: 'denseList' },
|
|
2446
|
+
{ value: 'table', text: 'table' },
|
|
2447
|
+
{ value: 'card', text: 'card' },
|
|
2448
|
+
],
|
|
2449
|
+
hint: 'Layout principal usado na lista inline e no dialog.',
|
|
2450
|
+
},
|
|
2451
|
+
{
|
|
2452
|
+
name: 'showCode',
|
|
2453
|
+
label: 'Mostrar codigo',
|
|
2454
|
+
editorType: 'checkbox',
|
|
2455
|
+
group: 'Exibicao de entidade',
|
|
2456
|
+
},
|
|
2457
|
+
{
|
|
2458
|
+
name: 'showDescription',
|
|
2459
|
+
label: 'Mostrar descricao',
|
|
2460
|
+
editorType: 'checkbox',
|
|
2461
|
+
group: 'Exibicao de entidade',
|
|
2462
|
+
},
|
|
2463
|
+
{
|
|
2464
|
+
name: 'showStatus',
|
|
2465
|
+
label: 'Mostrar status',
|
|
2466
|
+
editorType: 'checkbox',
|
|
2467
|
+
group: 'Exibicao de entidade',
|
|
2468
|
+
},
|
|
2469
|
+
{
|
|
2470
|
+
name: 'showAvatar',
|
|
2471
|
+
label: 'Mostrar avatar',
|
|
2472
|
+
editorType: 'checkbox',
|
|
2473
|
+
group: 'Exibicao de entidade',
|
|
2474
|
+
},
|
|
2475
|
+
{
|
|
2476
|
+
name: 'showBadges',
|
|
2477
|
+
label: 'Mostrar badges',
|
|
2478
|
+
editorType: 'checkbox',
|
|
2479
|
+
group: 'Exibicao de entidade',
|
|
2480
|
+
},
|
|
2481
|
+
{
|
|
2482
|
+
name: 'showDisabledReason',
|
|
2483
|
+
label: 'Mostrar motivo bloqueado',
|
|
2484
|
+
editorType: 'checkbox',
|
|
2485
|
+
group: 'Exibicao de entidade',
|
|
2486
|
+
},
|
|
2487
|
+
{
|
|
2488
|
+
name: 'showResultCount',
|
|
2489
|
+
label: 'Mostrar contador de resultados',
|
|
2490
|
+
editorType: 'checkbox',
|
|
2491
|
+
group: 'Exibicao de entidade',
|
|
2492
|
+
},
|
|
2493
|
+
{
|
|
2494
|
+
name: 'statusToneMap',
|
|
2495
|
+
label: 'Mapa de tons de status',
|
|
2496
|
+
editorType: 'textarea',
|
|
2497
|
+
group: 'Exibicao de entidade',
|
|
2498
|
+
hint: 'JSON object. Ex.: { \"ACTIVE\": \"success\", \"BLOCKED\": \"danger\" }.',
|
|
2499
|
+
},
|
|
2500
|
+
{
|
|
2501
|
+
name: 'badgeKeys',
|
|
2502
|
+
label: 'Chaves de badges',
|
|
2503
|
+
editorType: 'textarea',
|
|
2504
|
+
group: 'Exibicao de entidade',
|
|
2505
|
+
hint: 'JSON array ou uma linha por chave complementar de badge.',
|
|
2506
|
+
},
|
|
2507
|
+
{
|
|
2508
|
+
name: 'maxVisibleBadges',
|
|
2509
|
+
label: 'Maximo de badges visiveis',
|
|
2510
|
+
editorType: 'number',
|
|
2511
|
+
group: 'Exibicao de entidade',
|
|
2512
|
+
hint: 'Limita a quantidade de badges visiveis antes do overflow.',
|
|
2513
|
+
},
|
|
1382
2514
|
{
|
|
1383
2515
|
name: 'optionSource.searchPropertyPaths',
|
|
1384
2516
|
label: 'Campos de busca',
|
|
@@ -1413,6 +2545,48 @@ const entityLookupProperties = [
|
|
|
1413
2545
|
defaultValue: true,
|
|
1414
2546
|
hint: 'Mantem entidades ja selecionadas visiveis durante busca paginada.',
|
|
1415
2547
|
},
|
|
2548
|
+
{
|
|
2549
|
+
name: 'optionSource.filtering.availableFilters',
|
|
2550
|
+
label: 'Filtros disponiveis',
|
|
2551
|
+
editorType: 'textarea',
|
|
2552
|
+
group: 'Busca',
|
|
2553
|
+
hint: 'JSON array de filtros estruturados. Ex.: [{ \"field\": \"status\", \"type\": \"enum\", \"operators\": [\"in\"] }].',
|
|
2554
|
+
},
|
|
2555
|
+
{
|
|
2556
|
+
name: 'optionSource.filtering.defaultFilters',
|
|
2557
|
+
label: 'Filtros padrao',
|
|
2558
|
+
editorType: 'textarea',
|
|
2559
|
+
group: 'Busca',
|
|
2560
|
+
hint: 'JSON object. Ex.: { \"status\": [\"ACTIVE\"] }.',
|
|
2561
|
+
},
|
|
2562
|
+
{
|
|
2563
|
+
name: 'optionSource.filtering.sortOptions',
|
|
2564
|
+
label: 'Ordenacoes disponiveis',
|
|
2565
|
+
editorType: 'textarea',
|
|
2566
|
+
group: 'Busca',
|
|
2567
|
+
hint: 'JSON array. Ex.: [{ \"key\": \"code:asc\", \"field\": \"code\", \"direction\": \"asc\", \"label\": \"Codigo\" }].',
|
|
2568
|
+
},
|
|
2569
|
+
{
|
|
2570
|
+
name: 'optionSource.filtering.defaultSort',
|
|
2571
|
+
label: 'Ordenacao padrao',
|
|
2572
|
+
editorType: 'text',
|
|
2573
|
+
group: 'Busca',
|
|
2574
|
+
hint: 'Chave default do sort declarada em sortOptions.',
|
|
2575
|
+
},
|
|
2576
|
+
{
|
|
2577
|
+
name: 'optionSource.filtering.quickFilterFields',
|
|
2578
|
+
label: 'Campos de filtro rapido',
|
|
2579
|
+
editorType: 'textarea',
|
|
2580
|
+
group: 'Busca',
|
|
2581
|
+
hint: 'JSON array ou uma linha por campo rapido no toolbar do lookup.',
|
|
2582
|
+
},
|
|
2583
|
+
{
|
|
2584
|
+
name: 'optionSource.filtering.searchPlaceholder',
|
|
2585
|
+
label: 'Placeholder canonico de busca',
|
|
2586
|
+
editorType: 'text',
|
|
2587
|
+
group: 'Busca',
|
|
2588
|
+
hint: 'Fallback do optionSource.filtering quando o campo nao sobrescreve searchPlaceholder.',
|
|
2589
|
+
},
|
|
1416
2590
|
{
|
|
1417
2591
|
name: 'optionSource.dependsOn',
|
|
1418
2592
|
label: 'Dependencias',
|
|
@@ -1558,11 +2732,226 @@ const entityLookupProperties = [
|
|
|
1558
2732
|
editorType: 'select',
|
|
1559
2733
|
group: 'Navegacao',
|
|
1560
2734
|
options: [
|
|
1561
|
-
{ value: '
|
|
2735
|
+
{ value: 'samePage', text: 'samePage' },
|
|
2736
|
+
{ value: 'newTab', text: 'newTab' },
|
|
2737
|
+
{ value: 'drawer', text: 'Drawer' },
|
|
2738
|
+
{ value: 'modal', text: 'Modal' },
|
|
2739
|
+
],
|
|
2740
|
+
},
|
|
2741
|
+
{
|
|
2742
|
+
name: 'optionSource.detail.kind',
|
|
2743
|
+
label: 'Tipo de detalhe',
|
|
2744
|
+
editorType: 'select',
|
|
2745
|
+
group: 'Navegacao',
|
|
2746
|
+
options: [
|
|
2747
|
+
{ value: 'surface', text: 'surface' },
|
|
2748
|
+
{ value: 'route', text: 'route' },
|
|
2749
|
+
{ value: 'href', text: 'href' },
|
|
2750
|
+
],
|
|
2751
|
+
hint: 'Use surface como contrato canonico de plataforma; route/href ficam como fallback legado.',
|
|
2752
|
+
},
|
|
2753
|
+
{
|
|
2754
|
+
name: 'optionSource.detail.surfaceId',
|
|
2755
|
+
label: 'Surface de detalhe',
|
|
2756
|
+
editorType: 'text',
|
|
2757
|
+
group: 'Navegacao',
|
|
2758
|
+
hint: 'Id da surface item-level preferida. Ex.: view, detail.',
|
|
2759
|
+
},
|
|
2760
|
+
{
|
|
2761
|
+
name: 'optionSource.detail.presentation',
|
|
2762
|
+
label: 'Apresentacao da surface',
|
|
2763
|
+
editorType: 'select',
|
|
2764
|
+
group: 'Navegacao',
|
|
2765
|
+
options: [
|
|
1562
2766
|
{ value: 'drawer', text: 'Drawer' },
|
|
1563
|
-
{ value: '
|
|
1564
|
-
{ value: 'external', text: 'Externo' },
|
|
2767
|
+
{ value: 'modal', text: 'Modal' },
|
|
1565
2768
|
],
|
|
2769
|
+
hint: 'Como o runtime deve materializar a surface de detalhe.',
|
|
2770
|
+
},
|
|
2771
|
+
{
|
|
2772
|
+
name: 'optionSource.detail.preferredWidget',
|
|
2773
|
+
label: 'Widget preferido',
|
|
2774
|
+
editorType: 'text',
|
|
2775
|
+
group: 'Navegacao',
|
|
2776
|
+
hint: 'Hint semantico para autoria/discovery. Ex.: praxis-dynamic-form.',
|
|
2777
|
+
},
|
|
2778
|
+
{
|
|
2779
|
+
name: 'optionSource.detail.mode',
|
|
2780
|
+
label: 'Modo da surface',
|
|
2781
|
+
editorType: 'select',
|
|
2782
|
+
group: 'Navegacao',
|
|
2783
|
+
options: [
|
|
2784
|
+
{ value: 'view', text: 'view' },
|
|
2785
|
+
{ value: 'edit', text: 'edit' },
|
|
2786
|
+
{ value: 'create', text: 'create' },
|
|
2787
|
+
],
|
|
2788
|
+
hint: 'Modo semantico da surface de detalhe.',
|
|
2789
|
+
},
|
|
2790
|
+
{
|
|
2791
|
+
name: 'optionSource.create.hrefTemplate',
|
|
2792
|
+
label: 'Template create href',
|
|
2793
|
+
editorType: 'text',
|
|
2794
|
+
group: 'Navegacao',
|
|
2795
|
+
hint: 'Template opcional de criacao publicado pelo optionSource.',
|
|
2796
|
+
},
|
|
2797
|
+
{
|
|
2798
|
+
name: 'optionSource.create.routeTemplate',
|
|
2799
|
+
label: 'Template create route',
|
|
2800
|
+
editorType: 'text',
|
|
2801
|
+
group: 'Navegacao',
|
|
2802
|
+
hint: 'Rota opcional de criacao, por exemplo /suppliers/new.',
|
|
2803
|
+
},
|
|
2804
|
+
{
|
|
2805
|
+
name: 'optionSource.create.openMode',
|
|
2806
|
+
label: 'Modo de create',
|
|
2807
|
+
editorType: 'select',
|
|
2808
|
+
group: 'Navegacao',
|
|
2809
|
+
options: [
|
|
2810
|
+
{ value: 'samePage', text: 'samePage' },
|
|
2811
|
+
{ value: 'newTab', text: 'newTab' },
|
|
2812
|
+
{ value: 'drawer', text: 'Drawer' },
|
|
2813
|
+
{ value: 'modal', text: 'Modal' },
|
|
2814
|
+
],
|
|
2815
|
+
},
|
|
2816
|
+
{
|
|
2817
|
+
name: 'dialog.enabled',
|
|
2818
|
+
label: 'Dialog habilitado',
|
|
2819
|
+
editorType: 'checkbox',
|
|
2820
|
+
group: 'Dialogo avancado',
|
|
2821
|
+
},
|
|
2822
|
+
{
|
|
2823
|
+
name: 'dialog.title',
|
|
2824
|
+
label: 'Titulo do dialog',
|
|
2825
|
+
editorType: 'text',
|
|
2826
|
+
group: 'Dialogo avancado',
|
|
2827
|
+
},
|
|
2828
|
+
{
|
|
2829
|
+
name: 'dialog.size',
|
|
2830
|
+
label: 'Tamanho do dialog',
|
|
2831
|
+
editorType: 'select',
|
|
2832
|
+
group: 'Dialogo avancado',
|
|
2833
|
+
options: [
|
|
2834
|
+
{ value: 'sm', text: 'sm' },
|
|
2835
|
+
{ value: 'md', text: 'md' },
|
|
2836
|
+
{ value: 'lg', text: 'lg' },
|
|
2837
|
+
{ value: 'xl', text: 'xl' },
|
|
2838
|
+
{ value: 'full', text: 'full' },
|
|
2839
|
+
],
|
|
2840
|
+
},
|
|
2841
|
+
{
|
|
2842
|
+
name: 'dialog.previewPanel',
|
|
2843
|
+
label: 'Preview panel',
|
|
2844
|
+
editorType: 'checkbox',
|
|
2845
|
+
group: 'Dialogo avancado',
|
|
2846
|
+
},
|
|
2847
|
+
{
|
|
2848
|
+
name: 'dialog.allowColumnChooser',
|
|
2849
|
+
label: 'Permitir seletor de colunas',
|
|
2850
|
+
editorType: 'checkbox',
|
|
2851
|
+
group: 'Dialogo avancado',
|
|
2852
|
+
},
|
|
2853
|
+
{
|
|
2854
|
+
name: 'dialog.allowSavedViews',
|
|
2855
|
+
label: 'Permitir views salvas',
|
|
2856
|
+
editorType: 'checkbox',
|
|
2857
|
+
group: 'Dialogo avancado',
|
|
2858
|
+
},
|
|
2859
|
+
{
|
|
2860
|
+
name: 'dialog.resultColumns',
|
|
2861
|
+
label: 'Colunas do resultado',
|
|
2862
|
+
editorType: 'textarea',
|
|
2863
|
+
group: 'Dialogo avancado',
|
|
2864
|
+
hint: 'JSON array. Ex.: [{ \"field\": \"code\", \"label\": \"Codigo\", \"kind\": \"code\", \"width\": \"140px\" }].',
|
|
2865
|
+
},
|
|
2866
|
+
{
|
|
2867
|
+
name: 'dialog.openActionLabel',
|
|
2868
|
+
label: 'Label abrir dialog',
|
|
2869
|
+
editorType: 'text',
|
|
2870
|
+
group: 'Dialogo avancado',
|
|
2871
|
+
},
|
|
2872
|
+
{
|
|
2873
|
+
name: 'dialog.applyActionLabel',
|
|
2874
|
+
label: 'Label aplicar dialog',
|
|
2875
|
+
editorType: 'text',
|
|
2876
|
+
group: 'Dialogo avancado',
|
|
2877
|
+
},
|
|
2878
|
+
{
|
|
2879
|
+
name: 'dialog.cancelActionLabel',
|
|
2880
|
+
label: 'Label cancelar dialog',
|
|
2881
|
+
editorType: 'text',
|
|
2882
|
+
group: 'Dialogo avancado',
|
|
2883
|
+
},
|
|
2884
|
+
{
|
|
2885
|
+
name: 'detailActionLabel',
|
|
2886
|
+
label: 'Label detalhe',
|
|
2887
|
+
editorType: 'text',
|
|
2888
|
+
group: 'Acoes do lookup',
|
|
2889
|
+
},
|
|
2890
|
+
{
|
|
2891
|
+
name: 'changeActionLabel',
|
|
2892
|
+
label: 'Label trocar',
|
|
2893
|
+
editorType: 'text',
|
|
2894
|
+
group: 'Acoes do lookup',
|
|
2895
|
+
},
|
|
2896
|
+
{
|
|
2897
|
+
name: 'copyCodeActionLabel',
|
|
2898
|
+
label: 'Label copiar codigo',
|
|
2899
|
+
editorType: 'text',
|
|
2900
|
+
group: 'Acoes do lookup',
|
|
2901
|
+
},
|
|
2902
|
+
{
|
|
2903
|
+
name: 'copyIdActionLabel',
|
|
2904
|
+
label: 'Label copiar ID',
|
|
2905
|
+
editorType: 'text',
|
|
2906
|
+
group: 'Acoes do lookup',
|
|
2907
|
+
},
|
|
2908
|
+
{
|
|
2909
|
+
name: 'createActionLabel',
|
|
2910
|
+
label: 'Label criar',
|
|
2911
|
+
editorType: 'text',
|
|
2912
|
+
group: 'Acoes do lookup',
|
|
2913
|
+
},
|
|
2914
|
+
{
|
|
2915
|
+
name: 'clearActionLabel',
|
|
2916
|
+
label: 'Label limpar',
|
|
2917
|
+
editorType: 'text',
|
|
2918
|
+
group: 'Acoes do lookup',
|
|
2919
|
+
},
|
|
2920
|
+
{
|
|
2921
|
+
name: 'actions.showDetail',
|
|
2922
|
+
label: 'Acao: detalhe',
|
|
2923
|
+
editorType: 'checkbox',
|
|
2924
|
+
group: 'Acoes do lookup',
|
|
2925
|
+
},
|
|
2926
|
+
{
|
|
2927
|
+
name: 'actions.showChange',
|
|
2928
|
+
label: 'Acao: trocar',
|
|
2929
|
+
editorType: 'checkbox',
|
|
2930
|
+
group: 'Acoes do lookup',
|
|
2931
|
+
},
|
|
2932
|
+
{
|
|
2933
|
+
name: 'actions.showCopyCode',
|
|
2934
|
+
label: 'Acao: copiar codigo',
|
|
2935
|
+
editorType: 'checkbox',
|
|
2936
|
+
group: 'Acoes do lookup',
|
|
2937
|
+
},
|
|
2938
|
+
{
|
|
2939
|
+
name: 'actions.showCopyId',
|
|
2940
|
+
label: 'Acao: copiar ID',
|
|
2941
|
+
editorType: 'checkbox',
|
|
2942
|
+
group: 'Acoes do lookup',
|
|
2943
|
+
},
|
|
2944
|
+
{
|
|
2945
|
+
name: 'actions.showCreate',
|
|
2946
|
+
label: 'Acao: criar',
|
|
2947
|
+
editorType: 'checkbox',
|
|
2948
|
+
group: 'Acoes do lookup',
|
|
2949
|
+
},
|
|
2950
|
+
{
|
|
2951
|
+
name: 'actions.showClear',
|
|
2952
|
+
label: 'Acao: limpar',
|
|
2953
|
+
editorType: 'checkbox',
|
|
2954
|
+
group: 'Acoes do lookup',
|
|
1566
2955
|
},
|
|
1567
2956
|
];
|
|
1568
2957
|
|
|
@@ -3485,6 +4874,18 @@ const sliderProperties = [
|
|
|
3485
4874
|
// Formato/Comportamento
|
|
3486
4875
|
{ name: 'step', label: 'Step', editorType: 'number', group: 'Formato/Comportamento', hint: 'Incremento entre valores (ex.: 1, 0.5). Combine com min/max.' },
|
|
3487
4876
|
{ name: 'thumbLabel', label: 'Thumb label', editorType: 'checkbox', group: 'Formato/Comportamento', hint: 'Exibe valor sobre o thumb ao arrastar.' },
|
|
4877
|
+
{ name: 'valueLabelDisplay', label: 'Value label', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
4878
|
+
{ value: 'off', text: 'Desligado' },
|
|
4879
|
+
{ value: 'auto', text: 'Ao interagir' },
|
|
4880
|
+
{ value: 'on', text: 'Sempre visivel' },
|
|
4881
|
+
], hint: 'Controla quando o valor aparece no thumb.' },
|
|
4882
|
+
{ name: 'valueLabelFormat', label: 'Formato do valor', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
4883
|
+
{ value: 'number', text: 'Numero' },
|
|
4884
|
+
{ value: 'percent', text: 'Percentual' },
|
|
4885
|
+
{ value: 'currency', text: 'Moeda' },
|
|
4886
|
+
{ value: 'compact', text: 'Compacto' },
|
|
4887
|
+
{ value: 'storage', text: 'Armazenamento' },
|
|
4888
|
+
], hint: 'Formatter declarativo para labels e aria-valuetext.' },
|
|
3488
4889
|
{ name: 'vertical', label: 'Vertical', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'fmt.flags', inline: true, hint: 'Altera para eixo Y.' },
|
|
3489
4890
|
{ name: 'invert', label: 'Inverter', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'fmt.flags', inline: true, hint: 'Inverte direção (direita→esquerda).' },
|
|
3490
4891
|
{ name: 'showTicks', label: 'Ticks', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
@@ -3492,6 +4893,26 @@ const sliderProperties = [
|
|
|
3492
4893
|
{ value: true, text: 'Com ticks' },
|
|
3493
4894
|
{ value: 'auto', text: 'Auto' },
|
|
3494
4895
|
], hint: 'Marcas na trilha; auto calcula com base no step.' },
|
|
4896
|
+
{ name: 'marks', label: 'Marcadores (JSON)', editorType: 'textarea', group: 'Formato/Comportamento', hint: 'Array JSON. Ex.: [{"value":0,"label":"Baixo"},{"value":100,"label":"Alto","tone":"danger"}].' },
|
|
4897
|
+
{ name: 'semanticBands', label: 'Bandas semanticas (JSON)', editorType: 'textarea', group: 'Formato/Comportamento', hint: 'Array JSON. Ex.: [{"start":0,"end":40,"label":"Saudavel","tone":"success"}].' },
|
|
4898
|
+
{ name: 'distribution', label: 'Distribuicao (JSON)', editorType: 'textarea', group: 'Formato/Comportamento', hint: 'Array JSON ou config de histograma compacto. Ex.: {"colorMode":"gradient","bins":[4,8,16,12,6]}.' },
|
|
4899
|
+
{ name: 'track', label: 'Track', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
4900
|
+
{ value: 'normal', text: 'Normal' },
|
|
4901
|
+
{ value: 'inverted', text: 'Invertido' },
|
|
4902
|
+
{ value: 'none', text: 'Sem track ativo' },
|
|
4903
|
+
], hint: 'Apresentacao visual do track.' },
|
|
4904
|
+
{ name: 'size', label: 'Tamanho', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
4905
|
+
{ value: 'small', text: 'Pequeno' },
|
|
4906
|
+
{ value: 'medium', text: 'Medio' },
|
|
4907
|
+
{ value: 'large', text: 'Grande' },
|
|
4908
|
+
], hint: 'Densidade visual do slider.' },
|
|
4909
|
+
{ name: 'scale', label: 'Escala', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
4910
|
+
{ value: 'linear', text: 'Linear' },
|
|
4911
|
+
{ value: 'percent', text: 'Percentual' },
|
|
4912
|
+
{ value: 'log', text: 'Logaritmica' },
|
|
4913
|
+
{ value: 'pow2', text: 'Potencia 2' },
|
|
4914
|
+
], hint: 'Escala declarativa para dominios nao lineares.' },
|
|
4915
|
+
{ name: 'shiftStep', label: 'Shift step', editorType: 'number', group: 'Formato/Comportamento', hint: 'Incremento acelerado para teclado.' },
|
|
3495
4916
|
{ name: 'discrete', label: 'Discreto', editorType: 'checkbox', group: 'Formato/Comportamento', hint: 'Exibe ticks/labels fixos para valores discretos.' },
|
|
3496
4917
|
// Validação
|
|
3497
4918
|
{ name: 'min', label: 'Mínimo', editorType: 'number', group: 'Validação', row: 'val.minmax', inline: true },
|
|
@@ -3531,11 +4952,44 @@ const rangeSliderProperties = [
|
|
|
3531
4952
|
], hint: 'Escolha faixa para dois marcadores.' },
|
|
3532
4953
|
{ name: 'step', label: 'Step', editorType: 'number', group: 'Formato/Comportamento', hint: 'Incremento entre valores (ex.: 1, 0.5). Combine com min/max.' },
|
|
3533
4954
|
{ name: 'thumbLabel', label: 'Thumb label', editorType: 'checkbox', group: 'Formato/Comportamento', hint: 'Exibe o valor sobre o thumb ao arrastar.' },
|
|
4955
|
+
{ name: 'valueLabelDisplay', label: 'Value label', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
4956
|
+
{ value: 'off', text: 'Desligado' },
|
|
4957
|
+
{ value: 'auto', text: 'Ao interagir' },
|
|
4958
|
+
{ value: 'on', text: 'Sempre visivel' },
|
|
4959
|
+
], hint: 'Controla quando o valor aparece no thumb.' },
|
|
4960
|
+
{ name: 'valueLabelFormat', label: 'Formato do valor', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
4961
|
+
{ value: 'number', text: 'Numero' },
|
|
4962
|
+
{ value: 'percent', text: 'Percentual' },
|
|
4963
|
+
{ value: 'currency', text: 'Moeda' },
|
|
4964
|
+
{ value: 'compact', text: 'Compacto' },
|
|
4965
|
+
{ value: 'storage', text: 'Armazenamento' },
|
|
4966
|
+
], hint: 'Formatter declarativo para labels e aria-valuetext.' },
|
|
3534
4967
|
{ name: 'showTicks', label: 'Ticks', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
3535
4968
|
{ value: false, text: 'Sem ticks' },
|
|
3536
4969
|
{ value: true, text: 'Com ticks' },
|
|
3537
4970
|
{ value: 'auto', text: 'Auto' },
|
|
3538
4971
|
], hint: 'Marcas na trilha; auto calcula com base no step.' },
|
|
4972
|
+
{ name: 'marks', label: 'Marcadores (JSON)', editorType: 'textarea', group: 'Formato/Comportamento', hint: 'Array JSON. Ex.: [{"value":0,"label":"Min"},{"value":100,"label":"Max","tone":"danger"}].' },
|
|
4973
|
+
{ name: 'semanticBands', label: 'Bandas semanticas (JSON)', editorType: 'textarea', group: 'Formato/Comportamento', hint: 'Array JSON. Ex.: [{"start":0,"end":40,"label":"Baixo","tone":"success"}].' },
|
|
4974
|
+
{ name: 'distribution', label: 'Distribuicao (JSON)', editorType: 'textarea', group: 'Formato/Comportamento', hint: 'Array JSON ou config de histograma compacto. Ex.: {"colorMode":"gradient","bins":[4,8,16,12,6]}.' },
|
|
4975
|
+
{ name: 'track', label: 'Track', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
4976
|
+
{ value: 'normal', text: 'Normal' },
|
|
4977
|
+
{ value: 'inverted', text: 'Invertido' },
|
|
4978
|
+
{ value: 'none', text: 'Sem track ativo' },
|
|
4979
|
+
], hint: 'Apresentacao visual do track.' },
|
|
4980
|
+
{ name: 'size', label: 'Tamanho', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
4981
|
+
{ value: 'small', text: 'Pequeno' },
|
|
4982
|
+
{ value: 'medium', text: 'Medio' },
|
|
4983
|
+
{ value: 'large', text: 'Grande' },
|
|
4984
|
+
], hint: 'Densidade visual do slider.' },
|
|
4985
|
+
{ name: 'scale', label: 'Escala', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
4986
|
+
{ value: 'linear', text: 'Linear' },
|
|
4987
|
+
{ value: 'percent', text: 'Percentual' },
|
|
4988
|
+
{ value: 'log', text: 'Logaritmica' },
|
|
4989
|
+
{ value: 'pow2', text: 'Potencia 2' },
|
|
4990
|
+
], hint: 'Escala declarativa para dominios nao lineares.' },
|
|
4991
|
+
{ name: 'shiftStep', label: 'Shift step', editorType: 'number', group: 'Formato/Comportamento', hint: 'Incremento acelerado para teclado.' },
|
|
4992
|
+
{ name: 'disableThumbSwap', label: 'Bloquear troca dos thumbs', editorType: 'checkbox', group: 'Formato/Comportamento', hint: 'Mantem start/end sem permitir inversao visual no modo range.' },
|
|
3539
4993
|
{ name: 'discrete', label: 'Discreto', editorType: 'checkbox', group: 'Formato/Comportamento', hint: 'Mostra valor fixo por tick; use com showTicks.' },
|
|
3540
4994
|
{ name: 'readonly', label: 'Somente leitura', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'comp.state', inline: true, hint: 'Mantém o valor renderizado sem permitir ajuste do slider.' },
|
|
3541
4995
|
{ name: 'disabled', label: 'Desabilitado', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'comp.state', inline: true, hint: 'Desativa arraste, foco e presets rápidos.' },
|
|
@@ -5678,6 +7132,7 @@ const INLINE_FILTER_EDITOR_PROPERTIES_BY_CONTROL_TYPE = Object.freeze({
|
|
|
5678
7132
|
[INLINE_FILTER_CONTROL_TYPES.AUTOCOMPLETE]: selectProperties,
|
|
5679
7133
|
[INLINE_FILTER_CONTROL_TYPES.MULTI_SELECT]: selectProperties,
|
|
5680
7134
|
[INLINE_FILTER_CONTROL_TYPES.INPUT]: inputProperties,
|
|
7135
|
+
[INLINE_FILTER_CONTROL_TYPES.PHONE]: inputProperties,
|
|
5681
7136
|
[INLINE_FILTER_CONTROL_TYPES.TOGGLE]: toggleProperties,
|
|
5682
7137
|
[INLINE_FILTER_CONTROL_TYPES.RANGE]: rangeSliderProperties,
|
|
5683
7138
|
[INLINE_FILTER_CONTROL_TYPES.PERIOD_RANGE]: rangeSliderProperties,
|
|
@@ -5717,6 +7172,7 @@ class FieldMetadataEditorComponent {
|
|
|
5717
7172
|
cancel = new EventEmitter();
|
|
5718
7173
|
form;
|
|
5719
7174
|
normalizedProps = [];
|
|
7175
|
+
renderedProps = [];
|
|
5720
7176
|
initialSnapshot = null;
|
|
5721
7177
|
baselineReady = false;
|
|
5722
7178
|
// Lista de tipos suportados no editor (curada para os tipos com config disponível)
|
|
@@ -6179,6 +7635,7 @@ class FieldMetadataEditorComponent {
|
|
|
6179
7635
|
}
|
|
6180
7636
|
}
|
|
6181
7637
|
catch { }
|
|
7638
|
+
this.renderedProps = this.buildRenderedProps(this.normalizedProps);
|
|
6182
7639
|
// Register contextual validators for INPUT/TEXTAREA (minLength <= maxLength)
|
|
6183
7640
|
if (this.controlType === FieldControlType.INPUT ||
|
|
6184
7641
|
this.controlType === FieldControlType.TEXTAREA) {
|
|
@@ -6393,6 +7850,9 @@ class FieldMetadataEditorComponent {
|
|
|
6393
7850
|
});
|
|
6394
7851
|
}
|
|
6395
7852
|
apply() {
|
|
7853
|
+
if (!this.canEmitSettingsValue()) {
|
|
7854
|
+
return;
|
|
7855
|
+
}
|
|
6396
7856
|
const raw = this.factory.extractPatch(this.form);
|
|
6397
7857
|
const patch = this.buildDeltaPatch(raw, this.initialSnapshot);
|
|
6398
7858
|
this.normalizeKnownTextareaPatchValues(patch);
|
|
@@ -6420,6 +7880,9 @@ class FieldMetadataEditorComponent {
|
|
|
6420
7880
|
}
|
|
6421
7881
|
// Settings Panel value provider API
|
|
6422
7882
|
getSettingsValue() {
|
|
7883
|
+
if (!this.canEmitSettingsValue()) {
|
|
7884
|
+
return {};
|
|
7885
|
+
}
|
|
6423
7886
|
const raw = this.factory.extractPatch(this.form);
|
|
6424
7887
|
const patch = this.buildDeltaPatch(raw, this.initialSnapshot);
|
|
6425
7888
|
this.normalizeKnownTextareaPatchValues(patch);
|
|
@@ -6524,17 +7987,38 @@ class FieldMetadataEditorComponent {
|
|
|
6524
7987
|
}
|
|
6525
7988
|
normalizeEntityLookupPatchValues(patch) {
|
|
6526
7989
|
const optionSource = patch?.optionSource;
|
|
6527
|
-
if (
|
|
6528
|
-
|
|
7990
|
+
if (optionSource && typeof optionSource === 'object') {
|
|
7991
|
+
this.normalizeStringListProperty(optionSource, 'descriptionPropertyPaths');
|
|
7992
|
+
this.normalizeStringListProperty(optionSource, 'searchPropertyPaths');
|
|
7993
|
+
this.normalizeStringListProperty(optionSource, 'dependsOn');
|
|
7994
|
+
this.normalizeJsonObjectProperty(optionSource, 'dependencyFilterMap');
|
|
7995
|
+
this.normalizeJsonObjectProperty(optionSource, 'create');
|
|
7996
|
+
const selectionPolicy = optionSource.selectionPolicy;
|
|
7997
|
+
if (selectionPolicy && typeof selectionPolicy === 'object') {
|
|
7998
|
+
this.normalizeStringListProperty(selectionPolicy, 'allowedStatuses');
|
|
7999
|
+
this.normalizeStringListProperty(selectionPolicy, 'blockedStatuses');
|
|
8000
|
+
}
|
|
8001
|
+
const filtering = optionSource.filtering;
|
|
8002
|
+
if (filtering && typeof filtering === 'object') {
|
|
8003
|
+
this.normalizeJsonArrayProperty(filtering, 'availableFilters');
|
|
8004
|
+
this.normalizeJsonObjectProperty(filtering, 'defaultFilters');
|
|
8005
|
+
this.normalizeJsonArrayProperty(filtering, 'sortOptions');
|
|
8006
|
+
this.normalizeStringListProperty(filtering, 'quickFilterFields');
|
|
8007
|
+
}
|
|
8008
|
+
const display = optionSource.display;
|
|
8009
|
+
if (display && typeof display === 'object') {
|
|
8010
|
+
this.normalizeJsonArrayProperty(display, 'fields');
|
|
8011
|
+
this.normalizeStringListProperty(display, 'secondaryPropertyPaths');
|
|
8012
|
+
this.normalizeStringListProperty(display, 'badgePropertyPaths');
|
|
8013
|
+
this.normalizeStringListProperty(display, 'badgeKeys');
|
|
8014
|
+
this.normalizeJsonObjectProperty(display, 'statusToneMap');
|
|
8015
|
+
}
|
|
6529
8016
|
}
|
|
6530
|
-
this.normalizeStringListProperty(
|
|
6531
|
-
this.
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
if (selectionPolicy && typeof selectionPolicy === 'object') {
|
|
6536
|
-
this.normalizeStringListProperty(selectionPolicy, 'allowedStatuses');
|
|
6537
|
-
this.normalizeStringListProperty(selectionPolicy, 'blockedStatuses');
|
|
8017
|
+
this.normalizeStringListProperty(patch, 'badgeKeys');
|
|
8018
|
+
this.normalizeJsonObjectProperty(patch, 'statusToneMap');
|
|
8019
|
+
const dialog = patch?.dialog;
|
|
8020
|
+
if (dialog && typeof dialog === 'object') {
|
|
8021
|
+
this.normalizeJsonArrayProperty(dialog, 'resultColumns');
|
|
6538
8022
|
}
|
|
6539
8023
|
}
|
|
6540
8024
|
normalizeStringListProperty(target, key) {
|
|
@@ -6555,6 +8039,15 @@ class FieldMetadataEditorComponent {
|
|
|
6555
8039
|
target[key] = parsed;
|
|
6556
8040
|
}
|
|
6557
8041
|
}
|
|
8042
|
+
normalizeJsonArrayProperty(target, key) {
|
|
8043
|
+
if (!target || typeof target !== 'object' || typeof target[key] !== 'string') {
|
|
8044
|
+
return;
|
|
8045
|
+
}
|
|
8046
|
+
const parsed = this.parseArrayTextarea(target[key]);
|
|
8047
|
+
if (parsed) {
|
|
8048
|
+
target[key] = parsed;
|
|
8049
|
+
}
|
|
8050
|
+
}
|
|
6558
8051
|
normalizeArrayPatchValues(patch) {
|
|
6559
8052
|
const arrayPatch = patch?.array;
|
|
6560
8053
|
if (!arrayPatch || typeof arrayPatch !== 'object') {
|
|
@@ -6663,7 +8156,11 @@ class FieldMetadataEditorComponent {
|
|
|
6663
8156
|
}
|
|
6664
8157
|
}
|
|
6665
8158
|
reset() {
|
|
6666
|
-
this.
|
|
8159
|
+
const snapshot = this.cloneSnapshot(this.initialSnapshot ?? this.form.getRawValue());
|
|
8160
|
+
this.form.reset(snapshot);
|
|
8161
|
+
this.form.markAsPristine();
|
|
8162
|
+
this.form.markAsUntouched();
|
|
8163
|
+
this.form.updateValueAndValidity({ emitEvent: true });
|
|
6667
8164
|
this.isDirty$.next(false);
|
|
6668
8165
|
}
|
|
6669
8166
|
onControlTypeChange(next) {
|
|
@@ -6681,30 +8178,167 @@ class FieldMetadataEditorComponent {
|
|
|
6681
8178
|
const missingSharedProps = fieldSubmitProperties.filter((prop) => !propNames.has(prop.name));
|
|
6682
8179
|
return missingSharedProps.length ? [...props, ...missingSharedProps] : props;
|
|
6683
8180
|
}
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
8181
|
+
get isArrayEditor() {
|
|
8182
|
+
return this.controlType === FieldControlType.ARRAY_INPUT;
|
|
8183
|
+
}
|
|
8184
|
+
buildRenderedProps(props) {
|
|
8185
|
+
if (!this.isArrayEditor) {
|
|
8186
|
+
return props;
|
|
8187
|
+
}
|
|
8188
|
+
return (props || []).filter((prop) => prop?.name !== 'array.itemSchema.fields');
|
|
8189
|
+
}
|
|
8190
|
+
cloneSnapshot(value) {
|
|
8191
|
+
return this.cloneMetadataValue(value, new WeakMap());
|
|
8192
|
+
}
|
|
8193
|
+
cloneMetadataValue(value, seen) {
|
|
8194
|
+
if (!value || typeof value !== 'object') {
|
|
8195
|
+
return value;
|
|
8196
|
+
}
|
|
8197
|
+
if (seen.has(value)) {
|
|
8198
|
+
return seen.get(value);
|
|
8199
|
+
}
|
|
8200
|
+
if (value instanceof Date) {
|
|
8201
|
+
return new Date(value.getTime());
|
|
8202
|
+
}
|
|
8203
|
+
if (value instanceof RegExp) {
|
|
8204
|
+
return new RegExp(value.source, value.flags);
|
|
8205
|
+
}
|
|
8206
|
+
if (Array.isArray(value)) {
|
|
8207
|
+
const clone = [];
|
|
8208
|
+
seen.set(value, clone);
|
|
8209
|
+
clone.push(...value.map((entry) => this.cloneMetadataValue(entry, seen)));
|
|
8210
|
+
return clone;
|
|
8211
|
+
}
|
|
8212
|
+
if (value instanceof Map) {
|
|
8213
|
+
const clone = new Map();
|
|
8214
|
+
seen.set(value, clone);
|
|
8215
|
+
value.forEach((entry, key) => {
|
|
8216
|
+
clone.set(this.cloneMetadataValue(key, seen), this.cloneMetadataValue(entry, seen));
|
|
8217
|
+
});
|
|
8218
|
+
return clone;
|
|
8219
|
+
}
|
|
8220
|
+
if (value instanceof Set) {
|
|
8221
|
+
const clone = new Set();
|
|
8222
|
+
seen.set(value, clone);
|
|
8223
|
+
value.forEach((entry) => clone.add(this.cloneMetadataValue(entry, seen)));
|
|
8224
|
+
return clone;
|
|
8225
|
+
}
|
|
8226
|
+
const prototype = Object.getPrototypeOf(value);
|
|
8227
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
8228
|
+
return value;
|
|
8229
|
+
}
|
|
8230
|
+
const clone = {};
|
|
8231
|
+
seen.set(value, clone);
|
|
8232
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
8233
|
+
clone[key] = this.cloneMetadataValue(entry, seen);
|
|
8234
|
+
}
|
|
8235
|
+
return clone;
|
|
8236
|
+
}
|
|
8237
|
+
canEmitSettingsValue() {
|
|
8238
|
+
this.validateArrayItemSchemaFields();
|
|
8239
|
+
this.form.updateValueAndValidity({ emitEvent: true });
|
|
8240
|
+
const valid = this.form.valid;
|
|
8241
|
+
this.isValid$.next(valid);
|
|
8242
|
+
if (!valid) {
|
|
8243
|
+
this.form.markAllAsTouched();
|
|
8244
|
+
}
|
|
8245
|
+
return valid;
|
|
8246
|
+
}
|
|
8247
|
+
validateArrayItemSchemaFields() {
|
|
8248
|
+
if (!this.isArrayEditor) {
|
|
8249
|
+
return;
|
|
8250
|
+
}
|
|
8251
|
+
const control = this.form.get('array.itemSchema.fields');
|
|
8252
|
+
if (!control) {
|
|
8253
|
+
return;
|
|
8254
|
+
}
|
|
8255
|
+
const value = control.value;
|
|
8256
|
+
const fields = Array.isArray(value)
|
|
8257
|
+
? value
|
|
8258
|
+
: typeof value === 'string' && value.trim()
|
|
8259
|
+
? this.parseArrayItemFields(value)
|
|
8260
|
+
: [];
|
|
8261
|
+
const seen = new Set();
|
|
8262
|
+
const duplicated = new Set();
|
|
8263
|
+
let hasEmptyName = false;
|
|
8264
|
+
for (const field of fields) {
|
|
8265
|
+
const name = String(field?.name || '').trim();
|
|
8266
|
+
if (!name) {
|
|
8267
|
+
hasEmptyName = true;
|
|
8268
|
+
continue;
|
|
8269
|
+
}
|
|
8270
|
+
if (seen.has(name)) {
|
|
8271
|
+
duplicated.add(name);
|
|
8272
|
+
}
|
|
8273
|
+
seen.add(name);
|
|
8274
|
+
}
|
|
8275
|
+
this.setControlError(control, 'arrayFieldNameRequired', hasEmptyName);
|
|
8276
|
+
this.setControlError(control, 'arrayFieldNameDuplicated', duplicated.size ? { names: Array.from(duplicated) } : null);
|
|
8277
|
+
}
|
|
8278
|
+
parseArrayItemFields(value) {
|
|
8279
|
+
try {
|
|
8280
|
+
const parsed = JSON.parse(value);
|
|
8281
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
8282
|
+
}
|
|
8283
|
+
catch {
|
|
8284
|
+
return [];
|
|
8285
|
+
}
|
|
8286
|
+
}
|
|
8287
|
+
setControlError(control, key, value) {
|
|
8288
|
+
const errors = { ...(control.errors || {}) };
|
|
8289
|
+
const hasError = value !== null && value !== undefined && value !== false;
|
|
8290
|
+
if (hasError) {
|
|
8291
|
+
errors[key] = value === true ? true : value;
|
|
8292
|
+
}
|
|
8293
|
+
else {
|
|
8294
|
+
delete errors[key];
|
|
8295
|
+
}
|
|
8296
|
+
control.setErrors(Object.keys(errors).length ? errors : null);
|
|
8297
|
+
}
|
|
8298
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: FieldMetadataEditorComponent, deps: [{ token: ConfigRegistryService }, { token: SchemaNormalizerService }, { token: DynamicFormFactoryService }, { token: ContextValidatorRegistryService }], target: i0.ɵɵFactoryTarget.Component });
|
|
8299
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: FieldMetadataEditorComponent, isStandalone: true, selector: "praxis-field-metadata-editor", inputs: { controlType: "controlType", seed: "seed" }, outputs: { applied: "applied", cancel: "cancel" }, ngImport: i0, template: `
|
|
8300
|
+
@if (form) {
|
|
8301
|
+
<div class="p-3">
|
|
8302
|
+
<praxis-dynamic-editor-renderer
|
|
8303
|
+
[properties]="renderedProps"
|
|
8304
|
+
[form]="form"
|
|
8305
|
+
></praxis-dynamic-editor-renderer>
|
|
8306
|
+
@if (isArrayEditor) {
|
|
8307
|
+
<praxis-array-item-schema-fields-editor
|
|
8308
|
+
[control]="form.get('array.itemSchema.fields')"
|
|
8309
|
+
[arrayControl]="form.get('array')"
|
|
8310
|
+
></praxis-array-item-schema-fields-editor>
|
|
8311
|
+
}
|
|
8312
|
+
</div>
|
|
8313
|
+
}
|
|
8314
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "component", type: DynamicEditorRendererComponent, selector: "praxis-dynamic-editor-renderer", inputs: ["properties", "form"] }, { kind: "component", type: ArrayItemSchemaFieldsEditorComponent, selector: "praxis-array-item-schema-fields-editor", inputs: ["control", "arrayControl"] }] });
|
|
6693
8315
|
}
|
|
6694
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
8316
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: FieldMetadataEditorComponent, decorators: [{
|
|
6695
8317
|
type: Component,
|
|
6696
8318
|
args: [{
|
|
6697
8319
|
selector: 'praxis-field-metadata-editor',
|
|
6698
8320
|
standalone: true,
|
|
6699
|
-
imports: [
|
|
8321
|
+
imports: [
|
|
8322
|
+
ReactiveFormsModule,
|
|
8323
|
+
DynamicEditorRendererComponent,
|
|
8324
|
+
ArrayItemSchemaFieldsEditorComponent
|
|
8325
|
+
],
|
|
6700
8326
|
template: `
|
|
6701
|
-
|
|
6702
|
-
<
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
8327
|
+
@if (form) {
|
|
8328
|
+
<div class="p-3">
|
|
8329
|
+
<praxis-dynamic-editor-renderer
|
|
8330
|
+
[properties]="renderedProps"
|
|
8331
|
+
[form]="form"
|
|
8332
|
+
></praxis-dynamic-editor-renderer>
|
|
8333
|
+
@if (isArrayEditor) {
|
|
8334
|
+
<praxis-array-item-schema-fields-editor
|
|
8335
|
+
[control]="form.get('array.itemSchema.fields')"
|
|
8336
|
+
[arrayControl]="form.get('array')"
|
|
8337
|
+
></praxis-array-item-schema-fields-editor>
|
|
8338
|
+
}
|
|
8339
|
+
</div>
|
|
8340
|
+
}
|
|
8341
|
+
`,
|
|
6708
8342
|
}]
|
|
6709
8343
|
}], ctorParameters: () => [{ type: ConfigRegistryService }, { type: SchemaNormalizerService }, { type: DynamicFormFactoryService }, { type: ContextValidatorRegistryService }], propDecorators: { controlType: [{
|
|
6710
8344
|
type: Input
|
|
@@ -6771,10 +8405,10 @@ class CascadeRulesService {
|
|
|
6771
8405
|
dependencyValuePath: rule.dependencyValuePath || inferredValueKey || 'id',
|
|
6772
8406
|
};
|
|
6773
8407
|
}
|
|
6774
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
6775
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
8408
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CascadeRulesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
8409
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CascadeRulesService, providedIn: 'root' });
|
|
6776
8410
|
}
|
|
6777
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
8411
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CascadeRulesService, decorators: [{
|
|
6778
8412
|
type: Injectable,
|
|
6779
8413
|
args: [{ providedIn: 'root' }]
|
|
6780
8414
|
}] });
|
|
@@ -6788,10 +8422,10 @@ class CascadeManagerTabComponent {
|
|
|
6788
8422
|
searchTerm = '';
|
|
6789
8423
|
searchTimer = null;
|
|
6790
8424
|
searchValue = '';
|
|
6791
|
-
rules = signal(new Map(), ...(ngDevMode ? [{ debugName: "rules" }] : []));
|
|
6792
|
-
selectedFieldSig = signal(null, ...(ngDevMode ? [{ debugName: "selectedFieldSig" }] : []));
|
|
8425
|
+
rules = signal(new Map(), ...(ngDevMode ? [{ debugName: "rules" }] : /* istanbul ignore next */ []));
|
|
8426
|
+
selectedFieldSig = signal(null, ...(ngDevMode ? [{ debugName: "selectedFieldSig" }] : /* istanbul ignore next */ []));
|
|
6793
8427
|
// Inline editor state (MVP)
|
|
6794
|
-
editing = signal(false, ...(ngDevMode ? [{ debugName: "editing" }] : []));
|
|
8428
|
+
editing = signal(false, ...(ngDevMode ? [{ debugName: "editing" }] : /* istanbul ignore next */ []));
|
|
6795
8429
|
editDepList = [];
|
|
6796
8430
|
editEnable = true;
|
|
6797
8431
|
editReset = false;
|
|
@@ -7143,8 +8777,8 @@ class CascadeManagerTabComponent {
|
|
|
7143
8777
|
}
|
|
7144
8778
|
return [...new Set(suggestions)];
|
|
7145
8779
|
}
|
|
7146
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
7147
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
8780
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CascadeManagerTabComponent, deps: [{ token: CascadeRulesService }], target: i0.ɵɵFactoryTarget.Component });
|
|
8781
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: CascadeManagerTabComponent, isStandalone: true, selector: "praxis-cascade-manager-tab", inputs: { fields: "fields", connections: "connections" }, outputs: { apply: "apply", cancel: "cancel" }, usesOnChanges: true, ngImport: i0, template: `
|
|
7148
8782
|
<div class="cm-root">
|
|
7149
8783
|
<!-- Left: Fields list -->
|
|
7150
8784
|
<div class="cm-left">
|
|
@@ -7152,276 +8786,301 @@ class CascadeManagerTabComponent {
|
|
|
7152
8786
|
<mat-label>Buscar campos</mat-label>
|
|
7153
8787
|
<input matInput [ngModel]="searchTerm" (ngModelChange)="onSearchChange($event)">
|
|
7154
8788
|
</mat-form-field>
|
|
7155
|
-
|
|
8789
|
+
@if (fields.length <= 50) {
|
|
7156
8790
|
<div>
|
|
7157
|
-
|
|
7158
|
-
<div>
|
|
7159
|
-
<div>
|
|
7160
|
-
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
8791
|
+
@for (f of filteredFields(); track f) {
|
|
8792
|
+
<div (click)="selectField(f.name)" class="field-item" [class.selected]="f.name===selectedField()" tabindex="0" (keydown)="onFieldKeydown($event, f.name)">
|
|
8793
|
+
<div>
|
|
8794
|
+
<div>{{ labelFor(f.name) }}</div>
|
|
8795
|
+
<small style="opacity:.7">{{ f.name }}</small>
|
|
8796
|
+
</div>
|
|
8797
|
+
@if (hasRule(f.name)) {
|
|
8798
|
+
<div class="rule-badges">
|
|
8799
|
+
@if (isCascadeEnabled(f.name)) {
|
|
8800
|
+
<span class="rule-badge b-active">Ativo</span>
|
|
8801
|
+
}
|
|
8802
|
+
@if (loadOnChange(f.name)==='manual') {
|
|
8803
|
+
<span class="rule-badge b-manual">Manual</span>
|
|
8804
|
+
}
|
|
8805
|
+
@if (loadOnChange(f.name)==='immediate') {
|
|
8806
|
+
<span class="rule-badge b-immediate">Immediate</span>
|
|
8807
|
+
}
|
|
8808
|
+
</div>
|
|
8809
|
+
}
|
|
7166
8810
|
</div>
|
|
7167
|
-
|
|
8811
|
+
}
|
|
7168
8812
|
</div>
|
|
7169
|
-
|
|
7170
|
-
<ng-template #vscroll>
|
|
8813
|
+
} @else {
|
|
7171
8814
|
<cdk-virtual-scroll-viewport itemSize="44" style="height: calc(100vh - 240px)">
|
|
7172
8815
|
<div *cdkVirtualFor="let f of filteredFields()" (click)="selectField(f.name)" class="field-item" [class.selected]="f.name===selectedField()" tabindex="0" (keydown)="onFieldKeydown($event, f.name)">
|
|
7173
8816
|
<div>
|
|
7174
8817
|
<div>{{ labelFor(f.name) }}</div>
|
|
7175
8818
|
<small style="opacity:.7">{{ f.name }}</small>
|
|
7176
8819
|
</div>
|
|
7177
|
-
|
|
7178
|
-
<
|
|
7179
|
-
|
|
7180
|
-
|
|
7181
|
-
|
|
8820
|
+
@if (hasRule(f.name)) {
|
|
8821
|
+
<div class="rule-badges">
|
|
8822
|
+
@if (isCascadeEnabled(f.name)) {
|
|
8823
|
+
<span class="rule-badge b-active">Ativo</span>
|
|
8824
|
+
}
|
|
8825
|
+
@if (loadOnChange(f.name)==='manual') {
|
|
8826
|
+
<span class="rule-badge b-manual">Manual</span>
|
|
8827
|
+
}
|
|
8828
|
+
@if (loadOnChange(f.name)==='immediate') {
|
|
8829
|
+
<span class="rule-badge b-immediate">Immediate</span>
|
|
8830
|
+
}
|
|
8831
|
+
</div>
|
|
8832
|
+
}
|
|
7182
8833
|
</div>
|
|
7183
8834
|
</cdk-virtual-scroll-viewport>
|
|
7184
|
-
|
|
8835
|
+
}
|
|
7185
8836
|
</div>
|
|
7186
|
-
|
|
8837
|
+
|
|
7187
8838
|
<!-- Right: Rules for selected field -->
|
|
7188
|
-
|
|
7189
|
-
<div class="cm-
|
|
7190
|
-
<div>
|
|
7191
|
-
<h3 style="margin:0">{{ labelFor(sel) }}</h3>
|
|
7192
|
-
<div style="opacity:.7">{{ sel }}</div>
|
|
7193
|
-
</div>
|
|
7194
|
-
<div>
|
|
7195
|
-
<button mat-stroked-button color="primary" (click)="exportRules()" style="margin-right:8px">Exportar</button>
|
|
7196
|
-
<button mat-stroked-button (click)="toggleImport()" style="margin-right:8px">Importar</button>
|
|
7197
|
-
<button mat-flat-button color="primary" [matMenuTriggerFor]="addMenu" [disabled]="editing()">Adicionar</button>
|
|
7198
|
-
<mat-menu #addMenu="matMenu">
|
|
7199
|
-
<button mat-menu-item (click)="startEdit(sel)"><mat-icon [praxisIcon]="'add'"></mat-icon><span>Em branco</span></button>
|
|
7200
|
-
<button mat-menu-item (click)="startTemplate(sel, 'rota-destino')" [disabled]="!hasField('rotaId') || !hasField('destinoReqId')"><mat-icon [praxisIcon]="'bolt'"></mat-icon><span>Rota → Destino (id)</span></button>
|
|
7201
|
-
<button mat-menu-item (click)="startTemplate(sel, 'rota-destino-propriedade')" [disabled]="!hasField('rotaId') || !hasField('destinoReqId')"><mat-icon [praxisIcon]="'bolt'"></mat-icon><span>Rota + Destino → Propriedade (ids)</span></button>
|
|
7202
|
-
</mat-menu>
|
|
7203
|
-
</div>
|
|
7204
|
-
</div>
|
|
7205
|
-
|
|
7206
|
-
<!-- Conflict banner (connections) -->
|
|
7207
|
-
<div class="banner" *ngIf="hasConflict(sel)">
|
|
7208
|
-
<mat-icon [praxisIcon]="'link_off'"></mat-icon>
|
|
7209
|
-
<div>
|
|
7210
|
-
Connections atualizam este campo (filterCriteria). Considere desativar a cascata nativa ou usar ‘manual’.
|
|
7211
|
-
</div>
|
|
7212
|
-
<span class="spacer"></span>
|
|
7213
|
-
<button mat-stroked-button color="primary" (click)="quickSet(sel,'disable')">Desativar</button>
|
|
7214
|
-
<button mat-stroked-button color="accent" (click)="quickSet(sel,'manual')">Definir manual</button>
|
|
7215
|
-
</div>
|
|
7216
|
-
|
|
7217
|
-
<div *ngIf="importMode" class="import-panel">
|
|
7218
|
-
<div style="display:flex; gap:8px; align-items:center; margin-bottom:8px"><mat-icon [praxisIcon]="'upload'"></mat-icon><strong>Importar regras (JSON)</strong></div>
|
|
7219
|
-
<textarea matInput rows="6" class="full" [(ngModel)]="importJson" placeholder='{ "campo": { "dependencyFields": ["dep"], ... } }'></textarea>
|
|
7220
|
-
<div style="display:flex; gap:8px; justify-content:flex-end; padding-top:8px">
|
|
7221
|
-
<button mat-stroked-button (click)="toggleImport()">Cancelar</button>
|
|
7222
|
-
<button mat-flat-button color="primary" (click)="applyImport()">Aplicar</button>
|
|
7223
|
-
</div>
|
|
7224
|
-
</div>
|
|
7225
|
-
|
|
7226
|
-
<div *ngIf="!ruleFor(sel)">
|
|
7227
|
-
<div class="rules-empty">Nenhuma cascata configurada. Clique em “Adicionar” para montar a dependência.</div>
|
|
7228
|
-
</div>
|
|
7229
|
-
<div *ngIf="ruleFor(sel) as rule">
|
|
7230
|
-
<div class="rule-row">
|
|
8839
|
+
@if (selectedField(); as sel) {
|
|
8840
|
+
<div class="cm-right">
|
|
8841
|
+
<div class="cm-header">
|
|
7231
8842
|
<div>
|
|
7232
|
-
<
|
|
7233
|
-
<div style="opacity:.7">
|
|
8843
|
+
<h3 style="margin:0">{{ labelFor(sel) }}</h3>
|
|
8844
|
+
<div style="opacity:.7">{{ sel }}</div>
|
|
7234
8845
|
</div>
|
|
7235
|
-
<div
|
|
7236
|
-
<button mat-
|
|
7237
|
-
<button mat-
|
|
7238
|
-
<button mat-
|
|
8846
|
+
<div>
|
|
8847
|
+
<button mat-stroked-button color="primary" (click)="exportRules()" style="margin-right:8px">Exportar</button>
|
|
8848
|
+
<button mat-stroked-button (click)="toggleImport()" style="margin-right:8px">Importar</button>
|
|
8849
|
+
<button mat-flat-button color="primary" [matMenuTriggerFor]="addMenu" [disabled]="editing()">Adicionar</button>
|
|
8850
|
+
<mat-menu #addMenu="matMenu">
|
|
8851
|
+
<button mat-menu-item (click)="startEdit(sel)"><mat-icon [praxisIcon]="'add'"></mat-icon><span>Em branco</span></button>
|
|
8852
|
+
<button mat-menu-item (click)="startTemplate(sel, 'rota-destino')" [disabled]="!hasField('rotaId') || !hasField('destinoReqId')"><mat-icon [praxisIcon]="'bolt'"></mat-icon><span>Rota → Destino (id)</span></button>
|
|
8853
|
+
<button mat-menu-item (click)="startTemplate(sel, 'rota-destino-propriedade')" [disabled]="!hasField('rotaId') || !hasField('destinoReqId')"><mat-icon [praxisIcon]="'bolt'"></mat-icon><span>Rota + Destino → Propriedade (ids)</span></button>
|
|
8854
|
+
</mat-menu>
|
|
7239
8855
|
</div>
|
|
7240
8856
|
</div>
|
|
7241
|
-
|
|
7242
|
-
|
|
7243
|
-
|
|
7244
|
-
|
|
7245
|
-
|
|
7246
|
-
|
|
8857
|
+
<!-- Conflict banner (connections) -->
|
|
8858
|
+
@if (hasConflict(sel)) {
|
|
8859
|
+
<div class="banner">
|
|
8860
|
+
<mat-icon [praxisIcon]="'link_off'"></mat-icon>
|
|
8861
|
+
<div>
|
|
8862
|
+
Connections atualizam este campo (filterCriteria). Considere desativar a cascata nativa ou usar ‘manual’.
|
|
8863
|
+
</div>
|
|
8864
|
+
<span class="spacer"></span>
|
|
8865
|
+
<button mat-stroked-button color="primary" (click)="quickSet(sel,'disable')">Desativar</button>
|
|
8866
|
+
<button mat-stroked-button color="accent" (click)="quickSet(sel,'manual')">Definir manual</button>
|
|
8867
|
+
</div>
|
|
8868
|
+
}
|
|
8869
|
+
@if (importMode) {
|
|
8870
|
+
<div class="import-panel">
|
|
8871
|
+
<div style="display:flex; gap:8px; align-items:center; margin-bottom:8px"><mat-icon [praxisIcon]="'upload'"></mat-icon><strong>Importar regras (JSON)</strong></div>
|
|
8872
|
+
<textarea matInput rows="6" class="full" [(ngModel)]="importJson" placeholder='{ "campo": { "dependencyFields": ["dep"], ... } }'></textarea>
|
|
8873
|
+
<div style="display:flex; gap:8px; justify-content:flex-end; padding-top:8px">
|
|
8874
|
+
<button mat-stroked-button (click)="toggleImport()">Cancelar</button>
|
|
8875
|
+
<button mat-flat-button color="primary" (click)="applyImport()">Aplicar</button>
|
|
8876
|
+
</div>
|
|
8877
|
+
</div>
|
|
8878
|
+
}
|
|
8879
|
+
@if (!ruleFor(sel)) {
|
|
7247
8880
|
<div>
|
|
7248
|
-
<div>
|
|
7249
|
-
<small style="color: var(--md-sys-color-on-surface-variant);">Defina dependentes, quando recarregar e como mapear filtros</small>
|
|
8881
|
+
<div class="rules-empty">Nenhuma cascata configurada. Clique em “Adicionar” para montar a dependência.</div>
|
|
7250
8882
|
</div>
|
|
7251
|
-
|
|
7252
|
-
|
|
7253
|
-
|
|
7254
|
-
|
|
7255
|
-
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
|
|
8883
|
+
}
|
|
8884
|
+
@if (ruleFor(sel); as rule) {
|
|
8885
|
+
<div>
|
|
8886
|
+
<div class="rule-row">
|
|
8887
|
+
<div>
|
|
8888
|
+
<div><strong>Dependentes:</strong> {{ rule.dependencyFields.join(', ') || '—' }}</div>
|
|
8889
|
+
<div style="opacity:.7">Load on change: {{ rule.dependencyLoadOnChange || 'respectLoadOn' }} | Merge: {{ rule.dependencyMergeStrategy || 'merge' }}</div>
|
|
8890
|
+
</div>
|
|
8891
|
+
<div class="rule-actions">
|
|
8892
|
+
<button mat-icon-button matTooltip="Editar" (click)="startEdit(sel)"><mat-icon [praxisIcon]="'edit'"></mat-icon></button>
|
|
8893
|
+
<button mat-icon-button matTooltip="Duplicar" disabled><mat-icon [praxisIcon]="'content_copy'"></mat-icon></button>
|
|
8894
|
+
<button mat-icon-button matTooltip="Remover" (click)="remove(sel)"><mat-icon [praxisIcon]="'delete'"></mat-icon></button>
|
|
8895
|
+
</div>
|
|
8896
|
+
</div>
|
|
8897
|
+
</div>
|
|
8898
|
+
}
|
|
8899
|
+
<!-- Editor com seções (sem acordeão duplo) -->
|
|
8900
|
+
<div class="panel-surface">
|
|
8901
|
+
<div class="panel-header" style="padding: 12px 16px; border-bottom: 1px solid var(--md-sys-color-outline-variant);">
|
|
8902
|
+
<mat-icon [praxisIcon]="'rule'"></mat-icon>
|
|
8903
|
+
<div>
|
|
8904
|
+
<div>Cascata do campo</div>
|
|
8905
|
+
<small style="color: var(--md-sys-color-on-surface-variant);">Defina dependentes, quando recarregar e como mapear filtros</small>
|
|
8906
|
+
</div>
|
|
8907
|
+
</div>
|
|
8908
|
+
<div class="panel-stack">
|
|
8909
|
+
<!-- Básico -->
|
|
8910
|
+
<mat-expansion-panel [expanded]="true" class="stack-panel">
|
|
8911
|
+
<mat-expansion-panel-header>
|
|
8912
|
+
<mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'tune'"></mat-icon> Básico</mat-panel-title>
|
|
8913
|
+
</mat-expansion-panel-header>
|
|
8914
|
+
<div class="form-grid">
|
|
8915
|
+
<div class="form-row">
|
|
8916
|
+
<mat-form-field appearance="outline" class="full">
|
|
8917
|
+
<mat-label>Campos dependentes</mat-label>
|
|
8918
|
+
<mat-select multiple [(ngModel)]="editDepList" (ngModelChange)="onDepListChange()">
|
|
8919
|
+
@for (f of availableDependencyFields(); track f) {
|
|
8920
|
+
<mat-option [value]="f.name">
|
|
8921
|
+
{{ labelFor(f.name) }} ({{ f.name }})
|
|
8922
|
+
</mat-option>
|
|
8923
|
+
}
|
|
8924
|
+
</mat-select>
|
|
8925
|
+
<button
|
|
8926
|
+
mat-icon-button
|
|
8927
|
+
matSuffix
|
|
8928
|
+
class="help-icon-button"
|
|
8929
|
+
type="button"
|
|
8930
|
+
[matTooltip]="'Selecione os campos que controlam este valor'"
|
|
8931
|
+
matTooltipPosition="above"
|
|
8932
|
+
>
|
|
8933
|
+
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
8934
|
+
</button>
|
|
8935
|
+
</mat-form-field>
|
|
8936
|
+
</div>
|
|
8937
|
+
<div class="form-row">
|
|
8938
|
+
<mat-checkbox [(ngModel)]="editEnable">Ativar cascata nativa</mat-checkbox>
|
|
8939
|
+
<mat-checkbox [(ngModel)]="editReset">Resetar ao mudar</mat-checkbox>
|
|
8940
|
+
</div>
|
|
8941
|
+
</div>
|
|
8942
|
+
</mat-expansion-panel>
|
|
8943
|
+
<!-- Execução -->
|
|
8944
|
+
<mat-expansion-panel class="stack-panel">
|
|
8945
|
+
<mat-expansion-panel-header>
|
|
8946
|
+
<mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'schedule'"></mat-icon> Execução</mat-panel-title>
|
|
8947
|
+
</mat-expansion-panel-header>
|
|
8948
|
+
<div class="form-row">
|
|
8949
|
+
<mat-form-field appearance="outline">
|
|
8950
|
+
<mat-label>Carregar ao mudar</mat-label>
|
|
8951
|
+
<mat-select [(ngModel)]="editLoadOn">
|
|
8952
|
+
<mat-option value="respectLoadOn">Respeitar loadOn</mat-option>
|
|
8953
|
+
<mat-option value="immediate">Imediato</mat-option>
|
|
8954
|
+
<mat-option value="manual">Manual</mat-option>
|
|
8955
|
+
</mat-select>
|
|
8956
|
+
</mat-form-field>
|
|
8957
|
+
<mat-form-field appearance="outline">
|
|
8958
|
+
<mat-label>Merge de filtros</mat-label>
|
|
8959
|
+
<mat-select [(ngModel)]="editMerge">
|
|
8960
|
+
<mat-option value="merge">merge (padrão)</mat-option>
|
|
8961
|
+
<mat-option value="replace">replace</mat-option>
|
|
8962
|
+
</mat-select>
|
|
8963
|
+
</mat-form-field>
|
|
8964
|
+
<mat-form-field appearance="outline">
|
|
8965
|
+
<mat-label>Atraso (ms)</mat-label>
|
|
8966
|
+
<input matInput type="number" [(ngModel)]="editDebounce">
|
|
8967
|
+
</mat-form-field>
|
|
8968
|
+
</div>
|
|
8969
|
+
</mat-expansion-panel>
|
|
8970
|
+
<!-- Extração de valor -->
|
|
8971
|
+
<mat-expansion-panel class="stack-panel">
|
|
8972
|
+
<mat-expansion-panel-header>
|
|
8973
|
+
<mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'call_split'"></mat-icon> Extração de valor</mat-panel-title>
|
|
8974
|
+
</mat-expansion-panel-header>
|
|
7260
8975
|
<div class="form-row">
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
<mat-option *ngFor="let f of availableDependencyFields()" [value]="f.name">
|
|
7265
|
-
{{ labelFor(f.name) }} ({{ f.name }})
|
|
7266
|
-
</mat-option>
|
|
7267
|
-
</mat-select>
|
|
8976
|
+
<mat-form-field appearance="outline" class="full">
|
|
8977
|
+
<mat-label>ValuePath padrão</mat-label>
|
|
8978
|
+
<input matInput [(ngModel)]="editValuePath" placeholder="id">
|
|
7268
8979
|
<button
|
|
7269
8980
|
mat-icon-button
|
|
7270
8981
|
matSuffix
|
|
7271
8982
|
class="help-icon-button"
|
|
7272
8983
|
type="button"
|
|
7273
|
-
[matTooltip]="'
|
|
8984
|
+
[matTooltip]="'Preenchido automaticamente a partir de optionValueKey() ou \\'id\\''"
|
|
7274
8985
|
matTooltipPosition="above"
|
|
7275
|
-
|
|
8986
|
+
>
|
|
7276
8987
|
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
7277
8988
|
</button>
|
|
7278
|
-
|
|
7279
|
-
</div>
|
|
7280
|
-
<div class="form-row">
|
|
7281
|
-
<mat-checkbox [(ngModel)]="editEnable">Ativar cascata nativa</mat-checkbox>
|
|
7282
|
-
<mat-checkbox [(ngModel)]="editReset">Resetar ao mudar</mat-checkbox>
|
|
8989
|
+
</mat-form-field>
|
|
7283
8990
|
</div>
|
|
7284
|
-
</
|
|
7285
|
-
|
|
7286
|
-
|
|
7287
|
-
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
|
|
7295
|
-
|
|
7296
|
-
|
|
7297
|
-
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
|
|
7301
|
-
|
|
7302
|
-
|
|
7303
|
-
|
|
7304
|
-
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
|
|
7311
|
-
|
|
7312
|
-
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7349
|
-
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
>
|
|
7369
|
-
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
7370
|
-
</button>
|
|
7371
|
-
</mat-form-field>
|
|
7372
|
-
</td>
|
|
7373
|
-
<td>
|
|
7374
|
-
<mat-form-field appearance="outline" class="full">
|
|
7375
|
-
<mat-label>ValuePath</mat-label>
|
|
7376
|
-
<input matInput [(ngModel)]="row.valuePath" [matAutocomplete]="autoValue" [placeholder]="editValuePath||'id'">
|
|
7377
|
-
<mat-autocomplete #autoValue="matAutocomplete">
|
|
7378
|
-
<mat-option *ngFor="let opt of getValuePathSuggestions(row.dep)" [value]="opt">{{ opt }}</mat-option>
|
|
7379
|
-
</mat-autocomplete>
|
|
7380
|
-
<button
|
|
7381
|
-
mat-icon-button
|
|
7382
|
-
matSuffix
|
|
7383
|
-
class="help-icon-button"
|
|
7384
|
-
type="button"
|
|
7385
|
-
[matTooltip]="'Opcional'"
|
|
7386
|
-
matTooltipPosition="above"
|
|
7387
|
-
>
|
|
7388
|
-
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
7389
|
-
</button>
|
|
7390
|
-
</mat-form-field>
|
|
7391
|
-
</td>
|
|
7392
|
-
</tr>
|
|
7393
|
-
</tbody>
|
|
7394
|
-
</table>
|
|
7395
|
-
<ng-template #noMap>
|
|
7396
|
-
<div class="rules-empty">Defina dependentes para sugerir o mapeamento.</div>
|
|
7397
|
-
</ng-template>
|
|
7398
|
-
</mat-expansion-panel>
|
|
7399
|
-
|
|
7400
|
-
<!-- Preview -->
|
|
7401
|
-
<mat-expansion-panel class="stack-panel">
|
|
7402
|
-
<mat-expansion-panel-header>
|
|
7403
|
-
<mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'visibility'"></mat-icon> Preview</mat-panel-title>
|
|
7404
|
-
</mat-expansion-panel-header>
|
|
7405
|
-
<pre class="preview-box">{{ buildPreview() }}</pre>
|
|
7406
|
-
<div style="display:flex; justify-content:flex-end; padding-top:6px">
|
|
7407
|
-
<button mat-stroked-button (click)="copyPreview()">Copiar</button>
|
|
7408
|
-
</div>
|
|
7409
|
-
</mat-expansion-panel>
|
|
7410
|
-
</div>
|
|
7411
|
-
|
|
7412
|
-
<div style="display:flex; gap:8px; justify-content:flex-end; padding:8px 12px;">
|
|
7413
|
-
<button mat-stroked-button (click)="cancelEdit()">Cancelar</button>
|
|
7414
|
-
<button mat-flat-button color="primary" (click)="saveEdit(sel)">Salvar</button>
|
|
8991
|
+
</mat-expansion-panel>
|
|
8992
|
+
<!-- Mapeamento de filtros (tabela) -->
|
|
8993
|
+
<mat-expansion-panel class="stack-panel">
|
|
8994
|
+
<mat-expansion-panel-header>
|
|
8995
|
+
<mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'filter_alt'"></mat-icon> Mapeamento de filtros</mat-panel-title>
|
|
8996
|
+
</mat-expansion-panel-header>
|
|
8997
|
+
@if (editMapRows.length) {
|
|
8998
|
+
<table class="map-table">
|
|
8999
|
+
<thead>
|
|
9000
|
+
<tr>
|
|
9001
|
+
<th>Dependente</th>
|
|
9002
|
+
<th>Chave (dot‑path)</th>
|
|
9003
|
+
<th>ValuePath (opcional)</th>
|
|
9004
|
+
</tr>
|
|
9005
|
+
</thead>
|
|
9006
|
+
<tbody>
|
|
9007
|
+
@for (row of editMapRows; track row; let i = $index) {
|
|
9008
|
+
<tr>
|
|
9009
|
+
<td>{{ row.dep }}</td>
|
|
9010
|
+
<td>
|
|
9011
|
+
<mat-form-field appearance="outline" class="full">
|
|
9012
|
+
<mat-label>Chave</mat-label>
|
|
9013
|
+
<input matInput [(ngModel)]="row.key" [matAutocomplete]="autoKey" [placeholder]="row.dep">
|
|
9014
|
+
<mat-autocomplete #autoKey="matAutocomplete">
|
|
9015
|
+
@for (opt of getKeySuggestions(row.dep); track opt) {
|
|
9016
|
+
<mat-option [value]="opt">{{ opt }}</mat-option>
|
|
9017
|
+
}
|
|
9018
|
+
</mat-autocomplete>
|
|
9019
|
+
<button
|
|
9020
|
+
mat-icon-button
|
|
9021
|
+
matSuffix
|
|
9022
|
+
class="help-icon-button"
|
|
9023
|
+
type="button"
|
|
9024
|
+
[matTooltip]="'Use dot-notation (e.g. ' + row.dep + '.id) for nested objects'"
|
|
9025
|
+
matTooltipPosition="above"
|
|
9026
|
+
>
|
|
9027
|
+
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
9028
|
+
</button>
|
|
9029
|
+
</mat-form-field>
|
|
9030
|
+
</td>
|
|
9031
|
+
<td>
|
|
9032
|
+
<mat-form-field appearance="outline" class="full">
|
|
9033
|
+
<mat-label>ValuePath</mat-label>
|
|
9034
|
+
<input matInput [(ngModel)]="row.valuePath" [matAutocomplete]="autoValue" [placeholder]="editValuePath||'id'">
|
|
9035
|
+
<mat-autocomplete #autoValue="matAutocomplete">
|
|
9036
|
+
@for (opt of getValuePathSuggestions(row.dep); track opt) {
|
|
9037
|
+
<mat-option [value]="opt">{{ opt }}</mat-option>
|
|
9038
|
+
}
|
|
9039
|
+
</mat-autocomplete>
|
|
9040
|
+
<button
|
|
9041
|
+
mat-icon-button
|
|
9042
|
+
matSuffix
|
|
9043
|
+
class="help-icon-button"
|
|
9044
|
+
type="button"
|
|
9045
|
+
[matTooltip]="'Opcional'"
|
|
9046
|
+
matTooltipPosition="above"
|
|
9047
|
+
>
|
|
9048
|
+
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
9049
|
+
</button>
|
|
9050
|
+
</mat-form-field>
|
|
9051
|
+
</td>
|
|
9052
|
+
</tr>
|
|
9053
|
+
}
|
|
9054
|
+
</tbody>
|
|
9055
|
+
</table>
|
|
9056
|
+
} @else {
|
|
9057
|
+
<div class="rules-empty">Defina dependentes para sugerir o mapeamento.</div>
|
|
9058
|
+
}
|
|
9059
|
+
</mat-expansion-panel>
|
|
9060
|
+
<!-- Preview -->
|
|
9061
|
+
<mat-expansion-panel class="stack-panel">
|
|
9062
|
+
<mat-expansion-panel-header>
|
|
9063
|
+
<mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'visibility'"></mat-icon> Preview</mat-panel-title>
|
|
9064
|
+
</mat-expansion-panel-header>
|
|
9065
|
+
<pre class="preview-box">{{ buildPreview() }}</pre>
|
|
9066
|
+
<div style="display:flex; justify-content:flex-end; padding-top:6px">
|
|
9067
|
+
<button mat-stroked-button (click)="copyPreview()">Copiar</button>
|
|
9068
|
+
</div>
|
|
9069
|
+
</mat-expansion-panel>
|
|
9070
|
+
</div>
|
|
9071
|
+
<div style="display:flex; gap:8px; justify-content:flex-end; padding:8px 12px;">
|
|
9072
|
+
<button mat-stroked-button (click)="cancelEdit()">Cancelar</button>
|
|
9073
|
+
<button mat-flat-button color="primary" (click)="saveEdit(sel)">Salvar</button>
|
|
9074
|
+
</div>
|
|
7415
9075
|
</div>
|
|
7416
9076
|
</div>
|
|
7417
|
-
|
|
9077
|
+
}
|
|
7418
9078
|
</div>
|
|
7419
|
-
`, isInline: true, styles: [":host{display:block}.cm-root{display:grid;grid-template-columns:320px 1fr;gap:16px;min-height:420px}.cm-left{border-right:1px solid var(--md-sys-color-outline-variant);padding-right:8px}.cm-search{width:100%;margin-bottom:8px}.cm-right{padding-left:8px}.field-item{display:flex;align-items:center;justify-content:space-between;padding:8px 6px;cursor:pointer;border-radius:10px;transition:background .12s ease,border .12s ease;border:1px solid transparent}.field-item:hover{background:var(--md-sys-color-surface-container-high)}.field-item.selected{background:var(--md-sys-color-surface-container-highest);border-color:var(--md-sys-color-outline-variant)}.rule-badges{display:inline-flex;gap:6px}.rule-badge{font-size:11px;padding:2px 8px;border-radius:999px;font-weight:600}.b-active{background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.b-manual{background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface-variant)}.b-immediate{background:var(--md-sys-color-secondary-container);color:var(--md-sys-color-on-secondary-container)}.b-conflict{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container)}.cm-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.rules-empty{opacity:.8;font-style:italic;padding:8px 0;color:var(--md-sys-color-on-surface-variant)}.rule-row{display:grid;grid-template-columns:1fr auto;align-items:center;padding:10px 0;border-bottom:1px dashed var(--md-sys-color-outline-variant)}.rule-actions button{margin-left:4px}.banner{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container);padding:10px 12px;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;margin-bottom:10px;display:flex;gap:8px;align-items:center}.import-panel{background:var(--md-sys-color-surface-container-high);border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;padding:10px;margin-bottom:10px}.full{width:100%}.form-row{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.form-grid{display:grid;gap:12px;padding:8px 0}.map-table{width:100%;border-collapse:collapse}.map-table th,.map-table td{padding:8px;border-bottom:1px dashed var(--md-sys-color-outline-variant)}.kbd{font-family:monospace;background:var(--md-sys-color-surface-container-highest);padding:0 6px;border-radius:4px;color:var(--md-sys-color-on-surface-variant)}.panel-surface{background:var(--md-sys-color-surface-container-high);border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;box-shadow:var(--md-sys-elevation-level1, none);margin-bottom:12px}.panel-stack{display:grid;gap:12px;margin:12px 0}.stack-panel{background:var(--md-sys-color-surface-container-high);border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;box-shadow:var(--md-sys-elevation-level1, none)}.stack-panel .mat-expansion-panel-content{padding:0 16px 12px}.panel-header{display:flex;align-items:center;gap:8px}.preview-box{background:var(--md-sys-color-surface-container-low);border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;padding:8px;color:var(--md-sys-color-on-surface)}.help-icon-button{--mdc-icon-button-state-layer-size: 28px;--mdc-icon-button-icon-size: 18px;width:28px;height:28px;padding:0;display:inline-flex;align-items:center;justify-content:center;margin-right:-4px}.help-icon-button mat-icon{font-size:18px;width:18px;height:18px}.mat-mdc-form-field-icon-suffix{align-self:center}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i5$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i5$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i7.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i7.MatLabel, selector: "mat-label" }, { kind: "directive", type: i7.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "ngmodule", type: MatListModule }, { kind: "ngmodule", type: MatDividerModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i8$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i8$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i9.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i5.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i5.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i5.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i11.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i11.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i11.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i12.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "directive", type: i12.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i13.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i13.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i13.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }] });
|
|
9079
|
+
`, isInline: true, styles: [":host{display:block}.cm-root{display:grid;grid-template-columns:320px 1fr;gap:16px;min-height:420px}.cm-left{border-right:1px solid var(--md-sys-color-outline-variant);padding-right:8px}.cm-search{width:100%;margin-bottom:8px}.cm-right{padding-left:8px}.field-item{display:flex;align-items:center;justify-content:space-between;padding:8px 6px;cursor:pointer;border-radius:10px;transition:background .12s ease,border .12s ease;border:1px solid transparent}.field-item:hover{background:var(--md-sys-color-surface-container-high)}.field-item.selected{background:var(--md-sys-color-surface-container-highest);border-color:var(--md-sys-color-outline-variant)}.rule-badges{display:inline-flex;gap:6px}.rule-badge{font-size:11px;padding:2px 8px;border-radius:999px;font-weight:600}.b-active{background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.b-manual{background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface-variant)}.b-immediate{background:var(--md-sys-color-secondary-container);color:var(--md-sys-color-on-secondary-container)}.b-conflict{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container)}.cm-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.rules-empty{opacity:.8;font-style:italic;padding:8px 0;color:var(--md-sys-color-on-surface-variant)}.rule-row{display:grid;grid-template-columns:1fr auto;align-items:center;padding:10px 0;border-bottom:1px dashed var(--md-sys-color-outline-variant)}.rule-actions button{margin-left:4px}.banner{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container);padding:10px 12px;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;margin-bottom:10px;display:flex;gap:8px;align-items:center}.import-panel{background:var(--md-sys-color-surface-container-high);border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;padding:10px;margin-bottom:10px}.full{width:100%}.form-row{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.form-grid{display:grid;gap:12px;padding:8px 0}.map-table{width:100%;border-collapse:collapse}.map-table th,.map-table td{padding:8px;border-bottom:1px dashed var(--md-sys-color-outline-variant)}.kbd{font-family:monospace;background:var(--md-sys-color-surface-container-highest);padding:0 6px;border-radius:4px;color:var(--md-sys-color-on-surface-variant)}.panel-surface{background:var(--md-sys-color-surface-container-high);border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;box-shadow:var(--md-sys-elevation-level1, none);margin-bottom:12px}.panel-stack{display:grid;gap:12px;margin:12px 0}.stack-panel{background:var(--md-sys-color-surface-container-high);border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;box-shadow:var(--md-sys-elevation-level1, none)}.stack-panel .mat-expansion-panel-content{padding:0 16px 12px}.panel-header{display:flex;align-items:center;gap:8px}.preview-box{background:var(--md-sys-color-surface-container-low);border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;padding:8px;color:var(--md-sys-color-on-surface)}.help-icon-button{--mdc-icon-button-state-layer-size: 28px;--mdc-icon-button-icon-size: 18px;width:28px;height:28px;padding:0;display:inline-flex;align-items:center;justify-content:center;margin-right:-4px}.help-icon-button mat-icon{font-size:18px;width:18px;height:18px}.mat-mdc-form-field-icon-suffix{align-self:center}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i4.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i6$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i6$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i6$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i6$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "ngmodule", type: MatListModule }, { kind: "ngmodule", type: MatDividerModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i7.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i7.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i8$1.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i5.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i5.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i5.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i10.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i10.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i10.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i11.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "directive", type: i11.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i12.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i12.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i12.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }] });
|
|
7420
9080
|
}
|
|
7421
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
9081
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CascadeManagerTabComponent, decorators: [{
|
|
7422
9082
|
type: Component,
|
|
7423
9083
|
args: [{ selector: 'praxis-cascade-manager-tab', standalone: true, imports: [
|
|
7424
|
-
CommonModule,
|
|
7425
9084
|
ReactiveFormsModule,
|
|
7426
9085
|
FormsModule,
|
|
7427
9086
|
MatIconModule,
|
|
@@ -7437,7 +9096,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
7437
9096
|
MatMenuModule,
|
|
7438
9097
|
MatAutocompleteModule,
|
|
7439
9098
|
ScrollingModule,
|
|
7440
|
-
PraxisIconDirective
|
|
9099
|
+
PraxisIconDirective
|
|
7441
9100
|
], template: `
|
|
7442
9101
|
<div class="cm-root">
|
|
7443
9102
|
<!-- Left: Fields list -->
|
|
@@ -7446,271 +9105,297 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
7446
9105
|
<mat-label>Buscar campos</mat-label>
|
|
7447
9106
|
<input matInput [ngModel]="searchTerm" (ngModelChange)="onSearchChange($event)">
|
|
7448
9107
|
</mat-form-field>
|
|
7449
|
-
|
|
9108
|
+
@if (fields.length <= 50) {
|
|
7450
9109
|
<div>
|
|
7451
|
-
|
|
7452
|
-
<div>
|
|
7453
|
-
<div>
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
|
|
7458
|
-
|
|
7459
|
-
|
|
9110
|
+
@for (f of filteredFields(); track f) {
|
|
9111
|
+
<div (click)="selectField(f.name)" class="field-item" [class.selected]="f.name===selectedField()" tabindex="0" (keydown)="onFieldKeydown($event, f.name)">
|
|
9112
|
+
<div>
|
|
9113
|
+
<div>{{ labelFor(f.name) }}</div>
|
|
9114
|
+
<small style="opacity:.7">{{ f.name }}</small>
|
|
9115
|
+
</div>
|
|
9116
|
+
@if (hasRule(f.name)) {
|
|
9117
|
+
<div class="rule-badges">
|
|
9118
|
+
@if (isCascadeEnabled(f.name)) {
|
|
9119
|
+
<span class="rule-badge b-active">Ativo</span>
|
|
9120
|
+
}
|
|
9121
|
+
@if (loadOnChange(f.name)==='manual') {
|
|
9122
|
+
<span class="rule-badge b-manual">Manual</span>
|
|
9123
|
+
}
|
|
9124
|
+
@if (loadOnChange(f.name)==='immediate') {
|
|
9125
|
+
<span class="rule-badge b-immediate">Immediate</span>
|
|
9126
|
+
}
|
|
9127
|
+
</div>
|
|
9128
|
+
}
|
|
7460
9129
|
</div>
|
|
7461
|
-
|
|
9130
|
+
}
|
|
7462
9131
|
</div>
|
|
7463
|
-
|
|
7464
|
-
<ng-template #vscroll>
|
|
9132
|
+
} @else {
|
|
7465
9133
|
<cdk-virtual-scroll-viewport itemSize="44" style="height: calc(100vh - 240px)">
|
|
7466
9134
|
<div *cdkVirtualFor="let f of filteredFields()" (click)="selectField(f.name)" class="field-item" [class.selected]="f.name===selectedField()" tabindex="0" (keydown)="onFieldKeydown($event, f.name)">
|
|
7467
9135
|
<div>
|
|
7468
9136
|
<div>{{ labelFor(f.name) }}</div>
|
|
7469
9137
|
<small style="opacity:.7">{{ f.name }}</small>
|
|
7470
9138
|
</div>
|
|
7471
|
-
|
|
7472
|
-
<
|
|
7473
|
-
|
|
7474
|
-
|
|
7475
|
-
|
|
9139
|
+
@if (hasRule(f.name)) {
|
|
9140
|
+
<div class="rule-badges">
|
|
9141
|
+
@if (isCascadeEnabled(f.name)) {
|
|
9142
|
+
<span class="rule-badge b-active">Ativo</span>
|
|
9143
|
+
}
|
|
9144
|
+
@if (loadOnChange(f.name)==='manual') {
|
|
9145
|
+
<span class="rule-badge b-manual">Manual</span>
|
|
9146
|
+
}
|
|
9147
|
+
@if (loadOnChange(f.name)==='immediate') {
|
|
9148
|
+
<span class="rule-badge b-immediate">Immediate</span>
|
|
9149
|
+
}
|
|
9150
|
+
</div>
|
|
9151
|
+
}
|
|
7476
9152
|
</div>
|
|
7477
9153
|
</cdk-virtual-scroll-viewport>
|
|
7478
|
-
|
|
9154
|
+
}
|
|
7479
9155
|
</div>
|
|
7480
|
-
|
|
9156
|
+
|
|
7481
9157
|
<!-- Right: Rules for selected field -->
|
|
7482
|
-
|
|
7483
|
-
<div class="cm-
|
|
7484
|
-
<div>
|
|
7485
|
-
<h3 style="margin:0">{{ labelFor(sel) }}</h3>
|
|
7486
|
-
<div style="opacity:.7">{{ sel }}</div>
|
|
7487
|
-
</div>
|
|
7488
|
-
<div>
|
|
7489
|
-
<button mat-stroked-button color="primary" (click)="exportRules()" style="margin-right:8px">Exportar</button>
|
|
7490
|
-
<button mat-stroked-button (click)="toggleImport()" style="margin-right:8px">Importar</button>
|
|
7491
|
-
<button mat-flat-button color="primary" [matMenuTriggerFor]="addMenu" [disabled]="editing()">Adicionar</button>
|
|
7492
|
-
<mat-menu #addMenu="matMenu">
|
|
7493
|
-
<button mat-menu-item (click)="startEdit(sel)"><mat-icon [praxisIcon]="'add'"></mat-icon><span>Em branco</span></button>
|
|
7494
|
-
<button mat-menu-item (click)="startTemplate(sel, 'rota-destino')" [disabled]="!hasField('rotaId') || !hasField('destinoReqId')"><mat-icon [praxisIcon]="'bolt'"></mat-icon><span>Rota → Destino (id)</span></button>
|
|
7495
|
-
<button mat-menu-item (click)="startTemplate(sel, 'rota-destino-propriedade')" [disabled]="!hasField('rotaId') || !hasField('destinoReqId')"><mat-icon [praxisIcon]="'bolt'"></mat-icon><span>Rota + Destino → Propriedade (ids)</span></button>
|
|
7496
|
-
</mat-menu>
|
|
7497
|
-
</div>
|
|
7498
|
-
</div>
|
|
7499
|
-
|
|
7500
|
-
<!-- Conflict banner (connections) -->
|
|
7501
|
-
<div class="banner" *ngIf="hasConflict(sel)">
|
|
7502
|
-
<mat-icon [praxisIcon]="'link_off'"></mat-icon>
|
|
7503
|
-
<div>
|
|
7504
|
-
Connections atualizam este campo (filterCriteria). Considere desativar a cascata nativa ou usar ‘manual’.
|
|
7505
|
-
</div>
|
|
7506
|
-
<span class="spacer"></span>
|
|
7507
|
-
<button mat-stroked-button color="primary" (click)="quickSet(sel,'disable')">Desativar</button>
|
|
7508
|
-
<button mat-stroked-button color="accent" (click)="quickSet(sel,'manual')">Definir manual</button>
|
|
7509
|
-
</div>
|
|
7510
|
-
|
|
7511
|
-
<div *ngIf="importMode" class="import-panel">
|
|
7512
|
-
<div style="display:flex; gap:8px; align-items:center; margin-bottom:8px"><mat-icon [praxisIcon]="'upload'"></mat-icon><strong>Importar regras (JSON)</strong></div>
|
|
7513
|
-
<textarea matInput rows="6" class="full" [(ngModel)]="importJson" placeholder='{ "campo": { "dependencyFields": ["dep"], ... } }'></textarea>
|
|
7514
|
-
<div style="display:flex; gap:8px; justify-content:flex-end; padding-top:8px">
|
|
7515
|
-
<button mat-stroked-button (click)="toggleImport()">Cancelar</button>
|
|
7516
|
-
<button mat-flat-button color="primary" (click)="applyImport()">Aplicar</button>
|
|
7517
|
-
</div>
|
|
7518
|
-
</div>
|
|
7519
|
-
|
|
7520
|
-
<div *ngIf="!ruleFor(sel)">
|
|
7521
|
-
<div class="rules-empty">Nenhuma cascata configurada. Clique em “Adicionar” para montar a dependência.</div>
|
|
7522
|
-
</div>
|
|
7523
|
-
<div *ngIf="ruleFor(sel) as rule">
|
|
7524
|
-
<div class="rule-row">
|
|
9158
|
+
@if (selectedField(); as sel) {
|
|
9159
|
+
<div class="cm-right">
|
|
9160
|
+
<div class="cm-header">
|
|
7525
9161
|
<div>
|
|
7526
|
-
<
|
|
7527
|
-
<div style="opacity:.7">
|
|
9162
|
+
<h3 style="margin:0">{{ labelFor(sel) }}</h3>
|
|
9163
|
+
<div style="opacity:.7">{{ sel }}</div>
|
|
7528
9164
|
</div>
|
|
7529
|
-
<div
|
|
7530
|
-
<button mat-
|
|
7531
|
-
<button mat-
|
|
7532
|
-
<button mat-
|
|
9165
|
+
<div>
|
|
9166
|
+
<button mat-stroked-button color="primary" (click)="exportRules()" style="margin-right:8px">Exportar</button>
|
|
9167
|
+
<button mat-stroked-button (click)="toggleImport()" style="margin-right:8px">Importar</button>
|
|
9168
|
+
<button mat-flat-button color="primary" [matMenuTriggerFor]="addMenu" [disabled]="editing()">Adicionar</button>
|
|
9169
|
+
<mat-menu #addMenu="matMenu">
|
|
9170
|
+
<button mat-menu-item (click)="startEdit(sel)"><mat-icon [praxisIcon]="'add'"></mat-icon><span>Em branco</span></button>
|
|
9171
|
+
<button mat-menu-item (click)="startTemplate(sel, 'rota-destino')" [disabled]="!hasField('rotaId') || !hasField('destinoReqId')"><mat-icon [praxisIcon]="'bolt'"></mat-icon><span>Rota → Destino (id)</span></button>
|
|
9172
|
+
<button mat-menu-item (click)="startTemplate(sel, 'rota-destino-propriedade')" [disabled]="!hasField('rotaId') || !hasField('destinoReqId')"><mat-icon [praxisIcon]="'bolt'"></mat-icon><span>Rota + Destino → Propriedade (ids)</span></button>
|
|
9173
|
+
</mat-menu>
|
|
7533
9174
|
</div>
|
|
7534
9175
|
</div>
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7539
|
-
|
|
7540
|
-
|
|
9176
|
+
<!-- Conflict banner (connections) -->
|
|
9177
|
+
@if (hasConflict(sel)) {
|
|
9178
|
+
<div class="banner">
|
|
9179
|
+
<mat-icon [praxisIcon]="'link_off'"></mat-icon>
|
|
9180
|
+
<div>
|
|
9181
|
+
Connections atualizam este campo (filterCriteria). Considere desativar a cascata nativa ou usar ‘manual’.
|
|
9182
|
+
</div>
|
|
9183
|
+
<span class="spacer"></span>
|
|
9184
|
+
<button mat-stroked-button color="primary" (click)="quickSet(sel,'disable')">Desativar</button>
|
|
9185
|
+
<button mat-stroked-button color="accent" (click)="quickSet(sel,'manual')">Definir manual</button>
|
|
9186
|
+
</div>
|
|
9187
|
+
}
|
|
9188
|
+
@if (importMode) {
|
|
9189
|
+
<div class="import-panel">
|
|
9190
|
+
<div style="display:flex; gap:8px; align-items:center; margin-bottom:8px"><mat-icon [praxisIcon]="'upload'"></mat-icon><strong>Importar regras (JSON)</strong></div>
|
|
9191
|
+
<textarea matInput rows="6" class="full" [(ngModel)]="importJson" placeholder='{ "campo": { "dependencyFields": ["dep"], ... } }'></textarea>
|
|
9192
|
+
<div style="display:flex; gap:8px; justify-content:flex-end; padding-top:8px">
|
|
9193
|
+
<button mat-stroked-button (click)="toggleImport()">Cancelar</button>
|
|
9194
|
+
<button mat-flat-button color="primary" (click)="applyImport()">Aplicar</button>
|
|
9195
|
+
</div>
|
|
9196
|
+
</div>
|
|
9197
|
+
}
|
|
9198
|
+
@if (!ruleFor(sel)) {
|
|
7541
9199
|
<div>
|
|
7542
|
-
<div>
|
|
7543
|
-
<small style="color: var(--md-sys-color-on-surface-variant);">Defina dependentes, quando recarregar e como mapear filtros</small>
|
|
9200
|
+
<div class="rules-empty">Nenhuma cascata configurada. Clique em “Adicionar” para montar a dependência.</div>
|
|
7544
9201
|
</div>
|
|
7545
|
-
|
|
7546
|
-
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
9202
|
+
}
|
|
9203
|
+
@if (ruleFor(sel); as rule) {
|
|
9204
|
+
<div>
|
|
9205
|
+
<div class="rule-row">
|
|
9206
|
+
<div>
|
|
9207
|
+
<div><strong>Dependentes:</strong> {{ rule.dependencyFields.join(', ') || '—' }}</div>
|
|
9208
|
+
<div style="opacity:.7">Load on change: {{ rule.dependencyLoadOnChange || 'respectLoadOn' }} | Merge: {{ rule.dependencyMergeStrategy || 'merge' }}</div>
|
|
9209
|
+
</div>
|
|
9210
|
+
<div class="rule-actions">
|
|
9211
|
+
<button mat-icon-button matTooltip="Editar" (click)="startEdit(sel)"><mat-icon [praxisIcon]="'edit'"></mat-icon></button>
|
|
9212
|
+
<button mat-icon-button matTooltip="Duplicar" disabled><mat-icon [praxisIcon]="'content_copy'"></mat-icon></button>
|
|
9213
|
+
<button mat-icon-button matTooltip="Remover" (click)="remove(sel)"><mat-icon [praxisIcon]="'delete'"></mat-icon></button>
|
|
9214
|
+
</div>
|
|
9215
|
+
</div>
|
|
9216
|
+
</div>
|
|
9217
|
+
}
|
|
9218
|
+
<!-- Editor com seções (sem acordeão duplo) -->
|
|
9219
|
+
<div class="panel-surface">
|
|
9220
|
+
<div class="panel-header" style="padding: 12px 16px; border-bottom: 1px solid var(--md-sys-color-outline-variant);">
|
|
9221
|
+
<mat-icon [praxisIcon]="'rule'"></mat-icon>
|
|
9222
|
+
<div>
|
|
9223
|
+
<div>Cascata do campo</div>
|
|
9224
|
+
<small style="color: var(--md-sys-color-on-surface-variant);">Defina dependentes, quando recarregar e como mapear filtros</small>
|
|
9225
|
+
</div>
|
|
9226
|
+
</div>
|
|
9227
|
+
<div class="panel-stack">
|
|
9228
|
+
<!-- Básico -->
|
|
9229
|
+
<mat-expansion-panel [expanded]="true" class="stack-panel">
|
|
9230
|
+
<mat-expansion-panel-header>
|
|
9231
|
+
<mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'tune'"></mat-icon> Básico</mat-panel-title>
|
|
9232
|
+
</mat-expansion-panel-header>
|
|
9233
|
+
<div class="form-grid">
|
|
9234
|
+
<div class="form-row">
|
|
9235
|
+
<mat-form-field appearance="outline" class="full">
|
|
9236
|
+
<mat-label>Campos dependentes</mat-label>
|
|
9237
|
+
<mat-select multiple [(ngModel)]="editDepList" (ngModelChange)="onDepListChange()">
|
|
9238
|
+
@for (f of availableDependencyFields(); track f) {
|
|
9239
|
+
<mat-option [value]="f.name">
|
|
9240
|
+
{{ labelFor(f.name) }} ({{ f.name }})
|
|
9241
|
+
</mat-option>
|
|
9242
|
+
}
|
|
9243
|
+
</mat-select>
|
|
9244
|
+
<button
|
|
9245
|
+
mat-icon-button
|
|
9246
|
+
matSuffix
|
|
9247
|
+
class="help-icon-button"
|
|
9248
|
+
type="button"
|
|
9249
|
+
[matTooltip]="'Selecione os campos que controlam este valor'"
|
|
9250
|
+
matTooltipPosition="above"
|
|
9251
|
+
>
|
|
9252
|
+
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
9253
|
+
</button>
|
|
9254
|
+
</mat-form-field>
|
|
9255
|
+
</div>
|
|
9256
|
+
<div class="form-row">
|
|
9257
|
+
<mat-checkbox [(ngModel)]="editEnable">Ativar cascata nativa</mat-checkbox>
|
|
9258
|
+
<mat-checkbox [(ngModel)]="editReset">Resetar ao mudar</mat-checkbox>
|
|
9259
|
+
</div>
|
|
9260
|
+
</div>
|
|
9261
|
+
</mat-expansion-panel>
|
|
9262
|
+
<!-- Execução -->
|
|
9263
|
+
<mat-expansion-panel class="stack-panel">
|
|
9264
|
+
<mat-expansion-panel-header>
|
|
9265
|
+
<mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'schedule'"></mat-icon> Execução</mat-panel-title>
|
|
9266
|
+
</mat-expansion-panel-header>
|
|
9267
|
+
<div class="form-row">
|
|
9268
|
+
<mat-form-field appearance="outline">
|
|
9269
|
+
<mat-label>Carregar ao mudar</mat-label>
|
|
9270
|
+
<mat-select [(ngModel)]="editLoadOn">
|
|
9271
|
+
<mat-option value="respectLoadOn">Respeitar loadOn</mat-option>
|
|
9272
|
+
<mat-option value="immediate">Imediato</mat-option>
|
|
9273
|
+
<mat-option value="manual">Manual</mat-option>
|
|
9274
|
+
</mat-select>
|
|
9275
|
+
</mat-form-field>
|
|
9276
|
+
<mat-form-field appearance="outline">
|
|
9277
|
+
<mat-label>Merge de filtros</mat-label>
|
|
9278
|
+
<mat-select [(ngModel)]="editMerge">
|
|
9279
|
+
<mat-option value="merge">merge (padrão)</mat-option>
|
|
9280
|
+
<mat-option value="replace">replace</mat-option>
|
|
9281
|
+
</mat-select>
|
|
9282
|
+
</mat-form-field>
|
|
9283
|
+
<mat-form-field appearance="outline">
|
|
9284
|
+
<mat-label>Atraso (ms)</mat-label>
|
|
9285
|
+
<input matInput type="number" [(ngModel)]="editDebounce">
|
|
9286
|
+
</mat-form-field>
|
|
9287
|
+
</div>
|
|
9288
|
+
</mat-expansion-panel>
|
|
9289
|
+
<!-- Extração de valor -->
|
|
9290
|
+
<mat-expansion-panel class="stack-panel">
|
|
9291
|
+
<mat-expansion-panel-header>
|
|
9292
|
+
<mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'call_split'"></mat-icon> Extração de valor</mat-panel-title>
|
|
9293
|
+
</mat-expansion-panel-header>
|
|
7554
9294
|
<div class="form-row">
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
<mat-option *ngFor="let f of availableDependencyFields()" [value]="f.name">
|
|
7559
|
-
{{ labelFor(f.name) }} ({{ f.name }})
|
|
7560
|
-
</mat-option>
|
|
7561
|
-
</mat-select>
|
|
9295
|
+
<mat-form-field appearance="outline" class="full">
|
|
9296
|
+
<mat-label>ValuePath padrão</mat-label>
|
|
9297
|
+
<input matInput [(ngModel)]="editValuePath" placeholder="id">
|
|
7562
9298
|
<button
|
|
7563
9299
|
mat-icon-button
|
|
7564
9300
|
matSuffix
|
|
7565
9301
|
class="help-icon-button"
|
|
7566
9302
|
type="button"
|
|
7567
|
-
[matTooltip]="'
|
|
9303
|
+
[matTooltip]="'Preenchido automaticamente a partir de optionValueKey() ou \\'id\\''"
|
|
7568
9304
|
matTooltipPosition="above"
|
|
7569
|
-
|
|
9305
|
+
>
|
|
7570
9306
|
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
7571
9307
|
</button>
|
|
7572
|
-
|
|
7573
|
-
</div>
|
|
7574
|
-
<div class="form-row">
|
|
7575
|
-
<mat-checkbox [(ngModel)]="editEnable">Ativar cascata nativa</mat-checkbox>
|
|
7576
|
-
<mat-checkbox [(ngModel)]="editReset">Resetar ao mudar</mat-checkbox>
|
|
9308
|
+
</mat-form-field>
|
|
7577
9309
|
</div>
|
|
7578
|
-
</
|
|
7579
|
-
|
|
7580
|
-
|
|
7581
|
-
|
|
7582
|
-
|
|
7583
|
-
|
|
7584
|
-
|
|
7585
|
-
|
|
7586
|
-
|
|
7587
|
-
|
|
7588
|
-
|
|
7589
|
-
|
|
7590
|
-
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
|
|
7597
|
-
|
|
7598
|
-
|
|
7599
|
-
|
|
7600
|
-
|
|
7601
|
-
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
7606
|
-
|
|
7607
|
-
|
|
7608
|
-
|
|
7609
|
-
|
|
7610
|
-
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
|
|
7614
|
-
|
|
7615
|
-
|
|
7616
|
-
|
|
7617
|
-
|
|
7618
|
-
|
|
7619
|
-
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
7625
|
-
|
|
7626
|
-
|
|
7627
|
-
|
|
7628
|
-
|
|
7629
|
-
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7641
|
-
|
|
7642
|
-
|
|
7643
|
-
|
|
7644
|
-
|
|
7645
|
-
|
|
7646
|
-
|
|
7647
|
-
|
|
7648
|
-
|
|
7649
|
-
|
|
7650
|
-
|
|
7651
|
-
|
|
7652
|
-
|
|
7653
|
-
|
|
7654
|
-
|
|
7655
|
-
|
|
7656
|
-
|
|
7657
|
-
|
|
7658
|
-
|
|
7659
|
-
|
|
7660
|
-
|
|
7661
|
-
|
|
7662
|
-
>
|
|
7663
|
-
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
7664
|
-
</button>
|
|
7665
|
-
</mat-form-field>
|
|
7666
|
-
</td>
|
|
7667
|
-
<td>
|
|
7668
|
-
<mat-form-field appearance="outline" class="full">
|
|
7669
|
-
<mat-label>ValuePath</mat-label>
|
|
7670
|
-
<input matInput [(ngModel)]="row.valuePath" [matAutocomplete]="autoValue" [placeholder]="editValuePath||'id'">
|
|
7671
|
-
<mat-autocomplete #autoValue="matAutocomplete">
|
|
7672
|
-
<mat-option *ngFor="let opt of getValuePathSuggestions(row.dep)" [value]="opt">{{ opt }}</mat-option>
|
|
7673
|
-
</mat-autocomplete>
|
|
7674
|
-
<button
|
|
7675
|
-
mat-icon-button
|
|
7676
|
-
matSuffix
|
|
7677
|
-
class="help-icon-button"
|
|
7678
|
-
type="button"
|
|
7679
|
-
[matTooltip]="'Opcional'"
|
|
7680
|
-
matTooltipPosition="above"
|
|
7681
|
-
>
|
|
7682
|
-
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
7683
|
-
</button>
|
|
7684
|
-
</mat-form-field>
|
|
7685
|
-
</td>
|
|
7686
|
-
</tr>
|
|
7687
|
-
</tbody>
|
|
7688
|
-
</table>
|
|
7689
|
-
<ng-template #noMap>
|
|
7690
|
-
<div class="rules-empty">Defina dependentes para sugerir o mapeamento.</div>
|
|
7691
|
-
</ng-template>
|
|
7692
|
-
</mat-expansion-panel>
|
|
7693
|
-
|
|
7694
|
-
<!-- Preview -->
|
|
7695
|
-
<mat-expansion-panel class="stack-panel">
|
|
7696
|
-
<mat-expansion-panel-header>
|
|
7697
|
-
<mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'visibility'"></mat-icon> Preview</mat-panel-title>
|
|
7698
|
-
</mat-expansion-panel-header>
|
|
7699
|
-
<pre class="preview-box">{{ buildPreview() }}</pre>
|
|
7700
|
-
<div style="display:flex; justify-content:flex-end; padding-top:6px">
|
|
7701
|
-
<button mat-stroked-button (click)="copyPreview()">Copiar</button>
|
|
7702
|
-
</div>
|
|
7703
|
-
</mat-expansion-panel>
|
|
7704
|
-
</div>
|
|
7705
|
-
|
|
7706
|
-
<div style="display:flex; gap:8px; justify-content:flex-end; padding:8px 12px;">
|
|
7707
|
-
<button mat-stroked-button (click)="cancelEdit()">Cancelar</button>
|
|
7708
|
-
<button mat-flat-button color="primary" (click)="saveEdit(sel)">Salvar</button>
|
|
9310
|
+
</mat-expansion-panel>
|
|
9311
|
+
<!-- Mapeamento de filtros (tabela) -->
|
|
9312
|
+
<mat-expansion-panel class="stack-panel">
|
|
9313
|
+
<mat-expansion-panel-header>
|
|
9314
|
+
<mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'filter_alt'"></mat-icon> Mapeamento de filtros</mat-panel-title>
|
|
9315
|
+
</mat-expansion-panel-header>
|
|
9316
|
+
@if (editMapRows.length) {
|
|
9317
|
+
<table class="map-table">
|
|
9318
|
+
<thead>
|
|
9319
|
+
<tr>
|
|
9320
|
+
<th>Dependente</th>
|
|
9321
|
+
<th>Chave (dot‑path)</th>
|
|
9322
|
+
<th>ValuePath (opcional)</th>
|
|
9323
|
+
</tr>
|
|
9324
|
+
</thead>
|
|
9325
|
+
<tbody>
|
|
9326
|
+
@for (row of editMapRows; track row; let i = $index) {
|
|
9327
|
+
<tr>
|
|
9328
|
+
<td>{{ row.dep }}</td>
|
|
9329
|
+
<td>
|
|
9330
|
+
<mat-form-field appearance="outline" class="full">
|
|
9331
|
+
<mat-label>Chave</mat-label>
|
|
9332
|
+
<input matInput [(ngModel)]="row.key" [matAutocomplete]="autoKey" [placeholder]="row.dep">
|
|
9333
|
+
<mat-autocomplete #autoKey="matAutocomplete">
|
|
9334
|
+
@for (opt of getKeySuggestions(row.dep); track opt) {
|
|
9335
|
+
<mat-option [value]="opt">{{ opt }}</mat-option>
|
|
9336
|
+
}
|
|
9337
|
+
</mat-autocomplete>
|
|
9338
|
+
<button
|
|
9339
|
+
mat-icon-button
|
|
9340
|
+
matSuffix
|
|
9341
|
+
class="help-icon-button"
|
|
9342
|
+
type="button"
|
|
9343
|
+
[matTooltip]="'Use dot-notation (e.g. ' + row.dep + '.id) for nested objects'"
|
|
9344
|
+
matTooltipPosition="above"
|
|
9345
|
+
>
|
|
9346
|
+
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
9347
|
+
</button>
|
|
9348
|
+
</mat-form-field>
|
|
9349
|
+
</td>
|
|
9350
|
+
<td>
|
|
9351
|
+
<mat-form-field appearance="outline" class="full">
|
|
9352
|
+
<mat-label>ValuePath</mat-label>
|
|
9353
|
+
<input matInput [(ngModel)]="row.valuePath" [matAutocomplete]="autoValue" [placeholder]="editValuePath||'id'">
|
|
9354
|
+
<mat-autocomplete #autoValue="matAutocomplete">
|
|
9355
|
+
@for (opt of getValuePathSuggestions(row.dep); track opt) {
|
|
9356
|
+
<mat-option [value]="opt">{{ opt }}</mat-option>
|
|
9357
|
+
}
|
|
9358
|
+
</mat-autocomplete>
|
|
9359
|
+
<button
|
|
9360
|
+
mat-icon-button
|
|
9361
|
+
matSuffix
|
|
9362
|
+
class="help-icon-button"
|
|
9363
|
+
type="button"
|
|
9364
|
+
[matTooltip]="'Opcional'"
|
|
9365
|
+
matTooltipPosition="above"
|
|
9366
|
+
>
|
|
9367
|
+
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
9368
|
+
</button>
|
|
9369
|
+
</mat-form-field>
|
|
9370
|
+
</td>
|
|
9371
|
+
</tr>
|
|
9372
|
+
}
|
|
9373
|
+
</tbody>
|
|
9374
|
+
</table>
|
|
9375
|
+
} @else {
|
|
9376
|
+
<div class="rules-empty">Defina dependentes para sugerir o mapeamento.</div>
|
|
9377
|
+
}
|
|
9378
|
+
</mat-expansion-panel>
|
|
9379
|
+
<!-- Preview -->
|
|
9380
|
+
<mat-expansion-panel class="stack-panel">
|
|
9381
|
+
<mat-expansion-panel-header>
|
|
9382
|
+
<mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'visibility'"></mat-icon> Preview</mat-panel-title>
|
|
9383
|
+
</mat-expansion-panel-header>
|
|
9384
|
+
<pre class="preview-box">{{ buildPreview() }}</pre>
|
|
9385
|
+
<div style="display:flex; justify-content:flex-end; padding-top:6px">
|
|
9386
|
+
<button mat-stroked-button (click)="copyPreview()">Copiar</button>
|
|
9387
|
+
</div>
|
|
9388
|
+
</mat-expansion-panel>
|
|
9389
|
+
</div>
|
|
9390
|
+
<div style="display:flex; gap:8px; justify-content:flex-end; padding:8px 12px;">
|
|
9391
|
+
<button mat-stroked-button (click)="cancelEdit()">Cancelar</button>
|
|
9392
|
+
<button mat-flat-button color="primary" (click)="saveEdit(sel)">Salvar</button>
|
|
9393
|
+
</div>
|
|
7709
9394
|
</div>
|
|
7710
9395
|
</div>
|
|
7711
|
-
|
|
9396
|
+
}
|
|
7712
9397
|
</div>
|
|
7713
|
-
|
|
9398
|
+
`, styles: [":host{display:block}.cm-root{display:grid;grid-template-columns:320px 1fr;gap:16px;min-height:420px}.cm-left{border-right:1px solid var(--md-sys-color-outline-variant);padding-right:8px}.cm-search{width:100%;margin-bottom:8px}.cm-right{padding-left:8px}.field-item{display:flex;align-items:center;justify-content:space-between;padding:8px 6px;cursor:pointer;border-radius:10px;transition:background .12s ease,border .12s ease;border:1px solid transparent}.field-item:hover{background:var(--md-sys-color-surface-container-high)}.field-item.selected{background:var(--md-sys-color-surface-container-highest);border-color:var(--md-sys-color-outline-variant)}.rule-badges{display:inline-flex;gap:6px}.rule-badge{font-size:11px;padding:2px 8px;border-radius:999px;font-weight:600}.b-active{background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.b-manual{background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface-variant)}.b-immediate{background:var(--md-sys-color-secondary-container);color:var(--md-sys-color-on-secondary-container)}.b-conflict{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container)}.cm-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.rules-empty{opacity:.8;font-style:italic;padding:8px 0;color:var(--md-sys-color-on-surface-variant)}.rule-row{display:grid;grid-template-columns:1fr auto;align-items:center;padding:10px 0;border-bottom:1px dashed var(--md-sys-color-outline-variant)}.rule-actions button{margin-left:4px}.banner{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container);padding:10px 12px;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;margin-bottom:10px;display:flex;gap:8px;align-items:center}.import-panel{background:var(--md-sys-color-surface-container-high);border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;padding:10px;margin-bottom:10px}.full{width:100%}.form-row{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.form-grid{display:grid;gap:12px;padding:8px 0}.map-table{width:100%;border-collapse:collapse}.map-table th,.map-table td{padding:8px;border-bottom:1px dashed var(--md-sys-color-outline-variant)}.kbd{font-family:monospace;background:var(--md-sys-color-surface-container-highest);padding:0 6px;border-radius:4px;color:var(--md-sys-color-on-surface-variant)}.panel-surface{background:var(--md-sys-color-surface-container-high);border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;box-shadow:var(--md-sys-elevation-level1, none);margin-bottom:12px}.panel-stack{display:grid;gap:12px;margin:12px 0}.stack-panel{background:var(--md-sys-color-surface-container-high);border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;box-shadow:var(--md-sys-elevation-level1, none)}.stack-panel .mat-expansion-panel-content{padding:0 16px 12px}.panel-header{display:flex;align-items:center;gap:8px}.preview-box{background:var(--md-sys-color-surface-container-low);border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;padding:8px;color:var(--md-sys-color-on-surface)}.help-icon-button{--mdc-icon-button-state-layer-size: 28px;--mdc-icon-button-icon-size: 18px;width:28px;height:28px;padding:0;display:inline-flex;align-items:center;justify-content:center;margin-right:-4px}.help-icon-button mat-icon{font-size:18px;width:18px;height:18px}.mat-mdc-form-field-icon-suffix{align-self:center}\n"] }]
|
|
7714
9399
|
}], ctorParameters: () => [{ type: CascadeRulesService }], propDecorators: { fields: [{
|
|
7715
9400
|
type: Input
|
|
7716
9401
|
}], connections: [{
|
|
@@ -7753,6 +9438,388 @@ const METADATA_EDITOR_AI_CAPABILITY_CATALOG = {
|
|
|
7753
9438
|
enums: {},
|
|
7754
9439
|
};
|
|
7755
9440
|
|
|
9441
|
+
const fieldMetadataPropertySchema = {
|
|
9442
|
+
type: 'object',
|
|
9443
|
+
required: ['path', 'value'],
|
|
9444
|
+
properties: {
|
|
9445
|
+
path: {
|
|
9446
|
+
enum: [
|
|
9447
|
+
'name',
|
|
9448
|
+
'label',
|
|
9449
|
+
'description',
|
|
9450
|
+
'controlType',
|
|
9451
|
+
'placeholder',
|
|
9452
|
+
'defaultValue',
|
|
9453
|
+
'group',
|
|
9454
|
+
'order',
|
|
9455
|
+
'required',
|
|
9456
|
+
'disabled',
|
|
9457
|
+
'readOnly',
|
|
9458
|
+
'hidden',
|
|
9459
|
+
'source',
|
|
9460
|
+
'transient',
|
|
9461
|
+
'submitPolicy',
|
|
9462
|
+
'hint',
|
|
9463
|
+
'helpText',
|
|
9464
|
+
'tooltip',
|
|
9465
|
+
'options',
|
|
9466
|
+
'optionSource',
|
|
9467
|
+
'validators',
|
|
9468
|
+
'conditionalRequired',
|
|
9469
|
+
'conditionalDisplay',
|
|
9470
|
+
'visibleIn',
|
|
9471
|
+
'ariaLabel',
|
|
9472
|
+
'ariaDescribedBy',
|
|
9473
|
+
],
|
|
9474
|
+
},
|
|
9475
|
+
value: {},
|
|
9476
|
+
merge: { type: 'boolean' },
|
|
9477
|
+
},
|
|
9478
|
+
};
|
|
9479
|
+
const optionSourceSchema = {
|
|
9480
|
+
type: 'object',
|
|
9481
|
+
minProperties: 1,
|
|
9482
|
+
properties: {
|
|
9483
|
+
kind: { enum: ['static', 'remote', 'resource', 'lookup'] },
|
|
9484
|
+
resource: { type: 'string' },
|
|
9485
|
+
endpoint: { type: 'string' },
|
|
9486
|
+
valueField: { type: 'string' },
|
|
9487
|
+
labelField: { type: 'string' },
|
|
9488
|
+
dependsOn: { type: 'string' },
|
|
9489
|
+
dependencyFilterMap: { type: 'object' },
|
|
9490
|
+
display: {
|
|
9491
|
+
type: 'object',
|
|
9492
|
+
properties: {
|
|
9493
|
+
preset: { enum: ['compact', 'rich', 'directory', 'status', 'reference', 'hierarchical'] },
|
|
9494
|
+
usage: { enum: ['form', 'filter', 'table-cell', 'dashboard', 'wizard', 'review'] },
|
|
9495
|
+
density: { enum: ['compact', 'comfortable', 'rich'] },
|
|
9496
|
+
selectedLayout: { enum: ['card', 'inline', 'compact', 'token'] },
|
|
9497
|
+
resultLayout: { enum: ['list', 'denseList', 'table', 'card'] },
|
|
9498
|
+
primaryPropertyPath: { type: 'string' },
|
|
9499
|
+
fields: { type: 'array' },
|
|
9500
|
+
secondaryPropertyPaths: { type: 'array' },
|
|
9501
|
+
badgePropertyPaths: { type: 'array' },
|
|
9502
|
+
statusToneMap: { type: 'object' },
|
|
9503
|
+
},
|
|
9504
|
+
},
|
|
9505
|
+
detail: {
|
|
9506
|
+
type: 'object',
|
|
9507
|
+
properties: {
|
|
9508
|
+
kind: { enum: ['surface', 'route', 'href'] },
|
|
9509
|
+
surfaceId: { type: 'string' },
|
|
9510
|
+
presentation: { enum: ['drawer', 'modal'] },
|
|
9511
|
+
preferredWidget: { type: 'string' },
|
|
9512
|
+
mode: { enum: ['view', 'edit', 'create'] },
|
|
9513
|
+
},
|
|
9514
|
+
},
|
|
9515
|
+
options: { type: 'array' },
|
|
9516
|
+
},
|
|
9517
|
+
};
|
|
9518
|
+
const cascadeSchema = {
|
|
9519
|
+
type: 'object',
|
|
9520
|
+
required: ['dependentField'],
|
|
9521
|
+
properties: {
|
|
9522
|
+
dependentField: { type: 'string' },
|
|
9523
|
+
sourceField: { type: 'string' },
|
|
9524
|
+
strategy: { enum: ['replace', 'merge'] },
|
|
9525
|
+
debounceMs: { type: 'number' },
|
|
9526
|
+
loadMode: { enum: ['immediate', 'manual', 'respectLoadOn'] },
|
|
9527
|
+
dependencyFilterMap: { type: 'object' },
|
|
9528
|
+
optionSource: {
|
|
9529
|
+
type: 'object',
|
|
9530
|
+
properties: {
|
|
9531
|
+
kind: { enum: ['static', 'remote', 'resource', 'lookup'] },
|
|
9532
|
+
resource: { type: 'string' },
|
|
9533
|
+
endpoint: { type: 'string' },
|
|
9534
|
+
dependsOn: { type: 'string' },
|
|
9535
|
+
dependencyFilterMap: { type: 'object' },
|
|
9536
|
+
},
|
|
9537
|
+
},
|
|
9538
|
+
},
|
|
9539
|
+
};
|
|
9540
|
+
const PRAXIS_METADATA_EDITOR_AUTHORING_MANIFEST = {
|
|
9541
|
+
schemaVersion: '1.0.0',
|
|
9542
|
+
componentId: 'praxis-metadata-editor',
|
|
9543
|
+
ownerPackage: '@praxisui/metadata-editor',
|
|
9544
|
+
configSchemaId: 'FieldMetadata',
|
|
9545
|
+
manifestVersion: '1.0.0',
|
|
9546
|
+
runtimeInputs: [
|
|
9547
|
+
{ name: 'controlType', type: 'FieldControlType', description: 'Canonical control type used to resolve editor property coverage.' },
|
|
9548
|
+
{ name: 'seed', type: 'Partial<FieldMetadata | FieldDefinition>', description: 'Initial canonical field metadata used to hydrate the editor.' },
|
|
9549
|
+
{ name: 'fields', type: 'Array<FieldMetadata | FieldDefinition>', description: 'Available fields used by cascade authoring and context validation.' },
|
|
9550
|
+
{ name: 'properties', type: 'EditorProperty[]', description: 'Renderer property catalog resolved from ConfigRegistryService.' },
|
|
9551
|
+
{ name: 'form', type: 'FormGroup', description: 'Dynamic form generated from canonical editor properties by DynamicFormFactoryService.' },
|
|
9552
|
+
],
|
|
9553
|
+
editableTargets: [
|
|
9554
|
+
{ kind: 'fieldMetadata', resolver: 'field-metadata-json-path', description: 'Canonical FieldMetadata root and property paths.' },
|
|
9555
|
+
{ kind: 'controlType', resolver: 'dynamic-fields-control-type-discovery', description: 'Control type selected from dynamic-fields discovery and editor config registry.' },
|
|
9556
|
+
{ kind: 'optionSource', resolver: 'field-metadata-option-source', description: 'Canonical option source including backend x-ui option source fields where applicable.' },
|
|
9557
|
+
{ kind: 'cascade', resolver: 'metadata-editor-cascade-rules', description: 'Cascade dependencies and dependency filter mappings edited through CascadeManagerTab.' },
|
|
9558
|
+
{ kind: 'renderer', resolver: 'metadata-editor-renderer-property', description: 'Dynamic editor renderer property coverage and editor component mapping.' },
|
|
9559
|
+
{ kind: 'validation', resolver: 'field-metadata-validation-rules', description: 'Field validation rules and contextual validators.' },
|
|
9560
|
+
{ kind: 'contextHint', resolver: 'metadata-editor-context-hints', description: 'Authoring hints, help text and contextual guidance for metadata editors.' },
|
|
9561
|
+
{ kind: 'normalization', resolver: 'metadata-editor-schema-normalizer', description: 'Schema normalization and seed hydration rules.' },
|
|
9562
|
+
],
|
|
9563
|
+
operations: [
|
|
9564
|
+
{
|
|
9565
|
+
operationId: 'fieldMetadata.property.set',
|
|
9566
|
+
title: 'Set FieldMetadata property',
|
|
9567
|
+
scope: 'fieldMetadataPath',
|
|
9568
|
+
targetKind: 'fieldMetadata',
|
|
9569
|
+
target: { kind: 'fieldMetadata', resolver: 'field-metadata-json-path', ambiguityPolicy: 'fail', required: true },
|
|
9570
|
+
inputSchema: fieldMetadataPropertySchema,
|
|
9571
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'metadata-field-property-set', handlerContract: {
|
|
9572
|
+
reads: ['fieldMetadata', 'fieldMetadataPath', 'SchemaNormalizerService'],
|
|
9573
|
+
writes: ['fieldMetadata'],
|
|
9574
|
+
identityKeys: ['field.name', 'path'],
|
|
9575
|
+
inputSchema: fieldMetadataPropertySchema,
|
|
9576
|
+
failureModes: ['unknown-field-metadata-path', 'non-canonical-shape', 'readonly-field', 'normalization-failed'],
|
|
9577
|
+
description: 'Sets a canonical FieldMetadata property through a governed metadata path and re-runs schema normalization.',
|
|
9578
|
+
} }],
|
|
9579
|
+
destructive: false,
|
|
9580
|
+
requiresConfirmation: false,
|
|
9581
|
+
validators: ['field-metadata-shape-canonical', 'field-path-supported-by-editor', 'metadata-round-trip'],
|
|
9582
|
+
affectedPaths: ['fieldMetadata'],
|
|
9583
|
+
submissionImpact: 'affects-schema-backed-data',
|
|
9584
|
+
preconditions: ['field-metadata-loaded'],
|
|
9585
|
+
},
|
|
9586
|
+
{
|
|
9587
|
+
operationId: 'controlType.set',
|
|
9588
|
+
title: 'Set control type',
|
|
9589
|
+
scope: 'controlType',
|
|
9590
|
+
targetKind: 'controlType',
|
|
9591
|
+
target: { kind: 'controlType', resolver: 'dynamic-fields-control-type-discovery', ambiguityPolicy: 'fail', required: true },
|
|
9592
|
+
inputSchema: {
|
|
9593
|
+
type: 'object',
|
|
9594
|
+
required: ['controlType'],
|
|
9595
|
+
properties: {
|
|
9596
|
+
controlType: { type: 'string' },
|
|
9597
|
+
preserveCompatibleProperties: { type: 'boolean' },
|
|
9598
|
+
},
|
|
9599
|
+
},
|
|
9600
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'metadata-control-type-set', handlerContract: {
|
|
9601
|
+
reads: ['FieldControlType', 'ConfigRegistryService', 'dynamic-fields-discovery', 'fieldMetadata'],
|
|
9602
|
+
writes: ['fieldMetadata.controlType', 'properties', 'normalizedSeed'],
|
|
9603
|
+
identityKeys: ['field.name', 'controlType'],
|
|
9604
|
+
inputSchema: { type: 'object', required: ['controlType'], properties: { controlType: { type: 'string' }, preserveCompatibleProperties: { type: 'boolean' } } },
|
|
9605
|
+
failureModes: ['control-type-not-discovered', 'editor-coverage-missing', 'incompatible-metadata-preservation'],
|
|
9606
|
+
description: 'Changes control type only when dynamic-fields discovery and metadata-editor config registry both expose renderer/editor coverage.',
|
|
9607
|
+
} }],
|
|
9608
|
+
destructive: false,
|
|
9609
|
+
requiresConfirmation: false,
|
|
9610
|
+
validators: ['control-type-exists-in-discovery', 'editor-coverage-exists', 'metadata-round-trip'],
|
|
9611
|
+
affectedPaths: ['fieldMetadata.controlType', 'properties', 'normalizedSeed'],
|
|
9612
|
+
submissionImpact: 'affects-schema-backed-data',
|
|
9613
|
+
preconditions: ['dynamic-fields-discovery-loaded'],
|
|
9614
|
+
},
|
|
9615
|
+
{
|
|
9616
|
+
operationId: 'optionSource.configure',
|
|
9617
|
+
title: 'Configure option source',
|
|
9618
|
+
scope: 'dataBinding',
|
|
9619
|
+
targetKind: 'optionSource',
|
|
9620
|
+
target: { kind: 'optionSource', resolver: 'field-metadata-option-source', ambiguityPolicy: 'fail', required: false },
|
|
9621
|
+
inputSchema: optionSourceSchema,
|
|
9622
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'metadata-option-source-configure', handlerContract: {
|
|
9623
|
+
reads: ['fieldMetadata.optionSource', 'backend.x-ui.optionSource', 'SchemaNormalizerService'],
|
|
9624
|
+
writes: ['fieldMetadata.optionSource'],
|
|
9625
|
+
identityKeys: ['field.name', 'optionSource.kind', 'optionSource.resource'],
|
|
9626
|
+
inputSchema: optionSourceSchema,
|
|
9627
|
+
failureModes: ['invalid-option-source-kind', 'remote-source-missing-governed-resource', 'dependency-filter-map-invalid'],
|
|
9628
|
+
description: 'Configures canonical optionSource fields while preserving backend x-ui dependsOn and dependencyFilterMap semantics.',
|
|
9629
|
+
} }],
|
|
9630
|
+
destructive: false,
|
|
9631
|
+
requiresConfirmation: false,
|
|
9632
|
+
validators: ['option-source-shape-canonical', 'remote-option-source-governed', 'cascade-backend-shape-preserved', 'metadata-round-trip'],
|
|
9633
|
+
affectedPaths: ['fieldMetadata.optionSource'],
|
|
9634
|
+
submissionImpact: 'affects-remote-binding',
|
|
9635
|
+
preconditions: ['field-metadata-loaded'],
|
|
9636
|
+
},
|
|
9637
|
+
{
|
|
9638
|
+
operationId: 'cascade.configure',
|
|
9639
|
+
title: 'Configure cascade rule',
|
|
9640
|
+
scope: 'interaction',
|
|
9641
|
+
targetKind: 'cascade',
|
|
9642
|
+
target: { kind: 'cascade', resolver: 'metadata-editor-cascade-rules', ambiguityPolicy: 'fail', required: false },
|
|
9643
|
+
inputSchema: cascadeSchema,
|
|
9644
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'metadata-cascade-configure', handlerContract: {
|
|
9645
|
+
reads: ['CascadeManagerTabComponent', 'CascadeRulesService', 'fields', 'fieldMetadata.optionSource', 'fieldMetadata.dependencyFields', 'fieldMetadata.dependencyFilterMap'],
|
|
9646
|
+
writes: ['fieldMetadata.dependencyFields', 'fieldMetadata.enableDependencyCascade', 'fieldMetadata.resetOnDependentChange', 'fieldMetadata.dependencyFilterMap', 'fieldMetadata.dependencyValuePath', 'fieldMetadata.dependencyMergeStrategy', 'fieldMetadata.dependencyDebounceMs', 'fieldMetadata.dependencyLoadOnChange', 'fieldMetadata.optionSource.dependsOn', 'fieldMetadata.optionSource.dependencyFilterMap'],
|
|
9647
|
+
identityKeys: ['dependentField', 'sourceField'],
|
|
9648
|
+
inputSchema: cascadeSchema,
|
|
9649
|
+
failureModes: ['dependent-field-not-found', 'source-field-not-found', 'cascade-cycle-detected', 'dependency-filter-map-invalid'],
|
|
9650
|
+
description: 'Applies cascade dependencies through CascadeRulesService using the metadata-editor save contract while preserving backend x-ui optionSource dependency semantics during hydration.',
|
|
9651
|
+
} }],
|
|
9652
|
+
destructive: false,
|
|
9653
|
+
requiresConfirmation: false,
|
|
9654
|
+
validators: ['cascade-fields-exist', 'cascade-cycle-free', 'cascade-backend-shape-preserved', 'option-source-shape-canonical', 'remote-option-source-governed', 'metadata-round-trip'],
|
|
9655
|
+
affectedPaths: ['fieldMetadata.dependencyFields', 'fieldMetadata.enableDependencyCascade', 'fieldMetadata.resetOnDependentChange', 'fieldMetadata.dependencyFilterMap', 'fieldMetadata.dependencyValuePath', 'fieldMetadata.dependencyMergeStrategy', 'fieldMetadata.dependencyDebounceMs', 'fieldMetadata.dependencyLoadOnChange', 'fieldMetadata.optionSource.dependsOn', 'fieldMetadata.optionSource.dependencyFilterMap'],
|
|
9656
|
+
submissionImpact: 'affects-remote-binding',
|
|
9657
|
+
preconditions: ['field-list-loaded'],
|
|
9658
|
+
},
|
|
9659
|
+
{
|
|
9660
|
+
operationId: 'renderer.configure',
|
|
9661
|
+
title: 'Configure renderer coverage',
|
|
9662
|
+
scope: 'editorCoverage',
|
|
9663
|
+
targetKind: 'renderer',
|
|
9664
|
+
target: { kind: 'renderer', resolver: 'metadata-editor-renderer-property', ambiguityPolicy: 'fail', required: true },
|
|
9665
|
+
inputSchema: {
|
|
9666
|
+
type: 'object',
|
|
9667
|
+
required: ['propertyName', 'editorType'],
|
|
9668
|
+
properties: {
|
|
9669
|
+
propertyName: { type: 'string' },
|
|
9670
|
+
editorType: { type: 'string' },
|
|
9671
|
+
group: { type: 'string' },
|
|
9672
|
+
required: { type: 'boolean' },
|
|
9673
|
+
options: { type: 'array' },
|
|
9674
|
+
},
|
|
9675
|
+
},
|
|
9676
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'metadata-renderer-configure', handlerContract: {
|
|
9677
|
+
reads: ['DynamicEditorRendererComponent', 'EditorComponentRegistryService', 'ConfigRegistryService', 'EditorProperty'],
|
|
9678
|
+
writes: ['properties', 'editorCoverage'],
|
|
9679
|
+
identityKeys: ['controlType', 'propertyName'],
|
|
9680
|
+
inputSchema: { type: 'object', required: ['propertyName', 'editorType'], properties: { propertyName: { type: 'string' }, editorType: { type: 'string' }, group: { type: 'string' } } },
|
|
9681
|
+
failureModes: ['editor-type-not-registered', 'property-not-covered', 'json-only-coverage-not-accepted'],
|
|
9682
|
+
description: 'Configures visual renderer coverage for a metadata property and rejects JSON-only support for required visual authoring.',
|
|
9683
|
+
} }],
|
|
9684
|
+
destructive: false,
|
|
9685
|
+
requiresConfirmation: false,
|
|
9686
|
+
validators: ['renderer-editor-type-registered', 'visual-editor-coverage-required', 'editor-coverage-exists', 'metadata-round-trip'],
|
|
9687
|
+
affectedPaths: ['properties', 'editorCoverage'],
|
|
9688
|
+
submissionImpact: 'config-only',
|
|
9689
|
+
preconditions: ['config-registry-loaded'],
|
|
9690
|
+
},
|
|
9691
|
+
{
|
|
9692
|
+
operationId: 'validationRule.add',
|
|
9693
|
+
title: 'Add validation rule',
|
|
9694
|
+
scope: 'fieldMetadataPath',
|
|
9695
|
+
targetKind: 'validation',
|
|
9696
|
+
target: { kind: 'validation', resolver: 'field-metadata-validation-rules', ambiguityPolicy: 'fail', required: false },
|
|
9697
|
+
inputSchema: {
|
|
9698
|
+
type: 'object',
|
|
9699
|
+
required: ['rule'],
|
|
9700
|
+
properties: {
|
|
9701
|
+
rule: { type: 'object' },
|
|
9702
|
+
message: { type: 'string' },
|
|
9703
|
+
contextValidatorId: { type: 'string' },
|
|
9704
|
+
},
|
|
9705
|
+
},
|
|
9706
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'metadata-validation-rule-add', handlerContract: {
|
|
9707
|
+
reads: ['fieldMetadata.validators', 'ContextValidatorRegistryService', 'DynamicFormFactoryService'],
|
|
9708
|
+
writes: ['fieldMetadata.validators'],
|
|
9709
|
+
identityKeys: ['field.name', 'rule.type', 'contextValidatorId'],
|
|
9710
|
+
inputSchema: { type: 'object', required: ['rule'], properties: { rule: { type: 'object' }, message: { type: 'string' }, contextValidatorId: { type: 'string' } } },
|
|
9711
|
+
failureModes: ['validation-rule-invalid', 'context-validator-not-registered', 'duplicate-validation-rule'],
|
|
9712
|
+
description: 'Adds a canonical validation rule and verifies contextual validator availability before persistence.',
|
|
9713
|
+
} }],
|
|
9714
|
+
destructive: false,
|
|
9715
|
+
requiresConfirmation: false,
|
|
9716
|
+
validators: ['validation-rule-canonical', 'context-validator-registered', 'metadata-round-trip'],
|
|
9717
|
+
affectedPaths: ['fieldMetadata.validators'],
|
|
9718
|
+
submissionImpact: 'affects-submission',
|
|
9719
|
+
preconditions: ['field-metadata-loaded'],
|
|
9720
|
+
},
|
|
9721
|
+
{
|
|
9722
|
+
operationId: 'contextHint.set',
|
|
9723
|
+
title: 'Set metadata context hint',
|
|
9724
|
+
scope: 'meta',
|
|
9725
|
+
targetKind: 'contextHint',
|
|
9726
|
+
target: { kind: 'contextHint', resolver: 'metadata-editor-context-hints', ambiguityPolicy: 'fail', required: false },
|
|
9727
|
+
inputSchema: {
|
|
9728
|
+
type: 'object',
|
|
9729
|
+
required: ['hintPath', 'value'],
|
|
9730
|
+
properties: {
|
|
9731
|
+
hintPath: { enum: ['hint', 'helpText', 'description', 'tooltip', 'ariaLabel', 'ariaDescribedBy'] },
|
|
9732
|
+
value: {},
|
|
9733
|
+
localeKey: { type: 'string' },
|
|
9734
|
+
},
|
|
9735
|
+
},
|
|
9736
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'metadata-context-hint-set', handlerContract: {
|
|
9737
|
+
reads: ['fieldMetadata.hint', 'fieldMetadata.helpText', 'fieldMetadata.description', 'fieldMetadata.tooltip', 'fieldMetadata.ariaLabel', 'fieldMetadata.ariaDescribedBy', 'metadata-editor-i18n'],
|
|
9738
|
+
writes: ['fieldMetadata.hint', 'fieldMetadata.helpText', 'fieldMetadata.description', 'fieldMetadata.tooltip', 'fieldMetadata.ariaLabel', 'fieldMetadata.ariaDescribedBy'],
|
|
9739
|
+
identityKeys: ['field.name', 'hintPath'],
|
|
9740
|
+
inputSchema: { type: 'object', required: ['hintPath', 'value'], properties: { hintPath: { enum: ['hint', 'helpText', 'description', 'tooltip', 'ariaLabel', 'ariaDescribedBy'] }, value: {}, localeKey: { type: 'string' } } },
|
|
9741
|
+
failureModes: ['hint-path-not-canonical', 'hint-value-not-serializable', 'i18n-key-missing'],
|
|
9742
|
+
description: 'Sets structured FieldMetadata hint/help/accessibility text through canonical metadata paths and metadata-editor i18n constraints.',
|
|
9743
|
+
} }],
|
|
9744
|
+
destructive: false,
|
|
9745
|
+
requiresConfirmation: false,
|
|
9746
|
+
validators: ['context-hint-shape-canonical', 'context-hint-i18n-compatible', 'metadata-round-trip'],
|
|
9747
|
+
affectedPaths: ['fieldMetadata.hint', 'fieldMetadata.helpText', 'fieldMetadata.description', 'fieldMetadata.tooltip', 'fieldMetadata.ariaLabel', 'fieldMetadata.ariaDescribedBy'],
|
|
9748
|
+
submissionImpact: 'visual-only',
|
|
9749
|
+
preconditions: ['field-metadata-loaded'],
|
|
9750
|
+
},
|
|
9751
|
+
{
|
|
9752
|
+
operationId: 'normalization.apply',
|
|
9753
|
+
title: 'Apply schema normalization',
|
|
9754
|
+
scope: 'runtimeCoverage',
|
|
9755
|
+
targetKind: 'normalization',
|
|
9756
|
+
target: { kind: 'normalization', resolver: 'metadata-editor-schema-normalizer', ambiguityPolicy: 'fail', required: false },
|
|
9757
|
+
inputSchema: {
|
|
9758
|
+
type: 'object',
|
|
9759
|
+
properties: {
|
|
9760
|
+
mode: { enum: ['hydrate-seed', 'coerce-types', 'apply-defaults', 'preserve-advanced-properties'] },
|
|
9761
|
+
preserveUnknownCanonicalFields: { type: 'boolean' },
|
|
9762
|
+
},
|
|
9763
|
+
},
|
|
9764
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'metadata-normalization-apply', handlerContract: {
|
|
9765
|
+
reads: ['SchemaNormalizerService', 'fieldMetadata', 'properties', 'DynamicFormFactoryService'],
|
|
9766
|
+
writes: ['normalizedSeed', 'form', 'fieldMetadata'],
|
|
9767
|
+
identityKeys: ['field.name', 'controlType', 'mode'],
|
|
9768
|
+
inputSchema: { type: 'object', properties: { mode: { enum: ['hydrate-seed', 'coerce-types', 'apply-defaults', 'preserve-advanced-properties'] }, preserveUnknownCanonicalFields: { type: 'boolean' } } },
|
|
9769
|
+
failureModes: ['normalizer-missing', 'type-coercion-failed', 'advanced-property-lost', 'runtime-editor-drift'],
|
|
9770
|
+
description: 'Runs metadata-editor schema normalization while preserving canonical advanced FieldMetadata properties and editor/runtime round-trip.',
|
|
9771
|
+
} }],
|
|
9772
|
+
destructive: false,
|
|
9773
|
+
requiresConfirmation: false,
|
|
9774
|
+
validators: ['normalization-preserves-canonical-fields', 'runtime-editor-round-trip', 'metadata-round-trip'],
|
|
9775
|
+
affectedPaths: ['normalizedSeed', 'form', 'fieldMetadata'],
|
|
9776
|
+
submissionImpact: 'config-only',
|
|
9777
|
+
preconditions: ['field-metadata-loaded', 'editor-properties-loaded'],
|
|
9778
|
+
},
|
|
9779
|
+
],
|
|
9780
|
+
validators: [
|
|
9781
|
+
{ validatorId: 'field-metadata-shape-canonical', level: 'error', code: 'METADATA_FIELD_SHAPE_CANONICAL', description: 'Edited metadata must remain compatible with canonical FieldMetadata.' },
|
|
9782
|
+
{ validatorId: 'field-path-supported-by-editor', level: 'error', code: 'METADATA_FIELD_PATH_SUPPORTED', description: 'Edited paths must be supported by visual editor coverage or explicitly delegated.' },
|
|
9783
|
+
{ validatorId: 'control-type-exists-in-discovery', level: 'error', code: 'METADATA_CONTROL_TYPE_DISCOVERED', description: 'Control type must exist in dynamic-fields discovery.' },
|
|
9784
|
+
{ validatorId: 'editor-coverage-exists', level: 'error', code: 'METADATA_EDITOR_COVERAGE_EXISTS', description: 'Control type and property must have metadata-editor coverage.' },
|
|
9785
|
+
{ validatorId: 'option-source-shape-canonical', level: 'error', code: 'METADATA_OPTION_SOURCE_CANONICAL', description: 'Option source must use canonical FieldMetadata/x-ui shape.' },
|
|
9786
|
+
{ validatorId: 'remote-option-source-governed', level: 'error', code: 'METADATA_REMOTE_OPTION_SOURCE_GOVERNED', description: 'Remote option sources must resolve through governed resource or endpoint metadata.' },
|
|
9787
|
+
{ validatorId: 'cascade-fields-exist', level: 'error', code: 'METADATA_CASCADE_FIELDS_EXIST', description: 'Cascade source and dependent fields must exist.' },
|
|
9788
|
+
{ validatorId: 'cascade-cycle-free', level: 'error', code: 'METADATA_CASCADE_CYCLE_FREE', description: 'Cascade dependencies must not create cycles.' },
|
|
9789
|
+
{ validatorId: 'cascade-backend-shape-preserved', level: 'error', code: 'METADATA_CASCADE_BACKEND_SHAPE_PRESERVED', description: 'Cascade config must preserve x-ui optionSource dependsOn and dependencyFilterMap semantics.' },
|
|
9790
|
+
{ validatorId: 'renderer-editor-type-registered', level: 'error', code: 'METADATA_RENDERER_TYPE_REGISTERED', description: 'Renderer editor type must be registered.' },
|
|
9791
|
+
{ validatorId: 'visual-editor-coverage-required', level: 'error', code: 'METADATA_VISUAL_COVERAGE_REQUIRED', description: 'JSON-only support is not accepted for required visual authoring coverage.' },
|
|
9792
|
+
{ validatorId: 'validation-rule-canonical', level: 'error', code: 'METADATA_VALIDATION_RULE_CANONICAL', description: 'Validation rule must use canonical metadata shape.' },
|
|
9793
|
+
{ validatorId: 'context-validator-registered', level: 'error', code: 'METADATA_CONTEXT_VALIDATOR_REGISTERED', description: 'Contextual validator must be registered before use.' },
|
|
9794
|
+
{ validatorId: 'context-hint-shape-canonical', level: 'error', code: 'METADATA_CONTEXT_HINT_CANONICAL', description: 'Context hints must remain structured metadata, not free-form host-only strings.' },
|
|
9795
|
+
{ validatorId: 'context-hint-i18n-compatible', level: 'warning', code: 'METADATA_CONTEXT_HINT_I18N_COMPATIBLE', description: 'Authoring hint text must remain compatible with metadata-editor i18n.' },
|
|
9796
|
+
{ validatorId: 'normalization-preserves-canonical-fields', level: 'error', code: 'METADATA_NORMALIZATION_PRESERVES_FIELDS', description: 'Normalization must not drop canonical advanced FieldMetadata properties.' },
|
|
9797
|
+
{ validatorId: 'runtime-editor-round-trip', level: 'error', code: 'METADATA_RUNTIME_EDITOR_ROUND_TRIP', description: 'Runtime and editor must consume the same normalized metadata shape.' },
|
|
9798
|
+
{ validatorId: 'metadata-round-trip', level: 'error', code: 'METADATA_ROUND_TRIP', description: 'Open, edit, apply/save, reopen and runtime consume must preserve metadata.' },
|
|
9799
|
+
],
|
|
9800
|
+
roundTripRequirements: [
|
|
9801
|
+
'FieldMetadata is the canonical edited document shape; backend x-ui-derived metadata must not be rewritten into host-only aliases.',
|
|
9802
|
+
'Control type changes require both dynamic-fields discovery and metadata-editor visual coverage.',
|
|
9803
|
+
'Option source cascades preserve x-ui optionSource.dependsOn and optionSource.dependencyFilterMap semantics.',
|
|
9804
|
+
'JSON-only support is incomplete when visual editor coverage is required.',
|
|
9805
|
+
'Schema normalization must preserve advanced canonical properties through open, edit, apply/save, reopen and runtime consumption.',
|
|
9806
|
+
],
|
|
9807
|
+
examples: [
|
|
9808
|
+
{ id: 'set-label', request: 'Set this field label to Customer name.', operationId: 'fieldMetadata.property.set', params: { path: 'label', value: 'Customer name' }, isPositive: true },
|
|
9809
|
+
{ id: 'set-control-type', request: 'Change this field to a select control.', operationId: 'controlType.set', params: { controlType: 'select', preserveCompatibleProperties: true }, isPositive: true },
|
|
9810
|
+
{ id: 'configure-remote-options', request: 'Load options from the customers resource using id and name.', operationId: 'optionSource.configure', params: { kind: 'resource', resource: 'customers', valueField: 'id', labelField: 'name' }, isPositive: true },
|
|
9811
|
+
{ id: 'configure-cascade', request: 'Filter city options when state changes.', operationId: 'cascade.configure', params: { dependentField: 'city', sourceField: 'state', dependencyFilterMap: { stateId: 'state.id' } }, isPositive: true },
|
|
9812
|
+
{ id: 'configure-renderer', request: 'Expose placeholder in the visual editor as a text field.', operationId: 'renderer.configure', params: { propertyName: 'placeholder', editorType: 'text', group: 'Presentation' }, isPositive: true },
|
|
9813
|
+
{ id: 'add-required-validation', request: 'Make this field required with a validation message.', operationId: 'validationRule.add', params: { rule: { type: 'required' }, message: 'Required field' }, isPositive: true },
|
|
9814
|
+
{ id: 'set-context-hint', request: 'Add a contextual hint for sales users.', operationId: 'contextHint.set', params: { hintPath: 'helpText', value: 'Use the legal customer name.' }, isPositive: true },
|
|
9815
|
+
{ id: 'normalize-seed', request: 'Normalize this imported field metadata while preserving advanced properties.', operationId: 'normalization.apply', params: { mode: 'preserve-advanced-properties', preserveUnknownCanonicalFields: true }, isPositive: true },
|
|
9816
|
+
{ id: 'reject-unknown-control', request: 'Use an unknown control type named magic-picker.', operationId: 'controlType.set', params: { controlType: 'magic-picker' }, isPositive: false },
|
|
9817
|
+
{ id: 'reject-json-only-coverage', request: 'Support this field only by editing raw JSON.', operationId: 'renderer.configure', params: { propertyName: 'optionSource', editorType: 'json' }, isPositive: false },
|
|
9818
|
+
{ id: 'reject-cascade-cycle', request: 'Make field A depend on B and B depend on A.', operationId: 'cascade.configure', params: { dependentField: 'a', sourceField: 'b' }, isPositive: false },
|
|
9819
|
+
{ id: 'reject-host-only-option-source', request: 'Save a host-only URL string as the option source.', operationId: 'optionSource.configure', params: { kind: 'remote', endpoint: 'local-only://options' }, isPositive: false },
|
|
9820
|
+
],
|
|
9821
|
+
};
|
|
9822
|
+
|
|
7756
9823
|
/** Metadata for Praxis Metadata Editor component */
|
|
7757
9824
|
const PRAXIS_METADATA_EDITOR_COMPONENT_METADATA = {
|
|
7758
9825
|
id: 'praxis-metadata-editor',
|
|
@@ -7807,4 +9874,4 @@ function providePraxisMetadataEditorMetadata() {
|
|
|
7807
9874
|
* Generated bundle index. Do not edit.
|
|
7808
9875
|
*/
|
|
7809
9876
|
|
|
7810
|
-
export { CascadeManagerTabComponent, CascadeRulesService, ConfigRegistryService, ContextValidatorRegistryService, DynamicEditorRendererComponent, DynamicFormFactoryService, EditorComponentRegistryService, FieldMetadataEditorComponent, METADATA_EDITOR_AI_CAPABILITIES, METADATA_EDITOR_AI_CAPABILITY_CATALOG, PRAXIS_METADATA_EDITOR_COMPONENT_METADATA, SchemaNormalizerService, providePraxisMetadataEditorMetadata };
|
|
9877
|
+
export { CascadeManagerTabComponent, CascadeRulesService, ConfigRegistryService, ContextValidatorRegistryService, DynamicEditorRendererComponent, DynamicFormFactoryService, EditorComponentRegistryService, FieldMetadataEditorComponent, METADATA_EDITOR_AI_CAPABILITIES, METADATA_EDITOR_AI_CAPABILITY_CATALOG, PRAXIS_METADATA_EDITOR_AUTHORING_MANIFEST, PRAXIS_METADATA_EDITOR_COMPONENT_METADATA, SchemaNormalizerService, providePraxisMetadataEditorMetadata };
|