@praxisui/table 0.0.1 → 1.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/fesm2022/praxisui-table.mjs +52 -6
- package/fesm2022/praxisui-table.mjs.map +1 -1
- package/index.d.ts +5 -1
- package/package.json +14 -5
|
@@ -14286,7 +14286,14 @@ class PraxisTableConfigEditor {
|
|
|
14286
14286
|
panelRef;
|
|
14287
14287
|
behaviorEditor;
|
|
14288
14288
|
// Disponível somente quando a tabela estiver em um contexto CRUD
|
|
14289
|
+
set crudEditorSetter(ref) {
|
|
14290
|
+
this.crudEditor = ref;
|
|
14291
|
+
if (ref && !this._crudSubscribed) {
|
|
14292
|
+
this.subscribeCrudEditorChanges(ref);
|
|
14293
|
+
}
|
|
14294
|
+
}
|
|
14289
14295
|
crudEditor;
|
|
14296
|
+
_crudSubscribed = false;
|
|
14290
14297
|
sections = [
|
|
14291
14298
|
{ id: 'connect', label: 'Conexão', icon: 'link' },
|
|
14292
14299
|
{
|
|
@@ -14333,6 +14340,7 @@ class PraxisTableConfigEditor {
|
|
|
14333
14340
|
isValid$ = new BehaviorSubject(true);
|
|
14334
14341
|
isBusy$ = new BehaviorSubject(false);
|
|
14335
14342
|
columnMetas = [];
|
|
14343
|
+
hasCrudOverridesDirty = false;
|
|
14336
14344
|
constructor(cdr, configService, panelData, panelRef) {
|
|
14337
14345
|
this.cdr = cdr;
|
|
14338
14346
|
this.configService = configService;
|
|
@@ -14515,7 +14523,7 @@ class PraxisTableConfigEditor {
|
|
|
14515
14523
|
const normalizedEdited = this.normalizeTableConfig(this.editedConfig);
|
|
14516
14524
|
// Verificar se há alterações válidas usando comparação robusta
|
|
14517
14525
|
const resourceDirty = (this.resourcePath || '').trim() !== (this.initialResourcePath || '').trim();
|
|
14518
|
-
const hasChanges = resourceDirty || !this.deepEqual(normalizedOriginal, normalizedEdited);
|
|
14526
|
+
const hasChanges = resourceDirty || !this.deepEqual(normalizedOriginal, normalizedEdited) || this.hasCrudOverridesDirty;
|
|
14519
14527
|
const isValid = this.isValidJson;
|
|
14520
14528
|
const canSave = hasChanges && isValid;
|
|
14521
14529
|
const debug = (typeof window !== 'undefined' && window.DEBUG_PTABLE) === true;
|
|
@@ -14544,6 +14552,31 @@ class PraxisTableConfigEditor {
|
|
|
14544
14552
|
// isBusy$ será atualizado em operações específicas
|
|
14545
14553
|
this.cdr.markForCheck();
|
|
14546
14554
|
}
|
|
14555
|
+
subscribeCrudEditorChanges(ref) {
|
|
14556
|
+
try {
|
|
14557
|
+
const markDirty = () => {
|
|
14558
|
+
this.hasCrudOverridesDirty = true;
|
|
14559
|
+
// Não alteramos editedConfig; apenas marcamos estado para habilitar salvar
|
|
14560
|
+
this.updateCanSaveState();
|
|
14561
|
+
};
|
|
14562
|
+
if (ref?.defaultsForm?.valueChanges) {
|
|
14563
|
+
ref.defaultsForm.valueChanges.subscribe(markDirty);
|
|
14564
|
+
}
|
|
14565
|
+
if (ref?.actionForms?.valueChanges) {
|
|
14566
|
+
ref.actionForms.valueChanges.subscribe(markDirty);
|
|
14567
|
+
}
|
|
14568
|
+
// Também observar alterações por controle em caso de arrays dinâmicos
|
|
14569
|
+
if (Array.isArray(ref?.actionForms?.controls)) {
|
|
14570
|
+
for (const ctrl of ref.actionForms.controls) {
|
|
14571
|
+
ctrl.valueChanges?.subscribe?.(markDirty);
|
|
14572
|
+
}
|
|
14573
|
+
}
|
|
14574
|
+
this._crudSubscribed = true;
|
|
14575
|
+
}
|
|
14576
|
+
catch {
|
|
14577
|
+
// Se algo falhar, não bloqueia o editor
|
|
14578
|
+
}
|
|
14579
|
+
}
|
|
14547
14580
|
showSuccess(message) {
|
|
14548
14581
|
this.statusMessage = message;
|
|
14549
14582
|
this.hasSuccess = true;
|
|
@@ -14812,6 +14845,8 @@ class PraxisTableConfigEditor {
|
|
|
14812
14845
|
if (this.crudEditor && typeof this.crudEditor.saveOverrides === 'function') {
|
|
14813
14846
|
try {
|
|
14814
14847
|
this.crudEditor.saveOverrides();
|
|
14848
|
+
// Como as overrides foram salvas fora do TableConfig, limpar flag de dirty local
|
|
14849
|
+
this.hasCrudOverridesDirty = false;
|
|
14815
14850
|
}
|
|
14816
14851
|
catch (e) {
|
|
14817
14852
|
console.warn('[PraxisTableConfigEditor] CRUD overrides save failed', e);
|
|
@@ -14829,6 +14864,8 @@ class PraxisTableConfigEditor {
|
|
|
14829
14864
|
throw new Error('Configuração inválida');
|
|
14830
14865
|
}
|
|
14831
14866
|
this.showSuccess('Configurações salvas com sucesso!');
|
|
14867
|
+
// Recalcular estado de save após salvar overrides e config
|
|
14868
|
+
this.updateCanSaveState();
|
|
14832
14869
|
const value = this.getSettingsValue();
|
|
14833
14870
|
console.debug('[PraxisTableConfigEditor] onSave returning value', value);
|
|
14834
14871
|
// Importante: retornar o mesmo valor de getSettingsValue()
|
|
@@ -14983,7 +15020,7 @@ class PraxisTableConfigEditor {
|
|
|
14983
15020
|
this.isBusy$.complete();
|
|
14984
15021
|
}
|
|
14985
15022
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PraxisTableConfigEditor, deps: [{ token: i0.ChangeDetectorRef }, { token: i1$3.TableConfigService }, { token: SETTINGS_PANEL_DATA }, { token: SETTINGS_PANEL_REF, optional: true }], target: i0.ɵɵFactoryTarget.Component });
|
|
14986
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.4", type: PraxisTableConfigEditor, isStandalone: true, selector: "praxis-table-config-editor", providers: [TableConfigService], viewQueries: [{ propertyName: "behaviorEditor", first: true, predicate: BehaviorConfigEditorComponent, descendants: true }, { propertyName: "crudEditor", first: true, predicate: ["crudEditorRef"], descendants: true }], ngImport: i0, template: " <mat-tab-group class=\"config-tabs\" [(selectedIndex)]=\"activeSectionIndex\">\n <mat-tab *ngFor=\"let section of sections\">\n <ng-template mat-tab-label>\n <mat-icon *ngIf=\"section.icon\" [praxisIcon]=\"section.icon\"></mat-icon>\n <span>{{ section.label }}</span>\n </ng-template>\n <div class=\"tab-content\">\n <ng-container [ngSwitch]=\"section.id\">\n <div *ngSwitchCase=\"'connect'\" style=\"display:grid; gap:12px; padding: 8px; grid-template-columns: 1fr 240px; align-items: start;\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Recurso</mat-label>\n <input matInput [(ngModel)]=\"resourcePath\" (ngModelChange)=\"onResourcePathChange($event)\" placeholder=\"ex.: retaguarda/parametros\" />\n <mat-icon matSuffix>link</mat-icon>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Chave prim\u00E1ria</mat-label>\n <input matInput [(ngModel)]=\"idField\" (ngModelChange)=\"onIdFieldChange($event)\" placeholder=\"ex.: id, uuid, codigo\" />\n <mat-icon matSuffix>fingerprint</mat-icon>\n </mat-form-field>\n <small style=\"opacity:.75\">Defina o recurso da API e, se necess\u00E1rio, a chave prim\u00E1ria</small>\n\n <!-- Diverg\u00EAncia: idField -->\n <div *ngIf=\"idFieldDiverges\" style=\"grid-column: 1 / -1; padding: 8px 12px; border-radius: 6px; background: #fff4e5; color: #8a4b00; display:flex; align-items:center; gap: 8px;\">\n <mat-icon>warning</mat-icon>\n <div style=\"flex:1;\">\n A chave prim\u00E1ria no servidor diverge da configura\u00E7\u00E3o atual.\n <span style=\"opacity:.85\">(config: {{ idField || 'id' }} | servidor: {{ serverIdField || 'id' }})</span>\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onReconcileIdField()\">Reconciliar</button>\n </div>\n\n <!-- Diverg\u00EAncia: serverHash -->\n <div *ngIf=\"schemaHashDiverges\" style=\"grid-column: 1 / -1; padding: 8px 12px; border-radius: 6px; background: #e6f4ff; color: #0b5aaa; display:flex; align-items:center; gap: 8px;\">\n <mat-icon>info</mat-icon>\n <div style=\"flex:1;\">\n O schema no servidor foi atualizado desde a \u00FAltima sincroniza\u00E7\u00E3o.\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onAcceptServerHash()\">Atualizar metadados</button>\n </div>\n </div>\n\n <behavior-config-editor\n *ngSwitchCase=\"'overview'\"\n [config]=\"editedConfig\"\n (configChange)=\"onBehaviorConfigChange($event)\"\n (behaviorChange)=\"onBehaviorChange($event)\"\n ></behavior-config-editor>\n\n <columns-config-editor\n *ngSwitchCase=\"'columns'\"\n [config]=\"editedConfig\"\n (configChange)=\"onColumnsConfigChange($event)\"\n (columnChange)=\"onColumnChange($event)\"\n ></columns-config-editor>\n\n <header-appearance-editor\n *ngSwitchCase=\"'header'\"\n [config]=\"editedConfig\"\n (configChange)=\"onColumnsConfigChange($event)\"\n ></header-appearance-editor>\n\n <toolbar-actions-editor\n *ngSwitchCase=\"'toolbar'\"\n [config]=\"editedConfig\"\n (configChange)=\"onToolbarActionsConfigChange($event)\"\n (toolbarActionsChange)=\"onToolbarActionsChange($event)\"\n ></toolbar-actions-editor>\n\n <!-- Aba extra para integra\u00E7\u00F5es CRUD (vis\u00EDvel quando em contexto CRUD) -->\n <crud-integration-editor\n #crudEditorRef\n *ngSwitchCase=\"'crud'\"\n [tableId]=\"crudContext?.tableId || 'default'\"\n [crudContext]=\"crudContext\"\n ></crud-integration-editor>\n\n <filter-settings\n *ngSwitchCase=\"'filters'\"\n [metadata]=\"columnMetas\"\n [settings]=\"\n editedConfig.behavior?.filtering?.advancedFilters?.settings\n \"\n (settingsChange)=\"onFilterSettingsChange($event)\"\n ></filter-settings>\n\n <messages-localization-editor\n *ngSwitchCase=\"'messages'\"\n [config]=\"editedConfig\"\n (configChange)=\"onMessagesLocalizationConfigChange($event)\"\n (messagesLocalizationChange)=\"\n onMessagesLocalizationChange($event)\n \"\n ></messages-localization-editor>\n\n <confirm-dialog-appearance-editor\n *ngSwitchCase=\"'dialogs'\"\n [config]=\"editedConfig\"\n (configChange)=\"onJsonConfigChange($event)\"\n ></confirm-dialog-appearance-editor>\n\n <!-- Visual Rules Editor -->\n <table-visual-rules-editor\n *ngSwitchCase=\"'visualRules'\"\n [config]=\"editedConfig\"\n (configChange)=\"onVisualRulesConfigChange($event)\"\n ></table-visual-rules-editor>\n\n <json-config-editor\n *ngSwitchCase=\"'json'\"\n [config]=\"editedConfig\"\n (configChange)=\"onJsonConfigChange($event)\"\n (editorEvent)=\"onJsonEditorEvent($event)\"\n ></json-config-editor>\n </ng-container>\n </div>\n </mat-tab>\n </mat-tab-group>\n<div class=\"config-editor-status\" *ngIf=\"statusMessage\">\n <span\n class=\"status-text\"\n [class.error]=\"hasErrors\"\n [class.success]=\"hasSuccess\"\n >{{ statusMessage }}</span\n >\n</div>\n", styles: ["@charset \"UTF-8\";.config-tabs{flex:1 1 auto;display:flex;flex-direction:column}.config-tabs .mat-mdc-tab{min-width:120px}.config-tabs .mat-mdc-tab-body-wrapper,.config-tabs .mat-mdc-tab-group-container{flex:1 1 auto;min-height:0}.config-tabs .mat-mdc-tab-body-content{height:100%;min-height:0;overflow:visible}.tab-content{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;padding:8px 8px 56px;box-sizing:border-box;overflow:visible}.educational-card{margin-bottom:24px;background:var(--mat-sys-surface-container-low);border-left:4px solid var(--mat-sys-primary);flex-shrink:0}.config-editor-status{padding:12px 16px;margin-top:auto;border-top:1px solid rgba(0,0,0,.12);background:var(--mat-sys-surface-container);flex-shrink:0;position:relative}.status-text{font-size:.875rem;line-height:1.2}.status-text.error{color:var(--mat-sys-error)}.status-text.success{color:var(--mat-sys-primary)}@media (max-width: 768px){.tab-content{padding:8px}.config-editor-status{padding:12px 16px}:host{display:flex;flex-direction:column;flex:1 1 auto;min-height:0}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i4$1.MatTabLabel, selector: "[mat-tab-label], [matTabLabel]" }, { kind: "component", type: i4$1.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i4$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: FormsModule }, { kind: "directive", type: i1$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: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3$1.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: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: JsonConfigEditorComponent, selector: "json-config-editor", inputs: ["config"], outputs: ["configChange", "validationChange", "editorEvent"] }, { kind: "component", type: ColumnsConfigEditorComponent, selector: "columns-config-editor", inputs: ["config"], outputs: ["configChange", "columnChange"] }, { kind: "component", type: BehaviorConfigEditorComponent, selector: "behavior-config-editor", inputs: ["config"], outputs: ["configChange", "behaviorChange"] }, { kind: "component", type: HeaderAppearanceEditorComponent, selector: "header-appearance-editor", inputs: ["config"], outputs: ["configChange"] }, { kind: "component", type: ToolbarActionsEditorComponent, selector: "toolbar-actions-editor", inputs: ["config"], outputs: ["configChange", "toolbarActionsChange"] }, { kind: "component", type: MessagesLocalizationEditorComponent, selector: "messages-localization-editor", inputs: ["config"], outputs: ["configChange", "messagesLocalizationChange"] }, { kind: "component", type: FilterSettingsComponent, selector: "filter-settings", inputs: ["metadata", "settings", "configKey"], outputs: ["settingsChange"] }, { kind: "component", type: CrudIntegrationEditorComponent, selector: "crud-integration-editor", inputs: ["tableId", "crudContext"] }, { kind: "component", type: TableVisualRulesEditorComponent, selector: "table-visual-rules-editor", inputs: ["config"], outputs: ["configChange"] }, { kind: "component", type: ConfirmDialogAppearanceEditorComponent, selector: "confirm-dialog-appearance-editor", inputs: ["config"], outputs: ["configChange"] }] });
|
|
15023
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.4", type: PraxisTableConfigEditor, isStandalone: true, selector: "praxis-table-config-editor", providers: [TableConfigService], viewQueries: [{ propertyName: "behaviorEditor", first: true, predicate: BehaviorConfigEditorComponent, descendants: true }, { propertyName: "crudEditorSetter", first: true, predicate: ["crudEditorRef"], descendants: true }], ngImport: i0, template: " <mat-tab-group class=\"config-tabs\" [(selectedIndex)]=\"activeSectionIndex\">\n <mat-tab *ngFor=\"let section of sections\">\n <ng-template mat-tab-label>\n <mat-icon *ngIf=\"section.icon\" [praxisIcon]=\"section.icon\"></mat-icon>\n <span>{{ section.label }}</span>\n </ng-template>\n <div class=\"tab-content\">\n <ng-container [ngSwitch]=\"section.id\">\n <div *ngSwitchCase=\"'connect'\" style=\"display:grid; gap:12px; padding: 8px; grid-template-columns: 1fr 240px; align-items: start;\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Recurso</mat-label>\n <input matInput [(ngModel)]=\"resourcePath\" (ngModelChange)=\"onResourcePathChange($event)\" placeholder=\"ex.: employees\" />\n <mat-icon matSuffix>link</mat-icon>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Chave prim\u00E1ria</mat-label>\n <input matInput [(ngModel)]=\"idField\" (ngModelChange)=\"onIdFieldChange($event)\" placeholder=\"ex.: id, uuid, codigo\" />\n <mat-icon matSuffix>fingerprint</mat-icon>\n </mat-form-field>\n <small style=\"opacity:.75\">Defina o recurso da API e, se necess\u00E1rio, a chave prim\u00E1ria</small>\n\n <!-- Diverg\u00EAncia: idField -->\n <div *ngIf=\"idFieldDiverges\" style=\"grid-column: 1 / -1; padding: 8px 12px; border-radius: 6px; background: #fff4e5; color: #8a4b00; display:flex; align-items:center; gap: 8px;\">\n <mat-icon>warning</mat-icon>\n <div style=\"flex:1;\">\n A chave prim\u00E1ria no servidor diverge da configura\u00E7\u00E3o atual.\n <span style=\"opacity:.85\">(config: {{ idField || 'id' }} | servidor: {{ serverIdField || 'id' }})</span>\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onReconcileIdField()\">Reconciliar</button>\n </div>\n\n <!-- Diverg\u00EAncia: serverHash -->\n <div *ngIf=\"schemaHashDiverges\" style=\"grid-column: 1 / -1; padding: 8px 12px; border-radius: 6px; background: #e6f4ff; color: #0b5aaa; display:flex; align-items:center; gap: 8px;\">\n <mat-icon>info</mat-icon>\n <div style=\"flex:1;\">\n O schema no servidor foi atualizado desde a \u00FAltima sincroniza\u00E7\u00E3o.\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onAcceptServerHash()\">Atualizar metadados</button>\n </div>\n </div>\n\n <behavior-config-editor\n *ngSwitchCase=\"'overview'\"\n [config]=\"editedConfig\"\n (configChange)=\"onBehaviorConfigChange($event)\"\n (behaviorChange)=\"onBehaviorChange($event)\"\n ></behavior-config-editor>\n\n <columns-config-editor\n *ngSwitchCase=\"'columns'\"\n [config]=\"editedConfig\"\n (configChange)=\"onColumnsConfigChange($event)\"\n (columnChange)=\"onColumnChange($event)\"\n ></columns-config-editor>\n\n <header-appearance-editor\n *ngSwitchCase=\"'header'\"\n [config]=\"editedConfig\"\n (configChange)=\"onColumnsConfigChange($event)\"\n ></header-appearance-editor>\n\n <toolbar-actions-editor\n *ngSwitchCase=\"'toolbar'\"\n [config]=\"editedConfig\"\n (configChange)=\"onToolbarActionsConfigChange($event)\"\n (toolbarActionsChange)=\"onToolbarActionsChange($event)\"\n ></toolbar-actions-editor>\n\n <!-- Aba extra para integra\u00E7\u00F5es CRUD (vis\u00EDvel quando em contexto CRUD) -->\n <crud-integration-editor\n #crudEditorRef\n *ngSwitchCase=\"'crud'\"\n [tableId]=\"crudContext?.tableId || 'default'\"\n [crudContext]=\"crudContext\"\n ></crud-integration-editor>\n\n <filter-settings\n *ngSwitchCase=\"'filters'\"\n [metadata]=\"columnMetas\"\n [settings]=\"\n editedConfig.behavior?.filtering?.advancedFilters?.settings\n \"\n (settingsChange)=\"onFilterSettingsChange($event)\"\n ></filter-settings>\n\n <messages-localization-editor\n *ngSwitchCase=\"'messages'\"\n [config]=\"editedConfig\"\n (configChange)=\"onMessagesLocalizationConfigChange($event)\"\n (messagesLocalizationChange)=\"\n onMessagesLocalizationChange($event)\n \"\n ></messages-localization-editor>\n\n <confirm-dialog-appearance-editor\n *ngSwitchCase=\"'dialogs'\"\n [config]=\"editedConfig\"\n (configChange)=\"onJsonConfigChange($event)\"\n ></confirm-dialog-appearance-editor>\n\n <!-- Visual Rules Editor -->\n <table-visual-rules-editor\n *ngSwitchCase=\"'visualRules'\"\n [config]=\"editedConfig\"\n (configChange)=\"onVisualRulesConfigChange($event)\"\n ></table-visual-rules-editor>\n\n <json-config-editor\n *ngSwitchCase=\"'json'\"\n [config]=\"editedConfig\"\n (configChange)=\"onJsonConfigChange($event)\"\n (editorEvent)=\"onJsonEditorEvent($event)\"\n ></json-config-editor>\n </ng-container>\n </div>\n </mat-tab>\n </mat-tab-group>\n<div class=\"config-editor-status\" *ngIf=\"statusMessage\">\n <span\n class=\"status-text\"\n [class.error]=\"hasErrors\"\n [class.success]=\"hasSuccess\"\n >{{ statusMessage }}</span\n >\n</div>\n", styles: ["@charset \"UTF-8\";.config-tabs{flex:1 1 auto;display:flex;flex-direction:column}.config-tabs .mat-mdc-tab{min-width:120px}.config-tabs .mat-mdc-tab-body-wrapper,.config-tabs .mat-mdc-tab-group-container{flex:1 1 auto;min-height:0}.config-tabs .mat-mdc-tab-body-content{height:100%;min-height:0;overflow:visible}.tab-content{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;padding:8px 8px 56px;box-sizing:border-box;overflow:visible}.educational-card{margin-bottom:24px;background:var(--mat-sys-surface-container-low);border-left:4px solid var(--mat-sys-primary);flex-shrink:0}.config-editor-status{padding:12px 16px;margin-top:auto;border-top:1px solid rgba(0,0,0,.12);background:var(--mat-sys-surface-container);flex-shrink:0;position:relative}.status-text{font-size:.875rem;line-height:1.2}.status-text.error{color:var(--mat-sys-error)}.status-text.success{color:var(--mat-sys-primary)}@media (max-width: 768px){.tab-content{padding:8px}.config-editor-status{padding:12px 16px}:host{display:flex;flex-direction:column;flex:1 1 auto;min-height:0}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i4$1.MatTabLabel, selector: "[mat-tab-label], [matTabLabel]" }, { kind: "component", type: i4$1.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i4$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: FormsModule }, { kind: "directive", type: i1$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: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3$1.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: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: JsonConfigEditorComponent, selector: "json-config-editor", inputs: ["config"], outputs: ["configChange", "validationChange", "editorEvent"] }, { kind: "component", type: ColumnsConfigEditorComponent, selector: "columns-config-editor", inputs: ["config"], outputs: ["configChange", "columnChange"] }, { kind: "component", type: BehaviorConfigEditorComponent, selector: "behavior-config-editor", inputs: ["config"], outputs: ["configChange", "behaviorChange"] }, { kind: "component", type: HeaderAppearanceEditorComponent, selector: "header-appearance-editor", inputs: ["config"], outputs: ["configChange"] }, { kind: "component", type: ToolbarActionsEditorComponent, selector: "toolbar-actions-editor", inputs: ["config"], outputs: ["configChange", "toolbarActionsChange"] }, { kind: "component", type: MessagesLocalizationEditorComponent, selector: "messages-localization-editor", inputs: ["config"], outputs: ["configChange", "messagesLocalizationChange"] }, { kind: "component", type: FilterSettingsComponent, selector: "filter-settings", inputs: ["metadata", "settings", "configKey"], outputs: ["settingsChange"] }, { kind: "component", type: CrudIntegrationEditorComponent, selector: "crud-integration-editor", inputs: ["tableId", "crudContext"] }, { kind: "component", type: TableVisualRulesEditorComponent, selector: "table-visual-rules-editor", inputs: ["config"], outputs: ["configChange"] }, { kind: "component", type: ConfirmDialogAppearanceEditorComponent, selector: "confirm-dialog-appearance-editor", inputs: ["config"], outputs: ["configChange"] }] });
|
|
14987
15024
|
}
|
|
14988
15025
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PraxisTableConfigEditor, decorators: [{
|
|
14989
15026
|
type: Component,
|
|
@@ -15005,7 +15042,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
15005
15042
|
CrudIntegrationEditorComponent,
|
|
15006
15043
|
TableVisualRulesEditorComponent,
|
|
15007
15044
|
ConfirmDialogAppearanceEditorComponent,
|
|
15008
|
-
], providers: [TableConfigService], template: " <mat-tab-group class=\"config-tabs\" [(selectedIndex)]=\"activeSectionIndex\">\n <mat-tab *ngFor=\"let section of sections\">\n <ng-template mat-tab-label>\n <mat-icon *ngIf=\"section.icon\" [praxisIcon]=\"section.icon\"></mat-icon>\n <span>{{ section.label }}</span>\n </ng-template>\n <div class=\"tab-content\">\n <ng-container [ngSwitch]=\"section.id\">\n <div *ngSwitchCase=\"'connect'\" style=\"display:grid; gap:12px; padding: 8px; grid-template-columns: 1fr 240px; align-items: start;\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Recurso</mat-label>\n <input matInput [(ngModel)]=\"resourcePath\" (ngModelChange)=\"onResourcePathChange($event)\" placeholder=\"ex.:
|
|
15045
|
+
], providers: [TableConfigService], template: " <mat-tab-group class=\"config-tabs\" [(selectedIndex)]=\"activeSectionIndex\">\n <mat-tab *ngFor=\"let section of sections\">\n <ng-template mat-tab-label>\n <mat-icon *ngIf=\"section.icon\" [praxisIcon]=\"section.icon\"></mat-icon>\n <span>{{ section.label }}</span>\n </ng-template>\n <div class=\"tab-content\">\n <ng-container [ngSwitch]=\"section.id\">\n <div *ngSwitchCase=\"'connect'\" style=\"display:grid; gap:12px; padding: 8px; grid-template-columns: 1fr 240px; align-items: start;\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Recurso</mat-label>\n <input matInput [(ngModel)]=\"resourcePath\" (ngModelChange)=\"onResourcePathChange($event)\" placeholder=\"ex.: employees\" />\n <mat-icon matSuffix>link</mat-icon>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Chave prim\u00E1ria</mat-label>\n <input matInput [(ngModel)]=\"idField\" (ngModelChange)=\"onIdFieldChange($event)\" placeholder=\"ex.: id, uuid, codigo\" />\n <mat-icon matSuffix>fingerprint</mat-icon>\n </mat-form-field>\n <small style=\"opacity:.75\">Defina o recurso da API e, se necess\u00E1rio, a chave prim\u00E1ria</small>\n\n <!-- Diverg\u00EAncia: idField -->\n <div *ngIf=\"idFieldDiverges\" style=\"grid-column: 1 / -1; padding: 8px 12px; border-radius: 6px; background: #fff4e5; color: #8a4b00; display:flex; align-items:center; gap: 8px;\">\n <mat-icon>warning</mat-icon>\n <div style=\"flex:1;\">\n A chave prim\u00E1ria no servidor diverge da configura\u00E7\u00E3o atual.\n <span style=\"opacity:.85\">(config: {{ idField || 'id' }} | servidor: {{ serverIdField || 'id' }})</span>\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onReconcileIdField()\">Reconciliar</button>\n </div>\n\n <!-- Diverg\u00EAncia: serverHash -->\n <div *ngIf=\"schemaHashDiverges\" style=\"grid-column: 1 / -1; padding: 8px 12px; border-radius: 6px; background: #e6f4ff; color: #0b5aaa; display:flex; align-items:center; gap: 8px;\">\n <mat-icon>info</mat-icon>\n <div style=\"flex:1;\">\n O schema no servidor foi atualizado desde a \u00FAltima sincroniza\u00E7\u00E3o.\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onAcceptServerHash()\">Atualizar metadados</button>\n </div>\n </div>\n\n <behavior-config-editor\n *ngSwitchCase=\"'overview'\"\n [config]=\"editedConfig\"\n (configChange)=\"onBehaviorConfigChange($event)\"\n (behaviorChange)=\"onBehaviorChange($event)\"\n ></behavior-config-editor>\n\n <columns-config-editor\n *ngSwitchCase=\"'columns'\"\n [config]=\"editedConfig\"\n (configChange)=\"onColumnsConfigChange($event)\"\n (columnChange)=\"onColumnChange($event)\"\n ></columns-config-editor>\n\n <header-appearance-editor\n *ngSwitchCase=\"'header'\"\n [config]=\"editedConfig\"\n (configChange)=\"onColumnsConfigChange($event)\"\n ></header-appearance-editor>\n\n <toolbar-actions-editor\n *ngSwitchCase=\"'toolbar'\"\n [config]=\"editedConfig\"\n (configChange)=\"onToolbarActionsConfigChange($event)\"\n (toolbarActionsChange)=\"onToolbarActionsChange($event)\"\n ></toolbar-actions-editor>\n\n <!-- Aba extra para integra\u00E7\u00F5es CRUD (vis\u00EDvel quando em contexto CRUD) -->\n <crud-integration-editor\n #crudEditorRef\n *ngSwitchCase=\"'crud'\"\n [tableId]=\"crudContext?.tableId || 'default'\"\n [crudContext]=\"crudContext\"\n ></crud-integration-editor>\n\n <filter-settings\n *ngSwitchCase=\"'filters'\"\n [metadata]=\"columnMetas\"\n [settings]=\"\n editedConfig.behavior?.filtering?.advancedFilters?.settings\n \"\n (settingsChange)=\"onFilterSettingsChange($event)\"\n ></filter-settings>\n\n <messages-localization-editor\n *ngSwitchCase=\"'messages'\"\n [config]=\"editedConfig\"\n (configChange)=\"onMessagesLocalizationConfigChange($event)\"\n (messagesLocalizationChange)=\"\n onMessagesLocalizationChange($event)\n \"\n ></messages-localization-editor>\n\n <confirm-dialog-appearance-editor\n *ngSwitchCase=\"'dialogs'\"\n [config]=\"editedConfig\"\n (configChange)=\"onJsonConfigChange($event)\"\n ></confirm-dialog-appearance-editor>\n\n <!-- Visual Rules Editor -->\n <table-visual-rules-editor\n *ngSwitchCase=\"'visualRules'\"\n [config]=\"editedConfig\"\n (configChange)=\"onVisualRulesConfigChange($event)\"\n ></table-visual-rules-editor>\n\n <json-config-editor\n *ngSwitchCase=\"'json'\"\n [config]=\"editedConfig\"\n (configChange)=\"onJsonConfigChange($event)\"\n (editorEvent)=\"onJsonEditorEvent($event)\"\n ></json-config-editor>\n </ng-container>\n </div>\n </mat-tab>\n </mat-tab-group>\n<div class=\"config-editor-status\" *ngIf=\"statusMessage\">\n <span\n class=\"status-text\"\n [class.error]=\"hasErrors\"\n [class.success]=\"hasSuccess\"\n >{{ statusMessage }}</span\n >\n</div>\n", styles: ["@charset \"UTF-8\";.config-tabs{flex:1 1 auto;display:flex;flex-direction:column}.config-tabs .mat-mdc-tab{min-width:120px}.config-tabs .mat-mdc-tab-body-wrapper,.config-tabs .mat-mdc-tab-group-container{flex:1 1 auto;min-height:0}.config-tabs .mat-mdc-tab-body-content{height:100%;min-height:0;overflow:visible}.tab-content{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;padding:8px 8px 56px;box-sizing:border-box;overflow:visible}.educational-card{margin-bottom:24px;background:var(--mat-sys-surface-container-low);border-left:4px solid var(--mat-sys-primary);flex-shrink:0}.config-editor-status{padding:12px 16px;margin-top:auto;border-top:1px solid rgba(0,0,0,.12);background:var(--mat-sys-surface-container);flex-shrink:0;position:relative}.status-text{font-size:.875rem;line-height:1.2}.status-text.error{color:var(--mat-sys-error)}.status-text.success{color:var(--mat-sys-primary)}@media (max-width: 768px){.tab-content{padding:8px}.config-editor-status{padding:12px 16px}:host{display:flex;flex-direction:column;flex:1 1 auto;min-height:0}}\n"] }]
|
|
15009
15046
|
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1$3.TableConfigService }, { type: undefined, decorators: [{
|
|
15010
15047
|
type: Inject,
|
|
15011
15048
|
args: [SETTINGS_PANEL_DATA]
|
|
@@ -15017,7 +15054,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
15017
15054
|
}] }], propDecorators: { behaviorEditor: [{
|
|
15018
15055
|
type: ViewChild,
|
|
15019
15056
|
args: [BehaviorConfigEditorComponent]
|
|
15020
|
-
}],
|
|
15057
|
+
}], crudEditorSetter: [{
|
|
15021
15058
|
type: ViewChild,
|
|
15022
15059
|
args: ['crudEditorRef']
|
|
15023
15060
|
}] } });
|
|
@@ -19099,7 +19136,7 @@ class PraxisTable {
|
|
|
19099
19136
|
this.dataSubject.complete();
|
|
19100
19137
|
}
|
|
19101
19138
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PraxisTable, deps: [{ token: i1$3.GenericCrudService }, { token: i0.ChangeDetectorRef }, { token: i3$2.SettingsPanelService }, { token: DataFormattingService }, { token: i4$2.SpecificationBridgeService }, { token: CONFIG_STORAGE }, { token: CONNECTION_STORAGE }, { token: TableDefaultsProvider }, { token: i2$1.MatSnackBar }, { token: FilterConfigService }, { token: i8$2.PraxisDialog }, { token: i0.ElementRef }, { token: i1$3.GlobalConfigService }], target: i0.ɵɵFactoryTarget.Component });
|
|
19102
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.4", type: PraxisTable, isStandalone: true, selector: "praxis-table", inputs: { config: "config", resourcePath: "resourcePath", filterCriteria: "filterCriteria", notifyIfOutdated: "notifyIfOutdated", snoozeMs: "snoozeMs", autoOpenSettingsOnOutdated: "autoOpenSettingsOnOutdated", showToolbar: "showToolbar", toolbarV2: "toolbarV2", autoDelete: "autoDelete", editModeEnabled: "editModeEnabled", dense: "dense", tableId: "tableId", debugLayout: "debugLayout", crudContext: "crudContext", idField: "idField" }, outputs: { rowClick: "rowClick", rowAction: "rowAction", toolbarAction: "toolbarAction", bulkAction: "bulkAction", rowDoubleClick: "rowDoubleClick", schemaStatusChange: "schemaStatusChange", beforeDelete: "beforeDelete", afterDelete: "afterDelete", deleteError: "deleteError", beforeBulkDelete: "beforeBulkDelete", afterBulkDelete: "afterBulkDelete", bulkDeleteError: "bulkDeleteError" }, host: { properties: { "class.debug-layout": "debugLayout" } }, queries: [{ propertyName: "projectedFilter", first: true, predicate: PraxisFilter, descendants: true }], viewQueries: [{ propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }, { propertyName: "sort", first: true, predicate: MatSort, descendants: true }, { propertyName: "actionsHeaderCell", first: true, predicate: ["actionsHeaderCell"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<praxis-empty-state-card\n *ngIf=\"!resourcePath\"\n icon=\"link\"\n [title]=\"'Conecte a tabela \u00E0 fonte de dados'\"\n [description]=\"'Informe a rota do recurso da API para carregar colunas e dados automaticamente.'\"\n [primaryAction]=\"{ label: 'Conectar \u00E0 fonte de dados', icon: 'bolt', action: openQuickConnect.bind(this) }\"\n></praxis-empty-state-card>\n\n<!-- Error State with Quick Connect CTA -->\n<div class=\"ptable-error\" *ngIf=\"resourcePath && (schemaError || dataError)\" style=\"display:flex; align-items:center; gap:12px; padding:12px; border:1px solid var(--md-sys-color-error, #b00020); border-radius:8px; margin: 8px 0;\">\n <mat-icon color=\"warn\" aria-hidden=\"true\">error</mat-icon>\n <div style=\"flex:1\">\n <div style=\"font-weight:600\">Erro</div>\n <div>{{ errorMessage || 'Ocorreu um erro ao carregar a tabela.' }}</div>\n </div>\n <button mat-flat-button color=\"primary\" (click)=\"openQuickConnect()\">\n <mat-icon>bolt</mat-icon>\n Conectar a recurso\n </button>\n <button mat-stroked-button (click)=\"retryData()\" *ngIf=\"!schemaError\">Tentar novamente</button>\n <button mat-stroked-button (click)=\"reloadSchema()\" *ngIf=\"schemaError\">Recarregar colunas</button>\n </div>\n\n<!-- Inline banner for schema change (only in edit mode) -->\n<div *ngIf=\"shouldShowOutdatedInline()\" class=\"ptable-info-banner\" role=\"status\" aria-live=\"polite\">\n <div class=\"text\">O schema do servidor mudou. Reconciliar agora?</div>\n <div class=\"actions\">\n <button mat-stroked-button color=\"primary\" (click)=\"onReconcileRequested()\">\n <mat-icon>sync</mat-icon>\n Reconciliar\n </button>\n <button mat-button (click)=\"onSnoozeOutdated()\">Lembrar depois</button>\n <button mat-button (click)=\"onIgnoreOutdated()\">Ignorar</button>\n </div>\n </div>\n\n <ng-container *ngIf=\"resourcePath && !schemaError && !dataError && toolbarV2; else legacyHeader\">\n <div class=\"praxis-table-header\" [class.debug-layout]=\"debugLayout\" [class.edit-mode]=\"editModeEnabled\" *ngIf=\"showToolbar || editModeEnabled\">\n <praxis-table-toolbar\n *ngIf=\"showToolbar\"\n [config]=\"config\"\n [debugLayout]=\"debugLayout\"\n (toolbarAction)=\"onToolbarAction($event)\"\n (reset)=\"onResetPreferences()\"\n >\n <praxis-filter\n *ngIf=\"\n resourcePath &&\n config.behavior?.filtering?.advancedFilters?.enabled &&\n !projectedFilter\n \"\n advancedFilter\n [resourcePath]=\"resourcePath\"\n [formId]=\"tableId + '-filter'\"\n [editModeEnabled]=\"editModeEnabled\"\n [quickField]=\"\n config.behavior?.filtering?.advancedFilters?.settings?.quickField\n \"\n [alwaysVisibleFields]=\"\n config.behavior?.filtering?.advancedFilters?.settings\n ?.alwaysVisibleFields\n \"\n [allowSaveTags]=\"\n config.behavior?.filtering?.advancedFilters?.settings\n ?.allowSaveTags\n \"\n [changeDebounceMs]=\"\n config.behavior?.filtering?.advancedFilters?.settings\n ?.changeDebounceMs ?? 300\n \"\n [i18n]=\"getFilterI18n()\"\n [mode]=\"\n config.behavior?.filtering?.advancedFilters?.settings?.mode ??\n 'auto'\n \"\n [debugLayout]=\"debugLayout\"\n [showFilterSettings]=\"!editModeEnabled\"\n (submit)=\"onAdvancedFilterSubmit($event)\"\n (clear)=\"onAdvancedFilterClear()\"\n ></praxis-filter>\n <ng-content select=\"[advancedFilter]\" />\n <ng-content select=\"[toolbar]\" />\n <button end-actions mat-icon-button color=\"primary\" *ngIf=\"editModeEnabled\"\n (click)=\"openTableSettings()\" aria-label=\"Configura\u00E7\u00F5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u2014 Reconciliar' : 'Configura\u00E7\u00F5es'\"\n matTooltipPosition=\"below\">\n <mat-icon>settings</mat-icon>\n </button>\n </praxis-table-toolbar>\n <!-- Render a minimal settings button when toolbar is hidden but edit mode is enabled -->\n <div class=\"ptable-header-actions\" *ngIf=\"!showToolbar && editModeEnabled\">\n <button mat-icon-button color=\"primary\" (click)=\"openTableSettings()\" aria-label=\"Configura\u00E7\u00F5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u2014 Reconciliar' : 'Configura\u00E7\u00F5es'\"\n matTooltipPosition=\"below\">\n <mat-icon>settings</mat-icon>\n </button>\n <button mat-icon-button (click)=\"disconnect()\" aria-label=\"Desconectar\" matTooltip=\"Desconectar da fonte de dados\">\n <mat-icon>link_off</mat-icon>\n </button>\n </div>\n \n </div>\n</ng-container>\n<ng-template #legacyHeader>\n <ng-container *ngIf=\"resourcePath && !schemaError && !dataError\">\n <praxis-table-toolbar\n *ngIf=\"showToolbar\"\n [config]=\"config\"\n [debugLayout]=\"debugLayout\"\n (toolbarAction)=\"onToolbarAction($event)\"\n (reset)=\"onResetPreferences()\"\n >\n <praxis-filter\n *ngIf=\"\n resourcePath &&\n config.behavior?.filtering?.advancedFilters?.enabled &&\n !projectedFilter\n \"\n advancedFilter\n [resourcePath]=\"resourcePath\"\n [formId]=\"tableId + '-filter'\"\n [editModeEnabled]=\"editModeEnabled\"\n [quickField]=\"\n config.behavior?.filtering?.advancedFilters?.settings?.quickField\n \"\n [alwaysVisibleFields]=\"\n config.behavior?.filtering?.advancedFilters?.settings\n ?.alwaysVisibleFields\n \"\n [allowSaveTags]=\"\n config.behavior?.filtering?.advancedFilters?.settings?.allowSaveTags\n \"\n [changeDebounceMs]=\"\n config.behavior?.filtering?.advancedFilters?.settings\n ?.changeDebounceMs ?? 300\n \"\n [i18n]=\"getFilterI18n()\"\n [mode]=\"\n config.behavior?.filtering?.advancedFilters?.settings?.mode ??\n 'auto'\n \"\n [debugLayout]=\"debugLayout\"\n [showFilterSettings]=\"!editModeEnabled\"\n (submit)=\"onAdvancedFilterSubmit($event)\"\n (clear)=\"onAdvancedFilterClear()\"\n ></praxis-filter>\n <ng-content select=\"[advancedFilter]\" />\n <ng-content select=\"[toolbar]\" />\n <button end-actions mat-icon-button color=\"primary\" *ngIf=\"editModeEnabled\"\n (click)=\"openTableSettings()\" aria-label=\"Configura\u00E7\u00F5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u2014 Reconciliar' : 'Configura\u00E7\u00F5es'\"\n matTooltipPosition=\"below\">\n <mat-icon>settings</mat-icon>\n </button>\n </praxis-table-toolbar>\n <!-- Legacy header: settings button when toolbar hidden -->\n <div class=\"ptable-header-actions\" *ngIf=\"!showToolbar && editModeEnabled\">\n <button mat-icon-button color=\"primary\" (click)=\"openTableSettings()\" aria-label=\"Configura\u00E7\u00F5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u2014 Reconciliar' : 'Configura\u00E7\u00F5es'\"\n matTooltipPosition=\"below\">\n <mat-icon>settings</mat-icon>\n </button>\n </div>\n </ng-container>\n \n</ng-template>\n<table\n *ngIf=\"resourcePath && !schemaError && !dataError\"\n mat-table\n [dataSource]=\"dataSource\"\n matSort\n (matSortChange)=\"onSortChange($event)\"\n [matSortDisabled]=\"!getSortingEnabled()\"\n class=\"mat-elevation-z8\"\n>\n <ng-container\n *ngIf=\"config.behavior?.selection?.enabled\"\n matColumnDef=\"_select\"\n >\n <th mat-header-cell *matHeaderCellDef>\n <mat-checkbox\n (change)=\"masterToggle()\"\n [checked]=\"isAllSelected()\"\n [indeterminate]=\"selection.hasValue() && !isAllSelected()\"\n ></mat-checkbox>\n </th>\n <td mat-cell *matCellDef=\"let row\">\n <mat-checkbox\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleRow(row)\"\n [checked]=\"selection.isSelected(row)\"\n ></mat-checkbox>\n </td>\n </ng-container>\n <ng-container\n *ngFor=\"let column of visibleColumns\"\n [matColumnDef]=\"column.field\"\n [sticky]=\"column.sticky === true || column.sticky === 'start'\"\n [stickyEnd]=\"column.sticky === 'end'\"\n >\n <th\n mat-header-cell\n *matHeaderCellDef\n mat-sort-header\n [disabled]=\"!getSortingEnabled() || column.sortable === false\"\n [style.text-align]=\"column.align\"\n [style.width]=\"column.width\"\n [attr.style]=\"column.headerStyle\"\n >\n {{ column.header }}\n </th>\n <td\n mat-cell\n *matCellDef=\"let element\"\n [style.text-align]=\"column.align\"\n [style.width]=\"column.width\"\n [attr.style]=\"column.style\"\n [ngClass]=\"getCellClasses(element, column)\"\n [ngStyle]=\"getCellNgStyle(element, column)\"\n >\n <ng-container [ngSwitch]=\"getEffectiveRendererType(element, column)\">\n <!-- Icon renderer -->\n <ng-container *ngSwitchCase=\"'icon'\">\n <mat-icon\n [color]=\"getIconColor(element, column) || null\"\n [ngStyle]=\"getIconStyle(element, column)\"\n [attr.aria-label]=\"getIconAriaLabel(element, column) || null\"\n >{{ getIconName(element, column) }}</mat-icon\n >\n </ng-container>\n\n <!-- Image renderer -->\n <ng-container *ngSwitchCase=\"'image'\">\n <img\n class=\"pfx-cell-image\"\n [src]=\"getImageSrc(element, column)\"\n [attr.alt]=\"getImageAlt(element, column) || ''\"\n [attr.loading]=\"getImageLazy(element, column) ? 'lazy' : null\"\n [style.width.px]=\"getImageWidth(column)\"\n [style.height.px]=\"getImageHeight(column)\"\n [class.shape-rounded]=\"getImageShape(column) === 'rounded'\"\n [class.shape-circle]=\"getImageShape(column) === 'circle'\"\n [style.object-fit]=\"getImageFit(column)\"\n />\n </ng-container>\n\n <!-- Badge renderer -->\n <ng-container *ngSwitchCase=\"'badge'\">\n <span class=\"pfx-badge\" [ngClass]=\"getBadgeClasses(element, column)\">\n <mat-icon *ngIf=\"getBadgeIcon(element, column) as bi\" class=\"pfx-badge-icon\">{{ bi }}</mat-icon>\n <span class=\"pfx-badge-text\">{{ getBadgeText(element, column) }}</span>\n </span>\n </ng-container>\n\n <!-- Default text rendering -->\n <ng-container *ngSwitchDefault>\n {{ getCellValue(element, column) }}\n </ng-container>\n </ng-container>\n </td>\n </ng-container>\n <ng-container *ngIf=\"config.actions?.row?.enabled\" matColumnDef=\"_actions\" [sticky]=\"config.actions?.row?.sticky === true || config.actions?.row?.sticky === 'start'\" [stickyEnd]=\"config.actions?.row?.sticky === 'end'\">\n <th mat-header-cell *matHeaderCellDef #actionsHeaderCell [style.width]=\"config.actions?.row?.width\" class=\"praxis-actions-header\" [class.align-start]=\"getActionsHeaderAlign() === 'start'\" [class.align-center]=\"getActionsHeaderAlign() === 'center'\" [class.align-end]=\"getActionsHeaderAlign() === 'end'\">\n <div class=\"praxis-actions-header__content\" [matTooltip]=\"getActionsHeaderTooltip() || ''\" [matTooltipDisabled]=\"!getActionsHeaderTooltip()\">\n <mat-icon *ngIf=\"getActionsHeaderIcon() as hi\" [praxisIcon]=\"hi\"></mat-icon>\n <span class=\"label\" *ngIf=\"getActionsHeaderLabel() as hl\">{{ hl }}</span>\n </div>\n </th>\n <td\n mat-cell\n *matCellDef=\"let row\"\n class=\"praxis-actions-cell\"\n [class.dense]=\"dense\"\n [style.width]=\"config.actions?.row?.width\"\n >\n <div class=\"praxis-actions-cell__content\">\n <!-- A\u00E7\u00F5es inline -->\n <!-- Inline actions: icons mode -->\n <ng-container *ngIf=\"config.actions?.row?.display === 'icons' || !config.actions?.row?.display\">\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <button\n mat-icon-button\n class=\"praxis-icon-btn\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event)\"\n [matTooltip]=\"a.label || getActionId(a)\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"praxis-tooltip\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\"\n >\n <mat-icon [praxisIcon]=\"a.icon\"></mat-icon>\n </button>\n </ng-container>\n </ng-container>\n\n <!-- Inline actions: buttons mode (show label + icon) -->\n <ng-container *ngIf=\"config.actions?.row?.display === 'buttons'\">\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <button\n mat-flat-button\n class=\"praxis-row-btn\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event)\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\"\n >\n <mat-icon [praxisIcon]=\"a.icon\"></mat-icon>\n <span>{{ a.label || getActionId(a) }}</span>\n </button>\n </ng-container>\n </ng-container>\n\n <!-- Menu de overflow -->\n <button\n mat-icon-button\n class=\"praxis-icon-btn praxis-more-btn\"\n *ngIf=\"hasOverflowRowActions(row)\"\n [matMenuTriggerFor]=\"rowMoreMenu\"\n [color]=\"getRowMenuButtonColor() || null\"\n aria-label=\"Mais a\u00E7\u00F5es\"\n >\n <mat-icon [praxisIcon]=\"getRowMenuIcon()\"></mat-icon>\n </button>\n <mat-menu #rowMoreMenu=\"matMenu\" xPosition=\"before\">\n <ng-container\n *ngFor=\"let a of getOverflowRowActions(row); trackBy: trackAction\"\n >\n <button\n mat-menu-item\n (click)=\"onRowAction(getActionId(a), row, $event)\"\n [disabled]=\"isActionDisabled(a, row)\"\n >\n <mat-icon [color]=\"a.color || null\" [praxisIcon]=\"a.icon\"></mat-icon>\n <span>{{ a.label || getActionId(a) }}</span>\n </button>\n </ng-container>\n </mat-menu>\n </div>\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <ng-container *ngIf=\"!isVirtualized()\">\n <tr\n mat-row\n *matRowDef=\"let row; let i = index; columns: displayedColumns\"\n [ngClass]=\"getRowClasses(row)\"\n [ngStyle]=\"getRowNgStyle(row)\"\n (click)=\"onRowClicked(row, i)\"\n (dblclick)=\"onRowDoubleClicked(row, i)\"\n ></tr>\n </ng-container>\n</table>\n\n<!-- Virtual rows path (header preserved above) -->\n<ng-container *ngIf=\"resourcePath && !schemaError && !dataError && isVirtualized()\">\n <cdk-virtual-scroll-viewport\n class=\"ptable-viewport\"\n [itemSize]=\"getVirtItemHeight()\"\n [minBufferPx]=\"getVirtBufferSize() * getVirtItemHeight()\"\n [maxBufferPx]=\"getVirtBufferSize() * getVirtItemHeight() * 2\"\n [style.minHeight]=\"getVirtMinHeightStyle()\"\n >\n <table class=\"mat-mdc-table mat-elevation-z8\" style=\"width:100%\">\n <tbody>\n <tr class=\"mat-mdc-row\"\n *cdkVirtualFor=\"let row of dataSource.data; let i = index; trackBy: trackByRow\"\n [ngClass]=\"getRowClasses(row)\"\n [ngStyle]=\"getRowNgStyle(row)\"\n (click)=\"onRowClicked(row, i)\"\n (dblclick)=\"onRowDoubleClicked(row, i)\">\n <!-- Selection column -->\n <td class=\"mat-mdc-cell\" *ngIf=\"config.behavior?.selection?.enabled\">\n <mat-checkbox\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleRow(row)\"\n [checked]=\"selection.isSelected(row)\">\n </mat-checkbox>\n </td>\n <!-- Data columns -->\n <td class=\"mat-mdc-cell\"\n *ngFor=\"let column of visibleColumns\"\n [style.text-align]=\"column.align\"\n [style.width]=\"column.width\"\n [attr.style]=\"column.style\"\n [ngClass]=\"getCellClasses(row, column)\"\n [ngStyle]=\"getCellNgStyle(row, column)\">\n <ng-container [ngSwitch]=\"getEffectiveRendererType(row, column)\">\n <ng-container *ngSwitchCase=\"'icon'\">\n <mat-icon [color]=\"getIconColor(row, column) || null\"\n [ngStyle]=\"getIconStyle(row, column)\"\n [attr.aria-label]=\"getIconAriaLabel(row, column) || null\">\n {{ getIconName(row, column) }}\n </mat-icon>\n </ng-container>\n <ng-container *ngSwitchCase=\"'image'\">\n <img class=\"pfx-cell-image\"\n [src]=\"getImageSrc(row, column)\"\n [attr.alt]=\"getImageAlt(row, column) || ''\"\n [attr.loading]=\"getImageLazy(row, column) ? 'lazy' : null\"\n [style.width.px]=\"getImageWidth(column)\"\n [style.height.px]=\"getImageHeight(column)\"\n [class.shape-rounded]=\"getImageShape(column) === 'rounded'\"\n [class.shape-circle]=\"getImageShape(column) === 'circle'\"\n [style.object-fit]=\"getImageFit(column)\" />\n </ng-container>\n <ng-container *ngSwitchCase=\"'badge'\">\n <span class=\"pfx-badge\" [ngClass]=\"getBadgeClasses(row, column)\">\n <mat-icon *ngIf=\"getBadgeIcon(row, column) as bi\" class=\"pfx-badge-icon\">{{ bi }}</mat-icon>\n <span class=\"pfx-badge-text\">{{ getBadgeText(row, column) }}</span>\n </span>\n </ng-container>\n <ng-container *ngSwitchDefault>\n {{ getCellValue(row, column) }}\n </ng-container>\n </ng-container>\n </td>\n\n <!-- Actions column -->\n <td class=\"mat-mdc-cell praxis-actions-cell\" *ngIf=\"config.actions?.row?.enabled\" [class.dense]=\"dense\" [style.width]=\"config.actions?.row?.width\">\n <div class=\"praxis-actions-cell__content\">\n <ng-container *ngIf=\"config.actions?.row?.display === 'icons' || !config.actions?.row?.display\">\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <button mat-icon-button class=\"praxis-icon-btn\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event)\"\n [matTooltip]=\"a.label || getActionId(a)\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"praxis-tooltip\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\">\n <mat-icon [praxisIcon]=\"a.icon\"></mat-icon>\n </button>\n </ng-container>\n </ng-container>\n <ng-container *ngIf=\"config.actions?.row?.display === 'buttons'\">\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <button mat-flat-button class=\"praxis-row-btn\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event)\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\">\n <mat-icon [praxisIcon]=\"a.icon\"></mat-icon>\n <span>{{ a.label || getActionId(a) }}</span>\n </button>\n </ng-container>\n </ng-container>\n <button mat-icon-button class=\"praxis-icon-btn praxis-more-btn\"\n *ngIf=\"hasOverflowRowActions(row)\"\n [matMenuTriggerFor]=\"rowMoreMenuV\"\n [color]=\"getRowMenuButtonColor() || null\"\n aria-label=\"Mais a\u00E7\u00F5es\">\n <mat-icon [praxisIcon]=\"getRowMenuIcon()\"></mat-icon>\n </button>\n <mat-menu #rowMoreMenuV=\"matMenu\" xPosition=\"before\">\n <ng-container *ngFor=\"let a of getOverflowRowActions(row); trackBy: trackAction\">\n <button mat-menu-item (click)=\"onRowAction(getActionId(a), row, $event)\" [disabled]=\"isActionDisabled(a, row)\">\n <mat-icon [color]=\"a.color || null\" [praxisIcon]=\"a.icon\"></mat-icon>\n <span>{{ a.label || getActionId(a) }}</span>\n </button>\n </ng-container>\n </mat-menu>\n </div>\n </td>\n </tr>\n </tbody>\n </table>\n </cdk-virtual-scroll-viewport>\n</ng-container>\n<!-- Paginadores (top/bottom) -->\n<mat-paginator\n *ngIf=\"resourcePath && !schemaError && !dataError && getPaginationEnabled() && (config.behavior?.pagination?.position === 'top' || config.behavior?.pagination?.position === 'both')\"\n [length]=\"getPaginationLength()\"\n [pageSize]=\"getPaginationPageSize()\"\n [pageSizeOptions]=\"getPaginationPageSizeOptions()\"\n [showFirstLastButtons]=\"getPaginationShowFirstLast()\"\n (page)=\"onPageChange($event)\"\n [class.compact]=\"config.behavior?.pagination?.style === 'compact'\"\n>\n</mat-paginator>\n\n<mat-paginator\n *ngIf=\"resourcePath && !schemaError && !dataError && getPaginationEnabled() && (config.behavior?.pagination?.position === 'bottom' || config.behavior?.pagination?.position === 'both' || !config.behavior?.pagination?.position)\"\n [length]=\"getPaginationLength()\"\n [pageSize]=\"getPaginationPageSize()\"\n [pageSizeOptions]=\"getPaginationPageSizeOptions()\"\n [showFirstLastButtons]=\"getPaginationShowFirstLast()\"\n (page)=\"onPageChange($event)\"\n [class.compact]=\"config.behavior?.pagination?.style === 'compact'\"\n>\n</mat-paginator>\n", styles: ["@charset \"UTF-8\";table{width:100%}.praxis-actions-cell{height:100%;padding-inline:12px;white-space:nowrap}.praxis-actions-cell__content{display:flex;align-items:center;justify-content:flex-end;gap:8px;width:100%}.praxis-actions-cell.dense .praxis-actions-cell__content{gap:6px}.praxis-icon-btn{width:var(--p-actions-btn-size, 40px);height:var(--p-actions-btn-size, 40px);border:0;background:transparent;padding:0;display:inline-flex;align-items:center;justify-content:center;border-radius:9999px;cursor:pointer;--mat-icon-button-state-layer-size: var(--p-actions-btn-size, 40px)}.praxis-icon-btn:hover{background:var(--md-sys-color-surface-variant, rgba(255, 255, 255, .06))}.praxis-icon-btn:focus-visible{outline:2px solid var(--primary, #48a1ff);outline-offset:2px}.praxis-icon-btn mat-icon,.praxis-icon-btn .mat-icon{font-size:var(--p-actions-icon-size, 22px);width:var(--p-actions-icon-size, 22px);height:var(--p-actions-icon-size, 22px);line-height:var(--p-actions-icon-size, 22px)}.praxis-more-btn{width:var(--p-actions-more-btn-size, var(--p-actions-btn-size, 40px));height:var(--p-actions-more-btn-size, var(--p-actions-btn-size, 40px));--mat-icon-button-state-layer-size: var(--p-actions-more-btn-size, var(--p-actions-btn-size, 40px));background-image:var(--p-actions-more-btn-gradient, none);background-size:100% 100%;background-repeat:no-repeat}.praxis-more-btn mat-icon,.praxis-more-btn .mat-icon{font-size:var(--p-actions-more-icon-size, var(--p-actions-icon-size, 22px));width:var(--p-actions-more-icon-size, var(--p-actions-icon-size, 22px));height:var(--p-actions-more-icon-size, var(--p-actions-icon-size, 22px));line-height:var(--p-actions-more-icon-size, var(--p-actions-icon-size, 22px));color:var(--p-actions-more-icon-color);background-image:var(--p-actions-more-icon-gradient, none);-webkit-background-clip:text;background-clip:text}.praxis-icon-btn.destructive mat-icon{color:#ff6b6b}.mat-mdc-tooltip.praxis-tooltip{margin-top:-8px;margin-bottom:8px}.spacer{flex:1 1 auto}.praxis-table-header{display:flex;flex-wrap:wrap;align-items:flex-start;gap:8px;margin:16px 0 12px;width:100%;clear:both;position:relative}.praxis-table-header.debug-layout,:host.debug-layout .praxis-table-header{outline:2px dashed #ff4d4f;border-radius:8px}:host.debug-layout ::ng-deep .praxis-filter-bar{outline:1px dashed #f59e0b}:host.debug-layout ::ng-deep .quick-shell{outline:1px dashed #3b82f6}:host.debug-layout ::ng-deep .always-fields{outline:1px dashed #22c55e}:host.debug-layout ::ng-deep .praxis-filter-overlay .praxis-filter-advanced{outline:2px solid #a855f7}:host.debug-layout ::ng-deep .mat-mdc-table{outline:1px dashed #94a3b8}.praxis-table-header>praxis-table-toolbar{flex:1 0 100%}:host{--p-table-header-bg: var(--md-sys-color-surface-container-highest, #1d1d1f);--p-table-header-fg: var(--md-sys-color-on-surface, #e8f3f1);--p-table-border-color: var(--md-sys-color-outline-variant, rgba(255, 255, 255, .12));--p-table-row-even-bg: var(--md-sys-color-surface-container, rgba(255, 255, 255, .04));--p-table-row-hover-bg: color-mix(in srgb, var(--md-sys-color-primary, #3f51b5) 10%, transparent);--p-table-row-selected-bg: var(--md-sys-color-primary-container, rgba(63,81,181,.14));--p-table-badge-soft-primary-bg: color-mix(in srgb, var(--mat-sys-primary, var(--md-sys-color-primary)) 16%, transparent);--p-table-badge-soft-primary-fg: var(--mat-sys-primary, var(--md-sys-color-primary));--p-table-badge-soft-accent-bg: color-mix(in srgb, var(--mat-sys-secondary, var(--md-sys-color-secondary, #ff4081)) 14%, transparent);--p-table-badge-soft-accent-fg: var(--mat-sys-secondary, var(--md-sys-color-secondary, #ff4081));--p-table-badge-soft-warn-bg: color-mix(in srgb, var(--mat-sys-error, var(--md-sys-color-error, #f44336)) 14%, transparent);--p-table-badge-soft-warn-fg: var(--mat-sys-error, var(--md-sys-color-error, #f44336));--p-table-state-success-bg: color-mix(in srgb, var(--mat-sys-tertiary, var(--md-sys-color-tertiary, #388E3C)) 16%, transparent);--p-table-state-success-fg: var(--md-sys-color-on-surface, #c8e6c9);--p-table-state-warning-bg: color-mix(in srgb, var(--md-sys-color-secondary, #FFA000) 18%, transparent);--p-table-state-warning-fg: var(--md-sys-color-on-surface, #ffe0b2);--p-table-state-danger-bg: color-mix(in srgb, var(--md-sys-color-error, #e53935) 18%, transparent);--p-table-state-danger-fg: var(--md-sys-color-on-surface, #ffcdd2);--p-table-state-highlight-bg: color-mix(in srgb, var(--md-sys-color-primary, #2196f3) 16%, transparent);--p-table-state-highlight-fg: var(--md-sys-color-on-surface, #bbdefb)}:host ::ng-deep .mat-mdc-table{background:var(--md-sys-color-surface-container-highest, #1d1d1f);border-radius:12px;width:100%}:host ::ng-deep .mat-mdc-header-row{position:sticky;top:0;z-index:1;background:var(--p-table-header-bg);color:var(--p-table-header-fg);box-shadow:var(--p-table-header-shadow, 0 1px 0 var(--p-table-border-color));border-bottom:1px solid var(--p-table-border-color)}:host ::ng-deep .mat-mdc-header-cell,:host ::ng-deep .mat-sort-header-content,:host ::ng-deep .mat-mdc-header-row .mat-icon{color:var(--p-table-header-fg)!important;font-weight:600}:host ::ng-deep .mat-mdc-header-cell.mat-sort-header-sorted .mat-sort-header-arrow,:host ::ng-deep .mat-mdc-header-cell:hover .mat-sort-header-arrow{color:var(--p-table-header-fg)!important}:host ::ng-deep .mat-mdc-header-cell.mat-sort-header-sorted .mat-sort-header-arrow .mat-sort-header-indicator,:host ::ng-deep .mat-mdc-header-cell.mat-sort-header-sorted .mat-sort-header-arrow .mat-sort-header-stem,:host ::ng-deep .mat-mdc-header-cell.mat-sort-header-sorted .mat-sort-header-arrow .mat-sort-header-pointer,:host ::ng-deep .mat-mdc-header-cell.mat-sort-header-sorted .mat-sort-header-arrow .mat-sort-header-pointer-left,:host ::ng-deep .mat-mdc-header-cell.mat-sort-header-sorted .mat-sort-header-arrow .mat-sort-header-pointer-right{border-color:var(--p-table-header-fg)!important;background:var(--p-table-header-fg)!important}:host ::ng-deep .mat-mdc-header-cell{padding:var(--p-header-padding, 12px 16px)!important;font-size:var(--p-header-font-size, inherit);font-weight:var(--p-header-font-weight, 600);letter-spacing:var(--p-header-letter-spacing, normal);text-transform:var(--p-header-text-transform, none);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.praxis-actions-header{text-align:right}.praxis-actions-header.align-start{text-align:left}.praxis-actions-header.align-center{text-align:center}.praxis-actions-header.align-end{text-align:right}.praxis-actions-header .praxis-actions-header__content{display:inline-flex;align-items:center;gap:var(--p-actions-header-gap, 6px);color:var(--p-actions-header-color, inherit)}.praxis-actions-header .praxis-actions-header__content .mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}:host ::ng-deep .mat-mdc-header-cell .mat-sort-header-container{padding-right:20px}:host ::ng-deep .mat-mdc-cell{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}:host ::ng-deep .mat-mdc-row:hover{background:var(--p-table-row-hover-bg)}:host ::ng-deep .mat-mdc-row:nth-child(2n){background:var(--p-table-row-even-bg)}.ptable-info-banner{display:flex;gap:12px;align-items:center;padding:8px 12px;margin:8px 0;border-radius:8px;border:1px solid var(--mat-sys-primary, var(--md-sys-color-primary, #1a73e8));background:color-mix(in srgb,var(--mat-sys-primary, var(--md-sys-color-primary, #1a73e8)) 10%,transparent)}.ptable-info-banner .text{flex:1;font-weight:600}.ptable-info-banner .actions{display:flex;gap:8px}.pfx-cell-image{display:inline-block;vertical-align:middle;background:var(--md-sys-color-surface-variant, #2a2a2a);border:1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, .08))}.pfx-cell-image.shape-rounded{border-radius:8px}.pfx-cell-image.shape-circle{border-radius:9999px}.pfx-badge{display:inline-flex;align-items:center;gap:6px;line-height:1;padding:4px 8px;border-radius:9999px;font-size:12px;font-weight:600;border:1px solid transparent}.pfx-badge .pfx-badge-icon{font-size:16px;width:16px;height:16px}.pfx-badge--filled-primary{background:var(--mat-sys-primary, #3f51b5);color:var(--mat-sys-on-primary, #fff)}.pfx-badge--filled-accent{background:var(--mat-sys-secondary, #ff4081);color:var(--mat-sys-on-secondary, #fff)}.pfx-badge--filled-warn{background:var(--mat-sys-error, #f44336);color:var(--mat-sys-on-error, #fff)}.pfx-badge--outlined{background:transparent;border-color:var(--mat-sys-outline-variant, rgba(255, 255, 255, .24));color:inherit}.pfx-badge--soft-primary{background:var(--p-table-badge-soft-primary-bg);color:var(--p-table-badge-soft-primary-fg)}.pfx-badge--soft-accent{background:var(--p-table-badge-soft-accent-bg);color:var(--p-table-badge-soft-accent-fg)}.pfx-badge--soft-warn{background:var(--p-table-badge-soft-warn-bg);color:var(--p-table-badge-soft-warn-fg)}.row--success,.row--success td,td.row--success{background-color:var(--p-table-state-success-bg)!important;color:var(--p-table-state-success-fg)!important}.row--warning,.row--warning td,td.row--warning{background-color:var(--p-table-state-warning-bg)!important;color:var(--p-table-state-warning-fg)!important}.row--danger,.row--danger td,td.row--danger{background-color:var(--p-table-state-danger-bg)!important;color:var(--p-table-state-danger-fg)!important}.row--highlight,.row--highlight td,td.row--highlight{background-color:var(--p-table-state-highlight-bg)!important;color:var(--p-table-state-highlight-fg)!important;font-weight:600}.row--muted,.row--muted td,td.row--muted{opacity:.7;filter:saturate(.6)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i8.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i8.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i8.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i8.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i8.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i8.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i8.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i8.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i8.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i8.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.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: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i12.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "ngmodule", type: MatSortModule }, { kind: "directive", type: i13$1.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i13$1.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i5.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i5.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i5.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i10.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i9.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatBadgeModule }, { kind: "directive", type: i15.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i19.ɵɵCdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i19.ɵɵCdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i19.ɵɵCdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "component", type: PraxisTableToolbar, selector: "praxis-table-toolbar", inputs: ["config", "debugLayout"], outputs: ["toolbarAction", "reset"] }, { kind: "component", type: PraxisFilter, selector: "praxis-filter", inputs: ["resourcePath", "formId", "mode", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "editModeEnabled", "value", "quickField", "alwaysVisibleFields", "tags", "allowSaveTags", "persistenceKey", "i18n", "changeDebounceMs", "showFilterSettings", "summary", "summaryTemplate", "summaryMap", "autoSummary", "confirmTagDelete", "debugLayout", "placeBooleansInActions", "showToggleLabels", "alwaysMinWidth", "alwaysColsMd", "alwaysColsLg", "tagColor", "tagVariant", "tagButtonColor", "actionsButtonColor", "actionsVariant", "overlayVariant", "overlayBackdrop", "advancedOpenMode"], outputs: ["submit", "change", "clear", "modeChange", "requestSearch", "tagsChange", "metaChanged", "schemaStatusChange"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline"] }] });
|
|
19139
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.4", type: PraxisTable, isStandalone: true, selector: "praxis-table", inputs: { config: "config", resourcePath: "resourcePath", filterCriteria: "filterCriteria", notifyIfOutdated: "notifyIfOutdated", snoozeMs: "snoozeMs", autoOpenSettingsOnOutdated: "autoOpenSettingsOnOutdated", showToolbar: "showToolbar", toolbarV2: "toolbarV2", autoDelete: "autoDelete", editModeEnabled: "editModeEnabled", dense: "dense", tableId: "tableId", debugLayout: "debugLayout", crudContext: "crudContext", idField: "idField" }, outputs: { rowClick: "rowClick", rowAction: "rowAction", toolbarAction: "toolbarAction", bulkAction: "bulkAction", rowDoubleClick: "rowDoubleClick", schemaStatusChange: "schemaStatusChange", beforeDelete: "beforeDelete", afterDelete: "afterDelete", deleteError: "deleteError", beforeBulkDelete: "beforeBulkDelete", afterBulkDelete: "afterBulkDelete", bulkDeleteError: "bulkDeleteError" }, host: { properties: { "class.debug-layout": "debugLayout", "class.density-compact": "config?.appearance?.density === 'compact'", "class.density-comfortable": "config?.appearance?.density === 'comfortable'", "class.density-spacious": "config?.appearance?.density === 'spacious'", "class.row-borders": "config?.appearance?.borders?.showRowBorders !== false", "class.col-borders": "!!config?.appearance?.borders?.showColumnBorders" } }, queries: [{ propertyName: "projectedFilter", first: true, predicate: PraxisFilter, descendants: true }], viewQueries: [{ propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }, { propertyName: "sort", first: true, predicate: MatSort, descendants: true }, { propertyName: "actionsHeaderCell", first: true, predicate: ["actionsHeaderCell"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<praxis-empty-state-card\n *ngIf=\"!resourcePath\"\n icon=\"link\"\n [title]=\"'Conecte a tabela \u00E0 fonte de dados'\"\n [description]=\"'Informe a rota do recurso da API para carregar colunas e dados automaticamente.'\"\n [primaryAction]=\"{ label: 'Conectar \u00E0 fonte de dados', icon: 'bolt', action: openQuickConnect.bind(this) }\"\n></praxis-empty-state-card>\n\n<!-- Error State with Quick Connect CTA -->\n<div class=\"ptable-error\" *ngIf=\"resourcePath && (schemaError || dataError)\" style=\"display:flex; align-items:center; gap:12px; padding:12px; border:1px solid var(--md-sys-color-error, #b00020); border-radius:8px; margin: 8px 0;\">\n <mat-icon color=\"warn\" aria-hidden=\"true\">error</mat-icon>\n <div style=\"flex:1\">\n <div style=\"font-weight:600\">Erro</div>\n <div>{{ errorMessage || 'Ocorreu um erro ao carregar a tabela.' }}</div>\n </div>\n <button mat-flat-button color=\"primary\" (click)=\"openQuickConnect()\">\n <mat-icon>bolt</mat-icon>\n Conectar a recurso\n </button>\n <button mat-stroked-button (click)=\"retryData()\" *ngIf=\"!schemaError\">Tentar novamente</button>\n <button mat-stroked-button (click)=\"reloadSchema()\" *ngIf=\"schemaError\">Recarregar colunas</button>\n </div>\n\n<!-- Inline banner for schema change (only in edit mode) -->\n<div *ngIf=\"shouldShowOutdatedInline()\" class=\"ptable-info-banner\" role=\"status\" aria-live=\"polite\">\n <div class=\"text\">O schema do servidor mudou. Reconciliar agora?</div>\n <div class=\"actions\">\n <button mat-stroked-button color=\"primary\" (click)=\"onReconcileRequested()\">\n <mat-icon>sync</mat-icon>\n Reconciliar\n </button>\n <button mat-button (click)=\"onSnoozeOutdated()\">Lembrar depois</button>\n <button mat-button (click)=\"onIgnoreOutdated()\">Ignorar</button>\n </div>\n </div>\n\n <ng-container *ngIf=\"resourcePath && !schemaError && !dataError && toolbarV2; else legacyHeader\">\n <div class=\"praxis-table-header\" [class.debug-layout]=\"debugLayout\" [class.edit-mode]=\"editModeEnabled\" *ngIf=\"showToolbar || editModeEnabled\">\n <praxis-table-toolbar\n *ngIf=\"showToolbar\"\n [config]=\"config\"\n [debugLayout]=\"debugLayout\"\n (toolbarAction)=\"onToolbarAction($event)\"\n (reset)=\"onResetPreferences()\"\n >\n <praxis-filter\n *ngIf=\"\n resourcePath &&\n config.behavior?.filtering?.advancedFilters?.enabled &&\n !projectedFilter\n \"\n advancedFilter\n [resourcePath]=\"resourcePath\"\n [formId]=\"tableId + '-filter'\"\n [editModeEnabled]=\"editModeEnabled\"\n [quickField]=\"\n config.behavior?.filtering?.advancedFilters?.settings?.quickField\n \"\n [alwaysVisibleFields]=\"\n config.behavior?.filtering?.advancedFilters?.settings\n ?.alwaysVisibleFields\n \"\n [allowSaveTags]=\"\n config.behavior?.filtering?.advancedFilters?.settings\n ?.allowSaveTags\n \"\n [changeDebounceMs]=\"\n config.behavior?.filtering?.advancedFilters?.settings\n ?.changeDebounceMs ?? 300\n \"\n [i18n]=\"getFilterI18n()\"\n [mode]=\"\n config.behavior?.filtering?.advancedFilters?.settings?.mode ??\n 'auto'\n \"\n [debugLayout]=\"debugLayout\"\n [showFilterSettings]=\"!editModeEnabled\"\n (submit)=\"onAdvancedFilterSubmit($event)\"\n (clear)=\"onAdvancedFilterClear()\"\n ></praxis-filter>\n <ng-content select=\"[advancedFilter]\" />\n <ng-content select=\"[toolbar]\" />\n <button end-actions mat-icon-button color=\"primary\" *ngIf=\"editModeEnabled\"\n (click)=\"openTableSettings()\" aria-label=\"Configura\u00E7\u00F5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u2014 Reconciliar' : 'Configura\u00E7\u00F5es'\"\n matTooltipPosition=\"below\">\n <mat-icon>settings</mat-icon>\n </button>\n </praxis-table-toolbar>\n <!-- Render a minimal settings button when toolbar is hidden but edit mode is enabled -->\n <div class=\"ptable-header-actions\" *ngIf=\"!showToolbar && editModeEnabled\">\n <button mat-icon-button color=\"primary\" (click)=\"openTableSettings()\" aria-label=\"Configura\u00E7\u00F5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u2014 Reconciliar' : 'Configura\u00E7\u00F5es'\"\n matTooltipPosition=\"below\">\n <mat-icon>settings</mat-icon>\n </button>\n <button mat-icon-button (click)=\"disconnect()\" aria-label=\"Desconectar\" matTooltip=\"Desconectar da fonte de dados\">\n <mat-icon>link_off</mat-icon>\n </button>\n </div>\n \n </div>\n</ng-container>\n<ng-template #legacyHeader>\n <ng-container *ngIf=\"resourcePath && !schemaError && !dataError\">\n <praxis-table-toolbar\n *ngIf=\"showToolbar\"\n [config]=\"config\"\n [debugLayout]=\"debugLayout\"\n (toolbarAction)=\"onToolbarAction($event)\"\n (reset)=\"onResetPreferences()\"\n >\n <praxis-filter\n *ngIf=\"\n resourcePath &&\n config.behavior?.filtering?.advancedFilters?.enabled &&\n !projectedFilter\n \"\n advancedFilter\n [resourcePath]=\"resourcePath\"\n [formId]=\"tableId + '-filter'\"\n [editModeEnabled]=\"editModeEnabled\"\n [quickField]=\"\n config.behavior?.filtering?.advancedFilters?.settings?.quickField\n \"\n [alwaysVisibleFields]=\"\n config.behavior?.filtering?.advancedFilters?.settings\n ?.alwaysVisibleFields\n \"\n [allowSaveTags]=\"\n config.behavior?.filtering?.advancedFilters?.settings?.allowSaveTags\n \"\n [changeDebounceMs]=\"\n config.behavior?.filtering?.advancedFilters?.settings\n ?.changeDebounceMs ?? 300\n \"\n [i18n]=\"getFilterI18n()\"\n [mode]=\"\n config.behavior?.filtering?.advancedFilters?.settings?.mode ??\n 'auto'\n \"\n [debugLayout]=\"debugLayout\"\n [showFilterSettings]=\"!editModeEnabled\"\n (submit)=\"onAdvancedFilterSubmit($event)\"\n (clear)=\"onAdvancedFilterClear()\"\n ></praxis-filter>\n <ng-content select=\"[advancedFilter]\" />\n <ng-content select=\"[toolbar]\" />\n <button end-actions mat-icon-button color=\"primary\" *ngIf=\"editModeEnabled\"\n (click)=\"openTableSettings()\" aria-label=\"Configura\u00E7\u00F5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u2014 Reconciliar' : 'Configura\u00E7\u00F5es'\"\n matTooltipPosition=\"below\">\n <mat-icon>settings</mat-icon>\n </button>\n </praxis-table-toolbar>\n <!-- Legacy header: settings button when toolbar hidden -->\n <div class=\"ptable-header-actions\" *ngIf=\"!showToolbar && editModeEnabled\">\n <button mat-icon-button color=\"primary\" (click)=\"openTableSettings()\" aria-label=\"Configura\u00E7\u00F5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u2014 Reconciliar' : 'Configura\u00E7\u00F5es'\"\n matTooltipPosition=\"below\">\n <mat-icon>settings</mat-icon>\n </button>\n </div>\n </ng-container>\n \n</ng-template>\n<table\n *ngIf=\"resourcePath && !schemaError && !dataError\"\n mat-table\n [dataSource]=\"dataSource\"\n matSort\n (matSortChange)=\"onSortChange($event)\"\n [matSortDisabled]=\"!getSortingEnabled()\"\n class=\"mat-elevation-z8\"\n>\n <ng-container\n *ngIf=\"config.behavior?.selection?.enabled\"\n matColumnDef=\"_select\"\n >\n <th mat-header-cell *matHeaderCellDef>\n <mat-checkbox\n (change)=\"masterToggle()\"\n [checked]=\"isAllSelected()\"\n [indeterminate]=\"selection.hasValue() && !isAllSelected()\"\n ></mat-checkbox>\n </th>\n <td mat-cell *matCellDef=\"let row\">\n <mat-checkbox\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleRow(row)\"\n [checked]=\"selection.isSelected(row)\"\n ></mat-checkbox>\n </td>\n </ng-container>\n <ng-container\n *ngFor=\"let column of visibleColumns\"\n [matColumnDef]=\"column.field\"\n [sticky]=\"column.sticky === true || column.sticky === 'start'\"\n [stickyEnd]=\"column.sticky === 'end'\"\n >\n <th\n mat-header-cell\n *matHeaderCellDef\n mat-sort-header\n [disabled]=\"!getSortingEnabled() || column.sortable === false\"\n [style.text-align]=\"column.align\"\n [style.width]=\"column.width\"\n [attr.style]=\"column.headerStyle\"\n >\n {{ column.header }}\n </th>\n <td\n mat-cell\n *matCellDef=\"let element\"\n [style.text-align]=\"column.align\"\n [style.width]=\"column.width\"\n [attr.style]=\"column.style\"\n [ngClass]=\"getCellClasses(element, column)\"\n [ngStyle]=\"getCellNgStyle(element, column)\"\n >\n <ng-container [ngSwitch]=\"getEffectiveRendererType(element, column)\">\n <!-- Icon renderer -->\n <ng-container *ngSwitchCase=\"'icon'\">\n <mat-icon\n [color]=\"getIconColor(element, column) || null\"\n [ngStyle]=\"getIconStyle(element, column)\"\n [attr.aria-label]=\"getIconAriaLabel(element, column) || null\"\n >{{ getIconName(element, column) }}</mat-icon\n >\n </ng-container>\n\n <!-- Image renderer -->\n <ng-container *ngSwitchCase=\"'image'\">\n <img\n class=\"pfx-cell-image\"\n [src]=\"getImageSrc(element, column)\"\n [attr.alt]=\"getImageAlt(element, column) || ''\"\n [attr.loading]=\"getImageLazy(element, column) ? 'lazy' : null\"\n [style.width.px]=\"getImageWidth(column)\"\n [style.height.px]=\"getImageHeight(column)\"\n [class.shape-rounded]=\"getImageShape(column) === 'rounded'\"\n [class.shape-circle]=\"getImageShape(column) === 'circle'\"\n [style.object-fit]=\"getImageFit(column)\"\n />\n </ng-container>\n\n <!-- Badge renderer -->\n <ng-container *ngSwitchCase=\"'badge'\">\n <span class=\"pfx-badge\" [ngClass]=\"getBadgeClasses(element, column)\">\n <mat-icon *ngIf=\"getBadgeIcon(element, column) as bi\" class=\"pfx-badge-icon\">{{ bi }}</mat-icon>\n <span class=\"pfx-badge-text\">{{ getBadgeText(element, column) }}</span>\n </span>\n </ng-container>\n\n <!-- Default text rendering -->\n <ng-container *ngSwitchDefault>\n {{ getCellValue(element, column) }}\n </ng-container>\n </ng-container>\n </td>\n </ng-container>\n <ng-container *ngIf=\"config.actions?.row?.enabled\" matColumnDef=\"_actions\" [sticky]=\"config.actions?.row?.sticky === true || config.actions?.row?.sticky === 'start'\" [stickyEnd]=\"config.actions?.row?.sticky === 'end'\">\n <th mat-header-cell *matHeaderCellDef #actionsHeaderCell [style.width]=\"config.actions?.row?.width\" class=\"praxis-actions-header\" [class.align-start]=\"getActionsHeaderAlign() === 'start'\" [class.align-center]=\"getActionsHeaderAlign() === 'center'\" [class.align-end]=\"getActionsHeaderAlign() === 'end'\">\n <div class=\"praxis-actions-header__content\" [matTooltip]=\"getActionsHeaderTooltip() || ''\" [matTooltipDisabled]=\"!getActionsHeaderTooltip()\">\n <mat-icon *ngIf=\"getActionsHeaderIcon() as hi\" [praxisIcon]=\"hi\"></mat-icon>\n <span class=\"label\" *ngIf=\"getActionsHeaderLabel() as hl\">{{ hl }}</span>\n </div>\n </th>\n <td\n mat-cell\n *matCellDef=\"let row\"\n class=\"praxis-actions-cell\"\n [class.dense]=\"dense\"\n [style.width]=\"config.actions?.row?.width\"\n >\n <div class=\"praxis-actions-cell__content\">\n <!-- A\u00E7\u00F5es inline -->\n <!-- Inline actions: icons mode -->\n <ng-container *ngIf=\"config.actions?.row?.display === 'icons' || !config.actions?.row?.display\">\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <button\n mat-icon-button\n class=\"praxis-icon-btn\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event)\"\n [matTooltip]=\"a.label || getActionId(a)\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"praxis-tooltip\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\"\n >\n <mat-icon [praxisIcon]=\"a.icon\"></mat-icon>\n </button>\n </ng-container>\n </ng-container>\n\n <!-- Inline actions: buttons mode (show label + icon) -->\n <ng-container *ngIf=\"config.actions?.row?.display === 'buttons'\">\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <button\n mat-flat-button\n class=\"praxis-row-btn\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event)\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\"\n >\n <mat-icon [praxisIcon]=\"a.icon\"></mat-icon>\n <span>{{ a.label || getActionId(a) }}</span>\n </button>\n </ng-container>\n </ng-container>\n\n <!-- Menu de overflow -->\n <button\n mat-icon-button\n class=\"praxis-icon-btn praxis-more-btn\"\n *ngIf=\"hasOverflowRowActions(row)\"\n [matMenuTriggerFor]=\"rowMoreMenu\"\n [color]=\"getRowMenuButtonColor() || null\"\n aria-label=\"Mais a\u00E7\u00F5es\"\n >\n <mat-icon [praxisIcon]=\"getRowMenuIcon()\"></mat-icon>\n </button>\n <mat-menu #rowMoreMenu=\"matMenu\" xPosition=\"before\">\n <ng-container\n *ngFor=\"let a of getOverflowRowActions(row); trackBy: trackAction\"\n >\n <button\n mat-menu-item\n (click)=\"onRowAction(getActionId(a), row, $event)\"\n [disabled]=\"isActionDisabled(a, row)\"\n >\n <mat-icon [color]=\"a.color || null\" [praxisIcon]=\"a.icon\"></mat-icon>\n <span>{{ a.label || getActionId(a) }}</span>\n </button>\n </ng-container>\n </mat-menu>\n </div>\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <ng-container *ngIf=\"!isVirtualized()\">\n <tr\n mat-row\n *matRowDef=\"let row; let i = index; columns: displayedColumns\"\n [ngClass]=\"getRowClasses(row)\"\n [ngStyle]=\"getRowNgStyle(row)\"\n (click)=\"onRowClicked(row, i)\"\n (dblclick)=\"onRowDoubleClicked(row, i)\"\n ></tr>\n </ng-container>\n</table>\n\n<!-- Virtual rows path (header preserved above) -->\n<ng-container *ngIf=\"resourcePath && !schemaError && !dataError && isVirtualized()\">\n <cdk-virtual-scroll-viewport\n class=\"ptable-viewport\"\n [itemSize]=\"getVirtItemHeight()\"\n [minBufferPx]=\"getVirtBufferSize() * getVirtItemHeight()\"\n [maxBufferPx]=\"getVirtBufferSize() * getVirtItemHeight() * 2\"\n [style.minHeight]=\"getVirtMinHeightStyle()\"\n >\n <table class=\"mat-mdc-table mat-elevation-z8\" style=\"width:100%\">\n <tbody>\n <tr class=\"mat-mdc-row\"\n *cdkVirtualFor=\"let row of dataSource.data; let i = index; trackBy: trackByRow\"\n [ngClass]=\"getRowClasses(row)\"\n [ngStyle]=\"getRowNgStyle(row)\"\n (click)=\"onRowClicked(row, i)\"\n (dblclick)=\"onRowDoubleClicked(row, i)\">\n <!-- Selection column -->\n <td class=\"mat-mdc-cell\" *ngIf=\"config.behavior?.selection?.enabled\">\n <mat-checkbox\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleRow(row)\"\n [checked]=\"selection.isSelected(row)\">\n </mat-checkbox>\n </td>\n <!-- Data columns -->\n <td class=\"mat-mdc-cell\"\n *ngFor=\"let column of visibleColumns\"\n [style.text-align]=\"column.align\"\n [style.width]=\"column.width\"\n [attr.style]=\"column.style\"\n [ngClass]=\"getCellClasses(row, column)\"\n [ngStyle]=\"getCellNgStyle(row, column)\">\n <ng-container [ngSwitch]=\"getEffectiveRendererType(row, column)\">\n <ng-container *ngSwitchCase=\"'icon'\">\n <mat-icon [color]=\"getIconColor(row, column) || null\"\n [ngStyle]=\"getIconStyle(row, column)\"\n [attr.aria-label]=\"getIconAriaLabel(row, column) || null\">\n {{ getIconName(row, column) }}\n </mat-icon>\n </ng-container>\n <ng-container *ngSwitchCase=\"'image'\">\n <img class=\"pfx-cell-image\"\n [src]=\"getImageSrc(row, column)\"\n [attr.alt]=\"getImageAlt(row, column) || ''\"\n [attr.loading]=\"getImageLazy(row, column) ? 'lazy' : null\"\n [style.width.px]=\"getImageWidth(column)\"\n [style.height.px]=\"getImageHeight(column)\"\n [class.shape-rounded]=\"getImageShape(column) === 'rounded'\"\n [class.shape-circle]=\"getImageShape(column) === 'circle'\"\n [style.object-fit]=\"getImageFit(column)\" />\n </ng-container>\n <ng-container *ngSwitchCase=\"'badge'\">\n <span class=\"pfx-badge\" [ngClass]=\"getBadgeClasses(row, column)\">\n <mat-icon *ngIf=\"getBadgeIcon(row, column) as bi\" class=\"pfx-badge-icon\">{{ bi }}</mat-icon>\n <span class=\"pfx-badge-text\">{{ getBadgeText(row, column) }}</span>\n </span>\n </ng-container>\n <ng-container *ngSwitchDefault>\n {{ getCellValue(row, column) }}\n </ng-container>\n </ng-container>\n </td>\n\n <!-- Actions column -->\n <td class=\"mat-mdc-cell praxis-actions-cell\" *ngIf=\"config.actions?.row?.enabled\" [class.dense]=\"dense\" [style.width]=\"config.actions?.row?.width\">\n <div class=\"praxis-actions-cell__content\">\n <ng-container *ngIf=\"config.actions?.row?.display === 'icons' || !config.actions?.row?.display\">\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <button mat-icon-button class=\"praxis-icon-btn\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event)\"\n [matTooltip]=\"a.label || getActionId(a)\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"praxis-tooltip\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\">\n <mat-icon [praxisIcon]=\"a.icon\"></mat-icon>\n </button>\n </ng-container>\n </ng-container>\n <ng-container *ngIf=\"config.actions?.row?.display === 'buttons'\">\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <button mat-flat-button class=\"praxis-row-btn\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event)\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\">\n <mat-icon [praxisIcon]=\"a.icon\"></mat-icon>\n <span>{{ a.label || getActionId(a) }}</span>\n </button>\n </ng-container>\n </ng-container>\n <button mat-icon-button class=\"praxis-icon-btn praxis-more-btn\"\n *ngIf=\"hasOverflowRowActions(row)\"\n [matMenuTriggerFor]=\"rowMoreMenuV\"\n [color]=\"getRowMenuButtonColor() || null\"\n aria-label=\"Mais a\u00E7\u00F5es\">\n <mat-icon [praxisIcon]=\"getRowMenuIcon()\"></mat-icon>\n </button>\n <mat-menu #rowMoreMenuV=\"matMenu\" xPosition=\"before\">\n <ng-container *ngFor=\"let a of getOverflowRowActions(row); trackBy: trackAction\">\n <button mat-menu-item (click)=\"onRowAction(getActionId(a), row, $event)\" [disabled]=\"isActionDisabled(a, row)\">\n <mat-icon [color]=\"a.color || null\" [praxisIcon]=\"a.icon\"></mat-icon>\n <span>{{ a.label || getActionId(a) }}</span>\n </button>\n </ng-container>\n </mat-menu>\n </div>\n </td>\n </tr>\n </tbody>\n </table>\n </cdk-virtual-scroll-viewport>\n</ng-container>\n<!-- Paginadores (top/bottom) -->\n<mat-paginator\n *ngIf=\"resourcePath && !schemaError && !dataError && getPaginationEnabled() && (config.behavior?.pagination?.position === 'top' || config.behavior?.pagination?.position === 'both')\"\n [length]=\"getPaginationLength()\"\n [pageSize]=\"getPaginationPageSize()\"\n [pageSizeOptions]=\"getPaginationPageSizeOptions()\"\n [showFirstLastButtons]=\"getPaginationShowFirstLast()\"\n (page)=\"onPageChange($event)\"\n [class.compact]=\"config.behavior?.pagination?.style === 'compact'\"\n>\n</mat-paginator>\n\n<mat-paginator\n *ngIf=\"resourcePath && !schemaError && !dataError && getPaginationEnabled() && (config.behavior?.pagination?.position === 'bottom' || config.behavior?.pagination?.position === 'both' || !config.behavior?.pagination?.position)\"\n [length]=\"getPaginationLength()\"\n [pageSize]=\"getPaginationPageSize()\"\n [pageSizeOptions]=\"getPaginationPageSizeOptions()\"\n [showFirstLastButtons]=\"getPaginationShowFirstLast()\"\n (page)=\"onPageChange($event)\"\n [class.compact]=\"config.behavior?.pagination?.style === 'compact'\"\n>\n</mat-paginator>\n", styles: ["@charset \"UTF-8\";table{width:100%}.praxis-actions-cell{height:100%;padding-inline:12px;white-space:nowrap}:host.density-compact{--p-header-padding: 8px 12px;--p-actions-btn-size: 32px;--p-actions-icon-size: 18px}:host.density-comfortable{--p-header-padding: 12px 16px;--p-actions-btn-size: 40px;--p-actions-icon-size: 22px}:host.density-spacious{--p-header-padding: 16px 20px;--p-actions-btn-size: 44px;--p-actions-icon-size: 24px}:host.density-compact ::ng-deep .mat-mdc-cell{padding:8px 12px}:host.density-comfortable ::ng-deep .mat-mdc-cell{padding:12px 16px}:host.density-spacious ::ng-deep .mat-mdc-cell{padding:16px 20px}:host.density-compact .praxis-actions-cell{padding-inline:8px}:host.density-spacious .praxis-actions-cell{padding-inline:16px}.praxis-actions-cell__content{display:flex;align-items:center;justify-content:flex-end;gap:8px;width:100%}.praxis-actions-cell.dense .praxis-actions-cell__content{gap:6px}.praxis-icon-btn{width:var(--p-actions-btn-size, 40px);height:var(--p-actions-btn-size, 40px);border:0;background:transparent;padding:0;display:inline-flex;align-items:center;justify-content:center;border-radius:9999px;cursor:pointer;--mat-icon-button-state-layer-size: var(--p-actions-btn-size, 40px)}.praxis-icon-btn:hover{background:var(--md-sys-color-surface-variant, rgba(255, 255, 255, .06))}.praxis-icon-btn:focus-visible{outline:2px solid var(--primary, #48a1ff);outline-offset:2px}.praxis-icon-btn mat-icon,.praxis-icon-btn .mat-icon{font-size:var(--p-actions-icon-size, 22px);width:var(--p-actions-icon-size, 22px);height:var(--p-actions-icon-size, 22px);line-height:var(--p-actions-icon-size, 22px)}.praxis-more-btn{width:var(--p-actions-more-btn-size, var(--p-actions-btn-size, 40px));height:var(--p-actions-more-btn-size, var(--p-actions-btn-size, 40px));--mat-icon-button-state-layer-size: var(--p-actions-more-btn-size, var(--p-actions-btn-size, 40px));background-image:var(--p-actions-more-btn-gradient, none);background-size:100% 100%;background-repeat:no-repeat}.praxis-more-btn mat-icon,.praxis-more-btn .mat-icon{font-size:var(--p-actions-more-icon-size, var(--p-actions-icon-size, 22px));width:var(--p-actions-more-icon-size, var(--p-actions-icon-size, 22px));height:var(--p-actions-more-icon-size, var(--p-actions-icon-size, 22px));line-height:var(--p-actions-more-icon-size, var(--p-actions-icon-size, 22px));color:var(--p-actions-more-icon-color);background-image:var(--p-actions-more-icon-gradient, none);-webkit-background-clip:text;background-clip:text}.praxis-icon-btn.destructive mat-icon{color:#ff6b6b}.mat-mdc-tooltip.praxis-tooltip{margin-top:-8px;margin-bottom:8px}.spacer{flex:1 1 auto}.praxis-table-header{display:flex;flex-wrap:wrap;align-items:flex-start;gap:8px;margin:16px 0 12px;width:100%;clear:both;position:relative}.praxis-table-header.debug-layout,:host.debug-layout .praxis-table-header{outline:2px dashed #ff4d4f;border-radius:8px}:host.debug-layout ::ng-deep .praxis-filter-bar{outline:1px dashed #f59e0b}:host.debug-layout ::ng-deep .quick-shell{outline:1px dashed #3b82f6}:host.debug-layout ::ng-deep .always-fields{outline:1px dashed #22c55e}:host.debug-layout ::ng-deep .praxis-filter-overlay .praxis-filter-advanced{outline:2px solid #a855f7}:host.debug-layout ::ng-deep .mat-mdc-table{outline:1px dashed #94a3b8}.praxis-table-header>praxis-table-toolbar{flex:1 0 100%}:host{--p-table-header-bg: var(--md-sys-color-surface-container-highest, #1d1d1f);--p-table-header-fg: var(--md-sys-color-on-surface, #e8f3f1);--p-table-border-color: var(--md-sys-color-outline-variant, rgba(255, 255, 255, .12));--p-table-row-even-bg: var(--md-sys-color-surface-container, rgba(255, 255, 255, .04));--p-table-row-hover-bg: color-mix(in srgb, var(--md-sys-color-primary, #3f51b5) 10%, transparent);--p-table-row-selected-bg: var(--md-sys-color-primary-container, rgba(63,81,181,.14));--p-table-badge-soft-primary-bg: color-mix(in srgb, var(--mat-sys-primary, var(--md-sys-color-primary)) 16%, transparent);--p-table-badge-soft-primary-fg: var(--mat-sys-primary, var(--md-sys-color-primary));--p-table-badge-soft-accent-bg: color-mix(in srgb, var(--mat-sys-secondary, var(--md-sys-color-secondary, #ff4081)) 14%, transparent);--p-table-badge-soft-accent-fg: var(--mat-sys-secondary, var(--md-sys-color-secondary, #ff4081));--p-table-badge-soft-warn-bg: color-mix(in srgb, var(--mat-sys-error, var(--md-sys-color-error, #f44336)) 14%, transparent);--p-table-badge-soft-warn-fg: var(--mat-sys-error, var(--md-sys-color-error, #f44336));--p-table-state-success-bg: color-mix(in srgb, var(--mat-sys-tertiary, var(--md-sys-color-tertiary, #388E3C)) 16%, transparent);--p-table-state-success-fg: var(--md-sys-color-on-surface, #c8e6c9);--p-table-state-warning-bg: color-mix(in srgb, var(--md-sys-color-secondary, #FFA000) 18%, transparent);--p-table-state-warning-fg: var(--md-sys-color-on-surface, #ffe0b2);--p-table-state-danger-bg: color-mix(in srgb, var(--md-sys-color-error, #e53935) 18%, transparent);--p-table-state-danger-fg: var(--md-sys-color-on-surface, #ffcdd2);--p-table-state-highlight-bg: color-mix(in srgb, var(--md-sys-color-primary, #2196f3) 16%, transparent);--p-table-state-highlight-fg: var(--md-sys-color-on-surface, #bbdefb)}:host ::ng-deep .mat-mdc-table{background:var(--md-sys-color-surface-container-highest, #1d1d1f);border-radius:12px;width:100%}:host ::ng-deep .mat-mdc-header-row{position:sticky;top:0;z-index:1;background:var(--p-table-header-bg);color:var(--p-table-header-fg);box-shadow:var(--p-table-header-shadow, 0 1px 0 var(--p-table-border-color));border-bottom:1px solid var(--p-table-border-color)}:host ::ng-deep .mat-mdc-header-cell,:host ::ng-deep .mat-sort-header-content,:host ::ng-deep .mat-mdc-header-row .mat-icon{color:var(--p-table-header-fg)!important;font-weight:600}:host ::ng-deep .mat-mdc-header-cell.mat-sort-header-sorted .mat-sort-header-arrow,:host ::ng-deep .mat-mdc-header-cell:hover .mat-sort-header-arrow{color:var(--p-table-header-fg)!important}:host ::ng-deep .mat-mdc-header-cell.mat-sort-header-sorted .mat-sort-header-arrow .mat-sort-header-indicator,:host ::ng-deep .mat-mdc-header-cell.mat-sort-header-sorted .mat-sort-header-arrow .mat-sort-header-stem,:host ::ng-deep .mat-mdc-header-cell.mat-sort-header-sorted .mat-sort-header-arrow .mat-sort-header-pointer,:host ::ng-deep .mat-mdc-header-cell.mat-sort-header-sorted .mat-sort-header-arrow .mat-sort-header-pointer-left,:host ::ng-deep .mat-mdc-header-cell.mat-sort-header-sorted .mat-sort-header-arrow .mat-sort-header-pointer-right{border-color:var(--p-table-header-fg)!important;background:var(--p-table-header-fg)!important}:host ::ng-deep .mat-mdc-header-cell{padding:var(--p-header-padding, 12px 16px)!important;font-size:var(--p-header-font-size, inherit);font-weight:var(--p-header-font-weight, 600);letter-spacing:var(--p-header-letter-spacing, normal);text-transform:var(--p-header-text-transform, none);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.praxis-actions-header{text-align:right}.praxis-actions-header.align-start{text-align:left}.praxis-actions-header.align-center{text-align:center}.praxis-actions-header.align-end{text-align:right}.praxis-actions-header .praxis-actions-header__content{display:inline-flex;align-items:center;gap:var(--p-actions-header-gap, 6px);color:var(--p-actions-header-color, inherit)}.praxis-actions-header .praxis-actions-header__content .mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}:host ::ng-deep .mat-mdc-header-cell .mat-sort-header-container{padding-right:20px}:host ::ng-deep .mat-mdc-cell{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}:host ::ng-deep .mat-mdc-row:hover{background:var(--p-table-row-hover-bg)}:host ::ng-deep .mat-mdc-row:nth-child(2n){background:var(--p-table-row-even-bg)}:host.row-borders ::ng-deep .mat-mdc-row .mat-mdc-cell{border-bottom:1px solid var(--p-table-border-color)}:host.row-borders ::ng-deep .mat-mdc-header-row .mat-mdc-header-cell{border-bottom:none}:host.col-borders ::ng-deep .mat-mdc-header-row .mat-mdc-header-cell,:host.col-borders ::ng-deep .mat-mdc-row .mat-mdc-cell{border-right:1px solid var(--p-table-border-color)}:host.col-borders ::ng-deep .mat-mdc-header-row .mat-mdc-header-cell:last-child,:host.col-borders ::ng-deep .mat-mdc-row .mat-mdc-cell:last-child{border-right:none}.ptable-info-banner{display:flex;gap:12px;align-items:center;padding:8px 12px;margin:8px 0;border-radius:8px;border:1px solid var(--mat-sys-primary, var(--md-sys-color-primary, #1a73e8));background:color-mix(in srgb,var(--mat-sys-primary, var(--md-sys-color-primary, #1a73e8)) 10%,transparent)}.ptable-info-banner .text{flex:1;font-weight:600}.ptable-info-banner .actions{display:flex;gap:8px}.pfx-cell-image{display:inline-block;vertical-align:middle;background:var(--md-sys-color-surface-variant, #2a2a2a);border:1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, .08))}.pfx-cell-image.shape-rounded{border-radius:8px}.pfx-cell-image.shape-circle{border-radius:9999px}.pfx-badge{display:inline-flex;align-items:center;gap:6px;line-height:1;padding:4px 8px;border-radius:9999px;font-size:12px;font-weight:600;border:1px solid transparent}.pfx-badge .pfx-badge-icon{font-size:16px;width:16px;height:16px}.pfx-badge--filled-primary{background:var(--mat-sys-primary, #3f51b5);color:var(--mat-sys-on-primary, #fff)}.pfx-badge--filled-accent{background:var(--mat-sys-secondary, #ff4081);color:var(--mat-sys-on-secondary, #fff)}.pfx-badge--filled-warn{background:var(--mat-sys-error, #f44336);color:var(--mat-sys-on-error, #fff)}.pfx-badge--outlined{background:transparent;border-color:var(--mat-sys-outline-variant, rgba(255, 255, 255, .24));color:inherit}.pfx-badge--soft-primary{background:var(--p-table-badge-soft-primary-bg);color:var(--p-table-badge-soft-primary-fg)}.pfx-badge--soft-accent{background:var(--p-table-badge-soft-accent-bg);color:var(--p-table-badge-soft-accent-fg)}.pfx-badge--soft-warn{background:var(--p-table-badge-soft-warn-bg);color:var(--p-table-badge-soft-warn-fg)}.row--success,.row--success td,td.row--success{background-color:var(--p-table-state-success-bg)!important;color:var(--p-table-state-success-fg)!important}.row--warning,.row--warning td,td.row--warning{background-color:var(--p-table-state-warning-bg)!important;color:var(--p-table-state-warning-fg)!important}.row--danger,.row--danger td,td.row--danger{background-color:var(--p-table-state-danger-bg)!important;color:var(--p-table-state-danger-fg)!important}.row--highlight,.row--highlight td,td.row--highlight{background-color:var(--p-table-state-highlight-bg)!important;color:var(--p-table-state-highlight-fg)!important;font-weight:600}.row--muted,.row--muted td,td.row--muted{opacity:.7;filter:saturate(.6)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i8.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i8.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i8.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i8.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i8.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i8.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i8.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i8.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i8.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i8.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.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: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i12.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "ngmodule", type: MatSortModule }, { kind: "directive", type: i13$1.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i13$1.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i5.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i5.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i5.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i10.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i9.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatBadgeModule }, { kind: "directive", type: i15.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i19.ɵɵCdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i19.ɵɵCdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i19.ɵɵCdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "component", type: PraxisTableToolbar, selector: "praxis-table-toolbar", inputs: ["config", "debugLayout"], outputs: ["toolbarAction", "reset"] }, { kind: "component", type: PraxisFilter, selector: "praxis-filter", inputs: ["resourcePath", "formId", "mode", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "editModeEnabled", "value", "quickField", "alwaysVisibleFields", "tags", "allowSaveTags", "persistenceKey", "i18n", "changeDebounceMs", "showFilterSettings", "summary", "summaryTemplate", "summaryMap", "autoSummary", "confirmTagDelete", "debugLayout", "placeBooleansInActions", "showToggleLabels", "alwaysMinWidth", "alwaysColsMd", "alwaysColsLg", "tagColor", "tagVariant", "tagButtonColor", "actionsButtonColor", "actionsVariant", "overlayVariant", "overlayBackdrop", "advancedOpenMode"], outputs: ["submit", "change", "clear", "modeChange", "requestSearch", "tagsChange", "metaChanged", "schemaStatusChange"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline"] }] });
|
|
19103
19140
|
}
|
|
19104
19141
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PraxisTable, decorators: [{
|
|
19105
19142
|
type: Component,
|
|
@@ -19120,7 +19157,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
19120
19157
|
PraxisFilter,
|
|
19121
19158
|
PraxisIconDirective,
|
|
19122
19159
|
EmptyStateCardComponent,
|
|
19123
|
-
], host: { '[class.debug-layout]': 'debugLayout' }, template: "<praxis-empty-state-card\n *ngIf=\"!resourcePath\"\n icon=\"link\"\n [title]=\"'Conecte a tabela \u00E0 fonte de dados'\"\n [description]=\"'Informe a rota do recurso da API para carregar colunas e dados automaticamente.'\"\n [primaryAction]=\"{ label: 'Conectar \u00E0 fonte de dados', icon: 'bolt', action: openQuickConnect.bind(this) }\"\n></praxis-empty-state-card>\n\n<!-- Error State with Quick Connect CTA -->\n<div class=\"ptable-error\" *ngIf=\"resourcePath && (schemaError || dataError)\" style=\"display:flex; align-items:center; gap:12px; padding:12px; border:1px solid var(--md-sys-color-error, #b00020); border-radius:8px; margin: 8px 0;\">\n <mat-icon color=\"warn\" aria-hidden=\"true\">error</mat-icon>\n <div style=\"flex:1\">\n <div style=\"font-weight:600\">Erro</div>\n <div>{{ errorMessage || 'Ocorreu um erro ao carregar a tabela.' }}</div>\n </div>\n <button mat-flat-button color=\"primary\" (click)=\"openQuickConnect()\">\n <mat-icon>bolt</mat-icon>\n Conectar a recurso\n </button>\n <button mat-stroked-button (click)=\"retryData()\" *ngIf=\"!schemaError\">Tentar novamente</button>\n <button mat-stroked-button (click)=\"reloadSchema()\" *ngIf=\"schemaError\">Recarregar colunas</button>\n </div>\n\n<!-- Inline banner for schema change (only in edit mode) -->\n<div *ngIf=\"shouldShowOutdatedInline()\" class=\"ptable-info-banner\" role=\"status\" aria-live=\"polite\">\n <div class=\"text\">O schema do servidor mudou. Reconciliar agora?</div>\n <div class=\"actions\">\n <button mat-stroked-button color=\"primary\" (click)=\"onReconcileRequested()\">\n <mat-icon>sync</mat-icon>\n Reconciliar\n </button>\n <button mat-button (click)=\"onSnoozeOutdated()\">Lembrar depois</button>\n <button mat-button (click)=\"onIgnoreOutdated()\">Ignorar</button>\n </div>\n </div>\n\n <ng-container *ngIf=\"resourcePath && !schemaError && !dataError && toolbarV2; else legacyHeader\">\n <div class=\"praxis-table-header\" [class.debug-layout]=\"debugLayout\" [class.edit-mode]=\"editModeEnabled\" *ngIf=\"showToolbar || editModeEnabled\">\n <praxis-table-toolbar\n *ngIf=\"showToolbar\"\n [config]=\"config\"\n [debugLayout]=\"debugLayout\"\n (toolbarAction)=\"onToolbarAction($event)\"\n (reset)=\"onResetPreferences()\"\n >\n <praxis-filter\n *ngIf=\"\n resourcePath &&\n config.behavior?.filtering?.advancedFilters?.enabled &&\n !projectedFilter\n \"\n advancedFilter\n [resourcePath]=\"resourcePath\"\n [formId]=\"tableId + '-filter'\"\n [editModeEnabled]=\"editModeEnabled\"\n [quickField]=\"\n config.behavior?.filtering?.advancedFilters?.settings?.quickField\n \"\n [alwaysVisibleFields]=\"\n config.behavior?.filtering?.advancedFilters?.settings\n ?.alwaysVisibleFields\n \"\n [allowSaveTags]=\"\n config.behavior?.filtering?.advancedFilters?.settings\n ?.allowSaveTags\n \"\n [changeDebounceMs]=\"\n config.behavior?.filtering?.advancedFilters?.settings\n ?.changeDebounceMs ?? 300\n \"\n [i18n]=\"getFilterI18n()\"\n [mode]=\"\n config.behavior?.filtering?.advancedFilters?.settings?.mode ??\n 'auto'\n \"\n [debugLayout]=\"debugLayout\"\n [showFilterSettings]=\"!editModeEnabled\"\n (submit)=\"onAdvancedFilterSubmit($event)\"\n (clear)=\"onAdvancedFilterClear()\"\n ></praxis-filter>\n <ng-content select=\"[advancedFilter]\" />\n <ng-content select=\"[toolbar]\" />\n <button end-actions mat-icon-button color=\"primary\" *ngIf=\"editModeEnabled\"\n (click)=\"openTableSettings()\" aria-label=\"Configura\u00E7\u00F5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u2014 Reconciliar' : 'Configura\u00E7\u00F5es'\"\n matTooltipPosition=\"below\">\n <mat-icon>settings</mat-icon>\n </button>\n </praxis-table-toolbar>\n <!-- Render a minimal settings button when toolbar is hidden but edit mode is enabled -->\n <div class=\"ptable-header-actions\" *ngIf=\"!showToolbar && editModeEnabled\">\n <button mat-icon-button color=\"primary\" (click)=\"openTableSettings()\" aria-label=\"Configura\u00E7\u00F5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u2014 Reconciliar' : 'Configura\u00E7\u00F5es'\"\n matTooltipPosition=\"below\">\n <mat-icon>settings</mat-icon>\n </button>\n <button mat-icon-button (click)=\"disconnect()\" aria-label=\"Desconectar\" matTooltip=\"Desconectar da fonte de dados\">\n <mat-icon>link_off</mat-icon>\n </button>\n </div>\n \n </div>\n</ng-container>\n<ng-template #legacyHeader>\n <ng-container *ngIf=\"resourcePath && !schemaError && !dataError\">\n <praxis-table-toolbar\n *ngIf=\"showToolbar\"\n [config]=\"config\"\n [debugLayout]=\"debugLayout\"\n (toolbarAction)=\"onToolbarAction($event)\"\n (reset)=\"onResetPreferences()\"\n >\n <praxis-filter\n *ngIf=\"\n resourcePath &&\n config.behavior?.filtering?.advancedFilters?.enabled &&\n !projectedFilter\n \"\n advancedFilter\n [resourcePath]=\"resourcePath\"\n [formId]=\"tableId + '-filter'\"\n [editModeEnabled]=\"editModeEnabled\"\n [quickField]=\"\n config.behavior?.filtering?.advancedFilters?.settings?.quickField\n \"\n [alwaysVisibleFields]=\"\n config.behavior?.filtering?.advancedFilters?.settings\n ?.alwaysVisibleFields\n \"\n [allowSaveTags]=\"\n config.behavior?.filtering?.advancedFilters?.settings?.allowSaveTags\n \"\n [changeDebounceMs]=\"\n config.behavior?.filtering?.advancedFilters?.settings\n ?.changeDebounceMs ?? 300\n \"\n [i18n]=\"getFilterI18n()\"\n [mode]=\"\n config.behavior?.filtering?.advancedFilters?.settings?.mode ??\n 'auto'\n \"\n [debugLayout]=\"debugLayout\"\n [showFilterSettings]=\"!editModeEnabled\"\n (submit)=\"onAdvancedFilterSubmit($event)\"\n (clear)=\"onAdvancedFilterClear()\"\n ></praxis-filter>\n <ng-content select=\"[advancedFilter]\" />\n <ng-content select=\"[toolbar]\" />\n <button end-actions mat-icon-button color=\"primary\" *ngIf=\"editModeEnabled\"\n (click)=\"openTableSettings()\" aria-label=\"Configura\u00E7\u00F5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u2014 Reconciliar' : 'Configura\u00E7\u00F5es'\"\n matTooltipPosition=\"below\">\n <mat-icon>settings</mat-icon>\n </button>\n </praxis-table-toolbar>\n <!-- Legacy header: settings button when toolbar hidden -->\n <div class=\"ptable-header-actions\" *ngIf=\"!showToolbar && editModeEnabled\">\n <button mat-icon-button color=\"primary\" (click)=\"openTableSettings()\" aria-label=\"Configura\u00E7\u00F5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u2014 Reconciliar' : 'Configura\u00E7\u00F5es'\"\n matTooltipPosition=\"below\">\n <mat-icon>settings</mat-icon>\n </button>\n </div>\n </ng-container>\n \n</ng-template>\n<table\n *ngIf=\"resourcePath && !schemaError && !dataError\"\n mat-table\n [dataSource]=\"dataSource\"\n matSort\n (matSortChange)=\"onSortChange($event)\"\n [matSortDisabled]=\"!getSortingEnabled()\"\n class=\"mat-elevation-z8\"\n>\n <ng-container\n *ngIf=\"config.behavior?.selection?.enabled\"\n matColumnDef=\"_select\"\n >\n <th mat-header-cell *matHeaderCellDef>\n <mat-checkbox\n (change)=\"masterToggle()\"\n [checked]=\"isAllSelected()\"\n [indeterminate]=\"selection.hasValue() && !isAllSelected()\"\n ></mat-checkbox>\n </th>\n <td mat-cell *matCellDef=\"let row\">\n <mat-checkbox\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleRow(row)\"\n [checked]=\"selection.isSelected(row)\"\n ></mat-checkbox>\n </td>\n </ng-container>\n <ng-container\n *ngFor=\"let column of visibleColumns\"\n [matColumnDef]=\"column.field\"\n [sticky]=\"column.sticky === true || column.sticky === 'start'\"\n [stickyEnd]=\"column.sticky === 'end'\"\n >\n <th\n mat-header-cell\n *matHeaderCellDef\n mat-sort-header\n [disabled]=\"!getSortingEnabled() || column.sortable === false\"\n [style.text-align]=\"column.align\"\n [style.width]=\"column.width\"\n [attr.style]=\"column.headerStyle\"\n >\n {{ column.header }}\n </th>\n <td\n mat-cell\n *matCellDef=\"let element\"\n [style.text-align]=\"column.align\"\n [style.width]=\"column.width\"\n [attr.style]=\"column.style\"\n [ngClass]=\"getCellClasses(element, column)\"\n [ngStyle]=\"getCellNgStyle(element, column)\"\n >\n <ng-container [ngSwitch]=\"getEffectiveRendererType(element, column)\">\n <!-- Icon renderer -->\n <ng-container *ngSwitchCase=\"'icon'\">\n <mat-icon\n [color]=\"getIconColor(element, column) || null\"\n [ngStyle]=\"getIconStyle(element, column)\"\n [attr.aria-label]=\"getIconAriaLabel(element, column) || null\"\n >{{ getIconName(element, column) }}</mat-icon\n >\n </ng-container>\n\n <!-- Image renderer -->\n <ng-container *ngSwitchCase=\"'image'\">\n <img\n class=\"pfx-cell-image\"\n [src]=\"getImageSrc(element, column)\"\n [attr.alt]=\"getImageAlt(element, column) || ''\"\n [attr.loading]=\"getImageLazy(element, column) ? 'lazy' : null\"\n [style.width.px]=\"getImageWidth(column)\"\n [style.height.px]=\"getImageHeight(column)\"\n [class.shape-rounded]=\"getImageShape(column) === 'rounded'\"\n [class.shape-circle]=\"getImageShape(column) === 'circle'\"\n [style.object-fit]=\"getImageFit(column)\"\n />\n </ng-container>\n\n <!-- Badge renderer -->\n <ng-container *ngSwitchCase=\"'badge'\">\n <span class=\"pfx-badge\" [ngClass]=\"getBadgeClasses(element, column)\">\n <mat-icon *ngIf=\"getBadgeIcon(element, column) as bi\" class=\"pfx-badge-icon\">{{ bi }}</mat-icon>\n <span class=\"pfx-badge-text\">{{ getBadgeText(element, column) }}</span>\n </span>\n </ng-container>\n\n <!-- Default text rendering -->\n <ng-container *ngSwitchDefault>\n {{ getCellValue(element, column) }}\n </ng-container>\n </ng-container>\n </td>\n </ng-container>\n <ng-container *ngIf=\"config.actions?.row?.enabled\" matColumnDef=\"_actions\" [sticky]=\"config.actions?.row?.sticky === true || config.actions?.row?.sticky === 'start'\" [stickyEnd]=\"config.actions?.row?.sticky === 'end'\">\n <th mat-header-cell *matHeaderCellDef #actionsHeaderCell [style.width]=\"config.actions?.row?.width\" class=\"praxis-actions-header\" [class.align-start]=\"getActionsHeaderAlign() === 'start'\" [class.align-center]=\"getActionsHeaderAlign() === 'center'\" [class.align-end]=\"getActionsHeaderAlign() === 'end'\">\n <div class=\"praxis-actions-header__content\" [matTooltip]=\"getActionsHeaderTooltip() || ''\" [matTooltipDisabled]=\"!getActionsHeaderTooltip()\">\n <mat-icon *ngIf=\"getActionsHeaderIcon() as hi\" [praxisIcon]=\"hi\"></mat-icon>\n <span class=\"label\" *ngIf=\"getActionsHeaderLabel() as hl\">{{ hl }}</span>\n </div>\n </th>\n <td\n mat-cell\n *matCellDef=\"let row\"\n class=\"praxis-actions-cell\"\n [class.dense]=\"dense\"\n [style.width]=\"config.actions?.row?.width\"\n >\n <div class=\"praxis-actions-cell__content\">\n <!-- A\u00E7\u00F5es inline -->\n <!-- Inline actions: icons mode -->\n <ng-container *ngIf=\"config.actions?.row?.display === 'icons' || !config.actions?.row?.display\">\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <button\n mat-icon-button\n class=\"praxis-icon-btn\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event)\"\n [matTooltip]=\"a.label || getActionId(a)\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"praxis-tooltip\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\"\n >\n <mat-icon [praxisIcon]=\"a.icon\"></mat-icon>\n </button>\n </ng-container>\n </ng-container>\n\n <!-- Inline actions: buttons mode (show label + icon) -->\n <ng-container *ngIf=\"config.actions?.row?.display === 'buttons'\">\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <button\n mat-flat-button\n class=\"praxis-row-btn\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event)\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\"\n >\n <mat-icon [praxisIcon]=\"a.icon\"></mat-icon>\n <span>{{ a.label || getActionId(a) }}</span>\n </button>\n </ng-container>\n </ng-container>\n\n <!-- Menu de overflow -->\n <button\n mat-icon-button\n class=\"praxis-icon-btn praxis-more-btn\"\n *ngIf=\"hasOverflowRowActions(row)\"\n [matMenuTriggerFor]=\"rowMoreMenu\"\n [color]=\"getRowMenuButtonColor() || null\"\n aria-label=\"Mais a\u00E7\u00F5es\"\n >\n <mat-icon [praxisIcon]=\"getRowMenuIcon()\"></mat-icon>\n </button>\n <mat-menu #rowMoreMenu=\"matMenu\" xPosition=\"before\">\n <ng-container\n *ngFor=\"let a of getOverflowRowActions(row); trackBy: trackAction\"\n >\n <button\n mat-menu-item\n (click)=\"onRowAction(getActionId(a), row, $event)\"\n [disabled]=\"isActionDisabled(a, row)\"\n >\n <mat-icon [color]=\"a.color || null\" [praxisIcon]=\"a.icon\"></mat-icon>\n <span>{{ a.label || getActionId(a) }}</span>\n </button>\n </ng-container>\n </mat-menu>\n </div>\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <ng-container *ngIf=\"!isVirtualized()\">\n <tr\n mat-row\n *matRowDef=\"let row; let i = index; columns: displayedColumns\"\n [ngClass]=\"getRowClasses(row)\"\n [ngStyle]=\"getRowNgStyle(row)\"\n (click)=\"onRowClicked(row, i)\"\n (dblclick)=\"onRowDoubleClicked(row, i)\"\n ></tr>\n </ng-container>\n</table>\n\n<!-- Virtual rows path (header preserved above) -->\n<ng-container *ngIf=\"resourcePath && !schemaError && !dataError && isVirtualized()\">\n <cdk-virtual-scroll-viewport\n class=\"ptable-viewport\"\n [itemSize]=\"getVirtItemHeight()\"\n [minBufferPx]=\"getVirtBufferSize() * getVirtItemHeight()\"\n [maxBufferPx]=\"getVirtBufferSize() * getVirtItemHeight() * 2\"\n [style.minHeight]=\"getVirtMinHeightStyle()\"\n >\n <table class=\"mat-mdc-table mat-elevation-z8\" style=\"width:100%\">\n <tbody>\n <tr class=\"mat-mdc-row\"\n *cdkVirtualFor=\"let row of dataSource.data; let i = index; trackBy: trackByRow\"\n [ngClass]=\"getRowClasses(row)\"\n [ngStyle]=\"getRowNgStyle(row)\"\n (click)=\"onRowClicked(row, i)\"\n (dblclick)=\"onRowDoubleClicked(row, i)\">\n <!-- Selection column -->\n <td class=\"mat-mdc-cell\" *ngIf=\"config.behavior?.selection?.enabled\">\n <mat-checkbox\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleRow(row)\"\n [checked]=\"selection.isSelected(row)\">\n </mat-checkbox>\n </td>\n <!-- Data columns -->\n <td class=\"mat-mdc-cell\"\n *ngFor=\"let column of visibleColumns\"\n [style.text-align]=\"column.align\"\n [style.width]=\"column.width\"\n [attr.style]=\"column.style\"\n [ngClass]=\"getCellClasses(row, column)\"\n [ngStyle]=\"getCellNgStyle(row, column)\">\n <ng-container [ngSwitch]=\"getEffectiveRendererType(row, column)\">\n <ng-container *ngSwitchCase=\"'icon'\">\n <mat-icon [color]=\"getIconColor(row, column) || null\"\n [ngStyle]=\"getIconStyle(row, column)\"\n [attr.aria-label]=\"getIconAriaLabel(row, column) || null\">\n {{ getIconName(row, column) }}\n </mat-icon>\n </ng-container>\n <ng-container *ngSwitchCase=\"'image'\">\n <img class=\"pfx-cell-image\"\n [src]=\"getImageSrc(row, column)\"\n [attr.alt]=\"getImageAlt(row, column) || ''\"\n [attr.loading]=\"getImageLazy(row, column) ? 'lazy' : null\"\n [style.width.px]=\"getImageWidth(column)\"\n [style.height.px]=\"getImageHeight(column)\"\n [class.shape-rounded]=\"getImageShape(column) === 'rounded'\"\n [class.shape-circle]=\"getImageShape(column) === 'circle'\"\n [style.object-fit]=\"getImageFit(column)\" />\n </ng-container>\n <ng-container *ngSwitchCase=\"'badge'\">\n <span class=\"pfx-badge\" [ngClass]=\"getBadgeClasses(row, column)\">\n <mat-icon *ngIf=\"getBadgeIcon(row, column) as bi\" class=\"pfx-badge-icon\">{{ bi }}</mat-icon>\n <span class=\"pfx-badge-text\">{{ getBadgeText(row, column) }}</span>\n </span>\n </ng-container>\n <ng-container *ngSwitchDefault>\n {{ getCellValue(row, column) }}\n </ng-container>\n </ng-container>\n </td>\n\n <!-- Actions column -->\n <td class=\"mat-mdc-cell praxis-actions-cell\" *ngIf=\"config.actions?.row?.enabled\" [class.dense]=\"dense\" [style.width]=\"config.actions?.row?.width\">\n <div class=\"praxis-actions-cell__content\">\n <ng-container *ngIf=\"config.actions?.row?.display === 'icons' || !config.actions?.row?.display\">\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <button mat-icon-button class=\"praxis-icon-btn\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event)\"\n [matTooltip]=\"a.label || getActionId(a)\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"praxis-tooltip\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\">\n <mat-icon [praxisIcon]=\"a.icon\"></mat-icon>\n </button>\n </ng-container>\n </ng-container>\n <ng-container *ngIf=\"config.actions?.row?.display === 'buttons'\">\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <button mat-flat-button class=\"praxis-row-btn\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event)\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\">\n <mat-icon [praxisIcon]=\"a.icon\"></mat-icon>\n <span>{{ a.label || getActionId(a) }}</span>\n </button>\n </ng-container>\n </ng-container>\n <button mat-icon-button class=\"praxis-icon-btn praxis-more-btn\"\n *ngIf=\"hasOverflowRowActions(row)\"\n [matMenuTriggerFor]=\"rowMoreMenuV\"\n [color]=\"getRowMenuButtonColor() || null\"\n aria-label=\"Mais a\u00E7\u00F5es\">\n <mat-icon [praxisIcon]=\"getRowMenuIcon()\"></mat-icon>\n </button>\n <mat-menu #rowMoreMenuV=\"matMenu\" xPosition=\"before\">\n <ng-container *ngFor=\"let a of getOverflowRowActions(row); trackBy: trackAction\">\n <button mat-menu-item (click)=\"onRowAction(getActionId(a), row, $event)\" [disabled]=\"isActionDisabled(a, row)\">\n <mat-icon [color]=\"a.color || null\" [praxisIcon]=\"a.icon\"></mat-icon>\n <span>{{ a.label || getActionId(a) }}</span>\n </button>\n </ng-container>\n </mat-menu>\n </div>\n </td>\n </tr>\n </tbody>\n </table>\n </cdk-virtual-scroll-viewport>\n</ng-container>\n<!-- Paginadores (top/bottom) -->\n<mat-paginator\n *ngIf=\"resourcePath && !schemaError && !dataError && getPaginationEnabled() && (config.behavior?.pagination?.position === 'top' || config.behavior?.pagination?.position === 'both')\"\n [length]=\"getPaginationLength()\"\n [pageSize]=\"getPaginationPageSize()\"\n [pageSizeOptions]=\"getPaginationPageSizeOptions()\"\n [showFirstLastButtons]=\"getPaginationShowFirstLast()\"\n (page)=\"onPageChange($event)\"\n [class.compact]=\"config.behavior?.pagination?.style === 'compact'\"\n>\n</mat-paginator>\n\n<mat-paginator\n *ngIf=\"resourcePath && !schemaError && !dataError && getPaginationEnabled() && (config.behavior?.pagination?.position === 'bottom' || config.behavior?.pagination?.position === 'both' || !config.behavior?.pagination?.position)\"\n [length]=\"getPaginationLength()\"\n [pageSize]=\"getPaginationPageSize()\"\n [pageSizeOptions]=\"getPaginationPageSizeOptions()\"\n [showFirstLastButtons]=\"getPaginationShowFirstLast()\"\n (page)=\"onPageChange($event)\"\n [class.compact]=\"config.behavior?.pagination?.style === 'compact'\"\n>\n</mat-paginator>\n", styles: ["@charset \"UTF-8\";table{width:100%}.praxis-actions-cell{height:100%;padding-inline:12px;white-space:nowrap}.praxis-actions-cell__content{display:flex;align-items:center;justify-content:flex-end;gap:8px;width:100%}.praxis-actions-cell.dense .praxis-actions-cell__content{gap:6px}.praxis-icon-btn{width:var(--p-actions-btn-size, 40px);height:var(--p-actions-btn-size, 40px);border:0;background:transparent;padding:0;display:inline-flex;align-items:center;justify-content:center;border-radius:9999px;cursor:pointer;--mat-icon-button-state-layer-size: var(--p-actions-btn-size, 40px)}.praxis-icon-btn:hover{background:var(--md-sys-color-surface-variant, rgba(255, 255, 255, .06))}.praxis-icon-btn:focus-visible{outline:2px solid var(--primary, #48a1ff);outline-offset:2px}.praxis-icon-btn mat-icon,.praxis-icon-btn .mat-icon{font-size:var(--p-actions-icon-size, 22px);width:var(--p-actions-icon-size, 22px);height:var(--p-actions-icon-size, 22px);line-height:var(--p-actions-icon-size, 22px)}.praxis-more-btn{width:var(--p-actions-more-btn-size, var(--p-actions-btn-size, 40px));height:var(--p-actions-more-btn-size, var(--p-actions-btn-size, 40px));--mat-icon-button-state-layer-size: var(--p-actions-more-btn-size, var(--p-actions-btn-size, 40px));background-image:var(--p-actions-more-btn-gradient, none);background-size:100% 100%;background-repeat:no-repeat}.praxis-more-btn mat-icon,.praxis-more-btn .mat-icon{font-size:var(--p-actions-more-icon-size, var(--p-actions-icon-size, 22px));width:var(--p-actions-more-icon-size, var(--p-actions-icon-size, 22px));height:var(--p-actions-more-icon-size, var(--p-actions-icon-size, 22px));line-height:var(--p-actions-more-icon-size, var(--p-actions-icon-size, 22px));color:var(--p-actions-more-icon-color);background-image:var(--p-actions-more-icon-gradient, none);-webkit-background-clip:text;background-clip:text}.praxis-icon-btn.destructive mat-icon{color:#ff6b6b}.mat-mdc-tooltip.praxis-tooltip{margin-top:-8px;margin-bottom:8px}.spacer{flex:1 1 auto}.praxis-table-header{display:flex;flex-wrap:wrap;align-items:flex-start;gap:8px;margin:16px 0 12px;width:100%;clear:both;position:relative}.praxis-table-header.debug-layout,:host.debug-layout .praxis-table-header{outline:2px dashed #ff4d4f;border-radius:8px}:host.debug-layout ::ng-deep .praxis-filter-bar{outline:1px dashed #f59e0b}:host.debug-layout ::ng-deep .quick-shell{outline:1px dashed #3b82f6}:host.debug-layout ::ng-deep .always-fields{outline:1px dashed #22c55e}:host.debug-layout ::ng-deep .praxis-filter-overlay .praxis-filter-advanced{outline:2px solid #a855f7}:host.debug-layout ::ng-deep .mat-mdc-table{outline:1px dashed #94a3b8}.praxis-table-header>praxis-table-toolbar{flex:1 0 100%}:host{--p-table-header-bg: var(--md-sys-color-surface-container-highest, #1d1d1f);--p-table-header-fg: var(--md-sys-color-on-surface, #e8f3f1);--p-table-border-color: var(--md-sys-color-outline-variant, rgba(255, 255, 255, .12));--p-table-row-even-bg: var(--md-sys-color-surface-container, rgba(255, 255, 255, .04));--p-table-row-hover-bg: color-mix(in srgb, var(--md-sys-color-primary, #3f51b5) 10%, transparent);--p-table-row-selected-bg: var(--md-sys-color-primary-container, rgba(63,81,181,.14));--p-table-badge-soft-primary-bg: color-mix(in srgb, var(--mat-sys-primary, var(--md-sys-color-primary)) 16%, transparent);--p-table-badge-soft-primary-fg: var(--mat-sys-primary, var(--md-sys-color-primary));--p-table-badge-soft-accent-bg: color-mix(in srgb, var(--mat-sys-secondary, var(--md-sys-color-secondary, #ff4081)) 14%, transparent);--p-table-badge-soft-accent-fg: var(--mat-sys-secondary, var(--md-sys-color-secondary, #ff4081));--p-table-badge-soft-warn-bg: color-mix(in srgb, var(--mat-sys-error, var(--md-sys-color-error, #f44336)) 14%, transparent);--p-table-badge-soft-warn-fg: var(--mat-sys-error, var(--md-sys-color-error, #f44336));--p-table-state-success-bg: color-mix(in srgb, var(--mat-sys-tertiary, var(--md-sys-color-tertiary, #388E3C)) 16%, transparent);--p-table-state-success-fg: var(--md-sys-color-on-surface, #c8e6c9);--p-table-state-warning-bg: color-mix(in srgb, var(--md-sys-color-secondary, #FFA000) 18%, transparent);--p-table-state-warning-fg: var(--md-sys-color-on-surface, #ffe0b2);--p-table-state-danger-bg: color-mix(in srgb, var(--md-sys-color-error, #e53935) 18%, transparent);--p-table-state-danger-fg: var(--md-sys-color-on-surface, #ffcdd2);--p-table-state-highlight-bg: color-mix(in srgb, var(--md-sys-color-primary, #2196f3) 16%, transparent);--p-table-state-highlight-fg: var(--md-sys-color-on-surface, #bbdefb)}:host ::ng-deep .mat-mdc-table{background:var(--md-sys-color-surface-container-highest, #1d1d1f);border-radius:12px;width:100%}:host ::ng-deep .mat-mdc-header-row{position:sticky;top:0;z-index:1;background:var(--p-table-header-bg);color:var(--p-table-header-fg);box-shadow:var(--p-table-header-shadow, 0 1px 0 var(--p-table-border-color));border-bottom:1px solid var(--p-table-border-color)}:host ::ng-deep .mat-mdc-header-cell,:host ::ng-deep .mat-sort-header-content,:host ::ng-deep .mat-mdc-header-row .mat-icon{color:var(--p-table-header-fg)!important;font-weight:600}:host ::ng-deep .mat-mdc-header-cell.mat-sort-header-sorted .mat-sort-header-arrow,:host ::ng-deep .mat-mdc-header-cell:hover .mat-sort-header-arrow{color:var(--p-table-header-fg)!important}:host ::ng-deep .mat-mdc-header-cell.mat-sort-header-sorted .mat-sort-header-arrow .mat-sort-header-indicator,:host ::ng-deep .mat-mdc-header-cell.mat-sort-header-sorted .mat-sort-header-arrow .mat-sort-header-stem,:host ::ng-deep .mat-mdc-header-cell.mat-sort-header-sorted .mat-sort-header-arrow .mat-sort-header-pointer,:host ::ng-deep .mat-mdc-header-cell.mat-sort-header-sorted .mat-sort-header-arrow .mat-sort-header-pointer-left,:host ::ng-deep .mat-mdc-header-cell.mat-sort-header-sorted .mat-sort-header-arrow .mat-sort-header-pointer-right{border-color:var(--p-table-header-fg)!important;background:var(--p-table-header-fg)!important}:host ::ng-deep .mat-mdc-header-cell{padding:var(--p-header-padding, 12px 16px)!important;font-size:var(--p-header-font-size, inherit);font-weight:var(--p-header-font-weight, 600);letter-spacing:var(--p-header-letter-spacing, normal);text-transform:var(--p-header-text-transform, none);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.praxis-actions-header{text-align:right}.praxis-actions-header.align-start{text-align:left}.praxis-actions-header.align-center{text-align:center}.praxis-actions-header.align-end{text-align:right}.praxis-actions-header .praxis-actions-header__content{display:inline-flex;align-items:center;gap:var(--p-actions-header-gap, 6px);color:var(--p-actions-header-color, inherit)}.praxis-actions-header .praxis-actions-header__content .mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}:host ::ng-deep .mat-mdc-header-cell .mat-sort-header-container{padding-right:20px}:host ::ng-deep .mat-mdc-cell{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}:host ::ng-deep .mat-mdc-row:hover{background:var(--p-table-row-hover-bg)}:host ::ng-deep .mat-mdc-row:nth-child(2n){background:var(--p-table-row-even-bg)}.ptable-info-banner{display:flex;gap:12px;align-items:center;padding:8px 12px;margin:8px 0;border-radius:8px;border:1px solid var(--mat-sys-primary, var(--md-sys-color-primary, #1a73e8));background:color-mix(in srgb,var(--mat-sys-primary, var(--md-sys-color-primary, #1a73e8)) 10%,transparent)}.ptable-info-banner .text{flex:1;font-weight:600}.ptable-info-banner .actions{display:flex;gap:8px}.pfx-cell-image{display:inline-block;vertical-align:middle;background:var(--md-sys-color-surface-variant, #2a2a2a);border:1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, .08))}.pfx-cell-image.shape-rounded{border-radius:8px}.pfx-cell-image.shape-circle{border-radius:9999px}.pfx-badge{display:inline-flex;align-items:center;gap:6px;line-height:1;padding:4px 8px;border-radius:9999px;font-size:12px;font-weight:600;border:1px solid transparent}.pfx-badge .pfx-badge-icon{font-size:16px;width:16px;height:16px}.pfx-badge--filled-primary{background:var(--mat-sys-primary, #3f51b5);color:var(--mat-sys-on-primary, #fff)}.pfx-badge--filled-accent{background:var(--mat-sys-secondary, #ff4081);color:var(--mat-sys-on-secondary, #fff)}.pfx-badge--filled-warn{background:var(--mat-sys-error, #f44336);color:var(--mat-sys-on-error, #fff)}.pfx-badge--outlined{background:transparent;border-color:var(--mat-sys-outline-variant, rgba(255, 255, 255, .24));color:inherit}.pfx-badge--soft-primary{background:var(--p-table-badge-soft-primary-bg);color:var(--p-table-badge-soft-primary-fg)}.pfx-badge--soft-accent{background:var(--p-table-badge-soft-accent-bg);color:var(--p-table-badge-soft-accent-fg)}.pfx-badge--soft-warn{background:var(--p-table-badge-soft-warn-bg);color:var(--p-table-badge-soft-warn-fg)}.row--success,.row--success td,td.row--success{background-color:var(--p-table-state-success-bg)!important;color:var(--p-table-state-success-fg)!important}.row--warning,.row--warning td,td.row--warning{background-color:var(--p-table-state-warning-bg)!important;color:var(--p-table-state-warning-fg)!important}.row--danger,.row--danger td,td.row--danger{background-color:var(--p-table-state-danger-bg)!important;color:var(--p-table-state-danger-fg)!important}.row--highlight,.row--highlight td,td.row--highlight{background-color:var(--p-table-state-highlight-bg)!important;color:var(--p-table-state-highlight-fg)!important;font-weight:600}.row--muted,.row--muted td,td.row--muted{opacity:.7;filter:saturate(.6)}\n"] }]
|
|
19160
|
+
], host: {
|
|
19161
|
+
'[class.debug-layout]': 'debugLayout',
|
|
19162
|
+
// Apply density classes based on quick appearance setting
|
|
19163
|
+
"[class.density-compact]": "config?.appearance?.density === 'compact'",
|
|
19164
|
+
"[class.density-comfortable]": "config?.appearance?.density === 'comfortable'",
|
|
19165
|
+
"[class.density-spacious]": "config?.appearance?.density === 'spacious'",
|
|
19166
|
+
// Quick borders toggles (rows/columns)
|
|
19167
|
+
"[class.row-borders]": "config?.appearance?.borders?.showRowBorders !== false",
|
|
19168
|
+
"[class.col-borders]": "!!config?.appearance?.borders?.showColumnBorders",
|
|
19169
|
+
}, template: "<praxis-empty-state-card\n *ngIf=\"!resourcePath\"\n icon=\"link\"\n [title]=\"'Conecte a tabela \u00E0 fonte de dados'\"\n [description]=\"'Informe a rota do recurso da API para carregar colunas e dados automaticamente.'\"\n [primaryAction]=\"{ label: 'Conectar \u00E0 fonte de dados', icon: 'bolt', action: openQuickConnect.bind(this) }\"\n></praxis-empty-state-card>\n\n<!-- Error State with Quick Connect CTA -->\n<div class=\"ptable-error\" *ngIf=\"resourcePath && (schemaError || dataError)\" style=\"display:flex; align-items:center; gap:12px; padding:12px; border:1px solid var(--md-sys-color-error, #b00020); border-radius:8px; margin: 8px 0;\">\n <mat-icon color=\"warn\" aria-hidden=\"true\">error</mat-icon>\n <div style=\"flex:1\">\n <div style=\"font-weight:600\">Erro</div>\n <div>{{ errorMessage || 'Ocorreu um erro ao carregar a tabela.' }}</div>\n </div>\n <button mat-flat-button color=\"primary\" (click)=\"openQuickConnect()\">\n <mat-icon>bolt</mat-icon>\n Conectar a recurso\n </button>\n <button mat-stroked-button (click)=\"retryData()\" *ngIf=\"!schemaError\">Tentar novamente</button>\n <button mat-stroked-button (click)=\"reloadSchema()\" *ngIf=\"schemaError\">Recarregar colunas</button>\n </div>\n\n<!-- Inline banner for schema change (only in edit mode) -->\n<div *ngIf=\"shouldShowOutdatedInline()\" class=\"ptable-info-banner\" role=\"status\" aria-live=\"polite\">\n <div class=\"text\">O schema do servidor mudou. Reconciliar agora?</div>\n <div class=\"actions\">\n <button mat-stroked-button color=\"primary\" (click)=\"onReconcileRequested()\">\n <mat-icon>sync</mat-icon>\n Reconciliar\n </button>\n <button mat-button (click)=\"onSnoozeOutdated()\">Lembrar depois</button>\n <button mat-button (click)=\"onIgnoreOutdated()\">Ignorar</button>\n </div>\n </div>\n\n <ng-container *ngIf=\"resourcePath && !schemaError && !dataError && toolbarV2; else legacyHeader\">\n <div class=\"praxis-table-header\" [class.debug-layout]=\"debugLayout\" [class.edit-mode]=\"editModeEnabled\" *ngIf=\"showToolbar || editModeEnabled\">\n <praxis-table-toolbar\n *ngIf=\"showToolbar\"\n [config]=\"config\"\n [debugLayout]=\"debugLayout\"\n (toolbarAction)=\"onToolbarAction($event)\"\n (reset)=\"onResetPreferences()\"\n >\n <praxis-filter\n *ngIf=\"\n resourcePath &&\n config.behavior?.filtering?.advancedFilters?.enabled &&\n !projectedFilter\n \"\n advancedFilter\n [resourcePath]=\"resourcePath\"\n [formId]=\"tableId + '-filter'\"\n [editModeEnabled]=\"editModeEnabled\"\n [quickField]=\"\n config.behavior?.filtering?.advancedFilters?.settings?.quickField\n \"\n [alwaysVisibleFields]=\"\n config.behavior?.filtering?.advancedFilters?.settings\n ?.alwaysVisibleFields\n \"\n [allowSaveTags]=\"\n config.behavior?.filtering?.advancedFilters?.settings\n ?.allowSaveTags\n \"\n [changeDebounceMs]=\"\n config.behavior?.filtering?.advancedFilters?.settings\n ?.changeDebounceMs ?? 300\n \"\n [i18n]=\"getFilterI18n()\"\n [mode]=\"\n config.behavior?.filtering?.advancedFilters?.settings?.mode ??\n 'auto'\n \"\n [debugLayout]=\"debugLayout\"\n [showFilterSettings]=\"!editModeEnabled\"\n (submit)=\"onAdvancedFilterSubmit($event)\"\n (clear)=\"onAdvancedFilterClear()\"\n ></praxis-filter>\n <ng-content select=\"[advancedFilter]\" />\n <ng-content select=\"[toolbar]\" />\n <button end-actions mat-icon-button color=\"primary\" *ngIf=\"editModeEnabled\"\n (click)=\"openTableSettings()\" aria-label=\"Configura\u00E7\u00F5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u2014 Reconciliar' : 'Configura\u00E7\u00F5es'\"\n matTooltipPosition=\"below\">\n <mat-icon>settings</mat-icon>\n </button>\n </praxis-table-toolbar>\n <!-- Render a minimal settings button when toolbar is hidden but edit mode is enabled -->\n <div class=\"ptable-header-actions\" *ngIf=\"!showToolbar && editModeEnabled\">\n <button mat-icon-button color=\"primary\" (click)=\"openTableSettings()\" aria-label=\"Configura\u00E7\u00F5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u2014 Reconciliar' : 'Configura\u00E7\u00F5es'\"\n matTooltipPosition=\"below\">\n <mat-icon>settings</mat-icon>\n </button>\n <button mat-icon-button (click)=\"disconnect()\" aria-label=\"Desconectar\" matTooltip=\"Desconectar da fonte de dados\">\n <mat-icon>link_off</mat-icon>\n </button>\n </div>\n \n </div>\n</ng-container>\n<ng-template #legacyHeader>\n <ng-container *ngIf=\"resourcePath && !schemaError && !dataError\">\n <praxis-table-toolbar\n *ngIf=\"showToolbar\"\n [config]=\"config\"\n [debugLayout]=\"debugLayout\"\n (toolbarAction)=\"onToolbarAction($event)\"\n (reset)=\"onResetPreferences()\"\n >\n <praxis-filter\n *ngIf=\"\n resourcePath &&\n config.behavior?.filtering?.advancedFilters?.enabled &&\n !projectedFilter\n \"\n advancedFilter\n [resourcePath]=\"resourcePath\"\n [formId]=\"tableId + '-filter'\"\n [editModeEnabled]=\"editModeEnabled\"\n [quickField]=\"\n config.behavior?.filtering?.advancedFilters?.settings?.quickField\n \"\n [alwaysVisibleFields]=\"\n config.behavior?.filtering?.advancedFilters?.settings\n ?.alwaysVisibleFields\n \"\n [allowSaveTags]=\"\n config.behavior?.filtering?.advancedFilters?.settings?.allowSaveTags\n \"\n [changeDebounceMs]=\"\n config.behavior?.filtering?.advancedFilters?.settings\n ?.changeDebounceMs ?? 300\n \"\n [i18n]=\"getFilterI18n()\"\n [mode]=\"\n config.behavior?.filtering?.advancedFilters?.settings?.mode ??\n 'auto'\n \"\n [debugLayout]=\"debugLayout\"\n [showFilterSettings]=\"!editModeEnabled\"\n (submit)=\"onAdvancedFilterSubmit($event)\"\n (clear)=\"onAdvancedFilterClear()\"\n ></praxis-filter>\n <ng-content select=\"[advancedFilter]\" />\n <ng-content select=\"[toolbar]\" />\n <button end-actions mat-icon-button color=\"primary\" *ngIf=\"editModeEnabled\"\n (click)=\"openTableSettings()\" aria-label=\"Configura\u00E7\u00F5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u2014 Reconciliar' : 'Configura\u00E7\u00F5es'\"\n matTooltipPosition=\"below\">\n <mat-icon>settings</mat-icon>\n </button>\n </praxis-table-toolbar>\n <!-- Legacy header: settings button when toolbar hidden -->\n <div class=\"ptable-header-actions\" *ngIf=\"!showToolbar && editModeEnabled\">\n <button mat-icon-button color=\"primary\" (click)=\"openTableSettings()\" aria-label=\"Configura\u00E7\u00F5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u2014 Reconciliar' : 'Configura\u00E7\u00F5es'\"\n matTooltipPosition=\"below\">\n <mat-icon>settings</mat-icon>\n </button>\n </div>\n </ng-container>\n \n</ng-template>\n<table\n *ngIf=\"resourcePath && !schemaError && !dataError\"\n mat-table\n [dataSource]=\"dataSource\"\n matSort\n (matSortChange)=\"onSortChange($event)\"\n [matSortDisabled]=\"!getSortingEnabled()\"\n class=\"mat-elevation-z8\"\n>\n <ng-container\n *ngIf=\"config.behavior?.selection?.enabled\"\n matColumnDef=\"_select\"\n >\n <th mat-header-cell *matHeaderCellDef>\n <mat-checkbox\n (change)=\"masterToggle()\"\n [checked]=\"isAllSelected()\"\n [indeterminate]=\"selection.hasValue() && !isAllSelected()\"\n ></mat-checkbox>\n </th>\n <td mat-cell *matCellDef=\"let row\">\n <mat-checkbox\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleRow(row)\"\n [checked]=\"selection.isSelected(row)\"\n ></mat-checkbox>\n </td>\n </ng-container>\n <ng-container\n *ngFor=\"let column of visibleColumns\"\n [matColumnDef]=\"column.field\"\n [sticky]=\"column.sticky === true || column.sticky === 'start'\"\n [stickyEnd]=\"column.sticky === 'end'\"\n >\n <th\n mat-header-cell\n *matHeaderCellDef\n mat-sort-header\n [disabled]=\"!getSortingEnabled() || column.sortable === false\"\n [style.text-align]=\"column.align\"\n [style.width]=\"column.width\"\n [attr.style]=\"column.headerStyle\"\n >\n {{ column.header }}\n </th>\n <td\n mat-cell\n *matCellDef=\"let element\"\n [style.text-align]=\"column.align\"\n [style.width]=\"column.width\"\n [attr.style]=\"column.style\"\n [ngClass]=\"getCellClasses(element, column)\"\n [ngStyle]=\"getCellNgStyle(element, column)\"\n >\n <ng-container [ngSwitch]=\"getEffectiveRendererType(element, column)\">\n <!-- Icon renderer -->\n <ng-container *ngSwitchCase=\"'icon'\">\n <mat-icon\n [color]=\"getIconColor(element, column) || null\"\n [ngStyle]=\"getIconStyle(element, column)\"\n [attr.aria-label]=\"getIconAriaLabel(element, column) || null\"\n >{{ getIconName(element, column) }}</mat-icon\n >\n </ng-container>\n\n <!-- Image renderer -->\n <ng-container *ngSwitchCase=\"'image'\">\n <img\n class=\"pfx-cell-image\"\n [src]=\"getImageSrc(element, column)\"\n [attr.alt]=\"getImageAlt(element, column) || ''\"\n [attr.loading]=\"getImageLazy(element, column) ? 'lazy' : null\"\n [style.width.px]=\"getImageWidth(column)\"\n [style.height.px]=\"getImageHeight(column)\"\n [class.shape-rounded]=\"getImageShape(column) === 'rounded'\"\n [class.shape-circle]=\"getImageShape(column) === 'circle'\"\n [style.object-fit]=\"getImageFit(column)\"\n />\n </ng-container>\n\n <!-- Badge renderer -->\n <ng-container *ngSwitchCase=\"'badge'\">\n <span class=\"pfx-badge\" [ngClass]=\"getBadgeClasses(element, column)\">\n <mat-icon *ngIf=\"getBadgeIcon(element, column) as bi\" class=\"pfx-badge-icon\">{{ bi }}</mat-icon>\n <span class=\"pfx-badge-text\">{{ getBadgeText(element, column) }}</span>\n </span>\n </ng-container>\n\n <!-- Default text rendering -->\n <ng-container *ngSwitchDefault>\n {{ getCellValue(element, column) }}\n </ng-container>\n </ng-container>\n </td>\n </ng-container>\n <ng-container *ngIf=\"config.actions?.row?.enabled\" matColumnDef=\"_actions\" [sticky]=\"config.actions?.row?.sticky === true || config.actions?.row?.sticky === 'start'\" [stickyEnd]=\"config.actions?.row?.sticky === 'end'\">\n <th mat-header-cell *matHeaderCellDef #actionsHeaderCell [style.width]=\"config.actions?.row?.width\" class=\"praxis-actions-header\" [class.align-start]=\"getActionsHeaderAlign() === 'start'\" [class.align-center]=\"getActionsHeaderAlign() === 'center'\" [class.align-end]=\"getActionsHeaderAlign() === 'end'\">\n <div class=\"praxis-actions-header__content\" [matTooltip]=\"getActionsHeaderTooltip() || ''\" [matTooltipDisabled]=\"!getActionsHeaderTooltip()\">\n <mat-icon *ngIf=\"getActionsHeaderIcon() as hi\" [praxisIcon]=\"hi\"></mat-icon>\n <span class=\"label\" *ngIf=\"getActionsHeaderLabel() as hl\">{{ hl }}</span>\n </div>\n </th>\n <td\n mat-cell\n *matCellDef=\"let row\"\n class=\"praxis-actions-cell\"\n [class.dense]=\"dense\"\n [style.width]=\"config.actions?.row?.width\"\n >\n <div class=\"praxis-actions-cell__content\">\n <!-- A\u00E7\u00F5es inline -->\n <!-- Inline actions: icons mode -->\n <ng-container *ngIf=\"config.actions?.row?.display === 'icons' || !config.actions?.row?.display\">\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <button\n mat-icon-button\n class=\"praxis-icon-btn\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event)\"\n [matTooltip]=\"a.label || getActionId(a)\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"praxis-tooltip\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\"\n >\n <mat-icon [praxisIcon]=\"a.icon\"></mat-icon>\n </button>\n </ng-container>\n </ng-container>\n\n <!-- Inline actions: buttons mode (show label + icon) -->\n <ng-container *ngIf=\"config.actions?.row?.display === 'buttons'\">\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <button\n mat-flat-button\n class=\"praxis-row-btn\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event)\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\"\n >\n <mat-icon [praxisIcon]=\"a.icon\"></mat-icon>\n <span>{{ a.label || getActionId(a) }}</span>\n </button>\n </ng-container>\n </ng-container>\n\n <!-- Menu de overflow -->\n <button\n mat-icon-button\n class=\"praxis-icon-btn praxis-more-btn\"\n *ngIf=\"hasOverflowRowActions(row)\"\n [matMenuTriggerFor]=\"rowMoreMenu\"\n [color]=\"getRowMenuButtonColor() || null\"\n aria-label=\"Mais a\u00E7\u00F5es\"\n >\n <mat-icon [praxisIcon]=\"getRowMenuIcon()\"></mat-icon>\n </button>\n <mat-menu #rowMoreMenu=\"matMenu\" xPosition=\"before\">\n <ng-container\n *ngFor=\"let a of getOverflowRowActions(row); trackBy: trackAction\"\n >\n <button\n mat-menu-item\n (click)=\"onRowAction(getActionId(a), row, $event)\"\n [disabled]=\"isActionDisabled(a, row)\"\n >\n <mat-icon [color]=\"a.color || null\" [praxisIcon]=\"a.icon\"></mat-icon>\n <span>{{ a.label || getActionId(a) }}</span>\n </button>\n </ng-container>\n </mat-menu>\n </div>\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <ng-container *ngIf=\"!isVirtualized()\">\n <tr\n mat-row\n *matRowDef=\"let row; let i = index; columns: displayedColumns\"\n [ngClass]=\"getRowClasses(row)\"\n [ngStyle]=\"getRowNgStyle(row)\"\n (click)=\"onRowClicked(row, i)\"\n (dblclick)=\"onRowDoubleClicked(row, i)\"\n ></tr>\n </ng-container>\n</table>\n\n<!-- Virtual rows path (header preserved above) -->\n<ng-container *ngIf=\"resourcePath && !schemaError && !dataError && isVirtualized()\">\n <cdk-virtual-scroll-viewport\n class=\"ptable-viewport\"\n [itemSize]=\"getVirtItemHeight()\"\n [minBufferPx]=\"getVirtBufferSize() * getVirtItemHeight()\"\n [maxBufferPx]=\"getVirtBufferSize() * getVirtItemHeight() * 2\"\n [style.minHeight]=\"getVirtMinHeightStyle()\"\n >\n <table class=\"mat-mdc-table mat-elevation-z8\" style=\"width:100%\">\n <tbody>\n <tr class=\"mat-mdc-row\"\n *cdkVirtualFor=\"let row of dataSource.data; let i = index; trackBy: trackByRow\"\n [ngClass]=\"getRowClasses(row)\"\n [ngStyle]=\"getRowNgStyle(row)\"\n (click)=\"onRowClicked(row, i)\"\n (dblclick)=\"onRowDoubleClicked(row, i)\">\n <!-- Selection column -->\n <td class=\"mat-mdc-cell\" *ngIf=\"config.behavior?.selection?.enabled\">\n <mat-checkbox\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleRow(row)\"\n [checked]=\"selection.isSelected(row)\">\n </mat-checkbox>\n </td>\n <!-- Data columns -->\n <td class=\"mat-mdc-cell\"\n *ngFor=\"let column of visibleColumns\"\n [style.text-align]=\"column.align\"\n [style.width]=\"column.width\"\n [attr.style]=\"column.style\"\n [ngClass]=\"getCellClasses(row, column)\"\n [ngStyle]=\"getCellNgStyle(row, column)\">\n <ng-container [ngSwitch]=\"getEffectiveRendererType(row, column)\">\n <ng-container *ngSwitchCase=\"'icon'\">\n <mat-icon [color]=\"getIconColor(row, column) || null\"\n [ngStyle]=\"getIconStyle(row, column)\"\n [attr.aria-label]=\"getIconAriaLabel(row, column) || null\">\n {{ getIconName(row, column) }}\n </mat-icon>\n </ng-container>\n <ng-container *ngSwitchCase=\"'image'\">\n <img class=\"pfx-cell-image\"\n [src]=\"getImageSrc(row, column)\"\n [attr.alt]=\"getImageAlt(row, column) || ''\"\n [attr.loading]=\"getImageLazy(row, column) ? 'lazy' : null\"\n [style.width.px]=\"getImageWidth(column)\"\n [style.height.px]=\"getImageHeight(column)\"\n [class.shape-rounded]=\"getImageShape(column) === 'rounded'\"\n [class.shape-circle]=\"getImageShape(column) === 'circle'\"\n [style.object-fit]=\"getImageFit(column)\" />\n </ng-container>\n <ng-container *ngSwitchCase=\"'badge'\">\n <span class=\"pfx-badge\" [ngClass]=\"getBadgeClasses(row, column)\">\n <mat-icon *ngIf=\"getBadgeIcon(row, column) as bi\" class=\"pfx-badge-icon\">{{ bi }}</mat-icon>\n <span class=\"pfx-badge-text\">{{ getBadgeText(row, column) }}</span>\n </span>\n </ng-container>\n <ng-container *ngSwitchDefault>\n {{ getCellValue(row, column) }}\n </ng-container>\n </ng-container>\n </td>\n\n <!-- Actions column -->\n <td class=\"mat-mdc-cell praxis-actions-cell\" *ngIf=\"config.actions?.row?.enabled\" [class.dense]=\"dense\" [style.width]=\"config.actions?.row?.width\">\n <div class=\"praxis-actions-cell__content\">\n <ng-container *ngIf=\"config.actions?.row?.display === 'icons' || !config.actions?.row?.display\">\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <button mat-icon-button class=\"praxis-icon-btn\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event)\"\n [matTooltip]=\"a.label || getActionId(a)\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"praxis-tooltip\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\">\n <mat-icon [praxisIcon]=\"a.icon\"></mat-icon>\n </button>\n </ng-container>\n </ng-container>\n <ng-container *ngIf=\"config.actions?.row?.display === 'buttons'\">\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <button mat-flat-button class=\"praxis-row-btn\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event)\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\">\n <mat-icon [praxisIcon]=\"a.icon\"></mat-icon>\n <span>{{ a.label || getActionId(a) }}</span>\n </button>\n </ng-container>\n </ng-container>\n <button mat-icon-button class=\"praxis-icon-btn praxis-more-btn\"\n *ngIf=\"hasOverflowRowActions(row)\"\n [matMenuTriggerFor]=\"rowMoreMenuV\"\n [color]=\"getRowMenuButtonColor() || null\"\n aria-label=\"Mais a\u00E7\u00F5es\">\n <mat-icon [praxisIcon]=\"getRowMenuIcon()\"></mat-icon>\n </button>\n <mat-menu #rowMoreMenuV=\"matMenu\" xPosition=\"before\">\n <ng-container *ngFor=\"let a of getOverflowRowActions(row); trackBy: trackAction\">\n <button mat-menu-item (click)=\"onRowAction(getActionId(a), row, $event)\" [disabled]=\"isActionDisabled(a, row)\">\n <mat-icon [color]=\"a.color || null\" [praxisIcon]=\"a.icon\"></mat-icon>\n <span>{{ a.label || getActionId(a) }}</span>\n </button>\n </ng-container>\n </mat-menu>\n </div>\n </td>\n </tr>\n </tbody>\n </table>\n </cdk-virtual-scroll-viewport>\n</ng-container>\n<!-- Paginadores (top/bottom) -->\n<mat-paginator\n *ngIf=\"resourcePath && !schemaError && !dataError && getPaginationEnabled() && (config.behavior?.pagination?.position === 'top' || config.behavior?.pagination?.position === 'both')\"\n [length]=\"getPaginationLength()\"\n [pageSize]=\"getPaginationPageSize()\"\n [pageSizeOptions]=\"getPaginationPageSizeOptions()\"\n [showFirstLastButtons]=\"getPaginationShowFirstLast()\"\n (page)=\"onPageChange($event)\"\n [class.compact]=\"config.behavior?.pagination?.style === 'compact'\"\n>\n</mat-paginator>\n\n<mat-paginator\n *ngIf=\"resourcePath && !schemaError && !dataError && getPaginationEnabled() && (config.behavior?.pagination?.position === 'bottom' || config.behavior?.pagination?.position === 'both' || !config.behavior?.pagination?.position)\"\n [length]=\"getPaginationLength()\"\n [pageSize]=\"getPaginationPageSize()\"\n [pageSizeOptions]=\"getPaginationPageSizeOptions()\"\n [showFirstLastButtons]=\"getPaginationShowFirstLast()\"\n (page)=\"onPageChange($event)\"\n [class.compact]=\"config.behavior?.pagination?.style === 'compact'\"\n>\n</mat-paginator>\n", styles: ["@charset \"UTF-8\";table{width:100%}.praxis-actions-cell{height:100%;padding-inline:12px;white-space:nowrap}:host.density-compact{--p-header-padding: 8px 12px;--p-actions-btn-size: 32px;--p-actions-icon-size: 18px}:host.density-comfortable{--p-header-padding: 12px 16px;--p-actions-btn-size: 40px;--p-actions-icon-size: 22px}:host.density-spacious{--p-header-padding: 16px 20px;--p-actions-btn-size: 44px;--p-actions-icon-size: 24px}:host.density-compact ::ng-deep .mat-mdc-cell{padding:8px 12px}:host.density-comfortable ::ng-deep .mat-mdc-cell{padding:12px 16px}:host.density-spacious ::ng-deep .mat-mdc-cell{padding:16px 20px}:host.density-compact .praxis-actions-cell{padding-inline:8px}:host.density-spacious .praxis-actions-cell{padding-inline:16px}.praxis-actions-cell__content{display:flex;align-items:center;justify-content:flex-end;gap:8px;width:100%}.praxis-actions-cell.dense .praxis-actions-cell__content{gap:6px}.praxis-icon-btn{width:var(--p-actions-btn-size, 40px);height:var(--p-actions-btn-size, 40px);border:0;background:transparent;padding:0;display:inline-flex;align-items:center;justify-content:center;border-radius:9999px;cursor:pointer;--mat-icon-button-state-layer-size: var(--p-actions-btn-size, 40px)}.praxis-icon-btn:hover{background:var(--md-sys-color-surface-variant, rgba(255, 255, 255, .06))}.praxis-icon-btn:focus-visible{outline:2px solid var(--primary, #48a1ff);outline-offset:2px}.praxis-icon-btn mat-icon,.praxis-icon-btn .mat-icon{font-size:var(--p-actions-icon-size, 22px);width:var(--p-actions-icon-size, 22px);height:var(--p-actions-icon-size, 22px);line-height:var(--p-actions-icon-size, 22px)}.praxis-more-btn{width:var(--p-actions-more-btn-size, var(--p-actions-btn-size, 40px));height:var(--p-actions-more-btn-size, var(--p-actions-btn-size, 40px));--mat-icon-button-state-layer-size: var(--p-actions-more-btn-size, var(--p-actions-btn-size, 40px));background-image:var(--p-actions-more-btn-gradient, none);background-size:100% 100%;background-repeat:no-repeat}.praxis-more-btn mat-icon,.praxis-more-btn .mat-icon{font-size:var(--p-actions-more-icon-size, var(--p-actions-icon-size, 22px));width:var(--p-actions-more-icon-size, var(--p-actions-icon-size, 22px));height:var(--p-actions-more-icon-size, var(--p-actions-icon-size, 22px));line-height:var(--p-actions-more-icon-size, var(--p-actions-icon-size, 22px));color:var(--p-actions-more-icon-color);background-image:var(--p-actions-more-icon-gradient, none);-webkit-background-clip:text;background-clip:text}.praxis-icon-btn.destructive mat-icon{color:#ff6b6b}.mat-mdc-tooltip.praxis-tooltip{margin-top:-8px;margin-bottom:8px}.spacer{flex:1 1 auto}.praxis-table-header{display:flex;flex-wrap:wrap;align-items:flex-start;gap:8px;margin:16px 0 12px;width:100%;clear:both;position:relative}.praxis-table-header.debug-layout,:host.debug-layout .praxis-table-header{outline:2px dashed #ff4d4f;border-radius:8px}:host.debug-layout ::ng-deep .praxis-filter-bar{outline:1px dashed #f59e0b}:host.debug-layout ::ng-deep .quick-shell{outline:1px dashed #3b82f6}:host.debug-layout ::ng-deep .always-fields{outline:1px dashed #22c55e}:host.debug-layout ::ng-deep .praxis-filter-overlay .praxis-filter-advanced{outline:2px solid #a855f7}:host.debug-layout ::ng-deep .mat-mdc-table{outline:1px dashed #94a3b8}.praxis-table-header>praxis-table-toolbar{flex:1 0 100%}:host{--p-table-header-bg: var(--md-sys-color-surface-container-highest, #1d1d1f);--p-table-header-fg: var(--md-sys-color-on-surface, #e8f3f1);--p-table-border-color: var(--md-sys-color-outline-variant, rgba(255, 255, 255, .12));--p-table-row-even-bg: var(--md-sys-color-surface-container, rgba(255, 255, 255, .04));--p-table-row-hover-bg: color-mix(in srgb, var(--md-sys-color-primary, #3f51b5) 10%, transparent);--p-table-row-selected-bg: var(--md-sys-color-primary-container, rgba(63,81,181,.14));--p-table-badge-soft-primary-bg: color-mix(in srgb, var(--mat-sys-primary, var(--md-sys-color-primary)) 16%, transparent);--p-table-badge-soft-primary-fg: var(--mat-sys-primary, var(--md-sys-color-primary));--p-table-badge-soft-accent-bg: color-mix(in srgb, var(--mat-sys-secondary, var(--md-sys-color-secondary, #ff4081)) 14%, transparent);--p-table-badge-soft-accent-fg: var(--mat-sys-secondary, var(--md-sys-color-secondary, #ff4081));--p-table-badge-soft-warn-bg: color-mix(in srgb, var(--mat-sys-error, var(--md-sys-color-error, #f44336)) 14%, transparent);--p-table-badge-soft-warn-fg: var(--mat-sys-error, var(--md-sys-color-error, #f44336));--p-table-state-success-bg: color-mix(in srgb, var(--mat-sys-tertiary, var(--md-sys-color-tertiary, #388E3C)) 16%, transparent);--p-table-state-success-fg: var(--md-sys-color-on-surface, #c8e6c9);--p-table-state-warning-bg: color-mix(in srgb, var(--md-sys-color-secondary, #FFA000) 18%, transparent);--p-table-state-warning-fg: var(--md-sys-color-on-surface, #ffe0b2);--p-table-state-danger-bg: color-mix(in srgb, var(--md-sys-color-error, #e53935) 18%, transparent);--p-table-state-danger-fg: var(--md-sys-color-on-surface, #ffcdd2);--p-table-state-highlight-bg: color-mix(in srgb, var(--md-sys-color-primary, #2196f3) 16%, transparent);--p-table-state-highlight-fg: var(--md-sys-color-on-surface, #bbdefb)}:host ::ng-deep .mat-mdc-table{background:var(--md-sys-color-surface-container-highest, #1d1d1f);border-radius:12px;width:100%}:host ::ng-deep .mat-mdc-header-row{position:sticky;top:0;z-index:1;background:var(--p-table-header-bg);color:var(--p-table-header-fg);box-shadow:var(--p-table-header-shadow, 0 1px 0 var(--p-table-border-color));border-bottom:1px solid var(--p-table-border-color)}:host ::ng-deep .mat-mdc-header-cell,:host ::ng-deep .mat-sort-header-content,:host ::ng-deep .mat-mdc-header-row .mat-icon{color:var(--p-table-header-fg)!important;font-weight:600}:host ::ng-deep .mat-mdc-header-cell.mat-sort-header-sorted .mat-sort-header-arrow,:host ::ng-deep .mat-mdc-header-cell:hover .mat-sort-header-arrow{color:var(--p-table-header-fg)!important}:host ::ng-deep .mat-mdc-header-cell.mat-sort-header-sorted .mat-sort-header-arrow .mat-sort-header-indicator,:host ::ng-deep .mat-mdc-header-cell.mat-sort-header-sorted .mat-sort-header-arrow .mat-sort-header-stem,:host ::ng-deep .mat-mdc-header-cell.mat-sort-header-sorted .mat-sort-header-arrow .mat-sort-header-pointer,:host ::ng-deep .mat-mdc-header-cell.mat-sort-header-sorted .mat-sort-header-arrow .mat-sort-header-pointer-left,:host ::ng-deep .mat-mdc-header-cell.mat-sort-header-sorted .mat-sort-header-arrow .mat-sort-header-pointer-right{border-color:var(--p-table-header-fg)!important;background:var(--p-table-header-fg)!important}:host ::ng-deep .mat-mdc-header-cell{padding:var(--p-header-padding, 12px 16px)!important;font-size:var(--p-header-font-size, inherit);font-weight:var(--p-header-font-weight, 600);letter-spacing:var(--p-header-letter-spacing, normal);text-transform:var(--p-header-text-transform, none);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.praxis-actions-header{text-align:right}.praxis-actions-header.align-start{text-align:left}.praxis-actions-header.align-center{text-align:center}.praxis-actions-header.align-end{text-align:right}.praxis-actions-header .praxis-actions-header__content{display:inline-flex;align-items:center;gap:var(--p-actions-header-gap, 6px);color:var(--p-actions-header-color, inherit)}.praxis-actions-header .praxis-actions-header__content .mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}:host ::ng-deep .mat-mdc-header-cell .mat-sort-header-container{padding-right:20px}:host ::ng-deep .mat-mdc-cell{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}:host ::ng-deep .mat-mdc-row:hover{background:var(--p-table-row-hover-bg)}:host ::ng-deep .mat-mdc-row:nth-child(2n){background:var(--p-table-row-even-bg)}:host.row-borders ::ng-deep .mat-mdc-row .mat-mdc-cell{border-bottom:1px solid var(--p-table-border-color)}:host.row-borders ::ng-deep .mat-mdc-header-row .mat-mdc-header-cell{border-bottom:none}:host.col-borders ::ng-deep .mat-mdc-header-row .mat-mdc-header-cell,:host.col-borders ::ng-deep .mat-mdc-row .mat-mdc-cell{border-right:1px solid var(--p-table-border-color)}:host.col-borders ::ng-deep .mat-mdc-header-row .mat-mdc-header-cell:last-child,:host.col-borders ::ng-deep .mat-mdc-row .mat-mdc-cell:last-child{border-right:none}.ptable-info-banner{display:flex;gap:12px;align-items:center;padding:8px 12px;margin:8px 0;border-radius:8px;border:1px solid var(--mat-sys-primary, var(--md-sys-color-primary, #1a73e8));background:color-mix(in srgb,var(--mat-sys-primary, var(--md-sys-color-primary, #1a73e8)) 10%,transparent)}.ptable-info-banner .text{flex:1;font-weight:600}.ptable-info-banner .actions{display:flex;gap:8px}.pfx-cell-image{display:inline-block;vertical-align:middle;background:var(--md-sys-color-surface-variant, #2a2a2a);border:1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, .08))}.pfx-cell-image.shape-rounded{border-radius:8px}.pfx-cell-image.shape-circle{border-radius:9999px}.pfx-badge{display:inline-flex;align-items:center;gap:6px;line-height:1;padding:4px 8px;border-radius:9999px;font-size:12px;font-weight:600;border:1px solid transparent}.pfx-badge .pfx-badge-icon{font-size:16px;width:16px;height:16px}.pfx-badge--filled-primary{background:var(--mat-sys-primary, #3f51b5);color:var(--mat-sys-on-primary, #fff)}.pfx-badge--filled-accent{background:var(--mat-sys-secondary, #ff4081);color:var(--mat-sys-on-secondary, #fff)}.pfx-badge--filled-warn{background:var(--mat-sys-error, #f44336);color:var(--mat-sys-on-error, #fff)}.pfx-badge--outlined{background:transparent;border-color:var(--mat-sys-outline-variant, rgba(255, 255, 255, .24));color:inherit}.pfx-badge--soft-primary{background:var(--p-table-badge-soft-primary-bg);color:var(--p-table-badge-soft-primary-fg)}.pfx-badge--soft-accent{background:var(--p-table-badge-soft-accent-bg);color:var(--p-table-badge-soft-accent-fg)}.pfx-badge--soft-warn{background:var(--p-table-badge-soft-warn-bg);color:var(--p-table-badge-soft-warn-fg)}.row--success,.row--success td,td.row--success{background-color:var(--p-table-state-success-bg)!important;color:var(--p-table-state-success-fg)!important}.row--warning,.row--warning td,td.row--warning{background-color:var(--p-table-state-warning-bg)!important;color:var(--p-table-state-warning-fg)!important}.row--danger,.row--danger td,td.row--danger{background-color:var(--p-table-state-danger-bg)!important;color:var(--p-table-state-danger-fg)!important}.row--highlight,.row--highlight td,td.row--highlight{background-color:var(--p-table-state-highlight-bg)!important;color:var(--p-table-state-highlight-fg)!important;font-weight:600}.row--muted,.row--muted td,td.row--muted{opacity:.7;filter:saturate(.6)}\n"] }]
|
|
19124
19170
|
}], ctorParameters: () => [{ type: i1$3.GenericCrudService }, { type: i0.ChangeDetectorRef }, { type: i3$2.SettingsPanelService }, { type: DataFormattingService }, { type: i4$2.SpecificationBridgeService }, { type: undefined, decorators: [{
|
|
19125
19171
|
type: Inject,
|
|
19126
19172
|
args: [CONFIG_STORAGE]
|