@praxisui/metadata-editor 8.0.0-beta.7 → 8.0.0-beta.71
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 +3283 -757
- package/package.json +15 -6
- package/{index.d.ts → types/praxisui-metadata-editor.d.ts} +20 -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' },
|
|
@@ -1054,6 +1900,61 @@ const inputProperties = [
|
|
|
1054
1900
|
{ name: 'defaultValue', label: 'Valor padrão', editorType: 'text', group: 'Geral' },
|
|
1055
1901
|
];
|
|
1056
1902
|
|
|
1903
|
+
const arrayProperties = [
|
|
1904
|
+
{ name: 'label', label: 'Label', editorType: 'text', group: 'Geral' },
|
|
1905
|
+
{ name: 'hint', label: 'Hint', editorType: 'text', group: 'Geral' },
|
|
1906
|
+
{ name: 'defaultValue', label: 'Valor inicial (JSON array)', editorType: 'textarea', group: 'Geral' },
|
|
1907
|
+
{
|
|
1908
|
+
name: 'array.itemType',
|
|
1909
|
+
label: 'Tipo do item',
|
|
1910
|
+
editorType: 'select',
|
|
1911
|
+
group: 'Colecao',
|
|
1912
|
+
defaultValue: 'object',
|
|
1913
|
+
options: [
|
|
1914
|
+
{ value: 'object', text: 'Objeto' },
|
|
1915
|
+
],
|
|
1916
|
+
},
|
|
1917
|
+
{
|
|
1918
|
+
name: 'array.mode',
|
|
1919
|
+
label: 'Modo',
|
|
1920
|
+
editorType: 'select',
|
|
1921
|
+
group: 'Colecao',
|
|
1922
|
+
defaultValue: 'cards',
|
|
1923
|
+
options: [
|
|
1924
|
+
{ value: 'cards', text: 'Cards' },
|
|
1925
|
+
],
|
|
1926
|
+
},
|
|
1927
|
+
{ name: 'array.itemSchemaRef', label: 'Schema do item ($ref)', editorType: 'text', group: 'Colecao' },
|
|
1928
|
+
{ name: 'array.itemIdentityField', label: 'Campo de identidade', editorType: 'text', group: 'Colecao' },
|
|
1929
|
+
{ name: 'array.addLabel', label: 'Texto do botao adicionar', editorType: 'text', group: 'Colecao' },
|
|
1930
|
+
{ name: 'array.emptyState', label: 'Texto vazio', editorType: 'text', group: 'Colecao' },
|
|
1931
|
+
{ name: 'array.itemTitleTemplate', label: 'Template do titulo do item', editorType: 'text', group: 'Colecao', hint: 'Ex.: {{nome}} - {{cargo}}' },
|
|
1932
|
+
{ name: 'array.minItems', label: 'Minimo de itens', editorType: 'number', group: 'Validacao' },
|
|
1933
|
+
{ name: 'array.maxItems', label: 'Maximo de itens', editorType: 'number', group: 'Validacao' },
|
|
1934
|
+
{
|
|
1935
|
+
name: 'array.deleteMode',
|
|
1936
|
+
label: 'Modo de exclusao',
|
|
1937
|
+
editorType: 'select',
|
|
1938
|
+
group: 'Operacoes',
|
|
1939
|
+
defaultValue: 'removeFromPayload',
|
|
1940
|
+
options: [
|
|
1941
|
+
{ value: 'removeFromPayload', text: 'Remover do payload' },
|
|
1942
|
+
],
|
|
1943
|
+
},
|
|
1944
|
+
{ name: 'array.operations.add', label: 'Permitir adicionar', editorType: 'checkbox', group: 'Operacoes', defaultValue: true },
|
|
1945
|
+
{ name: 'array.operations.edit', label: 'Permitir editar', editorType: 'checkbox', group: 'Operacoes', defaultValue: true },
|
|
1946
|
+
{ name: 'array.operations.remove', label: 'Permitir remover', editorType: 'checkbox', group: 'Operacoes', defaultValue: true },
|
|
1947
|
+
{ name: 'array.collectionValidation.uniqueBy', label: 'Unico por campos', editorType: 'textarea', group: 'Validacao', hint: 'JSON array ou uma linha por campo. Ex.: ["email"]' },
|
|
1948
|
+
{ name: 'array.collectionValidation.exactlyOne.field', label: 'Exatamente um: campo', editorType: 'text', group: 'Validacao' },
|
|
1949
|
+
{ name: 'array.collectionValidation.exactlyOne.value', label: 'Exatamente um: valor tipado', editorType: 'text', group: 'Validacao', hint: 'Use true/false, numero ou texto. Ex.: true, 1, PRINCIPAL.' },
|
|
1950
|
+
{ name: 'array.collectionValidation.exactlyOne.message', label: 'Mensagem: exatamente um', editorType: 'text', group: 'Validacao' },
|
|
1951
|
+
{ name: 'array.collectionValidation.atLeastOne.field', label: 'Pelo menos um: campo', editorType: 'text', group: 'Validacao' },
|
|
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.' },
|
|
1953
|
+
{ name: 'array.collectionValidation.atLeastOne.message', label: 'Mensagem: pelo menos um', editorType: 'text', group: 'Validacao' },
|
|
1954
|
+
{ name: 'array.itemSchema.fields', label: 'Subcampos', editorType: 'field-list', group: 'Subcampos', hint: 'Lista FieldMetadata[] usada quando nao houver itemSchemaRef resolvido.' },
|
|
1955
|
+
{ name: 'required', label: 'Obrigatorio', editorType: 'checkbox', group: 'Validacao' },
|
|
1956
|
+
];
|
|
1957
|
+
|
|
1057
1958
|
// Editor de metadados para componentes SELECT-like
|
|
1058
1959
|
// Paridade com INPUT: grupos, linhas e propriedades canônicas
|
|
1059
1960
|
const selectProperties = [
|
|
@@ -1078,141 +1979,980 @@ const selectProperties = [
|
|
|
1078
1979
|
{ name: 'optionSelectedIconColor', label: 'Cor do selecionado', editorType: 'color', group: 'Ícones', row: 'icon.optionSelected', inline: true, hint: 'Tema (primary/accent/warn) ou cor CSS.' },
|
|
1079
1980
|
// 2) Dados/Opções
|
|
1080
1981
|
{
|
|
1081
|
-
name: 'options',
|
|
1082
|
-
label: 'Opções (estáticas)',
|
|
1083
|
-
editorType: 'textarea',
|
|
1084
|
-
group: 'Dados/Opções',
|
|
1085
|
-
hint: 'JSON: [{ "value":1, "text":"Um" }] ou linhas: valor|rótulo. Para múltiplos valores, use multiple=true.',
|
|
1982
|
+
name: 'options',
|
|
1983
|
+
label: 'Opções (estáticas)',
|
|
1984
|
+
editorType: 'textarea',
|
|
1985
|
+
group: 'Dados/Opções',
|
|
1986
|
+
hint: 'JSON: [{ "value":1, "text":"Um" }] ou linhas: valor|rótulo. Para múltiplos valores, use multiple=true.',
|
|
1987
|
+
},
|
|
1988
|
+
{ name: 'emptyOptionText', label: 'Texto da opção vazia', editorType: 'text', group: 'Dados/Opções' },
|
|
1989
|
+
{ name: 'multiple', label: 'Múltipla seleção', editorType: 'checkbox', group: 'Dados/Opções', row: 'dados.inline1', inline: true, hint: 'Permite selecionar mais de um item.' },
|
|
1990
|
+
{ name: 'optionLabelKey', label: 'Chave do rótulo (remoto)', editorType: 'text', group: 'Dados/Opções', hint: 'Campo exibido ao carregar via API.' },
|
|
1991
|
+
{ name: 'optionValueKey', label: 'Chave do valor (remoto)', editorType: 'text', group: 'Dados/Opções', hint: 'Campo salvo como value vindo da API.' },
|
|
1992
|
+
{ name: 'lookupIdKey', label: 'Lookup: chave de ID', editorType: 'text', group: 'Dados/Opções', hint: 'Usado por lookup inline para identificar o item (default: id).' },
|
|
1993
|
+
{ name: 'lookupLabelKey', label: 'Lookup: chave de label', editorType: 'text', group: 'Dados/Opções', hint: 'Define qual campo textual é exibido no trigger.' },
|
|
1994
|
+
{ name: 'lookupSubtitleKey', label: 'Lookup: chave de subtítulo', editorType: 'text', group: 'Dados/Opções', hint: 'Campo secundário exibido nas opções (quando disponível).' },
|
|
1995
|
+
// 3) Fonte Remota
|
|
1996
|
+
{ name: 'resourcePath', label: 'Resource Path', editorType: 'text', group: 'Fonte Remota' },
|
|
1997
|
+
{
|
|
1998
|
+
name: 'loadOn',
|
|
1999
|
+
label: 'Carregar em',
|
|
2000
|
+
editorType: 'select',
|
|
2001
|
+
group: 'Fonte Remota',
|
|
2002
|
+
options: [
|
|
2003
|
+
{ value: 'open', text: 'Ao abrir (padrão)' },
|
|
2004
|
+
{ value: 'init', text: 'Na inicialização' },
|
|
2005
|
+
{ value: 'none', text: 'Nenhum (manual)' },
|
|
2006
|
+
],
|
|
2007
|
+
hint: "Define quando o async-select realiza a primeira busca remota",
|
|
2008
|
+
},
|
|
2009
|
+
{ name: 'filterCriteria', label: 'Critérios de filtro (JSON)', editorType: 'textarea', group: 'Fonte Remota', hint: '{ "active": true }' },
|
|
2010
|
+
// 3.1) Dependências (Cascata Nativa – Fase 1)
|
|
2011
|
+
{ name: 'dependencyFields', label: 'Campos dependentes (CSV ou JSON)', editorType: 'text', group: 'Fonte Remota', hint: "Ex.: rotaId,destinoReqId" },
|
|
2012
|
+
{ name: 'resetOnDependentChange', label: 'Resetar ao mudar dependentes', editorType: 'checkbox', group: 'Fonte Remota' },
|
|
2013
|
+
{ name: 'enableDependencyCascade', label: 'Habilitar cascata nativa', editorType: 'checkbox', group: 'Fonte Remota', hint: 'Desative quando Connections controlarem filterCriteria' },
|
|
2014
|
+
{ name: 'dependencyFilterMap', label: 'Mapeamento de filtros (JSON)', editorType: 'textarea', group: 'Fonte Remota', hint: '{ "rotaId": "rotaId", "destinoReqId": { "key": "destinoId", "valuePath": "id" } }' },
|
|
2015
|
+
{ name: 'dependencyValuePath', label: 'Value path padrão (dep.)', editorType: 'text', group: 'Fonte Remota', hint: "Ex.: 'id' (default) ou 'code'" },
|
|
2016
|
+
{
|
|
2017
|
+
name: 'dependencyMergeStrategy',
|
|
2018
|
+
label: 'Merge de filtros (dep.)',
|
|
2019
|
+
editorType: 'select',
|
|
2020
|
+
group: 'Fonte Remota',
|
|
2021
|
+
options: [
|
|
2022
|
+
{ value: 'merge', text: 'merge (padrão)' },
|
|
2023
|
+
{ value: 'replace', text: 'replace (apenas chaves mapeadas)' },
|
|
2024
|
+
],
|
|
2025
|
+
hint: 'merge preserva chaves externas; replace substitui apenas chaves mapeadas',
|
|
2026
|
+
},
|
|
2027
|
+
{ name: 'dependencyDebounceMs', label: 'Debounce (ms) dependentes', editorType: 'number', group: 'Fonte Remota', hint: 'Padrão: 150' },
|
|
2028
|
+
{
|
|
2029
|
+
name: 'dependencyLoadOnChange', label: 'Recarregar ao mudar dependentes', editorType: 'select', group: 'Fonte Remota', options: [
|
|
2030
|
+
{ value: 'respectLoadOn', text: 'Respeitar loadOn (padrão)' },
|
|
2031
|
+
{ value: 'immediate', text: 'Imediato' },
|
|
2032
|
+
{ value: 'manual', text: 'Manual (Connections disparam)' },
|
|
2033
|
+
]
|
|
2034
|
+
},
|
|
2035
|
+
// 4) Formato/Comportamento
|
|
2036
|
+
{ name: 'selectAll', label: 'Selecionar todos', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'comp.inline1', inline: true, hint: 'Exibe opção para selecionar tudo (somente multiple).' },
|
|
2037
|
+
{ name: 'searchable', label: 'Buscável', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'comp.inline1', inline: true, hint: 'Mostra campo de busca; em remoto, filtra na API.' },
|
|
2038
|
+
{ name: 'searchPlaceholder', label: 'Placeholder da busca', editorType: 'text', group: 'Formato/Comportamento', hint: 'Texto do campo de busca no popover/lista.' },
|
|
2039
|
+
{ name: 'maxSelections', label: 'Máximo de seleções', editorType: 'number', group: 'Formato/Comportamento', hint: 'Limite de itens quando multiple=true.' },
|
|
2040
|
+
{ name: 'readonly', label: 'Somente leitura', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'comp.inline2', inline: true, hint: 'Mantém o valor visível sem permitir interação.' },
|
|
2041
|
+
{ name: 'disabled', label: 'Desabilitado', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'comp.inline2', inline: true, hint: 'Desativa interação, foco e ações do trigger.' },
|
|
2042
|
+
// 5) Validação
|
|
2043
|
+
{ name: 'required', label: 'Obrigatório', editorType: 'checkbox', group: 'Validação' },
|
|
2044
|
+
{ name: 'validators.requiredMessage', label: 'Mensagem: obrigatório', editorType: 'text', group: 'Validação' },
|
|
2045
|
+
// 6) Ações (Clear)
|
|
2046
|
+
{ name: 'clearButton.enabled', label: 'Clear: habilitar', editorType: 'checkbox', group: 'Ações', row: 'clear.inline1', inline: true, hint: 'Mostra botão para limpar seleção.' },
|
|
2047
|
+
{ name: 'clearButton.icon', label: 'Clear: ícone', editorType: 'text', group: 'Ações', row: 'clear.inline2', hint: "Material Symbols (ex.: mi:clear). Use o seletor ao lado para buscar." },
|
|
2048
|
+
{ name: 'clearButton.iconColor', label: 'Clear: cor do ícone', editorType: 'color', group: 'Ações', row: 'clear.inline2', hint: 'Tema (primary/accent/warn) ou cor CSS (ex.: #496ddb, rgb(73,109,219), red).' },
|
|
2049
|
+
{ name: 'clearButton.tooltip', label: 'Clear: tooltip', editorType: 'text', group: 'Ações', row: 'clear.inline2' },
|
|
2050
|
+
{ name: 'clearButton.ariaLabel', label: 'Clear: aria-label', editorType: 'text', group: 'Ações', row: 'clear.inline2', hint: 'Obrigatório quando não houver texto visível.' },
|
|
2051
|
+
{ name: 'clearButton.showOnlyWhenFilled', label: 'Clear: só quando preenchido', editorType: 'checkbox', group: 'Ações', row: 'clear.inline1', inline: true, hint: 'Exibe o botão apenas quando há valor.' },
|
|
2052
|
+
// 7) Aparência
|
|
2053
|
+
{
|
|
2054
|
+
name: 'materialDesign.appearance',
|
|
2055
|
+
label: 'Aparência',
|
|
2056
|
+
editorType: 'select',
|
|
2057
|
+
group: 'Aparência',
|
|
2058
|
+
options: [
|
|
2059
|
+
{ value: 'fill', text: 'Fill' },
|
|
2060
|
+
{ value: 'outline', text: 'Outline' },
|
|
2061
|
+
],
|
|
2062
|
+
},
|
|
2063
|
+
{
|
|
2064
|
+
name: 'materialDesign.color',
|
|
2065
|
+
label: 'Cor do tema',
|
|
2066
|
+
editorType: 'select',
|
|
2067
|
+
group: 'Aparência',
|
|
2068
|
+
options: [
|
|
2069
|
+
{ value: 'primary', text: 'Primária' },
|
|
2070
|
+
{ value: 'accent', text: 'Acento' },
|
|
2071
|
+
{ value: 'warn', text: 'Alerta' },
|
|
2072
|
+
],
|
|
2073
|
+
},
|
|
2074
|
+
{
|
|
2075
|
+
name: 'materialDesign.floatLabel',
|
|
2076
|
+
label: 'Comportamento do label',
|
|
2077
|
+
editorType: 'select',
|
|
2078
|
+
group: 'Aparência',
|
|
2079
|
+
options: [
|
|
2080
|
+
{ value: 'auto', text: 'Auto' },
|
|
2081
|
+
{ value: 'always', text: 'Sempre' },
|
|
2082
|
+
],
|
|
2083
|
+
},
|
|
2084
|
+
{
|
|
2085
|
+
name: 'materialDesign.subscriptSizing',
|
|
2086
|
+
label: 'Subscript sizing',
|
|
2087
|
+
editorType: 'select',
|
|
2088
|
+
group: 'Aparência',
|
|
2089
|
+
options: [
|
|
2090
|
+
{ value: 'fixed', text: 'Fixo' },
|
|
2091
|
+
{ value: 'dynamic', text: 'Dinâmico' },
|
|
2092
|
+
],
|
|
2093
|
+
},
|
|
2094
|
+
{ name: 'inlineAutoSize.minWidth', label: 'Inline: largura mínima (desktop)', editorType: 'number', group: 'Aparência', row: 'appearance.inlineAutoSizeDesktop', inline: true, hint: 'Largura mínima em px para desktop.' },
|
|
2095
|
+
{ name: 'inlineAutoSize.maxWidth', label: 'Inline: largura máxima (desktop)', editorType: 'number', group: 'Aparência', row: 'appearance.inlineAutoSizeDesktop', inline: true, hint: 'Largura máxima em px para desktop.' },
|
|
2096
|
+
{ name: 'inlineAutoSize.minWidthMobile', label: 'Inline: largura mínima (mobile)', editorType: 'number', group: 'Aparência', row: 'appearance.inlineAutoSizeMobile', inline: true, hint: 'Largura mínima em px para mobile.' },
|
|
2097
|
+
{ name: 'inlineAutoSize.maxWidthMobile', label: 'Inline: largura máxima (mobile)', editorType: 'number', group: 'Aparência', row: 'appearance.inlineAutoSizeMobile', inline: true, hint: 'Largura máxima em px para mobile.' },
|
|
2098
|
+
{ name: 'materialDesign.hideRequiredMarker', label: 'Ocultar * obrigatório', editorType: 'checkbox', group: 'Aparência' },
|
|
2099
|
+
{
|
|
2100
|
+
name: 'errorStateMatcher',
|
|
2101
|
+
label: 'Estratégia de erro',
|
|
2102
|
+
editorType: 'select',
|
|
2103
|
+
group: 'Aparência',
|
|
2104
|
+
options: [
|
|
2105
|
+
{ value: 'default', text: 'Padrão' },
|
|
2106
|
+
{ value: 'showOnDirtyAndInvalid', text: 'Mostrar ao sujar e inválido' },
|
|
2107
|
+
{ value: 'showOnSubmitted', text: 'Mostrar ao enviar' },
|
|
2108
|
+
{ value: 'showImmediately', text: 'Mostrar imediatamente' },
|
|
2109
|
+
],
|
|
2110
|
+
},
|
|
2111
|
+
// 8) Acessibilidade
|
|
2112
|
+
{ name: 'ariaLabel', label: 'ARIA label', editorType: 'text', group: 'Acessibilidade', hint: 'Use quando não houver label visível.' },
|
|
2113
|
+
{ name: 'ariaDescribedby', label: 'ARIA describedby', editorType: 'text', group: 'Acessibilidade', hint: 'IDs de ajuda/erro.' },
|
|
2114
|
+
{ name: 'ariaLabelledby', label: 'ARIA labelledby', editorType: 'text', group: 'Acessibilidade', hint: 'ID de elemento que atua como label.' },
|
|
2115
|
+
{ name: 'tabIndex', label: 'TabIndex', editorType: 'number', group: 'Acessibilidade' },
|
|
2116
|
+
{ name: 'dataAttributes', label: 'Data attributes (JSON)', editorType: 'textarea', group: 'Acessibilidade', hint: '{ "testId": "select-a" }' },
|
|
2117
|
+
];
|
|
2118
|
+
|
|
2119
|
+
function cloneProperty$7(prop) {
|
|
2120
|
+
return {
|
|
2121
|
+
...prop,
|
|
2122
|
+
options: Array.isArray(prop.options)
|
|
2123
|
+
? prop.options.map((opt) => typeof opt === 'object' && opt !== null ? { ...opt } : opt)
|
|
2124
|
+
: prop.options,
|
|
2125
|
+
};
|
|
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
|
+
}
|
|
2145
|
+
const entityLookupProperties = [
|
|
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
|
+
},
|
|
2160
|
+
{
|
|
2161
|
+
name: 'optionSource.key',
|
|
2162
|
+
label: 'Option source key',
|
|
2163
|
+
editorType: 'text',
|
|
2164
|
+
group: 'Fonte de entidade',
|
|
2165
|
+
required: true,
|
|
2166
|
+
hint: 'Chave publicada pelo backend para filtrar e reidratar a entidade.',
|
|
2167
|
+
},
|
|
2168
|
+
{
|
|
2169
|
+
name: 'optionSource.type',
|
|
2170
|
+
label: 'Tipo da fonte',
|
|
2171
|
+
editorType: 'select',
|
|
2172
|
+
group: 'Fonte de entidade',
|
|
2173
|
+
defaultValue: 'RESOURCE_ENTITY',
|
|
2174
|
+
options: [
|
|
2175
|
+
{ value: 'RESOURCE_ENTITY', text: 'RESOURCE_ENTITY' },
|
|
2176
|
+
{ value: 'LIGHT_LOOKUP', text: 'LIGHT_LOOKUP' },
|
|
2177
|
+
],
|
|
2178
|
+
hint: 'Use RESOURCE_ENTITY para entidades reais de dominio.',
|
|
2179
|
+
},
|
|
2180
|
+
{
|
|
2181
|
+
name: 'optionSource.entityKey',
|
|
2182
|
+
label: 'Entity key',
|
|
2183
|
+
editorType: 'text',
|
|
2184
|
+
group: 'Fonte de entidade',
|
|
2185
|
+
hint: 'Identificador semantico da entidade, por exemplo supplier, customer ou contract.',
|
|
2186
|
+
},
|
|
2187
|
+
{
|
|
2188
|
+
name: 'optionSource.resourcePath',
|
|
2189
|
+
label: 'Resource path',
|
|
2190
|
+
editorType: 'text',
|
|
2191
|
+
group: 'Fonte de entidade',
|
|
2192
|
+
hint: 'Caminho base do recurso que publica option-sources e by-ids.',
|
|
2193
|
+
},
|
|
2194
|
+
{
|
|
2195
|
+
name: 'optionSource.filterField',
|
|
2196
|
+
label: 'Campo de filtro principal',
|
|
2197
|
+
editorType: 'text',
|
|
2198
|
+
group: 'Fonte de entidade',
|
|
2199
|
+
hint: 'Campo usado por fontes derivadas quando houver filtro simples no backend.',
|
|
2200
|
+
},
|
|
2201
|
+
{
|
|
2202
|
+
name: 'optionSource.propertyPath',
|
|
2203
|
+
label: 'Property path',
|
|
2204
|
+
editorType: 'text',
|
|
2205
|
+
group: 'Fonte de entidade',
|
|
2206
|
+
hint: 'Caminho de propriedade usado por fontes derivadas ou projeções leves.',
|
|
2207
|
+
},
|
|
2208
|
+
{
|
|
2209
|
+
name: 'optionSource.valuePropertyPath',
|
|
2210
|
+
label: 'Campo de valor',
|
|
2211
|
+
editorType: 'text',
|
|
2212
|
+
group: 'Fonte de entidade',
|
|
2213
|
+
defaultValue: 'id',
|
|
2214
|
+
hint: 'Campo canonico salvo no payload do formulario.',
|
|
2215
|
+
},
|
|
2216
|
+
{
|
|
2217
|
+
name: 'optionSource.labelPropertyPath',
|
|
2218
|
+
label: 'Campo de label',
|
|
2219
|
+
editorType: 'text',
|
|
2220
|
+
group: 'Fonte de entidade',
|
|
2221
|
+
defaultValue: 'label',
|
|
2222
|
+
hint: 'Campo principal exibido para o usuario.',
|
|
2223
|
+
},
|
|
2224
|
+
{
|
|
2225
|
+
name: 'optionSource.codePropertyPath',
|
|
2226
|
+
label: 'Campo de codigo',
|
|
2227
|
+
editorType: 'text',
|
|
2228
|
+
group: 'Exibicao de entidade',
|
|
2229
|
+
hint: 'Codigo curto exibido antes do label, por exemplo FOR-8742.',
|
|
2230
|
+
},
|
|
2231
|
+
{
|
|
2232
|
+
name: 'optionSource.descriptionPropertyPaths',
|
|
2233
|
+
label: 'Campos de descricao',
|
|
2234
|
+
editorType: 'textarea',
|
|
2235
|
+
group: 'Exibicao de entidade',
|
|
2236
|
+
hint: 'JSON array ou uma linha por campo. Ex.: ["documentNumber", "city", "state"].',
|
|
2237
|
+
},
|
|
2238
|
+
{
|
|
2239
|
+
name: 'optionSource.statusPropertyPath',
|
|
2240
|
+
label: 'Campo de status',
|
|
2241
|
+
editorType: 'text',
|
|
2242
|
+
group: 'Exibicao de entidade',
|
|
2243
|
+
hint: 'Campo que informa status operacional da entidade.',
|
|
2244
|
+
},
|
|
2245
|
+
{
|
|
2246
|
+
name: 'optionSource.disabledPropertyPath',
|
|
2247
|
+
label: 'Campo disabled',
|
|
2248
|
+
editorType: 'text',
|
|
2249
|
+
group: 'Exibicao de entidade',
|
|
2250
|
+
hint: 'Campo booleano que indica bloqueio visual ou operacional da entidade.',
|
|
2251
|
+
},
|
|
2252
|
+
{
|
|
2253
|
+
name: 'optionSource.disabledReasonPropertyPath',
|
|
2254
|
+
label: 'Campo de motivo bloqueado',
|
|
2255
|
+
editorType: 'text',
|
|
2256
|
+
group: 'Exibicao de entidade',
|
|
2257
|
+
hint: 'Campo opcional com mensagem de bloqueio quando selectable=false.',
|
|
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
|
+
},
|
|
2514
|
+
{
|
|
2515
|
+
name: 'optionSource.searchPropertyPaths',
|
|
2516
|
+
label: 'Campos de busca',
|
|
2517
|
+
editorType: 'textarea',
|
|
2518
|
+
group: 'Busca',
|
|
2519
|
+
hint: 'JSON array ou uma linha por campo pesquisavel.',
|
|
2520
|
+
},
|
|
2521
|
+
{
|
|
2522
|
+
name: 'optionSource.searchMode',
|
|
2523
|
+
label: 'Modo de busca',
|
|
2524
|
+
editorType: 'select',
|
|
2525
|
+
group: 'Busca',
|
|
2526
|
+
options: [
|
|
2527
|
+
{ value: 'contains', text: 'contains' },
|
|
2528
|
+
{ value: 'startsWith', text: 'startsWith' },
|
|
2529
|
+
{ value: 'exact', text: 'exact' },
|
|
2530
|
+
],
|
|
2531
|
+
hint: 'Semantica de busca publicada pelo backend para esta fonte.',
|
|
2532
|
+
},
|
|
2533
|
+
{
|
|
2534
|
+
name: 'optionSource.pageSize',
|
|
2535
|
+
label: 'Tamanho da pagina',
|
|
2536
|
+
editorType: 'number',
|
|
2537
|
+
group: 'Busca',
|
|
2538
|
+
hint: 'Quantidade sugerida de resultados por pagina.',
|
|
2539
|
+
},
|
|
2540
|
+
{
|
|
2541
|
+
name: 'optionSource.includeIds',
|
|
2542
|
+
label: 'Incluir IDs selecionados',
|
|
2543
|
+
editorType: 'checkbox',
|
|
2544
|
+
group: 'Busca',
|
|
2545
|
+
defaultValue: true,
|
|
2546
|
+
hint: 'Mantem entidades ja selecionadas visiveis durante busca paginada.',
|
|
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
|
+
},
|
|
2590
|
+
{
|
|
2591
|
+
name: 'optionSource.dependsOn',
|
|
2592
|
+
label: 'Dependencias',
|
|
2593
|
+
editorType: 'textarea',
|
|
2594
|
+
group: 'Dependencias',
|
|
2595
|
+
hint: 'JSON array ou uma linha por campo do formulario que filtra o lookup.',
|
|
2596
|
+
},
|
|
2597
|
+
{
|
|
2598
|
+
name: 'optionSource.dependencyFilterMap',
|
|
2599
|
+
label: 'Mapa de filtros',
|
|
2600
|
+
editorType: 'textarea',
|
|
2601
|
+
group: 'Dependencias',
|
|
2602
|
+
hint: 'JSON object. Ex.: { "empresaId": "companyId", "fornecedorId": "supplierId" }.',
|
|
2603
|
+
},
|
|
2604
|
+
{
|
|
2605
|
+
name: 'optionSource.selectionPolicy.statusPropertyPath',
|
|
2606
|
+
label: 'Politica: campo de status',
|
|
2607
|
+
editorType: 'text',
|
|
2608
|
+
group: 'Politica de selecao',
|
|
2609
|
+
hint: 'Campo usado para aplicar allowedStatuses ou blockedStatuses.',
|
|
2610
|
+
},
|
|
2611
|
+
{
|
|
2612
|
+
name: 'optionSource.selectionPolicy.selectablePropertyPath',
|
|
2613
|
+
label: 'Politica: campo selectable',
|
|
2614
|
+
editorType: 'text',
|
|
2615
|
+
group: 'Politica de selecao',
|
|
2616
|
+
hint: 'Campo booleano publicado pelo backend para indicar se a entidade pode ser selecionada.',
|
|
2617
|
+
},
|
|
2618
|
+
{
|
|
2619
|
+
name: 'optionSource.selectionPolicy.allowedStatuses',
|
|
2620
|
+
label: 'Status permitidos',
|
|
2621
|
+
editorType: 'textarea',
|
|
2622
|
+
group: 'Politica de selecao',
|
|
2623
|
+
hint: 'JSON array ou uma linha por status. Ex.: ACTIVE, SIGNED.',
|
|
2624
|
+
},
|
|
2625
|
+
{
|
|
2626
|
+
name: 'optionSource.selectionPolicy.blockedStatuses',
|
|
2627
|
+
label: 'Status bloqueados',
|
|
2628
|
+
editorType: 'textarea',
|
|
2629
|
+
group: 'Politica de selecao',
|
|
2630
|
+
hint: 'JSON array ou uma linha por status.',
|
|
2631
|
+
},
|
|
2632
|
+
{
|
|
2633
|
+
name: 'optionSource.selectionPolicy.disabledReasonTemplate',
|
|
2634
|
+
label: 'Template de motivo bloqueado',
|
|
2635
|
+
editorType: 'text',
|
|
2636
|
+
group: 'Politica de selecao',
|
|
2637
|
+
hint: 'Mensagem template exibida quando a entidade nao pode ser selecionada.',
|
|
2638
|
+
},
|
|
2639
|
+
{
|
|
2640
|
+
name: 'optionSource.selectionPolicy.validationMessageTemplate',
|
|
2641
|
+
label: 'Template de validacao',
|
|
2642
|
+
editorType: 'text',
|
|
2643
|
+
group: 'Politica de selecao',
|
|
2644
|
+
hint: 'Mensagem template usada em validacoes de selecao.',
|
|
1086
2645
|
},
|
|
1087
|
-
{ name: 'emptyOptionText', label: 'Texto da opção vazia', editorType: 'text', group: 'Dados/Opções' },
|
|
1088
|
-
{ name: 'multiple', label: 'Múltipla seleção', editorType: 'checkbox', group: 'Dados/Opções', row: 'dados.inline1', inline: true, hint: 'Permite selecionar mais de um item.' },
|
|
1089
|
-
{ name: 'optionLabelKey', label: 'Chave do rótulo (remoto)', editorType: 'text', group: 'Dados/Opções', hint: 'Campo exibido ao carregar via API.' },
|
|
1090
|
-
{ name: 'optionValueKey', label: 'Chave do valor (remoto)', editorType: 'text', group: 'Dados/Opções', hint: 'Campo salvo como value vindo da API.' },
|
|
1091
|
-
{ name: 'lookupIdKey', label: 'Lookup: chave de ID', editorType: 'text', group: 'Dados/Opções', hint: 'Usado por lookup inline para identificar o item (default: id).' },
|
|
1092
|
-
{ name: 'lookupLabelKey', label: 'Lookup: chave de label', editorType: 'text', group: 'Dados/Opções', hint: 'Define qual campo textual é exibido no trigger.' },
|
|
1093
|
-
{ name: 'lookupSubtitleKey', label: 'Lookup: chave de subtítulo', editorType: 'text', group: 'Dados/Opções', hint: 'Campo secundário exibido nas opções (quando disponível).' },
|
|
1094
|
-
// 3) Fonte Remota
|
|
1095
|
-
{ name: 'resourcePath', label: 'Resource Path', editorType: 'text', group: 'Fonte Remota' },
|
|
1096
2646
|
{
|
|
1097
|
-
name: '
|
|
1098
|
-
label: '
|
|
2647
|
+
name: 'optionSource.selectionPolicy.allowRetainInvalidExistingValue',
|
|
2648
|
+
label: 'Manter valor invalido existente',
|
|
2649
|
+
editorType: 'checkbox',
|
|
2650
|
+
group: 'Politica de selecao',
|
|
2651
|
+
hint: 'Exibe entidade historica ja salva mesmo quando nao pode ser selecionada em novos registros.',
|
|
2652
|
+
},
|
|
2653
|
+
{
|
|
2654
|
+
name: 'optionSource.capabilities.filter',
|
|
2655
|
+
label: 'Capability: filter',
|
|
2656
|
+
editorType: 'checkbox',
|
|
2657
|
+
group: 'Capabilities',
|
|
2658
|
+
defaultValue: true,
|
|
2659
|
+
},
|
|
2660
|
+
{
|
|
2661
|
+
name: 'optionSource.capabilities.byIds',
|
|
2662
|
+
label: 'Capability: by-ids',
|
|
2663
|
+
editorType: 'checkbox',
|
|
2664
|
+
group: 'Capabilities',
|
|
2665
|
+
defaultValue: true,
|
|
2666
|
+
},
|
|
2667
|
+
{
|
|
2668
|
+
name: 'optionSource.capabilities.navigateToDetail',
|
|
2669
|
+
label: 'Capability: detalhe',
|
|
2670
|
+
editorType: 'checkbox',
|
|
2671
|
+
group: 'Capabilities',
|
|
2672
|
+
},
|
|
2673
|
+
{
|
|
2674
|
+
name: 'optionSource.capabilities.detail',
|
|
2675
|
+
label: 'Capability: endpoint de detalhe',
|
|
2676
|
+
editorType: 'checkbox',
|
|
2677
|
+
group: 'Capabilities',
|
|
2678
|
+
},
|
|
2679
|
+
{
|
|
2680
|
+
name: 'optionSource.capabilities.edit',
|
|
2681
|
+
label: 'Capability: editar',
|
|
2682
|
+
editorType: 'checkbox',
|
|
2683
|
+
group: 'Capabilities',
|
|
2684
|
+
},
|
|
2685
|
+
{
|
|
2686
|
+
name: 'optionSource.capabilities.create',
|
|
2687
|
+
label: 'Capability: criar',
|
|
2688
|
+
editorType: 'checkbox',
|
|
2689
|
+
group: 'Capabilities',
|
|
2690
|
+
},
|
|
2691
|
+
{
|
|
2692
|
+
name: 'optionSource.capabilities.multiSelect',
|
|
2693
|
+
label: 'Capability: multi-select',
|
|
2694
|
+
editorType: 'checkbox',
|
|
2695
|
+
group: 'Capabilities',
|
|
2696
|
+
},
|
|
2697
|
+
{
|
|
2698
|
+
name: 'optionSource.capabilities.recent',
|
|
2699
|
+
label: 'Capability: recentes',
|
|
2700
|
+
editorType: 'checkbox',
|
|
2701
|
+
group: 'Capabilities',
|
|
2702
|
+
},
|
|
2703
|
+
{
|
|
2704
|
+
name: 'optionSource.capabilities.favorites',
|
|
2705
|
+
label: 'Capability: favoritos',
|
|
2706
|
+
editorType: 'checkbox',
|
|
2707
|
+
group: 'Capabilities',
|
|
2708
|
+
},
|
|
2709
|
+
{
|
|
2710
|
+
name: 'optionSource.capabilities.auditSnapshot',
|
|
2711
|
+
label: 'Capability: snapshot de auditoria',
|
|
2712
|
+
editorType: 'checkbox',
|
|
2713
|
+
group: 'Capabilities',
|
|
2714
|
+
},
|
|
2715
|
+
{
|
|
2716
|
+
name: 'optionSource.detail.hrefTemplate',
|
|
2717
|
+
label: 'Template de detalhe',
|
|
2718
|
+
editorType: 'text',
|
|
2719
|
+
group: 'Navegacao',
|
|
2720
|
+
hint: 'Template de detalhe, por exemplo /contracts/{id}.',
|
|
2721
|
+
},
|
|
2722
|
+
{
|
|
2723
|
+
name: 'optionSource.detail.routeTemplate',
|
|
2724
|
+
label: 'Template de rota',
|
|
2725
|
+
editorType: 'text',
|
|
2726
|
+
group: 'Navegacao',
|
|
2727
|
+
hint: 'Template de rota de detalhe, por exemplo /contracts/:id.',
|
|
2728
|
+
},
|
|
2729
|
+
{
|
|
2730
|
+
name: 'optionSource.detail.openDetailMode',
|
|
2731
|
+
label: 'Modo de detalhe',
|
|
1099
2732
|
editorType: 'select',
|
|
1100
|
-
group: '
|
|
2733
|
+
group: 'Navegacao',
|
|
1101
2734
|
options: [
|
|
1102
|
-
{ value: '
|
|
1103
|
-
{ value: '
|
|
1104
|
-
{ value: '
|
|
2735
|
+
{ value: 'samePage', text: 'samePage' },
|
|
2736
|
+
{ value: 'newTab', text: 'newTab' },
|
|
2737
|
+
{ value: 'drawer', text: 'Drawer' },
|
|
2738
|
+
{ value: 'modal', text: 'Modal' },
|
|
1105
2739
|
],
|
|
1106
|
-
hint: "Define quando o async-select realiza a primeira busca remota",
|
|
1107
2740
|
},
|
|
1108
|
-
{ name: 'filterCriteria', label: 'Critérios de filtro (JSON)', editorType: 'textarea', group: 'Fonte Remota', hint: '{ "active": true }' },
|
|
1109
|
-
// 3.1) Dependências (Cascata Nativa – Fase 1)
|
|
1110
|
-
{ name: 'dependencyFields', label: 'Campos dependentes (CSV ou JSON)', editorType: 'text', group: 'Fonte Remota', hint: "Ex.: rotaId,destinoReqId" },
|
|
1111
|
-
{ name: 'resetOnDependentChange', label: 'Resetar ao mudar dependentes', editorType: 'checkbox', group: 'Fonte Remota' },
|
|
1112
|
-
{ name: 'enableDependencyCascade', label: 'Habilitar cascata nativa', editorType: 'checkbox', group: 'Fonte Remota', hint: 'Desative quando Connections controlarem filterCriteria' },
|
|
1113
|
-
{ name: 'dependencyFilterMap', label: 'Mapeamento de filtros (JSON)', editorType: 'textarea', group: 'Fonte Remota', hint: '{ "rotaId": "rotaId", "destinoReqId": { "key": "destinoId", "valuePath": "id" } }' },
|
|
1114
|
-
{ name: 'dependencyValuePath', label: 'Value path padrão (dep.)', editorType: 'text', group: 'Fonte Remota', hint: "Ex.: 'id' (default) ou 'code'" },
|
|
1115
2741
|
{
|
|
1116
|
-
name: '
|
|
1117
|
-
label: '
|
|
2742
|
+
name: 'optionSource.detail.kind',
|
|
2743
|
+
label: 'Tipo de detalhe',
|
|
1118
2744
|
editorType: 'select',
|
|
1119
|
-
group: '
|
|
2745
|
+
group: 'Navegacao',
|
|
1120
2746
|
options: [
|
|
1121
|
-
{ value: '
|
|
1122
|
-
{ value: '
|
|
2747
|
+
{ value: 'surface', text: 'surface' },
|
|
2748
|
+
{ value: 'route', text: 'route' },
|
|
2749
|
+
{ value: 'href', text: 'href' },
|
|
1123
2750
|
],
|
|
1124
|
-
hint: '
|
|
2751
|
+
hint: 'Use surface como contrato canonico de plataforma; route/href ficam como fallback legado.',
|
|
1125
2752
|
},
|
|
1126
|
-
{ name: 'dependencyDebounceMs', label: 'Debounce (ms) dependentes', editorType: 'number', group: 'Fonte Remota', hint: 'Padrão: 150' },
|
|
1127
2753
|
{
|
|
1128
|
-
name: '
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
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.',
|
|
1133
2759
|
},
|
|
1134
|
-
// 4) Formato/Comportamento
|
|
1135
|
-
{ name: 'selectAll', label: 'Selecionar todos', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'comp.inline1', inline: true, hint: 'Exibe opção para selecionar tudo (somente multiple).' },
|
|
1136
|
-
{ name: 'searchable', label: 'Buscável', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'comp.inline1', inline: true, hint: 'Mostra campo de busca; em remoto, filtra na API.' },
|
|
1137
|
-
{ name: 'searchPlaceholder', label: 'Placeholder da busca', editorType: 'text', group: 'Formato/Comportamento', hint: 'Texto do campo de busca no popover/lista.' },
|
|
1138
|
-
{ name: 'maxSelections', label: 'Máximo de seleções', editorType: 'number', group: 'Formato/Comportamento', hint: 'Limite de itens quando multiple=true.' },
|
|
1139
|
-
{ name: 'readonly', label: 'Somente leitura', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'comp.inline2', inline: true, hint: 'Mantém o valor visível sem permitir interação.' },
|
|
1140
|
-
{ name: 'disabled', label: 'Desabilitado', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'comp.inline2', inline: true, hint: 'Desativa interação, foco e ações do trigger.' },
|
|
1141
|
-
// 5) Validação
|
|
1142
|
-
{ name: 'required', label: 'Obrigatório', editorType: 'checkbox', group: 'Validação' },
|
|
1143
|
-
{ name: 'validators.requiredMessage', label: 'Mensagem: obrigatório', editorType: 'text', group: 'Validação' },
|
|
1144
|
-
// 6) Ações (Clear)
|
|
1145
|
-
{ name: 'clearButton.enabled', label: 'Clear: habilitar', editorType: 'checkbox', group: 'Ações', row: 'clear.inline1', inline: true, hint: 'Mostra botão para limpar seleção.' },
|
|
1146
|
-
{ name: 'clearButton.icon', label: 'Clear: ícone', editorType: 'text', group: 'Ações', row: 'clear.inline2', hint: "Material Symbols (ex.: mi:clear). Use o seletor ao lado para buscar." },
|
|
1147
|
-
{ name: 'clearButton.iconColor', label: 'Clear: cor do ícone', editorType: 'color', group: 'Ações', row: 'clear.inline2', hint: 'Tema (primary/accent/warn) ou cor CSS (ex.: #496ddb, rgb(73,109,219), red).' },
|
|
1148
|
-
{ name: 'clearButton.tooltip', label: 'Clear: tooltip', editorType: 'text', group: 'Ações', row: 'clear.inline2' },
|
|
1149
|
-
{ name: 'clearButton.ariaLabel', label: 'Clear: aria-label', editorType: 'text', group: 'Ações', row: 'clear.inline2', hint: 'Obrigatório quando não houver texto visível.' },
|
|
1150
|
-
{ name: 'clearButton.showOnlyWhenFilled', label: 'Clear: só quando preenchido', editorType: 'checkbox', group: 'Ações', row: 'clear.inline1', inline: true, hint: 'Exibe o botão apenas quando há valor.' },
|
|
1151
|
-
// 7) Aparência
|
|
1152
2760
|
{
|
|
1153
|
-
name: '
|
|
1154
|
-
label: '
|
|
2761
|
+
name: 'optionSource.detail.presentation',
|
|
2762
|
+
label: 'Apresentacao da surface',
|
|
1155
2763
|
editorType: 'select',
|
|
1156
|
-
group: '
|
|
2764
|
+
group: 'Navegacao',
|
|
1157
2765
|
options: [
|
|
1158
|
-
{ value: '
|
|
1159
|
-
{ value: '
|
|
2766
|
+
{ value: 'drawer', text: 'Drawer' },
|
|
2767
|
+
{ value: 'modal', text: 'Modal' },
|
|
1160
2768
|
],
|
|
2769
|
+
hint: 'Como o runtime deve materializar a surface de detalhe.',
|
|
1161
2770
|
},
|
|
1162
2771
|
{
|
|
1163
|
-
name: '
|
|
1164
|
-
label: '
|
|
1165
|
-
editorType: '
|
|
1166
|
-
group: '
|
|
1167
|
-
|
|
1168
|
-
{ value: 'primary', text: 'Primária' },
|
|
1169
|
-
{ value: 'accent', text: 'Acento' },
|
|
1170
|
-
{ value: 'warn', text: 'Alerta' },
|
|
1171
|
-
],
|
|
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.',
|
|
1172
2777
|
},
|
|
1173
2778
|
{
|
|
1174
|
-
name: '
|
|
1175
|
-
label: '
|
|
2779
|
+
name: 'optionSource.detail.mode',
|
|
2780
|
+
label: 'Modo da surface',
|
|
1176
2781
|
editorType: 'select',
|
|
1177
|
-
group: '
|
|
2782
|
+
group: 'Navegacao',
|
|
1178
2783
|
options: [
|
|
1179
|
-
{ value: '
|
|
1180
|
-
{ value: '
|
|
2784
|
+
{ value: 'view', text: 'view' },
|
|
2785
|
+
{ value: 'edit', text: 'edit' },
|
|
2786
|
+
{ value: 'create', text: 'create' },
|
|
1181
2787
|
],
|
|
2788
|
+
hint: 'Modo semantico da surface de detalhe.',
|
|
1182
2789
|
},
|
|
1183
2790
|
{
|
|
1184
|
-
name: '
|
|
1185
|
-
label: '
|
|
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',
|
|
1186
2807
|
editorType: 'select',
|
|
1187
|
-
group: '
|
|
2808
|
+
group: 'Navegacao',
|
|
1188
2809
|
options: [
|
|
1189
|
-
{ value: '
|
|
1190
|
-
{ value: '
|
|
2810
|
+
{ value: 'samePage', text: 'samePage' },
|
|
2811
|
+
{ value: 'newTab', text: 'newTab' },
|
|
2812
|
+
{ value: 'drawer', text: 'Drawer' },
|
|
2813
|
+
{ value: 'modal', text: 'Modal' },
|
|
1191
2814
|
],
|
|
1192
2815
|
},
|
|
1193
|
-
{ name: 'inlineAutoSize.minWidth', label: 'Inline: largura mínima (desktop)', editorType: 'number', group: 'Aparência', row: 'appearance.inlineAutoSizeDesktop', inline: true, hint: 'Largura mínima em px para desktop.' },
|
|
1194
|
-
{ name: 'inlineAutoSize.maxWidth', label: 'Inline: largura máxima (desktop)', editorType: 'number', group: 'Aparência', row: 'appearance.inlineAutoSizeDesktop', inline: true, hint: 'Largura máxima em px para desktop.' },
|
|
1195
|
-
{ name: 'inlineAutoSize.minWidthMobile', label: 'Inline: largura mínima (mobile)', editorType: 'number', group: 'Aparência', row: 'appearance.inlineAutoSizeMobile', inline: true, hint: 'Largura mínima em px para mobile.' },
|
|
1196
|
-
{ name: 'inlineAutoSize.maxWidthMobile', label: 'Inline: largura máxima (mobile)', editorType: 'number', group: 'Aparência', row: 'appearance.inlineAutoSizeMobile', inline: true, hint: 'Largura máxima em px para mobile.' },
|
|
1197
|
-
{ name: 'materialDesign.hideRequiredMarker', label: 'Ocultar * obrigatório', editorType: 'checkbox', group: 'Aparência' },
|
|
1198
2816
|
{
|
|
1199
|
-
name: '
|
|
1200
|
-
label: '
|
|
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',
|
|
1201
2831
|
editorType: 'select',
|
|
1202
|
-
group: '
|
|
2832
|
+
group: 'Dialogo avancado',
|
|
1203
2833
|
options: [
|
|
1204
|
-
{ value: '
|
|
1205
|
-
{ value: '
|
|
1206
|
-
{ value: '
|
|
1207
|
-
{ value: '
|
|
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' },
|
|
1208
2839
|
],
|
|
1209
2840
|
},
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
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',
|
|
2955
|
+
},
|
|
1216
2956
|
];
|
|
1217
2957
|
|
|
1218
2958
|
const transferListProperties = [
|
|
@@ -3134,6 +4874,18 @@ const sliderProperties = [
|
|
|
3134
4874
|
// Formato/Comportamento
|
|
3135
4875
|
{ name: 'step', label: 'Step', editorType: 'number', group: 'Formato/Comportamento', hint: 'Incremento entre valores (ex.: 1, 0.5). Combine com min/max.' },
|
|
3136
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.' },
|
|
3137
4889
|
{ name: 'vertical', label: 'Vertical', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'fmt.flags', inline: true, hint: 'Altera para eixo Y.' },
|
|
3138
4890
|
{ name: 'invert', label: 'Inverter', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'fmt.flags', inline: true, hint: 'Inverte direção (direita→esquerda).' },
|
|
3139
4891
|
{ name: 'showTicks', label: 'Ticks', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
@@ -3141,6 +4893,26 @@ const sliderProperties = [
|
|
|
3141
4893
|
{ value: true, text: 'Com ticks' },
|
|
3142
4894
|
{ value: 'auto', text: 'Auto' },
|
|
3143
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.' },
|
|
3144
4916
|
{ name: 'discrete', label: 'Discreto', editorType: 'checkbox', group: 'Formato/Comportamento', hint: 'Exibe ticks/labels fixos para valores discretos.' },
|
|
3145
4917
|
// Validação
|
|
3146
4918
|
{ name: 'min', label: 'Mínimo', editorType: 'number', group: 'Validação', row: 'val.minmax', inline: true },
|
|
@@ -3180,11 +4952,44 @@ const rangeSliderProperties = [
|
|
|
3180
4952
|
], hint: 'Escolha faixa para dois marcadores.' },
|
|
3181
4953
|
{ name: 'step', label: 'Step', editorType: 'number', group: 'Formato/Comportamento', hint: 'Incremento entre valores (ex.: 1, 0.5). Combine com min/max.' },
|
|
3182
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.' },
|
|
3183
4967
|
{ name: 'showTicks', label: 'Ticks', editorType: 'select', group: 'Formato/Comportamento', options: [
|
|
3184
4968
|
{ value: false, text: 'Sem ticks' },
|
|
3185
4969
|
{ value: true, text: 'Com ticks' },
|
|
3186
4970
|
{ value: 'auto', text: 'Auto' },
|
|
3187
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.' },
|
|
3188
4993
|
{ name: 'discrete', label: 'Discreto', editorType: 'checkbox', group: 'Formato/Comportamento', hint: 'Mostra valor fixo por tick; use com showTicks.' },
|
|
3189
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.' },
|
|
3190
4995
|
{ name: 'disabled', label: 'Desabilitado', editorType: 'checkbox', group: 'Formato/Comportamento', row: 'comp.state', inline: true, hint: 'Desativa arraste, foco e presets rápidos.' },
|
|
@@ -5323,7 +7128,7 @@ const INLINE_FILTER_EDITOR_PROPERTIES_BY_CONTROL_TYPE = Object.freeze({
|
|
|
5323
7128
|
[INLINE_FILTER_CONTROL_TYPES.SELECT]: selectProperties,
|
|
5324
7129
|
[INLINE_FILTER_CONTROL_TYPES.SEARCHABLE_SELECT]: selectProperties,
|
|
5325
7130
|
[INLINE_FILTER_CONTROL_TYPES.ASYNC_SELECT]: selectProperties,
|
|
5326
|
-
[INLINE_FILTER_CONTROL_TYPES.ENTITY_LOOKUP]:
|
|
7131
|
+
[INLINE_FILTER_CONTROL_TYPES.ENTITY_LOOKUP]: entityLookupProperties,
|
|
5327
7132
|
[INLINE_FILTER_CONTROL_TYPES.AUTOCOMPLETE]: selectProperties,
|
|
5328
7133
|
[INLINE_FILTER_CONTROL_TYPES.MULTI_SELECT]: selectProperties,
|
|
5329
7134
|
[INLINE_FILTER_CONTROL_TYPES.INPUT]: inputProperties,
|
|
@@ -5366,6 +7171,7 @@ class FieldMetadataEditorComponent {
|
|
|
5366
7171
|
cancel = new EventEmitter();
|
|
5367
7172
|
form;
|
|
5368
7173
|
normalizedProps = [];
|
|
7174
|
+
renderedProps = [];
|
|
5369
7175
|
initialSnapshot = null;
|
|
5370
7176
|
baselineReady = false;
|
|
5371
7177
|
// Lista de tipos suportados no editor (curada para os tipos com config disponível)
|
|
@@ -5380,10 +7186,12 @@ class FieldMetadataEditorComponent {
|
|
|
5380
7186
|
{ value: FieldControlType.URL_INPUT, label: 'URL' },
|
|
5381
7187
|
{ value: FieldControlType.SEARCH_INPUT, label: 'Busca' },
|
|
5382
7188
|
{ value: FieldControlType.PHONE, label: 'Telefone' },
|
|
7189
|
+
{ value: FieldControlType.ARRAY_INPUT, label: 'Colecao editavel' },
|
|
5383
7190
|
{ value: FieldControlType.SELECT, label: 'Seleção (Select)' },
|
|
5384
7191
|
{ value: FieldControlType.MULTI_SELECT, label: 'Seleção múltipla' },
|
|
5385
7192
|
{ value: FieldControlType.SEARCHABLE_SELECT, label: 'Seleção buscável' },
|
|
5386
7193
|
{ value: FieldControlType.ASYNC_SELECT, label: 'Seleção assíncrona' },
|
|
7194
|
+
{ value: FieldControlType.ENTITY_LOOKUP, label: 'Entity Lookup' },
|
|
5387
7195
|
{ value: FieldControlType.AUTO_COMPLETE, label: 'Auto-completar' },
|
|
5388
7196
|
{ value: FieldControlType.SELECTION_LIST, label: 'Lista de seleção' },
|
|
5389
7197
|
{ value: FieldControlType.TREE_SELECT, label: 'Árvore (Tree Select)' },
|
|
@@ -5471,9 +7279,12 @@ class FieldMetadataEditorComponent {
|
|
|
5471
7279
|
return inlineProps;
|
|
5472
7280
|
if (controlType === FieldControlType.INPUT)
|
|
5473
7281
|
return inputProperties;
|
|
7282
|
+
if (controlType === FieldControlType.ARRAY_INPUT)
|
|
7283
|
+
return arrayProperties;
|
|
7284
|
+
if (controlTypeToken === 'entitylookup')
|
|
7285
|
+
return entityLookupProperties;
|
|
5474
7286
|
if (controlTypeToken === 'searchableselect' ||
|
|
5475
|
-
controlTypeToken === 'asyncselect'
|
|
5476
|
-
controlTypeToken === 'entitylookup')
|
|
7287
|
+
controlTypeToken === 'asyncselect')
|
|
5477
7288
|
return selectProperties;
|
|
5478
7289
|
if (controlType === FieldControlType.NUMERIC_TEXT_BOX)
|
|
5479
7290
|
return numberProperties;
|
|
@@ -5518,6 +7329,8 @@ class FieldMetadataEditorComponent {
|
|
|
5518
7329
|
controlType === FieldControlType.AUTO_COMPLETE ||
|
|
5519
7330
|
controlType === FieldControlType.SELECTION_LIST)
|
|
5520
7331
|
return selectProperties;
|
|
7332
|
+
if (controlType === FieldControlType.ENTITY_LOOKUP)
|
|
7333
|
+
return entityLookupProperties;
|
|
5521
7334
|
if (controlType === FieldControlType.TEXTAREA)
|
|
5522
7335
|
return textareaProperties;
|
|
5523
7336
|
if (controlType === FieldControlType.TRANSFER_LIST)
|
|
@@ -5594,11 +7407,14 @@ class FieldMetadataEditorComponent {
|
|
|
5594
7407
|
this.registry.register(this.controlType, inputProperties);
|
|
5595
7408
|
props = inputProperties;
|
|
5596
7409
|
}
|
|
7410
|
+
else if (controlTypeToken === 'entitylookup') {
|
|
7411
|
+
this.registry.register(this.controlType, entityLookupProperties);
|
|
7412
|
+
props = entityLookupProperties;
|
|
7413
|
+
}
|
|
5597
7414
|
else if (
|
|
5598
7415
|
// Aliases for select-like
|
|
5599
7416
|
controlTypeToken === 'searchableselect' ||
|
|
5600
|
-
controlTypeToken === 'asyncselect'
|
|
5601
|
-
controlTypeToken === 'entitylookup') {
|
|
7417
|
+
controlTypeToken === 'asyncselect') {
|
|
5602
7418
|
this.registry.register(this.controlType, selectProperties);
|
|
5603
7419
|
props = selectProperties;
|
|
5604
7420
|
}
|
|
@@ -5683,6 +7499,10 @@ class FieldMetadataEditorComponent {
|
|
|
5683
7499
|
this.registry.register(this.controlType, selectProperties);
|
|
5684
7500
|
props = selectProperties;
|
|
5685
7501
|
}
|
|
7502
|
+
else if (this.controlType === FieldControlType.ENTITY_LOOKUP) {
|
|
7503
|
+
this.registry.register(this.controlType, entityLookupProperties);
|
|
7504
|
+
props = entityLookupProperties;
|
|
7505
|
+
}
|
|
5686
7506
|
else if (this.controlType === FieldControlType.TEXTAREA) {
|
|
5687
7507
|
this.registry.register(this.controlType, textareaProperties);
|
|
5688
7508
|
props = textareaProperties;
|
|
@@ -5814,6 +7634,7 @@ class FieldMetadataEditorComponent {
|
|
|
5814
7634
|
}
|
|
5815
7635
|
}
|
|
5816
7636
|
catch { }
|
|
7637
|
+
this.renderedProps = this.buildRenderedProps(this.normalizedProps);
|
|
5817
7638
|
// Register contextual validators for INPUT/TEXTAREA (minLength <= maxLength)
|
|
5818
7639
|
if (this.controlType === FieldControlType.INPUT ||
|
|
5819
7640
|
this.controlType === FieldControlType.TEXTAREA) {
|
|
@@ -5844,7 +7665,7 @@ class FieldMetadataEditorComponent {
|
|
|
5844
7665
|
const name = p?.name;
|
|
5845
7666
|
if (!name)
|
|
5846
7667
|
continue;
|
|
5847
|
-
const val = getByPath(this.seed, name);
|
|
7668
|
+
const val = this.toEditorControlValue(p, getByPath(this.seed, name));
|
|
5848
7669
|
// Preserve false/0; skip only null/undefined
|
|
5849
7670
|
if (val !== undefined && val !== null) {
|
|
5850
7671
|
this.factory.setValueByPath(this.form, name, val);
|
|
@@ -6028,6 +7849,9 @@ class FieldMetadataEditorComponent {
|
|
|
6028
7849
|
});
|
|
6029
7850
|
}
|
|
6030
7851
|
apply() {
|
|
7852
|
+
if (!this.canEmitSettingsValue()) {
|
|
7853
|
+
return;
|
|
7854
|
+
}
|
|
6031
7855
|
const raw = this.factory.extractPatch(this.form);
|
|
6032
7856
|
const patch = this.buildDeltaPatch(raw, this.initialSnapshot);
|
|
6033
7857
|
this.normalizeKnownTextareaPatchValues(patch);
|
|
@@ -6055,6 +7879,9 @@ class FieldMetadataEditorComponent {
|
|
|
6055
7879
|
}
|
|
6056
7880
|
// Settings Panel value provider API
|
|
6057
7881
|
getSettingsValue() {
|
|
7882
|
+
if (!this.canEmitSettingsValue()) {
|
|
7883
|
+
return {};
|
|
7884
|
+
}
|
|
6058
7885
|
const raw = this.factory.extractPatch(this.form);
|
|
6059
7886
|
const patch = this.buildDeltaPatch(raw, this.initialSnapshot);
|
|
6060
7887
|
this.normalizeKnownTextareaPatchValues(patch);
|
|
@@ -6148,13 +7975,134 @@ class FieldMetadataEditorComponent {
|
|
|
6148
7975
|
patch.inlineTimeTrackShifts = parsedTrackShifts;
|
|
6149
7976
|
}
|
|
6150
7977
|
}
|
|
6151
|
-
if (typeof patch.links === 'string') {
|
|
6152
|
-
const parsedLinks = this.parseArrayTextarea(patch.links);
|
|
6153
|
-
if (parsedLinks) {
|
|
6154
|
-
patch.links = parsedLinks;
|
|
7978
|
+
if (typeof patch.links === 'string') {
|
|
7979
|
+
const parsedLinks = this.parseArrayTextarea(patch.links);
|
|
7980
|
+
if (parsedLinks) {
|
|
7981
|
+
patch.links = parsedLinks;
|
|
7982
|
+
}
|
|
7983
|
+
}
|
|
7984
|
+
this.normalizeEntityLookupPatchValues(patch);
|
|
7985
|
+
this.normalizeArrayPatchValues(patch);
|
|
7986
|
+
}
|
|
7987
|
+
normalizeEntityLookupPatchValues(patch) {
|
|
7988
|
+
const optionSource = patch?.optionSource;
|
|
7989
|
+
if (optionSource && typeof optionSource === 'object') {
|
|
7990
|
+
this.normalizeStringListProperty(optionSource, 'descriptionPropertyPaths');
|
|
7991
|
+
this.normalizeStringListProperty(optionSource, 'searchPropertyPaths');
|
|
7992
|
+
this.normalizeStringListProperty(optionSource, 'dependsOn');
|
|
7993
|
+
this.normalizeJsonObjectProperty(optionSource, 'dependencyFilterMap');
|
|
7994
|
+
this.normalizeJsonObjectProperty(optionSource, 'create');
|
|
7995
|
+
const selectionPolicy = optionSource.selectionPolicy;
|
|
7996
|
+
if (selectionPolicy && typeof selectionPolicy === 'object') {
|
|
7997
|
+
this.normalizeStringListProperty(selectionPolicy, 'allowedStatuses');
|
|
7998
|
+
this.normalizeStringListProperty(selectionPolicy, 'blockedStatuses');
|
|
7999
|
+
}
|
|
8000
|
+
const filtering = optionSource.filtering;
|
|
8001
|
+
if (filtering && typeof filtering === 'object') {
|
|
8002
|
+
this.normalizeJsonArrayProperty(filtering, 'availableFilters');
|
|
8003
|
+
this.normalizeJsonObjectProperty(filtering, 'defaultFilters');
|
|
8004
|
+
this.normalizeJsonArrayProperty(filtering, 'sortOptions');
|
|
8005
|
+
this.normalizeStringListProperty(filtering, 'quickFilterFields');
|
|
8006
|
+
}
|
|
8007
|
+
const display = optionSource.display;
|
|
8008
|
+
if (display && typeof display === 'object') {
|
|
8009
|
+
this.normalizeJsonArrayProperty(display, 'fields');
|
|
8010
|
+
this.normalizeStringListProperty(display, 'secondaryPropertyPaths');
|
|
8011
|
+
this.normalizeStringListProperty(display, 'badgePropertyPaths');
|
|
8012
|
+
this.normalizeStringListProperty(display, 'badgeKeys');
|
|
8013
|
+
this.normalizeJsonObjectProperty(display, 'statusToneMap');
|
|
8014
|
+
}
|
|
8015
|
+
}
|
|
8016
|
+
this.normalizeStringListProperty(patch, 'badgeKeys');
|
|
8017
|
+
this.normalizeJsonObjectProperty(patch, 'statusToneMap');
|
|
8018
|
+
const dialog = patch?.dialog;
|
|
8019
|
+
if (dialog && typeof dialog === 'object') {
|
|
8020
|
+
this.normalizeJsonArrayProperty(dialog, 'resultColumns');
|
|
8021
|
+
}
|
|
8022
|
+
}
|
|
8023
|
+
normalizeStringListProperty(target, key) {
|
|
8024
|
+
if (!target || typeof target !== 'object' || typeof target[key] !== 'string') {
|
|
8025
|
+
return;
|
|
8026
|
+
}
|
|
8027
|
+
const parsed = this.parseStringListTextarea(target[key]);
|
|
8028
|
+
if (parsed) {
|
|
8029
|
+
target[key] = parsed;
|
|
8030
|
+
}
|
|
8031
|
+
}
|
|
8032
|
+
normalizeJsonObjectProperty(target, key) {
|
|
8033
|
+
if (!target || typeof target !== 'object' || typeof target[key] !== 'string') {
|
|
8034
|
+
return;
|
|
8035
|
+
}
|
|
8036
|
+
const parsed = this.tryParseJson(String(target[key]).trim());
|
|
8037
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
8038
|
+
target[key] = parsed;
|
|
8039
|
+
}
|
|
8040
|
+
}
|
|
8041
|
+
normalizeJsonArrayProperty(target, key) {
|
|
8042
|
+
if (!target || typeof target !== 'object' || typeof target[key] !== 'string') {
|
|
8043
|
+
return;
|
|
8044
|
+
}
|
|
8045
|
+
const parsed = this.parseArrayTextarea(target[key]);
|
|
8046
|
+
if (parsed) {
|
|
8047
|
+
target[key] = parsed;
|
|
8048
|
+
}
|
|
8049
|
+
}
|
|
8050
|
+
normalizeArrayPatchValues(patch) {
|
|
8051
|
+
const arrayPatch = patch?.array;
|
|
8052
|
+
if (!arrayPatch || typeof arrayPatch !== 'object') {
|
|
8053
|
+
return;
|
|
8054
|
+
}
|
|
8055
|
+
const collectionValidation = arrayPatch.collectionValidation;
|
|
8056
|
+
if (collectionValidation &&
|
|
8057
|
+
typeof collectionValidation === 'object' &&
|
|
8058
|
+
typeof collectionValidation.uniqueBy === 'string') {
|
|
8059
|
+
const parsedUniqueBy = this.parseStringListTextarea(collectionValidation.uniqueBy);
|
|
8060
|
+
if (parsedUniqueBy) {
|
|
8061
|
+
collectionValidation.uniqueBy = parsedUniqueBy;
|
|
8062
|
+
}
|
|
8063
|
+
}
|
|
8064
|
+
this.normalizeArrayCountRuleValue(collectionValidation?.exactlyOne);
|
|
8065
|
+
this.normalizeArrayCountRuleValue(collectionValidation?.atLeastOne);
|
|
8066
|
+
const itemSchema = arrayPatch.itemSchema;
|
|
8067
|
+
if (itemSchema &&
|
|
8068
|
+
typeof itemSchema === 'object' &&
|
|
8069
|
+
typeof itemSchema.fields === 'string') {
|
|
8070
|
+
const parsedFields = this.parseArrayTextarea(itemSchema.fields);
|
|
8071
|
+
if (parsedFields) {
|
|
8072
|
+
itemSchema.fields = parsedFields;
|
|
8073
|
+
}
|
|
8074
|
+
}
|
|
8075
|
+
if (typeof patch.defaultValue === 'string') {
|
|
8076
|
+
const parsedDefaultValue = this.parseArrayTextarea(patch.defaultValue);
|
|
8077
|
+
if (parsedDefaultValue) {
|
|
8078
|
+
patch.defaultValue = parsedDefaultValue;
|
|
6155
8079
|
}
|
|
6156
8080
|
}
|
|
6157
8081
|
}
|
|
8082
|
+
normalizeArrayCountRuleValue(rule) {
|
|
8083
|
+
if (!rule || typeof rule !== 'object' || typeof rule.value !== 'string') {
|
|
8084
|
+
return;
|
|
8085
|
+
}
|
|
8086
|
+
const raw = rule.value.trim();
|
|
8087
|
+
if (!raw) {
|
|
8088
|
+
return;
|
|
8089
|
+
}
|
|
8090
|
+
try {
|
|
8091
|
+
rule.value = JSON.parse(raw);
|
|
8092
|
+
}
|
|
8093
|
+
catch {
|
|
8094
|
+
rule.value = raw;
|
|
8095
|
+
}
|
|
8096
|
+
}
|
|
8097
|
+
toEditorControlValue(prop, value) {
|
|
8098
|
+
if (prop?.editorType === 'textarea' &&
|
|
8099
|
+
value !== undefined &&
|
|
8100
|
+
value !== null &&
|
|
8101
|
+
typeof value !== 'string') {
|
|
8102
|
+
return JSON.stringify(value, null, 2);
|
|
8103
|
+
}
|
|
8104
|
+
return value;
|
|
8105
|
+
}
|
|
6158
8106
|
/**
|
|
6159
8107
|
* Interpreta textarea como lista simples de strings:
|
|
6160
8108
|
* - JSON array de strings
|
|
@@ -6207,7 +8155,11 @@ class FieldMetadataEditorComponent {
|
|
|
6207
8155
|
}
|
|
6208
8156
|
}
|
|
6209
8157
|
reset() {
|
|
6210
|
-
this.
|
|
8158
|
+
const snapshot = this.cloneSnapshot(this.initialSnapshot ?? this.form.getRawValue());
|
|
8159
|
+
this.form.reset(snapshot);
|
|
8160
|
+
this.form.markAsPristine();
|
|
8161
|
+
this.form.markAsUntouched();
|
|
8162
|
+
this.form.updateValueAndValidity({ emitEvent: true });
|
|
6211
8163
|
this.isDirty$.next(false);
|
|
6212
8164
|
}
|
|
6213
8165
|
onControlTypeChange(next) {
|
|
@@ -6225,30 +8177,167 @@ class FieldMetadataEditorComponent {
|
|
|
6225
8177
|
const missingSharedProps = fieldSubmitProperties.filter((prop) => !propNames.has(prop.name));
|
|
6226
8178
|
return missingSharedProps.length ? [...props, ...missingSharedProps] : props;
|
|
6227
8179
|
}
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
8180
|
+
get isArrayEditor() {
|
|
8181
|
+
return this.controlType === FieldControlType.ARRAY_INPUT;
|
|
8182
|
+
}
|
|
8183
|
+
buildRenderedProps(props) {
|
|
8184
|
+
if (!this.isArrayEditor) {
|
|
8185
|
+
return props;
|
|
8186
|
+
}
|
|
8187
|
+
return (props || []).filter((prop) => prop?.name !== 'array.itemSchema.fields');
|
|
8188
|
+
}
|
|
8189
|
+
cloneSnapshot(value) {
|
|
8190
|
+
return this.cloneMetadataValue(value, new WeakMap());
|
|
8191
|
+
}
|
|
8192
|
+
cloneMetadataValue(value, seen) {
|
|
8193
|
+
if (!value || typeof value !== 'object') {
|
|
8194
|
+
return value;
|
|
8195
|
+
}
|
|
8196
|
+
if (seen.has(value)) {
|
|
8197
|
+
return seen.get(value);
|
|
8198
|
+
}
|
|
8199
|
+
if (value instanceof Date) {
|
|
8200
|
+
return new Date(value.getTime());
|
|
8201
|
+
}
|
|
8202
|
+
if (value instanceof RegExp) {
|
|
8203
|
+
return new RegExp(value.source, value.flags);
|
|
8204
|
+
}
|
|
8205
|
+
if (Array.isArray(value)) {
|
|
8206
|
+
const clone = [];
|
|
8207
|
+
seen.set(value, clone);
|
|
8208
|
+
clone.push(...value.map((entry) => this.cloneMetadataValue(entry, seen)));
|
|
8209
|
+
return clone;
|
|
8210
|
+
}
|
|
8211
|
+
if (value instanceof Map) {
|
|
8212
|
+
const clone = new Map();
|
|
8213
|
+
seen.set(value, clone);
|
|
8214
|
+
value.forEach((entry, key) => {
|
|
8215
|
+
clone.set(this.cloneMetadataValue(key, seen), this.cloneMetadataValue(entry, seen));
|
|
8216
|
+
});
|
|
8217
|
+
return clone;
|
|
8218
|
+
}
|
|
8219
|
+
if (value instanceof Set) {
|
|
8220
|
+
const clone = new Set();
|
|
8221
|
+
seen.set(value, clone);
|
|
8222
|
+
value.forEach((entry) => clone.add(this.cloneMetadataValue(entry, seen)));
|
|
8223
|
+
return clone;
|
|
8224
|
+
}
|
|
8225
|
+
const prototype = Object.getPrototypeOf(value);
|
|
8226
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
8227
|
+
return value;
|
|
8228
|
+
}
|
|
8229
|
+
const clone = {};
|
|
8230
|
+
seen.set(value, clone);
|
|
8231
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
8232
|
+
clone[key] = this.cloneMetadataValue(entry, seen);
|
|
8233
|
+
}
|
|
8234
|
+
return clone;
|
|
8235
|
+
}
|
|
8236
|
+
canEmitSettingsValue() {
|
|
8237
|
+
this.validateArrayItemSchemaFields();
|
|
8238
|
+
this.form.updateValueAndValidity({ emitEvent: true });
|
|
8239
|
+
const valid = this.form.valid;
|
|
8240
|
+
this.isValid$.next(valid);
|
|
8241
|
+
if (!valid) {
|
|
8242
|
+
this.form.markAllAsTouched();
|
|
8243
|
+
}
|
|
8244
|
+
return valid;
|
|
8245
|
+
}
|
|
8246
|
+
validateArrayItemSchemaFields() {
|
|
8247
|
+
if (!this.isArrayEditor) {
|
|
8248
|
+
return;
|
|
8249
|
+
}
|
|
8250
|
+
const control = this.form.get('array.itemSchema.fields');
|
|
8251
|
+
if (!control) {
|
|
8252
|
+
return;
|
|
8253
|
+
}
|
|
8254
|
+
const value = control.value;
|
|
8255
|
+
const fields = Array.isArray(value)
|
|
8256
|
+
? value
|
|
8257
|
+
: typeof value === 'string' && value.trim()
|
|
8258
|
+
? this.parseArrayItemFields(value)
|
|
8259
|
+
: [];
|
|
8260
|
+
const seen = new Set();
|
|
8261
|
+
const duplicated = new Set();
|
|
8262
|
+
let hasEmptyName = false;
|
|
8263
|
+
for (const field of fields) {
|
|
8264
|
+
const name = String(field?.name || '').trim();
|
|
8265
|
+
if (!name) {
|
|
8266
|
+
hasEmptyName = true;
|
|
8267
|
+
continue;
|
|
8268
|
+
}
|
|
8269
|
+
if (seen.has(name)) {
|
|
8270
|
+
duplicated.add(name);
|
|
8271
|
+
}
|
|
8272
|
+
seen.add(name);
|
|
8273
|
+
}
|
|
8274
|
+
this.setControlError(control, 'arrayFieldNameRequired', hasEmptyName);
|
|
8275
|
+
this.setControlError(control, 'arrayFieldNameDuplicated', duplicated.size ? { names: Array.from(duplicated) } : null);
|
|
8276
|
+
}
|
|
8277
|
+
parseArrayItemFields(value) {
|
|
8278
|
+
try {
|
|
8279
|
+
const parsed = JSON.parse(value);
|
|
8280
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
8281
|
+
}
|
|
8282
|
+
catch {
|
|
8283
|
+
return [];
|
|
8284
|
+
}
|
|
8285
|
+
}
|
|
8286
|
+
setControlError(control, key, value) {
|
|
8287
|
+
const errors = { ...(control.errors || {}) };
|
|
8288
|
+
const hasError = value !== null && value !== undefined && value !== false;
|
|
8289
|
+
if (hasError) {
|
|
8290
|
+
errors[key] = value === true ? true : value;
|
|
8291
|
+
}
|
|
8292
|
+
else {
|
|
8293
|
+
delete errors[key];
|
|
8294
|
+
}
|
|
8295
|
+
control.setErrors(Object.keys(errors).length ? errors : null);
|
|
8296
|
+
}
|
|
8297
|
+
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 });
|
|
8298
|
+
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: `
|
|
8299
|
+
@if (form) {
|
|
8300
|
+
<div class="p-3">
|
|
8301
|
+
<praxis-dynamic-editor-renderer
|
|
8302
|
+
[properties]="renderedProps"
|
|
8303
|
+
[form]="form"
|
|
8304
|
+
></praxis-dynamic-editor-renderer>
|
|
8305
|
+
@if (isArrayEditor) {
|
|
8306
|
+
<praxis-array-item-schema-fields-editor
|
|
8307
|
+
[control]="form.get('array.itemSchema.fields')"
|
|
8308
|
+
[arrayControl]="form.get('array')"
|
|
8309
|
+
></praxis-array-item-schema-fields-editor>
|
|
8310
|
+
}
|
|
8311
|
+
</div>
|
|
8312
|
+
}
|
|
8313
|
+
`, 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"] }] });
|
|
6237
8314
|
}
|
|
6238
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
8315
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: FieldMetadataEditorComponent, decorators: [{
|
|
6239
8316
|
type: Component,
|
|
6240
8317
|
args: [{
|
|
6241
8318
|
selector: 'praxis-field-metadata-editor',
|
|
6242
8319
|
standalone: true,
|
|
6243
|
-
imports: [
|
|
8320
|
+
imports: [
|
|
8321
|
+
ReactiveFormsModule,
|
|
8322
|
+
DynamicEditorRendererComponent,
|
|
8323
|
+
ArrayItemSchemaFieldsEditorComponent
|
|
8324
|
+
],
|
|
6244
8325
|
template: `
|
|
6245
|
-
|
|
6246
|
-
<
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
8326
|
+
@if (form) {
|
|
8327
|
+
<div class="p-3">
|
|
8328
|
+
<praxis-dynamic-editor-renderer
|
|
8329
|
+
[properties]="renderedProps"
|
|
8330
|
+
[form]="form"
|
|
8331
|
+
></praxis-dynamic-editor-renderer>
|
|
8332
|
+
@if (isArrayEditor) {
|
|
8333
|
+
<praxis-array-item-schema-fields-editor
|
|
8334
|
+
[control]="form.get('array.itemSchema.fields')"
|
|
8335
|
+
[arrayControl]="form.get('array')"
|
|
8336
|
+
></praxis-array-item-schema-fields-editor>
|
|
8337
|
+
}
|
|
8338
|
+
</div>
|
|
8339
|
+
}
|
|
8340
|
+
`,
|
|
6252
8341
|
}]
|
|
6253
8342
|
}], ctorParameters: () => [{ type: ConfigRegistryService }, { type: SchemaNormalizerService }, { type: DynamicFormFactoryService }, { type: ContextValidatorRegistryService }], propDecorators: { controlType: [{
|
|
6254
8343
|
type: Input
|
|
@@ -6267,15 +8356,19 @@ class CascadeRulesService {
|
|
|
6267
8356
|
return map;
|
|
6268
8357
|
}
|
|
6269
8358
|
hydrateRule(field) {
|
|
6270
|
-
const deps = field.dependencyFields
|
|
8359
|
+
const deps = (field.dependencyFields ??
|
|
8360
|
+
field.optionSource?.dependsOn);
|
|
6271
8361
|
if (!deps || deps.length === 0)
|
|
6272
8362
|
return null;
|
|
8363
|
+
const dependencyFilterMap = field.dependencyFilterMap ??
|
|
8364
|
+
field.optionSource?.dependencyFilterMap ??
|
|
8365
|
+
null;
|
|
6273
8366
|
const vm = {
|
|
6274
8367
|
targetField: field.name,
|
|
6275
8368
|
dependencyFields: [...deps],
|
|
6276
8369
|
enableDependencyCascade: field.enableDependencyCascade === false ? false : true,
|
|
6277
8370
|
resetOnDependentChange: !!field.resetOnDependentChange,
|
|
6278
|
-
dependencyFilterMap
|
|
8371
|
+
dependencyFilterMap,
|
|
6279
8372
|
dependencyValuePath: field.dependencyValuePath || null,
|
|
6280
8373
|
dependencyMergeStrategy: field.dependencyMergeStrategy || 'merge',
|
|
6281
8374
|
dependencyDebounceMs: field.dependencyDebounceMs != null
|
|
@@ -6311,10 +8404,10 @@ class CascadeRulesService {
|
|
|
6311
8404
|
dependencyValuePath: rule.dependencyValuePath || inferredValueKey || 'id',
|
|
6312
8405
|
};
|
|
6313
8406
|
}
|
|
6314
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
6315
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
8407
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CascadeRulesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
8408
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CascadeRulesService, providedIn: 'root' });
|
|
6316
8409
|
}
|
|
6317
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
8410
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CascadeRulesService, decorators: [{
|
|
6318
8411
|
type: Injectable,
|
|
6319
8412
|
args: [{ providedIn: 'root' }]
|
|
6320
8413
|
}] });
|
|
@@ -6328,10 +8421,10 @@ class CascadeManagerTabComponent {
|
|
|
6328
8421
|
searchTerm = '';
|
|
6329
8422
|
searchTimer = null;
|
|
6330
8423
|
searchValue = '';
|
|
6331
|
-
rules = signal(new Map(), ...(ngDevMode ? [{ debugName: "rules" }] : []));
|
|
6332
|
-
selectedFieldSig = signal(null, ...(ngDevMode ? [{ debugName: "selectedFieldSig" }] : []));
|
|
8424
|
+
rules = signal(new Map(), ...(ngDevMode ? [{ debugName: "rules" }] : /* istanbul ignore next */ []));
|
|
8425
|
+
selectedFieldSig = signal(null, ...(ngDevMode ? [{ debugName: "selectedFieldSig" }] : /* istanbul ignore next */ []));
|
|
6333
8426
|
// Inline editor state (MVP)
|
|
6334
|
-
editing = signal(false, ...(ngDevMode ? [{ debugName: "editing" }] : []));
|
|
8427
|
+
editing = signal(false, ...(ngDevMode ? [{ debugName: "editing" }] : /* istanbul ignore next */ []));
|
|
6335
8428
|
editDepList = [];
|
|
6336
8429
|
editEnable = true;
|
|
6337
8430
|
editReset = false;
|
|
@@ -6683,8 +8776,8 @@ class CascadeManagerTabComponent {
|
|
|
6683
8776
|
}
|
|
6684
8777
|
return [...new Set(suggestions)];
|
|
6685
8778
|
}
|
|
6686
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
6687
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
8779
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CascadeManagerTabComponent, deps: [{ token: CascadeRulesService }], target: i0.ɵɵFactoryTarget.Component });
|
|
8780
|
+
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: `
|
|
6688
8781
|
<div class="cm-root">
|
|
6689
8782
|
<!-- Left: Fields list -->
|
|
6690
8783
|
<div class="cm-left">
|
|
@@ -6692,276 +8785,301 @@ class CascadeManagerTabComponent {
|
|
|
6692
8785
|
<mat-label>Buscar campos</mat-label>
|
|
6693
8786
|
<input matInput [ngModel]="searchTerm" (ngModelChange)="onSearchChange($event)">
|
|
6694
8787
|
</mat-form-field>
|
|
6695
|
-
|
|
8788
|
+
@if (fields.length <= 50) {
|
|
6696
8789
|
<div>
|
|
6697
|
-
|
|
6698
|
-
<div>
|
|
6699
|
-
<div>
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
8790
|
+
@for (f of filteredFields(); track f) {
|
|
8791
|
+
<div (click)="selectField(f.name)" class="field-item" [class.selected]="f.name===selectedField()" tabindex="0" (keydown)="onFieldKeydown($event, f.name)">
|
|
8792
|
+
<div>
|
|
8793
|
+
<div>{{ labelFor(f.name) }}</div>
|
|
8794
|
+
<small style="opacity:.7">{{ f.name }}</small>
|
|
8795
|
+
</div>
|
|
8796
|
+
@if (hasRule(f.name)) {
|
|
8797
|
+
<div class="rule-badges">
|
|
8798
|
+
@if (isCascadeEnabled(f.name)) {
|
|
8799
|
+
<span class="rule-badge b-active">Ativo</span>
|
|
8800
|
+
}
|
|
8801
|
+
@if (loadOnChange(f.name)==='manual') {
|
|
8802
|
+
<span class="rule-badge b-manual">Manual</span>
|
|
8803
|
+
}
|
|
8804
|
+
@if (loadOnChange(f.name)==='immediate') {
|
|
8805
|
+
<span class="rule-badge b-immediate">Immediate</span>
|
|
8806
|
+
}
|
|
8807
|
+
</div>
|
|
8808
|
+
}
|
|
6706
8809
|
</div>
|
|
6707
|
-
|
|
8810
|
+
}
|
|
6708
8811
|
</div>
|
|
6709
|
-
|
|
6710
|
-
<ng-template #vscroll>
|
|
8812
|
+
} @else {
|
|
6711
8813
|
<cdk-virtual-scroll-viewport itemSize="44" style="height: calc(100vh - 240px)">
|
|
6712
8814
|
<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)">
|
|
6713
8815
|
<div>
|
|
6714
8816
|
<div>{{ labelFor(f.name) }}</div>
|
|
6715
8817
|
<small style="opacity:.7">{{ f.name }}</small>
|
|
6716
8818
|
</div>
|
|
6717
|
-
|
|
6718
|
-
<
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
|
|
8819
|
+
@if (hasRule(f.name)) {
|
|
8820
|
+
<div class="rule-badges">
|
|
8821
|
+
@if (isCascadeEnabled(f.name)) {
|
|
8822
|
+
<span class="rule-badge b-active">Ativo</span>
|
|
8823
|
+
}
|
|
8824
|
+
@if (loadOnChange(f.name)==='manual') {
|
|
8825
|
+
<span class="rule-badge b-manual">Manual</span>
|
|
8826
|
+
}
|
|
8827
|
+
@if (loadOnChange(f.name)==='immediate') {
|
|
8828
|
+
<span class="rule-badge b-immediate">Immediate</span>
|
|
8829
|
+
}
|
|
8830
|
+
</div>
|
|
8831
|
+
}
|
|
6722
8832
|
</div>
|
|
6723
8833
|
</cdk-virtual-scroll-viewport>
|
|
6724
|
-
|
|
8834
|
+
}
|
|
6725
8835
|
</div>
|
|
6726
|
-
|
|
8836
|
+
|
|
6727
8837
|
<!-- Right: Rules for selected field -->
|
|
6728
|
-
|
|
6729
|
-
<div class="cm-
|
|
6730
|
-
<div>
|
|
6731
|
-
<h3 style="margin:0">{{ labelFor(sel) }}</h3>
|
|
6732
|
-
<div style="opacity:.7">{{ sel }}</div>
|
|
6733
|
-
</div>
|
|
6734
|
-
<div>
|
|
6735
|
-
<button mat-stroked-button color="primary" (click)="exportRules()" style="margin-right:8px">Exportar</button>
|
|
6736
|
-
<button mat-stroked-button (click)="toggleImport()" style="margin-right:8px">Importar</button>
|
|
6737
|
-
<button mat-flat-button color="primary" [matMenuTriggerFor]="addMenu" [disabled]="editing()">Adicionar</button>
|
|
6738
|
-
<mat-menu #addMenu="matMenu">
|
|
6739
|
-
<button mat-menu-item (click)="startEdit(sel)"><mat-icon [praxisIcon]="'add'"></mat-icon><span>Em branco</span></button>
|
|
6740
|
-
<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>
|
|
6741
|
-
<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>
|
|
6742
|
-
</mat-menu>
|
|
6743
|
-
</div>
|
|
6744
|
-
</div>
|
|
6745
|
-
|
|
6746
|
-
<!-- Conflict banner (connections) -->
|
|
6747
|
-
<div class="banner" *ngIf="hasConflict(sel)">
|
|
6748
|
-
<mat-icon [praxisIcon]="'link_off'"></mat-icon>
|
|
6749
|
-
<div>
|
|
6750
|
-
Connections atualizam este campo (filterCriteria). Considere desativar a cascata nativa ou usar ‘manual’.
|
|
6751
|
-
</div>
|
|
6752
|
-
<span class="spacer"></span>
|
|
6753
|
-
<button mat-stroked-button color="primary" (click)="quickSet(sel,'disable')">Desativar</button>
|
|
6754
|
-
<button mat-stroked-button color="accent" (click)="quickSet(sel,'manual')">Definir manual</button>
|
|
6755
|
-
</div>
|
|
6756
|
-
|
|
6757
|
-
<div *ngIf="importMode" class="import-panel">
|
|
6758
|
-
<div style="display:flex; gap:8px; align-items:center; margin-bottom:8px"><mat-icon [praxisIcon]="'upload'"></mat-icon><strong>Importar regras (JSON)</strong></div>
|
|
6759
|
-
<textarea matInput rows="6" class="full" [(ngModel)]="importJson" placeholder='{ "campo": { "dependencyFields": ["dep"], ... } }'></textarea>
|
|
6760
|
-
<div style="display:flex; gap:8px; justify-content:flex-end; padding-top:8px">
|
|
6761
|
-
<button mat-stroked-button (click)="toggleImport()">Cancelar</button>
|
|
6762
|
-
<button mat-flat-button color="primary" (click)="applyImport()">Aplicar</button>
|
|
6763
|
-
</div>
|
|
6764
|
-
</div>
|
|
6765
|
-
|
|
6766
|
-
<div *ngIf="!ruleFor(sel)">
|
|
6767
|
-
<div class="rules-empty">Nenhuma cascata configurada. Clique em “Adicionar” para montar a dependência.</div>
|
|
6768
|
-
</div>
|
|
6769
|
-
<div *ngIf="ruleFor(sel) as rule">
|
|
6770
|
-
<div class="rule-row">
|
|
8838
|
+
@if (selectedField(); as sel) {
|
|
8839
|
+
<div class="cm-right">
|
|
8840
|
+
<div class="cm-header">
|
|
6771
8841
|
<div>
|
|
6772
|
-
<
|
|
6773
|
-
<div style="opacity:.7">
|
|
8842
|
+
<h3 style="margin:0">{{ labelFor(sel) }}</h3>
|
|
8843
|
+
<div style="opacity:.7">{{ sel }}</div>
|
|
6774
8844
|
</div>
|
|
6775
|
-
<div
|
|
6776
|
-
<button mat-
|
|
6777
|
-
<button mat-
|
|
6778
|
-
<button mat-
|
|
8845
|
+
<div>
|
|
8846
|
+
<button mat-stroked-button color="primary" (click)="exportRules()" style="margin-right:8px">Exportar</button>
|
|
8847
|
+
<button mat-stroked-button (click)="toggleImport()" style="margin-right:8px">Importar</button>
|
|
8848
|
+
<button mat-flat-button color="primary" [matMenuTriggerFor]="addMenu" [disabled]="editing()">Adicionar</button>
|
|
8849
|
+
<mat-menu #addMenu="matMenu">
|
|
8850
|
+
<button mat-menu-item (click)="startEdit(sel)"><mat-icon [praxisIcon]="'add'"></mat-icon><span>Em branco</span></button>
|
|
8851
|
+
<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>
|
|
8852
|
+
<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>
|
|
8853
|
+
</mat-menu>
|
|
6779
8854
|
</div>
|
|
6780
8855
|
</div>
|
|
6781
|
-
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
|
|
6786
|
-
|
|
8856
|
+
<!-- Conflict banner (connections) -->
|
|
8857
|
+
@if (hasConflict(sel)) {
|
|
8858
|
+
<div class="banner">
|
|
8859
|
+
<mat-icon [praxisIcon]="'link_off'"></mat-icon>
|
|
8860
|
+
<div>
|
|
8861
|
+
Connections atualizam este campo (filterCriteria). Considere desativar a cascata nativa ou usar ‘manual’.
|
|
8862
|
+
</div>
|
|
8863
|
+
<span class="spacer"></span>
|
|
8864
|
+
<button mat-stroked-button color="primary" (click)="quickSet(sel,'disable')">Desativar</button>
|
|
8865
|
+
<button mat-stroked-button color="accent" (click)="quickSet(sel,'manual')">Definir manual</button>
|
|
8866
|
+
</div>
|
|
8867
|
+
}
|
|
8868
|
+
@if (importMode) {
|
|
8869
|
+
<div class="import-panel">
|
|
8870
|
+
<div style="display:flex; gap:8px; align-items:center; margin-bottom:8px"><mat-icon [praxisIcon]="'upload'"></mat-icon><strong>Importar regras (JSON)</strong></div>
|
|
8871
|
+
<textarea matInput rows="6" class="full" [(ngModel)]="importJson" placeholder='{ "campo": { "dependencyFields": ["dep"], ... } }'></textarea>
|
|
8872
|
+
<div style="display:flex; gap:8px; justify-content:flex-end; padding-top:8px">
|
|
8873
|
+
<button mat-stroked-button (click)="toggleImport()">Cancelar</button>
|
|
8874
|
+
<button mat-flat-button color="primary" (click)="applyImport()">Aplicar</button>
|
|
8875
|
+
</div>
|
|
8876
|
+
</div>
|
|
8877
|
+
}
|
|
8878
|
+
@if (!ruleFor(sel)) {
|
|
6787
8879
|
<div>
|
|
6788
|
-
<div>
|
|
6789
|
-
<small style="color: var(--md-sys-color-on-surface-variant);">Defina dependentes, quando recarregar e como mapear filtros</small>
|
|
8880
|
+
<div class="rules-empty">Nenhuma cascata configurada. Clique em “Adicionar” para montar a dependência.</div>
|
|
6790
8881
|
</div>
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
8882
|
+
}
|
|
8883
|
+
@if (ruleFor(sel); as rule) {
|
|
8884
|
+
<div>
|
|
8885
|
+
<div class="rule-row">
|
|
8886
|
+
<div>
|
|
8887
|
+
<div><strong>Dependentes:</strong> {{ rule.dependencyFields.join(', ') || '—' }}</div>
|
|
8888
|
+
<div style="opacity:.7">Load on change: {{ rule.dependencyLoadOnChange || 'respectLoadOn' }} | Merge: {{ rule.dependencyMergeStrategy || 'merge' }}</div>
|
|
8889
|
+
</div>
|
|
8890
|
+
<div class="rule-actions">
|
|
8891
|
+
<button mat-icon-button matTooltip="Editar" (click)="startEdit(sel)"><mat-icon [praxisIcon]="'edit'"></mat-icon></button>
|
|
8892
|
+
<button mat-icon-button matTooltip="Duplicar" disabled><mat-icon [praxisIcon]="'content_copy'"></mat-icon></button>
|
|
8893
|
+
<button mat-icon-button matTooltip="Remover" (click)="remove(sel)"><mat-icon [praxisIcon]="'delete'"></mat-icon></button>
|
|
8894
|
+
</div>
|
|
8895
|
+
</div>
|
|
8896
|
+
</div>
|
|
8897
|
+
}
|
|
8898
|
+
<!-- Editor com seções (sem acordeão duplo) -->
|
|
8899
|
+
<div class="panel-surface">
|
|
8900
|
+
<div class="panel-header" style="padding: 12px 16px; border-bottom: 1px solid var(--md-sys-color-outline-variant);">
|
|
8901
|
+
<mat-icon [praxisIcon]="'rule'"></mat-icon>
|
|
8902
|
+
<div>
|
|
8903
|
+
<div>Cascata do campo</div>
|
|
8904
|
+
<small style="color: var(--md-sys-color-on-surface-variant);">Defina dependentes, quando recarregar e como mapear filtros</small>
|
|
8905
|
+
</div>
|
|
8906
|
+
</div>
|
|
8907
|
+
<div class="panel-stack">
|
|
8908
|
+
<!-- Básico -->
|
|
8909
|
+
<mat-expansion-panel [expanded]="true" class="stack-panel">
|
|
8910
|
+
<mat-expansion-panel-header>
|
|
8911
|
+
<mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'tune'"></mat-icon> Básico</mat-panel-title>
|
|
8912
|
+
</mat-expansion-panel-header>
|
|
8913
|
+
<div class="form-grid">
|
|
8914
|
+
<div class="form-row">
|
|
8915
|
+
<mat-form-field appearance="outline" class="full">
|
|
8916
|
+
<mat-label>Campos dependentes</mat-label>
|
|
8917
|
+
<mat-select multiple [(ngModel)]="editDepList" (ngModelChange)="onDepListChange()">
|
|
8918
|
+
@for (f of availableDependencyFields(); track f) {
|
|
8919
|
+
<mat-option [value]="f.name">
|
|
8920
|
+
{{ labelFor(f.name) }} ({{ f.name }})
|
|
8921
|
+
</mat-option>
|
|
8922
|
+
}
|
|
8923
|
+
</mat-select>
|
|
8924
|
+
<button
|
|
8925
|
+
mat-icon-button
|
|
8926
|
+
matSuffix
|
|
8927
|
+
class="help-icon-button"
|
|
8928
|
+
type="button"
|
|
8929
|
+
[matTooltip]="'Selecione os campos que controlam este valor'"
|
|
8930
|
+
matTooltipPosition="above"
|
|
8931
|
+
>
|
|
8932
|
+
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
8933
|
+
</button>
|
|
8934
|
+
</mat-form-field>
|
|
8935
|
+
</div>
|
|
8936
|
+
<div class="form-row">
|
|
8937
|
+
<mat-checkbox [(ngModel)]="editEnable">Ativar cascata nativa</mat-checkbox>
|
|
8938
|
+
<mat-checkbox [(ngModel)]="editReset">Resetar ao mudar</mat-checkbox>
|
|
8939
|
+
</div>
|
|
8940
|
+
</div>
|
|
8941
|
+
</mat-expansion-panel>
|
|
8942
|
+
<!-- Execução -->
|
|
8943
|
+
<mat-expansion-panel class="stack-panel">
|
|
8944
|
+
<mat-expansion-panel-header>
|
|
8945
|
+
<mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'schedule'"></mat-icon> Execução</mat-panel-title>
|
|
8946
|
+
</mat-expansion-panel-header>
|
|
8947
|
+
<div class="form-row">
|
|
8948
|
+
<mat-form-field appearance="outline">
|
|
8949
|
+
<mat-label>Carregar ao mudar</mat-label>
|
|
8950
|
+
<mat-select [(ngModel)]="editLoadOn">
|
|
8951
|
+
<mat-option value="respectLoadOn">Respeitar loadOn</mat-option>
|
|
8952
|
+
<mat-option value="immediate">Imediato</mat-option>
|
|
8953
|
+
<mat-option value="manual">Manual</mat-option>
|
|
8954
|
+
</mat-select>
|
|
8955
|
+
</mat-form-field>
|
|
8956
|
+
<mat-form-field appearance="outline">
|
|
8957
|
+
<mat-label>Merge de filtros</mat-label>
|
|
8958
|
+
<mat-select [(ngModel)]="editMerge">
|
|
8959
|
+
<mat-option value="merge">merge (padrão)</mat-option>
|
|
8960
|
+
<mat-option value="replace">replace</mat-option>
|
|
8961
|
+
</mat-select>
|
|
8962
|
+
</mat-form-field>
|
|
8963
|
+
<mat-form-field appearance="outline">
|
|
8964
|
+
<mat-label>Atraso (ms)</mat-label>
|
|
8965
|
+
<input matInput type="number" [(ngModel)]="editDebounce">
|
|
8966
|
+
</mat-form-field>
|
|
8967
|
+
</div>
|
|
8968
|
+
</mat-expansion-panel>
|
|
8969
|
+
<!-- Extração de valor -->
|
|
8970
|
+
<mat-expansion-panel class="stack-panel">
|
|
8971
|
+
<mat-expansion-panel-header>
|
|
8972
|
+
<mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'call_split'"></mat-icon> Extração de valor</mat-panel-title>
|
|
8973
|
+
</mat-expansion-panel-header>
|
|
6800
8974
|
<div class="form-row">
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
<mat-option *ngFor="let f of availableDependencyFields()" [value]="f.name">
|
|
6805
|
-
{{ labelFor(f.name) }} ({{ f.name }})
|
|
6806
|
-
</mat-option>
|
|
6807
|
-
</mat-select>
|
|
8975
|
+
<mat-form-field appearance="outline" class="full">
|
|
8976
|
+
<mat-label>ValuePath padrão</mat-label>
|
|
8977
|
+
<input matInput [(ngModel)]="editValuePath" placeholder="id">
|
|
6808
8978
|
<button
|
|
6809
8979
|
mat-icon-button
|
|
6810
8980
|
matSuffix
|
|
6811
8981
|
class="help-icon-button"
|
|
6812
8982
|
type="button"
|
|
6813
|
-
[matTooltip]="'
|
|
8983
|
+
[matTooltip]="'Preenchido automaticamente a partir de optionValueKey() ou \\'id\\''"
|
|
6814
8984
|
matTooltipPosition="above"
|
|
6815
|
-
|
|
8985
|
+
>
|
|
6816
8986
|
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
6817
8987
|
</button>
|
|
6818
|
-
|
|
6819
|
-
</div>
|
|
6820
|
-
<div class="form-row">
|
|
6821
|
-
<mat-checkbox [(ngModel)]="editEnable">Ativar cascata nativa</mat-checkbox>
|
|
6822
|
-
<mat-checkbox [(ngModel)]="editReset">Resetar ao mudar</mat-checkbox>
|
|
8988
|
+
</mat-form-field>
|
|
6823
8989
|
</div>
|
|
6824
|
-
</
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
>
|
|
6909
|
-
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
6910
|
-
</button>
|
|
6911
|
-
</mat-form-field>
|
|
6912
|
-
</td>
|
|
6913
|
-
<td>
|
|
6914
|
-
<mat-form-field appearance="outline" class="full">
|
|
6915
|
-
<mat-label>ValuePath</mat-label>
|
|
6916
|
-
<input matInput [(ngModel)]="row.valuePath" [matAutocomplete]="autoValue" [placeholder]="editValuePath||'id'">
|
|
6917
|
-
<mat-autocomplete #autoValue="matAutocomplete">
|
|
6918
|
-
<mat-option *ngFor="let opt of getValuePathSuggestions(row.dep)" [value]="opt">{{ opt }}</mat-option>
|
|
6919
|
-
</mat-autocomplete>
|
|
6920
|
-
<button
|
|
6921
|
-
mat-icon-button
|
|
6922
|
-
matSuffix
|
|
6923
|
-
class="help-icon-button"
|
|
6924
|
-
type="button"
|
|
6925
|
-
[matTooltip]="'Opcional'"
|
|
6926
|
-
matTooltipPosition="above"
|
|
6927
|
-
>
|
|
6928
|
-
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
6929
|
-
</button>
|
|
6930
|
-
</mat-form-field>
|
|
6931
|
-
</td>
|
|
6932
|
-
</tr>
|
|
6933
|
-
</tbody>
|
|
6934
|
-
</table>
|
|
6935
|
-
<ng-template #noMap>
|
|
6936
|
-
<div class="rules-empty">Defina dependentes para sugerir o mapeamento.</div>
|
|
6937
|
-
</ng-template>
|
|
6938
|
-
</mat-expansion-panel>
|
|
6939
|
-
|
|
6940
|
-
<!-- Preview -->
|
|
6941
|
-
<mat-expansion-panel class="stack-panel">
|
|
6942
|
-
<mat-expansion-panel-header>
|
|
6943
|
-
<mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'visibility'"></mat-icon> Preview</mat-panel-title>
|
|
6944
|
-
</mat-expansion-panel-header>
|
|
6945
|
-
<pre class="preview-box">{{ buildPreview() }}</pre>
|
|
6946
|
-
<div style="display:flex; justify-content:flex-end; padding-top:6px">
|
|
6947
|
-
<button mat-stroked-button (click)="copyPreview()">Copiar</button>
|
|
6948
|
-
</div>
|
|
6949
|
-
</mat-expansion-panel>
|
|
6950
|
-
</div>
|
|
6951
|
-
|
|
6952
|
-
<div style="display:flex; gap:8px; justify-content:flex-end; padding:8px 12px;">
|
|
6953
|
-
<button mat-stroked-button (click)="cancelEdit()">Cancelar</button>
|
|
6954
|
-
<button mat-flat-button color="primary" (click)="saveEdit(sel)">Salvar</button>
|
|
8990
|
+
</mat-expansion-panel>
|
|
8991
|
+
<!-- Mapeamento de filtros (tabela) -->
|
|
8992
|
+
<mat-expansion-panel class="stack-panel">
|
|
8993
|
+
<mat-expansion-panel-header>
|
|
8994
|
+
<mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'filter_alt'"></mat-icon> Mapeamento de filtros</mat-panel-title>
|
|
8995
|
+
</mat-expansion-panel-header>
|
|
8996
|
+
@if (editMapRows.length) {
|
|
8997
|
+
<table class="map-table">
|
|
8998
|
+
<thead>
|
|
8999
|
+
<tr>
|
|
9000
|
+
<th>Dependente</th>
|
|
9001
|
+
<th>Chave (dot‑path)</th>
|
|
9002
|
+
<th>ValuePath (opcional)</th>
|
|
9003
|
+
</tr>
|
|
9004
|
+
</thead>
|
|
9005
|
+
<tbody>
|
|
9006
|
+
@for (row of editMapRows; track row; let i = $index) {
|
|
9007
|
+
<tr>
|
|
9008
|
+
<td>{{ row.dep }}</td>
|
|
9009
|
+
<td>
|
|
9010
|
+
<mat-form-field appearance="outline" class="full">
|
|
9011
|
+
<mat-label>Chave</mat-label>
|
|
9012
|
+
<input matInput [(ngModel)]="row.key" [matAutocomplete]="autoKey" [placeholder]="row.dep">
|
|
9013
|
+
<mat-autocomplete #autoKey="matAutocomplete">
|
|
9014
|
+
@for (opt of getKeySuggestions(row.dep); track opt) {
|
|
9015
|
+
<mat-option [value]="opt">{{ opt }}</mat-option>
|
|
9016
|
+
}
|
|
9017
|
+
</mat-autocomplete>
|
|
9018
|
+
<button
|
|
9019
|
+
mat-icon-button
|
|
9020
|
+
matSuffix
|
|
9021
|
+
class="help-icon-button"
|
|
9022
|
+
type="button"
|
|
9023
|
+
[matTooltip]="'Use dot-notation (e.g. ' + row.dep + '.id) for nested objects'"
|
|
9024
|
+
matTooltipPosition="above"
|
|
9025
|
+
>
|
|
9026
|
+
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
9027
|
+
</button>
|
|
9028
|
+
</mat-form-field>
|
|
9029
|
+
</td>
|
|
9030
|
+
<td>
|
|
9031
|
+
<mat-form-field appearance="outline" class="full">
|
|
9032
|
+
<mat-label>ValuePath</mat-label>
|
|
9033
|
+
<input matInput [(ngModel)]="row.valuePath" [matAutocomplete]="autoValue" [placeholder]="editValuePath||'id'">
|
|
9034
|
+
<mat-autocomplete #autoValue="matAutocomplete">
|
|
9035
|
+
@for (opt of getValuePathSuggestions(row.dep); track opt) {
|
|
9036
|
+
<mat-option [value]="opt">{{ opt }}</mat-option>
|
|
9037
|
+
}
|
|
9038
|
+
</mat-autocomplete>
|
|
9039
|
+
<button
|
|
9040
|
+
mat-icon-button
|
|
9041
|
+
matSuffix
|
|
9042
|
+
class="help-icon-button"
|
|
9043
|
+
type="button"
|
|
9044
|
+
[matTooltip]="'Opcional'"
|
|
9045
|
+
matTooltipPosition="above"
|
|
9046
|
+
>
|
|
9047
|
+
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
9048
|
+
</button>
|
|
9049
|
+
</mat-form-field>
|
|
9050
|
+
</td>
|
|
9051
|
+
</tr>
|
|
9052
|
+
}
|
|
9053
|
+
</tbody>
|
|
9054
|
+
</table>
|
|
9055
|
+
} @else {
|
|
9056
|
+
<div class="rules-empty">Defina dependentes para sugerir o mapeamento.</div>
|
|
9057
|
+
}
|
|
9058
|
+
</mat-expansion-panel>
|
|
9059
|
+
<!-- Preview -->
|
|
9060
|
+
<mat-expansion-panel class="stack-panel">
|
|
9061
|
+
<mat-expansion-panel-header>
|
|
9062
|
+
<mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'visibility'"></mat-icon> Preview</mat-panel-title>
|
|
9063
|
+
</mat-expansion-panel-header>
|
|
9064
|
+
<pre class="preview-box">{{ buildPreview() }}</pre>
|
|
9065
|
+
<div style="display:flex; justify-content:flex-end; padding-top:6px">
|
|
9066
|
+
<button mat-stroked-button (click)="copyPreview()">Copiar</button>
|
|
9067
|
+
</div>
|
|
9068
|
+
</mat-expansion-panel>
|
|
9069
|
+
</div>
|
|
9070
|
+
<div style="display:flex; gap:8px; justify-content:flex-end; padding:8px 12px;">
|
|
9071
|
+
<button mat-stroked-button (click)="cancelEdit()">Cancelar</button>
|
|
9072
|
+
<button mat-flat-button color="primary" (click)="saveEdit(sel)">Salvar</button>
|
|
9073
|
+
</div>
|
|
6955
9074
|
</div>
|
|
6956
9075
|
</div>
|
|
6957
|
-
|
|
9076
|
+
}
|
|
6958
9077
|
</div>
|
|
6959
|
-
`, 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"] }] });
|
|
9078
|
+
`, 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"] }] });
|
|
6960
9079
|
}
|
|
6961
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
9080
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CascadeManagerTabComponent, decorators: [{
|
|
6962
9081
|
type: Component,
|
|
6963
9082
|
args: [{ selector: 'praxis-cascade-manager-tab', standalone: true, imports: [
|
|
6964
|
-
CommonModule,
|
|
6965
9083
|
ReactiveFormsModule,
|
|
6966
9084
|
FormsModule,
|
|
6967
9085
|
MatIconModule,
|
|
@@ -6977,7 +9095,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
6977
9095
|
MatMenuModule,
|
|
6978
9096
|
MatAutocompleteModule,
|
|
6979
9097
|
ScrollingModule,
|
|
6980
|
-
PraxisIconDirective
|
|
9098
|
+
PraxisIconDirective
|
|
6981
9099
|
], template: `
|
|
6982
9100
|
<div class="cm-root">
|
|
6983
9101
|
<!-- Left: Fields list -->
|
|
@@ -6986,271 +9104,297 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
6986
9104
|
<mat-label>Buscar campos</mat-label>
|
|
6987
9105
|
<input matInput [ngModel]="searchTerm" (ngModelChange)="onSearchChange($event)">
|
|
6988
9106
|
</mat-form-field>
|
|
6989
|
-
|
|
9107
|
+
@if (fields.length <= 50) {
|
|
6990
9108
|
<div>
|
|
6991
|
-
|
|
6992
|
-
<div>
|
|
6993
|
-
<div>
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
|
|
6998
|
-
|
|
6999
|
-
|
|
9109
|
+
@for (f of filteredFields(); track f) {
|
|
9110
|
+
<div (click)="selectField(f.name)" class="field-item" [class.selected]="f.name===selectedField()" tabindex="0" (keydown)="onFieldKeydown($event, f.name)">
|
|
9111
|
+
<div>
|
|
9112
|
+
<div>{{ labelFor(f.name) }}</div>
|
|
9113
|
+
<small style="opacity:.7">{{ f.name }}</small>
|
|
9114
|
+
</div>
|
|
9115
|
+
@if (hasRule(f.name)) {
|
|
9116
|
+
<div class="rule-badges">
|
|
9117
|
+
@if (isCascadeEnabled(f.name)) {
|
|
9118
|
+
<span class="rule-badge b-active">Ativo</span>
|
|
9119
|
+
}
|
|
9120
|
+
@if (loadOnChange(f.name)==='manual') {
|
|
9121
|
+
<span class="rule-badge b-manual">Manual</span>
|
|
9122
|
+
}
|
|
9123
|
+
@if (loadOnChange(f.name)==='immediate') {
|
|
9124
|
+
<span class="rule-badge b-immediate">Immediate</span>
|
|
9125
|
+
}
|
|
9126
|
+
</div>
|
|
9127
|
+
}
|
|
7000
9128
|
</div>
|
|
7001
|
-
|
|
9129
|
+
}
|
|
7002
9130
|
</div>
|
|
7003
|
-
|
|
7004
|
-
<ng-template #vscroll>
|
|
9131
|
+
} @else {
|
|
7005
9132
|
<cdk-virtual-scroll-viewport itemSize="44" style="height: calc(100vh - 240px)">
|
|
7006
9133
|
<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)">
|
|
7007
9134
|
<div>
|
|
7008
9135
|
<div>{{ labelFor(f.name) }}</div>
|
|
7009
9136
|
<small style="opacity:.7">{{ f.name }}</small>
|
|
7010
9137
|
</div>
|
|
7011
|
-
|
|
7012
|
-
<
|
|
7013
|
-
|
|
7014
|
-
|
|
7015
|
-
|
|
9138
|
+
@if (hasRule(f.name)) {
|
|
9139
|
+
<div class="rule-badges">
|
|
9140
|
+
@if (isCascadeEnabled(f.name)) {
|
|
9141
|
+
<span class="rule-badge b-active">Ativo</span>
|
|
9142
|
+
}
|
|
9143
|
+
@if (loadOnChange(f.name)==='manual') {
|
|
9144
|
+
<span class="rule-badge b-manual">Manual</span>
|
|
9145
|
+
}
|
|
9146
|
+
@if (loadOnChange(f.name)==='immediate') {
|
|
9147
|
+
<span class="rule-badge b-immediate">Immediate</span>
|
|
9148
|
+
}
|
|
9149
|
+
</div>
|
|
9150
|
+
}
|
|
7016
9151
|
</div>
|
|
7017
9152
|
</cdk-virtual-scroll-viewport>
|
|
7018
|
-
|
|
9153
|
+
}
|
|
7019
9154
|
</div>
|
|
7020
|
-
|
|
9155
|
+
|
|
7021
9156
|
<!-- Right: Rules for selected field -->
|
|
7022
|
-
|
|
7023
|
-
<div class="cm-
|
|
7024
|
-
<div>
|
|
7025
|
-
<h3 style="margin:0">{{ labelFor(sel) }}</h3>
|
|
7026
|
-
<div style="opacity:.7">{{ sel }}</div>
|
|
7027
|
-
</div>
|
|
7028
|
-
<div>
|
|
7029
|
-
<button mat-stroked-button color="primary" (click)="exportRules()" style="margin-right:8px">Exportar</button>
|
|
7030
|
-
<button mat-stroked-button (click)="toggleImport()" style="margin-right:8px">Importar</button>
|
|
7031
|
-
<button mat-flat-button color="primary" [matMenuTriggerFor]="addMenu" [disabled]="editing()">Adicionar</button>
|
|
7032
|
-
<mat-menu #addMenu="matMenu">
|
|
7033
|
-
<button mat-menu-item (click)="startEdit(sel)"><mat-icon [praxisIcon]="'add'"></mat-icon><span>Em branco</span></button>
|
|
7034
|
-
<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>
|
|
7035
|
-
<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>
|
|
7036
|
-
</mat-menu>
|
|
7037
|
-
</div>
|
|
7038
|
-
</div>
|
|
7039
|
-
|
|
7040
|
-
<!-- Conflict banner (connections) -->
|
|
7041
|
-
<div class="banner" *ngIf="hasConflict(sel)">
|
|
7042
|
-
<mat-icon [praxisIcon]="'link_off'"></mat-icon>
|
|
7043
|
-
<div>
|
|
7044
|
-
Connections atualizam este campo (filterCriteria). Considere desativar a cascata nativa ou usar ‘manual’.
|
|
7045
|
-
</div>
|
|
7046
|
-
<span class="spacer"></span>
|
|
7047
|
-
<button mat-stroked-button color="primary" (click)="quickSet(sel,'disable')">Desativar</button>
|
|
7048
|
-
<button mat-stroked-button color="accent" (click)="quickSet(sel,'manual')">Definir manual</button>
|
|
7049
|
-
</div>
|
|
7050
|
-
|
|
7051
|
-
<div *ngIf="importMode" class="import-panel">
|
|
7052
|
-
<div style="display:flex; gap:8px; align-items:center; margin-bottom:8px"><mat-icon [praxisIcon]="'upload'"></mat-icon><strong>Importar regras (JSON)</strong></div>
|
|
7053
|
-
<textarea matInput rows="6" class="full" [(ngModel)]="importJson" placeholder='{ "campo": { "dependencyFields": ["dep"], ... } }'></textarea>
|
|
7054
|
-
<div style="display:flex; gap:8px; justify-content:flex-end; padding-top:8px">
|
|
7055
|
-
<button mat-stroked-button (click)="toggleImport()">Cancelar</button>
|
|
7056
|
-
<button mat-flat-button color="primary" (click)="applyImport()">Aplicar</button>
|
|
7057
|
-
</div>
|
|
7058
|
-
</div>
|
|
7059
|
-
|
|
7060
|
-
<div *ngIf="!ruleFor(sel)">
|
|
7061
|
-
<div class="rules-empty">Nenhuma cascata configurada. Clique em “Adicionar” para montar a dependência.</div>
|
|
7062
|
-
</div>
|
|
7063
|
-
<div *ngIf="ruleFor(sel) as rule">
|
|
7064
|
-
<div class="rule-row">
|
|
9157
|
+
@if (selectedField(); as sel) {
|
|
9158
|
+
<div class="cm-right">
|
|
9159
|
+
<div class="cm-header">
|
|
7065
9160
|
<div>
|
|
7066
|
-
<
|
|
7067
|
-
<div style="opacity:.7">
|
|
9161
|
+
<h3 style="margin:0">{{ labelFor(sel) }}</h3>
|
|
9162
|
+
<div style="opacity:.7">{{ sel }}</div>
|
|
7068
9163
|
</div>
|
|
7069
|
-
<div
|
|
7070
|
-
<button mat-
|
|
7071
|
-
<button mat-
|
|
7072
|
-
<button mat-
|
|
9164
|
+
<div>
|
|
9165
|
+
<button mat-stroked-button color="primary" (click)="exportRules()" style="margin-right:8px">Exportar</button>
|
|
9166
|
+
<button mat-stroked-button (click)="toggleImport()" style="margin-right:8px">Importar</button>
|
|
9167
|
+
<button mat-flat-button color="primary" [matMenuTriggerFor]="addMenu" [disabled]="editing()">Adicionar</button>
|
|
9168
|
+
<mat-menu #addMenu="matMenu">
|
|
9169
|
+
<button mat-menu-item (click)="startEdit(sel)"><mat-icon [praxisIcon]="'add'"></mat-icon><span>Em branco</span></button>
|
|
9170
|
+
<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>
|
|
9171
|
+
<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>
|
|
9172
|
+
</mat-menu>
|
|
7073
9173
|
</div>
|
|
7074
9174
|
</div>
|
|
7075
|
-
|
|
7076
|
-
|
|
7077
|
-
|
|
7078
|
-
|
|
7079
|
-
|
|
7080
|
-
|
|
9175
|
+
<!-- Conflict banner (connections) -->
|
|
9176
|
+
@if (hasConflict(sel)) {
|
|
9177
|
+
<div class="banner">
|
|
9178
|
+
<mat-icon [praxisIcon]="'link_off'"></mat-icon>
|
|
9179
|
+
<div>
|
|
9180
|
+
Connections atualizam este campo (filterCriteria). Considere desativar a cascata nativa ou usar ‘manual’.
|
|
9181
|
+
</div>
|
|
9182
|
+
<span class="spacer"></span>
|
|
9183
|
+
<button mat-stroked-button color="primary" (click)="quickSet(sel,'disable')">Desativar</button>
|
|
9184
|
+
<button mat-stroked-button color="accent" (click)="quickSet(sel,'manual')">Definir manual</button>
|
|
9185
|
+
</div>
|
|
9186
|
+
}
|
|
9187
|
+
@if (importMode) {
|
|
9188
|
+
<div class="import-panel">
|
|
9189
|
+
<div style="display:flex; gap:8px; align-items:center; margin-bottom:8px"><mat-icon [praxisIcon]="'upload'"></mat-icon><strong>Importar regras (JSON)</strong></div>
|
|
9190
|
+
<textarea matInput rows="6" class="full" [(ngModel)]="importJson" placeholder='{ "campo": { "dependencyFields": ["dep"], ... } }'></textarea>
|
|
9191
|
+
<div style="display:flex; gap:8px; justify-content:flex-end; padding-top:8px">
|
|
9192
|
+
<button mat-stroked-button (click)="toggleImport()">Cancelar</button>
|
|
9193
|
+
<button mat-flat-button color="primary" (click)="applyImport()">Aplicar</button>
|
|
9194
|
+
</div>
|
|
9195
|
+
</div>
|
|
9196
|
+
}
|
|
9197
|
+
@if (!ruleFor(sel)) {
|
|
7081
9198
|
<div>
|
|
7082
|
-
<div>
|
|
7083
|
-
<small style="color: var(--md-sys-color-on-surface-variant);">Defina dependentes, quando recarregar e como mapear filtros</small>
|
|
9199
|
+
<div class="rules-empty">Nenhuma cascata configurada. Clique em “Adicionar” para montar a dependência.</div>
|
|
7084
9200
|
</div>
|
|
7085
|
-
|
|
7086
|
-
|
|
7087
|
-
|
|
7088
|
-
|
|
7089
|
-
|
|
7090
|
-
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
|
|
9201
|
+
}
|
|
9202
|
+
@if (ruleFor(sel); as rule) {
|
|
9203
|
+
<div>
|
|
9204
|
+
<div class="rule-row">
|
|
9205
|
+
<div>
|
|
9206
|
+
<div><strong>Dependentes:</strong> {{ rule.dependencyFields.join(', ') || '—' }}</div>
|
|
9207
|
+
<div style="opacity:.7">Load on change: {{ rule.dependencyLoadOnChange || 'respectLoadOn' }} | Merge: {{ rule.dependencyMergeStrategy || 'merge' }}</div>
|
|
9208
|
+
</div>
|
|
9209
|
+
<div class="rule-actions">
|
|
9210
|
+
<button mat-icon-button matTooltip="Editar" (click)="startEdit(sel)"><mat-icon [praxisIcon]="'edit'"></mat-icon></button>
|
|
9211
|
+
<button mat-icon-button matTooltip="Duplicar" disabled><mat-icon [praxisIcon]="'content_copy'"></mat-icon></button>
|
|
9212
|
+
<button mat-icon-button matTooltip="Remover" (click)="remove(sel)"><mat-icon [praxisIcon]="'delete'"></mat-icon></button>
|
|
9213
|
+
</div>
|
|
9214
|
+
</div>
|
|
9215
|
+
</div>
|
|
9216
|
+
}
|
|
9217
|
+
<!-- Editor com seções (sem acordeão duplo) -->
|
|
9218
|
+
<div class="panel-surface">
|
|
9219
|
+
<div class="panel-header" style="padding: 12px 16px; border-bottom: 1px solid var(--md-sys-color-outline-variant);">
|
|
9220
|
+
<mat-icon [praxisIcon]="'rule'"></mat-icon>
|
|
9221
|
+
<div>
|
|
9222
|
+
<div>Cascata do campo</div>
|
|
9223
|
+
<small style="color: var(--md-sys-color-on-surface-variant);">Defina dependentes, quando recarregar e como mapear filtros</small>
|
|
9224
|
+
</div>
|
|
9225
|
+
</div>
|
|
9226
|
+
<div class="panel-stack">
|
|
9227
|
+
<!-- Básico -->
|
|
9228
|
+
<mat-expansion-panel [expanded]="true" class="stack-panel">
|
|
9229
|
+
<mat-expansion-panel-header>
|
|
9230
|
+
<mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'tune'"></mat-icon> Básico</mat-panel-title>
|
|
9231
|
+
</mat-expansion-panel-header>
|
|
9232
|
+
<div class="form-grid">
|
|
9233
|
+
<div class="form-row">
|
|
9234
|
+
<mat-form-field appearance="outline" class="full">
|
|
9235
|
+
<mat-label>Campos dependentes</mat-label>
|
|
9236
|
+
<mat-select multiple [(ngModel)]="editDepList" (ngModelChange)="onDepListChange()">
|
|
9237
|
+
@for (f of availableDependencyFields(); track f) {
|
|
9238
|
+
<mat-option [value]="f.name">
|
|
9239
|
+
{{ labelFor(f.name) }} ({{ f.name }})
|
|
9240
|
+
</mat-option>
|
|
9241
|
+
}
|
|
9242
|
+
</mat-select>
|
|
9243
|
+
<button
|
|
9244
|
+
mat-icon-button
|
|
9245
|
+
matSuffix
|
|
9246
|
+
class="help-icon-button"
|
|
9247
|
+
type="button"
|
|
9248
|
+
[matTooltip]="'Selecione os campos que controlam este valor'"
|
|
9249
|
+
matTooltipPosition="above"
|
|
9250
|
+
>
|
|
9251
|
+
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
9252
|
+
</button>
|
|
9253
|
+
</mat-form-field>
|
|
9254
|
+
</div>
|
|
9255
|
+
<div class="form-row">
|
|
9256
|
+
<mat-checkbox [(ngModel)]="editEnable">Ativar cascata nativa</mat-checkbox>
|
|
9257
|
+
<mat-checkbox [(ngModel)]="editReset">Resetar ao mudar</mat-checkbox>
|
|
9258
|
+
</div>
|
|
9259
|
+
</div>
|
|
9260
|
+
</mat-expansion-panel>
|
|
9261
|
+
<!-- Execução -->
|
|
9262
|
+
<mat-expansion-panel class="stack-panel">
|
|
9263
|
+
<mat-expansion-panel-header>
|
|
9264
|
+
<mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'schedule'"></mat-icon> Execução</mat-panel-title>
|
|
9265
|
+
</mat-expansion-panel-header>
|
|
9266
|
+
<div class="form-row">
|
|
9267
|
+
<mat-form-field appearance="outline">
|
|
9268
|
+
<mat-label>Carregar ao mudar</mat-label>
|
|
9269
|
+
<mat-select [(ngModel)]="editLoadOn">
|
|
9270
|
+
<mat-option value="respectLoadOn">Respeitar loadOn</mat-option>
|
|
9271
|
+
<mat-option value="immediate">Imediato</mat-option>
|
|
9272
|
+
<mat-option value="manual">Manual</mat-option>
|
|
9273
|
+
</mat-select>
|
|
9274
|
+
</mat-form-field>
|
|
9275
|
+
<mat-form-field appearance="outline">
|
|
9276
|
+
<mat-label>Merge de filtros</mat-label>
|
|
9277
|
+
<mat-select [(ngModel)]="editMerge">
|
|
9278
|
+
<mat-option value="merge">merge (padrão)</mat-option>
|
|
9279
|
+
<mat-option value="replace">replace</mat-option>
|
|
9280
|
+
</mat-select>
|
|
9281
|
+
</mat-form-field>
|
|
9282
|
+
<mat-form-field appearance="outline">
|
|
9283
|
+
<mat-label>Atraso (ms)</mat-label>
|
|
9284
|
+
<input matInput type="number" [(ngModel)]="editDebounce">
|
|
9285
|
+
</mat-form-field>
|
|
9286
|
+
</div>
|
|
9287
|
+
</mat-expansion-panel>
|
|
9288
|
+
<!-- Extração de valor -->
|
|
9289
|
+
<mat-expansion-panel class="stack-panel">
|
|
9290
|
+
<mat-expansion-panel-header>
|
|
9291
|
+
<mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'call_split'"></mat-icon> Extração de valor</mat-panel-title>
|
|
9292
|
+
</mat-expansion-panel-header>
|
|
7094
9293
|
<div class="form-row">
|
|
7095
|
-
|
|
7096
|
-
|
|
7097
|
-
|
|
7098
|
-
<mat-option *ngFor="let f of availableDependencyFields()" [value]="f.name">
|
|
7099
|
-
{{ labelFor(f.name) }} ({{ f.name }})
|
|
7100
|
-
</mat-option>
|
|
7101
|
-
</mat-select>
|
|
9294
|
+
<mat-form-field appearance="outline" class="full">
|
|
9295
|
+
<mat-label>ValuePath padrão</mat-label>
|
|
9296
|
+
<input matInput [(ngModel)]="editValuePath" placeholder="id">
|
|
7102
9297
|
<button
|
|
7103
9298
|
mat-icon-button
|
|
7104
9299
|
matSuffix
|
|
7105
9300
|
class="help-icon-button"
|
|
7106
9301
|
type="button"
|
|
7107
|
-
[matTooltip]="'
|
|
9302
|
+
[matTooltip]="'Preenchido automaticamente a partir de optionValueKey() ou \\'id\\''"
|
|
7108
9303
|
matTooltipPosition="above"
|
|
7109
|
-
|
|
9304
|
+
>
|
|
7110
9305
|
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
7111
9306
|
</button>
|
|
7112
|
-
|
|
7113
|
-
</div>
|
|
7114
|
-
<div class="form-row">
|
|
7115
|
-
<mat-checkbox [(ngModel)]="editEnable">Ativar cascata nativa</mat-checkbox>
|
|
7116
|
-
<mat-checkbox [(ngModel)]="editReset">Resetar ao mudar</mat-checkbox>
|
|
9307
|
+
</mat-form-field>
|
|
7117
9308
|
</div>
|
|
7118
|
-
</
|
|
7119
|
-
|
|
7120
|
-
|
|
7121
|
-
|
|
7122
|
-
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7130
|
-
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
|
|
7154
|
-
|
|
7155
|
-
|
|
7156
|
-
|
|
7157
|
-
|
|
7158
|
-
|
|
7159
|
-
|
|
7160
|
-
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
|
|
7178
|
-
|
|
7179
|
-
|
|
7180
|
-
|
|
7181
|
-
|
|
7182
|
-
|
|
7183
|
-
|
|
7184
|
-
|
|
7185
|
-
|
|
7186
|
-
|
|
7187
|
-
|
|
7188
|
-
|
|
7189
|
-
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
>
|
|
7203
|
-
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
7204
|
-
</button>
|
|
7205
|
-
</mat-form-field>
|
|
7206
|
-
</td>
|
|
7207
|
-
<td>
|
|
7208
|
-
<mat-form-field appearance="outline" class="full">
|
|
7209
|
-
<mat-label>ValuePath</mat-label>
|
|
7210
|
-
<input matInput [(ngModel)]="row.valuePath" [matAutocomplete]="autoValue" [placeholder]="editValuePath||'id'">
|
|
7211
|
-
<mat-autocomplete #autoValue="matAutocomplete">
|
|
7212
|
-
<mat-option *ngFor="let opt of getValuePathSuggestions(row.dep)" [value]="opt">{{ opt }}</mat-option>
|
|
7213
|
-
</mat-autocomplete>
|
|
7214
|
-
<button
|
|
7215
|
-
mat-icon-button
|
|
7216
|
-
matSuffix
|
|
7217
|
-
class="help-icon-button"
|
|
7218
|
-
type="button"
|
|
7219
|
-
[matTooltip]="'Opcional'"
|
|
7220
|
-
matTooltipPosition="above"
|
|
7221
|
-
>
|
|
7222
|
-
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
7223
|
-
</button>
|
|
7224
|
-
</mat-form-field>
|
|
7225
|
-
</td>
|
|
7226
|
-
</tr>
|
|
7227
|
-
</tbody>
|
|
7228
|
-
</table>
|
|
7229
|
-
<ng-template #noMap>
|
|
7230
|
-
<div class="rules-empty">Defina dependentes para sugerir o mapeamento.</div>
|
|
7231
|
-
</ng-template>
|
|
7232
|
-
</mat-expansion-panel>
|
|
7233
|
-
|
|
7234
|
-
<!-- Preview -->
|
|
7235
|
-
<mat-expansion-panel class="stack-panel">
|
|
7236
|
-
<mat-expansion-panel-header>
|
|
7237
|
-
<mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'visibility'"></mat-icon> Preview</mat-panel-title>
|
|
7238
|
-
</mat-expansion-panel-header>
|
|
7239
|
-
<pre class="preview-box">{{ buildPreview() }}</pre>
|
|
7240
|
-
<div style="display:flex; justify-content:flex-end; padding-top:6px">
|
|
7241
|
-
<button mat-stroked-button (click)="copyPreview()">Copiar</button>
|
|
7242
|
-
</div>
|
|
7243
|
-
</mat-expansion-panel>
|
|
7244
|
-
</div>
|
|
7245
|
-
|
|
7246
|
-
<div style="display:flex; gap:8px; justify-content:flex-end; padding:8px 12px;">
|
|
7247
|
-
<button mat-stroked-button (click)="cancelEdit()">Cancelar</button>
|
|
7248
|
-
<button mat-flat-button color="primary" (click)="saveEdit(sel)">Salvar</button>
|
|
9309
|
+
</mat-expansion-panel>
|
|
9310
|
+
<!-- Mapeamento de filtros (tabela) -->
|
|
9311
|
+
<mat-expansion-panel class="stack-panel">
|
|
9312
|
+
<mat-expansion-panel-header>
|
|
9313
|
+
<mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'filter_alt'"></mat-icon> Mapeamento de filtros</mat-panel-title>
|
|
9314
|
+
</mat-expansion-panel-header>
|
|
9315
|
+
@if (editMapRows.length) {
|
|
9316
|
+
<table class="map-table">
|
|
9317
|
+
<thead>
|
|
9318
|
+
<tr>
|
|
9319
|
+
<th>Dependente</th>
|
|
9320
|
+
<th>Chave (dot‑path)</th>
|
|
9321
|
+
<th>ValuePath (opcional)</th>
|
|
9322
|
+
</tr>
|
|
9323
|
+
</thead>
|
|
9324
|
+
<tbody>
|
|
9325
|
+
@for (row of editMapRows; track row; let i = $index) {
|
|
9326
|
+
<tr>
|
|
9327
|
+
<td>{{ row.dep }}</td>
|
|
9328
|
+
<td>
|
|
9329
|
+
<mat-form-field appearance="outline" class="full">
|
|
9330
|
+
<mat-label>Chave</mat-label>
|
|
9331
|
+
<input matInput [(ngModel)]="row.key" [matAutocomplete]="autoKey" [placeholder]="row.dep">
|
|
9332
|
+
<mat-autocomplete #autoKey="matAutocomplete">
|
|
9333
|
+
@for (opt of getKeySuggestions(row.dep); track opt) {
|
|
9334
|
+
<mat-option [value]="opt">{{ opt }}</mat-option>
|
|
9335
|
+
}
|
|
9336
|
+
</mat-autocomplete>
|
|
9337
|
+
<button
|
|
9338
|
+
mat-icon-button
|
|
9339
|
+
matSuffix
|
|
9340
|
+
class="help-icon-button"
|
|
9341
|
+
type="button"
|
|
9342
|
+
[matTooltip]="'Use dot-notation (e.g. ' + row.dep + '.id) for nested objects'"
|
|
9343
|
+
matTooltipPosition="above"
|
|
9344
|
+
>
|
|
9345
|
+
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
9346
|
+
</button>
|
|
9347
|
+
</mat-form-field>
|
|
9348
|
+
</td>
|
|
9349
|
+
<td>
|
|
9350
|
+
<mat-form-field appearance="outline" class="full">
|
|
9351
|
+
<mat-label>ValuePath</mat-label>
|
|
9352
|
+
<input matInput [(ngModel)]="row.valuePath" [matAutocomplete]="autoValue" [placeholder]="editValuePath||'id'">
|
|
9353
|
+
<mat-autocomplete #autoValue="matAutocomplete">
|
|
9354
|
+
@for (opt of getValuePathSuggestions(row.dep); track opt) {
|
|
9355
|
+
<mat-option [value]="opt">{{ opt }}</mat-option>
|
|
9356
|
+
}
|
|
9357
|
+
</mat-autocomplete>
|
|
9358
|
+
<button
|
|
9359
|
+
mat-icon-button
|
|
9360
|
+
matSuffix
|
|
9361
|
+
class="help-icon-button"
|
|
9362
|
+
type="button"
|
|
9363
|
+
[matTooltip]="'Opcional'"
|
|
9364
|
+
matTooltipPosition="above"
|
|
9365
|
+
>
|
|
9366
|
+
<mat-icon [praxisIcon]="'help_outline'"></mat-icon>
|
|
9367
|
+
</button>
|
|
9368
|
+
</mat-form-field>
|
|
9369
|
+
</td>
|
|
9370
|
+
</tr>
|
|
9371
|
+
}
|
|
9372
|
+
</tbody>
|
|
9373
|
+
</table>
|
|
9374
|
+
} @else {
|
|
9375
|
+
<div class="rules-empty">Defina dependentes para sugerir o mapeamento.</div>
|
|
9376
|
+
}
|
|
9377
|
+
</mat-expansion-panel>
|
|
9378
|
+
<!-- Preview -->
|
|
9379
|
+
<mat-expansion-panel class="stack-panel">
|
|
9380
|
+
<mat-expansion-panel-header>
|
|
9381
|
+
<mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'visibility'"></mat-icon> Preview</mat-panel-title>
|
|
9382
|
+
</mat-expansion-panel-header>
|
|
9383
|
+
<pre class="preview-box">{{ buildPreview() }}</pre>
|
|
9384
|
+
<div style="display:flex; justify-content:flex-end; padding-top:6px">
|
|
9385
|
+
<button mat-stroked-button (click)="copyPreview()">Copiar</button>
|
|
9386
|
+
</div>
|
|
9387
|
+
</mat-expansion-panel>
|
|
9388
|
+
</div>
|
|
9389
|
+
<div style="display:flex; gap:8px; justify-content:flex-end; padding:8px 12px;">
|
|
9390
|
+
<button mat-stroked-button (click)="cancelEdit()">Cancelar</button>
|
|
9391
|
+
<button mat-flat-button color="primary" (click)="saveEdit(sel)">Salvar</button>
|
|
9392
|
+
</div>
|
|
7249
9393
|
</div>
|
|
7250
9394
|
</div>
|
|
7251
|
-
|
|
9395
|
+
}
|
|
7252
9396
|
</div>
|
|
7253
|
-
|
|
9397
|
+
`, 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"] }]
|
|
7254
9398
|
}], ctorParameters: () => [{ type: CascadeRulesService }], propDecorators: { fields: [{
|
|
7255
9399
|
type: Input
|
|
7256
9400
|
}], connections: [{
|
|
@@ -7293,6 +9437,388 @@ const METADATA_EDITOR_AI_CAPABILITY_CATALOG = {
|
|
|
7293
9437
|
enums: {},
|
|
7294
9438
|
};
|
|
7295
9439
|
|
|
9440
|
+
const fieldMetadataPropertySchema = {
|
|
9441
|
+
type: 'object',
|
|
9442
|
+
required: ['path', 'value'],
|
|
9443
|
+
properties: {
|
|
9444
|
+
path: {
|
|
9445
|
+
enum: [
|
|
9446
|
+
'name',
|
|
9447
|
+
'label',
|
|
9448
|
+
'description',
|
|
9449
|
+
'controlType',
|
|
9450
|
+
'placeholder',
|
|
9451
|
+
'defaultValue',
|
|
9452
|
+
'group',
|
|
9453
|
+
'order',
|
|
9454
|
+
'required',
|
|
9455
|
+
'disabled',
|
|
9456
|
+
'readOnly',
|
|
9457
|
+
'hidden',
|
|
9458
|
+
'source',
|
|
9459
|
+
'transient',
|
|
9460
|
+
'submitPolicy',
|
|
9461
|
+
'hint',
|
|
9462
|
+
'helpText',
|
|
9463
|
+
'tooltip',
|
|
9464
|
+
'options',
|
|
9465
|
+
'optionSource',
|
|
9466
|
+
'validators',
|
|
9467
|
+
'conditionalRequired',
|
|
9468
|
+
'conditionalDisplay',
|
|
9469
|
+
'visibleIn',
|
|
9470
|
+
'ariaLabel',
|
|
9471
|
+
'ariaDescribedBy',
|
|
9472
|
+
],
|
|
9473
|
+
},
|
|
9474
|
+
value: {},
|
|
9475
|
+
merge: { type: 'boolean' },
|
|
9476
|
+
},
|
|
9477
|
+
};
|
|
9478
|
+
const optionSourceSchema = {
|
|
9479
|
+
type: 'object',
|
|
9480
|
+
minProperties: 1,
|
|
9481
|
+
properties: {
|
|
9482
|
+
kind: { enum: ['static', 'remote', 'resource', 'lookup'] },
|
|
9483
|
+
resource: { type: 'string' },
|
|
9484
|
+
endpoint: { type: 'string' },
|
|
9485
|
+
valueField: { type: 'string' },
|
|
9486
|
+
labelField: { type: 'string' },
|
|
9487
|
+
dependsOn: { type: 'string' },
|
|
9488
|
+
dependencyFilterMap: { type: 'object' },
|
|
9489
|
+
display: {
|
|
9490
|
+
type: 'object',
|
|
9491
|
+
properties: {
|
|
9492
|
+
preset: { enum: ['compact', 'rich', 'directory', 'status', 'reference', 'hierarchical'] },
|
|
9493
|
+
usage: { enum: ['form', 'filter', 'table-cell', 'dashboard', 'wizard', 'review'] },
|
|
9494
|
+
density: { enum: ['compact', 'comfortable', 'rich'] },
|
|
9495
|
+
selectedLayout: { enum: ['card', 'inline', 'compact', 'token'] },
|
|
9496
|
+
resultLayout: { enum: ['list', 'denseList', 'table', 'card'] },
|
|
9497
|
+
primaryPropertyPath: { type: 'string' },
|
|
9498
|
+
fields: { type: 'array' },
|
|
9499
|
+
secondaryPropertyPaths: { type: 'array' },
|
|
9500
|
+
badgePropertyPaths: { type: 'array' },
|
|
9501
|
+
statusToneMap: { type: 'object' },
|
|
9502
|
+
},
|
|
9503
|
+
},
|
|
9504
|
+
detail: {
|
|
9505
|
+
type: 'object',
|
|
9506
|
+
properties: {
|
|
9507
|
+
kind: { enum: ['surface', 'route', 'href'] },
|
|
9508
|
+
surfaceId: { type: 'string' },
|
|
9509
|
+
presentation: { enum: ['drawer', 'modal'] },
|
|
9510
|
+
preferredWidget: { type: 'string' },
|
|
9511
|
+
mode: { enum: ['view', 'edit', 'create'] },
|
|
9512
|
+
},
|
|
9513
|
+
},
|
|
9514
|
+
options: { type: 'array' },
|
|
9515
|
+
},
|
|
9516
|
+
};
|
|
9517
|
+
const cascadeSchema = {
|
|
9518
|
+
type: 'object',
|
|
9519
|
+
required: ['dependentField'],
|
|
9520
|
+
properties: {
|
|
9521
|
+
dependentField: { type: 'string' },
|
|
9522
|
+
sourceField: { type: 'string' },
|
|
9523
|
+
strategy: { enum: ['replace', 'merge'] },
|
|
9524
|
+
debounceMs: { type: 'number' },
|
|
9525
|
+
loadMode: { enum: ['immediate', 'manual', 'respectLoadOn'] },
|
|
9526
|
+
dependencyFilterMap: { type: 'object' },
|
|
9527
|
+
optionSource: {
|
|
9528
|
+
type: 'object',
|
|
9529
|
+
properties: {
|
|
9530
|
+
kind: { enum: ['static', 'remote', 'resource', 'lookup'] },
|
|
9531
|
+
resource: { type: 'string' },
|
|
9532
|
+
endpoint: { type: 'string' },
|
|
9533
|
+
dependsOn: { type: 'string' },
|
|
9534
|
+
dependencyFilterMap: { type: 'object' },
|
|
9535
|
+
},
|
|
9536
|
+
},
|
|
9537
|
+
},
|
|
9538
|
+
};
|
|
9539
|
+
const PRAXIS_METADATA_EDITOR_AUTHORING_MANIFEST = {
|
|
9540
|
+
schemaVersion: '1.0.0',
|
|
9541
|
+
componentId: 'praxis-metadata-editor',
|
|
9542
|
+
ownerPackage: '@praxisui/metadata-editor',
|
|
9543
|
+
configSchemaId: 'FieldMetadata',
|
|
9544
|
+
manifestVersion: '1.0.0',
|
|
9545
|
+
runtimeInputs: [
|
|
9546
|
+
{ name: 'controlType', type: 'FieldControlType', description: 'Canonical control type used to resolve editor property coverage.' },
|
|
9547
|
+
{ name: 'seed', type: 'Partial<FieldMetadata | FieldDefinition>', description: 'Initial canonical field metadata used to hydrate the editor.' },
|
|
9548
|
+
{ name: 'fields', type: 'Array<FieldMetadata | FieldDefinition>', description: 'Available fields used by cascade authoring and context validation.' },
|
|
9549
|
+
{ name: 'properties', type: 'EditorProperty[]', description: 'Renderer property catalog resolved from ConfigRegistryService.' },
|
|
9550
|
+
{ name: 'form', type: 'FormGroup', description: 'Dynamic form generated from canonical editor properties by DynamicFormFactoryService.' },
|
|
9551
|
+
],
|
|
9552
|
+
editableTargets: [
|
|
9553
|
+
{ kind: 'fieldMetadata', resolver: 'field-metadata-json-path', description: 'Canonical FieldMetadata root and property paths.' },
|
|
9554
|
+
{ kind: 'controlType', resolver: 'dynamic-fields-control-type-discovery', description: 'Control type selected from dynamic-fields discovery and editor config registry.' },
|
|
9555
|
+
{ kind: 'optionSource', resolver: 'field-metadata-option-source', description: 'Canonical option source including backend x-ui option source fields where applicable.' },
|
|
9556
|
+
{ kind: 'cascade', resolver: 'metadata-editor-cascade-rules', description: 'Cascade dependencies and dependency filter mappings edited through CascadeManagerTab.' },
|
|
9557
|
+
{ kind: 'renderer', resolver: 'metadata-editor-renderer-property', description: 'Dynamic editor renderer property coverage and editor component mapping.' },
|
|
9558
|
+
{ kind: 'validation', resolver: 'field-metadata-validation-rules', description: 'Field validation rules and contextual validators.' },
|
|
9559
|
+
{ kind: 'contextHint', resolver: 'metadata-editor-context-hints', description: 'Authoring hints, help text and contextual guidance for metadata editors.' },
|
|
9560
|
+
{ kind: 'normalization', resolver: 'metadata-editor-schema-normalizer', description: 'Schema normalization and seed hydration rules.' },
|
|
9561
|
+
],
|
|
9562
|
+
operations: [
|
|
9563
|
+
{
|
|
9564
|
+
operationId: 'fieldMetadata.property.set',
|
|
9565
|
+
title: 'Set FieldMetadata property',
|
|
9566
|
+
scope: 'fieldMetadataPath',
|
|
9567
|
+
targetKind: 'fieldMetadata',
|
|
9568
|
+
target: { kind: 'fieldMetadata', resolver: 'field-metadata-json-path', ambiguityPolicy: 'fail', required: true },
|
|
9569
|
+
inputSchema: fieldMetadataPropertySchema,
|
|
9570
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'metadata-field-property-set', handlerContract: {
|
|
9571
|
+
reads: ['fieldMetadata', 'fieldMetadataPath', 'SchemaNormalizerService'],
|
|
9572
|
+
writes: ['fieldMetadata'],
|
|
9573
|
+
identityKeys: ['field.name', 'path'],
|
|
9574
|
+
inputSchema: fieldMetadataPropertySchema,
|
|
9575
|
+
failureModes: ['unknown-field-metadata-path', 'non-canonical-shape', 'readonly-field', 'normalization-failed'],
|
|
9576
|
+
description: 'Sets a canonical FieldMetadata property through a governed metadata path and re-runs schema normalization.',
|
|
9577
|
+
} }],
|
|
9578
|
+
destructive: false,
|
|
9579
|
+
requiresConfirmation: false,
|
|
9580
|
+
validators: ['field-metadata-shape-canonical', 'field-path-supported-by-editor', 'metadata-round-trip'],
|
|
9581
|
+
affectedPaths: ['fieldMetadata'],
|
|
9582
|
+
submissionImpact: 'affects-schema-backed-data',
|
|
9583
|
+
preconditions: ['field-metadata-loaded'],
|
|
9584
|
+
},
|
|
9585
|
+
{
|
|
9586
|
+
operationId: 'controlType.set',
|
|
9587
|
+
title: 'Set control type',
|
|
9588
|
+
scope: 'controlType',
|
|
9589
|
+
targetKind: 'controlType',
|
|
9590
|
+
target: { kind: 'controlType', resolver: 'dynamic-fields-control-type-discovery', ambiguityPolicy: 'fail', required: true },
|
|
9591
|
+
inputSchema: {
|
|
9592
|
+
type: 'object',
|
|
9593
|
+
required: ['controlType'],
|
|
9594
|
+
properties: {
|
|
9595
|
+
controlType: { type: 'string' },
|
|
9596
|
+
preserveCompatibleProperties: { type: 'boolean' },
|
|
9597
|
+
},
|
|
9598
|
+
},
|
|
9599
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'metadata-control-type-set', handlerContract: {
|
|
9600
|
+
reads: ['FieldControlType', 'ConfigRegistryService', 'dynamic-fields-discovery', 'fieldMetadata'],
|
|
9601
|
+
writes: ['fieldMetadata.controlType', 'properties', 'normalizedSeed'],
|
|
9602
|
+
identityKeys: ['field.name', 'controlType'],
|
|
9603
|
+
inputSchema: { type: 'object', required: ['controlType'], properties: { controlType: { type: 'string' }, preserveCompatibleProperties: { type: 'boolean' } } },
|
|
9604
|
+
failureModes: ['control-type-not-discovered', 'editor-coverage-missing', 'incompatible-metadata-preservation'],
|
|
9605
|
+
description: 'Changes control type only when dynamic-fields discovery and metadata-editor config registry both expose renderer/editor coverage.',
|
|
9606
|
+
} }],
|
|
9607
|
+
destructive: false,
|
|
9608
|
+
requiresConfirmation: false,
|
|
9609
|
+
validators: ['control-type-exists-in-discovery', 'editor-coverage-exists', 'metadata-round-trip'],
|
|
9610
|
+
affectedPaths: ['fieldMetadata.controlType', 'properties', 'normalizedSeed'],
|
|
9611
|
+
submissionImpact: 'affects-schema-backed-data',
|
|
9612
|
+
preconditions: ['dynamic-fields-discovery-loaded'],
|
|
9613
|
+
},
|
|
9614
|
+
{
|
|
9615
|
+
operationId: 'optionSource.configure',
|
|
9616
|
+
title: 'Configure option source',
|
|
9617
|
+
scope: 'dataBinding',
|
|
9618
|
+
targetKind: 'optionSource',
|
|
9619
|
+
target: { kind: 'optionSource', resolver: 'field-metadata-option-source', ambiguityPolicy: 'fail', required: false },
|
|
9620
|
+
inputSchema: optionSourceSchema,
|
|
9621
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'metadata-option-source-configure', handlerContract: {
|
|
9622
|
+
reads: ['fieldMetadata.optionSource', 'backend.x-ui.optionSource', 'SchemaNormalizerService'],
|
|
9623
|
+
writes: ['fieldMetadata.optionSource'],
|
|
9624
|
+
identityKeys: ['field.name', 'optionSource.kind', 'optionSource.resource'],
|
|
9625
|
+
inputSchema: optionSourceSchema,
|
|
9626
|
+
failureModes: ['invalid-option-source-kind', 'remote-source-missing-governed-resource', 'dependency-filter-map-invalid'],
|
|
9627
|
+
description: 'Configures canonical optionSource fields while preserving backend x-ui dependsOn and dependencyFilterMap semantics.',
|
|
9628
|
+
} }],
|
|
9629
|
+
destructive: false,
|
|
9630
|
+
requiresConfirmation: false,
|
|
9631
|
+
validators: ['option-source-shape-canonical', 'remote-option-source-governed', 'cascade-backend-shape-preserved', 'metadata-round-trip'],
|
|
9632
|
+
affectedPaths: ['fieldMetadata.optionSource'],
|
|
9633
|
+
submissionImpact: 'affects-remote-binding',
|
|
9634
|
+
preconditions: ['field-metadata-loaded'],
|
|
9635
|
+
},
|
|
9636
|
+
{
|
|
9637
|
+
operationId: 'cascade.configure',
|
|
9638
|
+
title: 'Configure cascade rule',
|
|
9639
|
+
scope: 'interaction',
|
|
9640
|
+
targetKind: 'cascade',
|
|
9641
|
+
target: { kind: 'cascade', resolver: 'metadata-editor-cascade-rules', ambiguityPolicy: 'fail', required: false },
|
|
9642
|
+
inputSchema: cascadeSchema,
|
|
9643
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'metadata-cascade-configure', handlerContract: {
|
|
9644
|
+
reads: ['CascadeManagerTabComponent', 'CascadeRulesService', 'fields', 'fieldMetadata.optionSource', 'fieldMetadata.dependencyFields', 'fieldMetadata.dependencyFilterMap'],
|
|
9645
|
+
writes: ['fieldMetadata.dependencyFields', 'fieldMetadata.enableDependencyCascade', 'fieldMetadata.resetOnDependentChange', 'fieldMetadata.dependencyFilterMap', 'fieldMetadata.dependencyValuePath', 'fieldMetadata.dependencyMergeStrategy', 'fieldMetadata.dependencyDebounceMs', 'fieldMetadata.dependencyLoadOnChange', 'fieldMetadata.optionSource.dependsOn', 'fieldMetadata.optionSource.dependencyFilterMap'],
|
|
9646
|
+
identityKeys: ['dependentField', 'sourceField'],
|
|
9647
|
+
inputSchema: cascadeSchema,
|
|
9648
|
+
failureModes: ['dependent-field-not-found', 'source-field-not-found', 'cascade-cycle-detected', 'dependency-filter-map-invalid'],
|
|
9649
|
+
description: 'Applies cascade dependencies through CascadeRulesService using the metadata-editor save contract while preserving backend x-ui optionSource dependency semantics during hydration.',
|
|
9650
|
+
} }],
|
|
9651
|
+
destructive: false,
|
|
9652
|
+
requiresConfirmation: false,
|
|
9653
|
+
validators: ['cascade-fields-exist', 'cascade-cycle-free', 'cascade-backend-shape-preserved', 'option-source-shape-canonical', 'remote-option-source-governed', 'metadata-round-trip'],
|
|
9654
|
+
affectedPaths: ['fieldMetadata.dependencyFields', 'fieldMetadata.enableDependencyCascade', 'fieldMetadata.resetOnDependentChange', 'fieldMetadata.dependencyFilterMap', 'fieldMetadata.dependencyValuePath', 'fieldMetadata.dependencyMergeStrategy', 'fieldMetadata.dependencyDebounceMs', 'fieldMetadata.dependencyLoadOnChange', 'fieldMetadata.optionSource.dependsOn', 'fieldMetadata.optionSource.dependencyFilterMap'],
|
|
9655
|
+
submissionImpact: 'affects-remote-binding',
|
|
9656
|
+
preconditions: ['field-list-loaded'],
|
|
9657
|
+
},
|
|
9658
|
+
{
|
|
9659
|
+
operationId: 'renderer.configure',
|
|
9660
|
+
title: 'Configure renderer coverage',
|
|
9661
|
+
scope: 'editorCoverage',
|
|
9662
|
+
targetKind: 'renderer',
|
|
9663
|
+
target: { kind: 'renderer', resolver: 'metadata-editor-renderer-property', ambiguityPolicy: 'fail', required: true },
|
|
9664
|
+
inputSchema: {
|
|
9665
|
+
type: 'object',
|
|
9666
|
+
required: ['propertyName', 'editorType'],
|
|
9667
|
+
properties: {
|
|
9668
|
+
propertyName: { type: 'string' },
|
|
9669
|
+
editorType: { type: 'string' },
|
|
9670
|
+
group: { type: 'string' },
|
|
9671
|
+
required: { type: 'boolean' },
|
|
9672
|
+
options: { type: 'array' },
|
|
9673
|
+
},
|
|
9674
|
+
},
|
|
9675
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'metadata-renderer-configure', handlerContract: {
|
|
9676
|
+
reads: ['DynamicEditorRendererComponent', 'EditorComponentRegistryService', 'ConfigRegistryService', 'EditorProperty'],
|
|
9677
|
+
writes: ['properties', 'editorCoverage'],
|
|
9678
|
+
identityKeys: ['controlType', 'propertyName'],
|
|
9679
|
+
inputSchema: { type: 'object', required: ['propertyName', 'editorType'], properties: { propertyName: { type: 'string' }, editorType: { type: 'string' }, group: { type: 'string' } } },
|
|
9680
|
+
failureModes: ['editor-type-not-registered', 'property-not-covered', 'json-only-coverage-not-accepted'],
|
|
9681
|
+
description: 'Configures visual renderer coverage for a metadata property and rejects JSON-only support for required visual authoring.',
|
|
9682
|
+
} }],
|
|
9683
|
+
destructive: false,
|
|
9684
|
+
requiresConfirmation: false,
|
|
9685
|
+
validators: ['renderer-editor-type-registered', 'visual-editor-coverage-required', 'editor-coverage-exists', 'metadata-round-trip'],
|
|
9686
|
+
affectedPaths: ['properties', 'editorCoverage'],
|
|
9687
|
+
submissionImpact: 'config-only',
|
|
9688
|
+
preconditions: ['config-registry-loaded'],
|
|
9689
|
+
},
|
|
9690
|
+
{
|
|
9691
|
+
operationId: 'validationRule.add',
|
|
9692
|
+
title: 'Add validation rule',
|
|
9693
|
+
scope: 'fieldMetadataPath',
|
|
9694
|
+
targetKind: 'validation',
|
|
9695
|
+
target: { kind: 'validation', resolver: 'field-metadata-validation-rules', ambiguityPolicy: 'fail', required: false },
|
|
9696
|
+
inputSchema: {
|
|
9697
|
+
type: 'object',
|
|
9698
|
+
required: ['rule'],
|
|
9699
|
+
properties: {
|
|
9700
|
+
rule: { type: 'object' },
|
|
9701
|
+
message: { type: 'string' },
|
|
9702
|
+
contextValidatorId: { type: 'string' },
|
|
9703
|
+
},
|
|
9704
|
+
},
|
|
9705
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'metadata-validation-rule-add', handlerContract: {
|
|
9706
|
+
reads: ['fieldMetadata.validators', 'ContextValidatorRegistryService', 'DynamicFormFactoryService'],
|
|
9707
|
+
writes: ['fieldMetadata.validators'],
|
|
9708
|
+
identityKeys: ['field.name', 'rule.type', 'contextValidatorId'],
|
|
9709
|
+
inputSchema: { type: 'object', required: ['rule'], properties: { rule: { type: 'object' }, message: { type: 'string' }, contextValidatorId: { type: 'string' } } },
|
|
9710
|
+
failureModes: ['validation-rule-invalid', 'context-validator-not-registered', 'duplicate-validation-rule'],
|
|
9711
|
+
description: 'Adds a canonical validation rule and verifies contextual validator availability before persistence.',
|
|
9712
|
+
} }],
|
|
9713
|
+
destructive: false,
|
|
9714
|
+
requiresConfirmation: false,
|
|
9715
|
+
validators: ['validation-rule-canonical', 'context-validator-registered', 'metadata-round-trip'],
|
|
9716
|
+
affectedPaths: ['fieldMetadata.validators'],
|
|
9717
|
+
submissionImpact: 'affects-submission',
|
|
9718
|
+
preconditions: ['field-metadata-loaded'],
|
|
9719
|
+
},
|
|
9720
|
+
{
|
|
9721
|
+
operationId: 'contextHint.set',
|
|
9722
|
+
title: 'Set metadata context hint',
|
|
9723
|
+
scope: 'meta',
|
|
9724
|
+
targetKind: 'contextHint',
|
|
9725
|
+
target: { kind: 'contextHint', resolver: 'metadata-editor-context-hints', ambiguityPolicy: 'fail', required: false },
|
|
9726
|
+
inputSchema: {
|
|
9727
|
+
type: 'object',
|
|
9728
|
+
required: ['hintPath', 'value'],
|
|
9729
|
+
properties: {
|
|
9730
|
+
hintPath: { enum: ['hint', 'helpText', 'description', 'tooltip', 'ariaLabel', 'ariaDescribedBy'] },
|
|
9731
|
+
value: {},
|
|
9732
|
+
localeKey: { type: 'string' },
|
|
9733
|
+
},
|
|
9734
|
+
},
|
|
9735
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'metadata-context-hint-set', handlerContract: {
|
|
9736
|
+
reads: ['fieldMetadata.hint', 'fieldMetadata.helpText', 'fieldMetadata.description', 'fieldMetadata.tooltip', 'fieldMetadata.ariaLabel', 'fieldMetadata.ariaDescribedBy', 'metadata-editor-i18n'],
|
|
9737
|
+
writes: ['fieldMetadata.hint', 'fieldMetadata.helpText', 'fieldMetadata.description', 'fieldMetadata.tooltip', 'fieldMetadata.ariaLabel', 'fieldMetadata.ariaDescribedBy'],
|
|
9738
|
+
identityKeys: ['field.name', 'hintPath'],
|
|
9739
|
+
inputSchema: { type: 'object', required: ['hintPath', 'value'], properties: { hintPath: { enum: ['hint', 'helpText', 'description', 'tooltip', 'ariaLabel', 'ariaDescribedBy'] }, value: {}, localeKey: { type: 'string' } } },
|
|
9740
|
+
failureModes: ['hint-path-not-canonical', 'hint-value-not-serializable', 'i18n-key-missing'],
|
|
9741
|
+
description: 'Sets structured FieldMetadata hint/help/accessibility text through canonical metadata paths and metadata-editor i18n constraints.',
|
|
9742
|
+
} }],
|
|
9743
|
+
destructive: false,
|
|
9744
|
+
requiresConfirmation: false,
|
|
9745
|
+
validators: ['context-hint-shape-canonical', 'context-hint-i18n-compatible', 'metadata-round-trip'],
|
|
9746
|
+
affectedPaths: ['fieldMetadata.hint', 'fieldMetadata.helpText', 'fieldMetadata.description', 'fieldMetadata.tooltip', 'fieldMetadata.ariaLabel', 'fieldMetadata.ariaDescribedBy'],
|
|
9747
|
+
submissionImpact: 'visual-only',
|
|
9748
|
+
preconditions: ['field-metadata-loaded'],
|
|
9749
|
+
},
|
|
9750
|
+
{
|
|
9751
|
+
operationId: 'normalization.apply',
|
|
9752
|
+
title: 'Apply schema normalization',
|
|
9753
|
+
scope: 'runtimeCoverage',
|
|
9754
|
+
targetKind: 'normalization',
|
|
9755
|
+
target: { kind: 'normalization', resolver: 'metadata-editor-schema-normalizer', ambiguityPolicy: 'fail', required: false },
|
|
9756
|
+
inputSchema: {
|
|
9757
|
+
type: 'object',
|
|
9758
|
+
properties: {
|
|
9759
|
+
mode: { enum: ['hydrate-seed', 'coerce-types', 'apply-defaults', 'preserve-advanced-properties'] },
|
|
9760
|
+
preserveUnknownCanonicalFields: { type: 'boolean' },
|
|
9761
|
+
},
|
|
9762
|
+
},
|
|
9763
|
+
effects: [{ kind: 'compile-domain-patch', handler: 'metadata-normalization-apply', handlerContract: {
|
|
9764
|
+
reads: ['SchemaNormalizerService', 'fieldMetadata', 'properties', 'DynamicFormFactoryService'],
|
|
9765
|
+
writes: ['normalizedSeed', 'form', 'fieldMetadata'],
|
|
9766
|
+
identityKeys: ['field.name', 'controlType', 'mode'],
|
|
9767
|
+
inputSchema: { type: 'object', properties: { mode: { enum: ['hydrate-seed', 'coerce-types', 'apply-defaults', 'preserve-advanced-properties'] }, preserveUnknownCanonicalFields: { type: 'boolean' } } },
|
|
9768
|
+
failureModes: ['normalizer-missing', 'type-coercion-failed', 'advanced-property-lost', 'runtime-editor-drift'],
|
|
9769
|
+
description: 'Runs metadata-editor schema normalization while preserving canonical advanced FieldMetadata properties and editor/runtime round-trip.',
|
|
9770
|
+
} }],
|
|
9771
|
+
destructive: false,
|
|
9772
|
+
requiresConfirmation: false,
|
|
9773
|
+
validators: ['normalization-preserves-canonical-fields', 'runtime-editor-round-trip', 'metadata-round-trip'],
|
|
9774
|
+
affectedPaths: ['normalizedSeed', 'form', 'fieldMetadata'],
|
|
9775
|
+
submissionImpact: 'config-only',
|
|
9776
|
+
preconditions: ['field-metadata-loaded', 'editor-properties-loaded'],
|
|
9777
|
+
},
|
|
9778
|
+
],
|
|
9779
|
+
validators: [
|
|
9780
|
+
{ validatorId: 'field-metadata-shape-canonical', level: 'error', code: 'METADATA_FIELD_SHAPE_CANONICAL', description: 'Edited metadata must remain compatible with canonical FieldMetadata.' },
|
|
9781
|
+
{ 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.' },
|
|
9782
|
+
{ validatorId: 'control-type-exists-in-discovery', level: 'error', code: 'METADATA_CONTROL_TYPE_DISCOVERED', description: 'Control type must exist in dynamic-fields discovery.' },
|
|
9783
|
+
{ validatorId: 'editor-coverage-exists', level: 'error', code: 'METADATA_EDITOR_COVERAGE_EXISTS', description: 'Control type and property must have metadata-editor coverage.' },
|
|
9784
|
+
{ validatorId: 'option-source-shape-canonical', level: 'error', code: 'METADATA_OPTION_SOURCE_CANONICAL', description: 'Option source must use canonical FieldMetadata/x-ui shape.' },
|
|
9785
|
+
{ 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.' },
|
|
9786
|
+
{ validatorId: 'cascade-fields-exist', level: 'error', code: 'METADATA_CASCADE_FIELDS_EXIST', description: 'Cascade source and dependent fields must exist.' },
|
|
9787
|
+
{ validatorId: 'cascade-cycle-free', level: 'error', code: 'METADATA_CASCADE_CYCLE_FREE', description: 'Cascade dependencies must not create cycles.' },
|
|
9788
|
+
{ 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.' },
|
|
9789
|
+
{ validatorId: 'renderer-editor-type-registered', level: 'error', code: 'METADATA_RENDERER_TYPE_REGISTERED', description: 'Renderer editor type must be registered.' },
|
|
9790
|
+
{ validatorId: 'visual-editor-coverage-required', level: 'error', code: 'METADATA_VISUAL_COVERAGE_REQUIRED', description: 'JSON-only support is not accepted for required visual authoring coverage.' },
|
|
9791
|
+
{ validatorId: 'validation-rule-canonical', level: 'error', code: 'METADATA_VALIDATION_RULE_CANONICAL', description: 'Validation rule must use canonical metadata shape.' },
|
|
9792
|
+
{ validatorId: 'context-validator-registered', level: 'error', code: 'METADATA_CONTEXT_VALIDATOR_REGISTERED', description: 'Contextual validator must be registered before use.' },
|
|
9793
|
+
{ 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.' },
|
|
9794
|
+
{ validatorId: 'context-hint-i18n-compatible', level: 'warning', code: 'METADATA_CONTEXT_HINT_I18N_COMPATIBLE', description: 'Authoring hint text must remain compatible with metadata-editor i18n.' },
|
|
9795
|
+
{ validatorId: 'normalization-preserves-canonical-fields', level: 'error', code: 'METADATA_NORMALIZATION_PRESERVES_FIELDS', description: 'Normalization must not drop canonical advanced FieldMetadata properties.' },
|
|
9796
|
+
{ validatorId: 'runtime-editor-round-trip', level: 'error', code: 'METADATA_RUNTIME_EDITOR_ROUND_TRIP', description: 'Runtime and editor must consume the same normalized metadata shape.' },
|
|
9797
|
+
{ validatorId: 'metadata-round-trip', level: 'error', code: 'METADATA_ROUND_TRIP', description: 'Open, edit, apply/save, reopen and runtime consume must preserve metadata.' },
|
|
9798
|
+
],
|
|
9799
|
+
roundTripRequirements: [
|
|
9800
|
+
'FieldMetadata is the canonical edited document shape; backend x-ui-derived metadata must not be rewritten into host-only aliases.',
|
|
9801
|
+
'Control type changes require both dynamic-fields discovery and metadata-editor visual coverage.',
|
|
9802
|
+
'Option source cascades preserve x-ui optionSource.dependsOn and optionSource.dependencyFilterMap semantics.',
|
|
9803
|
+
'JSON-only support is incomplete when visual editor coverage is required.',
|
|
9804
|
+
'Schema normalization must preserve advanced canonical properties through open, edit, apply/save, reopen and runtime consumption.',
|
|
9805
|
+
],
|
|
9806
|
+
examples: [
|
|
9807
|
+
{ id: 'set-label', request: 'Set this field label to Customer name.', operationId: 'fieldMetadata.property.set', params: { path: 'label', value: 'Customer name' }, isPositive: true },
|
|
9808
|
+
{ id: 'set-control-type', request: 'Change this field to a select control.', operationId: 'controlType.set', params: { controlType: 'select', preserveCompatibleProperties: true }, isPositive: true },
|
|
9809
|
+
{ 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 },
|
|
9810
|
+
{ id: 'configure-cascade', request: 'Filter city options when state changes.', operationId: 'cascade.configure', params: { dependentField: 'city', sourceField: 'state', dependencyFilterMap: { stateId: 'state.id' } }, isPositive: true },
|
|
9811
|
+
{ 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 },
|
|
9812
|
+
{ 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 },
|
|
9813
|
+
{ 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 },
|
|
9814
|
+
{ 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 },
|
|
9815
|
+
{ id: 'reject-unknown-control', request: 'Use an unknown control type named magic-picker.', operationId: 'controlType.set', params: { controlType: 'magic-picker' }, isPositive: false },
|
|
9816
|
+
{ id: 'reject-json-only-coverage', request: 'Support this field only by editing raw JSON.', operationId: 'renderer.configure', params: { propertyName: 'optionSource', editorType: 'json' }, isPositive: false },
|
|
9817
|
+
{ 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 },
|
|
9818
|
+
{ 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 },
|
|
9819
|
+
],
|
|
9820
|
+
};
|
|
9821
|
+
|
|
7296
9822
|
/** Metadata for Praxis Metadata Editor component */
|
|
7297
9823
|
const PRAXIS_METADATA_EDITOR_COMPONENT_METADATA = {
|
|
7298
9824
|
id: 'praxis-metadata-editor',
|
|
@@ -7347,4 +9873,4 @@ function providePraxisMetadataEditorMetadata() {
|
|
|
7347
9873
|
* Generated bundle index. Do not edit.
|
|
7348
9874
|
*/
|
|
7349
9875
|
|
|
7350
|
-
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 };
|
|
9876
|
+
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 };
|