@praxisui/tabs 8.0.0-beta.31 → 8.0.0-beta.33

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.
@@ -3,27 +3,27 @@ import { inject, Input, Inject, Component, ChangeDetectorRef, effect, EventEmitt
3
3
  import { ActivatedRoute } from '@angular/router';
4
4
  import * as i1$1 from '@angular/common';
5
5
  import { CommonModule } from '@angular/common';
6
- import * as i3$1 from '@angular/material/tabs';
6
+ import * as i3 from '@angular/material/tabs';
7
7
  import { MatTabsModule } from '@angular/material/tabs';
8
- import * as i7 from '@angular/material/icon';
8
+ import * as i4$1 from '@angular/material/icon';
9
9
  import { MatIconModule } from '@angular/material/icon';
10
- import * as i11 from '@angular/material/tooltip';
10
+ import * as i10 from '@angular/material/tooltip';
11
11
  import { MatTooltipModule } from '@angular/material/tooltip';
12
12
  import * as i1 from '@praxisui/core';
13
13
  import { providePraxisI18n, PraxisI18nService, PraxisIconDirective, providePraxisI18nConfig, deepMerge, ASYNC_CONFIG_STORAGE, ComponentKeyService, LoggerService, EmptyStateCardComponent, DynamicWidgetLoaderDirective, ComponentMetadataRegistry } from '@praxisui/core';
14
- import * as i6$1 from '@angular/material/button';
14
+ import * as i6 from '@angular/material/button';
15
15
  import { MatButtonModule } from '@angular/material/button';
16
- import * as i3 from '@angular/forms';
16
+ import * as i2_1 from '@angular/forms';
17
17
  import { FormsModule, FormGroup, FormControl, ReactiveFormsModule } from '@angular/forms';
18
18
  import { DynamicFieldLoaderDirective } from '@praxisui/dynamic-fields';
19
19
  import { SETTINGS_PANEL_DATA, SettingsPanelService } from '@praxisui/settings-panel';
20
- import * as i5 from '@angular/material/form-field';
20
+ import * as i4 from '@angular/material/form-field';
21
21
  import { MatFormFieldModule } from '@angular/material/form-field';
22
- import * as i6 from '@angular/material/input';
22
+ import * as i5 from '@angular/material/input';
23
23
  import { MatInputModule } from '@angular/material/input';
24
- import * as i9 from '@angular/material/slide-toggle';
24
+ import * as i8 from '@angular/material/slide-toggle';
25
25
  import { MatSlideToggleModule } from '@angular/material/slide-toggle';
26
- import * as i10 from '@angular/cdk/drag-drop';
26
+ import * as i9 from '@angular/cdk/drag-drop';
27
27
  import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
28
28
  import { BehaviorSubject, firstValueFrom, Subject, Subscription } from 'rxjs';
29
29
  import { produce } from 'immer';
@@ -980,8 +980,8 @@ class PraxisTabsConfigEditor {
980
980
  moveItemInArray(l.widgets, event.previousIndex, event.currentIndex);
981
981
  this.onAppearanceChange();
982
982
  }
983
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisTabsConfigEditor, deps: [{ token: SETTINGS_PANEL_DATA }, { token: i1.ComponentMetadataRegistry }], target: i0.ɵɵFactoryTarget.Component });
984
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: PraxisTabsConfigEditor, isStandalone: true, selector: "praxis-tabs-config-editor", inputs: { document: "document" }, providers: [providePraxisI18nConfig(PRAXIS_TABS_I18N_CONFIG)], ngImport: i0, template: `
983
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisTabsConfigEditor, deps: [{ token: SETTINGS_PANEL_DATA }, { token: i1.ComponentMetadataRegistry }], target: i0.ɵɵFactoryTarget.Component });
984
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisTabsConfigEditor, isStandalone: true, selector: "praxis-tabs-config-editor", inputs: { document: "document" }, providers: [providePraxisI18nConfig(PRAXIS_TABS_I18N_CONFIG)], ngImport: i0, template: `
985
985
  <div class="editor-shell">
986
986
  <div class="editor-topbar">
987
987
  <mat-form-field appearance="outline" class="editor-mode-field">
@@ -992,36 +992,41 @@ class PraxisTabsConfigEditor {
992
992
  </select>
993
993
  </mat-form-field>
994
994
  </div>
995
-
996
- <div *ngIf="diagnostics.length" class="editor-diagnostics">
997
- <div class="editor-diagnostics-title">{{ t('editor.diagnostics.title', 'Diagnosticos de authoring') }}</div>
998
- <div
999
- *ngFor="let diagnostic of diagnostics"
1000
- class="editor-diagnostic"
1001
- [class.level-error]="diagnostic.level === 'error'"
1002
- [class.level-warning]="diagnostic.level === 'warning'"
1003
- [class.level-info]="diagnostic.level === 'info'"
1004
- >
1005
- <strong>{{ diagnosticLevelLabel(diagnostic.level) }}</strong>
1006
- <span>{{ diagnostic.message }}</span>
1007
- <code *ngIf="diagnostic.path">{{ diagnostic.path }}</code>
995
+
996
+ @if (diagnostics.length) {
997
+ <div class="editor-diagnostics">
998
+ <div class="editor-diagnostics-title">{{ t('editor.diagnostics.title', 'Diagnosticos de authoring') }}</div>
999
+ @for (diagnostic of diagnostics; track diagnostic) {
1000
+ <div
1001
+ class="editor-diagnostic"
1002
+ [class.level-error]="diagnostic.level === 'error'"
1003
+ [class.level-warning]="diagnostic.level === 'warning'"
1004
+ [class.level-info]="diagnostic.level === 'info'"
1005
+ >
1006
+ <strong>{{ diagnosticLevelLabel(diagnostic.level) }}</strong>
1007
+ <span>{{ diagnostic.message }}</span>
1008
+ @if (diagnostic.path) {
1009
+ <code>{{ diagnostic.path }}</code>
1010
+ }
1011
+ </div>
1012
+ }
1008
1013
  </div>
1009
- </div>
1010
-
1011
- <mat-tab-group class="editor-tabs">
1012
- <mat-tab [label]="t('editor.tabs.behavior', 'Comportamento')">
1013
- <div class="editor-section">
1014
- <div class="editor-row">
1015
- <mat-slide-toggle [(ngModel)]="behavior.closeable" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.closeable', 'Fechavel') }}</mat-slide-toggle>
1016
- <mat-slide-toggle [(ngModel)]="behavior.lazyLoad" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.lazyLoad', 'Carregar sob demanda') }}</mat-slide-toggle>
1017
- <mat-slide-toggle [(ngModel)]="behavior.reorderable" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.reorderable', 'Reordenavel') }}</mat-slide-toggle>
1014
+ }
1015
+
1016
+ <mat-tab-group class="editor-tabs">
1017
+ <mat-tab [label]="t('editor.tabs.behavior', 'Comportamento')">
1018
+ <div class="editor-section">
1019
+ <div class="editor-row">
1020
+ <mat-slide-toggle [(ngModel)]="behavior.closeable" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.closeable', 'Fechavel') }}</mat-slide-toggle>
1021
+ <mat-slide-toggle [(ngModel)]="behavior.lazyLoad" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.lazyLoad', 'Carregar sob demanda') }}</mat-slide-toggle>
1022
+ <mat-slide-toggle [(ngModel)]="behavior.reorderable" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.reorderable', 'Reordenavel') }}</mat-slide-toggle>
1023
+ </div>
1018
1024
  </div>
1019
- </div>
1020
- </mat-tab>
1021
- <mat-tab [label]="t('editor.tabs.group', 'Grupo')" [disabled]="primaryMode !== 'group'">
1022
- <div class="editor-section">
1023
- <div class="editor-grid two">
1024
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.groupAlignTabs', 'Alinhamento das abas') }}</mat-label>
1025
+ </mat-tab>
1026
+ <mat-tab [label]="t('editor.tabs.group', 'Grupo')" [disabled]="primaryMode !== 'group'">
1027
+ <div class="editor-section">
1028
+ <div class="editor-grid two">
1029
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.groupAlignTabs', 'Alinhamento das abas') }}</mat-label>
1025
1030
  <select matNativeControl [(ngModel)]="group.alignTabs" (ngModelChange)="onAppearanceChange()">
1026
1031
  <option [ngValue]="undefined">{{ t('editor.options.default', 'Padrao') }}</option>
1027
1032
  <option value="start">{{ t('editor.options.start', 'Inicio') }}</option>
@@ -1030,428 +1035,445 @@ class PraxisTabsConfigEditor {
1030
1035
  </select>
1031
1036
  </mat-form-field>
1032
1037
  <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.groupHeaderPosition', 'Posicao do header') }}</mat-label>
1033
- <select matNativeControl [(ngModel)]="group.headerPosition" (ngModelChange)="onAppearanceChange()">
1034
- <option [ngValue]="undefined">{{ t('editor.options.aboveDefault', 'Acima (padrao)') }}</option>
1035
- <option value="above">{{ t('editor.options.above', 'Acima') }}</option>
1036
- <option value="below">{{ t('editor.options.below', 'Abaixo') }}</option>
1037
- </select>
1038
- </mat-form-field>
1039
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.selectedIndex', 'Indice selecionado') }}</mat-label>
1040
- <input matInput type="number" [(ngModel)]="group.selectedIndex" (ngModelChange)="onAppearanceChange()" />
1041
- <button
1042
- mat-icon-button
1043
- matSuffix
1044
- class="help-icon-button"
1045
- type="button"
1046
- [matTooltip]="t('editor.hints.selectedIndex', 'selectedIndex')"
1047
- matTooltipPosition="above"
1048
- >
1049
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1050
- </button>
1051
- </mat-form-field>
1052
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.animationDuration', 'Duracao da animacao') }}</mat-label>
1053
- <input matInput [(ngModel)]="group.animationDuration" (ngModelChange)="onAppearanceChange()" [placeholder]="t('editor.fields.placeholder.animationDuration', '500ms')" />
1054
- <button
1055
- mat-icon-button
1056
- matSuffix
1057
- class="help-icon-button"
1058
- type="button"
1059
- [matTooltip]="t('editor.hints.animationDuration', 'animationDuration')"
1060
- matTooltipPosition="above"
1061
- >
1062
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1063
- </button>
1064
- </mat-form-field>
1065
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.color', 'Cor (M2)') }}</mat-label>
1066
- <select matNativeControl [(ngModel)]="group.color" (ngModelChange)="onAppearanceChange()">
1067
- <option [ngValue]="undefined">{{ t('editor.options.none', '(nenhuma)') }}</option>
1068
- <option value="primary">{{ t('editor.options.primary', 'Primary') }}</option>
1069
- <option value="accent">{{ t('editor.options.accent', 'Accent') }}</option>
1070
- <option value="warn">{{ t('editor.options.warn', 'Warn') }}</option>
1071
- </select>
1072
- <button
1073
- mat-icon-button
1074
- matSuffix
1075
- class="help-icon-button"
1076
- type="button"
1077
- [matTooltip]="t('editor.hints.preferTokens', 'Preferir tokens em Estilo.')"
1078
- matTooltipPosition="above"
1079
- >
1080
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1081
- </button>
1082
- </mat-form-field>
1083
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.backgroundColor', 'Cor de fundo (M2)') }}</mat-label>
1084
- <select matNativeControl [(ngModel)]="group.backgroundColor" (ngModelChange)="onAppearanceChange()">
1085
- <option [ngValue]="undefined">{{ t('editor.options.none', '(nenhuma)') }}</option>
1086
- <option value="primary">{{ t('editor.options.primary', 'Primary') }}</option>
1087
- <option value="accent">{{ t('editor.options.accent', 'Accent') }}</option>
1088
- <option value="warn">{{ t('editor.options.warn', 'Warn') }}</option>
1089
- </select>
1090
- <button
1091
- mat-icon-button
1092
- matSuffix
1093
- class="help-icon-button"
1094
- type="button"
1095
- [matTooltip]="t('editor.hints.preferTokens', 'Preferir tokens em Estilo.')"
1096
- matTooltipPosition="above"
1097
- >
1098
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1099
- </button>
1100
- </mat-form-field>
1101
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.ariaLabel', 'aria-label') }}</mat-label>
1102
- <input matInput [(ngModel)]="group.ariaLabel" (ngModelChange)="onAppearanceChange()" />
1103
- </mat-form-field>
1104
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.ariaLabelledby', 'aria-labelledby') }}</mat-label>
1105
- <input matInput [(ngModel)]="group.ariaLabelledby" (ngModelChange)="onAppearanceChange()" />
1106
- </mat-form-field>
1107
- </div>
1108
- <div class="editor-row">
1109
- <mat-slide-toggle [(ngModel)]="group.dynamicHeight" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.dynamicHeight', 'Altura dinamica') }}</mat-slide-toggle>
1110
- <mat-slide-toggle [(ngModel)]="group.fitInkBarToContent" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.fitInkBarToContent', 'Indicador ajustado ao conteudo') }}</mat-slide-toggle>
1111
- <mat-slide-toggle [(ngModel)]="group.disablePagination" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disablePagination', 'Sem paginacao') }}</mat-slide-toggle>
1112
- <mat-slide-toggle [(ngModel)]="group.disableRipple" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disableRipple', 'Sem ripple') }}</mat-slide-toggle>
1113
- <mat-slide-toggle [(ngModel)]="group.preserveContent" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.preserveContent', 'Preservar conteudo') }}</mat-slide-toggle>
1114
- <mat-slide-toggle [(ngModel)]="group.stretchTabs" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.stretchTabs', 'Esticar abas') }}</mat-slide-toggle>
1115
- </div>
1038
+ <select matNativeControl [(ngModel)]="group.headerPosition" (ngModelChange)="onAppearanceChange()">
1039
+ <option [ngValue]="undefined">{{ t('editor.options.aboveDefault', 'Acima (padrao)') }}</option>
1040
+ <option value="above">{{ t('editor.options.above', 'Acima') }}</option>
1041
+ <option value="below">{{ t('editor.options.below', 'Abaixo') }}</option>
1042
+ </select>
1043
+ </mat-form-field>
1044
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.selectedIndex', 'Indice selecionado') }}</mat-label>
1045
+ <input matInput type="number" [(ngModel)]="group.selectedIndex" (ngModelChange)="onAppearanceChange()" />
1046
+ <button
1047
+ mat-icon-button
1048
+ matSuffix
1049
+ class="help-icon-button"
1050
+ type="button"
1051
+ [matTooltip]="t('editor.hints.selectedIndex', 'selectedIndex')"
1052
+ matTooltipPosition="above"
1053
+ >
1054
+ <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1055
+ </button>
1056
+ </mat-form-field>
1057
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.animationDuration', 'Duracao da animacao') }}</mat-label>
1058
+ <input matInput [(ngModel)]="group.animationDuration" (ngModelChange)="onAppearanceChange()" [placeholder]="t('editor.fields.placeholder.animationDuration', '500ms')" />
1059
+ <button
1060
+ mat-icon-button
1061
+ matSuffix
1062
+ class="help-icon-button"
1063
+ type="button"
1064
+ [matTooltip]="t('editor.hints.animationDuration', 'animationDuration')"
1065
+ matTooltipPosition="above"
1066
+ >
1067
+ <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1068
+ </button>
1069
+ </mat-form-field>
1070
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.color', 'Cor (M2)') }}</mat-label>
1071
+ <select matNativeControl [(ngModel)]="group.color" (ngModelChange)="onAppearanceChange()">
1072
+ <option [ngValue]="undefined">{{ t('editor.options.none', '(nenhuma)') }}</option>
1073
+ <option value="primary">{{ t('editor.options.primary', 'Primary') }}</option>
1074
+ <option value="accent">{{ t('editor.options.accent', 'Accent') }}</option>
1075
+ <option value="warn">{{ t('editor.options.warn', 'Warn') }}</option>
1076
+ </select>
1077
+ <button
1078
+ mat-icon-button
1079
+ matSuffix
1080
+ class="help-icon-button"
1081
+ type="button"
1082
+ [matTooltip]="t('editor.hints.preferTokens', 'Preferir tokens em Estilo.')"
1083
+ matTooltipPosition="above"
1084
+ >
1085
+ <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1086
+ </button>
1087
+ </mat-form-field>
1088
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.backgroundColor', 'Cor de fundo (M2)') }}</mat-label>
1089
+ <select matNativeControl [(ngModel)]="group.backgroundColor" (ngModelChange)="onAppearanceChange()">
1090
+ <option [ngValue]="undefined">{{ t('editor.options.none', '(nenhuma)') }}</option>
1091
+ <option value="primary">{{ t('editor.options.primary', 'Primary') }}</option>
1092
+ <option value="accent">{{ t('editor.options.accent', 'Accent') }}</option>
1093
+ <option value="warn">{{ t('editor.options.warn', 'Warn') }}</option>
1094
+ </select>
1095
+ <button
1096
+ mat-icon-button
1097
+ matSuffix
1098
+ class="help-icon-button"
1099
+ type="button"
1100
+ [matTooltip]="t('editor.hints.preferTokens', 'Preferir tokens em Estilo.')"
1101
+ matTooltipPosition="above"
1102
+ >
1103
+ <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1104
+ </button>
1105
+ </mat-form-field>
1106
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.ariaLabel', 'aria-label') }}</mat-label>
1107
+ <input matInput [(ngModel)]="group.ariaLabel" (ngModelChange)="onAppearanceChange()" />
1108
+ </mat-form-field>
1109
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.ariaLabelledby', 'aria-labelledby') }}</mat-label>
1110
+ <input matInput [(ngModel)]="group.ariaLabelledby" (ngModelChange)="onAppearanceChange()" />
1111
+ </mat-form-field>
1112
+ </div>
1113
+ <div class="editor-row">
1114
+ <mat-slide-toggle [(ngModel)]="group.dynamicHeight" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.dynamicHeight', 'Altura dinamica') }}</mat-slide-toggle>
1115
+ <mat-slide-toggle [(ngModel)]="group.fitInkBarToContent" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.fitInkBarToContent', 'Indicador ajustado ao conteudo') }}</mat-slide-toggle>
1116
+ <mat-slide-toggle [(ngModel)]="group.disablePagination" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disablePagination', 'Sem paginacao') }}</mat-slide-toggle>
1117
+ <mat-slide-toggle [(ngModel)]="group.disableRipple" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disableRipple', 'Sem ripple') }}</mat-slide-toggle>
1118
+ <mat-slide-toggle [(ngModel)]="group.preserveContent" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.preserveContent', 'Preservar conteudo') }}</mat-slide-toggle>
1119
+ <mat-slide-toggle [(ngModel)]="group.stretchTabs" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.stretchTabs', 'Esticar abas') }}</mat-slide-toggle>
1120
+ </div>
1121
+ </div>
1122
+ </mat-tab>
1123
+
1124
+ <mat-tab [label]="t('editor.tabs.nav', 'Navegacao')" [disabled]="primaryMode !== 'nav'">
1125
+ <div class="editor-section">
1126
+ <div class="editor-grid two">
1127
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.selectedIndex', 'Indice selecionado') }}</mat-label>
1128
+ <input matInput type="number" [(ngModel)]="nav.selectedIndex" (ngModelChange)="onAppearanceChange()" />
1129
+ <button
1130
+ mat-icon-button
1131
+ matSuffix
1132
+ class="help-icon-button"
1133
+ type="button"
1134
+ [matTooltip]="t('editor.hints.selectedIndex', 'selectedIndex')"
1135
+ matTooltipPosition="above"
1136
+ >
1137
+ <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1138
+ </button>
1139
+ </mat-form-field>
1140
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.animationDuration', 'Duracao da animacao') }}</mat-label>
1141
+ <input matInput [(ngModel)]="nav.animationDuration" (ngModelChange)="onAppearanceChange()" [placeholder]="t('editor.fields.placeholder.animationDuration', '500ms')" />
1142
+ <button
1143
+ mat-icon-button
1144
+ matSuffix
1145
+ class="help-icon-button"
1146
+ type="button"
1147
+ [matTooltip]="t('editor.hints.animationDuration', 'animationDuration')"
1148
+ matTooltipPosition="above"
1149
+ >
1150
+ <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1151
+ </button>
1152
+ </mat-form-field>
1153
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.ariaLabel', 'aria-label') }}</mat-label>
1154
+ <input matInput [(ngModel)]="nav.ariaLabel" (ngModelChange)="onAppearanceChange()" />
1155
+ </mat-form-field>
1156
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.ariaLabelledby', 'aria-labelledby') }}</mat-label>
1157
+ <input matInput [(ngModel)]="nav.ariaLabelledby" (ngModelChange)="onAppearanceChange()" />
1158
+ </mat-form-field>
1159
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.color', 'Cor (M2)') }}</mat-label>
1160
+ <select matNativeControl [(ngModel)]="nav.color" (ngModelChange)="onAppearanceChange()">
1161
+ <option [ngValue]="undefined">{{ t('editor.options.none', '(nenhuma)') }}</option>
1162
+ <option value="primary">{{ t('editor.options.primary', 'Primary') }}</option>
1163
+ <option value="accent">{{ t('editor.options.accent', 'Accent') }}</option>
1164
+ <option value="warn">{{ t('editor.options.warn', 'Warn') }}</option>
1165
+ </select>
1166
+ <button
1167
+ mat-icon-button
1168
+ matSuffix
1169
+ class="help-icon-button"
1170
+ type="button"
1171
+ [matTooltip]="t('editor.hints.preferTokens', 'Preferir tokens em Estilo.')"
1172
+ matTooltipPosition="above"
1173
+ >
1174
+ <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1175
+ </button>
1176
+ </mat-form-field>
1177
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.backgroundColor', 'Cor de fundo (M2)') }}</mat-label>
1178
+ <select matNativeControl [(ngModel)]="nav.backgroundColor" (ngModelChange)="onAppearanceChange()">
1179
+ <option [ngValue]="undefined">{{ t('editor.options.none', '(nenhuma)') }}</option>
1180
+ <option value="primary">{{ t('editor.options.primary', 'Primary') }}</option>
1181
+ <option value="accent">{{ t('editor.options.accent', 'Accent') }}</option>
1182
+ <option value="warn">{{ t('editor.options.warn', 'Warn') }}</option>
1183
+ </select>
1184
+ <button
1185
+ mat-icon-button
1186
+ matSuffix
1187
+ class="help-icon-button"
1188
+ type="button"
1189
+ [matTooltip]="t('editor.hints.preferTokens', 'Preferir tokens em Estilo.')"
1190
+ matTooltipPosition="above"
1191
+ >
1192
+ <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1193
+ </button>
1194
+ </mat-form-field>
1195
+ </div>
1196
+ <div class="editor-row">
1197
+ <mat-slide-toggle [(ngModel)]="nav.fitInkBarToContent" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.fitInkBarToContent', 'Indicador ajustado ao conteudo') }}</mat-slide-toggle>
1198
+ <mat-slide-toggle [(ngModel)]="nav.disablePagination" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disablePagination', 'Sem paginacao') }}</mat-slide-toggle>
1199
+ <mat-slide-toggle [(ngModel)]="nav.disableRipple" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disableRipple', 'Sem ripple') }}</mat-slide-toggle>
1200
+ <mat-slide-toggle [(ngModel)]="nav.stretchTabs" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.stretchTabs', 'Esticar abas') }}</mat-slide-toggle>
1201
+ </div>
1202
+ </div>
1203
+ </mat-tab>
1204
+ <mat-tab [label]="t('editor.tabs.json', 'JSON')">
1205
+ <div class="editor-section">
1206
+ <div class="editor-toolbar">
1207
+ <button mat-button (click)="formatJson()" [disabled]="!isValid">
1208
+ <mat-icon [praxisIcon]="'format_align_left'"></mat-icon>{{ t('editor.actions.format', 'Formatar') }}
1209
+ </button>
1210
+ <button mat-button (click)="reset()">
1211
+ <mat-icon [praxisIcon]="'restart_alt'"></mat-icon>{{ t('editor.actions.reset', 'Resetar') }}
1212
+ </button>
1116
1213
  </div>
1117
- </mat-tab>
1118
-
1119
- <mat-tab [label]="t('editor.tabs.nav', 'Navegacao')" [disabled]="primaryMode !== 'nav'">
1120
- <div class="editor-section">
1121
- <div class="editor-grid two">
1122
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.selectedIndex', 'Indice selecionado') }}</mat-label>
1123
- <input matInput type="number" [(ngModel)]="nav.selectedIndex" (ngModelChange)="onAppearanceChange()" />
1124
- <button
1125
- mat-icon-button
1126
- matSuffix
1127
- class="help-icon-button"
1128
- type="button"
1129
- [matTooltip]="t('editor.hints.selectedIndex', 'selectedIndex')"
1130
- matTooltipPosition="above"
1131
- >
1132
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1133
- </button>
1134
- </mat-form-field>
1135
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.animationDuration', 'Duracao da animacao') }}</mat-label>
1136
- <input matInput [(ngModel)]="nav.animationDuration" (ngModelChange)="onAppearanceChange()" [placeholder]="t('editor.fields.placeholder.animationDuration', '500ms')" />
1137
- <button
1138
- mat-icon-button
1139
- matSuffix
1140
- class="help-icon-button"
1141
- type="button"
1142
- [matTooltip]="t('editor.hints.animationDuration', 'animationDuration')"
1143
- matTooltipPosition="above"
1144
- >
1145
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1146
- </button>
1147
- </mat-form-field>
1148
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.ariaLabel', 'aria-label') }}</mat-label>
1149
- <input matInput [(ngModel)]="nav.ariaLabel" (ngModelChange)="onAppearanceChange()" />
1150
- </mat-form-field>
1151
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.ariaLabelledby', 'aria-labelledby') }}</mat-label>
1152
- <input matInput [(ngModel)]="nav.ariaLabelledby" (ngModelChange)="onAppearanceChange()" />
1153
- </mat-form-field>
1154
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.color', 'Cor (M2)') }}</mat-label>
1155
- <select matNativeControl [(ngModel)]="nav.color" (ngModelChange)="onAppearanceChange()">
1156
- <option [ngValue]="undefined">{{ t('editor.options.none', '(nenhuma)') }}</option>
1157
- <option value="primary">{{ t('editor.options.primary', 'Primary') }}</option>
1158
- <option value="accent">{{ t('editor.options.accent', 'Accent') }}</option>
1159
- <option value="warn">{{ t('editor.options.warn', 'Warn') }}</option>
1160
- </select>
1161
- <button
1162
- mat-icon-button
1163
- matSuffix
1164
- class="help-icon-button"
1165
- type="button"
1166
- [matTooltip]="t('editor.hints.preferTokens', 'Preferir tokens em Estilo.')"
1167
- matTooltipPosition="above"
1168
- >
1169
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1170
- </button>
1171
- </mat-form-field>
1172
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.backgroundColor', 'Cor de fundo (M2)') }}</mat-label>
1173
- <select matNativeControl [(ngModel)]="nav.backgroundColor" (ngModelChange)="onAppearanceChange()">
1174
- <option [ngValue]="undefined">{{ t('editor.options.none', '(nenhuma)') }}</option>
1175
- <option value="primary">{{ t('editor.options.primary', 'Primary') }}</option>
1176
- <option value="accent">{{ t('editor.options.accent', 'Accent') }}</option>
1177
- <option value="warn">{{ t('editor.options.warn', 'Warn') }}</option>
1178
- </select>
1179
- <button
1180
- mat-icon-button
1181
- matSuffix
1182
- class="help-icon-button"
1183
- type="button"
1184
- [matTooltip]="t('editor.hints.preferTokens', 'Preferir tokens em Estilo.')"
1185
- matTooltipPosition="above"
1214
+
1215
+ <mat-form-field appearance="outline" class="json-textarea-field full">
1216
+ <mat-label>{{ t('editor.fields.jsonConfig', 'Configuracao JSON') }}</mat-label>
1217
+ <textarea
1218
+ matInput
1219
+ [(ngModel)]="jsonText"
1220
+ (ngModelChange)="onJsonTextChange($event)"
1221
+ rows="22"
1222
+ spellcheck="false"
1223
+ class="editor-json"
1224
+ ></textarea>
1225
+ @if (isValid) {
1226
+ <mat-hint>{{ t('editor.json.valid', 'JSON valido') }}</mat-hint>
1227
+ }
1228
+ @if (!isValid && jsonText) {
1229
+ <mat-error>{{ t('editor.json.invalidPrefix', 'JSON invalido:') }} {{ errorMsg }}</mat-error>
1230
+ }
1231
+ </mat-form-field>
1232
+ </div>
1233
+ </mat-tab>
1234
+
1235
+ <mat-tab [label]="t('editor.tabs.style', 'Estilo')">
1236
+ <div class="editor-section editor-section-lg">
1237
+ <div class="editor-row">
1238
+ <span class="editor-muted">{{ t('editor.presets.title', 'Presets:') }}</span>
1239
+ <button mat-button color="primary" (click)="applyPreset('primary')">
1240
+ <mat-icon [praxisIcon]="'palette'"></mat-icon>
1241
+ {{ t('editor.presets.primary', 'Primario') }}
1242
+ </button>
1243
+ <button mat-button (click)="applyPreset('neutral')">
1244
+ <mat-icon [praxisIcon]="'contrast'"></mat-icon>
1245
+ {{ t('editor.presets.neutral', 'Neutro') }}
1246
+ </button>
1247
+ <button mat-button (click)="applyPreset('high-contrast')">
1248
+ <mat-icon [praxisIcon]="'visibility'"></mat-icon>
1249
+ {{ t('editor.presets.highContrast', 'Alto contraste') }}
1250
+ </button>
1251
+ <button mat-button (click)="clearTokens()">
1252
+ <mat-icon [praxisIcon]="'backspace'"></mat-icon>
1253
+ {{ t('editor.actions.clearTokens', 'Limpar tokens') }}
1254
+ </button>
1255
+ </div>
1256
+
1257
+ <div class="editor-grid two tight">
1258
+ <mat-form-field appearance="outline">
1259
+ <mat-label>{{ t('editor.fields.themeClass', 'Classe de tema (opcional)') }}</mat-label>
1260
+ <input matInput [(ngModel)]="appearance.themeClass" (ngModelChange)="onAppearanceChange()" [placeholder]="t('editor.fields.placeholder.themeClass', 'ex.: tabs-accented')" />
1261
+ </mat-form-field>
1262
+
1263
+ <mat-form-field appearance="outline">
1264
+ <mat-label>{{ t('editor.fields.density', 'Densidade') }}</mat-label>
1265
+ <select matNativeControl [(ngModel)]="appearance.density" (ngModelChange)="onAppearanceChange()">
1266
+ <option [ngValue]="undefined">{{ t('editor.density.default', 'Padrao') }}</option>
1267
+ <option value="compact">{{ t('editor.density.compact', 'Compacta') }}</option>
1268
+ <option value="comfortable">{{ t('editor.density.comfortable', 'Confortavel') }}</option>
1269
+ <option value="spacious">{{ t('editor.density.spacious', 'Espacosa') }}</option>
1270
+ </select>
1271
+ </mat-form-field>
1272
+ </div>
1273
+
1274
+ <div>
1275
+ <div class="editor-title-row">
1276
+ <h3 class="editor-title">{{ t('editor.sections.tokens', 'Tokens (Material 3)') }}</h3>
1277
+ <button
1278
+ mat-icon-button
1279
+ class="help-icon-button"
1280
+ type="button"
1281
+ [matTooltip]="t('editor.hints.tokenValueHelp', 'Valores aceitam CSS vars ou cores hex/rgb.')"
1282
+ matTooltipPosition="above"
1186
1283
  >
1187
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1188
- </button>
1189
- </mat-form-field>
1284
+ <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1285
+ </button>
1190
1286
  </div>
1191
- <div class="editor-row">
1192
- <mat-slide-toggle [(ngModel)]="nav.fitInkBarToContent" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.fitInkBarToContent', 'Indicador ajustado ao conteudo') }}</mat-slide-toggle>
1193
- <mat-slide-toggle [(ngModel)]="nav.disablePagination" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disablePagination', 'Sem paginacao') }}</mat-slide-toggle>
1194
- <mat-slide-toggle [(ngModel)]="nav.disableRipple" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disableRipple', 'Sem ripple') }}</mat-slide-toggle>
1195
- <mat-slide-toggle [(ngModel)]="nav.stretchTabs" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.stretchTabs', 'Esticar abas') }}</mat-slide-toggle>
1287
+ <div class="editor-grid two">
1288
+ @for (token of tokenList; track token) {
1289
+ <mat-form-field appearance="outline">
1290
+ <mat-label>{{ t(token.labelKey, token.fallback) }}</mat-label>
1291
+ <input matInput [placeholder]="t('editor.placeholders.tokenValue', 'var(--md-sys-color-primary) / #RRGGBB')" [ngModel]="appearance.tokens?.[token.key]" (ngModelChange)="onTokenChange(token.key, $event)" />
1292
+ </mat-form-field>
1293
+ }
1196
1294
  </div>
1197
1295
  </div>
1198
- </mat-tab>
1199
- <mat-tab [label]="t('editor.tabs.json', 'JSON')">
1200
- <div class="editor-section">
1201
- <div class="editor-toolbar">
1202
- <button mat-button (click)="formatJson()" [disabled]="!isValid">
1203
- <mat-icon [praxisIcon]="'format_align_left'"></mat-icon>{{ t('editor.actions.format', 'Formatar') }}
1204
- </button>
1205
- <button mat-button (click)="reset()">
1206
- <mat-icon [praxisIcon]="'restart_alt'"></mat-icon>{{ t('editor.actions.reset', 'Resetar') }}
1207
- </button>
1208
- </div>
1209
-
1296
+
1297
+ <div>
1298
+ <h3 class="editor-title">{{ t('editor.sections.customCss', 'CSS personalizado') }}</h3>
1210
1299
  <mat-form-field appearance="outline" class="json-textarea-field full">
1211
- <mat-label>{{ t('editor.fields.jsonConfig', 'Configuracao JSON') }}</mat-label>
1212
- <textarea
1213
- matInput
1214
- [(ngModel)]="jsonText"
1215
- (ngModelChange)="onJsonTextChange($event)"
1216
- rows="22"
1217
- spellcheck="false"
1218
- class="editor-json"
1219
- ></textarea>
1220
- <mat-hint *ngIf="isValid">{{ t('editor.json.valid', 'JSON valido') }}</mat-hint>
1221
- <mat-error *ngIf="!isValid && jsonText">{{ t('editor.json.invalidPrefix', 'JSON invalido:') }} {{ errorMsg }}</mat-error>
1300
+ <mat-label>{{ t('editor.fields.customCss', 'CSS a ser injetado no componente') }}</mat-label>
1301
+ <textarea matInput rows="10" [(ngModel)]="appearance.customCss" (ngModelChange)="onAppearanceChange()" [placeholder]="t('editor.placeholders.customCssExample', '.praxis-tabs-root .mdc-tab__text-label { font-weight: 600; }')"></textarea>
1222
1302
  </mat-form-field>
1223
1303
  </div>
1224
- </mat-tab>
1225
-
1226
- <mat-tab [label]="t('editor.tabs.style', 'Estilo')">
1227
- <div class="editor-section editor-section-lg">
1228
- <div class="editor-row">
1229
- <span class="editor-muted">{{ t('editor.presets.title', 'Presets:') }}</span>
1230
- <button mat-button color="primary" (click)="applyPreset('primary')">
1231
- <mat-icon [praxisIcon]="'palette'"></mat-icon>
1232
- {{ t('editor.presets.primary', 'Primario') }}
1233
- </button>
1234
- <button mat-button (click)="applyPreset('neutral')">
1235
- <mat-icon [praxisIcon]="'contrast'"></mat-icon>
1236
- {{ t('editor.presets.neutral', 'Neutro') }}
1237
- </button>
1238
- <button mat-button (click)="applyPreset('high-contrast')">
1239
- <mat-icon [praxisIcon]="'visibility'"></mat-icon>
1240
- {{ t('editor.presets.highContrast', 'Alto contraste') }}
1241
- </button>
1242
- <button mat-button (click)="clearTokens()">
1243
- <mat-icon [praxisIcon]="'backspace'"></mat-icon>
1244
- {{ t('editor.actions.clearTokens', 'Limpar tokens') }}
1245
- </button>
1246
- </div>
1247
-
1248
- <div class="editor-grid two tight">
1249
- <mat-form-field appearance="outline">
1250
- <mat-label>{{ t('editor.fields.themeClass', 'Classe de tema (opcional)') }}</mat-label>
1251
- <input matInput [(ngModel)]="appearance.themeClass" (ngModelChange)="onAppearanceChange()" [placeholder]="t('editor.fields.placeholder.themeClass', 'ex.: tabs-accented')" />
1252
- </mat-form-field>
1253
-
1254
- <mat-form-field appearance="outline">
1255
- <mat-label>{{ t('editor.fields.density', 'Densidade') }}</mat-label>
1256
- <select matNativeControl [(ngModel)]="appearance.density" (ngModelChange)="onAppearanceChange()">
1257
- <option [ngValue]="undefined">{{ t('editor.density.default', 'Padrao') }}</option>
1258
- <option value="compact">{{ t('editor.density.compact', 'Compacta') }}</option>
1259
- <option value="comfortable">{{ t('editor.density.comfortable', 'Confortavel') }}</option>
1260
- <option value="spacious">{{ t('editor.density.spacious', 'Espacosa') }}</option>
1261
- </select>
1262
- </mat-form-field>
1263
- </div>
1264
-
1265
- <div>
1266
- <div class="editor-title-row">
1267
- <h3 class="editor-title">{{ t('editor.sections.tokens', 'Tokens (Material 3)') }}</h3>
1268
- <button
1269
- mat-icon-button
1270
- class="help-icon-button"
1271
- type="button"
1272
- [matTooltip]="t('editor.hints.tokenValueHelp', 'Valores aceitam CSS vars ou cores hex/rgb.')"
1273
- matTooltipPosition="above"
1274
- >
1275
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1276
- </button>
1277
- </div>
1278
- <div class="editor-grid two">
1279
- <ng-container *ngFor="let token of tokenList">
1280
- <mat-form-field appearance="outline">
1281
- <mat-label>{{ t(token.labelKey, token.fallback) }}</mat-label>
1282
- <input matInput [placeholder]="t('editor.placeholders.tokenValue', 'var(--md-sys-color-primary) / #RRGGBB')" [ngModel]="appearance.tokens?.[token.key]" (ngModelChange)="onTokenChange(token.key, $event)" />
1304
+
1305
+ <div>
1306
+ <h3 class="editor-title">{{ t('editor.sections.scssSnippet', 'Snippet SCSS (para uso em styles.scss)') }}</h3>
1307
+ <pre class="editor-code">
1308
+ @use '@angular/material' as mat;
1309
+ {{ scssSnippet() }}
1310
+ </pre>
1311
+ </div>
1312
+ </div>
1313
+ </mat-tab>
1314
+
1315
+ <mat-tab [label]="t('editor.tabs.items', 'Abas')" [disabled]="primaryMode !== 'group'">
1316
+ <div class="editor-section">
1317
+ <button mat-stroked-button color="primary" (click)="addTab()"><mat-icon [praxisIcon]="'add'"></mat-icon>{{ t('editor.actions.addTab', 'Adicionar aba') }}</button>
1318
+ @if (editedConfig.tabs?.length) {
1319
+ <div class="editor-grid">
1320
+ @for (tab of editedConfig.tabs; track tab; let i = $index) {
1321
+ <div class="editor-card">
1322
+ <div class="editor-card-header">
1323
+ <strong class="editor-card-title">{{ t('editor.cards.tab', 'Aba') }} #{{ i+1 }}</strong>
1324
+ <button mat-icon-button (click)="moveTab(i, -1)" [disabled]="i===0"><mat-icon [praxisIcon]="'arrow_upward'"></mat-icon></button>
1325
+ <button mat-icon-button (click)="moveTab(i, 1)" [disabled]="i===editedConfig.tabs!.length-1"><mat-icon [praxisIcon]="'arrow_downward'"></mat-icon></button>
1326
+ <button mat-icon-button color="warn" (click)="removeTab(i)"><mat-icon [praxisIcon]="'delete'"></mat-icon></button>
1327
+ </div>
1328
+ <div class="editor-grid two tight">
1329
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.id', 'ID') }}</mat-label>
1330
+ <input matInput [(ngModel)]="tab.id" (ngModelChange)="onAppearanceChange()" />
1283
1331
  </mat-form-field>
1284
- </ng-container>
1285
- </div>
1286
- </div>
1287
-
1288
- <div>
1289
- <h3 class="editor-title">{{ t('editor.sections.customCss', 'CSS personalizado') }}</h3>
1290
- <mat-form-field appearance="outline" class="json-textarea-field full">
1291
- <mat-label>{{ t('editor.fields.customCss', 'CSS a ser injetado no componente') }}</mat-label>
1292
- <textarea matInput rows="10" [(ngModel)]="appearance.customCss" (ngModelChange)="onAppearanceChange()" [placeholder]="t('editor.placeholders.customCssExample', '.praxis-tabs-root .mdc-tab__text-label { font-weight: 600; }')"></textarea>
1332
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.label', 'Rotulo') }}</mat-label>
1333
+ <input matInput [(ngModel)]="tab.textLabel" (ngModelChange)="onAppearanceChange()" />
1334
+ </mat-form-field>
1335
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.icon', 'Icone') }}</mat-label>
1336
+ <input matInput [(ngModel)]="tab.icon" (ngModelChange)="onAppearanceChange()" />
1293
1337
  </mat-form-field>
1294
- </div>
1295
-
1296
- <div>
1297
- <h3 class="editor-title">{{ t('editor.sections.scssSnippet', 'Snippet SCSS (para uso em styles.scss)') }}</h3>
1298
- <pre class="editor-code">
1299
- @use '@angular/material' as mat;
1300
- {{ scssSnippet() }}
1301
- </pre>
1302
- </div>
1303
- </div>
1304
- </mat-tab>
1305
-
1306
- <mat-tab [label]="t('editor.tabs.items', 'Abas')" [disabled]="primaryMode !== 'group'">
1307
- <div class="editor-section">
1308
- <button mat-stroked-button color="primary" (click)="addTab()"><mat-icon [praxisIcon]="'add'"></mat-icon>{{ t('editor.actions.addTab', 'Adicionar aba') }}</button>
1309
- <div *ngIf="editedConfig.tabs?.length; else noTabs" class="editor-grid">
1310
- <div *ngFor="let tab of editedConfig.tabs; let i = index" class="editor-card">
1338
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.labelClass', 'Classe do rotulo') }}</mat-label>
1339
+ <input matInput [(ngModel)]="tab.labelClass" (ngModelChange)="onAppearanceChange()" />
1340
+ </mat-form-field>
1341
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.bodyClass', 'Classe do conteudo') }}</mat-label>
1342
+ <input matInput [(ngModel)]="tab.bodyClass" (ngModelChange)="onAppearanceChange()" />
1343
+ </mat-form-field>
1344
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.ariaLabel', 'aria-label') }}</mat-label>
1345
+ <input matInput [(ngModel)]="tab.ariaLabel" (ngModelChange)="onAppearanceChange()" />
1346
+ </mat-form-field>
1347
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.ariaLabelledby', 'aria-labelledby') }}</mat-label>
1348
+ <input matInput [(ngModel)]="tab.ariaLabelledby" (ngModelChange)="onAppearanceChange()" />
1349
+ </mat-form-field>
1350
+ </div>
1351
+ <div class="editor-row">
1352
+ <mat-slide-toggle [(ngModel)]="tab.disabled" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disabled', 'Desativada') }}</mat-slide-toggle>
1353
+ <mat-slide-toggle [ngModel]="tab.visible !== false" (ngModelChange)="tab.visible = $event; onAppearanceChange()">{{ t('editor.toggles.visible', 'Visivel') }}</mat-slide-toggle>
1354
+ </div>
1355
+ <!-- Widgets (componentes dinâmicos) -->
1356
+ <div class="editor-divider editor-grid">
1357
+ <div class="editor-row">
1358
+ <mat-form-field appearance="outline" class="editor-field-min">
1359
+ <mat-label>{{ t('editor.fields.addComponent', 'Adicionar componente') }}</mat-label>
1360
+ <select matNativeControl [(ngModel)]="selectedTabWidgetId[i]">
1361
+ <option [ngValue]="''">{{ t('editor.fields.selectPlaceholder', '(selecione)') }}</option>
1362
+ @for (c of componentOptions; track c) {
1363
+ <option [ngValue]="c.id">{{ c.friendlyName || c.id }}</option>
1364
+ }
1365
+ </select>
1366
+ </mat-form-field>
1367
+ <button mat-stroked-button (click)="addWidgetToTab(i)"><mat-icon [praxisIcon]="'add'"></mat-icon>{{ t('editor.actions.add', 'Adicionar') }}</button>
1368
+ <span class="editor-spacer"></span>
1369
+ <mat-form-field appearance="outline" class="editor-field-240">
1370
+ <mat-label>{{ t('editor.fields.resourcePath', 'Recurso (resourcePath)') }}</mat-label>
1371
+ <input matInput [(ngModel)]="quickResourcePathTab[i]" [placeholder]="t('editor.fields.placeholder.resourcePath', 'ex.: usuarios')" />
1372
+ </mat-form-field>
1373
+ <button mat-button (click)="addPresetToTab(i, 'form')"><mat-icon [praxisIcon]="'description'"></mat-icon>{{ t('editor.templates.form', 'Form') }}</button>
1374
+ <button mat-button (click)="addPresetToTab(i, 'table')"><mat-icon [praxisIcon]="'grid_on'"></mat-icon>{{ t('editor.templates.table', 'Tabela') }}</button>
1375
+ <button mat-button (click)="addPresetToTab(i, 'crud')"><mat-icon [praxisIcon]="'dynamic_form'"></mat-icon>{{ t('editor.templates.crud', 'CRUD') }}</button>
1376
+ </div>
1377
+ @if (tab.widgets?.length) {
1378
+ <div class="editor-grid" cdkDropList [cdkDropListData]="tab.widgets || []" (cdkDropListDropped)="onTabWidgetDrop(i, $event)">
1379
+ @for (w of tab.widgets; track w; let wi = $index) {
1380
+ <div cdkDrag class="editor-card dashed">
1311
1381
  <div class="editor-card-header">
1312
- <strong class="editor-card-title">{{ t('editor.cards.tab', 'Aba') }} #{{ i+1 }}</strong>
1313
- <button mat-icon-button (click)="moveTab(i, -1)" [disabled]="i===0"><mat-icon [praxisIcon]="'arrow_upward'"></mat-icon></button>
1314
- <button mat-icon-button (click)="moveTab(i, 1)" [disabled]="i===editedConfig.tabs!.length-1"><mat-icon [praxisIcon]="'arrow_downward'"></mat-icon></button>
1315
- <button mat-icon-button color="warn" (click)="removeTab(i)"><mat-icon [praxisIcon]="'delete'"></mat-icon></button>
1382
+ <button mat-icon-button cdkDragHandle [matTooltip]="t('editor.actions.dragToReorder', 'Arrastar para reordenar')" [attr.aria-label]="t('editor.actions.dragToReorder', 'Arrastar para reordenar')">
1383
+ <mat-icon [praxisIcon]="'drag_indicator'"></mat-icon>
1384
+ </button>
1385
+ <strong class="editor-card-title">{{ getCompName(w.id) }}</strong>
1386
+ <button mat-icon-button color="warn" (click)="removeWidgetFromTab(i, wi)" [matTooltip]="t('editor.actions.removeComponent', 'Remover componente')" [attr.aria-label]="t('editor.actions.removeComponent', 'Remover componente')"><mat-icon [praxisIcon]="'delete'"></mat-icon></button>
1316
1387
  </div>
1317
1388
  <div class="editor-grid two tight">
1318
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.id', 'ID') }}</mat-label>
1319
- <input matInput [(ngModel)]="tab.id" (ngModelChange)="onAppearanceChange()" />
1320
- </mat-form-field>
1321
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.label', 'Rotulo') }}</mat-label>
1322
- <input matInput [(ngModel)]="tab.textLabel" (ngModelChange)="onAppearanceChange()" />
1323
- </mat-form-field>
1324
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.icon', 'Icone') }}</mat-label>
1325
- <input matInput [(ngModel)]="tab.icon" (ngModelChange)="onAppearanceChange()" />
1326
- </mat-form-field>
1327
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.labelClass', 'Classe do rotulo') }}</mat-label>
1328
- <input matInput [(ngModel)]="tab.labelClass" (ngModelChange)="onAppearanceChange()" />
1329
- </mat-form-field>
1330
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.bodyClass', 'Classe do conteudo') }}</mat-label>
1331
- <input matInput [(ngModel)]="tab.bodyClass" (ngModelChange)="onAppearanceChange()" />
1332
- </mat-form-field>
1333
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.ariaLabel', 'aria-label') }}</mat-label>
1334
- <input matInput [(ngModel)]="tab.ariaLabel" (ngModelChange)="onAppearanceChange()" />
1389
+ <mat-form-field appearance="outline">
1390
+ <mat-label>{{ t('editor.fields.inputsJson', 'Inputs (JSON)') }}</mat-label>
1391
+ <textarea matInput rows="4" [ngModel]="stringify(w.inputs)" (ngModelChange)="updateWidgetInputsTab(i, wi, $event)"></textarea>
1335
1392
  </mat-form-field>
1336
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.ariaLabelledby', 'aria-labelledby') }}</mat-label>
1337
- <input matInput [(ngModel)]="tab.ariaLabelledby" (ngModelChange)="onAppearanceChange()" />
1393
+ <mat-form-field appearance="outline">
1394
+ <mat-label>{{ t('editor.fields.outputsJson', 'Outputs (JSON)') }}</mat-label>
1395
+ <textarea matInput rows="4" [ngModel]="stringify(w.outputs)" (ngModelChange)="updateWidgetOutputsTab(i, wi, $event)"></textarea>
1338
1396
  </mat-form-field>
1339
1397
  </div>
1340
- <div class="editor-row">
1341
- <mat-slide-toggle [(ngModel)]="tab.disabled" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disabled', 'Desativada') }}</mat-slide-toggle>
1342
- <mat-slide-toggle [ngModel]="tab.visible !== false" (ngModelChange)="tab.visible = $event; onAppearanceChange()">{{ t('editor.toggles.visible', 'Visivel') }}</mat-slide-toggle>
1343
- </div>
1344
-
1345
- <!-- Widgets (componentes dinâmicos) -->
1346
- <div class="editor-divider editor-grid">
1347
- <div class="editor-row">
1348
- <mat-form-field appearance="outline" class="editor-field-min">
1349
- <mat-label>{{ t('editor.fields.addComponent', 'Adicionar componente') }}</mat-label>
1350
- <select matNativeControl [(ngModel)]="selectedTabWidgetId[i]">
1351
- <option [ngValue]="''">{{ t('editor.fields.selectPlaceholder', '(selecione)') }}</option>
1352
- <option *ngFor="let c of componentOptions" [ngValue]="c.id">{{ c.friendlyName || c.id }}</option>
1353
- </select>
1354
- </mat-form-field>
1355
- <button mat-stroked-button (click)="addWidgetToTab(i)"><mat-icon [praxisIcon]="'add'"></mat-icon>{{ t('editor.actions.add', 'Adicionar') }}</button>
1356
- <span class="editor-spacer"></span>
1357
- <mat-form-field appearance="outline" class="editor-field-240">
1358
- <mat-label>{{ t('editor.fields.resourcePath', 'Recurso (resourcePath)') }}</mat-label>
1359
- <input matInput [(ngModel)]="quickResourcePathTab[i]" [placeholder]="t('editor.fields.placeholder.resourcePath', 'ex.: usuarios')" />
1360
- </mat-form-field>
1361
- <button mat-button (click)="addPresetToTab(i, 'form')"><mat-icon [praxisIcon]="'description'"></mat-icon>{{ t('editor.templates.form', 'Form') }}</button>
1362
- <button mat-button (click)="addPresetToTab(i, 'table')"><mat-icon [praxisIcon]="'grid_on'"></mat-icon>{{ t('editor.templates.table', 'Tabela') }}</button>
1363
- <button mat-button (click)="addPresetToTab(i, 'crud')"><mat-icon [praxisIcon]="'dynamic_form'"></mat-icon>{{ t('editor.templates.crud', 'CRUD') }}</button>
1364
- </div>
1365
-
1366
- <div *ngIf="tab.widgets?.length" class="editor-grid" cdkDropList [cdkDropListData]="tab.widgets || []" (cdkDropListDropped)="onTabWidgetDrop(i, $event)">
1367
- <div *ngFor="let w of tab.widgets; let wi = index" cdkDrag class="editor-card dashed">
1368
- <div class="editor-card-header">
1369
- <button mat-icon-button cdkDragHandle [matTooltip]="t('editor.actions.dragToReorder', 'Arrastar para reordenar')" [attr.aria-label]="t('editor.actions.dragToReorder', 'Arrastar para reordenar')">
1370
- <mat-icon [praxisIcon]="'drag_indicator'"></mat-icon>
1371
- </button>
1372
- <strong class="editor-card-title">{{ getCompName(w.id) }}</strong>
1373
- <button mat-icon-button color="warn" (click)="removeWidgetFromTab(i, wi)" [matTooltip]="t('editor.actions.removeComponent', 'Remover componente')" [attr.aria-label]="t('editor.actions.removeComponent', 'Remover componente')"><mat-icon [praxisIcon]="'delete'"></mat-icon></button>
1374
- </div>
1375
- <div class="editor-grid two tight">
1376
- <mat-form-field appearance="outline">
1377
- <mat-label>{{ t('editor.fields.inputsJson', 'Inputs (JSON)') }}</mat-label>
1378
- <textarea matInput rows="4" [ngModel]="stringify(w.inputs)" (ngModelChange)="updateWidgetInputsTab(i, wi, $event)"></textarea>
1379
- </mat-form-field>
1380
- <mat-form-field appearance="outline">
1381
- <mat-label>{{ t('editor.fields.outputsJson', 'Outputs (JSON)') }}</mat-label>
1382
- <textarea matInput rows="4" [ngModel]="stringify(w.outputs)" (ngModelChange)="updateWidgetOutputsTab(i, wi, $event)"></textarea>
1383
- </mat-form-field>
1384
- </div>
1385
- </div>
1386
- </div>
1387
- </div>
1388
1398
  </div>
1389
- </div>
1390
- <ng-template #noTabs><em class="editor-muted">{{ t('editor.empty.noTabs', 'Nenhuma aba definida.') }}</em></ng-template>
1399
+ }
1391
1400
  </div>
1392
- </mat-tab>
1393
-
1394
- <mat-tab [label]="t('editor.tabs.accessibility', 'Acessibilidade')">
1395
- <div class="editor-section">
1396
- <div class="editor-row">
1397
- <mat-slide-toggle [(ngModel)]="accessibility.highContrast" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.highContrast', 'Alto contraste') }}</mat-slide-toggle>
1398
- <mat-slide-toggle [(ngModel)]="accessibility.reduceMotion" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.reduceMotion', 'Reduzir movimento') }}</mat-slide-toggle>
1399
- </div>
1401
+ }
1402
+ </div>
1403
+ </div>
1404
+ }
1405
+ </div>
1406
+ } @else {
1407
+ <em class="editor-muted">{{ t('editor.empty.noTabs', 'Nenhuma aba definida.') }}</em>
1408
+ }
1409
+ </div>
1410
+ </mat-tab>
1411
+
1412
+ <mat-tab [label]="t('editor.tabs.accessibility', 'Acessibilidade')">
1413
+ <div class="editor-section">
1414
+ <div class="editor-row">
1415
+ <mat-slide-toggle [(ngModel)]="accessibility.highContrast" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.highContrast', 'Alto contraste') }}</mat-slide-toggle>
1416
+ <mat-slide-toggle [(ngModel)]="accessibility.reduceMotion" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.reduceMotion', 'Reduzir movimento') }}</mat-slide-toggle>
1400
1417
  </div>
1401
- </mat-tab>
1402
-
1403
- <mat-tab [label]="t('editor.tabs.links', 'Links')" [disabled]="primaryMode !== 'nav'">
1404
- <div class="editor-section">
1405
- <button mat-stroked-button color="primary" (click)="addLink()"><mat-icon [praxisIcon]="'add_link'"></mat-icon>{{ t('editor.actions.addLink', 'Adicionar link') }}</button>
1406
- <div *ngIf="nav.links?.length; else noLinks" class="editor-grid">
1407
- <div *ngFor="let l of nav.links; let i = index" class="editor-card">
1408
- <div class="editor-card-header">
1409
- <strong class="editor-card-title">{{ t('editor.cards.link', 'Link') }} #{{ i+1 }}</strong>
1410
- <button mat-icon-button (click)="moveLink(i, -1)" [disabled]="i===0"><mat-icon [praxisIcon]="'arrow_upward'"></mat-icon></button>
1411
- <button mat-icon-button (click)="moveLink(i, 1)" [disabled]="i===nav.links!.length-1"><mat-icon [praxisIcon]="'arrow_downward'"></mat-icon></button>
1412
- <button mat-icon-button color="warn" (click)="removeLink(i)"><mat-icon [praxisIcon]="'delete'"></mat-icon></button>
1413
- </div>
1414
- <div class="editor-grid two tight">
1415
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.id', 'ID') }}</mat-label>
1418
+ </div>
1419
+ </mat-tab>
1420
+
1421
+ <mat-tab [label]="t('editor.tabs.links', 'Links')" [disabled]="primaryMode !== 'nav'">
1422
+ <div class="editor-section">
1423
+ <button mat-stroked-button color="primary" (click)="addLink()"><mat-icon [praxisIcon]="'add_link'"></mat-icon>{{ t('editor.actions.addLink', 'Adicionar link') }}</button>
1424
+ @if (nav.links?.length) {
1425
+ <div class="editor-grid">
1426
+ @for (l of nav.links; track l; let i = $index) {
1427
+ <div class="editor-card">
1428
+ <div class="editor-card-header">
1429
+ <strong class="editor-card-title">{{ t('editor.cards.link', 'Link') }} #{{ i+1 }}</strong>
1430
+ <button mat-icon-button (click)="moveLink(i, -1)" [disabled]="i===0"><mat-icon [praxisIcon]="'arrow_upward'"></mat-icon></button>
1431
+ <button mat-icon-button (click)="moveLink(i, 1)" [disabled]="i===nav.links!.length-1"><mat-icon [praxisIcon]="'arrow_downward'"></mat-icon></button>
1432
+ <button mat-icon-button color="warn" (click)="removeLink(i)"><mat-icon [praxisIcon]="'delete'"></mat-icon></button>
1433
+ </div>
1434
+ <div class="editor-grid two tight">
1435
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.id', 'ID') }}</mat-label>
1416
1436
  <input matInput [(ngModel)]="l.id" (ngModelChange)="onAppearanceChange()" />
1417
1437
  </mat-form-field>
1418
1438
  <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.label', 'Rotulo') }}</mat-label>
1419
- <input matInput [(ngModel)]="l.label" (ngModelChange)="onAppearanceChange()" />
1420
- </mat-form-field>
1421
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.icon', 'Icone') }}</mat-label>
1422
- <input matInput [(ngModel)]="l.icon" (ngModelChange)="onAppearanceChange()" />
1423
- </mat-form-field>
1424
- </div>
1425
- <div class="editor-row">
1426
- <mat-slide-toggle [(ngModel)]="l.disabled" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.linkDisabled', 'Desativado') }}</mat-slide-toggle>
1427
- <mat-slide-toggle [ngModel]="l.visible !== false" (ngModelChange)="l.visible = $event; onAppearanceChange()">{{ t('editor.toggles.visible', 'Visivel') }}</mat-slide-toggle>
1428
- <mat-slide-toggle [(ngModel)]="l.disableRipple" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disableRipple', 'Sem ripple') }}</mat-slide-toggle>
1429
- <mat-slide-toggle [(ngModel)]="l.fitInkBarToContent" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.fitInkBarToContent', 'Indicador ajustado ao conteudo') }}</mat-slide-toggle>
1430
- </div>
1431
-
1432
- <!-- Widgets para Links -->
1433
- <div class="editor-divider editor-grid">
1434
- <div class="editor-row">
1435
- <mat-form-field appearance="outline" class="editor-field-min">
1436
- <mat-label>{{ t('editor.fields.addComponent', 'Adicionar componente') }}</mat-label>
1437
- <select matNativeControl [(ngModel)]="selectedLinkWidgetId[i]">
1438
- <option [ngValue]="''">{{ t('editor.fields.selectPlaceholder', '(selecione)') }}</option>
1439
- <option *ngFor="let c of componentOptions" [ngValue]="c.id">{{ c.friendlyName || c.id }}</option>
1440
- </select>
1441
- </mat-form-field>
1442
- <button mat-stroked-button (click)="addWidgetToLink(i)"><mat-icon [praxisIcon]="'add'"></mat-icon>{{ t('editor.actions.add', 'Adicionar') }}</button>
1443
- <span class="editor-spacer"></span>
1444
- <mat-form-field appearance="outline" class="editor-field-240">
1445
- <mat-label>{{ t('editor.fields.resourcePath', 'Recurso (resourcePath)') }}</mat-label>
1446
- <input matInput [(ngModel)]="quickResourcePathLink[i]" [placeholder]="t('editor.fields.placeholder.resourcePath', 'ex.: usuarios')" />
1447
- </mat-form-field>
1448
- <button mat-button (click)="addPresetToLink(i, 'form')"><mat-icon [praxisIcon]="'description'"></mat-icon>{{ t('editor.templates.form', 'Form') }}</button>
1449
- <button mat-button (click)="addPresetToLink(i, 'table')"><mat-icon [praxisIcon]="'grid_on'"></mat-icon>{{ t('editor.templates.table', 'Tabela') }}</button>
1450
- <button mat-button (click)="addPresetToLink(i, 'crud')"><mat-icon [praxisIcon]="'dynamic_form'"></mat-icon>{{ t('editor.templates.crud', 'CRUD') }}</button>
1451
- </div>
1452
-
1453
- <div *ngIf="l.widgets?.length" class="editor-grid" cdkDropList [cdkDropListData]="l.widgets || []" (cdkDropListDropped)="onLinkWidgetDrop(i, $event)">
1454
- <div *ngFor="let w of l.widgets; let wi = index" cdkDrag class="editor-card dashed">
1439
+ <input matInput [(ngModel)]="l.label" (ngModelChange)="onAppearanceChange()" />
1440
+ </mat-form-field>
1441
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.icon', 'Icone') }}</mat-label>
1442
+ <input matInput [(ngModel)]="l.icon" (ngModelChange)="onAppearanceChange()" />
1443
+ </mat-form-field>
1444
+ </div>
1445
+ <div class="editor-row">
1446
+ <mat-slide-toggle [(ngModel)]="l.disabled" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.linkDisabled', 'Desativado') }}</mat-slide-toggle>
1447
+ <mat-slide-toggle [ngModel]="l.visible !== false" (ngModelChange)="l.visible = $event; onAppearanceChange()">{{ t('editor.toggles.visible', 'Visivel') }}</mat-slide-toggle>
1448
+ <mat-slide-toggle [(ngModel)]="l.disableRipple" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disableRipple', 'Sem ripple') }}</mat-slide-toggle>
1449
+ <mat-slide-toggle [(ngModel)]="l.fitInkBarToContent" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.fitInkBarToContent', 'Indicador ajustado ao conteudo') }}</mat-slide-toggle>
1450
+ </div>
1451
+ <!-- Widgets para Links -->
1452
+ <div class="editor-divider editor-grid">
1453
+ <div class="editor-row">
1454
+ <mat-form-field appearance="outline" class="editor-field-min">
1455
+ <mat-label>{{ t('editor.fields.addComponent', 'Adicionar componente') }}</mat-label>
1456
+ <select matNativeControl [(ngModel)]="selectedLinkWidgetId[i]">
1457
+ <option [ngValue]="''">{{ t('editor.fields.selectPlaceholder', '(selecione)') }}</option>
1458
+ @for (c of componentOptions; track c) {
1459
+ <option [ngValue]="c.id">{{ c.friendlyName || c.id }}</option>
1460
+ }
1461
+ </select>
1462
+ </mat-form-field>
1463
+ <button mat-stroked-button (click)="addWidgetToLink(i)"><mat-icon [praxisIcon]="'add'"></mat-icon>{{ t('editor.actions.add', 'Adicionar') }}</button>
1464
+ <span class="editor-spacer"></span>
1465
+ <mat-form-field appearance="outline" class="editor-field-240">
1466
+ <mat-label>{{ t('editor.fields.resourcePath', 'Recurso (resourcePath)') }}</mat-label>
1467
+ <input matInput [(ngModel)]="quickResourcePathLink[i]" [placeholder]="t('editor.fields.placeholder.resourcePath', 'ex.: usuarios')" />
1468
+ </mat-form-field>
1469
+ <button mat-button (click)="addPresetToLink(i, 'form')"><mat-icon [praxisIcon]="'description'"></mat-icon>{{ t('editor.templates.form', 'Form') }}</button>
1470
+ <button mat-button (click)="addPresetToLink(i, 'table')"><mat-icon [praxisIcon]="'grid_on'"></mat-icon>{{ t('editor.templates.table', 'Tabela') }}</button>
1471
+ <button mat-button (click)="addPresetToLink(i, 'crud')"><mat-icon [praxisIcon]="'dynamic_form'"></mat-icon>{{ t('editor.templates.crud', 'CRUD') }}</button>
1472
+ </div>
1473
+ @if (l.widgets?.length) {
1474
+ <div class="editor-grid" cdkDropList [cdkDropListData]="l.widgets || []" (cdkDropListDropped)="onLinkWidgetDrop(i, $event)">
1475
+ @for (w of l.widgets; track w; let wi = $index) {
1476
+ <div cdkDrag class="editor-card dashed">
1455
1477
  <div class="editor-card-header">
1456
1478
  <button mat-icon-button cdkDragHandle [matTooltip]="t('editor.actions.dragToReorder', 'Arrastar para reordenar')" [attr.aria-label]="t('editor.actions.dragToReorder', 'Arrastar para reordenar')">
1457
1479
  <mat-icon [praxisIcon]="'drag_indicator'"></mat-icon>
@@ -1470,21 +1492,25 @@ class PraxisTabsConfigEditor {
1470
1492
  </mat-form-field>
1471
1493
  </div>
1472
1494
  </div>
1473
- </div>
1495
+ }
1474
1496
  </div>
1475
- </div>
1497
+ }
1476
1498
  </div>
1477
- <ng-template #noLinks><em class="editor-muted">{{ t('editor.empty.noLinks', 'Nenhum link definido.') }}</em></ng-template>
1478
1499
  </div>
1479
- </mat-tab>
1500
+ }
1501
+ </div>
1502
+ } @else {
1503
+ <em class="editor-muted">{{ t('editor.empty.noLinks', 'Nenhum link definido.') }}</em>
1504
+ }
1505
+ </div>
1506
+ </mat-tab>
1480
1507
  </mat-tab-group>
1481
1508
  </div>
1482
- `, isInline: true, styles: [":host{display:block;color:var(--md-sys-color-on-surface)}.editor-shell{display:grid;gap:12px}.editor-topbar{display:flex;align-items:start;justify-content:flex-start}.editor-mode-field{width:min(320px,100%)}.editor-diagnostics{display:grid;gap:8px}.editor-diagnostics-title{font-size:.95rem;font-weight:600;color:var(--md-sys-color-on-surface)}.editor-diagnostic{display:grid;gap:4px;padding:10px 12px;border-radius:10px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface)}.editor-diagnostic code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;color:var(--md-sys-color-on-surface-variant)}.editor-diagnostic.level-error{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container);border-color:var(--md-sys-color-error)}.editor-diagnostic.level-warning{background:var(--md-sys-color-tertiary-container, var(--md-sys-color-surface-container));color:var(--md-sys-color-on-tertiary-container, var(--md-sys-color-on-surface));border-color:var(--md-sys-color-outline-variant)}.editor-tabs{--editor-surface: var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));--editor-border: 1px solid var(--md-sys-color-outline-variant);--editor-radius: 12px;--editor-muted: var(--md-sys-color-on-surface-variant)}.editor-section{padding:12px;display:grid;gap:12px;background:var(--editor-surface);border:var(--editor-border);border-radius:var(--editor-radius)}.editor-section-lg{gap:16px}.editor-row{display:flex;gap:10px;flex-wrap:wrap;align-items:center}.editor-grid{display:grid;gap:12px}.editor-grid.two{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.editor-grid.tight{gap:8px}.editor-toolbar{display:flex;gap:8px;flex-wrap:wrap;align-items:center}.editor-muted{color:var(--editor-muted)}.editor-title{margin:6px 0 8px;font-size:1rem}.editor-title-row{display:flex;align-items:center;gap:8px}.editor-code{white-space:pre-wrap;background:var(--md-sys-color-surface-container);padding:8px;border-radius:6px;border:1px solid var(--md-sys-color-outline-variant)}.editor-card{border:1px solid var(--md-sys-color-outline-variant);padding:10px;border-radius:10px;display:grid;gap:8px;background:var(--md-sys-color-surface)}.editor-card.dashed{border-style:dashed}.editor-card-header{display:flex;align-items:center;gap:8px}.editor-card-title{flex:1;font-weight:600}.editor-divider{border-top:1px solid var(--md-sys-color-outline-variant);padding-top:8px}.editor-spacer{flex:1}.editor-field-min{min-width:260px}.editor-section .editor-field-min{width:260px;max-width:260px}.editor-field-240{width:240px}.editor-section .editor-field-240{width:240px;max-width:240px}.editor-json{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.editor-section .mat-mdc-form-field{width:100%;max-width:520px;--mdc-outlined-text-field-container-height: 48px;--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-outline);--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-error-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-focus-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-hover-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-label-text-color: var(--md-sys-color-on-surface-variant);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-supporting-text-color: var(--md-sys-color-on-surface-variant)}.editor-section .mat-mdc-form-field.full{max-width:none}.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: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i3.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i3$1.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i3$1.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatLabel, selector: "mat-label" }, { kind: "directive", type: i5.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i5.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i5.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i6.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: "ngmodule", type: MatIconModule }, { kind: "component", type: i7.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i6$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: i6$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i9.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i10.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i10.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i10.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i11.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
1509
+ `, isInline: true, styles: [":host{display:block;color:var(--md-sys-color-on-surface)}.editor-shell{display:grid;gap:12px}.editor-topbar{display:flex;align-items:start;justify-content:flex-start}.editor-mode-field{width:min(320px,100%)}.editor-diagnostics{display:grid;gap:8px}.editor-diagnostics-title{font-size:.95rem;font-weight:600;color:var(--md-sys-color-on-surface)}.editor-diagnostic{display:grid;gap:4px;padding:10px 12px;border-radius:10px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface)}.editor-diagnostic code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;color:var(--md-sys-color-on-surface-variant)}.editor-diagnostic.level-error{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container);border-color:var(--md-sys-color-error)}.editor-diagnostic.level-warning{background:var(--md-sys-color-tertiary-container, var(--md-sys-color-surface-container));color:var(--md-sys-color-on-tertiary-container, var(--md-sys-color-on-surface));border-color:var(--md-sys-color-outline-variant)}.editor-tabs{--editor-surface: var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));--editor-border: 1px solid var(--md-sys-color-outline-variant);--editor-radius: 12px;--editor-muted: var(--md-sys-color-on-surface-variant)}.editor-section{padding:12px;display:grid;gap:12px;background:var(--editor-surface);border:var(--editor-border);border-radius:var(--editor-radius)}.editor-section-lg{gap:16px}.editor-row{display:flex;gap:10px;flex-wrap:wrap;align-items:center}.editor-grid{display:grid;gap:12px}.editor-grid.two{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.editor-grid.tight{gap:8px}.editor-toolbar{display:flex;gap:8px;flex-wrap:wrap;align-items:center}.editor-muted{color:var(--editor-muted)}.editor-title{margin:6px 0 8px;font-size:1rem}.editor-title-row{display:flex;align-items:center;gap:8px}.editor-code{white-space:pre-wrap;background:var(--md-sys-color-surface-container);padding:8px;border-radius:6px;border:1px solid var(--md-sys-color-outline-variant)}.editor-card{border:1px solid var(--md-sys-color-outline-variant);padding:10px;border-radius:10px;display:grid;gap:8px;background:var(--md-sys-color-surface)}.editor-card.dashed{border-style:dashed}.editor-card-header{display:flex;align-items:center;gap:8px}.editor-card-title{flex:1;font-weight:600}.editor-divider{border-top:1px solid var(--md-sys-color-outline-variant);padding-top:8px}.editor-spacer{flex:1}.editor-field-min{min-width:260px}.editor-section .editor-field-min{width:260px;max-width:260px}.editor-field-240{width:240px}.editor-section .editor-field-240{width:240px;max-width:240px}.editor-json{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.editor-section .mat-mdc-form-field{width:100%;max-width:520px;--mdc-outlined-text-field-container-height: 48px;--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-outline);--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-error-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-focus-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-hover-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-label-text-color: var(--md-sys-color-on-surface-variant);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-supporting-text-color: var(--md-sys-color-on-surface-variant)}.editor-section .mat-mdc-form-field.full{max-width:none}.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: FormsModule }, { kind: "directive", type: i2_1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2_1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2_1.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_1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2_1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2_1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2_1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i3.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i3.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i4.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.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: "ngmodule", type: MatIconModule }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i6.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: i6.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i8.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i9.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i9.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i9.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i10.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
1483
1510
  }
1484
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisTabsConfigEditor, decorators: [{
1511
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisTabsConfigEditor, decorators: [{
1485
1512
  type: Component,
1486
1513
  args: [{ selector: 'praxis-tabs-config-editor', standalone: true, imports: [
1487
- CommonModule,
1488
1514
  FormsModule,
1489
1515
  MatTabsModule,
1490
1516
  MatFormFieldModule,
@@ -1494,7 +1520,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
1494
1520
  MatButtonModule,
1495
1521
  MatSlideToggleModule,
1496
1522
  DragDropModule,
1497
- MatTooltipModule,
1523
+ MatTooltipModule
1498
1524
  ], providers: [providePraxisI18nConfig(PRAXIS_TABS_I18N_CONFIG)], template: `
1499
1525
  <div class="editor-shell">
1500
1526
  <div class="editor-topbar">
@@ -1506,36 +1532,41 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
1506
1532
  </select>
1507
1533
  </mat-form-field>
1508
1534
  </div>
1509
-
1510
- <div *ngIf="diagnostics.length" class="editor-diagnostics">
1511
- <div class="editor-diagnostics-title">{{ t('editor.diagnostics.title', 'Diagnosticos de authoring') }}</div>
1512
- <div
1513
- *ngFor="let diagnostic of diagnostics"
1514
- class="editor-diagnostic"
1515
- [class.level-error]="diagnostic.level === 'error'"
1516
- [class.level-warning]="diagnostic.level === 'warning'"
1517
- [class.level-info]="diagnostic.level === 'info'"
1518
- >
1519
- <strong>{{ diagnosticLevelLabel(diagnostic.level) }}</strong>
1520
- <span>{{ diagnostic.message }}</span>
1521
- <code *ngIf="diagnostic.path">{{ diagnostic.path }}</code>
1535
+
1536
+ @if (diagnostics.length) {
1537
+ <div class="editor-diagnostics">
1538
+ <div class="editor-diagnostics-title">{{ t('editor.diagnostics.title', 'Diagnosticos de authoring') }}</div>
1539
+ @for (diagnostic of diagnostics; track diagnostic) {
1540
+ <div
1541
+ class="editor-diagnostic"
1542
+ [class.level-error]="diagnostic.level === 'error'"
1543
+ [class.level-warning]="diagnostic.level === 'warning'"
1544
+ [class.level-info]="diagnostic.level === 'info'"
1545
+ >
1546
+ <strong>{{ diagnosticLevelLabel(diagnostic.level) }}</strong>
1547
+ <span>{{ diagnostic.message }}</span>
1548
+ @if (diagnostic.path) {
1549
+ <code>{{ diagnostic.path }}</code>
1550
+ }
1551
+ </div>
1552
+ }
1522
1553
  </div>
1523
- </div>
1524
-
1525
- <mat-tab-group class="editor-tabs">
1526
- <mat-tab [label]="t('editor.tabs.behavior', 'Comportamento')">
1527
- <div class="editor-section">
1528
- <div class="editor-row">
1529
- <mat-slide-toggle [(ngModel)]="behavior.closeable" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.closeable', 'Fechavel') }}</mat-slide-toggle>
1530
- <mat-slide-toggle [(ngModel)]="behavior.lazyLoad" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.lazyLoad', 'Carregar sob demanda') }}</mat-slide-toggle>
1531
- <mat-slide-toggle [(ngModel)]="behavior.reorderable" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.reorderable', 'Reordenavel') }}</mat-slide-toggle>
1554
+ }
1555
+
1556
+ <mat-tab-group class="editor-tabs">
1557
+ <mat-tab [label]="t('editor.tabs.behavior', 'Comportamento')">
1558
+ <div class="editor-section">
1559
+ <div class="editor-row">
1560
+ <mat-slide-toggle [(ngModel)]="behavior.closeable" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.closeable', 'Fechavel') }}</mat-slide-toggle>
1561
+ <mat-slide-toggle [(ngModel)]="behavior.lazyLoad" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.lazyLoad', 'Carregar sob demanda') }}</mat-slide-toggle>
1562
+ <mat-slide-toggle [(ngModel)]="behavior.reorderable" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.reorderable', 'Reordenavel') }}</mat-slide-toggle>
1563
+ </div>
1532
1564
  </div>
1533
- </div>
1534
- </mat-tab>
1535
- <mat-tab [label]="t('editor.tabs.group', 'Grupo')" [disabled]="primaryMode !== 'group'">
1536
- <div class="editor-section">
1537
- <div class="editor-grid two">
1538
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.groupAlignTabs', 'Alinhamento das abas') }}</mat-label>
1565
+ </mat-tab>
1566
+ <mat-tab [label]="t('editor.tabs.group', 'Grupo')" [disabled]="primaryMode !== 'group'">
1567
+ <div class="editor-section">
1568
+ <div class="editor-grid two">
1569
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.groupAlignTabs', 'Alinhamento das abas') }}</mat-label>
1539
1570
  <select matNativeControl [(ngModel)]="group.alignTabs" (ngModelChange)="onAppearanceChange()">
1540
1571
  <option [ngValue]="undefined">{{ t('editor.options.default', 'Padrao') }}</option>
1541
1572
  <option value="start">{{ t('editor.options.start', 'Inicio') }}</option>
@@ -1544,428 +1575,445 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
1544
1575
  </select>
1545
1576
  </mat-form-field>
1546
1577
  <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.groupHeaderPosition', 'Posicao do header') }}</mat-label>
1547
- <select matNativeControl [(ngModel)]="group.headerPosition" (ngModelChange)="onAppearanceChange()">
1548
- <option [ngValue]="undefined">{{ t('editor.options.aboveDefault', 'Acima (padrao)') }}</option>
1549
- <option value="above">{{ t('editor.options.above', 'Acima') }}</option>
1550
- <option value="below">{{ t('editor.options.below', 'Abaixo') }}</option>
1551
- </select>
1552
- </mat-form-field>
1553
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.selectedIndex', 'Indice selecionado') }}</mat-label>
1554
- <input matInput type="number" [(ngModel)]="group.selectedIndex" (ngModelChange)="onAppearanceChange()" />
1555
- <button
1556
- mat-icon-button
1557
- matSuffix
1558
- class="help-icon-button"
1559
- type="button"
1560
- [matTooltip]="t('editor.hints.selectedIndex', 'selectedIndex')"
1561
- matTooltipPosition="above"
1562
- >
1563
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1564
- </button>
1565
- </mat-form-field>
1566
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.animationDuration', 'Duracao da animacao') }}</mat-label>
1567
- <input matInput [(ngModel)]="group.animationDuration" (ngModelChange)="onAppearanceChange()" [placeholder]="t('editor.fields.placeholder.animationDuration', '500ms')" />
1568
- <button
1569
- mat-icon-button
1570
- matSuffix
1571
- class="help-icon-button"
1572
- type="button"
1573
- [matTooltip]="t('editor.hints.animationDuration', 'animationDuration')"
1574
- matTooltipPosition="above"
1575
- >
1576
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1577
- </button>
1578
- </mat-form-field>
1579
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.color', 'Cor (M2)') }}</mat-label>
1580
- <select matNativeControl [(ngModel)]="group.color" (ngModelChange)="onAppearanceChange()">
1581
- <option [ngValue]="undefined">{{ t('editor.options.none', '(nenhuma)') }}</option>
1582
- <option value="primary">{{ t('editor.options.primary', 'Primary') }}</option>
1583
- <option value="accent">{{ t('editor.options.accent', 'Accent') }}</option>
1584
- <option value="warn">{{ t('editor.options.warn', 'Warn') }}</option>
1585
- </select>
1586
- <button
1587
- mat-icon-button
1588
- matSuffix
1589
- class="help-icon-button"
1590
- type="button"
1591
- [matTooltip]="t('editor.hints.preferTokens', 'Preferir tokens em Estilo.')"
1592
- matTooltipPosition="above"
1593
- >
1594
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1595
- </button>
1596
- </mat-form-field>
1597
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.backgroundColor', 'Cor de fundo (M2)') }}</mat-label>
1598
- <select matNativeControl [(ngModel)]="group.backgroundColor" (ngModelChange)="onAppearanceChange()">
1599
- <option [ngValue]="undefined">{{ t('editor.options.none', '(nenhuma)') }}</option>
1600
- <option value="primary">{{ t('editor.options.primary', 'Primary') }}</option>
1601
- <option value="accent">{{ t('editor.options.accent', 'Accent') }}</option>
1602
- <option value="warn">{{ t('editor.options.warn', 'Warn') }}</option>
1603
- </select>
1604
- <button
1605
- mat-icon-button
1606
- matSuffix
1607
- class="help-icon-button"
1608
- type="button"
1609
- [matTooltip]="t('editor.hints.preferTokens', 'Preferir tokens em Estilo.')"
1610
- matTooltipPosition="above"
1611
- >
1612
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1613
- </button>
1614
- </mat-form-field>
1615
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.ariaLabel', 'aria-label') }}</mat-label>
1616
- <input matInput [(ngModel)]="group.ariaLabel" (ngModelChange)="onAppearanceChange()" />
1617
- </mat-form-field>
1618
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.ariaLabelledby', 'aria-labelledby') }}</mat-label>
1619
- <input matInput [(ngModel)]="group.ariaLabelledby" (ngModelChange)="onAppearanceChange()" />
1620
- </mat-form-field>
1621
- </div>
1622
- <div class="editor-row">
1623
- <mat-slide-toggle [(ngModel)]="group.dynamicHeight" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.dynamicHeight', 'Altura dinamica') }}</mat-slide-toggle>
1624
- <mat-slide-toggle [(ngModel)]="group.fitInkBarToContent" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.fitInkBarToContent', 'Indicador ajustado ao conteudo') }}</mat-slide-toggle>
1625
- <mat-slide-toggle [(ngModel)]="group.disablePagination" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disablePagination', 'Sem paginacao') }}</mat-slide-toggle>
1626
- <mat-slide-toggle [(ngModel)]="group.disableRipple" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disableRipple', 'Sem ripple') }}</mat-slide-toggle>
1627
- <mat-slide-toggle [(ngModel)]="group.preserveContent" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.preserveContent', 'Preservar conteudo') }}</mat-slide-toggle>
1628
- <mat-slide-toggle [(ngModel)]="group.stretchTabs" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.stretchTabs', 'Esticar abas') }}</mat-slide-toggle>
1629
- </div>
1578
+ <select matNativeControl [(ngModel)]="group.headerPosition" (ngModelChange)="onAppearanceChange()">
1579
+ <option [ngValue]="undefined">{{ t('editor.options.aboveDefault', 'Acima (padrao)') }}</option>
1580
+ <option value="above">{{ t('editor.options.above', 'Acima') }}</option>
1581
+ <option value="below">{{ t('editor.options.below', 'Abaixo') }}</option>
1582
+ </select>
1583
+ </mat-form-field>
1584
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.selectedIndex', 'Indice selecionado') }}</mat-label>
1585
+ <input matInput type="number" [(ngModel)]="group.selectedIndex" (ngModelChange)="onAppearanceChange()" />
1586
+ <button
1587
+ mat-icon-button
1588
+ matSuffix
1589
+ class="help-icon-button"
1590
+ type="button"
1591
+ [matTooltip]="t('editor.hints.selectedIndex', 'selectedIndex')"
1592
+ matTooltipPosition="above"
1593
+ >
1594
+ <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1595
+ </button>
1596
+ </mat-form-field>
1597
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.animationDuration', 'Duracao da animacao') }}</mat-label>
1598
+ <input matInput [(ngModel)]="group.animationDuration" (ngModelChange)="onAppearanceChange()" [placeholder]="t('editor.fields.placeholder.animationDuration', '500ms')" />
1599
+ <button
1600
+ mat-icon-button
1601
+ matSuffix
1602
+ class="help-icon-button"
1603
+ type="button"
1604
+ [matTooltip]="t('editor.hints.animationDuration', 'animationDuration')"
1605
+ matTooltipPosition="above"
1606
+ >
1607
+ <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1608
+ </button>
1609
+ </mat-form-field>
1610
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.color', 'Cor (M2)') }}</mat-label>
1611
+ <select matNativeControl [(ngModel)]="group.color" (ngModelChange)="onAppearanceChange()">
1612
+ <option [ngValue]="undefined">{{ t('editor.options.none', '(nenhuma)') }}</option>
1613
+ <option value="primary">{{ t('editor.options.primary', 'Primary') }}</option>
1614
+ <option value="accent">{{ t('editor.options.accent', 'Accent') }}</option>
1615
+ <option value="warn">{{ t('editor.options.warn', 'Warn') }}</option>
1616
+ </select>
1617
+ <button
1618
+ mat-icon-button
1619
+ matSuffix
1620
+ class="help-icon-button"
1621
+ type="button"
1622
+ [matTooltip]="t('editor.hints.preferTokens', 'Preferir tokens em Estilo.')"
1623
+ matTooltipPosition="above"
1624
+ >
1625
+ <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1626
+ </button>
1627
+ </mat-form-field>
1628
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.backgroundColor', 'Cor de fundo (M2)') }}</mat-label>
1629
+ <select matNativeControl [(ngModel)]="group.backgroundColor" (ngModelChange)="onAppearanceChange()">
1630
+ <option [ngValue]="undefined">{{ t('editor.options.none', '(nenhuma)') }}</option>
1631
+ <option value="primary">{{ t('editor.options.primary', 'Primary') }}</option>
1632
+ <option value="accent">{{ t('editor.options.accent', 'Accent') }}</option>
1633
+ <option value="warn">{{ t('editor.options.warn', 'Warn') }}</option>
1634
+ </select>
1635
+ <button
1636
+ mat-icon-button
1637
+ matSuffix
1638
+ class="help-icon-button"
1639
+ type="button"
1640
+ [matTooltip]="t('editor.hints.preferTokens', 'Preferir tokens em Estilo.')"
1641
+ matTooltipPosition="above"
1642
+ >
1643
+ <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1644
+ </button>
1645
+ </mat-form-field>
1646
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.ariaLabel', 'aria-label') }}</mat-label>
1647
+ <input matInput [(ngModel)]="group.ariaLabel" (ngModelChange)="onAppearanceChange()" />
1648
+ </mat-form-field>
1649
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.ariaLabelledby', 'aria-labelledby') }}</mat-label>
1650
+ <input matInput [(ngModel)]="group.ariaLabelledby" (ngModelChange)="onAppearanceChange()" />
1651
+ </mat-form-field>
1652
+ </div>
1653
+ <div class="editor-row">
1654
+ <mat-slide-toggle [(ngModel)]="group.dynamicHeight" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.dynamicHeight', 'Altura dinamica') }}</mat-slide-toggle>
1655
+ <mat-slide-toggle [(ngModel)]="group.fitInkBarToContent" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.fitInkBarToContent', 'Indicador ajustado ao conteudo') }}</mat-slide-toggle>
1656
+ <mat-slide-toggle [(ngModel)]="group.disablePagination" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disablePagination', 'Sem paginacao') }}</mat-slide-toggle>
1657
+ <mat-slide-toggle [(ngModel)]="group.disableRipple" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disableRipple', 'Sem ripple') }}</mat-slide-toggle>
1658
+ <mat-slide-toggle [(ngModel)]="group.preserveContent" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.preserveContent', 'Preservar conteudo') }}</mat-slide-toggle>
1659
+ <mat-slide-toggle [(ngModel)]="group.stretchTabs" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.stretchTabs', 'Esticar abas') }}</mat-slide-toggle>
1660
+ </div>
1661
+ </div>
1662
+ </mat-tab>
1663
+
1664
+ <mat-tab [label]="t('editor.tabs.nav', 'Navegacao')" [disabled]="primaryMode !== 'nav'">
1665
+ <div class="editor-section">
1666
+ <div class="editor-grid two">
1667
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.selectedIndex', 'Indice selecionado') }}</mat-label>
1668
+ <input matInput type="number" [(ngModel)]="nav.selectedIndex" (ngModelChange)="onAppearanceChange()" />
1669
+ <button
1670
+ mat-icon-button
1671
+ matSuffix
1672
+ class="help-icon-button"
1673
+ type="button"
1674
+ [matTooltip]="t('editor.hints.selectedIndex', 'selectedIndex')"
1675
+ matTooltipPosition="above"
1676
+ >
1677
+ <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1678
+ </button>
1679
+ </mat-form-field>
1680
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.animationDuration', 'Duracao da animacao') }}</mat-label>
1681
+ <input matInput [(ngModel)]="nav.animationDuration" (ngModelChange)="onAppearanceChange()" [placeholder]="t('editor.fields.placeholder.animationDuration', '500ms')" />
1682
+ <button
1683
+ mat-icon-button
1684
+ matSuffix
1685
+ class="help-icon-button"
1686
+ type="button"
1687
+ [matTooltip]="t('editor.hints.animationDuration', 'animationDuration')"
1688
+ matTooltipPosition="above"
1689
+ >
1690
+ <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1691
+ </button>
1692
+ </mat-form-field>
1693
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.ariaLabel', 'aria-label') }}</mat-label>
1694
+ <input matInput [(ngModel)]="nav.ariaLabel" (ngModelChange)="onAppearanceChange()" />
1695
+ </mat-form-field>
1696
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.ariaLabelledby', 'aria-labelledby') }}</mat-label>
1697
+ <input matInput [(ngModel)]="nav.ariaLabelledby" (ngModelChange)="onAppearanceChange()" />
1698
+ </mat-form-field>
1699
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.color', 'Cor (M2)') }}</mat-label>
1700
+ <select matNativeControl [(ngModel)]="nav.color" (ngModelChange)="onAppearanceChange()">
1701
+ <option [ngValue]="undefined">{{ t('editor.options.none', '(nenhuma)') }}</option>
1702
+ <option value="primary">{{ t('editor.options.primary', 'Primary') }}</option>
1703
+ <option value="accent">{{ t('editor.options.accent', 'Accent') }}</option>
1704
+ <option value="warn">{{ t('editor.options.warn', 'Warn') }}</option>
1705
+ </select>
1706
+ <button
1707
+ mat-icon-button
1708
+ matSuffix
1709
+ class="help-icon-button"
1710
+ type="button"
1711
+ [matTooltip]="t('editor.hints.preferTokens', 'Preferir tokens em Estilo.')"
1712
+ matTooltipPosition="above"
1713
+ >
1714
+ <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1715
+ </button>
1716
+ </mat-form-field>
1717
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.backgroundColor', 'Cor de fundo (M2)') }}</mat-label>
1718
+ <select matNativeControl [(ngModel)]="nav.backgroundColor" (ngModelChange)="onAppearanceChange()">
1719
+ <option [ngValue]="undefined">{{ t('editor.options.none', '(nenhuma)') }}</option>
1720
+ <option value="primary">{{ t('editor.options.primary', 'Primary') }}</option>
1721
+ <option value="accent">{{ t('editor.options.accent', 'Accent') }}</option>
1722
+ <option value="warn">{{ t('editor.options.warn', 'Warn') }}</option>
1723
+ </select>
1724
+ <button
1725
+ mat-icon-button
1726
+ matSuffix
1727
+ class="help-icon-button"
1728
+ type="button"
1729
+ [matTooltip]="t('editor.hints.preferTokens', 'Preferir tokens em Estilo.')"
1730
+ matTooltipPosition="above"
1731
+ >
1732
+ <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1733
+ </button>
1734
+ </mat-form-field>
1735
+ </div>
1736
+ <div class="editor-row">
1737
+ <mat-slide-toggle [(ngModel)]="nav.fitInkBarToContent" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.fitInkBarToContent', 'Indicador ajustado ao conteudo') }}</mat-slide-toggle>
1738
+ <mat-slide-toggle [(ngModel)]="nav.disablePagination" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disablePagination', 'Sem paginacao') }}</mat-slide-toggle>
1739
+ <mat-slide-toggle [(ngModel)]="nav.disableRipple" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disableRipple', 'Sem ripple') }}</mat-slide-toggle>
1740
+ <mat-slide-toggle [(ngModel)]="nav.stretchTabs" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.stretchTabs', 'Esticar abas') }}</mat-slide-toggle>
1741
+ </div>
1742
+ </div>
1743
+ </mat-tab>
1744
+ <mat-tab [label]="t('editor.tabs.json', 'JSON')">
1745
+ <div class="editor-section">
1746
+ <div class="editor-toolbar">
1747
+ <button mat-button (click)="formatJson()" [disabled]="!isValid">
1748
+ <mat-icon [praxisIcon]="'format_align_left'"></mat-icon>{{ t('editor.actions.format', 'Formatar') }}
1749
+ </button>
1750
+ <button mat-button (click)="reset()">
1751
+ <mat-icon [praxisIcon]="'restart_alt'"></mat-icon>{{ t('editor.actions.reset', 'Resetar') }}
1752
+ </button>
1630
1753
  </div>
1631
- </mat-tab>
1632
-
1633
- <mat-tab [label]="t('editor.tabs.nav', 'Navegacao')" [disabled]="primaryMode !== 'nav'">
1634
- <div class="editor-section">
1635
- <div class="editor-grid two">
1636
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.selectedIndex', 'Indice selecionado') }}</mat-label>
1637
- <input matInput type="number" [(ngModel)]="nav.selectedIndex" (ngModelChange)="onAppearanceChange()" />
1638
- <button
1639
- mat-icon-button
1640
- matSuffix
1641
- class="help-icon-button"
1642
- type="button"
1643
- [matTooltip]="t('editor.hints.selectedIndex', 'selectedIndex')"
1644
- matTooltipPosition="above"
1645
- >
1646
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1647
- </button>
1648
- </mat-form-field>
1649
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.animationDuration', 'Duracao da animacao') }}</mat-label>
1650
- <input matInput [(ngModel)]="nav.animationDuration" (ngModelChange)="onAppearanceChange()" [placeholder]="t('editor.fields.placeholder.animationDuration', '500ms')" />
1651
- <button
1652
- mat-icon-button
1653
- matSuffix
1654
- class="help-icon-button"
1655
- type="button"
1656
- [matTooltip]="t('editor.hints.animationDuration', 'animationDuration')"
1657
- matTooltipPosition="above"
1658
- >
1659
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1660
- </button>
1661
- </mat-form-field>
1662
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.ariaLabel', 'aria-label') }}</mat-label>
1663
- <input matInput [(ngModel)]="nav.ariaLabel" (ngModelChange)="onAppearanceChange()" />
1664
- </mat-form-field>
1665
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.ariaLabelledby', 'aria-labelledby') }}</mat-label>
1666
- <input matInput [(ngModel)]="nav.ariaLabelledby" (ngModelChange)="onAppearanceChange()" />
1667
- </mat-form-field>
1668
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.color', 'Cor (M2)') }}</mat-label>
1669
- <select matNativeControl [(ngModel)]="nav.color" (ngModelChange)="onAppearanceChange()">
1670
- <option [ngValue]="undefined">{{ t('editor.options.none', '(nenhuma)') }}</option>
1671
- <option value="primary">{{ t('editor.options.primary', 'Primary') }}</option>
1672
- <option value="accent">{{ t('editor.options.accent', 'Accent') }}</option>
1673
- <option value="warn">{{ t('editor.options.warn', 'Warn') }}</option>
1674
- </select>
1675
- <button
1676
- mat-icon-button
1677
- matSuffix
1678
- class="help-icon-button"
1679
- type="button"
1680
- [matTooltip]="t('editor.hints.preferTokens', 'Preferir tokens em Estilo.')"
1681
- matTooltipPosition="above"
1682
- >
1683
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1684
- </button>
1685
- </mat-form-field>
1686
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.backgroundColor', 'Cor de fundo (M2)') }}</mat-label>
1687
- <select matNativeControl [(ngModel)]="nav.backgroundColor" (ngModelChange)="onAppearanceChange()">
1688
- <option [ngValue]="undefined">{{ t('editor.options.none', '(nenhuma)') }}</option>
1689
- <option value="primary">{{ t('editor.options.primary', 'Primary') }}</option>
1690
- <option value="accent">{{ t('editor.options.accent', 'Accent') }}</option>
1691
- <option value="warn">{{ t('editor.options.warn', 'Warn') }}</option>
1692
- </select>
1693
- <button
1694
- mat-icon-button
1695
- matSuffix
1696
- class="help-icon-button"
1697
- type="button"
1698
- [matTooltip]="t('editor.hints.preferTokens', 'Preferir tokens em Estilo.')"
1699
- matTooltipPosition="above"
1754
+
1755
+ <mat-form-field appearance="outline" class="json-textarea-field full">
1756
+ <mat-label>{{ t('editor.fields.jsonConfig', 'Configuracao JSON') }}</mat-label>
1757
+ <textarea
1758
+ matInput
1759
+ [(ngModel)]="jsonText"
1760
+ (ngModelChange)="onJsonTextChange($event)"
1761
+ rows="22"
1762
+ spellcheck="false"
1763
+ class="editor-json"
1764
+ ></textarea>
1765
+ @if (isValid) {
1766
+ <mat-hint>{{ t('editor.json.valid', 'JSON valido') }}</mat-hint>
1767
+ }
1768
+ @if (!isValid && jsonText) {
1769
+ <mat-error>{{ t('editor.json.invalidPrefix', 'JSON invalido:') }} {{ errorMsg }}</mat-error>
1770
+ }
1771
+ </mat-form-field>
1772
+ </div>
1773
+ </mat-tab>
1774
+
1775
+ <mat-tab [label]="t('editor.tabs.style', 'Estilo')">
1776
+ <div class="editor-section editor-section-lg">
1777
+ <div class="editor-row">
1778
+ <span class="editor-muted">{{ t('editor.presets.title', 'Presets:') }}</span>
1779
+ <button mat-button color="primary" (click)="applyPreset('primary')">
1780
+ <mat-icon [praxisIcon]="'palette'"></mat-icon>
1781
+ {{ t('editor.presets.primary', 'Primario') }}
1782
+ </button>
1783
+ <button mat-button (click)="applyPreset('neutral')">
1784
+ <mat-icon [praxisIcon]="'contrast'"></mat-icon>
1785
+ {{ t('editor.presets.neutral', 'Neutro') }}
1786
+ </button>
1787
+ <button mat-button (click)="applyPreset('high-contrast')">
1788
+ <mat-icon [praxisIcon]="'visibility'"></mat-icon>
1789
+ {{ t('editor.presets.highContrast', 'Alto contraste') }}
1790
+ </button>
1791
+ <button mat-button (click)="clearTokens()">
1792
+ <mat-icon [praxisIcon]="'backspace'"></mat-icon>
1793
+ {{ t('editor.actions.clearTokens', 'Limpar tokens') }}
1794
+ </button>
1795
+ </div>
1796
+
1797
+ <div class="editor-grid two tight">
1798
+ <mat-form-field appearance="outline">
1799
+ <mat-label>{{ t('editor.fields.themeClass', 'Classe de tema (opcional)') }}</mat-label>
1800
+ <input matInput [(ngModel)]="appearance.themeClass" (ngModelChange)="onAppearanceChange()" [placeholder]="t('editor.fields.placeholder.themeClass', 'ex.: tabs-accented')" />
1801
+ </mat-form-field>
1802
+
1803
+ <mat-form-field appearance="outline">
1804
+ <mat-label>{{ t('editor.fields.density', 'Densidade') }}</mat-label>
1805
+ <select matNativeControl [(ngModel)]="appearance.density" (ngModelChange)="onAppearanceChange()">
1806
+ <option [ngValue]="undefined">{{ t('editor.density.default', 'Padrao') }}</option>
1807
+ <option value="compact">{{ t('editor.density.compact', 'Compacta') }}</option>
1808
+ <option value="comfortable">{{ t('editor.density.comfortable', 'Confortavel') }}</option>
1809
+ <option value="spacious">{{ t('editor.density.spacious', 'Espacosa') }}</option>
1810
+ </select>
1811
+ </mat-form-field>
1812
+ </div>
1813
+
1814
+ <div>
1815
+ <div class="editor-title-row">
1816
+ <h3 class="editor-title">{{ t('editor.sections.tokens', 'Tokens (Material 3)') }}</h3>
1817
+ <button
1818
+ mat-icon-button
1819
+ class="help-icon-button"
1820
+ type="button"
1821
+ [matTooltip]="t('editor.hints.tokenValueHelp', 'Valores aceitam CSS vars ou cores hex/rgb.')"
1822
+ matTooltipPosition="above"
1700
1823
  >
1701
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1702
- </button>
1703
- </mat-form-field>
1824
+ <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1825
+ </button>
1704
1826
  </div>
1705
- <div class="editor-row">
1706
- <mat-slide-toggle [(ngModel)]="nav.fitInkBarToContent" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.fitInkBarToContent', 'Indicador ajustado ao conteudo') }}</mat-slide-toggle>
1707
- <mat-slide-toggle [(ngModel)]="nav.disablePagination" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disablePagination', 'Sem paginacao') }}</mat-slide-toggle>
1708
- <mat-slide-toggle [(ngModel)]="nav.disableRipple" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disableRipple', 'Sem ripple') }}</mat-slide-toggle>
1709
- <mat-slide-toggle [(ngModel)]="nav.stretchTabs" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.stretchTabs', 'Esticar abas') }}</mat-slide-toggle>
1827
+ <div class="editor-grid two">
1828
+ @for (token of tokenList; track token) {
1829
+ <mat-form-field appearance="outline">
1830
+ <mat-label>{{ t(token.labelKey, token.fallback) }}</mat-label>
1831
+ <input matInput [placeholder]="t('editor.placeholders.tokenValue', 'var(--md-sys-color-primary) / #RRGGBB')" [ngModel]="appearance.tokens?.[token.key]" (ngModelChange)="onTokenChange(token.key, $event)" />
1832
+ </mat-form-field>
1833
+ }
1710
1834
  </div>
1711
1835
  </div>
1712
- </mat-tab>
1713
- <mat-tab [label]="t('editor.tabs.json', 'JSON')">
1714
- <div class="editor-section">
1715
- <div class="editor-toolbar">
1716
- <button mat-button (click)="formatJson()" [disabled]="!isValid">
1717
- <mat-icon [praxisIcon]="'format_align_left'"></mat-icon>{{ t('editor.actions.format', 'Formatar') }}
1718
- </button>
1719
- <button mat-button (click)="reset()">
1720
- <mat-icon [praxisIcon]="'restart_alt'"></mat-icon>{{ t('editor.actions.reset', 'Resetar') }}
1721
- </button>
1722
- </div>
1723
-
1836
+
1837
+ <div>
1838
+ <h3 class="editor-title">{{ t('editor.sections.customCss', 'CSS personalizado') }}</h3>
1724
1839
  <mat-form-field appearance="outline" class="json-textarea-field full">
1725
- <mat-label>{{ t('editor.fields.jsonConfig', 'Configuracao JSON') }}</mat-label>
1726
- <textarea
1727
- matInput
1728
- [(ngModel)]="jsonText"
1729
- (ngModelChange)="onJsonTextChange($event)"
1730
- rows="22"
1731
- spellcheck="false"
1732
- class="editor-json"
1733
- ></textarea>
1734
- <mat-hint *ngIf="isValid">{{ t('editor.json.valid', 'JSON valido') }}</mat-hint>
1735
- <mat-error *ngIf="!isValid && jsonText">{{ t('editor.json.invalidPrefix', 'JSON invalido:') }} {{ errorMsg }}</mat-error>
1840
+ <mat-label>{{ t('editor.fields.customCss', 'CSS a ser injetado no componente') }}</mat-label>
1841
+ <textarea matInput rows="10" [(ngModel)]="appearance.customCss" (ngModelChange)="onAppearanceChange()" [placeholder]="t('editor.placeholders.customCssExample', '.praxis-tabs-root .mdc-tab__text-label { font-weight: 600; }')"></textarea>
1736
1842
  </mat-form-field>
1737
1843
  </div>
1738
- </mat-tab>
1739
-
1740
- <mat-tab [label]="t('editor.tabs.style', 'Estilo')">
1741
- <div class="editor-section editor-section-lg">
1742
- <div class="editor-row">
1743
- <span class="editor-muted">{{ t('editor.presets.title', 'Presets:') }}</span>
1744
- <button mat-button color="primary" (click)="applyPreset('primary')">
1745
- <mat-icon [praxisIcon]="'palette'"></mat-icon>
1746
- {{ t('editor.presets.primary', 'Primario') }}
1747
- </button>
1748
- <button mat-button (click)="applyPreset('neutral')">
1749
- <mat-icon [praxisIcon]="'contrast'"></mat-icon>
1750
- {{ t('editor.presets.neutral', 'Neutro') }}
1751
- </button>
1752
- <button mat-button (click)="applyPreset('high-contrast')">
1753
- <mat-icon [praxisIcon]="'visibility'"></mat-icon>
1754
- {{ t('editor.presets.highContrast', 'Alto contraste') }}
1755
- </button>
1756
- <button mat-button (click)="clearTokens()">
1757
- <mat-icon [praxisIcon]="'backspace'"></mat-icon>
1758
- {{ t('editor.actions.clearTokens', 'Limpar tokens') }}
1759
- </button>
1760
- </div>
1761
-
1762
- <div class="editor-grid two tight">
1763
- <mat-form-field appearance="outline">
1764
- <mat-label>{{ t('editor.fields.themeClass', 'Classe de tema (opcional)') }}</mat-label>
1765
- <input matInput [(ngModel)]="appearance.themeClass" (ngModelChange)="onAppearanceChange()" [placeholder]="t('editor.fields.placeholder.themeClass', 'ex.: tabs-accented')" />
1766
- </mat-form-field>
1767
-
1768
- <mat-form-field appearance="outline">
1769
- <mat-label>{{ t('editor.fields.density', 'Densidade') }}</mat-label>
1770
- <select matNativeControl [(ngModel)]="appearance.density" (ngModelChange)="onAppearanceChange()">
1771
- <option [ngValue]="undefined">{{ t('editor.density.default', 'Padrao') }}</option>
1772
- <option value="compact">{{ t('editor.density.compact', 'Compacta') }}</option>
1773
- <option value="comfortable">{{ t('editor.density.comfortable', 'Confortavel') }}</option>
1774
- <option value="spacious">{{ t('editor.density.spacious', 'Espacosa') }}</option>
1775
- </select>
1776
- </mat-form-field>
1777
- </div>
1778
-
1779
- <div>
1780
- <div class="editor-title-row">
1781
- <h3 class="editor-title">{{ t('editor.sections.tokens', 'Tokens (Material 3)') }}</h3>
1782
- <button
1783
- mat-icon-button
1784
- class="help-icon-button"
1785
- type="button"
1786
- [matTooltip]="t('editor.hints.tokenValueHelp', 'Valores aceitam CSS vars ou cores hex/rgb.')"
1787
- matTooltipPosition="above"
1788
- >
1789
- <mat-icon [praxisIcon]="'help_outline'"></mat-icon>
1790
- </button>
1791
- </div>
1792
- <div class="editor-grid two">
1793
- <ng-container *ngFor="let token of tokenList">
1794
- <mat-form-field appearance="outline">
1795
- <mat-label>{{ t(token.labelKey, token.fallback) }}</mat-label>
1796
- <input matInput [placeholder]="t('editor.placeholders.tokenValue', 'var(--md-sys-color-primary) / #RRGGBB')" [ngModel]="appearance.tokens?.[token.key]" (ngModelChange)="onTokenChange(token.key, $event)" />
1844
+
1845
+ <div>
1846
+ <h3 class="editor-title">{{ t('editor.sections.scssSnippet', 'Snippet SCSS (para uso em styles.scss)') }}</h3>
1847
+ <pre class="editor-code">
1848
+ @use '@angular/material' as mat;
1849
+ {{ scssSnippet() }}
1850
+ </pre>
1851
+ </div>
1852
+ </div>
1853
+ </mat-tab>
1854
+
1855
+ <mat-tab [label]="t('editor.tabs.items', 'Abas')" [disabled]="primaryMode !== 'group'">
1856
+ <div class="editor-section">
1857
+ <button mat-stroked-button color="primary" (click)="addTab()"><mat-icon [praxisIcon]="'add'"></mat-icon>{{ t('editor.actions.addTab', 'Adicionar aba') }}</button>
1858
+ @if (editedConfig.tabs?.length) {
1859
+ <div class="editor-grid">
1860
+ @for (tab of editedConfig.tabs; track tab; let i = $index) {
1861
+ <div class="editor-card">
1862
+ <div class="editor-card-header">
1863
+ <strong class="editor-card-title">{{ t('editor.cards.tab', 'Aba') }} #{{ i+1 }}</strong>
1864
+ <button mat-icon-button (click)="moveTab(i, -1)" [disabled]="i===0"><mat-icon [praxisIcon]="'arrow_upward'"></mat-icon></button>
1865
+ <button mat-icon-button (click)="moveTab(i, 1)" [disabled]="i===editedConfig.tabs!.length-1"><mat-icon [praxisIcon]="'arrow_downward'"></mat-icon></button>
1866
+ <button mat-icon-button color="warn" (click)="removeTab(i)"><mat-icon [praxisIcon]="'delete'"></mat-icon></button>
1867
+ </div>
1868
+ <div class="editor-grid two tight">
1869
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.id', 'ID') }}</mat-label>
1870
+ <input matInput [(ngModel)]="tab.id" (ngModelChange)="onAppearanceChange()" />
1797
1871
  </mat-form-field>
1798
- </ng-container>
1799
- </div>
1800
- </div>
1801
-
1802
- <div>
1803
- <h3 class="editor-title">{{ t('editor.sections.customCss', 'CSS personalizado') }}</h3>
1804
- <mat-form-field appearance="outline" class="json-textarea-field full">
1805
- <mat-label>{{ t('editor.fields.customCss', 'CSS a ser injetado no componente') }}</mat-label>
1806
- <textarea matInput rows="10" [(ngModel)]="appearance.customCss" (ngModelChange)="onAppearanceChange()" [placeholder]="t('editor.placeholders.customCssExample', '.praxis-tabs-root .mdc-tab__text-label { font-weight: 600; }')"></textarea>
1872
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.label', 'Rotulo') }}</mat-label>
1873
+ <input matInput [(ngModel)]="tab.textLabel" (ngModelChange)="onAppearanceChange()" />
1874
+ </mat-form-field>
1875
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.icon', 'Icone') }}</mat-label>
1876
+ <input matInput [(ngModel)]="tab.icon" (ngModelChange)="onAppearanceChange()" />
1807
1877
  </mat-form-field>
1808
- </div>
1809
-
1810
- <div>
1811
- <h3 class="editor-title">{{ t('editor.sections.scssSnippet', 'Snippet SCSS (para uso em styles.scss)') }}</h3>
1812
- <pre class="editor-code">
1813
- @use '@angular/material' as mat;
1814
- {{ scssSnippet() }}
1815
- </pre>
1816
- </div>
1817
- </div>
1818
- </mat-tab>
1819
-
1820
- <mat-tab [label]="t('editor.tabs.items', 'Abas')" [disabled]="primaryMode !== 'group'">
1821
- <div class="editor-section">
1822
- <button mat-stroked-button color="primary" (click)="addTab()"><mat-icon [praxisIcon]="'add'"></mat-icon>{{ t('editor.actions.addTab', 'Adicionar aba') }}</button>
1823
- <div *ngIf="editedConfig.tabs?.length; else noTabs" class="editor-grid">
1824
- <div *ngFor="let tab of editedConfig.tabs; let i = index" class="editor-card">
1878
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.labelClass', 'Classe do rotulo') }}</mat-label>
1879
+ <input matInput [(ngModel)]="tab.labelClass" (ngModelChange)="onAppearanceChange()" />
1880
+ </mat-form-field>
1881
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.bodyClass', 'Classe do conteudo') }}</mat-label>
1882
+ <input matInput [(ngModel)]="tab.bodyClass" (ngModelChange)="onAppearanceChange()" />
1883
+ </mat-form-field>
1884
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.ariaLabel', 'aria-label') }}</mat-label>
1885
+ <input matInput [(ngModel)]="tab.ariaLabel" (ngModelChange)="onAppearanceChange()" />
1886
+ </mat-form-field>
1887
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.ariaLabelledby', 'aria-labelledby') }}</mat-label>
1888
+ <input matInput [(ngModel)]="tab.ariaLabelledby" (ngModelChange)="onAppearanceChange()" />
1889
+ </mat-form-field>
1890
+ </div>
1891
+ <div class="editor-row">
1892
+ <mat-slide-toggle [(ngModel)]="tab.disabled" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disabled', 'Desativada') }}</mat-slide-toggle>
1893
+ <mat-slide-toggle [ngModel]="tab.visible !== false" (ngModelChange)="tab.visible = $event; onAppearanceChange()">{{ t('editor.toggles.visible', 'Visivel') }}</mat-slide-toggle>
1894
+ </div>
1895
+ <!-- Widgets (componentes dinâmicos) -->
1896
+ <div class="editor-divider editor-grid">
1897
+ <div class="editor-row">
1898
+ <mat-form-field appearance="outline" class="editor-field-min">
1899
+ <mat-label>{{ t('editor.fields.addComponent', 'Adicionar componente') }}</mat-label>
1900
+ <select matNativeControl [(ngModel)]="selectedTabWidgetId[i]">
1901
+ <option [ngValue]="''">{{ t('editor.fields.selectPlaceholder', '(selecione)') }}</option>
1902
+ @for (c of componentOptions; track c) {
1903
+ <option [ngValue]="c.id">{{ c.friendlyName || c.id }}</option>
1904
+ }
1905
+ </select>
1906
+ </mat-form-field>
1907
+ <button mat-stroked-button (click)="addWidgetToTab(i)"><mat-icon [praxisIcon]="'add'"></mat-icon>{{ t('editor.actions.add', 'Adicionar') }}</button>
1908
+ <span class="editor-spacer"></span>
1909
+ <mat-form-field appearance="outline" class="editor-field-240">
1910
+ <mat-label>{{ t('editor.fields.resourcePath', 'Recurso (resourcePath)') }}</mat-label>
1911
+ <input matInput [(ngModel)]="quickResourcePathTab[i]" [placeholder]="t('editor.fields.placeholder.resourcePath', 'ex.: usuarios')" />
1912
+ </mat-form-field>
1913
+ <button mat-button (click)="addPresetToTab(i, 'form')"><mat-icon [praxisIcon]="'description'"></mat-icon>{{ t('editor.templates.form', 'Form') }}</button>
1914
+ <button mat-button (click)="addPresetToTab(i, 'table')"><mat-icon [praxisIcon]="'grid_on'"></mat-icon>{{ t('editor.templates.table', 'Tabela') }}</button>
1915
+ <button mat-button (click)="addPresetToTab(i, 'crud')"><mat-icon [praxisIcon]="'dynamic_form'"></mat-icon>{{ t('editor.templates.crud', 'CRUD') }}</button>
1916
+ </div>
1917
+ @if (tab.widgets?.length) {
1918
+ <div class="editor-grid" cdkDropList [cdkDropListData]="tab.widgets || []" (cdkDropListDropped)="onTabWidgetDrop(i, $event)">
1919
+ @for (w of tab.widgets; track w; let wi = $index) {
1920
+ <div cdkDrag class="editor-card dashed">
1825
1921
  <div class="editor-card-header">
1826
- <strong class="editor-card-title">{{ t('editor.cards.tab', 'Aba') }} #{{ i+1 }}</strong>
1827
- <button mat-icon-button (click)="moveTab(i, -1)" [disabled]="i===0"><mat-icon [praxisIcon]="'arrow_upward'"></mat-icon></button>
1828
- <button mat-icon-button (click)="moveTab(i, 1)" [disabled]="i===editedConfig.tabs!.length-1"><mat-icon [praxisIcon]="'arrow_downward'"></mat-icon></button>
1829
- <button mat-icon-button color="warn" (click)="removeTab(i)"><mat-icon [praxisIcon]="'delete'"></mat-icon></button>
1922
+ <button mat-icon-button cdkDragHandle [matTooltip]="t('editor.actions.dragToReorder', 'Arrastar para reordenar')" [attr.aria-label]="t('editor.actions.dragToReorder', 'Arrastar para reordenar')">
1923
+ <mat-icon [praxisIcon]="'drag_indicator'"></mat-icon>
1924
+ </button>
1925
+ <strong class="editor-card-title">{{ getCompName(w.id) }}</strong>
1926
+ <button mat-icon-button color="warn" (click)="removeWidgetFromTab(i, wi)" [matTooltip]="t('editor.actions.removeComponent', 'Remover componente')" [attr.aria-label]="t('editor.actions.removeComponent', 'Remover componente')"><mat-icon [praxisIcon]="'delete'"></mat-icon></button>
1830
1927
  </div>
1831
1928
  <div class="editor-grid two tight">
1832
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.id', 'ID') }}</mat-label>
1833
- <input matInput [(ngModel)]="tab.id" (ngModelChange)="onAppearanceChange()" />
1834
- </mat-form-field>
1835
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.label', 'Rotulo') }}</mat-label>
1836
- <input matInput [(ngModel)]="tab.textLabel" (ngModelChange)="onAppearanceChange()" />
1837
- </mat-form-field>
1838
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.icon', 'Icone') }}</mat-label>
1839
- <input matInput [(ngModel)]="tab.icon" (ngModelChange)="onAppearanceChange()" />
1840
- </mat-form-field>
1841
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.labelClass', 'Classe do rotulo') }}</mat-label>
1842
- <input matInput [(ngModel)]="tab.labelClass" (ngModelChange)="onAppearanceChange()" />
1843
- </mat-form-field>
1844
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.bodyClass', 'Classe do conteudo') }}</mat-label>
1845
- <input matInput [(ngModel)]="tab.bodyClass" (ngModelChange)="onAppearanceChange()" />
1846
- </mat-form-field>
1847
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.ariaLabel', 'aria-label') }}</mat-label>
1848
- <input matInput [(ngModel)]="tab.ariaLabel" (ngModelChange)="onAppearanceChange()" />
1929
+ <mat-form-field appearance="outline">
1930
+ <mat-label>{{ t('editor.fields.inputsJson', 'Inputs (JSON)') }}</mat-label>
1931
+ <textarea matInput rows="4" [ngModel]="stringify(w.inputs)" (ngModelChange)="updateWidgetInputsTab(i, wi, $event)"></textarea>
1849
1932
  </mat-form-field>
1850
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.ariaLabelledby', 'aria-labelledby') }}</mat-label>
1851
- <input matInput [(ngModel)]="tab.ariaLabelledby" (ngModelChange)="onAppearanceChange()" />
1933
+ <mat-form-field appearance="outline">
1934
+ <mat-label>{{ t('editor.fields.outputsJson', 'Outputs (JSON)') }}</mat-label>
1935
+ <textarea matInput rows="4" [ngModel]="stringify(w.outputs)" (ngModelChange)="updateWidgetOutputsTab(i, wi, $event)"></textarea>
1852
1936
  </mat-form-field>
1853
1937
  </div>
1854
- <div class="editor-row">
1855
- <mat-slide-toggle [(ngModel)]="tab.disabled" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disabled', 'Desativada') }}</mat-slide-toggle>
1856
- <mat-slide-toggle [ngModel]="tab.visible !== false" (ngModelChange)="tab.visible = $event; onAppearanceChange()">{{ t('editor.toggles.visible', 'Visivel') }}</mat-slide-toggle>
1857
- </div>
1858
-
1859
- <!-- Widgets (componentes dinâmicos) -->
1860
- <div class="editor-divider editor-grid">
1861
- <div class="editor-row">
1862
- <mat-form-field appearance="outline" class="editor-field-min">
1863
- <mat-label>{{ t('editor.fields.addComponent', 'Adicionar componente') }}</mat-label>
1864
- <select matNativeControl [(ngModel)]="selectedTabWidgetId[i]">
1865
- <option [ngValue]="''">{{ t('editor.fields.selectPlaceholder', '(selecione)') }}</option>
1866
- <option *ngFor="let c of componentOptions" [ngValue]="c.id">{{ c.friendlyName || c.id }}</option>
1867
- </select>
1868
- </mat-form-field>
1869
- <button mat-stroked-button (click)="addWidgetToTab(i)"><mat-icon [praxisIcon]="'add'"></mat-icon>{{ t('editor.actions.add', 'Adicionar') }}</button>
1870
- <span class="editor-spacer"></span>
1871
- <mat-form-field appearance="outline" class="editor-field-240">
1872
- <mat-label>{{ t('editor.fields.resourcePath', 'Recurso (resourcePath)') }}</mat-label>
1873
- <input matInput [(ngModel)]="quickResourcePathTab[i]" [placeholder]="t('editor.fields.placeholder.resourcePath', 'ex.: usuarios')" />
1874
- </mat-form-field>
1875
- <button mat-button (click)="addPresetToTab(i, 'form')"><mat-icon [praxisIcon]="'description'"></mat-icon>{{ t('editor.templates.form', 'Form') }}</button>
1876
- <button mat-button (click)="addPresetToTab(i, 'table')"><mat-icon [praxisIcon]="'grid_on'"></mat-icon>{{ t('editor.templates.table', 'Tabela') }}</button>
1877
- <button mat-button (click)="addPresetToTab(i, 'crud')"><mat-icon [praxisIcon]="'dynamic_form'"></mat-icon>{{ t('editor.templates.crud', 'CRUD') }}</button>
1878
- </div>
1879
-
1880
- <div *ngIf="tab.widgets?.length" class="editor-grid" cdkDropList [cdkDropListData]="tab.widgets || []" (cdkDropListDropped)="onTabWidgetDrop(i, $event)">
1881
- <div *ngFor="let w of tab.widgets; let wi = index" cdkDrag class="editor-card dashed">
1882
- <div class="editor-card-header">
1883
- <button mat-icon-button cdkDragHandle [matTooltip]="t('editor.actions.dragToReorder', 'Arrastar para reordenar')" [attr.aria-label]="t('editor.actions.dragToReorder', 'Arrastar para reordenar')">
1884
- <mat-icon [praxisIcon]="'drag_indicator'"></mat-icon>
1885
- </button>
1886
- <strong class="editor-card-title">{{ getCompName(w.id) }}</strong>
1887
- <button mat-icon-button color="warn" (click)="removeWidgetFromTab(i, wi)" [matTooltip]="t('editor.actions.removeComponent', 'Remover componente')" [attr.aria-label]="t('editor.actions.removeComponent', 'Remover componente')"><mat-icon [praxisIcon]="'delete'"></mat-icon></button>
1888
- </div>
1889
- <div class="editor-grid two tight">
1890
- <mat-form-field appearance="outline">
1891
- <mat-label>{{ t('editor.fields.inputsJson', 'Inputs (JSON)') }}</mat-label>
1892
- <textarea matInput rows="4" [ngModel]="stringify(w.inputs)" (ngModelChange)="updateWidgetInputsTab(i, wi, $event)"></textarea>
1893
- </mat-form-field>
1894
- <mat-form-field appearance="outline">
1895
- <mat-label>{{ t('editor.fields.outputsJson', 'Outputs (JSON)') }}</mat-label>
1896
- <textarea matInput rows="4" [ngModel]="stringify(w.outputs)" (ngModelChange)="updateWidgetOutputsTab(i, wi, $event)"></textarea>
1897
- </mat-form-field>
1898
- </div>
1899
- </div>
1900
- </div>
1901
- </div>
1902
1938
  </div>
1903
- </div>
1904
- <ng-template #noTabs><em class="editor-muted">{{ t('editor.empty.noTabs', 'Nenhuma aba definida.') }}</em></ng-template>
1939
+ }
1905
1940
  </div>
1906
- </mat-tab>
1907
-
1908
- <mat-tab [label]="t('editor.tabs.accessibility', 'Acessibilidade')">
1909
- <div class="editor-section">
1910
- <div class="editor-row">
1911
- <mat-slide-toggle [(ngModel)]="accessibility.highContrast" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.highContrast', 'Alto contraste') }}</mat-slide-toggle>
1912
- <mat-slide-toggle [(ngModel)]="accessibility.reduceMotion" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.reduceMotion', 'Reduzir movimento') }}</mat-slide-toggle>
1913
- </div>
1941
+ }
1942
+ </div>
1943
+ </div>
1944
+ }
1945
+ </div>
1946
+ } @else {
1947
+ <em class="editor-muted">{{ t('editor.empty.noTabs', 'Nenhuma aba definida.') }}</em>
1948
+ }
1949
+ </div>
1950
+ </mat-tab>
1951
+
1952
+ <mat-tab [label]="t('editor.tabs.accessibility', 'Acessibilidade')">
1953
+ <div class="editor-section">
1954
+ <div class="editor-row">
1955
+ <mat-slide-toggle [(ngModel)]="accessibility.highContrast" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.highContrast', 'Alto contraste') }}</mat-slide-toggle>
1956
+ <mat-slide-toggle [(ngModel)]="accessibility.reduceMotion" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.reduceMotion', 'Reduzir movimento') }}</mat-slide-toggle>
1914
1957
  </div>
1915
- </mat-tab>
1916
-
1917
- <mat-tab [label]="t('editor.tabs.links', 'Links')" [disabled]="primaryMode !== 'nav'">
1918
- <div class="editor-section">
1919
- <button mat-stroked-button color="primary" (click)="addLink()"><mat-icon [praxisIcon]="'add_link'"></mat-icon>{{ t('editor.actions.addLink', 'Adicionar link') }}</button>
1920
- <div *ngIf="nav.links?.length; else noLinks" class="editor-grid">
1921
- <div *ngFor="let l of nav.links; let i = index" class="editor-card">
1922
- <div class="editor-card-header">
1923
- <strong class="editor-card-title">{{ t('editor.cards.link', 'Link') }} #{{ i+1 }}</strong>
1924
- <button mat-icon-button (click)="moveLink(i, -1)" [disabled]="i===0"><mat-icon [praxisIcon]="'arrow_upward'"></mat-icon></button>
1925
- <button mat-icon-button (click)="moveLink(i, 1)" [disabled]="i===nav.links!.length-1"><mat-icon [praxisIcon]="'arrow_downward'"></mat-icon></button>
1926
- <button mat-icon-button color="warn" (click)="removeLink(i)"><mat-icon [praxisIcon]="'delete'"></mat-icon></button>
1927
- </div>
1928
- <div class="editor-grid two tight">
1929
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.id', 'ID') }}</mat-label>
1958
+ </div>
1959
+ </mat-tab>
1960
+
1961
+ <mat-tab [label]="t('editor.tabs.links', 'Links')" [disabled]="primaryMode !== 'nav'">
1962
+ <div class="editor-section">
1963
+ <button mat-stroked-button color="primary" (click)="addLink()"><mat-icon [praxisIcon]="'add_link'"></mat-icon>{{ t('editor.actions.addLink', 'Adicionar link') }}</button>
1964
+ @if (nav.links?.length) {
1965
+ <div class="editor-grid">
1966
+ @for (l of nav.links; track l; let i = $index) {
1967
+ <div class="editor-card">
1968
+ <div class="editor-card-header">
1969
+ <strong class="editor-card-title">{{ t('editor.cards.link', 'Link') }} #{{ i+1 }}</strong>
1970
+ <button mat-icon-button (click)="moveLink(i, -1)" [disabled]="i===0"><mat-icon [praxisIcon]="'arrow_upward'"></mat-icon></button>
1971
+ <button mat-icon-button (click)="moveLink(i, 1)" [disabled]="i===nav.links!.length-1"><mat-icon [praxisIcon]="'arrow_downward'"></mat-icon></button>
1972
+ <button mat-icon-button color="warn" (click)="removeLink(i)"><mat-icon [praxisIcon]="'delete'"></mat-icon></button>
1973
+ </div>
1974
+ <div class="editor-grid two tight">
1975
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.id', 'ID') }}</mat-label>
1930
1976
  <input matInput [(ngModel)]="l.id" (ngModelChange)="onAppearanceChange()" />
1931
1977
  </mat-form-field>
1932
1978
  <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.label', 'Rotulo') }}</mat-label>
1933
- <input matInput [(ngModel)]="l.label" (ngModelChange)="onAppearanceChange()" />
1934
- </mat-form-field>
1935
- <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.icon', 'Icone') }}</mat-label>
1936
- <input matInput [(ngModel)]="l.icon" (ngModelChange)="onAppearanceChange()" />
1937
- </mat-form-field>
1938
- </div>
1939
- <div class="editor-row">
1940
- <mat-slide-toggle [(ngModel)]="l.disabled" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.linkDisabled', 'Desativado') }}</mat-slide-toggle>
1941
- <mat-slide-toggle [ngModel]="l.visible !== false" (ngModelChange)="l.visible = $event; onAppearanceChange()">{{ t('editor.toggles.visible', 'Visivel') }}</mat-slide-toggle>
1942
- <mat-slide-toggle [(ngModel)]="l.disableRipple" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disableRipple', 'Sem ripple') }}</mat-slide-toggle>
1943
- <mat-slide-toggle [(ngModel)]="l.fitInkBarToContent" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.fitInkBarToContent', 'Indicador ajustado ao conteudo') }}</mat-slide-toggle>
1944
- </div>
1945
-
1946
- <!-- Widgets para Links -->
1947
- <div class="editor-divider editor-grid">
1948
- <div class="editor-row">
1949
- <mat-form-field appearance="outline" class="editor-field-min">
1950
- <mat-label>{{ t('editor.fields.addComponent', 'Adicionar componente') }}</mat-label>
1951
- <select matNativeControl [(ngModel)]="selectedLinkWidgetId[i]">
1952
- <option [ngValue]="''">{{ t('editor.fields.selectPlaceholder', '(selecione)') }}</option>
1953
- <option *ngFor="let c of componentOptions" [ngValue]="c.id">{{ c.friendlyName || c.id }}</option>
1954
- </select>
1955
- </mat-form-field>
1956
- <button mat-stroked-button (click)="addWidgetToLink(i)"><mat-icon [praxisIcon]="'add'"></mat-icon>{{ t('editor.actions.add', 'Adicionar') }}</button>
1957
- <span class="editor-spacer"></span>
1958
- <mat-form-field appearance="outline" class="editor-field-240">
1959
- <mat-label>{{ t('editor.fields.resourcePath', 'Recurso (resourcePath)') }}</mat-label>
1960
- <input matInput [(ngModel)]="quickResourcePathLink[i]" [placeholder]="t('editor.fields.placeholder.resourcePath', 'ex.: usuarios')" />
1961
- </mat-form-field>
1962
- <button mat-button (click)="addPresetToLink(i, 'form')"><mat-icon [praxisIcon]="'description'"></mat-icon>{{ t('editor.templates.form', 'Form') }}</button>
1963
- <button mat-button (click)="addPresetToLink(i, 'table')"><mat-icon [praxisIcon]="'grid_on'"></mat-icon>{{ t('editor.templates.table', 'Tabela') }}</button>
1964
- <button mat-button (click)="addPresetToLink(i, 'crud')"><mat-icon [praxisIcon]="'dynamic_form'"></mat-icon>{{ t('editor.templates.crud', 'CRUD') }}</button>
1965
- </div>
1966
-
1967
- <div *ngIf="l.widgets?.length" class="editor-grid" cdkDropList [cdkDropListData]="l.widgets || []" (cdkDropListDropped)="onLinkWidgetDrop(i, $event)">
1968
- <div *ngFor="let w of l.widgets; let wi = index" cdkDrag class="editor-card dashed">
1979
+ <input matInput [(ngModel)]="l.label" (ngModelChange)="onAppearanceChange()" />
1980
+ </mat-form-field>
1981
+ <mat-form-field appearance="outline"><mat-label>{{ t('editor.fields.icon', 'Icone') }}</mat-label>
1982
+ <input matInput [(ngModel)]="l.icon" (ngModelChange)="onAppearanceChange()" />
1983
+ </mat-form-field>
1984
+ </div>
1985
+ <div class="editor-row">
1986
+ <mat-slide-toggle [(ngModel)]="l.disabled" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.linkDisabled', 'Desativado') }}</mat-slide-toggle>
1987
+ <mat-slide-toggle [ngModel]="l.visible !== false" (ngModelChange)="l.visible = $event; onAppearanceChange()">{{ t('editor.toggles.visible', 'Visivel') }}</mat-slide-toggle>
1988
+ <mat-slide-toggle [(ngModel)]="l.disableRipple" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.disableRipple', 'Sem ripple') }}</mat-slide-toggle>
1989
+ <mat-slide-toggle [(ngModel)]="l.fitInkBarToContent" (ngModelChange)="onAppearanceChange()">{{ t('editor.toggles.fitInkBarToContent', 'Indicador ajustado ao conteudo') }}</mat-slide-toggle>
1990
+ </div>
1991
+ <!-- Widgets para Links -->
1992
+ <div class="editor-divider editor-grid">
1993
+ <div class="editor-row">
1994
+ <mat-form-field appearance="outline" class="editor-field-min">
1995
+ <mat-label>{{ t('editor.fields.addComponent', 'Adicionar componente') }}</mat-label>
1996
+ <select matNativeControl [(ngModel)]="selectedLinkWidgetId[i]">
1997
+ <option [ngValue]="''">{{ t('editor.fields.selectPlaceholder', '(selecione)') }}</option>
1998
+ @for (c of componentOptions; track c) {
1999
+ <option [ngValue]="c.id">{{ c.friendlyName || c.id }}</option>
2000
+ }
2001
+ </select>
2002
+ </mat-form-field>
2003
+ <button mat-stroked-button (click)="addWidgetToLink(i)"><mat-icon [praxisIcon]="'add'"></mat-icon>{{ t('editor.actions.add', 'Adicionar') }}</button>
2004
+ <span class="editor-spacer"></span>
2005
+ <mat-form-field appearance="outline" class="editor-field-240">
2006
+ <mat-label>{{ t('editor.fields.resourcePath', 'Recurso (resourcePath)') }}</mat-label>
2007
+ <input matInput [(ngModel)]="quickResourcePathLink[i]" [placeholder]="t('editor.fields.placeholder.resourcePath', 'ex.: usuarios')" />
2008
+ </mat-form-field>
2009
+ <button mat-button (click)="addPresetToLink(i, 'form')"><mat-icon [praxisIcon]="'description'"></mat-icon>{{ t('editor.templates.form', 'Form') }}</button>
2010
+ <button mat-button (click)="addPresetToLink(i, 'table')"><mat-icon [praxisIcon]="'grid_on'"></mat-icon>{{ t('editor.templates.table', 'Tabela') }}</button>
2011
+ <button mat-button (click)="addPresetToLink(i, 'crud')"><mat-icon [praxisIcon]="'dynamic_form'"></mat-icon>{{ t('editor.templates.crud', 'CRUD') }}</button>
2012
+ </div>
2013
+ @if (l.widgets?.length) {
2014
+ <div class="editor-grid" cdkDropList [cdkDropListData]="l.widgets || []" (cdkDropListDropped)="onLinkWidgetDrop(i, $event)">
2015
+ @for (w of l.widgets; track w; let wi = $index) {
2016
+ <div cdkDrag class="editor-card dashed">
1969
2017
  <div class="editor-card-header">
1970
2018
  <button mat-icon-button cdkDragHandle [matTooltip]="t('editor.actions.dragToReorder', 'Arrastar para reordenar')" [attr.aria-label]="t('editor.actions.dragToReorder', 'Arrastar para reordenar')">
1971
2019
  <mat-icon [praxisIcon]="'drag_indicator'"></mat-icon>
@@ -1984,16 +2032,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
1984
2032
  </mat-form-field>
1985
2033
  </div>
1986
2034
  </div>
1987
- </div>
2035
+ }
1988
2036
  </div>
1989
- </div>
2037
+ }
1990
2038
  </div>
1991
- <ng-template #noLinks><em class="editor-muted">{{ t('editor.empty.noLinks', 'Nenhum link definido.') }}</em></ng-template>
1992
2039
  </div>
1993
- </mat-tab>
2040
+ }
2041
+ </div>
2042
+ } @else {
2043
+ <em class="editor-muted">{{ t('editor.empty.noLinks', 'Nenhum link definido.') }}</em>
2044
+ }
2045
+ </div>
2046
+ </mat-tab>
1994
2047
  </mat-tab-group>
1995
2048
  </div>
1996
- `, styles: [":host{display:block;color:var(--md-sys-color-on-surface)}.editor-shell{display:grid;gap:12px}.editor-topbar{display:flex;align-items:start;justify-content:flex-start}.editor-mode-field{width:min(320px,100%)}.editor-diagnostics{display:grid;gap:8px}.editor-diagnostics-title{font-size:.95rem;font-weight:600;color:var(--md-sys-color-on-surface)}.editor-diagnostic{display:grid;gap:4px;padding:10px 12px;border-radius:10px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface)}.editor-diagnostic code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;color:var(--md-sys-color-on-surface-variant)}.editor-diagnostic.level-error{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container);border-color:var(--md-sys-color-error)}.editor-diagnostic.level-warning{background:var(--md-sys-color-tertiary-container, var(--md-sys-color-surface-container));color:var(--md-sys-color-on-tertiary-container, var(--md-sys-color-on-surface));border-color:var(--md-sys-color-outline-variant)}.editor-tabs{--editor-surface: var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));--editor-border: 1px solid var(--md-sys-color-outline-variant);--editor-radius: 12px;--editor-muted: var(--md-sys-color-on-surface-variant)}.editor-section{padding:12px;display:grid;gap:12px;background:var(--editor-surface);border:var(--editor-border);border-radius:var(--editor-radius)}.editor-section-lg{gap:16px}.editor-row{display:flex;gap:10px;flex-wrap:wrap;align-items:center}.editor-grid{display:grid;gap:12px}.editor-grid.two{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.editor-grid.tight{gap:8px}.editor-toolbar{display:flex;gap:8px;flex-wrap:wrap;align-items:center}.editor-muted{color:var(--editor-muted)}.editor-title{margin:6px 0 8px;font-size:1rem}.editor-title-row{display:flex;align-items:center;gap:8px}.editor-code{white-space:pre-wrap;background:var(--md-sys-color-surface-container);padding:8px;border-radius:6px;border:1px solid var(--md-sys-color-outline-variant)}.editor-card{border:1px solid var(--md-sys-color-outline-variant);padding:10px;border-radius:10px;display:grid;gap:8px;background:var(--md-sys-color-surface)}.editor-card.dashed{border-style:dashed}.editor-card-header{display:flex;align-items:center;gap:8px}.editor-card-title{flex:1;font-weight:600}.editor-divider{border-top:1px solid var(--md-sys-color-outline-variant);padding-top:8px}.editor-spacer{flex:1}.editor-field-min{min-width:260px}.editor-section .editor-field-min{width:260px;max-width:260px}.editor-field-240{width:240px}.editor-section .editor-field-240{width:240px;max-width:240px}.editor-json{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.editor-section .mat-mdc-form-field{width:100%;max-width:520px;--mdc-outlined-text-field-container-height: 48px;--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-outline);--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-error-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-focus-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-hover-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-label-text-color: var(--md-sys-color-on-surface-variant);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-supporting-text-color: var(--md-sys-color-on-surface-variant)}.editor-section .mat-mdc-form-field.full{max-width:none}.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"] }]
2049
+ `, styles: [":host{display:block;color:var(--md-sys-color-on-surface)}.editor-shell{display:grid;gap:12px}.editor-topbar{display:flex;align-items:start;justify-content:flex-start}.editor-mode-field{width:min(320px,100%)}.editor-diagnostics{display:grid;gap:8px}.editor-diagnostics-title{font-size:.95rem;font-weight:600;color:var(--md-sys-color-on-surface)}.editor-diagnostic{display:grid;gap:4px;padding:10px 12px;border-radius:10px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface)}.editor-diagnostic code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;color:var(--md-sys-color-on-surface-variant)}.editor-diagnostic.level-error{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container);border-color:var(--md-sys-color-error)}.editor-diagnostic.level-warning{background:var(--md-sys-color-tertiary-container, var(--md-sys-color-surface-container));color:var(--md-sys-color-on-tertiary-container, var(--md-sys-color-on-surface));border-color:var(--md-sys-color-outline-variant)}.editor-tabs{--editor-surface: var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));--editor-border: 1px solid var(--md-sys-color-outline-variant);--editor-radius: 12px;--editor-muted: var(--md-sys-color-on-surface-variant)}.editor-section{padding:12px;display:grid;gap:12px;background:var(--editor-surface);border:var(--editor-border);border-radius:var(--editor-radius)}.editor-section-lg{gap:16px}.editor-row{display:flex;gap:10px;flex-wrap:wrap;align-items:center}.editor-grid{display:grid;gap:12px}.editor-grid.two{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.editor-grid.tight{gap:8px}.editor-toolbar{display:flex;gap:8px;flex-wrap:wrap;align-items:center}.editor-muted{color:var(--editor-muted)}.editor-title{margin:6px 0 8px;font-size:1rem}.editor-title-row{display:flex;align-items:center;gap:8px}.editor-code{white-space:pre-wrap;background:var(--md-sys-color-surface-container);padding:8px;border-radius:6px;border:1px solid var(--md-sys-color-outline-variant)}.editor-card{border:1px solid var(--md-sys-color-outline-variant);padding:10px;border-radius:10px;display:grid;gap:8px;background:var(--md-sys-color-surface)}.editor-card.dashed{border-style:dashed}.editor-card-header{display:flex;align-items:center;gap:8px}.editor-card-title{flex:1;font-weight:600}.editor-divider{border-top:1px solid var(--md-sys-color-outline-variant);padding-top:8px}.editor-spacer{flex:1}.editor-field-min{min-width:260px}.editor-section .editor-field-min{width:260px;max-width:260px}.editor-field-240{width:240px}.editor-section .editor-field-240{width:240px;max-width:240px}.editor-json{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.editor-section .mat-mdc-form-field{width:100%;max-width:520px;--mdc-outlined-text-field-container-height: 48px;--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-outline);--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-error-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-focus-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-hover-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-label-text-color: var(--md-sys-color-on-surface-variant);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-supporting-text-color: var(--md-sys-color-on-surface-variant)}.editor-section .mat-mdc-form-field.full{max-width:none}.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"] }]
1997
2050
  }], ctorParameters: () => [{ type: undefined, decorators: [{
1998
2051
  type: Inject,
1999
2052
  args: [SETTINGS_PANEL_DATA]
@@ -2095,8 +2148,8 @@ class TabsQuickSetupComponent {
2095
2148
  onSave() {
2096
2149
  return this.buildOutputDocument();
2097
2150
  }
2098
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: TabsQuickSetupComponent, deps: [{ token: SETTINGS_PANEL_DATA }], target: i0.ɵɵFactoryTarget.Component });
2099
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: TabsQuickSetupComponent, isStandalone: true, selector: "praxis-tabs-quick-setup", providers: [providePraxisI18nConfig(PRAXIS_TABS_I18N_CONFIG)], ngImport: i0, template: `
2151
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: TabsQuickSetupComponent, deps: [{ token: SETTINGS_PANEL_DATA }], target: i0.ɵɵFactoryTarget.Component });
2152
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: TabsQuickSetupComponent, isStandalone: true, selector: "praxis-tabs-quick-setup", providers: [providePraxisI18nConfig(PRAXIS_TABS_I18N_CONFIG)], ngImport: i0, template: `
2100
2153
  <div class="setup-root">
2101
2154
  <div class="setup-row">
2102
2155
  <button mat-stroked-button [color]="model.mode==='group' ? 'primary' : undefined" (click)="setMode('group')">
@@ -2108,7 +2161,7 @@ class TabsQuickSetupComponent {
2108
2161
  {{ t('quickSetup.mode.nav', 'Navegacao por links') }}
2109
2162
  </button>
2110
2163
  </div>
2111
-
2164
+
2112
2165
  <div class="setup-grid">
2113
2166
  <mat-form-field appearance="outline">
2114
2167
  <mat-label>{{ t('editor.fields.addLabel', 'Adicionar rotulo') }}</mat-label>
@@ -2119,35 +2172,37 @@ class TabsQuickSetupComponent {
2119
2172
  {{ t('editor.actions.add', 'Adicionar') }}
2120
2173
  </button>
2121
2174
  </div>
2122
-
2123
- <div *ngIf="model.labels.length; else emptyLabels" class="setup-row wrap">
2124
- <div *ngFor="let l of model.labels; let i = index" class="chip">
2125
- <span>{{ l }}</span>
2126
- <button mat-icon-button (click)="removeLabel(i)" [attr.aria-label]="t('editor.actions.removeLabel', 'Remover rotulo')"><mat-icon [praxisIcon]="'close'"></mat-icon></button>
2175
+
2176
+ @if (model.labels.length) {
2177
+ <div class="setup-row wrap">
2178
+ @for (l of model.labels; track l; let i = $index) {
2179
+ <div class="chip">
2180
+ <span>{{ l }}</span>
2181
+ <button mat-icon-button (click)="removeLabel(i)" [attr.aria-label]="t('editor.actions.removeLabel', 'Remover rotulo')"><mat-icon [praxisIcon]="'close'"></mat-icon></button>
2182
+ </div>
2183
+ }
2127
2184
  </div>
2128
- </div>
2129
- <ng-template #emptyLabels>
2185
+ } @else {
2130
2186
  <em class="hint">{{ t('quickSetup.hints.emptyLabels', 'Adicione um ou mais rotulos para criar as abas.') }}</em>
2131
- </ng-template>
2132
-
2187
+ }
2188
+
2133
2189
  <div class="setup-row">
2134
2190
  <mat-slide-toggle [(ngModel)]="model.dynamicHeight" (ngModelChange)="updateState()">{{ t('editor.toggles.dynamicHeight', 'Altura dinamica') }}</mat-slide-toggle>
2135
2191
  <mat-slide-toggle [(ngModel)]="model.stretchTabs" (ngModelChange)="updateState()">{{ t('editor.toggles.stretchTabs', 'Esticar abas') }}</mat-slide-toggle>
2136
2192
  </div>
2137
2193
  </div>
2138
- `, isInline: true, styles: [":host{display:block;color:var(--md-sys-color-on-surface)}.setup-root{display:grid;gap:12px;padding:8px;background:var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:12px}.setup-row{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.setup-row.wrap{flex-wrap:wrap}.setup-grid{display:grid;grid-template-columns:1fr auto;gap:8px;align-items:start}.setup-root .mat-mdc-form-field{width:100%;max-width:520px;--mdc-outlined-text-field-container-height: 48px;--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-outline);--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-error-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-focus-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-hover-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-label-text-color: var(--md-sys-color-on-surface-variant);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-supporting-text-color: var(--md-sys-color-on-surface-variant)}.chip{display:inline-flex;align-items:center;gap:6px;padding:4px 8px;border:1px solid var(--md-sys-color-outline-variant);border-radius:16px;background:var(--md-sys-color-surface)}.hint{color:var(--md-sys-color-on-surface-variant)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i6.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: "ngmodule", type: MatIconModule }, { kind: "component", type: i7.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i6$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: i6$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i9.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }] });
2194
+ `, isInline: true, styles: [":host{display:block;color:var(--md-sys-color-on-surface)}.setup-root{display:grid;gap:12px;padding:8px;background:var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:12px}.setup-row{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.setup-row.wrap{flex-wrap:wrap}.setup-grid{display:grid;grid-template-columns:1fr auto;gap:8px;align-items:start}.setup-root .mat-mdc-form-field{width:100%;max-width:520px;--mdc-outlined-text-field-container-height: 48px;--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-outline);--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-error-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-focus-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-hover-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-label-text-color: var(--md-sys-color-on-surface-variant);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-supporting-text-color: var(--md-sys-color-on-surface-variant)}.chip{display:inline-flex;align-items:center;gap:6px;padding:4px 8px;border:1px solid var(--md-sys-color-outline-variant);border-radius:16px;background:var(--md-sys-color-surface)}.hint{color:var(--md-sys-color-on-surface-variant)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2_1.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_1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2_1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.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: "ngmodule", type: MatIconModule }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i6.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: i6.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i8.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }] });
2139
2195
  }
2140
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: TabsQuickSetupComponent, decorators: [{
2196
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: TabsQuickSetupComponent, decorators: [{
2141
2197
  type: Component,
2142
2198
  args: [{ selector: 'praxis-tabs-quick-setup', standalone: true, imports: [
2143
- CommonModule,
2144
2199
  FormsModule,
2145
2200
  MatFormFieldModule,
2146
2201
  MatInputModule,
2147
2202
  MatIconModule,
2148
2203
  PraxisIconDirective,
2149
2204
  MatButtonModule,
2150
- MatSlideToggleModule,
2205
+ MatSlideToggleModule
2151
2206
  ], providers: [providePraxisI18nConfig(PRAXIS_TABS_I18N_CONFIG)], template: `
2152
2207
  <div class="setup-root">
2153
2208
  <div class="setup-row">
@@ -2160,7 +2215,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2160
2215
  {{ t('quickSetup.mode.nav', 'Navegacao por links') }}
2161
2216
  </button>
2162
2217
  </div>
2163
-
2218
+
2164
2219
  <div class="setup-grid">
2165
2220
  <mat-form-field appearance="outline">
2166
2221
  <mat-label>{{ t('editor.fields.addLabel', 'Adicionar rotulo') }}</mat-label>
@@ -2171,23 +2226,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2171
2226
  {{ t('editor.actions.add', 'Adicionar') }}
2172
2227
  </button>
2173
2228
  </div>
2174
-
2175
- <div *ngIf="model.labels.length; else emptyLabels" class="setup-row wrap">
2176
- <div *ngFor="let l of model.labels; let i = index" class="chip">
2177
- <span>{{ l }}</span>
2178
- <button mat-icon-button (click)="removeLabel(i)" [attr.aria-label]="t('editor.actions.removeLabel', 'Remover rotulo')"><mat-icon [praxisIcon]="'close'"></mat-icon></button>
2229
+
2230
+ @if (model.labels.length) {
2231
+ <div class="setup-row wrap">
2232
+ @for (l of model.labels; track l; let i = $index) {
2233
+ <div class="chip">
2234
+ <span>{{ l }}</span>
2235
+ <button mat-icon-button (click)="removeLabel(i)" [attr.aria-label]="t('editor.actions.removeLabel', 'Remover rotulo')"><mat-icon [praxisIcon]="'close'"></mat-icon></button>
2236
+ </div>
2237
+ }
2179
2238
  </div>
2180
- </div>
2181
- <ng-template #emptyLabels>
2239
+ } @else {
2182
2240
  <em class="hint">{{ t('quickSetup.hints.emptyLabels', 'Adicione um ou mais rotulos para criar as abas.') }}</em>
2183
- </ng-template>
2184
-
2241
+ }
2242
+
2185
2243
  <div class="setup-row">
2186
2244
  <mat-slide-toggle [(ngModel)]="model.dynamicHeight" (ngModelChange)="updateState()">{{ t('editor.toggles.dynamicHeight', 'Altura dinamica') }}</mat-slide-toggle>
2187
2245
  <mat-slide-toggle [(ngModel)]="model.stretchTabs" (ngModelChange)="updateState()">{{ t('editor.toggles.stretchTabs', 'Esticar abas') }}</mat-slide-toggle>
2188
2246
  </div>
2189
2247
  </div>
2190
- `, styles: [":host{display:block;color:var(--md-sys-color-on-surface)}.setup-root{display:grid;gap:12px;padding:8px;background:var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:12px}.setup-row{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.setup-row.wrap{flex-wrap:wrap}.setup-grid{display:grid;grid-template-columns:1fr auto;gap:8px;align-items:start}.setup-root .mat-mdc-form-field{width:100%;max-width:520px;--mdc-outlined-text-field-container-height: 48px;--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-outline);--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-error-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-focus-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-hover-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-label-text-color: var(--md-sys-color-on-surface-variant);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-supporting-text-color: var(--md-sys-color-on-surface-variant)}.chip{display:inline-flex;align-items:center;gap:6px;padding:4px 8px;border:1px solid var(--md-sys-color-outline-variant);border-radius:16px;background:var(--md-sys-color-surface)}.hint{color:var(--md-sys-color-on-surface-variant)}\n"] }]
2248
+ `, styles: [":host{display:block;color:var(--md-sys-color-on-surface)}.setup-root{display:grid;gap:12px;padding:8px;background:var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:12px}.setup-row{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.setup-row.wrap{flex-wrap:wrap}.setup-grid{display:grid;grid-template-columns:1fr auto;gap:8px;align-items:start}.setup-root .mat-mdc-form-field{width:100%;max-width:520px;--mdc-outlined-text-field-container-height: 48px;--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-outline);--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-error-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-focus-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-hover-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-label-text-color: var(--md-sys-color-on-surface-variant);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-supporting-text-color: var(--md-sys-color-on-surface-variant)}.chip{display:inline-flex;align-items:center;gap:6px;padding:4px 8px;border:1px solid var(--md-sys-color-outline-variant);border-radius:16px;background:var(--md-sys-color-surface)}.hint{color:var(--md-sys-color-on-surface-variant)}\n"] }]
2191
2249
  }], ctorParameters: () => [{ type: undefined, decorators: [{
2192
2250
  type: Inject,
2193
2251
  args: [SETTINGS_PANEL_DATA]
@@ -3064,7 +3122,7 @@ class PraxisTabs {
3064
3122
  if (!this.aiAssistantOpen) {
3065
3123
  this.openAiAssistantFromSession(session);
3066
3124
  }
3067
- }, ...(ngDevMode ? [{ debugName: "aiAssistantSessionEffect" }] : []));
3125
+ }, ...(ngDevMode ? [{ debugName: "aiAssistantSessionEffect" }] : /* istanbul ignore next */ []));
3068
3126
  warnedMissingId = false;
3069
3127
  loadedStorageKey = null;
3070
3128
  config = null;
@@ -3105,8 +3163,8 @@ class PraxisTabs {
3105
3163
  aiAssistantController = null;
3106
3164
  aiAssistantStateSubscription = null;
3107
3165
  // Signals to manage local state for selection in Nav mode and Group mode
3108
- currentNavIndex = signal(0, ...(ngDevMode ? [{ debugName: "currentNavIndex" }] : []));
3109
- selectedIndexSignal = signal(0, ...(ngDevMode ? [{ debugName: "selectedIndexSignal" }] : []));
3166
+ currentNavIndex = signal(0, ...(ngDevMode ? [{ debugName: "currentNavIndex" }] : /* istanbul ignore next */ []));
3167
+ selectedIndexSignal = signal(0, ...(ngDevMode ? [{ debugName: "selectedIndexSignal" }] : /* istanbul ignore next */ []));
3110
3168
  groupLoaded = new Set();
3111
3169
  navLoaded = new Set();
3112
3170
  controlledSelectedIndex;
@@ -4144,8 +4202,8 @@ class PraxisTabs {
4144
4202
  catch { }
4145
4203
  return JSON.parse(JSON.stringify(config));
4146
4204
  }
4147
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisTabs, deps: [], target: i0.ɵɵFactoryTarget.Component });
4148
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: PraxisTabs, isStandalone: true, selector: "praxis-tabs", inputs: { config: "config", tabsId: "tabsId", componentInstanceId: "componentInstanceId", configPersistenceStrategy: "configPersistenceStrategy", selectedIndex: "selectedIndex", enableCustomization: "enableCustomization", form: "form", context: "context" }, outputs: { animationDone: "animationDone", focusChange: "focusChange", selectedIndexChange: "selectedIndexChange", selectedTabChange: "selectedTabChange", indexFocused: "indexFocused", selectFocusedIndex: "selectFocusedIndex", configChange: "configChange", widgetEvent: "widgetEvent" }, providers: [providePraxisI18nConfig(PRAXIS_TABS_I18N_CONFIG)], usesOnChanges: true, ngImport: i0, template: `
4205
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisTabs, deps: [], target: i0.ɵɵFactoryTarget.Component });
4206
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisTabs, isStandalone: true, selector: "praxis-tabs", inputs: { config: "config", tabsId: "tabsId", componentInstanceId: "componentInstanceId", configPersistenceStrategy: "configPersistenceStrategy", selectedIndex: "selectedIndex", enableCustomization: "enableCustomization", form: "form", context: "context" }, outputs: { animationDone: "animationDone", focusChange: "focusChange", selectedIndexChange: "selectedIndexChange", selectedTabChange: "selectedTabChange", indexFocused: "indexFocused", selectFocusedIndex: "selectFocusedIndex", configChange: "configChange", widgetEvent: "widgetEvent" }, providers: [providePraxisI18nConfig(PRAXIS_TABS_I18N_CONFIG)], usesOnChanges: true, ngImport: i0, template: `
4149
4207
  <div
4150
4208
  class="praxis-tabs-root"
4151
4209
  [class.density-compact]="config?.appearance?.density === 'compact'"
@@ -4155,60 +4213,67 @@ class PraxisTabs {
4155
4213
  [class.reduce-motion]="config?.accessibility?.reduceMotion"
4156
4214
  [ngClass]="config?.appearance?.themeClass || ''"
4157
4215
  [attr.data-tabs-id]="styleScopeId()"
4158
- >
4216
+ >
4159
4217
  <!-- Inline custom CSS, if provided -->
4160
- <style *ngIf="safeCustomCss() as css" [innerHTML]="css"></style>
4218
+ @if (safeCustomCss(); as css) {
4219
+ <style [innerHTML]="css"></style>
4220
+ }
4161
4221
  <!-- Runtime style derived from tokens -->
4162
- <style *ngIf="styleCss() as s" [innerHTML]="s"></style>
4163
-
4164
- <div class="tabs-ai-assistant" *ngIf="enableCustomization">
4165
- <button
4166
- mat-mini-fab
4167
- type="button"
4168
- color="primary"
4169
- class="tabs-ai-assistant-trigger"
4170
- (click)="openAiAssistant()"
4171
- matTooltip="Copiloto semantico Praxis"
4172
- aria-label="Abrir copiloto semantico Praxis das abas"
4173
- data-testid="praxis-tabs-ai-assistant-trigger"
4174
- >
4175
- <mat-icon [praxisIcon]="'auto_awesome'"></mat-icon>
4176
- </button>
4177
- </div>
4178
-
4179
- <praxis-ai-assistant-shell
4180
- *ngIf="aiAssistantOpen && aiAssistantViewState"
4181
- [labels]="aiAssistantLabels"
4182
- [mode]="aiAssistantViewState.mode"
4183
- [state]="aiAssistantViewState.state"
4184
- [contextItems]="aiAssistantViewState.contextItems"
4185
- [attachments]="aiAssistantViewState.attachments"
4186
- [messages]="aiAssistantViewState.messages"
4187
- [quickReplies]="aiAssistantViewState.quickReplies"
4188
- [prompt]="aiAssistantPrompt"
4189
- [statusText]="aiAssistantViewState.statusText"
4190
- [errorText]="aiAssistantViewState.errorText"
4191
- [busy]="aiAssistantViewState.state === 'processing' || aiAssistantViewState.state === 'applying'"
4192
- [canApply]="aiAssistantViewState.canApply"
4193
- [layout]="aiAssistantLayout"
4194
- testIdPrefix="praxis-tabs-ai-assistant"
4195
- panelTestId="praxis-tabs-ai-assistant-panel"
4196
- submitTestId="praxis-tabs-ai-assistant-submit"
4197
- applyTestId="praxis-tabs-ai-assistant-apply"
4198
- (promptChange)="onAiAssistantPromptChange($event)"
4199
- (submitPrompt)="onAiAssistantSubmit($event)"
4200
- (apply)="onAiAssistantApply()"
4201
- (retryTurn)="onAiAssistantRetry()"
4202
- (cancelTurn)="onAiAssistantCancel()"
4203
- (quickReply)="onAiAssistantQuickReply($event)"
4204
- (editMessage)="onAiAssistantEditMessage($event)"
4205
- (resendMessage)="onAiAssistantResendMessage($event)"
4206
- (layoutChange)="onAiAssistantLayoutChange($event)"
4207
- (close)="closeAiAssistant()"
4208
- ></praxis-ai-assistant-shell>
4209
-
4222
+ @if (styleCss(); as s) {
4223
+ <style [innerHTML]="s"></style>
4224
+ }
4225
+
4226
+ @if (enableCustomization) {
4227
+ <div class="tabs-ai-assistant">
4228
+ <button
4229
+ mat-mini-fab
4230
+ type="button"
4231
+ color="primary"
4232
+ class="tabs-ai-assistant-trigger"
4233
+ (click)="openAiAssistant()"
4234
+ matTooltip="Copiloto semantico Praxis"
4235
+ aria-label="Abrir copiloto semantico Praxis das abas"
4236
+ data-testid="praxis-tabs-ai-assistant-trigger"
4237
+ >
4238
+ <mat-icon [praxisIcon]="'auto_awesome'"></mat-icon>
4239
+ </button>
4240
+ </div>
4241
+ }
4242
+
4243
+ @if (aiAssistantOpen && aiAssistantViewState) {
4244
+ <praxis-ai-assistant-shell
4245
+ [labels]="aiAssistantLabels"
4246
+ [mode]="aiAssistantViewState.mode"
4247
+ [state]="aiAssistantViewState.state"
4248
+ [contextItems]="aiAssistantViewState.contextItems"
4249
+ [attachments]="aiAssistantViewState.attachments"
4250
+ [messages]="aiAssistantViewState.messages"
4251
+ [quickReplies]="aiAssistantViewState.quickReplies"
4252
+ [prompt]="aiAssistantPrompt"
4253
+ [statusText]="aiAssistantViewState.statusText"
4254
+ [errorText]="aiAssistantViewState.errorText"
4255
+ [busy]="aiAssistantViewState.state === 'processing' || aiAssistantViewState.state === 'applying'"
4256
+ [canApply]="aiAssistantViewState.canApply"
4257
+ [layout]="aiAssistantLayout"
4258
+ testIdPrefix="praxis-tabs-ai-assistant"
4259
+ panelTestId="praxis-tabs-ai-assistant-panel"
4260
+ submitTestId="praxis-tabs-ai-assistant-submit"
4261
+ applyTestId="praxis-tabs-ai-assistant-apply"
4262
+ (promptChange)="onAiAssistantPromptChange($event)"
4263
+ (submitPrompt)="onAiAssistantSubmit($event)"
4264
+ (apply)="onAiAssistantApply()"
4265
+ (retryTurn)="onAiAssistantRetry()"
4266
+ (cancelTurn)="onAiAssistantCancel()"
4267
+ (quickReply)="onAiAssistantQuickReply($event)"
4268
+ (editMessage)="onAiAssistantEditMessage($event)"
4269
+ (resendMessage)="onAiAssistantResendMessage($event)"
4270
+ (layoutChange)="onAiAssistantLayoutChange($event)"
4271
+ (close)="closeAiAssistant()"
4272
+ ></praxis-ai-assistant-shell>
4273
+ }
4274
+
4210
4275
  <!-- Empty state (global) -->
4211
- <ng-container *ngIf="isEmptyGlobal(); else notEmpty">
4276
+ @if (isEmptyGlobal()) {
4212
4277
  <praxis-empty-state-card
4213
4278
  icon="tab"
4214
4279
  [title]="t('emptyState.title', 'Nenhuma aba configurada')"
@@ -4219,216 +4284,220 @@ class PraxisTabs {
4219
4284
  { label: t('emptyState.secondaryOpenEditor', 'Abrir editor completo'), icon: 'tune', action: openEditor.bind(this) }
4220
4285
  ]"
4221
4286
  ></praxis-empty-state-card>
4222
- </ng-container>
4223
-
4224
- <ng-template #notEmpty>
4225
- <!-- Tab Nav variant -->
4226
- <ng-container *ngIf="isNavMode(); else groupMode">
4227
- <nav
4228
- mat-tab-nav-bar
4229
- [tabPanel]="tabPanel"
4230
- cdkDropList
4231
- cdkDropListOrientation="horizontal"
4232
- [cdkDropListDisabled]="!config?.behavior?.reorderable"
4233
- (cdkDropListDropped)="onVisibleNavDrop($event)"
4234
- [disablePagination]="config?.nav?.disablePagination"
4235
- [fitInkBarToContent]="config?.nav?.fitInkBarToContent"
4236
- [mat-stretch-tabs]="config?.nav?.stretchTabs"
4237
- [color]="config?.nav?.color"
4238
- [backgroundColor]="config?.nav?.backgroundColor"
4239
- [selectedIndex]="selectedVisibleNavIndex()"
4240
- [attr.aria-label]="config?.nav?.ariaLabel || config?.group?.ariaLabel || null"
4241
- [attr.aria-labelledby]="config?.nav?.ariaLabelledby || config?.group?.ariaLabelledby || null"
4242
- [animationDuration]="effectiveAnimationDuration()"
4243
- (indexFocused)="indexFocused.emit($event)"
4244
- (selectFocusedIndex)="selectFocusedIndex.emit($event)"
4245
- >
4246
- <a
4247
- mat-tab-link
4248
- *ngFor="let entry of visibleNavLinkEntries(); let i = index; trackBy: trackVisibleNavLink"
4249
- cdkDrag
4250
- [cdkDragDisabled]="!config?.behavior?.reorderable"
4251
- cdkDragLockAxis="x"
4252
- [active]="getNavActive(entry.index)"
4253
- [disabled]="entry.link.disabled"
4254
- [disableRipple]="config?.nav?.disableRipple || entry.link.disableRipple"
4255
- [fitInkBarToContent]="entry.link.fitInkBarToContent || false"
4256
- [id]="entry.link.id || ''"
4257
- (click)="onNavClick(entry.index)"
4258
- >
4259
- <span *ngIf="config?.behavior?.reorderable" class="drag-handle" cdkDragHandle>
4260
- <mat-icon fontIcon="drag_indicator"></mat-icon>
4261
- </span>
4262
- <mat-icon *ngIf="entry.link.icon" class="tab-label-icon" [praxisIcon]="entry.link.icon"></mat-icon>
4263
- {{ entry.link.label }}
4264
- </a>
4265
- </nav>
4266
-
4267
- <mat-tab-nav-panel #tabPanel>
4268
- <div class="praxis-tabnav-content" *ngIf="currentNavIndex() >= 0">
4269
- <ng-container
4270
- *ngIf="config?.nav?.links?.[currentNavIndex()] as l"
4287
+ } @else {
4288
+ <!-- Tab Nav variant -->
4289
+ @if (isNavMode()) {
4290
+ <nav
4291
+ mat-tab-nav-bar
4292
+ [tabPanel]="tabPanel"
4293
+ cdkDropList
4294
+ cdkDropListOrientation="horizontal"
4295
+ [cdkDropListDisabled]="!config?.behavior?.reorderable"
4296
+ (cdkDropListDropped)="onVisibleNavDrop($event)"
4297
+ [disablePagination]="config?.nav?.disablePagination"
4298
+ [fitInkBarToContent]="config?.nav?.fitInkBarToContent"
4299
+ [mat-stretch-tabs]="config?.nav?.stretchTabs"
4300
+ [color]="config?.nav?.color"
4301
+ [backgroundColor]="config?.nav?.backgroundColor"
4302
+ [selectedIndex]="selectedVisibleNavIndex()"
4303
+ [attr.aria-label]="config?.nav?.ariaLabel || config?.group?.ariaLabel || null"
4304
+ [attr.aria-labelledby]="config?.nav?.ariaLabelledby || config?.group?.ariaLabelledby || null"
4305
+ [animationDuration]="effectiveAnimationDuration()"
4306
+ (indexFocused)="indexFocused.emit($event)"
4307
+ (selectFocusedIndex)="selectFocusedIndex.emit($event)"
4271
4308
  >
4272
- <ng-container *ngIf="(l.content?.length || safeWidgetDefinitions(l.widgets).length) && navContentReady(currentNavIndex()); else emptyNav">
4273
- <ng-container *ngIf="l.content">
4274
- <ng-container
4275
- dynamicFieldLoader
4276
- [fields]="l.content || []"
4277
- [formGroup]="effectiveContentForm()"
4278
- ></ng-container>
4279
- </ng-container>
4280
- <ng-container *ngIf="safeWidgetDefinitions(l.widgets).length">
4281
- <ng-container *ngFor="let w of safeWidgetDefinitions(l.widgets); let wi = index; trackBy: trackWidgetDefinition">
4282
- <ng-container
4283
- [dynamicWidgetLoader]="resolveWidgetDefinition(w)"
4284
- [context]="context || {}"
4285
- (widgetEvent)="emitWidgetEvent(linkEventPath(l.id, currentNavIndex()), $event)"
4286
- ></ng-container>
4287
- </ng-container>
4288
- </ng-container>
4289
- </ng-container>
4290
- <ng-template #emptyNav>
4291
- <praxis-empty-state-card
4292
- [inline]="true"
4293
- icon="add_to_queue"
4294
- [title]="t('emptyState.navTitle', 'Sem conteudo neste link')"
4295
- [description]="t('emptyState.navDescription', 'Adicione conteudo ou use o editor para configurar.')"
4296
- [primaryAction]="{ label: t('emptyState.openEditor', 'Abrir editor'), icon: 'tune', action: openEditor.bind(this) }"
4297
- ></praxis-empty-state-card>
4298
- </ng-template>
4299
- </ng-container>
4300
- </div>
4301
- </mat-tab-nav-panel>
4302
- </ng-container>
4303
-
4304
- <!-- Tab Group variant -->
4305
- <ng-template #groupMode>
4306
- <mat-tab-group
4307
- [dynamicHeight]="config?.group?.dynamicHeight"
4308
- [disableRipple]="config?.group?.disableRipple"
4309
- [disablePagination]="config?.group?.disablePagination"
4310
- [fitInkBarToContent]="config?.group?.fitInkBarToContent"
4311
- [headerPosition]="config?.group?.headerPosition ?? 'above'"
4312
- [preserveContent]="config?.group?.preserveContent"
4313
- [selectedIndex]="selectedVisibleTabIndex()"
4314
- [mat-stretch-tabs]="config?.group?.stretchTabs"
4315
- [mat-align-tabs]="config?.group?.alignTabs || 'start'"
4316
- [color]="config?.group?.color"
4317
- [backgroundColor]="config?.group?.backgroundColor"
4318
- [animationDuration]="effectiveAnimationDuration()"
4319
- [attr.aria-label]="config?.group?.ariaLabel || null"
4320
- [attr.aria-labelledby]="config?.group?.ariaLabelledby || null"
4321
- (animationDone)="animationDone.emit()"
4322
- (focusChange)="focusChange.emit($event)"
4323
- (selectedIndexChange)="onVisibleTabIndexChange($event)"
4324
- (selectedTabChange)="selectedTabChange.emit($event)"
4325
- class="praxis-tabs-group"
4326
- >
4327
- <mat-tab
4328
- *ngFor="let entry of visibleTabEntries(); let i = index; trackBy: trackVisibleTab"
4329
- [disabled]="entry.tab.disabled"
4330
- [labelClass]="entry.tab.labelClass ?? ''"
4331
- [bodyClass]="entry.tab.bodyClass ?? ''"
4332
- [id]="entry.tab.id || ''"
4333
- [attr.aria-label]="entry.tab.ariaLabel || null"
4334
- [attr.aria-labelledby]="entry.tab.ariaLabelledby || null"
4335
- >
4336
- <ng-template mat-tab-label>
4337
- <mat-icon *ngIf="entry.tab.icon" class="tab-label-icon" [praxisIcon]="entry.tab.icon"></mat-icon>
4338
- <span>{{ entry.tab.textLabel }}</span>
4339
- <button
4340
- *ngIf="config?.behavior?.closeable"
4341
- mat-icon-button
4342
- type="button"
4343
- (click)="closeTab(entry.index); $event.stopPropagation()"
4344
- (keydown.enter)="$event.stopPropagation()"
4345
- (keydown.space)="$event.stopPropagation()"
4346
- [attr.aria-label]="t('chrome.closeTab', 'Fechar aba')"
4347
- >
4348
- <mat-icon fontIcon="close"></mat-icon>
4349
- </button>
4350
- <ng-container *ngIf="config?.behavior?.reorderable">
4351
- <button
4352
- mat-icon-button
4353
- type="button"
4354
- (click)="moveTab(entry.index, -1); $event.stopPropagation()"
4355
- (keydown.enter)="$event.stopPropagation()"
4356
- (keydown.space)="$event.stopPropagation()"
4357
- [disabled]="entry.index===0"
4358
- [attr.aria-label]="t('chrome.moveTabLeft', 'Mover aba para esquerda')"
4309
+ @for (entry of visibleNavLinkEntries(); track trackVisibleNavLink(i, entry); let i = $index) {
4310
+ <a
4311
+ mat-tab-link
4312
+ cdkDrag
4313
+ [cdkDragDisabled]="!config?.behavior?.reorderable"
4314
+ cdkDragLockAxis="x"
4315
+ [active]="getNavActive(entry.index)"
4316
+ [disabled]="entry.link.disabled"
4317
+ [disableRipple]="config?.nav?.disableRipple || entry.link.disableRipple"
4318
+ [fitInkBarToContent]="entry.link.fitInkBarToContent || false"
4319
+ [id]="entry.link.id || ''"
4320
+ (click)="onNavClick(entry.index)"
4359
4321
  >
4360
- <mat-icon fontIcon="arrow_back"></mat-icon>
4361
- </button>
4362
- <button
4363
- mat-icon-button
4364
- type="button"
4365
- (click)="moveTab(entry.index, 1); $event.stopPropagation()"
4366
- (keydown.enter)="$event.stopPropagation()"
4367
- (keydown.space)="$event.stopPropagation()"
4368
- [disabled]="entry.index===(config?.tabs?.length||1)-1"
4369
- [attr.aria-label]="t('chrome.moveTabRight', 'Mover aba para direita')"
4322
+ @if (config?.behavior?.reorderable) {
4323
+ <span class="drag-handle" cdkDragHandle>
4324
+ <mat-icon fontIcon="drag_indicator"></mat-icon>
4325
+ </span>
4326
+ }
4327
+ @if (entry.link.icon) {
4328
+ <mat-icon class="tab-label-icon" [praxisIcon]="entry.link.icon"></mat-icon>
4329
+ }
4330
+ {{ entry.link.label }}
4331
+ </a>
4332
+ }
4333
+ </nav>
4334
+ <mat-tab-nav-panel #tabPanel>
4335
+ @if (currentNavIndex() >= 0) {
4336
+ <div class="praxis-tabnav-content">
4337
+ @if (config?.nav?.links?.[currentNavIndex()]; as l) {
4338
+ @if ((l.content?.length || safeWidgetDefinitions(l.widgets).length) && navContentReady(currentNavIndex())) {
4339
+ @if (l.content) {
4340
+ <ng-container
4341
+ dynamicFieldLoader
4342
+ [fields]="l.content || []"
4343
+ [formGroup]="effectiveContentForm()"
4344
+ ></ng-container>
4345
+ }
4346
+ @if (safeWidgetDefinitions(l.widgets).length) {
4347
+ @for (w of safeWidgetDefinitions(l.widgets); track trackWidgetDefinition(wi, w); let wi = $index) {
4348
+ <ng-container
4349
+ [dynamicWidgetLoader]="resolveWidgetDefinition(w)"
4350
+ [context]="context || {}"
4351
+ (widgetEvent)="emitWidgetEvent(linkEventPath(l.id, currentNavIndex()), $event)"
4352
+ ></ng-container>
4353
+ }
4354
+ }
4355
+ } @else {
4356
+ <praxis-empty-state-card
4357
+ [inline]="true"
4358
+ icon="add_to_queue"
4359
+ [title]="t('emptyState.navTitle', 'Sem conteudo neste link')"
4360
+ [description]="t('emptyState.navDescription', 'Adicione conteudo ou use o editor para configurar.')"
4361
+ [primaryAction]="{ label: t('emptyState.openEditor', 'Abrir editor'), icon: 'tune', action: openEditor.bind(this) }"
4362
+ ></praxis-empty-state-card>
4363
+ }
4364
+ }
4365
+ </div>
4366
+ }
4367
+ </mat-tab-nav-panel>
4368
+ } @else {
4369
+ <mat-tab-group
4370
+ [dynamicHeight]="config?.group?.dynamicHeight"
4371
+ [disableRipple]="config?.group?.disableRipple"
4372
+ [disablePagination]="config?.group?.disablePagination"
4373
+ [fitInkBarToContent]="config?.group?.fitInkBarToContent"
4374
+ [headerPosition]="config?.group?.headerPosition ?? 'above'"
4375
+ [preserveContent]="config?.group?.preserveContent"
4376
+ [selectedIndex]="selectedVisibleTabIndex()"
4377
+ [mat-stretch-tabs]="config?.group?.stretchTabs"
4378
+ [mat-align-tabs]="config?.group?.alignTabs || 'start'"
4379
+ [color]="config?.group?.color"
4380
+ [backgroundColor]="config?.group?.backgroundColor"
4381
+ [animationDuration]="effectiveAnimationDuration()"
4382
+ [attr.aria-label]="config?.group?.ariaLabel || null"
4383
+ [attr.aria-labelledby]="config?.group?.ariaLabelledby || null"
4384
+ (animationDone)="animationDone.emit()"
4385
+ (focusChange)="focusChange.emit($event)"
4386
+ (selectedIndexChange)="onVisibleTabIndexChange($event)"
4387
+ (selectedTabChange)="selectedTabChange.emit($event)"
4388
+ class="praxis-tabs-group"
4389
+ >
4390
+ @for (entry of visibleTabEntries(); track trackVisibleTab(i, entry); let i = $index) {
4391
+ <mat-tab
4392
+ [disabled]="entry.tab.disabled"
4393
+ [labelClass]="entry.tab.labelClass ?? ''"
4394
+ [bodyClass]="entry.tab.bodyClass ?? ''"
4395
+ [id]="entry.tab.id || ''"
4396
+ [attr.aria-label]="entry.tab.ariaLabel || null"
4397
+ [attr.aria-labelledby]="entry.tab.ariaLabelledby || null"
4370
4398
  >
4371
- <mat-icon fontIcon="arrow_forward"></mat-icon>
4372
- </button>
4373
- </ng-container>
4374
- </ng-template>
4375
-
4376
- <ng-container *ngIf="(entry.tab.content?.length || safeWidgetDefinitions(entry.tab.widgets).length) && groupContentReady(entry.index); else emptyTab">
4377
- <ng-container *ngIf="entry.tab.content">
4378
- <ng-container
4379
- dynamicFieldLoader
4380
- [fields]="entry.tab.content || []"
4381
- [formGroup]="effectiveContentForm()"
4382
- ></ng-container>
4383
- </ng-container>
4384
- <ng-container *ngIf="safeWidgetDefinitions(entry.tab.widgets).length">
4385
- <ng-container *ngFor="let w of safeWidgetDefinitions(entry.tab.widgets); let wi = index; trackBy: trackWidgetDefinition">
4386
- <ng-container
4387
- [dynamicWidgetLoader]="resolveWidgetDefinition(w)"
4388
- [context]="context || {}"
4389
- (widgetEvent)="emitWidgetEvent(tabEventPath(entry.tab.id, entry.index), $event)"
4390
- ></ng-container>
4391
- </ng-container>
4392
- </ng-container>
4393
- </ng-container>
4394
- <ng-template #emptyTab>
4395
- <praxis-empty-state-card
4396
- [inline]="true"
4397
- icon="dashboard_customize"
4398
- [title]="t('emptyState.tabTitle', 'Sem conteudo nesta aba')"
4399
- [description]="t('emptyState.tabDescription', 'Adicione conteudo ou use o editor para configurar.')"
4400
- [primaryAction]="{ label: t('emptyState.openEditor', 'Abrir editor'), icon: 'tune', action: openEditor.bind(this) }"
4401
- ></praxis-empty-state-card>
4402
- </ng-template>
4403
- </mat-tab>
4404
- </mat-tab-group>
4405
- </ng-template>
4406
- </ng-template>
4407
-
4399
+ <ng-template mat-tab-label>
4400
+ @if (entry.tab.icon) {
4401
+ <mat-icon class="tab-label-icon" [praxisIcon]="entry.tab.icon"></mat-icon>
4402
+ }
4403
+ <span>{{ entry.tab.textLabel }}</span>
4404
+ @if (config?.behavior?.closeable) {
4405
+ <button
4406
+ mat-icon-button
4407
+ type="button"
4408
+ (click)="closeTab(entry.index); $event.stopPropagation()"
4409
+ (keydown.enter)="$event.stopPropagation()"
4410
+ (keydown.space)="$event.stopPropagation()"
4411
+ [attr.aria-label]="t('chrome.closeTab', 'Fechar aba')"
4412
+ >
4413
+ <mat-icon fontIcon="close"></mat-icon>
4414
+ </button>
4415
+ }
4416
+ @if (config?.behavior?.reorderable) {
4417
+ <button
4418
+ mat-icon-button
4419
+ type="button"
4420
+ (click)="moveTab(entry.index, -1); $event.stopPropagation()"
4421
+ (keydown.enter)="$event.stopPropagation()"
4422
+ (keydown.space)="$event.stopPropagation()"
4423
+ [disabled]="entry.index===0"
4424
+ [attr.aria-label]="t('chrome.moveTabLeft', 'Mover aba para esquerda')"
4425
+ >
4426
+ <mat-icon fontIcon="arrow_back"></mat-icon>
4427
+ </button>
4428
+ <button
4429
+ mat-icon-button
4430
+ type="button"
4431
+ (click)="moveTab(entry.index, 1); $event.stopPropagation()"
4432
+ (keydown.enter)="$event.stopPropagation()"
4433
+ (keydown.space)="$event.stopPropagation()"
4434
+ [disabled]="entry.index===(config?.tabs?.length||1)-1"
4435
+ [attr.aria-label]="t('chrome.moveTabRight', 'Mover aba para direita')"
4436
+ >
4437
+ <mat-icon fontIcon="arrow_forward"></mat-icon>
4438
+ </button>
4439
+ }
4440
+ </ng-template>
4441
+ @if ((entry.tab.content?.length || safeWidgetDefinitions(entry.tab.widgets).length) && groupContentReady(entry.index)) {
4442
+ @if (entry.tab.content) {
4443
+ <ng-container
4444
+ dynamicFieldLoader
4445
+ [fields]="entry.tab.content || []"
4446
+ [formGroup]="effectiveContentForm()"
4447
+ ></ng-container>
4448
+ }
4449
+ @if (safeWidgetDefinitions(entry.tab.widgets).length) {
4450
+ @for (w of safeWidgetDefinitions(entry.tab.widgets); track trackWidgetDefinition(wi, w); let wi = $index) {
4451
+ <ng-container
4452
+ [dynamicWidgetLoader]="resolveWidgetDefinition(w)"
4453
+ [context]="context || {}"
4454
+ (widgetEvent)="emitWidgetEvent(tabEventPath(entry.tab.id, entry.index), $event)"
4455
+ ></ng-container>
4456
+ }
4457
+ }
4458
+ } @else {
4459
+ <praxis-empty-state-card
4460
+ [inline]="true"
4461
+ icon="dashboard_customize"
4462
+ [title]="t('emptyState.tabTitle', 'Sem conteudo nesta aba')"
4463
+ [description]="t('emptyState.tabDescription', 'Adicione conteudo ou use o editor para configurar.')"
4464
+ [primaryAction]="{ label: t('emptyState.openEditor', 'Abrir editor'), icon: 'tune', action: openEditor.bind(this) }"
4465
+ ></praxis-empty-state-card>
4466
+ }
4467
+ </mat-tab>
4468
+ }
4469
+ </mat-tab-group>
4470
+ }
4471
+ <!-- Tab Group variant -->
4472
+ }
4473
+
4474
+
4408
4475
  <!-- Edit button -->
4409
- <button
4410
- *ngIf="enableCustomization"
4411
- mat-fab
4412
- class="edit-fab"
4413
- [attr.aria-label]="t('chrome.editTabs', 'Editar abas')"
4414
- (click)="openEditor()"
4415
- >
4416
- <mat-icon fontIcon="edit"></mat-icon>
4417
- </button>
4418
- <button
4419
- *ngIf="enableCustomization && tabsId"
4420
- mat-mini-fab
4421
- class="edit-fab edit-fab-secondary"
4422
- [attr.aria-label]="t('settings.resetPreferences', 'Redefinir preferencias de abas')"
4423
- (click)="resetPreferences()"
4424
- [matTooltip]="t('settings.resetPreferences', 'Redefinir preferencias de abas')"
4425
- >
4426
- <mat-icon [praxisIcon]="'restart_alt'"></mat-icon>
4427
- </button>
4476
+ @if (enableCustomization) {
4477
+ <button
4478
+ mat-fab
4479
+ class="edit-fab"
4480
+ [attr.aria-label]="t('chrome.editTabs', 'Editar abas')"
4481
+ (click)="openEditor()"
4482
+ >
4483
+ <mat-icon fontIcon="edit"></mat-icon>
4484
+ </button>
4485
+ }
4486
+ @if (enableCustomization && tabsId) {
4487
+ <button
4488
+ mat-mini-fab
4489
+ class="edit-fab edit-fab-secondary"
4490
+ [attr.aria-label]="t('settings.resetPreferences', 'Redefinir preferencias de abas')"
4491
+ (click)="resetPreferences()"
4492
+ [matTooltip]="t('settings.resetPreferences', 'Redefinir preferencias de abas')"
4493
+ >
4494
+ <mat-icon [praxisIcon]="'restart_alt'"></mat-icon>
4495
+ </button>
4496
+ }
4428
4497
  </div>
4429
- `, isInline: true, styles: [".praxis-tabs-root{position:relative;display:block}.praxis-tabs-group.align-start .mat-mdc-tab-header{justify-content:flex-start}.praxis-tabs-group.align-center .mat-mdc-tab-header{justify-content:center}.praxis-tabs-group.align-end .mat-mdc-tab-header{justify-content:flex-end}.density-compact .mat-mdc-tab-body-content{padding:8px}.density-comfortable .mat-mdc-tab-body-content{padding:16px}.density-spacious .mat-mdc-tab-body-content{padding:24px}.tabs-ai-assistant{position:absolute;right:12px;bottom:72px;z-index:3}.tabs-ai-assistant-trigger{box-shadow:var(--md-sys-elevation-level2)}.edit-fab{position:absolute;right:12px;bottom:12px;z-index:2}.edit-fab-secondary{right:56px}.tab-empty{padding:16px;color:var(--md-sys-color-on-surface-variant);font-style:italic}.high-contrast{filter:contrast(1.2)}.reduce-motion{--mat-animation-duration: 0ms}.drag-handle{display:inline-flex;align-items:center;vertical-align:middle;margin-right:4px;cursor:grab}.tab-label-icon{font-size:18px;width:18px;height:18px;margin-right:6px;vertical-align:middle}:host-context(.pdx-gridster-item) .praxis-tabs-root{display:flex;flex-direction:column;height:100%;min-height:0}:host-context(.pdx-gridster-item) .praxis-tabs-group,:host-context(.pdx-gridster-item) .mat-mdc-tab-group{flex:1 1 auto;min-height:0}:host-context(.pdx-gridster-item) .mat-mdc-tab-body-wrapper,:host-context(.pdx-gridster-item) .mat-mdc-tab-body-content{height:100%;min-height:0}:host-context(.pdx-gridster-item) .mat-mdc-tab-body-content{overflow:auto}:host-context(.pdx-gridster-item) .praxis-tabnav-content{flex:1 1 auto;min-height:0;overflow:auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i3$1.MatTabLabel, selector: "[mat-tab-label], [matTabLabel]" }, { kind: "component", type: i3$1.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i3$1.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "component", type: i3$1.MatTabNav, selector: "[mat-tab-nav-bar]", inputs: ["fitInkBarToContent", "mat-stretch-tabs", "animationDuration", "backgroundColor", "disableRipple", "color", "tabPanel"], exportAs: ["matTabNavBar", "matTabNav"] }, { kind: "component", type: i3$1.MatTabNavPanel, selector: "mat-tab-nav-panel", inputs: ["id"], exportAs: ["matTabNavPanel"] }, { kind: "component", type: i3$1.MatTabLink, selector: "[mat-tab-link], [matTabLink]", inputs: ["active", "disabled", "disableRipple", "tabIndex", "id"], exportAs: ["matTabLink"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i7.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i11.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i6$1.MatMiniFabButton, selector: "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i6$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i6$1.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i10.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i10.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i10.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline", "tone"] }, { 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: "directive", type: DynamicWidgetLoaderDirective, selector: "[dynamicWidgetLoader]", inputs: ["dynamicWidgetLoader", "ownerWidgetKey", "context", "strictValidation", "autoWireOutputs"], outputs: ["widgetEvent", "widgetDiagnostic"], exportAs: ["dynamicWidgetLoader"] }, { kind: "component", type: PraxisAiAssistantShellComponent, selector: "praxis-ai-assistant-shell", inputs: ["labels", "mode", "state", "contextItems", "attachments", "messages", "quickReplies", "prompt", "statusText", "errorText", "testIdPrefix", "panelTestId", "submitTestId", "applyTestId", "primaryAction", "secondaryActions", "governanceActions", "busy", "canSubmit", "canApply", "submitOnEnter", "showAttachAction", "enablePastedAttachments", "enableFileAttachments", "attachmentAccept", "attachmentMultiple", "draggable", "resizable", "minWidth", "minHeight", "margin", "layout"], outputs: ["promptChange", "submitPrompt", "apply", "retryTurn", "cancelTurn", "shellAction", "close", "attach", "attachmentsPasted", "attachmentsSelected", "removeAttachment", "messageAction", "editMessage", "resendMessage", "quickReply", "layoutChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4498
+ `, isInline: true, styles: [".praxis-tabs-root{position:relative;display:block}.praxis-tabs-group.align-start .mat-mdc-tab-header{justify-content:flex-start}.praxis-tabs-group.align-center .mat-mdc-tab-header{justify-content:center}.praxis-tabs-group.align-end .mat-mdc-tab-header{justify-content:flex-end}.density-compact .mat-mdc-tab-body-content{padding:8px}.density-comfortable .mat-mdc-tab-body-content{padding:16px}.density-spacious .mat-mdc-tab-body-content{padding:24px}.tabs-ai-assistant{position:absolute;right:12px;bottom:72px;z-index:3}.tabs-ai-assistant-trigger{box-shadow:var(--md-sys-elevation-level2)}.edit-fab{position:absolute;right:12px;bottom:12px;z-index:2}.edit-fab-secondary{right:56px}.tab-empty{padding:16px;color:var(--md-sys-color-on-surface-variant);font-style:italic}.high-contrast{filter:contrast(1.2)}.reduce-motion{--mat-animation-duration: 0ms}.drag-handle{display:inline-flex;align-items:center;vertical-align:middle;margin-right:4px;cursor:grab}.tab-label-icon{font-size:18px;width:18px;height:18px;margin-right:6px;vertical-align:middle}:host-context(.pdx-gridster-item) .praxis-tabs-root{display:flex;flex-direction:column;height:100%;min-height:0}:host-context(.pdx-gridster-item) .praxis-tabs-group,:host-context(.pdx-gridster-item) .mat-mdc-tab-group{flex:1 1 auto;min-height:0}:host-context(.pdx-gridster-item) .mat-mdc-tab-body-wrapper,:host-context(.pdx-gridster-item) .mat-mdc-tab-body-content{height:100%;min-height:0}:host-context(.pdx-gridster-item) .mat-mdc-tab-body-content{overflow:auto}:host-context(.pdx-gridster-item) .praxis-tabnav-content{flex:1 1 auto;min-height:0;overflow:auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2_1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2_1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i3.MatTabLabel, selector: "[mat-tab-label], [matTabLabel]" }, { kind: "component", type: i3.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i3.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "component", type: i3.MatTabNav, selector: "[mat-tab-nav-bar]", inputs: ["fitInkBarToContent", "mat-stretch-tabs", "animationDuration", "backgroundColor", "disableRipple", "color", "tabPanel"], exportAs: ["matTabNavBar", "matTabNav"] }, { kind: "component", type: i3.MatTabNavPanel, selector: "mat-tab-nav-panel", inputs: ["id"], exportAs: ["matTabNavPanel"] }, { kind: "component", type: i3.MatTabLink, selector: "[mat-tab-link], [matTabLink]", inputs: ["active", "disabled", "disableRipple", "tabIndex", "id"], exportAs: ["matTabLink"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i10.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i6.MatMiniFabButton, selector: "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i6.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i6.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i9.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i9.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i9.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline", "tone"] }, { 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: "directive", type: DynamicWidgetLoaderDirective, selector: "[dynamicWidgetLoader]", inputs: ["dynamicWidgetLoader", "ownerWidgetKey", "context", "strictValidation", "autoWireOutputs"], outputs: ["widgetEvent", "widgetDiagnostic"], exportAs: ["dynamicWidgetLoader"] }, { kind: "component", type: PraxisAiAssistantShellComponent, selector: "praxis-ai-assistant-shell", inputs: ["labels", "mode", "state", "contextItems", "attachments", "messages", "quickReplies", "prompt", "statusText", "errorText", "testIdPrefix", "panelTestId", "submitTestId", "applyTestId", "primaryAction", "secondaryActions", "governanceActions", "busy", "canSubmit", "canApply", "submitOnEnter", "showAttachAction", "enablePastedAttachments", "enableFileAttachments", "attachmentAccept", "attachmentMultiple", "draggable", "resizable", "minWidth", "minHeight", "margin", "layout"], outputs: ["promptChange", "submitPrompt", "apply", "retryTurn", "cancelTurn", "shellAction", "close", "attach", "attachmentsPasted", "attachmentsSelected", "removeAttachment", "messageAction", "editMessage", "resendMessage", "quickReply", "layoutChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4430
4499
  }
4431
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisTabs, decorators: [{
4500
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisTabs, decorators: [{
4432
4501
  type: Component,
4433
4502
  args: [{ selector: 'praxis-tabs', standalone: true, providers: [providePraxisI18nConfig(PRAXIS_TABS_I18N_CONFIG)], imports: [
4434
4503
  CommonModule,
@@ -4453,60 +4522,67 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
4453
4522
  [class.reduce-motion]="config?.accessibility?.reduceMotion"
4454
4523
  [ngClass]="config?.appearance?.themeClass || ''"
4455
4524
  [attr.data-tabs-id]="styleScopeId()"
4456
- >
4525
+ >
4457
4526
  <!-- Inline custom CSS, if provided -->
4458
- <style *ngIf="safeCustomCss() as css" [innerHTML]="css"></style>
4527
+ @if (safeCustomCss(); as css) {
4528
+ <style [innerHTML]="css"></style>
4529
+ }
4459
4530
  <!-- Runtime style derived from tokens -->
4460
- <style *ngIf="styleCss() as s" [innerHTML]="s"></style>
4461
-
4462
- <div class="tabs-ai-assistant" *ngIf="enableCustomization">
4463
- <button
4464
- mat-mini-fab
4465
- type="button"
4466
- color="primary"
4467
- class="tabs-ai-assistant-trigger"
4468
- (click)="openAiAssistant()"
4469
- matTooltip="Copiloto semantico Praxis"
4470
- aria-label="Abrir copiloto semantico Praxis das abas"
4471
- data-testid="praxis-tabs-ai-assistant-trigger"
4472
- >
4473
- <mat-icon [praxisIcon]="'auto_awesome'"></mat-icon>
4474
- </button>
4475
- </div>
4476
-
4477
- <praxis-ai-assistant-shell
4478
- *ngIf="aiAssistantOpen && aiAssistantViewState"
4479
- [labels]="aiAssistantLabels"
4480
- [mode]="aiAssistantViewState.mode"
4481
- [state]="aiAssistantViewState.state"
4482
- [contextItems]="aiAssistantViewState.contextItems"
4483
- [attachments]="aiAssistantViewState.attachments"
4484
- [messages]="aiAssistantViewState.messages"
4485
- [quickReplies]="aiAssistantViewState.quickReplies"
4486
- [prompt]="aiAssistantPrompt"
4487
- [statusText]="aiAssistantViewState.statusText"
4488
- [errorText]="aiAssistantViewState.errorText"
4489
- [busy]="aiAssistantViewState.state === 'processing' || aiAssistantViewState.state === 'applying'"
4490
- [canApply]="aiAssistantViewState.canApply"
4491
- [layout]="aiAssistantLayout"
4492
- testIdPrefix="praxis-tabs-ai-assistant"
4493
- panelTestId="praxis-tabs-ai-assistant-panel"
4494
- submitTestId="praxis-tabs-ai-assistant-submit"
4495
- applyTestId="praxis-tabs-ai-assistant-apply"
4496
- (promptChange)="onAiAssistantPromptChange($event)"
4497
- (submitPrompt)="onAiAssistantSubmit($event)"
4498
- (apply)="onAiAssistantApply()"
4499
- (retryTurn)="onAiAssistantRetry()"
4500
- (cancelTurn)="onAiAssistantCancel()"
4501
- (quickReply)="onAiAssistantQuickReply($event)"
4502
- (editMessage)="onAiAssistantEditMessage($event)"
4503
- (resendMessage)="onAiAssistantResendMessage($event)"
4504
- (layoutChange)="onAiAssistantLayoutChange($event)"
4505
- (close)="closeAiAssistant()"
4506
- ></praxis-ai-assistant-shell>
4507
-
4531
+ @if (styleCss(); as s) {
4532
+ <style [innerHTML]="s"></style>
4533
+ }
4534
+
4535
+ @if (enableCustomization) {
4536
+ <div class="tabs-ai-assistant">
4537
+ <button
4538
+ mat-mini-fab
4539
+ type="button"
4540
+ color="primary"
4541
+ class="tabs-ai-assistant-trigger"
4542
+ (click)="openAiAssistant()"
4543
+ matTooltip="Copiloto semantico Praxis"
4544
+ aria-label="Abrir copiloto semantico Praxis das abas"
4545
+ data-testid="praxis-tabs-ai-assistant-trigger"
4546
+ >
4547
+ <mat-icon [praxisIcon]="'auto_awesome'"></mat-icon>
4548
+ </button>
4549
+ </div>
4550
+ }
4551
+
4552
+ @if (aiAssistantOpen && aiAssistantViewState) {
4553
+ <praxis-ai-assistant-shell
4554
+ [labels]="aiAssistantLabels"
4555
+ [mode]="aiAssistantViewState.mode"
4556
+ [state]="aiAssistantViewState.state"
4557
+ [contextItems]="aiAssistantViewState.contextItems"
4558
+ [attachments]="aiAssistantViewState.attachments"
4559
+ [messages]="aiAssistantViewState.messages"
4560
+ [quickReplies]="aiAssistantViewState.quickReplies"
4561
+ [prompt]="aiAssistantPrompt"
4562
+ [statusText]="aiAssistantViewState.statusText"
4563
+ [errorText]="aiAssistantViewState.errorText"
4564
+ [busy]="aiAssistantViewState.state === 'processing' || aiAssistantViewState.state === 'applying'"
4565
+ [canApply]="aiAssistantViewState.canApply"
4566
+ [layout]="aiAssistantLayout"
4567
+ testIdPrefix="praxis-tabs-ai-assistant"
4568
+ panelTestId="praxis-tabs-ai-assistant-panel"
4569
+ submitTestId="praxis-tabs-ai-assistant-submit"
4570
+ applyTestId="praxis-tabs-ai-assistant-apply"
4571
+ (promptChange)="onAiAssistantPromptChange($event)"
4572
+ (submitPrompt)="onAiAssistantSubmit($event)"
4573
+ (apply)="onAiAssistantApply()"
4574
+ (retryTurn)="onAiAssistantRetry()"
4575
+ (cancelTurn)="onAiAssistantCancel()"
4576
+ (quickReply)="onAiAssistantQuickReply($event)"
4577
+ (editMessage)="onAiAssistantEditMessage($event)"
4578
+ (resendMessage)="onAiAssistantResendMessage($event)"
4579
+ (layoutChange)="onAiAssistantLayoutChange($event)"
4580
+ (close)="closeAiAssistant()"
4581
+ ></praxis-ai-assistant-shell>
4582
+ }
4583
+
4508
4584
  <!-- Empty state (global) -->
4509
- <ng-container *ngIf="isEmptyGlobal(); else notEmpty">
4585
+ @if (isEmptyGlobal()) {
4510
4586
  <praxis-empty-state-card
4511
4587
  icon="tab"
4512
4588
  [title]="t('emptyState.title', 'Nenhuma aba configurada')"
@@ -4517,214 +4593,218 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
4517
4593
  { label: t('emptyState.secondaryOpenEditor', 'Abrir editor completo'), icon: 'tune', action: openEditor.bind(this) }
4518
4594
  ]"
4519
4595
  ></praxis-empty-state-card>
4520
- </ng-container>
4521
-
4522
- <ng-template #notEmpty>
4523
- <!-- Tab Nav variant -->
4524
- <ng-container *ngIf="isNavMode(); else groupMode">
4525
- <nav
4526
- mat-tab-nav-bar
4527
- [tabPanel]="tabPanel"
4528
- cdkDropList
4529
- cdkDropListOrientation="horizontal"
4530
- [cdkDropListDisabled]="!config?.behavior?.reorderable"
4531
- (cdkDropListDropped)="onVisibleNavDrop($event)"
4532
- [disablePagination]="config?.nav?.disablePagination"
4533
- [fitInkBarToContent]="config?.nav?.fitInkBarToContent"
4534
- [mat-stretch-tabs]="config?.nav?.stretchTabs"
4535
- [color]="config?.nav?.color"
4536
- [backgroundColor]="config?.nav?.backgroundColor"
4537
- [selectedIndex]="selectedVisibleNavIndex()"
4538
- [attr.aria-label]="config?.nav?.ariaLabel || config?.group?.ariaLabel || null"
4539
- [attr.aria-labelledby]="config?.nav?.ariaLabelledby || config?.group?.ariaLabelledby || null"
4540
- [animationDuration]="effectiveAnimationDuration()"
4541
- (indexFocused)="indexFocused.emit($event)"
4542
- (selectFocusedIndex)="selectFocusedIndex.emit($event)"
4543
- >
4544
- <a
4545
- mat-tab-link
4546
- *ngFor="let entry of visibleNavLinkEntries(); let i = index; trackBy: trackVisibleNavLink"
4547
- cdkDrag
4548
- [cdkDragDisabled]="!config?.behavior?.reorderable"
4549
- cdkDragLockAxis="x"
4550
- [active]="getNavActive(entry.index)"
4551
- [disabled]="entry.link.disabled"
4552
- [disableRipple]="config?.nav?.disableRipple || entry.link.disableRipple"
4553
- [fitInkBarToContent]="entry.link.fitInkBarToContent || false"
4554
- [id]="entry.link.id || ''"
4555
- (click)="onNavClick(entry.index)"
4556
- >
4557
- <span *ngIf="config?.behavior?.reorderable" class="drag-handle" cdkDragHandle>
4558
- <mat-icon fontIcon="drag_indicator"></mat-icon>
4559
- </span>
4560
- <mat-icon *ngIf="entry.link.icon" class="tab-label-icon" [praxisIcon]="entry.link.icon"></mat-icon>
4561
- {{ entry.link.label }}
4562
- </a>
4563
- </nav>
4564
-
4565
- <mat-tab-nav-panel #tabPanel>
4566
- <div class="praxis-tabnav-content" *ngIf="currentNavIndex() >= 0">
4567
- <ng-container
4568
- *ngIf="config?.nav?.links?.[currentNavIndex()] as l"
4596
+ } @else {
4597
+ <!-- Tab Nav variant -->
4598
+ @if (isNavMode()) {
4599
+ <nav
4600
+ mat-tab-nav-bar
4601
+ [tabPanel]="tabPanel"
4602
+ cdkDropList
4603
+ cdkDropListOrientation="horizontal"
4604
+ [cdkDropListDisabled]="!config?.behavior?.reorderable"
4605
+ (cdkDropListDropped)="onVisibleNavDrop($event)"
4606
+ [disablePagination]="config?.nav?.disablePagination"
4607
+ [fitInkBarToContent]="config?.nav?.fitInkBarToContent"
4608
+ [mat-stretch-tabs]="config?.nav?.stretchTabs"
4609
+ [color]="config?.nav?.color"
4610
+ [backgroundColor]="config?.nav?.backgroundColor"
4611
+ [selectedIndex]="selectedVisibleNavIndex()"
4612
+ [attr.aria-label]="config?.nav?.ariaLabel || config?.group?.ariaLabel || null"
4613
+ [attr.aria-labelledby]="config?.nav?.ariaLabelledby || config?.group?.ariaLabelledby || null"
4614
+ [animationDuration]="effectiveAnimationDuration()"
4615
+ (indexFocused)="indexFocused.emit($event)"
4616
+ (selectFocusedIndex)="selectFocusedIndex.emit($event)"
4569
4617
  >
4570
- <ng-container *ngIf="(l.content?.length || safeWidgetDefinitions(l.widgets).length) && navContentReady(currentNavIndex()); else emptyNav">
4571
- <ng-container *ngIf="l.content">
4572
- <ng-container
4573
- dynamicFieldLoader
4574
- [fields]="l.content || []"
4575
- [formGroup]="effectiveContentForm()"
4576
- ></ng-container>
4577
- </ng-container>
4578
- <ng-container *ngIf="safeWidgetDefinitions(l.widgets).length">
4579
- <ng-container *ngFor="let w of safeWidgetDefinitions(l.widgets); let wi = index; trackBy: trackWidgetDefinition">
4580
- <ng-container
4581
- [dynamicWidgetLoader]="resolveWidgetDefinition(w)"
4582
- [context]="context || {}"
4583
- (widgetEvent)="emitWidgetEvent(linkEventPath(l.id, currentNavIndex()), $event)"
4584
- ></ng-container>
4585
- </ng-container>
4586
- </ng-container>
4587
- </ng-container>
4588
- <ng-template #emptyNav>
4589
- <praxis-empty-state-card
4590
- [inline]="true"
4591
- icon="add_to_queue"
4592
- [title]="t('emptyState.navTitle', 'Sem conteudo neste link')"
4593
- [description]="t('emptyState.navDescription', 'Adicione conteudo ou use o editor para configurar.')"
4594
- [primaryAction]="{ label: t('emptyState.openEditor', 'Abrir editor'), icon: 'tune', action: openEditor.bind(this) }"
4595
- ></praxis-empty-state-card>
4596
- </ng-template>
4597
- </ng-container>
4598
- </div>
4599
- </mat-tab-nav-panel>
4600
- </ng-container>
4601
-
4602
- <!-- Tab Group variant -->
4603
- <ng-template #groupMode>
4604
- <mat-tab-group
4605
- [dynamicHeight]="config?.group?.dynamicHeight"
4606
- [disableRipple]="config?.group?.disableRipple"
4607
- [disablePagination]="config?.group?.disablePagination"
4608
- [fitInkBarToContent]="config?.group?.fitInkBarToContent"
4609
- [headerPosition]="config?.group?.headerPosition ?? 'above'"
4610
- [preserveContent]="config?.group?.preserveContent"
4611
- [selectedIndex]="selectedVisibleTabIndex()"
4612
- [mat-stretch-tabs]="config?.group?.stretchTabs"
4613
- [mat-align-tabs]="config?.group?.alignTabs || 'start'"
4614
- [color]="config?.group?.color"
4615
- [backgroundColor]="config?.group?.backgroundColor"
4616
- [animationDuration]="effectiveAnimationDuration()"
4617
- [attr.aria-label]="config?.group?.ariaLabel || null"
4618
- [attr.aria-labelledby]="config?.group?.ariaLabelledby || null"
4619
- (animationDone)="animationDone.emit()"
4620
- (focusChange)="focusChange.emit($event)"
4621
- (selectedIndexChange)="onVisibleTabIndexChange($event)"
4622
- (selectedTabChange)="selectedTabChange.emit($event)"
4623
- class="praxis-tabs-group"
4624
- >
4625
- <mat-tab
4626
- *ngFor="let entry of visibleTabEntries(); let i = index; trackBy: trackVisibleTab"
4627
- [disabled]="entry.tab.disabled"
4628
- [labelClass]="entry.tab.labelClass ?? ''"
4629
- [bodyClass]="entry.tab.bodyClass ?? ''"
4630
- [id]="entry.tab.id || ''"
4631
- [attr.aria-label]="entry.tab.ariaLabel || null"
4632
- [attr.aria-labelledby]="entry.tab.ariaLabelledby || null"
4633
- >
4634
- <ng-template mat-tab-label>
4635
- <mat-icon *ngIf="entry.tab.icon" class="tab-label-icon" [praxisIcon]="entry.tab.icon"></mat-icon>
4636
- <span>{{ entry.tab.textLabel }}</span>
4637
- <button
4638
- *ngIf="config?.behavior?.closeable"
4639
- mat-icon-button
4640
- type="button"
4641
- (click)="closeTab(entry.index); $event.stopPropagation()"
4642
- (keydown.enter)="$event.stopPropagation()"
4643
- (keydown.space)="$event.stopPropagation()"
4644
- [attr.aria-label]="t('chrome.closeTab', 'Fechar aba')"
4645
- >
4646
- <mat-icon fontIcon="close"></mat-icon>
4647
- </button>
4648
- <ng-container *ngIf="config?.behavior?.reorderable">
4649
- <button
4650
- mat-icon-button
4651
- type="button"
4652
- (click)="moveTab(entry.index, -1); $event.stopPropagation()"
4653
- (keydown.enter)="$event.stopPropagation()"
4654
- (keydown.space)="$event.stopPropagation()"
4655
- [disabled]="entry.index===0"
4656
- [attr.aria-label]="t('chrome.moveTabLeft', 'Mover aba para esquerda')"
4618
+ @for (entry of visibleNavLinkEntries(); track trackVisibleNavLink(i, entry); let i = $index) {
4619
+ <a
4620
+ mat-tab-link
4621
+ cdkDrag
4622
+ [cdkDragDisabled]="!config?.behavior?.reorderable"
4623
+ cdkDragLockAxis="x"
4624
+ [active]="getNavActive(entry.index)"
4625
+ [disabled]="entry.link.disabled"
4626
+ [disableRipple]="config?.nav?.disableRipple || entry.link.disableRipple"
4627
+ [fitInkBarToContent]="entry.link.fitInkBarToContent || false"
4628
+ [id]="entry.link.id || ''"
4629
+ (click)="onNavClick(entry.index)"
4657
4630
  >
4658
- <mat-icon fontIcon="arrow_back"></mat-icon>
4659
- </button>
4660
- <button
4661
- mat-icon-button
4662
- type="button"
4663
- (click)="moveTab(entry.index, 1); $event.stopPropagation()"
4664
- (keydown.enter)="$event.stopPropagation()"
4665
- (keydown.space)="$event.stopPropagation()"
4666
- [disabled]="entry.index===(config?.tabs?.length||1)-1"
4667
- [attr.aria-label]="t('chrome.moveTabRight', 'Mover aba para direita')"
4631
+ @if (config?.behavior?.reorderable) {
4632
+ <span class="drag-handle" cdkDragHandle>
4633
+ <mat-icon fontIcon="drag_indicator"></mat-icon>
4634
+ </span>
4635
+ }
4636
+ @if (entry.link.icon) {
4637
+ <mat-icon class="tab-label-icon" [praxisIcon]="entry.link.icon"></mat-icon>
4638
+ }
4639
+ {{ entry.link.label }}
4640
+ </a>
4641
+ }
4642
+ </nav>
4643
+ <mat-tab-nav-panel #tabPanel>
4644
+ @if (currentNavIndex() >= 0) {
4645
+ <div class="praxis-tabnav-content">
4646
+ @if (config?.nav?.links?.[currentNavIndex()]; as l) {
4647
+ @if ((l.content?.length || safeWidgetDefinitions(l.widgets).length) && navContentReady(currentNavIndex())) {
4648
+ @if (l.content) {
4649
+ <ng-container
4650
+ dynamicFieldLoader
4651
+ [fields]="l.content || []"
4652
+ [formGroup]="effectiveContentForm()"
4653
+ ></ng-container>
4654
+ }
4655
+ @if (safeWidgetDefinitions(l.widgets).length) {
4656
+ @for (w of safeWidgetDefinitions(l.widgets); track trackWidgetDefinition(wi, w); let wi = $index) {
4657
+ <ng-container
4658
+ [dynamicWidgetLoader]="resolveWidgetDefinition(w)"
4659
+ [context]="context || {}"
4660
+ (widgetEvent)="emitWidgetEvent(linkEventPath(l.id, currentNavIndex()), $event)"
4661
+ ></ng-container>
4662
+ }
4663
+ }
4664
+ } @else {
4665
+ <praxis-empty-state-card
4666
+ [inline]="true"
4667
+ icon="add_to_queue"
4668
+ [title]="t('emptyState.navTitle', 'Sem conteudo neste link')"
4669
+ [description]="t('emptyState.navDescription', 'Adicione conteudo ou use o editor para configurar.')"
4670
+ [primaryAction]="{ label: t('emptyState.openEditor', 'Abrir editor'), icon: 'tune', action: openEditor.bind(this) }"
4671
+ ></praxis-empty-state-card>
4672
+ }
4673
+ }
4674
+ </div>
4675
+ }
4676
+ </mat-tab-nav-panel>
4677
+ } @else {
4678
+ <mat-tab-group
4679
+ [dynamicHeight]="config?.group?.dynamicHeight"
4680
+ [disableRipple]="config?.group?.disableRipple"
4681
+ [disablePagination]="config?.group?.disablePagination"
4682
+ [fitInkBarToContent]="config?.group?.fitInkBarToContent"
4683
+ [headerPosition]="config?.group?.headerPosition ?? 'above'"
4684
+ [preserveContent]="config?.group?.preserveContent"
4685
+ [selectedIndex]="selectedVisibleTabIndex()"
4686
+ [mat-stretch-tabs]="config?.group?.stretchTabs"
4687
+ [mat-align-tabs]="config?.group?.alignTabs || 'start'"
4688
+ [color]="config?.group?.color"
4689
+ [backgroundColor]="config?.group?.backgroundColor"
4690
+ [animationDuration]="effectiveAnimationDuration()"
4691
+ [attr.aria-label]="config?.group?.ariaLabel || null"
4692
+ [attr.aria-labelledby]="config?.group?.ariaLabelledby || null"
4693
+ (animationDone)="animationDone.emit()"
4694
+ (focusChange)="focusChange.emit($event)"
4695
+ (selectedIndexChange)="onVisibleTabIndexChange($event)"
4696
+ (selectedTabChange)="selectedTabChange.emit($event)"
4697
+ class="praxis-tabs-group"
4698
+ >
4699
+ @for (entry of visibleTabEntries(); track trackVisibleTab(i, entry); let i = $index) {
4700
+ <mat-tab
4701
+ [disabled]="entry.tab.disabled"
4702
+ [labelClass]="entry.tab.labelClass ?? ''"
4703
+ [bodyClass]="entry.tab.bodyClass ?? ''"
4704
+ [id]="entry.tab.id || ''"
4705
+ [attr.aria-label]="entry.tab.ariaLabel || null"
4706
+ [attr.aria-labelledby]="entry.tab.ariaLabelledby || null"
4668
4707
  >
4669
- <mat-icon fontIcon="arrow_forward"></mat-icon>
4670
- </button>
4671
- </ng-container>
4672
- </ng-template>
4673
-
4674
- <ng-container *ngIf="(entry.tab.content?.length || safeWidgetDefinitions(entry.tab.widgets).length) && groupContentReady(entry.index); else emptyTab">
4675
- <ng-container *ngIf="entry.tab.content">
4676
- <ng-container
4677
- dynamicFieldLoader
4678
- [fields]="entry.tab.content || []"
4679
- [formGroup]="effectiveContentForm()"
4680
- ></ng-container>
4681
- </ng-container>
4682
- <ng-container *ngIf="safeWidgetDefinitions(entry.tab.widgets).length">
4683
- <ng-container *ngFor="let w of safeWidgetDefinitions(entry.tab.widgets); let wi = index; trackBy: trackWidgetDefinition">
4684
- <ng-container
4685
- [dynamicWidgetLoader]="resolveWidgetDefinition(w)"
4686
- [context]="context || {}"
4687
- (widgetEvent)="emitWidgetEvent(tabEventPath(entry.tab.id, entry.index), $event)"
4688
- ></ng-container>
4689
- </ng-container>
4690
- </ng-container>
4691
- </ng-container>
4692
- <ng-template #emptyTab>
4693
- <praxis-empty-state-card
4694
- [inline]="true"
4695
- icon="dashboard_customize"
4696
- [title]="t('emptyState.tabTitle', 'Sem conteudo nesta aba')"
4697
- [description]="t('emptyState.tabDescription', 'Adicione conteudo ou use o editor para configurar.')"
4698
- [primaryAction]="{ label: t('emptyState.openEditor', 'Abrir editor'), icon: 'tune', action: openEditor.bind(this) }"
4699
- ></praxis-empty-state-card>
4700
- </ng-template>
4701
- </mat-tab>
4702
- </mat-tab-group>
4703
- </ng-template>
4704
- </ng-template>
4705
-
4708
+ <ng-template mat-tab-label>
4709
+ @if (entry.tab.icon) {
4710
+ <mat-icon class="tab-label-icon" [praxisIcon]="entry.tab.icon"></mat-icon>
4711
+ }
4712
+ <span>{{ entry.tab.textLabel }}</span>
4713
+ @if (config?.behavior?.closeable) {
4714
+ <button
4715
+ mat-icon-button
4716
+ type="button"
4717
+ (click)="closeTab(entry.index); $event.stopPropagation()"
4718
+ (keydown.enter)="$event.stopPropagation()"
4719
+ (keydown.space)="$event.stopPropagation()"
4720
+ [attr.aria-label]="t('chrome.closeTab', 'Fechar aba')"
4721
+ >
4722
+ <mat-icon fontIcon="close"></mat-icon>
4723
+ </button>
4724
+ }
4725
+ @if (config?.behavior?.reorderable) {
4726
+ <button
4727
+ mat-icon-button
4728
+ type="button"
4729
+ (click)="moveTab(entry.index, -1); $event.stopPropagation()"
4730
+ (keydown.enter)="$event.stopPropagation()"
4731
+ (keydown.space)="$event.stopPropagation()"
4732
+ [disabled]="entry.index===0"
4733
+ [attr.aria-label]="t('chrome.moveTabLeft', 'Mover aba para esquerda')"
4734
+ >
4735
+ <mat-icon fontIcon="arrow_back"></mat-icon>
4736
+ </button>
4737
+ <button
4738
+ mat-icon-button
4739
+ type="button"
4740
+ (click)="moveTab(entry.index, 1); $event.stopPropagation()"
4741
+ (keydown.enter)="$event.stopPropagation()"
4742
+ (keydown.space)="$event.stopPropagation()"
4743
+ [disabled]="entry.index===(config?.tabs?.length||1)-1"
4744
+ [attr.aria-label]="t('chrome.moveTabRight', 'Mover aba para direita')"
4745
+ >
4746
+ <mat-icon fontIcon="arrow_forward"></mat-icon>
4747
+ </button>
4748
+ }
4749
+ </ng-template>
4750
+ @if ((entry.tab.content?.length || safeWidgetDefinitions(entry.tab.widgets).length) && groupContentReady(entry.index)) {
4751
+ @if (entry.tab.content) {
4752
+ <ng-container
4753
+ dynamicFieldLoader
4754
+ [fields]="entry.tab.content || []"
4755
+ [formGroup]="effectiveContentForm()"
4756
+ ></ng-container>
4757
+ }
4758
+ @if (safeWidgetDefinitions(entry.tab.widgets).length) {
4759
+ @for (w of safeWidgetDefinitions(entry.tab.widgets); track trackWidgetDefinition(wi, w); let wi = $index) {
4760
+ <ng-container
4761
+ [dynamicWidgetLoader]="resolveWidgetDefinition(w)"
4762
+ [context]="context || {}"
4763
+ (widgetEvent)="emitWidgetEvent(tabEventPath(entry.tab.id, entry.index), $event)"
4764
+ ></ng-container>
4765
+ }
4766
+ }
4767
+ } @else {
4768
+ <praxis-empty-state-card
4769
+ [inline]="true"
4770
+ icon="dashboard_customize"
4771
+ [title]="t('emptyState.tabTitle', 'Sem conteudo nesta aba')"
4772
+ [description]="t('emptyState.tabDescription', 'Adicione conteudo ou use o editor para configurar.')"
4773
+ [primaryAction]="{ label: t('emptyState.openEditor', 'Abrir editor'), icon: 'tune', action: openEditor.bind(this) }"
4774
+ ></praxis-empty-state-card>
4775
+ }
4776
+ </mat-tab>
4777
+ }
4778
+ </mat-tab-group>
4779
+ }
4780
+ <!-- Tab Group variant -->
4781
+ }
4782
+
4783
+
4706
4784
  <!-- Edit button -->
4707
- <button
4708
- *ngIf="enableCustomization"
4709
- mat-fab
4710
- class="edit-fab"
4711
- [attr.aria-label]="t('chrome.editTabs', 'Editar abas')"
4712
- (click)="openEditor()"
4713
- >
4714
- <mat-icon fontIcon="edit"></mat-icon>
4715
- </button>
4716
- <button
4717
- *ngIf="enableCustomization && tabsId"
4718
- mat-mini-fab
4719
- class="edit-fab edit-fab-secondary"
4720
- [attr.aria-label]="t('settings.resetPreferences', 'Redefinir preferencias de abas')"
4721
- (click)="resetPreferences()"
4722
- [matTooltip]="t('settings.resetPreferences', 'Redefinir preferencias de abas')"
4723
- >
4724
- <mat-icon [praxisIcon]="'restart_alt'"></mat-icon>
4725
- </button>
4785
+ @if (enableCustomization) {
4786
+ <button
4787
+ mat-fab
4788
+ class="edit-fab"
4789
+ [attr.aria-label]="t('chrome.editTabs', 'Editar abas')"
4790
+ (click)="openEditor()"
4791
+ >
4792
+ <mat-icon fontIcon="edit"></mat-icon>
4793
+ </button>
4794
+ }
4795
+ @if (enableCustomization && tabsId) {
4796
+ <button
4797
+ mat-mini-fab
4798
+ class="edit-fab edit-fab-secondary"
4799
+ [attr.aria-label]="t('settings.resetPreferences', 'Redefinir preferencias de abas')"
4800
+ (click)="resetPreferences()"
4801
+ [matTooltip]="t('settings.resetPreferences', 'Redefinir preferencias de abas')"
4802
+ >
4803
+ <mat-icon [praxisIcon]="'restart_alt'"></mat-icon>
4804
+ </button>
4805
+ }
4726
4806
  </div>
4727
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".praxis-tabs-root{position:relative;display:block}.praxis-tabs-group.align-start .mat-mdc-tab-header{justify-content:flex-start}.praxis-tabs-group.align-center .mat-mdc-tab-header{justify-content:center}.praxis-tabs-group.align-end .mat-mdc-tab-header{justify-content:flex-end}.density-compact .mat-mdc-tab-body-content{padding:8px}.density-comfortable .mat-mdc-tab-body-content{padding:16px}.density-spacious .mat-mdc-tab-body-content{padding:24px}.tabs-ai-assistant{position:absolute;right:12px;bottom:72px;z-index:3}.tabs-ai-assistant-trigger{box-shadow:var(--md-sys-elevation-level2)}.edit-fab{position:absolute;right:12px;bottom:12px;z-index:2}.edit-fab-secondary{right:56px}.tab-empty{padding:16px;color:var(--md-sys-color-on-surface-variant);font-style:italic}.high-contrast{filter:contrast(1.2)}.reduce-motion{--mat-animation-duration: 0ms}.drag-handle{display:inline-flex;align-items:center;vertical-align:middle;margin-right:4px;cursor:grab}.tab-label-icon{font-size:18px;width:18px;height:18px;margin-right:6px;vertical-align:middle}:host-context(.pdx-gridster-item) .praxis-tabs-root{display:flex;flex-direction:column;height:100%;min-height:0}:host-context(.pdx-gridster-item) .praxis-tabs-group,:host-context(.pdx-gridster-item) .mat-mdc-tab-group{flex:1 1 auto;min-height:0}:host-context(.pdx-gridster-item) .mat-mdc-tab-body-wrapper,:host-context(.pdx-gridster-item) .mat-mdc-tab-body-content{height:100%;min-height:0}:host-context(.pdx-gridster-item) .mat-mdc-tab-body-content{overflow:auto}:host-context(.pdx-gridster-item) .praxis-tabnav-content{flex:1 1 auto;min-height:0;overflow:auto}\n"] }]
4807
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".praxis-tabs-root{position:relative;display:block}.praxis-tabs-group.align-start .mat-mdc-tab-header{justify-content:flex-start}.praxis-tabs-group.align-center .mat-mdc-tab-header{justify-content:center}.praxis-tabs-group.align-end .mat-mdc-tab-header{justify-content:flex-end}.density-compact .mat-mdc-tab-body-content{padding:8px}.density-comfortable .mat-mdc-tab-body-content{padding:16px}.density-spacious .mat-mdc-tab-body-content{padding:24px}.tabs-ai-assistant{position:absolute;right:12px;bottom:72px;z-index:3}.tabs-ai-assistant-trigger{box-shadow:var(--md-sys-elevation-level2)}.edit-fab{position:absolute;right:12px;bottom:12px;z-index:2}.edit-fab-secondary{right:56px}.tab-empty{padding:16px;color:var(--md-sys-color-on-surface-variant);font-style:italic}.high-contrast{filter:contrast(1.2)}.reduce-motion{--mat-animation-duration: 0ms}.drag-handle{display:inline-flex;align-items:center;vertical-align:middle;margin-right:4px;cursor:grab}.tab-label-icon{font-size:18px;width:18px;height:18px;margin-right:6px;vertical-align:middle}:host-context(.pdx-gridster-item) .praxis-tabs-root{display:flex;flex-direction:column;height:100%;min-height:0}:host-context(.pdx-gridster-item) .praxis-tabs-group,:host-context(.pdx-gridster-item) .mat-mdc-tab-group{flex:1 1 auto;min-height:0}:host-context(.pdx-gridster-item) .mat-mdc-tab-body-wrapper,:host-context(.pdx-gridster-item) .mat-mdc-tab-body-content{height:100%;min-height:0}:host-context(.pdx-gridster-item) .mat-mdc-tab-body-content{overflow:auto}:host-context(.pdx-gridster-item) .praxis-tabnav-content{flex:1 1 auto;min-height:0;overflow:auto}\n"] }]
4728
4808
  }], propDecorators: { config: [{
4729
4809
  type: Input
4730
4810
  }], tabsId: [{
@@ -4833,19 +4913,19 @@ class PraxisTabsWidgetConfigEditor {
4833
4913
  },
4834
4914
  });
4835
4915
  }
4836
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisTabsWidgetConfigEditor, deps: [], target: i0.ɵɵFactoryTarget.Component });
4837
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: PraxisTabsWidgetConfigEditor, isStandalone: true, selector: "praxis-tabs-widget-config-editor", inputs: { inputs: "inputs", widgetKey: "widgetKey" }, viewQueries: [{ propertyName: "tabsEditor", first: true, predicate: ["tabsEditor"], descendants: true }], ngImport: i0, template: `
4916
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisTabsWidgetConfigEditor, deps: [], target: i0.ɵɵFactoryTarget.Component });
4917
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.14", type: PraxisTabsWidgetConfigEditor, isStandalone: true, selector: "praxis-tabs-widget-config-editor", inputs: { inputs: "inputs", widgetKey: "widgetKey" }, viewQueries: [{ propertyName: "tabsEditor", first: true, predicate: ["tabsEditor"], descendants: true }], ngImport: i0, template: `
4838
4918
  <section data-testid="tabs-widget-config-editor">
4839
4919
  <praxis-tabs-config-editor #tabsEditor [document]="editorDocument" />
4840
4920
  </section>
4841
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: PraxisTabsConfigEditor, selector: "praxis-tabs-config-editor", inputs: ["document"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4921
+ `, isInline: true, dependencies: [{ kind: "component", type: PraxisTabsConfigEditor, selector: "praxis-tabs-config-editor", inputs: ["document"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4842
4922
  }
4843
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisTabsWidgetConfigEditor, decorators: [{
4923
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisTabsWidgetConfigEditor, decorators: [{
4844
4924
  type: Component,
4845
4925
  args: [{
4846
4926
  selector: 'praxis-tabs-widget-config-editor',
4847
4927
  standalone: true,
4848
- imports: [CommonModule, PraxisTabsConfigEditor],
4928
+ imports: [PraxisTabsConfigEditor],
4849
4929
  template: `
4850
4930
  <section data-testid="tabs-widget-config-editor">
4851
4931
  <praxis-tabs-config-editor #tabsEditor [document]="editorDocument" />