@praxisui/metadata-editor 8.0.0-beta.31 → 8.0.0-beta.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,40 +1,40 @@
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 i2 from '@angular/forms';
3
+ import * as i1 from '@angular/forms';
4
4
  import { FormControl, FormGroup, ReactiveFormsModule, FormArray, FormBuilder, FormsModule } from '@angular/forms';
5
5
  import * as i3 from '@angular/common';
6
6
  import { CommonModule } from '@angular/common';
7
7
  import { DynamicFieldLoaderDirective } from '@praxisui/dynamic-fields';
8
- import * as i2$2 from '@praxisui/core';
8
+ import * as i2$1 from '@praxisui/core';
9
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 i5$1 from '@angular/material/button';
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';
20
- import * as i2$1 from '@angular/material/list';
20
+ import * as i2 from '@angular/material/list';
21
21
  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 i7 from '@angular/material/input';
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 i8$1 from '@angular/material/select';
29
+ import * as i7 from '@angular/material/select';
30
30
  import { MatSelectModule } from '@angular/material/select';
31
- import * as i9 from '@angular/material/checkbox';
31
+ import * as i8$1 from '@angular/material/checkbox';
32
32
  import { MatCheckboxModule } from '@angular/material/checkbox';
33
- import * as i11 from '@angular/material/menu';
33
+ import * as i10 from '@angular/material/menu';
34
34
  import { MatMenuModule } from '@angular/material/menu';
35
- import * as i12 from '@angular/material/autocomplete';
35
+ import * as i11 from '@angular/material/autocomplete';
36
36
  import { MatAutocompleteModule } from '@angular/material/autocomplete';
37
- import * as i13 from '@angular/cdk/scrolling';
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: "20.3.17", ngImport: i0, type: ConfigRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
55
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ConfigRegistryService, providedIn: 'root' });
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: "20.3.17", ngImport: i0, type: ConfigRegistryService, decorators: [{
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: "20.3.17", ngImport: i0, type: ContextValidatorRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
73
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ContextValidatorRegistryService, providedIn: 'root' });
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: "20.3.17", ngImport: i0, type: ContextValidatorRegistryService, decorators: [{
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: "20.3.17", ngImport: i0, type: SchemaNormalizerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
186
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SchemaNormalizerService, providedIn: 'root' });
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: "20.3.17", ngImport: i0, type: SchemaNormalizerService, decorators: [{
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: "20.3.17", ngImport: i0, type: DynamicFormFactoryService, deps: [{ token: i2.FormBuilder }, { token: ContextValidatorRegistryService }], target: i0.ɵɵFactoryTarget.Injectable });
280
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DynamicFormFactoryService, providedIn: 'root' });
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: "20.3.17", ngImport: i0, type: DynamicFormFactoryService, decorators: [{
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: i2.FormBuilder }, { type: ContextValidatorRegistryService }] });
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: "20.3.17", ngImport: i0, type: EditorComponentRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
309
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: EditorComponentRegistryService, providedIn: 'root' });
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: "20.3.17", ngImport: i0, type: EditorComponentRegistryService, decorators: [{
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: "20.3.17", ngImport: i0, type: RegexHelpDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
353
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: RegexHelpDialogComponent, isStandalone: true, selector: "praxis-regex-help-dialog", ngImport: i0, template: `
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: CommonModule }, { kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: i1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatListModule }, { kind: "component", type: i2$1.MatNavList, selector: "mat-nav-list", exportAs: ["matNavList"] }, { kind: "component", type: i2$1.MatListItem, selector: "mat-list-item, a[mat-list-item], button[mat-list-item]", inputs: ["activated"], exportAs: ["matListItem"] }, { kind: "directive", type: i2$1.MatListItemLine, selector: "[matListItemLine]" }, { kind: "directive", type: i2$1.MatListItemTitle, selector: "[matListItemTitle]" }, { kind: "ngmodule", type: MatButtonModule }] });
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: "20.3.17", ngImport: i0, type: RegexHelpDialogComponent, decorators: [{
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: [CommonModule, MatDialogModule, MatListModule, MatButtonModule],
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>
@@ -684,79 +684,87 @@ class DynamicEditorRendererComponent {
684
684
  this.form?.markAsDirty();
685
685
  }
686
686
  }
687
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DynamicEditorRendererComponent, deps: [{ token: i1.MatDialog }, { token: i2$2.IconPickerService }], target: i0.ɵɵFactoryTarget.Component });
688
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: DynamicEditorRendererComponent, isStandalone: true, selector: "praxis-dynamic-editor-renderer", inputs: { properties: "properties", form: "form" }, usesOnChanges: true, ngImport: i0, template: `
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: `
689
689
  <ng-template #item let-field="field" let-index="index" let-content="content">
690
690
  <div class="editor-item" [attr.data-control-type]="field.controlType">
691
691
  <ng-container [ngTemplateOutlet]="content"></ng-container>
692
-
692
+
693
693
  <div class="actions-container">
694
694
  <!-- Botão de ajuda para regex -->
695
- <button
696
- *ngIf="field.name === 'pattern'"
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>
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
+ }
706
707
  <!-- Botão para escolher ícone (prefix/suffix) -->
707
- <button
708
- *ngIf="isIconField(field.name)"
709
- type="button"
710
- mat-icon-button
711
- class="icon-picker-btn decorator-btn"
712
- (click)="openIconPicker(field.name)"
713
- aria-label="Escolher ícone"
714
- matTooltip="Escolher ícone"
715
- >
716
- <mat-icon [praxisIcon]="'apps'"></mat-icon>
717
- </button>
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
+ }
718
720
  <!-- Botão de ajuda (fallback quando suffix não é aplicado) -->
719
- <button
720
- *ngIf="field.hint && !useHelpSuffix(field)"
721
- type="button"
722
- mat-icon-button
723
- class="hint-btn decorator-btn"
724
- [class.has-icon-picker]="isIconField(field.name)"
725
- [matTooltip]="field.hint"
726
- aria-label="Ajuda"
727
- >
728
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
729
- </button>
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
+ }
730
733
  </div>
731
734
  </div>
732
735
  </ng-template>
733
-
736
+
734
737
  <div class="editor-surface">
735
738
  <mat-accordion class="section-accordion" multi>
736
- <mat-expansion-panel class="section-card" *ngFor="let g of groupOrder">
737
- <mat-expansion-panel-header class="section-header">
738
- <mat-panel-title>
739
- <span class="section-title">
740
- <mat-icon class="section-icon" [praxisIcon]="groupIcon(g)"></mat-icon>
741
- {{ g }}
742
- </span>
743
- </mat-panel-title>
744
- </mat-expansion-panel-header>
745
-
746
- <div *ngFor="let row of groupRows[g]" class="editor-row" [ngClass]="rowClass(row)">
747
- <ng-container *ngIf="ready"
748
- dynamicFieldLoader
749
- [fields]="row"
750
- [formGroup]="form"
751
- [itemTemplate]="item">
752
- </ng-container>
753
- </div>
754
- </mat-expansion-panel>
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
+ }
755
763
  </mat-accordion>
756
764
  </div>
757
- `, 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.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.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: i5$1.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 });
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 });
758
766
  }
759
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DynamicEditorRendererComponent, decorators: [{
767
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicEditorRendererComponent, decorators: [{
760
768
  type: Component,
761
769
  args: [{ selector: 'praxis-dynamic-editor-renderer', standalone: true, imports: [
762
770
  CommonModule,
@@ -772,73 +780,81 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
772
780
  <ng-template #item let-field="field" let-index="index" let-content="content">
773
781
  <div class="editor-item" [attr.data-control-type]="field.controlType">
774
782
  <ng-container [ngTemplateOutlet]="content"></ng-container>
775
-
783
+
776
784
  <div class="actions-container">
777
785
  <!-- Botão de ajuda para regex -->
778
- <button
779
- *ngIf="field.name === 'pattern'"
780
- type="button"
781
- mat-icon-button
782
- class="regex-help-btn decorator-btn"
783
- (click)="openRegexHelp()"
784
- aria-label="Exemplos de regex"
785
- matTooltip="Exemplos de regex"
786
- >
787
- <mat-icon [praxisIcon]="'search'"></mat-icon>
788
- </button>
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
+ }
789
798
  <!-- Botão para escolher ícone (prefix/suffix) -->
790
- <button
791
- *ngIf="isIconField(field.name)"
792
- type="button"
793
- mat-icon-button
794
- class="icon-picker-btn decorator-btn"
795
- (click)="openIconPicker(field.name)"
796
- aria-label="Escolher ícone"
797
- matTooltip="Escolher ícone"
798
- >
799
- <mat-icon [praxisIcon]="'apps'"></mat-icon>
800
- </button>
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
+ }
801
811
  <!-- Botão de ajuda (fallback quando suffix não é aplicado) -->
802
- <button
803
- *ngIf="field.hint && !useHelpSuffix(field)"
804
- type="button"
805
- mat-icon-button
806
- class="hint-btn decorator-btn"
807
- [class.has-icon-picker]="isIconField(field.name)"
808
- [matTooltip]="field.hint"
809
- aria-label="Ajuda"
810
- >
811
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
812
- </button>
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
+ }
813
824
  </div>
814
825
  </div>
815
826
  </ng-template>
816
-
827
+
817
828
  <div class="editor-surface">
818
829
  <mat-accordion class="section-accordion" multi>
819
- <mat-expansion-panel class="section-card" *ngFor="let g of groupOrder">
820
- <mat-expansion-panel-header class="section-header">
821
- <mat-panel-title>
822
- <span class="section-title">
823
- <mat-icon class="section-icon" [praxisIcon]="groupIcon(g)"></mat-icon>
824
- {{ g }}
825
- </span>
826
- </mat-panel-title>
827
- </mat-expansion-panel-header>
828
-
829
- <div *ngFor="let row of groupRows[g]" class="editor-row" [ngClass]="rowClass(row)">
830
- <ng-container *ngIf="ready"
831
- dynamicFieldLoader
832
- [fields]="row"
833
- [formGroup]="form"
834
- [itemTemplate]="item">
835
- </ng-container>
836
- </div>
837
- </mat-expansion-panel>
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
+ }
838
854
  </mat-accordion>
839
855
  </div>
840
- `, 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"] }]
841
- }], ctorParameters: () => [{ type: i1.MatDialog }, { type: i2$2.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: [{
842
858
  type: Input
843
859
  }], form: [{
844
860
  type: Input
@@ -1447,13 +1463,13 @@ class ArrayItemSchemaFieldsEditorComponent {
1447
1463
  escapeRegExp(value) {
1448
1464
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1449
1465
  }
1450
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ArrayItemSchemaFieldsEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1451
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: ArrayItemSchemaFieldsEditorComponent, isStandalone: true, selector: "praxis-array-item-schema-fields-editor", inputs: { control: "control", arrayControl: "arrayControl" }, providers: [providePraxisMetadataEditorI18n()], usesOnChanges: true, ngImport: i0, template: `
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: `
1452
1468
  <section
1453
1469
  class="array-fields-editor"
1454
1470
  [attr.data-schema-source]="schemaSourceState"
1455
1471
  [attr.aria-label]="text('praxis.metadataEditor.arrayFields.section', 'Subcampos da cole\u00e7\u00e3o')"
1456
- >
1472
+ >
1457
1473
  <header class="array-fields-editor__header">
1458
1474
  <div>
1459
1475
  <h3>{{ text('praxis.metadataEditor.arrayFields.title', 'Subcampos') }}</h3>
@@ -1465,104 +1481,106 @@ class ArrayItemSchemaFieldsEditorComponent {
1465
1481
  {{ text('praxis.metadataEditor.arrayFields.add', 'Adicionar subcampo') }}
1466
1482
  </button>
1467
1483
  </header>
1468
-
1469
- <div
1470
- class="array-fields-editor__warning"
1471
- *ngIf="duplicateNames.length"
1472
- role="alert"
1473
- >
1474
- {{
1484
+
1485
+ @if (duplicateNames.length) {
1486
+ <div
1487
+ class="array-fields-editor__warning"
1488
+ role="alert"
1489
+ >
1490
+ {{
1475
1491
  text(
1476
- 'praxis.metadataEditor.arrayFields.duplicateNames',
1477
- undefined,
1478
- 'Existem nomes duplicados: {{names}}',
1479
- { names: duplicateNames.join(', ') }
1492
+ 'praxis.metadataEditor.arrayFields.duplicateNames',
1493
+ undefined,
1494
+ 'Existem nomes duplicados: {{names}}',
1495
+ { names: duplicateNames.join(', ') }
1480
1496
  )
1481
- }}
1482
- </div>
1483
-
1484
- <div
1485
- class="array-fields-editor__empty"
1486
- *ngIf="formArray.length === 0"
1487
- >
1488
- {{ text('praxis.metadataEditor.arrayFields.empty', 'Nenhum subcampo local configurado.') }}
1489
- </div>
1490
-
1491
- <div class="array-fields-editor__list" [formGroup]="form">
1497
+ }}
1498
+ </div>
1499
+ }
1500
+
1501
+ @if (formArray.length === 0) {
1492
1502
  <div
1493
- class="array-fields-editor__row"
1494
- *ngFor="let row of rows; let i = index"
1495
- [formGroup]="row"
1496
- >
1497
- <label>
1498
- <span>{{ text('praxis.metadataEditor.arrayFields.name', 'Nome') }}</span>
1499
- <input
1500
- type="text"
1501
- formControlName="name"
1502
- (blur)="onNameBlur(i)"
1503
- [attr.aria-invalid]="isNameInvalid(i) ? 'true' : null"
1504
- [attr.aria-describedby]="nameErrorDescribedBy(i)"
1505
- />
1506
- <span
1507
- class="array-fields-editor__field-error"
1508
- *ngIf="nameErrorMessage(i) as nameError"
1509
- [id]="nameErrorId(i)"
1510
- >
1511
- {{ nameError }}
1512
- </span>
1513
- </label>
1514
-
1515
- <label>
1516
- <span>{{ text('praxis.metadataEditor.arrayFields.label', 'Label') }}</span>
1517
- <input type="text" formControlName="label" />
1518
- </label>
1519
-
1520
- <label>
1521
- <span>{{ text('praxis.metadataEditor.arrayFields.controlType', 'Controle') }}</span>
1522
- <select formControlName="controlType">
1523
- <option *ngFor="let option of controlTypeOptions" [value]="option">
1524
- {{ controlTypeLabel(option) }}
1525
- </option>
1526
- </select>
1527
- </label>
1528
-
1529
- <label class="array-fields-editor__check">
1530
- <input type="checkbox" formControlName="required" />
1531
- <span>{{ text('praxis.metadataEditor.arrayFields.required', 'Obrigat\u00f3rio') }}</span>
1532
- </label>
1533
-
1534
- <div class="array-fields-editor__actions">
1535
- <button type="button" (click)="moveField(i, -1)" [disabled]="i === 0">
1536
- {{ text('praxis.metadataEditor.arrayFields.moveUp', 'Subir') }}
1537
- </button>
1538
- <button type="button" (click)="moveField(i, 1)" [disabled]="i === formArray.length - 1">
1539
- {{ text('praxis.metadataEditor.arrayFields.moveDown', 'Descer') }}
1540
- </button>
1541
- <button type="button" (click)="duplicateField(i)">
1542
- {{ text('praxis.metadataEditor.arrayFields.duplicate', 'Duplicar') }}
1543
- </button>
1544
- <button
1545
- type="button"
1546
- (click)="removeField(i)"
1547
- [disabled]="isReferenced(row.get('name')?.value)"
1548
- [title]="removeTitle(row.get('name')?.value)"
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"
1549
1514
  >
1550
- {{ text('praxis.metadataEditor.arrayFields.remove', 'Remover') }}
1551
- </button>
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>
1552
1570
  </div>
1553
- </div>
1571
+ }
1554
1572
  </div>
1555
1573
  </section>
1556
- `, 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: CommonModule }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.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: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] });
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"] }] });
1557
1575
  }
1558
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ArrayItemSchemaFieldsEditorComponent, decorators: [{
1576
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ArrayItemSchemaFieldsEditorComponent, decorators: [{
1559
1577
  type: Component,
1560
- args: [{ selector: 'praxis-array-item-schema-fields-editor', standalone: true, imports: [CommonModule, ReactiveFormsModule], providers: [providePraxisMetadataEditorI18n()], template: `
1578
+ args: [{ selector: 'praxis-array-item-schema-fields-editor', standalone: true, imports: [ReactiveFormsModule], providers: [providePraxisMetadataEditorI18n()], template: `
1561
1579
  <section
1562
1580
  class="array-fields-editor"
1563
1581
  [attr.data-schema-source]="schemaSourceState"
1564
1582
  [attr.aria-label]="text('praxis.metadataEditor.arrayFields.section', 'Subcampos da cole\u00e7\u00e3o')"
1565
- >
1583
+ >
1566
1584
  <header class="array-fields-editor__header">
1567
1585
  <div>
1568
1586
  <h3>{{ text('praxis.metadataEditor.arrayFields.title', 'Subcampos') }}</h3>
@@ -1574,95 +1592,97 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
1574
1592
  {{ text('praxis.metadataEditor.arrayFields.add', 'Adicionar subcampo') }}
1575
1593
  </button>
1576
1594
  </header>
1577
-
1578
- <div
1579
- class="array-fields-editor__warning"
1580
- *ngIf="duplicateNames.length"
1581
- role="alert"
1582
- >
1583
- {{
1595
+
1596
+ @if (duplicateNames.length) {
1597
+ <div
1598
+ class="array-fields-editor__warning"
1599
+ role="alert"
1600
+ >
1601
+ {{
1584
1602
  text(
1585
- 'praxis.metadataEditor.arrayFields.duplicateNames',
1586
- undefined,
1587
- 'Existem nomes duplicados: {{names}}',
1588
- { names: duplicateNames.join(', ') }
1603
+ 'praxis.metadataEditor.arrayFields.duplicateNames',
1604
+ undefined,
1605
+ 'Existem nomes duplicados: {{names}}',
1606
+ { names: duplicateNames.join(', ') }
1589
1607
  )
1590
- }}
1591
- </div>
1592
-
1593
- <div
1594
- class="array-fields-editor__empty"
1595
- *ngIf="formArray.length === 0"
1596
- >
1597
- {{ text('praxis.metadataEditor.arrayFields.empty', 'Nenhum subcampo local configurado.') }}
1598
- </div>
1599
-
1600
- <div class="array-fields-editor__list" [formGroup]="form">
1608
+ }}
1609
+ </div>
1610
+ }
1611
+
1612
+ @if (formArray.length === 0) {
1601
1613
  <div
1602
- class="array-fields-editor__row"
1603
- *ngFor="let row of rows; let i = index"
1604
- [formGroup]="row"
1605
- >
1606
- <label>
1607
- <span>{{ text('praxis.metadataEditor.arrayFields.name', 'Nome') }}</span>
1608
- <input
1609
- type="text"
1610
- formControlName="name"
1611
- (blur)="onNameBlur(i)"
1612
- [attr.aria-invalid]="isNameInvalid(i) ? 'true' : null"
1613
- [attr.aria-describedby]="nameErrorDescribedBy(i)"
1614
- />
1615
- <span
1616
- class="array-fields-editor__field-error"
1617
- *ngIf="nameErrorMessage(i) as nameError"
1618
- [id]="nameErrorId(i)"
1619
- >
1620
- {{ nameError }}
1621
- </span>
1622
- </label>
1623
-
1624
- <label>
1625
- <span>{{ text('praxis.metadataEditor.arrayFields.label', 'Label') }}</span>
1626
- <input type="text" formControlName="label" />
1627
- </label>
1628
-
1629
- <label>
1630
- <span>{{ text('praxis.metadataEditor.arrayFields.controlType', 'Controle') }}</span>
1631
- <select formControlName="controlType">
1632
- <option *ngFor="let option of controlTypeOptions" [value]="option">
1633
- {{ controlTypeLabel(option) }}
1634
- </option>
1635
- </select>
1636
- </label>
1637
-
1638
- <label class="array-fields-editor__check">
1639
- <input type="checkbox" formControlName="required" />
1640
- <span>{{ text('praxis.metadataEditor.arrayFields.required', 'Obrigat\u00f3rio') }}</span>
1641
- </label>
1642
-
1643
- <div class="array-fields-editor__actions">
1644
- <button type="button" (click)="moveField(i, -1)" [disabled]="i === 0">
1645
- {{ text('praxis.metadataEditor.arrayFields.moveUp', 'Subir') }}
1646
- </button>
1647
- <button type="button" (click)="moveField(i, 1)" [disabled]="i === formArray.length - 1">
1648
- {{ text('praxis.metadataEditor.arrayFields.moveDown', 'Descer') }}
1649
- </button>
1650
- <button type="button" (click)="duplicateField(i)">
1651
- {{ text('praxis.metadataEditor.arrayFields.duplicate', 'Duplicar') }}
1652
- </button>
1653
- <button
1654
- type="button"
1655
- (click)="removeField(i)"
1656
- [disabled]="isReferenced(row.get('name')?.value)"
1657
- [title]="removeTitle(row.get('name')?.value)"
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"
1658
1625
  >
1659
- {{ text('praxis.metadataEditor.arrayFields.remove', 'Remover') }}
1660
- </button>
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>
1661
1681
  </div>
1662
- </div>
1682
+ }
1663
1683
  </div>
1664
1684
  </section>
1665
- `, 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"] }]
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"] }]
1666
1686
  }], propDecorators: { control: [{
1667
1687
  type: Input
1668
1688
  }], arrayControl: [{
@@ -8274,47 +8294,50 @@ class FieldMetadataEditorComponent {
8274
8294
  }
8275
8295
  control.setErrors(Object.keys(errors).length ? errors : null);
8276
8296
  }
8277
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: FieldMetadataEditorComponent, deps: [{ token: ConfigRegistryService }, { token: SchemaNormalizerService }, { token: DynamicFormFactoryService }, { token: ContextValidatorRegistryService }], target: i0.ɵɵFactoryTarget.Component });
8278
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: FieldMetadataEditorComponent, isStandalone: true, selector: "praxis-field-metadata-editor", inputs: { controlType: "controlType", seed: "seed" }, outputs: { applied: "applied", cancel: "cancel" }, ngImport: i0, template: `
8279
- <div class="p-3" *ngIf="form">
8280
- <praxis-dynamic-editor-renderer
8281
- [properties]="renderedProps"
8282
- [form]="form"
8283
- ></praxis-dynamic-editor-renderer>
8284
-
8285
- <praxis-array-item-schema-fields-editor
8286
- *ngIf="isArrayEditor"
8287
- [control]="form.get('array.itemSchema.fields')"
8288
- [arrayControl]="form.get('array')"
8289
- ></praxis-array-item-schema-fields-editor>
8290
- </div>
8291
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { 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"] }] });
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"] }] });
8292
8314
  }
8293
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: FieldMetadataEditorComponent, decorators: [{
8315
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: FieldMetadataEditorComponent, decorators: [{
8294
8316
  type: Component,
8295
8317
  args: [{
8296
8318
  selector: 'praxis-field-metadata-editor',
8297
8319
  standalone: true,
8298
8320
  imports: [
8299
- CommonModule,
8300
8321
  ReactiveFormsModule,
8301
8322
  DynamicEditorRendererComponent,
8302
- ArrayItemSchemaFieldsEditorComponent,
8323
+ ArrayItemSchemaFieldsEditorComponent
8303
8324
  ],
8304
8325
  template: `
8305
- <div class="p-3" *ngIf="form">
8306
- <praxis-dynamic-editor-renderer
8307
- [properties]="renderedProps"
8308
- [form]="form"
8309
- ></praxis-dynamic-editor-renderer>
8310
-
8311
- <praxis-array-item-schema-fields-editor
8312
- *ngIf="isArrayEditor"
8313
- [control]="form.get('array.itemSchema.fields')"
8314
- [arrayControl]="form.get('array')"
8315
- ></praxis-array-item-schema-fields-editor>
8316
- </div>
8317
- `,
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
+ `,
8318
8341
  }]
8319
8342
  }], ctorParameters: () => [{ type: ConfigRegistryService }, { type: SchemaNormalizerService }, { type: DynamicFormFactoryService }, { type: ContextValidatorRegistryService }], propDecorators: { controlType: [{
8320
8343
  type: Input
@@ -8381,10 +8404,10 @@ class CascadeRulesService {
8381
8404
  dependencyValuePath: rule.dependencyValuePath || inferredValueKey || 'id',
8382
8405
  };
8383
8406
  }
8384
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: CascadeRulesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
8385
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: CascadeRulesService, providedIn: 'root' });
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' });
8386
8409
  }
8387
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: CascadeRulesService, decorators: [{
8410
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CascadeRulesService, decorators: [{
8388
8411
  type: Injectable,
8389
8412
  args: [{ providedIn: 'root' }]
8390
8413
  }] });
@@ -8398,10 +8421,10 @@ class CascadeManagerTabComponent {
8398
8421
  searchTerm = '';
8399
8422
  searchTimer = null;
8400
8423
  searchValue = '';
8401
- rules = signal(new Map(), ...(ngDevMode ? [{ debugName: "rules" }] : []));
8402
- 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 */ []));
8403
8426
  // Inline editor state (MVP)
8404
- editing = signal(false, ...(ngDevMode ? [{ debugName: "editing" }] : []));
8427
+ editing = signal(false, ...(ngDevMode ? [{ debugName: "editing" }] : /* istanbul ignore next */ []));
8405
8428
  editDepList = [];
8406
8429
  editEnable = true;
8407
8430
  editReset = false;
@@ -8753,8 +8776,8 @@ class CascadeManagerTabComponent {
8753
8776
  }
8754
8777
  return [...new Set(suggestions)];
8755
8778
  }
8756
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: CascadeManagerTabComponent, deps: [{ token: CascadeRulesService }], target: i0.ɵɵFactoryTarget.Component });
8757
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: CascadeManagerTabComponent, isStandalone: true, selector: "praxis-cascade-manager-tab", inputs: { fields: "fields", connections: "connections" }, outputs: { apply: "apply", cancel: "cancel" }, usesOnChanges: true, ngImport: i0, template: `
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: `
8758
8781
  <div class="cm-root">
8759
8782
  <!-- Left: Fields list -->
8760
8783
  <div class="cm-left">
@@ -8762,276 +8785,301 @@ class CascadeManagerTabComponent {
8762
8785
  <mat-label>Buscar campos</mat-label>
8763
8786
  <input matInput [ngModel]="searchTerm" (ngModelChange)="onSearchChange($event)">
8764
8787
  </mat-form-field>
8765
- <ng-container *ngIf="fields.length <= 50; else vscroll">
8788
+ @if (fields.length <= 50) {
8766
8789
  <div>
8767
- <div *ngFor="let f of filteredFields()" (click)="selectField(f.name)" class="field-item" [class.selected]="f.name===selectedField()" tabindex="0" (keydown)="onFieldKeydown($event, f.name)">
8768
- <div>
8769
- <div>{{ labelFor(f.name) }}</div>
8770
- <small style="opacity:.7">{{ f.name }}</small>
8771
- </div>
8772
- <div class="rule-badges" *ngIf="hasRule(f.name)">
8773
- <span class="rule-badge b-active" *ngIf="isCascadeEnabled(f.name)">Ativo</span>
8774
- <span class="rule-badge b-manual" *ngIf="loadOnChange(f.name)==='manual'">Manual</span>
8775
- <span class="rule-badge b-immediate" *ngIf="loadOnChange(f.name)==='immediate'">Immediate</span>
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
+ }
8776
8809
  </div>
8777
- </div>
8810
+ }
8778
8811
  </div>
8779
- </ng-container>
8780
- <ng-template #vscroll>
8812
+ } @else {
8781
8813
  <cdk-virtual-scroll-viewport itemSize="44" style="height: calc(100vh - 240px)">
8782
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)">
8783
8815
  <div>
8784
8816
  <div>{{ labelFor(f.name) }}</div>
8785
8817
  <small style="opacity:.7">{{ f.name }}</small>
8786
8818
  </div>
8787
- <div class="rule-badges" *ngIf="hasRule(f.name)">
8788
- <span class="rule-badge b-active" *ngIf="isCascadeEnabled(f.name)">Ativo</span>
8789
- <span class="rule-badge b-manual" *ngIf="loadOnChange(f.name)==='manual'">Manual</span>
8790
- <span class="rule-badge b-immediate" *ngIf="loadOnChange(f.name)==='immediate'">Immediate</span>
8791
- </div>
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
+ }
8792
8832
  </div>
8793
8833
  </cdk-virtual-scroll-viewport>
8794
- </ng-template>
8834
+ }
8795
8835
  </div>
8796
-
8836
+
8797
8837
  <!-- Right: Rules for selected field -->
8798
- <div class="cm-right" *ngIf="selectedField() as sel">
8799
- <div class="cm-header">
8800
- <div>
8801
- <h3 style="margin:0">{{ labelFor(sel) }}</h3>
8802
- <div style="opacity:.7">{{ sel }}</div>
8803
- </div>
8804
- <div>
8805
- <button mat-stroked-button color="primary" (click)="exportRules()" style="margin-right:8px">Exportar</button>
8806
- <button mat-stroked-button (click)="toggleImport()" style="margin-right:8px">Importar</button>
8807
- <button mat-flat-button color="primary" [matMenuTriggerFor]="addMenu" [disabled]="editing()">Adicionar</button>
8808
- <mat-menu #addMenu="matMenu">
8809
- <button mat-menu-item (click)="startEdit(sel)"><mat-icon [praxisIcon]="'add'"></mat-icon><span>Em branco</span></button>
8810
- <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>
8811
- <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>
8812
- </mat-menu>
8813
- </div>
8814
- </div>
8815
-
8816
- <!-- Conflict banner (connections) -->
8817
- <div class="banner" *ngIf="hasConflict(sel)">
8818
- <mat-icon [praxisIcon]="'link_off'"></mat-icon>
8819
- <div>
8820
- Connections atualizam este campo (filterCriteria). Considere desativar a cascata nativa ou usar ‘manual’.
8821
- </div>
8822
- <span class="spacer"></span>
8823
- <button mat-stroked-button color="primary" (click)="quickSet(sel,'disable')">Desativar</button>
8824
- <button mat-stroked-button color="accent" (click)="quickSet(sel,'manual')">Definir manual</button>
8825
- </div>
8826
-
8827
- <div *ngIf="importMode" class="import-panel">
8828
- <div style="display:flex; gap:8px; align-items:center; margin-bottom:8px"><mat-icon [praxisIcon]="'upload'"></mat-icon><strong>Importar regras (JSON)</strong></div>
8829
- <textarea matInput rows="6" class="full" [(ngModel)]="importJson" placeholder='{ "campo": { "dependencyFields": ["dep"], ... } }'></textarea>
8830
- <div style="display:flex; gap:8px; justify-content:flex-end; padding-top:8px">
8831
- <button mat-stroked-button (click)="toggleImport()">Cancelar</button>
8832
- <button mat-flat-button color="primary" (click)="applyImport()">Aplicar</button>
8833
- </div>
8834
- </div>
8835
-
8836
- <div *ngIf="!ruleFor(sel)">
8837
- <div class="rules-empty">Nenhuma cascata configurada. Clique em “Adicionar” para montar a dependência.</div>
8838
- </div>
8839
- <div *ngIf="ruleFor(sel) as rule">
8840
- <div class="rule-row">
8838
+ @if (selectedField(); as sel) {
8839
+ <div class="cm-right">
8840
+ <div class="cm-header">
8841
8841
  <div>
8842
- <div><strong>Dependentes:</strong> {{ rule.dependencyFields.join(', ') || '—' }}</div>
8843
- <div style="opacity:.7">Load on change: {{ rule.dependencyLoadOnChange || 'respectLoadOn' }} | Merge: {{ rule.dependencyMergeStrategy || 'merge' }}</div>
8842
+ <h3 style="margin:0">{{ labelFor(sel) }}</h3>
8843
+ <div style="opacity:.7">{{ sel }}</div>
8844
8844
  </div>
8845
- <div class="rule-actions">
8846
- <button mat-icon-button matTooltip="Editar" (click)="startEdit(sel)"><mat-icon [praxisIcon]="'edit'"></mat-icon></button>
8847
- <button mat-icon-button matTooltip="Duplicar" disabled><mat-icon [praxisIcon]="'content_copy'"></mat-icon></button>
8848
- <button mat-icon-button matTooltip="Remover" (click)="remove(sel)"><mat-icon [praxisIcon]="'delete'"></mat-icon></button>
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>
8849
8854
  </div>
8850
8855
  </div>
8851
- </div>
8852
-
8853
- <!-- Editor com seções (sem acordeão duplo) -->
8854
- <div class="panel-surface">
8855
- <div class="panel-header" style="padding: 12px 16px; border-bottom: 1px solid var(--md-sys-color-outline-variant);">
8856
- <mat-icon [praxisIcon]="'rule'"></mat-icon>
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)) {
8857
8879
  <div>
8858
- <div>Cascata do campo</div>
8859
- <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>
8860
8881
  </div>
8861
- </div>
8862
-
8863
- <div class="panel-stack">
8864
- <!-- Básico -->
8865
- <mat-expansion-panel [expanded]="true" class="stack-panel">
8866
- <mat-expansion-panel-header>
8867
- <mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'tune'"></mat-icon> Básico</mat-panel-title>
8868
- </mat-expansion-panel-header>
8869
- <div class="form-grid">
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>
8870
8947
  <div class="form-row">
8871
- <mat-form-field appearance="outline" class="full">
8872
- <mat-label>Campos dependentes</mat-label>
8873
- <mat-select multiple [(ngModel)]="editDepList" (ngModelChange)="onDepListChange()">
8874
- <mat-option *ngFor="let f of availableDependencyFields()" [value]="f.name">
8875
- {{ labelFor(f.name) }} ({{ f.name }})
8876
- </mat-option>
8877
- </mat-select>
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>
8974
+ <div class="form-row">
8975
+ <mat-form-field appearance="outline" class="full">
8976
+ <mat-label>ValuePath padrão</mat-label>
8977
+ <input matInput [(ngModel)]="editValuePath" placeholder="id">
8878
8978
  <button
8879
8979
  mat-icon-button
8880
8980
  matSuffix
8881
8981
  class="help-icon-button"
8882
8982
  type="button"
8883
- [matTooltip]="'Selecione os campos que controlam este valor'"
8983
+ [matTooltip]="'Preenchido automaticamente a partir de optionValueKey() ou \\'id\\''"
8884
8984
  matTooltipPosition="above"
8885
- >
8985
+ >
8886
8986
  <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
8887
8987
  </button>
8888
- </mat-form-field>
8889
- </div>
8890
- <div class="form-row">
8891
- <mat-checkbox [(ngModel)]="editEnable">Ativar cascata nativa</mat-checkbox>
8892
- <mat-checkbox [(ngModel)]="editReset">Resetar ao mudar</mat-checkbox>
8988
+ </mat-form-field>
8893
8989
  </div>
8894
- </div>
8895
- </mat-expansion-panel>
8896
-
8897
- <!-- Execução -->
8898
- <mat-expansion-panel class="stack-panel">
8899
- <mat-expansion-panel-header>
8900
- <mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'schedule'"></mat-icon> Execução</mat-panel-title>
8901
- </mat-expansion-panel-header>
8902
- <div class="form-row">
8903
- <mat-form-field appearance="outline">
8904
- <mat-label>Carregar ao mudar</mat-label>
8905
- <mat-select [(ngModel)]="editLoadOn">
8906
- <mat-option value="respectLoadOn">Respeitar loadOn</mat-option>
8907
- <mat-option value="immediate">Imediato</mat-option>
8908
- <mat-option value="manual">Manual</mat-option>
8909
- </mat-select>
8910
- </mat-form-field>
8911
- <mat-form-field appearance="outline">
8912
- <mat-label>Merge de filtros</mat-label>
8913
- <mat-select [(ngModel)]="editMerge">
8914
- <mat-option value="merge">merge (padrão)</mat-option>
8915
- <mat-option value="replace">replace</mat-option>
8916
- </mat-select>
8917
- </mat-form-field>
8918
- <mat-form-field appearance="outline">
8919
- <mat-label>Atraso (ms)</mat-label>
8920
- <input matInput type="number" [(ngModel)]="editDebounce">
8921
- </mat-form-field>
8922
- </div>
8923
- </mat-expansion-panel>
8924
-
8925
- <!-- Extração de valor -->
8926
- <mat-expansion-panel class="stack-panel">
8927
- <mat-expansion-panel-header>
8928
- <mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'call_split'"></mat-icon> Extração de valor</mat-panel-title>
8929
- </mat-expansion-panel-header>
8930
- <div class="form-row">
8931
- <mat-form-field appearance="outline" class="full">
8932
- <mat-label>ValuePath padrão</mat-label>
8933
- <input matInput [(ngModel)]="editValuePath" placeholder="id">
8934
- <button
8935
- mat-icon-button
8936
- matSuffix
8937
- class="help-icon-button"
8938
- type="button"
8939
- [matTooltip]="'Preenchido automaticamente a partir de optionValueKey() ou \\'id\\''"
8940
- matTooltipPosition="above"
8941
- >
8942
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
8943
- </button>
8944
- </mat-form-field>
8945
- </div>
8946
- </mat-expansion-panel>
8947
-
8948
- <!-- Mapeamento de filtros (tabela) -->
8949
- <mat-expansion-panel class="stack-panel">
8950
- <mat-expansion-panel-header>
8951
- <mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'filter_alt'"></mat-icon> Mapeamento de filtros</mat-panel-title>
8952
- </mat-expansion-panel-header>
8953
- <table class="map-table" *ngIf="editMapRows.length; else noMap">
8954
- <thead>
8955
- <tr>
8956
- <th>Dependente</th>
8957
- <th>Chave (dot‑path)</th>
8958
- <th>ValuePath (opcional)</th>
8959
- </tr>
8960
- </thead>
8961
- <tbody>
8962
- <tr *ngFor="let row of editMapRows; let i = index">
8963
- <td>{{ row.dep }}</td>
8964
- <td>
8965
- <mat-form-field appearance="outline" class="full">
8966
- <mat-label>Chave</mat-label>
8967
- <input matInput [(ngModel)]="row.key" [matAutocomplete]="autoKey" [placeholder]="row.dep">
8968
- <mat-autocomplete #autoKey="matAutocomplete">
8969
- <mat-option *ngFor="let opt of getKeySuggestions(row.dep)" [value]="opt">{{ opt }}</mat-option>
8970
- </mat-autocomplete>
8971
- <button
8972
- mat-icon-button
8973
- matSuffix
8974
- class="help-icon-button"
8975
- type="button"
8976
- [matTooltip]="'Use dot-notation (e.g. ' + row.dep + '.id) for nested objects'"
8977
- matTooltipPosition="above"
8978
- >
8979
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
8980
- </button>
8981
- </mat-form-field>
8982
- </td>
8983
- <td>
8984
- <mat-form-field appearance="outline" class="full">
8985
- <mat-label>ValuePath</mat-label>
8986
- <input matInput [(ngModel)]="row.valuePath" [matAutocomplete]="autoValue" [placeholder]="editValuePath||'id'">
8987
- <mat-autocomplete #autoValue="matAutocomplete">
8988
- <mat-option *ngFor="let opt of getValuePathSuggestions(row.dep)" [value]="opt">{{ opt }}</mat-option>
8989
- </mat-autocomplete>
8990
- <button
8991
- mat-icon-button
8992
- matSuffix
8993
- class="help-icon-button"
8994
- type="button"
8995
- [matTooltip]="'Opcional'"
8996
- matTooltipPosition="above"
8997
- >
8998
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
8999
- </button>
9000
- </mat-form-field>
9001
- </td>
9002
- </tr>
9003
- </tbody>
9004
- </table>
9005
- <ng-template #noMap>
9006
- <div class="rules-empty">Defina dependentes para sugerir o mapeamento.</div>
9007
- </ng-template>
9008
- </mat-expansion-panel>
9009
-
9010
- <!-- Preview -->
9011
- <mat-expansion-panel class="stack-panel">
9012
- <mat-expansion-panel-header>
9013
- <mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'visibility'"></mat-icon> Preview</mat-panel-title>
9014
- </mat-expansion-panel-header>
9015
- <pre class="preview-box">{{ buildPreview() }}</pre>
9016
- <div style="display:flex; justify-content:flex-end; padding-top:6px">
9017
- <button mat-stroked-button (click)="copyPreview()">Copiar</button>
9018
- </div>
9019
- </mat-expansion-panel>
9020
- </div>
9021
-
9022
- <div style="display:flex; gap:8px; justify-content:flex-end; padding:8px 12px;">
9023
- <button mat-stroked-button (click)="cancelEdit()">Cancelar</button>
9024
- <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>
9025
9074
  </div>
9026
9075
  </div>
9027
- </div>
9076
+ }
9028
9077
  </div>
9029
- `, 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.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.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: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.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"] }] });
9030
9079
  }
9031
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: CascadeManagerTabComponent, decorators: [{
9080
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CascadeManagerTabComponent, decorators: [{
9032
9081
  type: Component,
9033
9082
  args: [{ selector: 'praxis-cascade-manager-tab', standalone: true, imports: [
9034
- CommonModule,
9035
9083
  ReactiveFormsModule,
9036
9084
  FormsModule,
9037
9085
  MatIconModule,
@@ -9047,7 +9095,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
9047
9095
  MatMenuModule,
9048
9096
  MatAutocompleteModule,
9049
9097
  ScrollingModule,
9050
- PraxisIconDirective,
9098
+ PraxisIconDirective
9051
9099
  ], template: `
9052
9100
  <div class="cm-root">
9053
9101
  <!-- Left: Fields list -->
@@ -9056,271 +9104,297 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
9056
9104
  <mat-label>Buscar campos</mat-label>
9057
9105
  <input matInput [ngModel]="searchTerm" (ngModelChange)="onSearchChange($event)">
9058
9106
  </mat-form-field>
9059
- <ng-container *ngIf="fields.length <= 50; else vscroll">
9107
+ @if (fields.length <= 50) {
9060
9108
  <div>
9061
- <div *ngFor="let f of filteredFields()" (click)="selectField(f.name)" class="field-item" [class.selected]="f.name===selectedField()" tabindex="0" (keydown)="onFieldKeydown($event, f.name)">
9062
- <div>
9063
- <div>{{ labelFor(f.name) }}</div>
9064
- <small style="opacity:.7">{{ f.name }}</small>
9065
- </div>
9066
- <div class="rule-badges" *ngIf="hasRule(f.name)">
9067
- <span class="rule-badge b-active" *ngIf="isCascadeEnabled(f.name)">Ativo</span>
9068
- <span class="rule-badge b-manual" *ngIf="loadOnChange(f.name)==='manual'">Manual</span>
9069
- <span class="rule-badge b-immediate" *ngIf="loadOnChange(f.name)==='immediate'">Immediate</span>
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
+ }
9070
9128
  </div>
9071
- </div>
9129
+ }
9072
9130
  </div>
9073
- </ng-container>
9074
- <ng-template #vscroll>
9131
+ } @else {
9075
9132
  <cdk-virtual-scroll-viewport itemSize="44" style="height: calc(100vh - 240px)">
9076
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)">
9077
9134
  <div>
9078
9135
  <div>{{ labelFor(f.name) }}</div>
9079
9136
  <small style="opacity:.7">{{ f.name }}</small>
9080
9137
  </div>
9081
- <div class="rule-badges" *ngIf="hasRule(f.name)">
9082
- <span class="rule-badge b-active" *ngIf="isCascadeEnabled(f.name)">Ativo</span>
9083
- <span class="rule-badge b-manual" *ngIf="loadOnChange(f.name)==='manual'">Manual</span>
9084
- <span class="rule-badge b-immediate" *ngIf="loadOnChange(f.name)==='immediate'">Immediate</span>
9085
- </div>
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
+ }
9086
9151
  </div>
9087
9152
  </cdk-virtual-scroll-viewport>
9088
- </ng-template>
9153
+ }
9089
9154
  </div>
9090
-
9155
+
9091
9156
  <!-- Right: Rules for selected field -->
9092
- <div class="cm-right" *ngIf="selectedField() as sel">
9093
- <div class="cm-header">
9094
- <div>
9095
- <h3 style="margin:0">{{ labelFor(sel) }}</h3>
9096
- <div style="opacity:.7">{{ sel }}</div>
9097
- </div>
9098
- <div>
9099
- <button mat-stroked-button color="primary" (click)="exportRules()" style="margin-right:8px">Exportar</button>
9100
- <button mat-stroked-button (click)="toggleImport()" style="margin-right:8px">Importar</button>
9101
- <button mat-flat-button color="primary" [matMenuTriggerFor]="addMenu" [disabled]="editing()">Adicionar</button>
9102
- <mat-menu #addMenu="matMenu">
9103
- <button mat-menu-item (click)="startEdit(sel)"><mat-icon [praxisIcon]="'add'"></mat-icon><span>Em branco</span></button>
9104
- <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>
9105
- <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>
9106
- </mat-menu>
9107
- </div>
9108
- </div>
9109
-
9110
- <!-- Conflict banner (connections) -->
9111
- <div class="banner" *ngIf="hasConflict(sel)">
9112
- <mat-icon [praxisIcon]="'link_off'"></mat-icon>
9113
- <div>
9114
- Connections atualizam este campo (filterCriteria). Considere desativar a cascata nativa ou usar ‘manual’.
9115
- </div>
9116
- <span class="spacer"></span>
9117
- <button mat-stroked-button color="primary" (click)="quickSet(sel,'disable')">Desativar</button>
9118
- <button mat-stroked-button color="accent" (click)="quickSet(sel,'manual')">Definir manual</button>
9119
- </div>
9120
-
9121
- <div *ngIf="importMode" class="import-panel">
9122
- <div style="display:flex; gap:8px; align-items:center; margin-bottom:8px"><mat-icon [praxisIcon]="'upload'"></mat-icon><strong>Importar regras (JSON)</strong></div>
9123
- <textarea matInput rows="6" class="full" [(ngModel)]="importJson" placeholder='{ "campo": { "dependencyFields": ["dep"], ... } }'></textarea>
9124
- <div style="display:flex; gap:8px; justify-content:flex-end; padding-top:8px">
9125
- <button mat-stroked-button (click)="toggleImport()">Cancelar</button>
9126
- <button mat-flat-button color="primary" (click)="applyImport()">Aplicar</button>
9127
- </div>
9128
- </div>
9129
-
9130
- <div *ngIf="!ruleFor(sel)">
9131
- <div class="rules-empty">Nenhuma cascata configurada. Clique em “Adicionar” para montar a dependência.</div>
9132
- </div>
9133
- <div *ngIf="ruleFor(sel) as rule">
9134
- <div class="rule-row">
9157
+ @if (selectedField(); as sel) {
9158
+ <div class="cm-right">
9159
+ <div class="cm-header">
9135
9160
  <div>
9136
- <div><strong>Dependentes:</strong> {{ rule.dependencyFields.join(', ') || '—' }}</div>
9137
- <div style="opacity:.7">Load on change: {{ rule.dependencyLoadOnChange || 'respectLoadOn' }} | Merge: {{ rule.dependencyMergeStrategy || 'merge' }}</div>
9161
+ <h3 style="margin:0">{{ labelFor(sel) }}</h3>
9162
+ <div style="opacity:.7">{{ sel }}</div>
9138
9163
  </div>
9139
- <div class="rule-actions">
9140
- <button mat-icon-button matTooltip="Editar" (click)="startEdit(sel)"><mat-icon [praxisIcon]="'edit'"></mat-icon></button>
9141
- <button mat-icon-button matTooltip="Duplicar" disabled><mat-icon [praxisIcon]="'content_copy'"></mat-icon></button>
9142
- <button mat-icon-button matTooltip="Remover" (click)="remove(sel)"><mat-icon [praxisIcon]="'delete'"></mat-icon></button>
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>
9143
9173
  </div>
9144
9174
  </div>
9145
- </div>
9146
-
9147
- <!-- Editor com seções (sem acordeão duplo) -->
9148
- <div class="panel-surface">
9149
- <div class="panel-header" style="padding: 12px 16px; border-bottom: 1px solid var(--md-sys-color-outline-variant);">
9150
- <mat-icon [praxisIcon]="'rule'"></mat-icon>
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)) {
9151
9198
  <div>
9152
- <div>Cascata do campo</div>
9153
- <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>
9154
9200
  </div>
9155
- </div>
9156
-
9157
- <div class="panel-stack">
9158
- <!-- Básico -->
9159
- <mat-expansion-panel [expanded]="true" class="stack-panel">
9160
- <mat-expansion-panel-header>
9161
- <mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'tune'"></mat-icon> Básico</mat-panel-title>
9162
- </mat-expansion-panel-header>
9163
- <div class="form-grid">
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>
9164
9293
  <div class="form-row">
9165
- <mat-form-field appearance="outline" class="full">
9166
- <mat-label>Campos dependentes</mat-label>
9167
- <mat-select multiple [(ngModel)]="editDepList" (ngModelChange)="onDepListChange()">
9168
- <mat-option *ngFor="let f of availableDependencyFields()" [value]="f.name">
9169
- {{ labelFor(f.name) }} ({{ f.name }})
9170
- </mat-option>
9171
- </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">
9172
9297
  <button
9173
9298
  mat-icon-button
9174
9299
  matSuffix
9175
9300
  class="help-icon-button"
9176
9301
  type="button"
9177
- [matTooltip]="'Selecione os campos que controlam este valor'"
9302
+ [matTooltip]="'Preenchido automaticamente a partir de optionValueKey() ou \\'id\\''"
9178
9303
  matTooltipPosition="above"
9179
- >
9304
+ >
9180
9305
  <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
9181
9306
  </button>
9182
- </mat-form-field>
9183
- </div>
9184
- <div class="form-row">
9185
- <mat-checkbox [(ngModel)]="editEnable">Ativar cascata nativa</mat-checkbox>
9186
- <mat-checkbox [(ngModel)]="editReset">Resetar ao mudar</mat-checkbox>
9307
+ </mat-form-field>
9187
9308
  </div>
9188
- </div>
9189
- </mat-expansion-panel>
9190
-
9191
- <!-- Execução -->
9192
- <mat-expansion-panel class="stack-panel">
9193
- <mat-expansion-panel-header>
9194
- <mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'schedule'"></mat-icon> Execução</mat-panel-title>
9195
- </mat-expansion-panel-header>
9196
- <div class="form-row">
9197
- <mat-form-field appearance="outline">
9198
- <mat-label>Carregar ao mudar</mat-label>
9199
- <mat-select [(ngModel)]="editLoadOn">
9200
- <mat-option value="respectLoadOn">Respeitar loadOn</mat-option>
9201
- <mat-option value="immediate">Imediato</mat-option>
9202
- <mat-option value="manual">Manual</mat-option>
9203
- </mat-select>
9204
- </mat-form-field>
9205
- <mat-form-field appearance="outline">
9206
- <mat-label>Merge de filtros</mat-label>
9207
- <mat-select [(ngModel)]="editMerge">
9208
- <mat-option value="merge">merge (padrão)</mat-option>
9209
- <mat-option value="replace">replace</mat-option>
9210
- </mat-select>
9211
- </mat-form-field>
9212
- <mat-form-field appearance="outline">
9213
- <mat-label>Atraso (ms)</mat-label>
9214
- <input matInput type="number" [(ngModel)]="editDebounce">
9215
- </mat-form-field>
9216
- </div>
9217
- </mat-expansion-panel>
9218
-
9219
- <!-- Extração de valor -->
9220
- <mat-expansion-panel class="stack-panel">
9221
- <mat-expansion-panel-header>
9222
- <mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'call_split'"></mat-icon> Extração de valor</mat-panel-title>
9223
- </mat-expansion-panel-header>
9224
- <div class="form-row">
9225
- <mat-form-field appearance="outline" class="full">
9226
- <mat-label>ValuePath padrão</mat-label>
9227
- <input matInput [(ngModel)]="editValuePath" placeholder="id">
9228
- <button
9229
- mat-icon-button
9230
- matSuffix
9231
- class="help-icon-button"
9232
- type="button"
9233
- [matTooltip]="'Preenchido automaticamente a partir de optionValueKey() ou \\'id\\''"
9234
- matTooltipPosition="above"
9235
- >
9236
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
9237
- </button>
9238
- </mat-form-field>
9239
- </div>
9240
- </mat-expansion-panel>
9241
-
9242
- <!-- Mapeamento de filtros (tabela) -->
9243
- <mat-expansion-panel class="stack-panel">
9244
- <mat-expansion-panel-header>
9245
- <mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'filter_alt'"></mat-icon> Mapeamento de filtros</mat-panel-title>
9246
- </mat-expansion-panel-header>
9247
- <table class="map-table" *ngIf="editMapRows.length; else noMap">
9248
- <thead>
9249
- <tr>
9250
- <th>Dependente</th>
9251
- <th>Chave (dot‑path)</th>
9252
- <th>ValuePath (opcional)</th>
9253
- </tr>
9254
- </thead>
9255
- <tbody>
9256
- <tr *ngFor="let row of editMapRows; let i = index">
9257
- <td>{{ row.dep }}</td>
9258
- <td>
9259
- <mat-form-field appearance="outline" class="full">
9260
- <mat-label>Chave</mat-label>
9261
- <input matInput [(ngModel)]="row.key" [matAutocomplete]="autoKey" [placeholder]="row.dep">
9262
- <mat-autocomplete #autoKey="matAutocomplete">
9263
- <mat-option *ngFor="let opt of getKeySuggestions(row.dep)" [value]="opt">{{ opt }}</mat-option>
9264
- </mat-autocomplete>
9265
- <button
9266
- mat-icon-button
9267
- matSuffix
9268
- class="help-icon-button"
9269
- type="button"
9270
- [matTooltip]="'Use dot-notation (e.g. ' + row.dep + '.id) for nested objects'"
9271
- matTooltipPosition="above"
9272
- >
9273
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
9274
- </button>
9275
- </mat-form-field>
9276
- </td>
9277
- <td>
9278
- <mat-form-field appearance="outline" class="full">
9279
- <mat-label>ValuePath</mat-label>
9280
- <input matInput [(ngModel)]="row.valuePath" [matAutocomplete]="autoValue" [placeholder]="editValuePath||'id'">
9281
- <mat-autocomplete #autoValue="matAutocomplete">
9282
- <mat-option *ngFor="let opt of getValuePathSuggestions(row.dep)" [value]="opt">{{ opt }}</mat-option>
9283
- </mat-autocomplete>
9284
- <button
9285
- mat-icon-button
9286
- matSuffix
9287
- class="help-icon-button"
9288
- type="button"
9289
- [matTooltip]="'Opcional'"
9290
- matTooltipPosition="above"
9291
- >
9292
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
9293
- </button>
9294
- </mat-form-field>
9295
- </td>
9296
- </tr>
9297
- </tbody>
9298
- </table>
9299
- <ng-template #noMap>
9300
- <div class="rules-empty">Defina dependentes para sugerir o mapeamento.</div>
9301
- </ng-template>
9302
- </mat-expansion-panel>
9303
-
9304
- <!-- Preview -->
9305
- <mat-expansion-panel class="stack-panel">
9306
- <mat-expansion-panel-header>
9307
- <mat-panel-title class="panel-header"><mat-icon [praxisIcon]="'visibility'"></mat-icon> Preview</mat-panel-title>
9308
- </mat-expansion-panel-header>
9309
- <pre class="preview-box">{{ buildPreview() }}</pre>
9310
- <div style="display:flex; justify-content:flex-end; padding-top:6px">
9311
- <button mat-stroked-button (click)="copyPreview()">Copiar</button>
9312
- </div>
9313
- </mat-expansion-panel>
9314
- </div>
9315
-
9316
- <div style="display:flex; gap:8px; justify-content:flex-end; padding:8px 12px;">
9317
- <button mat-stroked-button (click)="cancelEdit()">Cancelar</button>
9318
- <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>
9319
9393
  </div>
9320
9394
  </div>
9321
- </div>
9395
+ }
9322
9396
  </div>
9323
- `, 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"] }]
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"] }]
9324
9398
  }], ctorParameters: () => [{ type: CascadeRulesService }], propDecorators: { fields: [{
9325
9399
  type: Input
9326
9400
  }], connections: [{