@praxisui/table 5.0.0-beta.0 → 7.0.0-beta.0

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.
@@ -34,6 +34,7 @@ import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
34
34
  import { Subject, debounceTime, takeUntil, of, firstValueFrom, BehaviorSubject, take as take$1 } from 'rxjs';
35
35
  import * as i1 from '@praxisui/core';
36
36
  import { LoggerService, createCorporateLoggerConfig, ConsoleLoggerSink, PraxisJsonLogicService, PraxisIconDirective, PraxisI18nService, isTableConfigV2, providePraxisI18nConfig, GLOBAL_ACTION_SPEC_CATALOG, SURFACE_OPEN_I18N_NAMESPACE, getGlobalActionUiSchema, SurfaceOpenActionEditorComponent, SURFACE_OPEN_I18N_CONFIG, INLINE_FILTER_CONTROL_TYPES, INLINE_FILTER_CONTROL_TYPE_VALUES, FieldControlType, normalizeControlTypeToken, ASYNC_CONFIG_STORAGE, resolveControlTypeAlias, mapFieldDefinitionsToMetadata, PraxisJsonLogicError, GenericCrudService, TableConfigService, createDefaultTableConfig, fillUndefined, deepMerge, INLINE_FILTER_ALIAS_TOKENS, GlobalConfigService, buildSchemaId, MemoryCacheAdapter, LocalStorageCacheAdapter, SchemaMetadataClient, resolveInlineFilterControlType, fetchWithETag, ComponentMetadataRegistry, ResourceQuickConnectComponent, translateResourceAvailabilityReason, PRAXIS_LOADING_CTX, translateResourceDiscoveryText, supportsImplicitValuePresentation, resolveValuePresentation, translateUnavailableWorkflowMessage, CONNECTION_STORAGE, PRAXIS_LOADING_RENDERER, EmptyStateCardComponent, RESOURCE_DISCOVERY_I18N_CONFIG, AnalyticsStatsRequestBuilderService, buildApiUrl, normalizePraxisDataQueryContext, API_URL } from '@praxisui/core';
37
+ import { PraxisRichContent } from '@praxisui/rich-content';
37
38
  import * as i2 from '@angular/material/toolbar';
38
39
  import { MatToolbarModule } from '@angular/material/toolbar';
39
40
  import * as i4 from '@angular/material/tabs';
@@ -1168,7 +1169,7 @@ class PraxisTableToolbar {
1168
1169
  }
1169
1170
  </div>
1170
1171
  </mat-toolbar>
1171
- `, isInline: true, styles: [":host{display:block}.praxis-toolbar{background:transparent;box-shadow:none;padding:var(--pfx-toolbar-pad-y, 6px) var(--pfx-toolbar-pad-x, 12px);min-height:var(--pfx-filter-h, 44px);display:flex;flex-direction:row;align-items:flex-start;gap:8px;flex-wrap:wrap}.praxis-toolbar.mat-toolbar-single-row,.praxis-toolbar .mat-toolbar-row{height:auto}.praxis-toolbar.mat-toolbar-multiple-rows{min-height:var(--pfx-filter-h, 44px)}.toolbar-main{display:flex;align-items:stretch;justify-content:flex-start;min-width:0;gap:8px;min-height:var(--pfx-filter-h, 44px);flex:1 1 auto}.toolbar-main>*{min-width:0}.toolbar-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px;min-height:var(--pfx-filter-h, 44px);flex:0 0 auto;margin-left:auto}.actions-group{display:flex;align-items:center;gap:8px}.action-btn{height:40px}.action-btn--fab{width:48px;height:48px}.pfx-tonal{background:var(--md-sys-color-secondary-container);color:var(--md-sys-color-on-secondary-container)}.pfx-tonal:hover{background:var(--md-sys-color-secondary-container)}.mobile-actions{display:none;align-items:center;gap:8px}.overflow-trigger{height:40px}.end-actions{display:flex;align-items:center;gap:8px;margin-left:4px;padding-left:12px;border-left:1px solid color-mix(in srgb,var(--md-sys-color-outline) 68%,transparent)}@media(min-width:1024px){.toolbar-actions{justify-content:flex-end}}@media(max-width:1023px){.actions-group{display:none}.mobile-actions{display:flex}.toolbar-actions{justify-content:flex-end}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatToolbarModule }, { kind: "component", type: i2.MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }, { 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: "component", type: i3.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i17.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: i17.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i17.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"] }] });
1172
+ `, isInline: true, styles: [":host{display:block}.praxis-toolbar{background:linear-gradient(180deg,color-mix(in srgb,var(--md-sys-color-surface, #fff) 96%,transparent),color-mix(in srgb,var(--md-sys-color-surface-container-low, #f6f8fc) 100%,transparent));box-shadow:none;padding:var(--pfx-toolbar-pad-y, 6px) var(--pfx-toolbar-pad-x, 12px);min-height:var(--pfx-filter-h, 44px);display:flex;flex-direction:row;align-items:flex-start;gap:6px;flex-wrap:wrap;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #d7dbe5) 78%,transparent);border-radius:10px}.praxis-toolbar.mat-toolbar-single-row,.praxis-toolbar .mat-toolbar-row{height:auto}.praxis-toolbar.mat-toolbar-multiple-rows{min-height:var(--pfx-filter-h, 44px)}.toolbar-main{display:flex;align-items:stretch;justify-content:flex-start;min-width:0;gap:6px;min-height:var(--pfx-filter-h, 44px);flex:1 1 auto}.toolbar-main>*{min-width:0}.toolbar-actions{display:flex;align-items:center;justify-content:flex-end;gap:6px;min-height:var(--pfx-filter-h, 44px);flex:0 0 auto;margin-left:auto}.actions-group{display:flex;align-items:center;gap:6px}.action-btn{height:36px;min-width:36px;border-radius:8px;font-size:12px;line-height:1.2}.action-btn--fab{width:42px;height:42px;box-shadow:none}.pfx-tonal{background:var(--md-sys-color-secondary-container);color:var(--md-sys-color-on-secondary-container)}.pfx-tonal:hover{background:var(--md-sys-color-secondary-container)}.mobile-actions{display:none;align-items:center;gap:6px}.overflow-trigger{height:36px}.action-btn .mat-icon,.action-btn--fab .mat-icon,.overflow-trigger .mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}.action-btn.mat-mdc-button-base,.action-btn--fab.mat-mdc-button-base,.overflow-trigger.mat-mdc-button-base{--mdc-text-button-container-height: 36px;--mdc-protected-button-container-height: 36px;--mdc-outlined-button-container-height: 36px}.action-btn.mat-mdc-unelevated-button:not(:disabled),.action-btn.mat-mdc-outlined-button:not(:disabled),.action-btn.pfx-tonal:not(:disabled){border-color:color-mix(in srgb,var(--md-sys-color-outline-variant, #d7dbe5) 82%,transparent)}.action-btn.mat-mdc-unelevated-button:not(:disabled){box-shadow:none}.action-btn.mat-mdc-button-base:not(.mat-mdc-icon-button){padding-inline:10px}.end-actions{display:flex;align-items:center;gap:6px;margin-left:4px;padding-left:10px;border-left:1px solid color-mix(in srgb,var(--md-sys-color-outline) 68%,transparent)}@media(min-width:1024px){.toolbar-actions{justify-content:flex-end}}@media(max-width:1023px){.actions-group{display:none}.mobile-actions{display:flex}.toolbar-actions{justify-content:flex-end}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatToolbarModule }, { kind: "component", type: i2.MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }, { 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: "component", type: i3.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i17.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: i17.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i17.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"] }] });
1172
1173
  }
1173
1174
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisTableToolbar, decorators: [{
1174
1175
  type: Component,
@@ -1541,7 +1542,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
1541
1542
  `, host: {
1542
1543
  '[class.praxis-toolbar-footer]': "placement === 'footer'",
1543
1544
  '(document:keydown)': 'onDocumentKeydown($event)',
1544
- }, styles: [":host{display:block}.praxis-toolbar{background:transparent;box-shadow:none;padding:var(--pfx-toolbar-pad-y, 6px) var(--pfx-toolbar-pad-x, 12px);min-height:var(--pfx-filter-h, 44px);display:flex;flex-direction:row;align-items:flex-start;gap:8px;flex-wrap:wrap}.praxis-toolbar.mat-toolbar-single-row,.praxis-toolbar .mat-toolbar-row{height:auto}.praxis-toolbar.mat-toolbar-multiple-rows{min-height:var(--pfx-filter-h, 44px)}.toolbar-main{display:flex;align-items:stretch;justify-content:flex-start;min-width:0;gap:8px;min-height:var(--pfx-filter-h, 44px);flex:1 1 auto}.toolbar-main>*{min-width:0}.toolbar-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px;min-height:var(--pfx-filter-h, 44px);flex:0 0 auto;margin-left:auto}.actions-group{display:flex;align-items:center;gap:8px}.action-btn{height:40px}.action-btn--fab{width:48px;height:48px}.pfx-tonal{background:var(--md-sys-color-secondary-container);color:var(--md-sys-color-on-secondary-container)}.pfx-tonal:hover{background:var(--md-sys-color-secondary-container)}.mobile-actions{display:none;align-items:center;gap:8px}.overflow-trigger{height:40px}.end-actions{display:flex;align-items:center;gap:8px;margin-left:4px;padding-left:12px;border-left:1px solid color-mix(in srgb,var(--md-sys-color-outline) 68%,transparent)}@media(min-width:1024px){.toolbar-actions{justify-content:flex-end}}@media(max-width:1023px){.actions-group{display:none}.mobile-actions{display:flex}.toolbar-actions{justify-content:flex-end}}\n"] }]
1545
+ }, styles: [":host{display:block}.praxis-toolbar{background:linear-gradient(180deg,color-mix(in srgb,var(--md-sys-color-surface, #fff) 96%,transparent),color-mix(in srgb,var(--md-sys-color-surface-container-low, #f6f8fc) 100%,transparent));box-shadow:none;padding:var(--pfx-toolbar-pad-y, 6px) var(--pfx-toolbar-pad-x, 12px);min-height:var(--pfx-filter-h, 44px);display:flex;flex-direction:row;align-items:flex-start;gap:6px;flex-wrap:wrap;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant, #d7dbe5) 78%,transparent);border-radius:10px}.praxis-toolbar.mat-toolbar-single-row,.praxis-toolbar .mat-toolbar-row{height:auto}.praxis-toolbar.mat-toolbar-multiple-rows{min-height:var(--pfx-filter-h, 44px)}.toolbar-main{display:flex;align-items:stretch;justify-content:flex-start;min-width:0;gap:6px;min-height:var(--pfx-filter-h, 44px);flex:1 1 auto}.toolbar-main>*{min-width:0}.toolbar-actions{display:flex;align-items:center;justify-content:flex-end;gap:6px;min-height:var(--pfx-filter-h, 44px);flex:0 0 auto;margin-left:auto}.actions-group{display:flex;align-items:center;gap:6px}.action-btn{height:36px;min-width:36px;border-radius:8px;font-size:12px;line-height:1.2}.action-btn--fab{width:42px;height:42px;box-shadow:none}.pfx-tonal{background:var(--md-sys-color-secondary-container);color:var(--md-sys-color-on-secondary-container)}.pfx-tonal:hover{background:var(--md-sys-color-secondary-container)}.mobile-actions{display:none;align-items:center;gap:6px}.overflow-trigger{height:36px}.action-btn .mat-icon,.action-btn--fab .mat-icon,.overflow-trigger .mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}.action-btn.mat-mdc-button-base,.action-btn--fab.mat-mdc-button-base,.overflow-trigger.mat-mdc-button-base{--mdc-text-button-container-height: 36px;--mdc-protected-button-container-height: 36px;--mdc-outlined-button-container-height: 36px}.action-btn.mat-mdc-unelevated-button:not(:disabled),.action-btn.mat-mdc-outlined-button:not(:disabled),.action-btn.pfx-tonal:not(:disabled){border-color:color-mix(in srgb,var(--md-sys-color-outline-variant, #d7dbe5) 82%,transparent)}.action-btn.mat-mdc-unelevated-button:not(:disabled){box-shadow:none}.action-btn.mat-mdc-button-base:not(.mat-mdc-icon-button){padding-inline:10px}.end-actions{display:flex;align-items:center;gap:6px;margin-left:4px;padding-left:10px;border-left:1px solid color-mix(in srgb,var(--md-sys-color-outline) 68%,transparent)}@media(min-width:1024px){.toolbar-actions{justify-content:flex-end}}@media(max-width:1023px){.actions-group{display:none}.mobile-actions{display:flex}.toolbar-actions{justify-content:flex-end}}\n"] }]
1545
1546
  }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i1.LoggerService, decorators: [{
1546
1547
  type: Optional
1547
1548
  }] }], propDecorators: { config: [{
@@ -7031,6 +7032,38 @@ const PRAXIS_TABLE_EDITOR_I18N_CONFIG = {
7031
7032
  'sections.messages': 'Mensagens e localização',
7032
7033
  'sections.dialogs': 'Diálogos',
7033
7034
  'sections.json': 'JSON',
7035
+ 'inlineAuthoring.title': 'Estrutura da tabela',
7036
+ 'inlineAuthoring.description': 'Edite o TableConfig canônico usado por esta superfície de CRUD.',
7037
+ 'inlineAuthoring.essentialsTitle': 'Essenciais da tabela',
7038
+ 'inlineAuthoring.essentialsDescription': 'Defina a presença da toolbar e os textos que o usuário vê primeiro.',
7039
+ 'inlineAuthoring.advancedTitle': 'Detalhes avançados da tabela',
7040
+ 'inlineAuthoring.advancedDescription': 'Abra os próximos painéis apenas quando precisar ajustar comportamento da lista, textos de estado ou colunas visíveis.',
7041
+ 'inlineAuthoring.advanced.behavior': 'Comportamento',
7042
+ 'inlineAuthoring.advanced.states': 'Textos de estado',
7043
+ 'inlineAuthoring.advanced.columns': 'Colunas',
7044
+ 'inlineAuthoring.toolbarTitle': 'Título da barra',
7045
+ 'inlineAuthoring.columnsTitle': 'Colunas',
7046
+ 'inlineAuthoring.columnsDescription': 'Abra apenas quando precisar ajustar campos visíveis, rótulos e renderizadores.',
7047
+ 'inlineAuthoring.summary.toolbar': 'Toolbar',
7048
+ 'inlineAuthoring.summary.density': 'Densidade',
7049
+ 'inlineAuthoring.summary.actions': 'Ações',
7050
+ 'inlineAuthoring.summary.behavior': 'Comportamento da lista',
7051
+ 'inlineAuthoring.summary.states': 'Mensagens de estado',
7052
+ 'inlineAuthoring.summary.columns': 'Estrutura visível',
7053
+ 'inlineAuthoring.summary.toolbarHidden': 'Oculta',
7054
+ 'inlineAuthoring.summary.toolbarDefault': 'Visível sem título customizado',
7055
+ 'inlineAuthoring.summary.paginationOff': 'Paginação desativada',
7056
+ 'inlineAuthoring.summary.sortingOff': 'Ordenação desativada',
7057
+ 'inlineAuthoring.summary.statesDefault': 'Textos padrão',
7058
+ 'inlineAuthoring.summary.statesCustom': '{count} estados customizados',
7059
+ 'inlineAuthoring.summary.columnsEmpty': 'Nenhuma coluna configurada ainda',
7060
+ 'inlineAuthoring.columnCount': '{count} colunas',
7061
+ 'inlineAuthoring.behaviorTitle': 'Comportamento da lista',
7062
+ 'inlineAuthoring.behaviorDescription': 'Controle paginação e ordenação com a mesma semântica canônica já usada pelo editor completo da tabela.',
7063
+ 'inlineAuthoring.statesTitle': 'Mensagens de estado',
7064
+ 'inlineAuthoring.statesDescription': 'Ajuste as primeiras mensagens que o usuário lê quando a lista está carregando, vazia, filtrada ou com erro.',
7065
+ 'inlineAuthoring.noneOption': 'Nenhum',
7066
+ 'inlineAuthoring.readonlyHint': 'Esta superfície de authoring está somente leitura no contexto atual.',
7034
7067
  'connect.resource': 'Recurso',
7035
7068
  'connect.resource.placeholder': 'ex.: employees',
7036
7069
  'connect.primaryKey': 'Chave primária',
@@ -7606,7 +7639,9 @@ const PRAXIS_TABLE_EDITOR_I18N_CONFIG = {
7606
7639
  'behavior.pagination.positionHint': 'Paginator no topo/baixo/ambos.',
7607
7640
  'behavior.pagination.style': 'Estilo do paginator',
7608
7641
  'behavior.pagination.style.default': 'Padrao',
7642
+ 'behavior.pagination.style.simple': 'Simples',
7609
7643
  'behavior.pagination.style.compact': 'Compacto',
7644
+ 'behavior.pagination.style.advanced': 'Avancado',
7610
7645
  'behavior.pagination.styleHint': 'Compacto para densidade alta.',
7611
7646
  'behavior.sorting.title': 'Ordenacao',
7612
7647
  'behavior.sorting.description': 'Controle como as colunas podem ser ordenadas',
@@ -7726,7 +7761,7 @@ const PRAXIS_TABLE_EDITOR_I18N_CONFIG = {
7726
7761
  'behavior.expansion.detailSource.resourcePath': 'Resource Path',
7727
7762
  'behavior.expansion.detailSource.hypermedia': 'Hypermedia',
7728
7763
  'behavior.expansion.inlineSchema': 'Inline schema (JSON)',
7729
- 'behavior.expansion.inlineSchemaHint': 'Aceita nos como card/formRef/tableRef/chartRef/richText/tabs/list, conforme schemaContract.allowedNodes.',
7764
+ 'behavior.expansion.inlineSchemaHint': 'Aceita nós como card/mediaBlock/cardGrid/detailList/timeline/actionBar/formRef/tableRef/chartRef/diagramEmbed/richText/tabs/list, conforme schemaContract.allowedNodes.',
7730
7765
  'behavior.expansion.inlineSchemaError': 'JSON invalido: o contrato atual nao foi aplicado para',
7731
7766
  'behavior.expansion.allowList': 'Resource allow list',
7732
7767
  'behavior.expansion.resourceKind': 'Tipo de recurso',
@@ -7773,8 +7808,8 @@ const PRAXIS_TABLE_EDITOR_I18N_CONFIG = {
7773
7808
  'behavior.appearance.density.compact': 'Compacta',
7774
7809
  'behavior.appearance.density.comfortable': 'Confortavel',
7775
7810
  'behavior.appearance.density.spacious': 'Espacosa',
7776
- 'crud.defaults.title': 'Padroes do CRUD',
7777
- 'crud.defaults.description': 'Comportamento ao abrir formularios a partir da tabela',
7811
+ 'crud.defaults.title': 'Overrides de runtime do CRUD',
7812
+ 'crud.defaults.description': 'Ajustes operacionais de abertura usados quando a tabela aciona o fluxo CRUD.',
7778
7813
  'crud.defaults.openMode': 'Modo de abertura',
7779
7814
  'crud.mode.modal': 'Modal',
7780
7815
  'crud.mode.route': 'Rota',
@@ -8023,6 +8058,38 @@ const PRAXIS_TABLE_EDITOR_I18N_CONFIG = {
8023
8058
  'sections.messages': 'Messages and localization',
8024
8059
  'sections.dialogs': 'Dialogs',
8025
8060
  'sections.json': 'JSON',
8061
+ 'inlineAuthoring.title': 'Table structure',
8062
+ 'inlineAuthoring.description': 'Edit the canonical TableConfig used by this CRUD surface.',
8063
+ 'inlineAuthoring.essentialsTitle': 'Table essentials',
8064
+ 'inlineAuthoring.essentialsDescription': 'Define the toolbar presence and the texts users see first.',
8065
+ 'inlineAuthoring.advancedTitle': 'Advanced table details',
8066
+ 'inlineAuthoring.advancedDescription': 'Open the next panels only when you need to tune list behavior, state copy, or visible columns.',
8067
+ 'inlineAuthoring.advanced.behavior': 'Behavior',
8068
+ 'inlineAuthoring.advanced.states': 'State copy',
8069
+ 'inlineAuthoring.advanced.columns': 'Columns',
8070
+ 'inlineAuthoring.toolbarTitle': 'Toolbar title',
8071
+ 'inlineAuthoring.columnsTitle': 'Columns',
8072
+ 'inlineAuthoring.columnsDescription': 'Open only when you need to adjust visible fields, labels, and renderers.',
8073
+ 'inlineAuthoring.summary.toolbar': 'Toolbar',
8074
+ 'inlineAuthoring.summary.density': 'Density',
8075
+ 'inlineAuthoring.summary.actions': 'Actions',
8076
+ 'inlineAuthoring.summary.behavior': 'List behavior',
8077
+ 'inlineAuthoring.summary.states': 'State messages',
8078
+ 'inlineAuthoring.summary.columns': 'Visible structure',
8079
+ 'inlineAuthoring.summary.toolbarHidden': 'Hidden',
8080
+ 'inlineAuthoring.summary.toolbarDefault': 'Visible without custom title',
8081
+ 'inlineAuthoring.summary.paginationOff': 'Pagination off',
8082
+ 'inlineAuthoring.summary.sortingOff': 'Sorting off',
8083
+ 'inlineAuthoring.summary.statesDefault': 'Default copy',
8084
+ 'inlineAuthoring.summary.statesCustom': '{count} customized states',
8085
+ 'inlineAuthoring.summary.columnsEmpty': 'No columns configured yet',
8086
+ 'inlineAuthoring.columnCount': '{count} columns',
8087
+ 'inlineAuthoring.behaviorTitle': 'List behavior',
8088
+ 'inlineAuthoring.behaviorDescription': 'Control pagination and sorting with the same canonical semantics already used by the full table editor.',
8089
+ 'inlineAuthoring.statesTitle': 'State messages',
8090
+ 'inlineAuthoring.statesDescription': 'Adjust the first messages users read when the list is loading, empty, filtered, or broken.',
8091
+ 'inlineAuthoring.noneOption': 'None',
8092
+ 'inlineAuthoring.readonlyHint': 'This authoring surface is read-only in the current context.',
8026
8093
  'connect.resource': 'Resource',
8027
8094
  'connect.resource.placeholder': 'e.g. employees',
8028
8095
  'connect.primaryKey': 'Primary key',
@@ -8598,7 +8665,9 @@ const PRAXIS_TABLE_EDITOR_I18N_CONFIG = {
8598
8665
  'behavior.pagination.positionHint': 'Paginator on top/bottom/both.',
8599
8666
  'behavior.pagination.style': 'Paginator style',
8600
8667
  'behavior.pagination.style.default': 'Default',
8668
+ 'behavior.pagination.style.simple': 'Simple',
8601
8669
  'behavior.pagination.style.compact': 'Compact',
8670
+ 'behavior.pagination.style.advanced': 'Advanced',
8602
8671
  'behavior.pagination.styleHint': 'Compact for high density.',
8603
8672
  'behavior.sorting.title': 'Sorting',
8604
8673
  'behavior.sorting.description': 'Control how columns can be sorted',
@@ -8718,7 +8787,7 @@ const PRAXIS_TABLE_EDITOR_I18N_CONFIG = {
8718
8787
  'behavior.expansion.detailSource.resourcePath': 'Resource Path',
8719
8788
  'behavior.expansion.detailSource.hypermedia': 'Hypermedia',
8720
8789
  'behavior.expansion.inlineSchema': 'Inline schema (JSON)',
8721
- 'behavior.expansion.inlineSchemaHint': 'Accepts nodes such as card/formRef/tableRef/chartRef/richText/tabs/list, according to schemaContract.allowedNodes.',
8790
+ 'behavior.expansion.inlineSchemaHint': 'Accepts nodes such as card/mediaBlock/cardGrid/detailList/timeline/actionBar/formRef/tableRef/chartRef/diagramEmbed/richText/tabs/list, according to schemaContract.allowedNodes.',
8722
8791
  'behavior.expansion.inlineSchemaError': 'Invalid JSON: the current contract was not applied to',
8723
8792
  'behavior.expansion.allowList': 'Resource allow list',
8724
8793
  'behavior.expansion.resourceKind': 'Resource kind',
@@ -8765,8 +8834,8 @@ const PRAXIS_TABLE_EDITOR_I18N_CONFIG = {
8765
8834
  'behavior.appearance.density.compact': 'Compact',
8766
8835
  'behavior.appearance.density.comfortable': 'Comfortable',
8767
8836
  'behavior.appearance.density.spacious': 'Spacious',
8768
- 'crud.defaults.title': 'CRUD defaults',
8769
- 'crud.defaults.description': 'Behavior when opening forms from the table',
8837
+ 'crud.defaults.title': 'CRUD runtime overrides',
8838
+ 'crud.defaults.description': 'Operational open-mode settings applied when the table launches the CRUD flow.',
8770
8839
  'crud.defaults.openMode': 'Open mode',
8771
8840
  'crud.mode.modal': 'Modal',
8772
8841
  'crud.mode.route': 'Route',
@@ -14414,12 +14483,18 @@ const DEFAULT_EXPANSION_ALLOWED_NODES$1 = [
14414
14483
  'tabs',
14415
14484
  'tab',
14416
14485
  'card',
14486
+ 'mediaBlock',
14487
+ 'cardGrid',
14488
+ 'timeline',
14417
14489
  'value',
14418
14490
  'action',
14491
+ 'actionBar',
14419
14492
  'list',
14493
+ 'detailList',
14420
14494
  'formRef',
14421
14495
  'tableRef',
14422
14496
  'chartRef',
14497
+ 'diagramEmbed',
14423
14498
  'richText',
14424
14499
  'templateRef',
14425
14500
  ];
@@ -16088,7 +16163,7 @@ class BehaviorConfigEditorComponent {
16088
16163
  matSuffix
16089
16164
  type="button"
16090
16165
  class="help-icon-button"
16091
- [matTooltip]="tx('behavior.expansion.inlineSchemaHint', 'Accepts nodes such as card/formRef/tableRef/chartRef/richText/tabs/list, according to schemaContract.allowedNodes.')"
16166
+ [matTooltip]="tx('behavior.expansion.inlineSchemaHint', 'Accepts nodes such as card/mediaBlock/cardGrid/timeline/actionBar/detailList/formRef/tableRef/chartRef/templateRef/diagramEmbed/richText/tabs/list, according to schemaContract.allowedNodes.')"
16092
16167
  >
16093
16168
  <mat-icon>help_outline</mat-icon>
16094
16169
  </button>
@@ -17080,7 +17155,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
17080
17155
  matSuffix
17081
17156
  type="button"
17082
17157
  class="help-icon-button"
17083
- [matTooltip]="tx('behavior.expansion.inlineSchemaHint', 'Accepts nodes such as card/formRef/tableRef/chartRef/richText/tabs/list, according to schemaContract.allowedNodes.')"
17158
+ [matTooltip]="tx('behavior.expansion.inlineSchemaHint', 'Accepts nodes such as card/mediaBlock/cardGrid/timeline/actionBar/detailList/formRef/tableRef/chartRef/templateRef/diagramEmbed/richText/tabs/list, according to schemaContract.allowedNodes.')"
17084
17159
  >
17085
17160
  <mat-icon>help_outline</mat-icon>
17086
17161
  </button>
@@ -36111,6 +36186,12 @@ const PRAXIS_TABLE_RUNTIME_I18N_CONFIG = {
36111
36186
  namespaces: {
36112
36187
  [PRAXIS_TABLE_RUNTIME_I18N_NAMESPACE]: {
36113
36188
  'pt-BR': {
36189
+ 'table.chrome.settings': 'Configurações',
36190
+ 'table.chrome.settings.reconcile': 'Schema do servidor mudou - reconciliar',
36191
+ 'table.chrome.disconnect': 'Desconectar',
36192
+ 'table.chrome.disconnectTooltip': 'Desconectar da fonte de dados',
36193
+ 'table.chrome.rowActions.more': 'Mais ações',
36194
+ 'table.chrome.rowActions.loading': 'Carregando ações contextuais',
36114
36195
  'table.expansion.toggle.ariaLabel.expand': 'Expandir detalhes do item',
36115
36196
  'table.expansion.toggle.ariaLabel.collapse': 'Recolher detalhes do item',
36116
36197
  'table.expansion.region.ariaLabel': 'Detalhes do item expandido (linha {position})',
@@ -36157,8 +36238,29 @@ const PRAXIS_TABLE_RUNTIME_I18N_CONFIG = {
36157
36238
  'table.expansion.hypermedia.operations.create': 'Criar',
36158
36239
  'table.expansion.hypermedia.operations.edit': 'Editar',
36159
36240
  'table.expansion.hypermedia.operations.delete': 'Excluir',
36241
+ 'table.expansion.actionBar.title': 'Ações rápidas',
36242
+ 'table.expansion.actionBar.empty': 'Nenhuma ação disponível.',
36243
+ 'table.expansion.timeline.title': 'Linha do tempo',
36244
+ 'table.expansion.timeline.empty': 'Sem eventos recentes.',
36245
+ 'table.expansion.embed.formRef.label': 'Formulário governado',
36246
+ 'table.expansion.embed.tableRef.label': 'Tabela governada',
36247
+ 'table.expansion.embed.chartRef.label': 'Gráfico governado',
36248
+ 'table.expansion.embed.templateRef.label': 'Template governado',
36249
+ 'table.expansion.embed.diagramEmbed.label': 'Diagrama governado',
36250
+ 'table.expansion.embed.empty': 'Nenhum conteúdo de embed disponível.',
36251
+ 'table.expansion.embed.reference': 'Referência',
36252
+ 'table.expansion.embed.provider': 'Provedor',
36253
+ 'table.expansion.embed.preset': 'Preset',
36254
+ 'table.expansion.embed.inputs': 'Inputs',
36255
+ 'table.expansion.embed.diagramSource': 'Fonte do diagrama',
36160
36256
  },
36161
36257
  'en-US': {
36258
+ 'table.chrome.settings': 'Settings',
36259
+ 'table.chrome.settings.reconcile': 'Server schema changed - reconcile',
36260
+ 'table.chrome.disconnect': 'Disconnect',
36261
+ 'table.chrome.disconnectTooltip': 'Disconnect from the data source',
36262
+ 'table.chrome.rowActions.more': 'More actions',
36263
+ 'table.chrome.rowActions.loading': 'Loading contextual actions',
36162
36264
  'table.expansion.toggle.ariaLabel.expand': 'Expand item details',
36163
36265
  'table.expansion.toggle.ariaLabel.collapse': 'Collapse item details',
36164
36266
  'table.expansion.region.ariaLabel': 'Expanded item details (row {position})',
@@ -36205,6 +36307,21 @@ const PRAXIS_TABLE_RUNTIME_I18N_CONFIG = {
36205
36307
  'table.expansion.hypermedia.operations.create': 'Create',
36206
36308
  'table.expansion.hypermedia.operations.edit': 'Edit',
36207
36309
  'table.expansion.hypermedia.operations.delete': 'Delete',
36310
+ 'table.expansion.actionBar.title': 'Quick actions',
36311
+ 'table.expansion.actionBar.empty': 'No actions available.',
36312
+ 'table.expansion.timeline.title': 'Timeline',
36313
+ 'table.expansion.timeline.empty': 'No recent events.',
36314
+ 'table.expansion.embed.formRef.label': 'Governed form',
36315
+ 'table.expansion.embed.tableRef.label': 'Governed table',
36316
+ 'table.expansion.embed.chartRef.label': 'Governed chart',
36317
+ 'table.expansion.embed.templateRef.label': 'Governed template',
36318
+ 'table.expansion.embed.diagramEmbed.label': 'Governed diagram',
36319
+ 'table.expansion.embed.empty': 'No embed content available.',
36320
+ 'table.expansion.embed.reference': 'Reference',
36321
+ 'table.expansion.embed.provider': 'Provider',
36322
+ 'table.expansion.embed.preset': 'Preset',
36323
+ 'table.expansion.embed.inputs': 'Inputs',
36324
+ 'table.expansion.embed.diagramSource': 'Diagram source',
36208
36325
  },
36209
36326
  },
36210
36327
  },
@@ -36251,12 +36368,18 @@ const DEFAULT_EXPANSION_ALLOWED_NODES = [
36251
36368
  'tabs',
36252
36369
  'tab',
36253
36370
  'card',
36371
+ 'mediaBlock',
36372
+ 'cardGrid',
36254
36373
  'value',
36255
36374
  'action',
36375
+ 'actionBar',
36256
36376
  'list',
36377
+ 'detailList',
36378
+ 'timeline',
36257
36379
  'formRef',
36258
36380
  'tableRef',
36259
36381
  'chartRef',
36382
+ 'diagramEmbed',
36260
36383
  'richText',
36261
36384
  'templateRef',
36262
36385
  ];
@@ -36378,6 +36501,7 @@ class PraxisTable {
36378
36501
  metadataChange = new EventEmitter();
36379
36502
  loadingStateChange = new EventEmitter();
36380
36503
  collectionLinksChange = new EventEmitter();
36504
+ latestLoadingState = null;
36381
36505
  paginator;
36382
36506
  sort;
36383
36507
  materialTable;
@@ -36518,6 +36642,16 @@ class PraxisTable {
36518
36642
  return true;
36519
36643
  return this.selection.selected.length === 0;
36520
36644
  }
36645
+ getFloatingBulkActionRichContentNodes(action) {
36646
+ const icon = String(action?.icon || 'done_all').trim() || 'done_all';
36647
+ return [
36648
+ {
36649
+ type: 'icon',
36650
+ icon,
36651
+ className: 'praxis-floating-bulk-actions__icon',
36652
+ },
36653
+ ];
36654
+ }
36521
36655
  shouldShowToolbarActionsTop() {
36522
36656
  const pos = this.getToolbarActionsPosition();
36523
36657
  return pos === 'top' || pos === 'both';
@@ -36691,6 +36825,31 @@ class PraxisTable {
36691
36825
  this.cdr.markForCheck();
36692
36826
  }
36693
36827
  }
36828
+ scheduleResponsiveHorizontalScrollRefresh() {
36829
+ const scheduler = typeof globalThis.requestAnimationFrame === 'function'
36830
+ ? globalThis.requestAnimationFrame.bind(globalThis)
36831
+ : null;
36832
+ if (!scheduler) {
36833
+ this.refreshResponsiveHorizontalScroll();
36834
+ return;
36835
+ }
36836
+ if (this.resizeObserverRefreshFrameId !== null) {
36837
+ return;
36838
+ }
36839
+ this.resizeObserverRefreshFrameId = scheduler(() => {
36840
+ this.resizeObserverRefreshFrameId = null;
36841
+ this.refreshResponsiveHorizontalScroll();
36842
+ });
36843
+ }
36844
+ cancelResponsiveHorizontalScrollRefresh() {
36845
+ if (this.resizeObserverRefreshFrameId === null) {
36846
+ return;
36847
+ }
36848
+ if (typeof globalThis.cancelAnimationFrame === 'function') {
36849
+ globalThis.cancelAnimationFrame(this.resizeObserverRefreshFrameId);
36850
+ }
36851
+ this.resizeObserverRefreshFrameId = null;
36852
+ }
36694
36853
  hasBottomPaginator() {
36695
36854
  if (!this.getPaginationEnabled())
36696
36855
  return false;
@@ -36734,6 +36893,82 @@ class PraxisTable {
36734
36893
  return true;
36735
36894
  return this.isLocalMode() && !this.isLocalDataModeFeatureEnabled();
36736
36895
  }
36896
+ shouldShowNoDataState() {
36897
+ if (this.shouldShowEmptyState() ||
36898
+ !this.shouldRenderDataSurface() ||
36899
+ this.schemaError ||
36900
+ this.dataError ||
36901
+ this.isTableDataLoading()) {
36902
+ return false;
36903
+ }
36904
+ if (this.isRemoteMode() && !this.hasResolvedRemoteDataLoad()) {
36905
+ return false;
36906
+ }
36907
+ return this.getRenderedRowCount() === 0;
36908
+ }
36909
+ shouldShowLoadingSurface() {
36910
+ if (this.shouldShowEmptyState() ||
36911
+ !this.shouldRenderDataSurface() ||
36912
+ this.schemaError ||
36913
+ this.dataError) {
36914
+ return false;
36915
+ }
36916
+ return this.isTableDataLoading() && this.getRenderedRowCount() === 0;
36917
+ }
36918
+ getLoadingSurfaceMessage() {
36919
+ return this.config?.messages?.states?.loading || 'Carregando dados...';
36920
+ }
36921
+ getNoDataStateTitle() {
36922
+ const emptyState = this.resolveNoDataStateConfig();
36923
+ return emptyState.message;
36924
+ }
36925
+ getNoDataStateDescription() {
36926
+ const emptyState = this.resolveNoDataStateConfig();
36927
+ const actions = emptyState.actions ?? [];
36928
+ if (!actions.length) {
36929
+ return undefined;
36930
+ }
36931
+ return this.hasActiveDataFilters()
36932
+ ? 'Ajuste os filtros ou abra outra carteira operacional.'
36933
+ : 'Use a ação principal para iniciar a próxima operação.';
36934
+ }
36935
+ getTableSettingsLabel() {
36936
+ return translateTableRuntimeText(this.i18n, 'table.chrome.settings', 'Configurações');
36937
+ }
36938
+ getTableSettingsTooltip() {
36939
+ return this.schemaOutdated
36940
+ ? translateTableRuntimeText(this.i18n, 'table.chrome.settings.reconcile', 'Schema do servidor mudou - reconciliar')
36941
+ : this.getTableSettingsLabel();
36942
+ }
36943
+ getDisconnectLabel() {
36944
+ return translateTableRuntimeText(this.i18n, 'table.chrome.disconnect', 'Desconectar');
36945
+ }
36946
+ getDisconnectTooltip() {
36947
+ return translateTableRuntimeText(this.i18n, 'table.chrome.disconnectTooltip', 'Desconectar da fonte de dados');
36948
+ }
36949
+ getNoDataStateIcon() {
36950
+ const emptyState = this.resolveNoDataStateConfig();
36951
+ return (emptyState.icon || '').trim();
36952
+ }
36953
+ getNoDataStatePrimaryAction() {
36954
+ const actions = this.resolveNoDataStateConfig().actions ?? [];
36955
+ const action = actions.find((entry) => entry?.primary) ?? actions[0];
36956
+ return action ? this.mapEmptyStateAction(action) : null;
36957
+ }
36958
+ getNoDataStateSecondaryActions() {
36959
+ const actions = this.resolveNoDataStateConfig().actions ?? [];
36960
+ const primary = this.getNoDataStatePrimaryAction();
36961
+ const primaryId = primary?.label ? primary.label : null;
36962
+ return actions
36963
+ .filter((entry) => {
36964
+ if (!entry)
36965
+ return false;
36966
+ if (entry.primary && primaryId)
36967
+ return false;
36968
+ return true;
36969
+ })
36970
+ .map((entry) => this.mapEmptyStateAction(entry));
36971
+ }
36737
36972
  shouldRenderAdvancedFilter() {
36738
36973
  if (!this.config?.behavior?.filtering?.advancedFilters?.enabled) {
36739
36974
  return false;
@@ -36901,6 +37136,7 @@ class PraxisTable {
36901
37136
  allowComparisons: true,
36902
37137
  });
36903
37138
  resizeObserver;
37139
+ resizeObserverRefreshFrameId = null;
36904
37140
  removeViewportChangeListeners = null;
36905
37141
  warnedUnsupportedFeatures = new Set();
36906
37142
  warnedStrictCspStyleMode = false;
@@ -37324,7 +37560,7 @@ class PraxisTable {
37324
37560
  });
37325
37561
  }
37326
37562
  emitLoadingState(phase, status, message, error) {
37327
- this.loadingStateChange.emit({
37563
+ this.latestLoadingState = {
37328
37564
  phase,
37329
37565
  status,
37330
37566
  message,
@@ -37335,8 +37571,71 @@ class PraxisTable {
37335
37571
  resourcePath: this.resourcePath,
37336
37572
  },
37337
37573
  timestamp: new Date().toISOString(),
37574
+ };
37575
+ this.loadingStateChange.emit({
37576
+ ...this.latestLoadingState,
37338
37577
  });
37339
37578
  }
37579
+ hasResolvedRemoteDataLoad() {
37580
+ return this.latestLoadingState?.status === 'success';
37581
+ }
37582
+ isTableDataLoading() {
37583
+ return this.latestLoadingState?.status === 'loading';
37584
+ }
37585
+ getRenderedRowCount() {
37586
+ return Array.isArray(this.dataSource?.data) ? this.dataSource.data.length : 0;
37587
+ }
37588
+ hasActiveDataFilters() {
37589
+ const criteria = this.getEffectiveFilterCriteria();
37590
+ const values = Object.values(criteria || {});
37591
+ return values.some((value) => this.hasMeaningfulFilterValue(value));
37592
+ }
37593
+ hasMeaningfulFilterValue(value) {
37594
+ if (value == null)
37595
+ return false;
37596
+ if (typeof value === 'string')
37597
+ return value.trim().length > 0;
37598
+ if (typeof value === 'number' || typeof value === 'boolean')
37599
+ return true;
37600
+ if (Array.isArray(value)) {
37601
+ return value.some((entry) => this.hasMeaningfulFilterValue(entry));
37602
+ }
37603
+ if (typeof value === 'object') {
37604
+ return Object.values(value).some((entry) => this.hasMeaningfulFilterValue(entry));
37605
+ }
37606
+ return false;
37607
+ }
37608
+ resolveNoDataStateConfig() {
37609
+ const baseEmptyState = this.config?.behavior?.emptyState;
37610
+ const contextualEmptyState = this.hasActiveDataFilters()
37611
+ ? (baseEmptyState?.contexts?.filtered ?? baseEmptyState?.contexts?.searched)
37612
+ : baseEmptyState?.contexts?.initial;
37613
+ const merged = {
37614
+ ...(baseEmptyState || {}),
37615
+ ...(contextualEmptyState || {}),
37616
+ };
37617
+ const fallbackMessage = this.hasActiveDataFilters()
37618
+ ? this.config?.messages?.states?.noResults || 'Nenhum resultado encontrado.'
37619
+ : this.config?.messages?.states?.empty || 'Nenhum dado disponivel.';
37620
+ return {
37621
+ message: String(merged.message || fallbackMessage),
37622
+ icon: typeof merged.icon === 'string' ? merged.icon : undefined,
37623
+ actions: Array.isArray(merged.actions) ? merged.actions : [],
37624
+ };
37625
+ }
37626
+ mapEmptyStateAction(action) {
37627
+ return {
37628
+ label: action.label,
37629
+ icon: action.icon,
37630
+ action: () => this.onEmptyStateAction(action.action),
37631
+ };
37632
+ }
37633
+ onEmptyStateAction(actionId) {
37634
+ const action = String(actionId || '').trim();
37635
+ if (!action)
37636
+ return;
37637
+ this.onToolbarAction({ action });
37638
+ }
37340
37639
  buildLoadingContext(phase, label, blocking) {
37341
37640
  const componentId = this.tableId || this.componentInstanceId || 'table';
37342
37641
  return {
@@ -37801,24 +38100,839 @@ class PraxisTable {
37801
38100
  }
37802
38101
  return '—';
37803
38102
  }
38103
+ getExpansionDetailListEntries(row, node) {
38104
+ const field = String(node?.field || '').trim();
38105
+ const staticItems = Array.isArray(node?.items) ? node.items : null;
38106
+ if (field) {
38107
+ const resolved = this.getNestedPropertyValue(row, field);
38108
+ if (Array.isArray(resolved)) {
38109
+ return resolved;
38110
+ }
38111
+ }
38112
+ if (staticItems) {
38113
+ return staticItems;
38114
+ }
38115
+ return [];
38116
+ }
37804
38117
  getExpansionDetailListItems(row, node) {
38118
+ return this.getExpansionDetailListEntries(row, node)
38119
+ .map((entry) => this.formatExpansionDetailListEntry(entry))
38120
+ .filter((entry) => entry.length > 0);
38121
+ }
38122
+ getExpansionDetailRichListEntries(row, node) {
38123
+ const field = String(node?.field || '').trim();
38124
+ const staticItems = Array.isArray(node?.items) ? node.items : null;
38125
+ if (field) {
38126
+ const resolved = this.getNestedPropertyValue(row, field);
38127
+ if (Array.isArray(resolved)) {
38128
+ return resolved;
38129
+ }
38130
+ }
38131
+ if (staticItems) {
38132
+ return staticItems;
38133
+ }
38134
+ return [];
38135
+ }
38136
+ getExpansionDetailRichListEmptyText(node) {
38137
+ return String(node?.emptyText || 'Sem itens.').trim() || 'Sem itens.';
38138
+ }
38139
+ getExpansionDetailRichListItemNodes(node) {
38140
+ return Array.isArray(node?.itemSchema?.nodes)
38141
+ ? node.itemSchema.nodes
38142
+ : [];
38143
+ }
38144
+ getExpansionDetailCardGridHeaderNodes(node) {
38145
+ const title = String(node?.title || '').trim();
38146
+ const subtitle = String(node?.subtitle || '').trim();
38147
+ const items = [];
38148
+ if (title) {
38149
+ items.push({
38150
+ type: 'text',
38151
+ text: title,
38152
+ className: 'pfx-expansion-node-card-grid__title-text',
38153
+ });
38154
+ }
38155
+ if (subtitle) {
38156
+ items.push({
38157
+ type: 'text',
38158
+ text: subtitle,
38159
+ className: 'pfx-expansion-node-card-grid__subtitle-text',
38160
+ });
38161
+ }
38162
+ if (!items.length) {
38163
+ return [];
38164
+ }
38165
+ return [
38166
+ {
38167
+ type: 'compose',
38168
+ direction: 'column',
38169
+ gap: 'xs',
38170
+ items,
38171
+ },
38172
+ ];
38173
+ }
38174
+ getExpansionDetailCardGridCards(node) {
38175
+ return Array.isArray(node?.cards) ? node.cards : [];
38176
+ }
38177
+ getExpansionDetailCardGridStyles(node) {
38178
+ const columns = node?.columns === 'auto'
38179
+ ? 'repeat(auto-fit, minmax(var(--pfx-expansion-card-grid-min-width, 220px), 1fr))'
38180
+ : `repeat(${this.normalizeExpansionDetailCardGridColumns(node?.columns)}, minmax(0, 1fr))`;
38181
+ const minCardWidth = this.normalizeExpansionDetailCardGridMinWidth(node?.minCardWidth);
38182
+ return {
38183
+ '--pfx-expansion-card-grid-columns': columns,
38184
+ '--pfx-expansion-card-grid-min-width': `${minCardWidth}px`,
38185
+ };
38186
+ }
38187
+ getExpansionDetailCardGridCardNodes(card) {
38188
+ const content = this.normalizeExpansionDetailCardGridCardContent(card?.content);
38189
+ return [
38190
+ {
38191
+ type: 'card',
38192
+ id: card?.id ? String(card.id) : undefined,
38193
+ title: card?.title ? String(card.title) : undefined,
38194
+ subtitle: card?.subtitle ? String(card.subtitle) : undefined,
38195
+ content,
38196
+ className: 'pfx-expansion-node-card-grid__card',
38197
+ },
38198
+ ];
38199
+ }
38200
+ getExpansionDetailTimelineEntries(row, node) {
37805
38201
  const field = String(node?.field || '').trim();
37806
38202
  const staticItems = Array.isArray(node?.items) ? node.items : null;
37807
38203
  if (field) {
37808
38204
  const resolved = this.getNestedPropertyValue(row, field);
37809
38205
  if (Array.isArray(resolved)) {
37810
- return resolved
37811
- .map((entry) => this.formatExpansionDetailListEntry(entry))
37812
- .filter((entry) => entry.length > 0);
38206
+ return resolved;
37813
38207
  }
37814
38208
  }
37815
38209
  if (staticItems) {
37816
- return staticItems
37817
- .map((entry) => this.formatExpansionDetailListEntry(entry))
37818
- .filter((entry) => entry.length > 0);
38210
+ return staticItems;
37819
38211
  }
37820
38212
  return [];
37821
38213
  }
38214
+ getExpansionDetailTimelineEmptyText(node) {
38215
+ return (String(node?.emptyText
38216
+ || this.resolveTableMessage('table.expansion.timeline.empty', 'Sem eventos recentes.')).trim()
38217
+ || this.resolveTableMessage('table.expansion.timeline.empty', 'Sem eventos recentes.'));
38218
+ }
38219
+ getExpansionDetailTimelineNodes(row, node) {
38220
+ const timelineNode = this.buildExpansionDetailTimelineRichNode(row, node);
38221
+ return timelineNode ? [timelineNode] : [];
38222
+ }
38223
+ getExpansionDetailRichListItemContext(row, rowIndex, node, entry, itemIndex) {
38224
+ const baseContext = this.getExpansionDetailRichContentContext(row, rowIndex);
38225
+ const canonicalAlias = 'detailItem';
38226
+ const canonicalIndexAlias = 'detailIndex';
38227
+ const itemAlias = String(node?.itemContext?.itemAlias || canonicalAlias).trim() || canonicalAlias;
38228
+ const indexAlias = String(node?.itemContext?.indexAlias || canonicalIndexAlias).trim() || canonicalIndexAlias;
38229
+ const extendedContext = {
38230
+ ...baseContext,
38231
+ [canonicalAlias]: entry,
38232
+ [canonicalIndexAlias]: itemIndex,
38233
+ };
38234
+ if (itemAlias !== canonicalAlias) {
38235
+ extendedContext[itemAlias] = entry;
38236
+ }
38237
+ if (indexAlias !== canonicalIndexAlias) {
38238
+ extendedContext[indexAlias] = itemIndex;
38239
+ }
38240
+ return extendedContext;
38241
+ }
38242
+ getExpansionDetailRichListItemClassName(node) {
38243
+ const layout = String(node?.itemSchema?.layout || 'stack').trim().toLowerCase();
38244
+ if (layout === 'row') {
38245
+ return 'pfx-expansion-node-rich-list__item pfx-expansion-node-rich-list__item--row';
38246
+ }
38247
+ if (layout === 'card-list') {
38248
+ return 'pfx-expansion-node-rich-list__item pfx-expansion-node-rich-list__item--card-list';
38249
+ }
38250
+ return 'pfx-expansion-node-rich-list__item pfx-expansion-node-rich-list__item--stack';
38251
+ }
38252
+ getExpansionDetailRichListItemKey(node, entry, itemIndex) {
38253
+ const keyField = String(node?.itemKeyField || '').trim();
38254
+ if (keyField && entry && typeof entry === 'object' && !Array.isArray(entry)) {
38255
+ const resolved = this.getNestedPropertyValue(entry, keyField);
38256
+ const normalized = String(resolved ?? '').trim();
38257
+ if (normalized) {
38258
+ return normalized;
38259
+ }
38260
+ }
38261
+ const label = this.formatExpansionDetailListEntry(entry);
38262
+ return label ? `${itemIndex}:${label}` : `item-${itemIndex}`;
38263
+ }
38264
+ getExpansionDetailRichListItemActions(node, row, rowIndex, entry, itemIndex) {
38265
+ const actions = Array.isArray(node?.itemActions) ? node.itemActions : [];
38266
+ const context = this.getExpansionDetailRichListItemContext(row, rowIndex, node, entry, itemIndex);
38267
+ return actions.filter((action) => this.isExpansionDetailRichListItemActionVisible(action, context));
38268
+ }
38269
+ normalizeExpansionDetailCardGridColumns(columns) {
38270
+ const normalized = Number(columns);
38271
+ if (normalized === 4 || normalized === 3 || normalized === 2) {
38272
+ return normalized;
38273
+ }
38274
+ return 1;
38275
+ }
38276
+ normalizeExpansionDetailCardGridMinWidth(minCardWidth) {
38277
+ const normalized = Number(minCardWidth);
38278
+ return Number.isFinite(normalized) && normalized >= 160 ? normalized : 220;
38279
+ }
38280
+ getExpansionDetailTimelineFieldName(node, fieldName) {
38281
+ const explicit = String(node?.itemSchema?.[fieldName] || '').trim();
38282
+ if (explicit) {
38283
+ return explicit;
38284
+ }
38285
+ const defaults = {
38286
+ titleField: 'label',
38287
+ subtitleField: 'secondary',
38288
+ metaField: 'status',
38289
+ iconField: 'icon',
38290
+ badgeField: 'badge',
38291
+ };
38292
+ return defaults[fieldName];
38293
+ }
38294
+ buildExpansionDetailTimelineRichNode(row, node) {
38295
+ const items = this.getExpansionDetailTimelineEntries(row, node)
38296
+ .map((entry, itemIndex) => this.mapExpansionDetailTimelineItem(entry, node, itemIndex))
38297
+ .filter((item) => item != null);
38298
+ return {
38299
+ type: 'timeline',
38300
+ id: node?.id ? String(node.id) : undefined,
38301
+ className: 'pfx-expansion-node pfx-expansion-node-timeline',
38302
+ title: this.getExpansionDetailNodeTitle(node, this.resolveTableMessage('table.expansion.timeline.title', 'Linha do tempo')) || undefined,
38303
+ emptyText: this.getExpansionDetailTimelineEmptyText(node),
38304
+ items,
38305
+ };
38306
+ }
38307
+ mapExpansionDetailTimelineItem(entry, node, itemIndex) {
38308
+ if (entry === null || entry === undefined) {
38309
+ return null;
38310
+ }
38311
+ if (typeof entry === 'string'
38312
+ || typeof entry === 'number'
38313
+ || typeof entry === 'boolean'
38314
+ || typeof entry === 'bigint') {
38315
+ const title = String(entry).trim();
38316
+ return title
38317
+ ? {
38318
+ id: `timeline-${itemIndex}`,
38319
+ title,
38320
+ }
38321
+ : null;
38322
+ }
38323
+ if (Array.isArray(entry)) {
38324
+ const title = entry
38325
+ .map((nested) => this.formatExpansionDetailListEntry(nested))
38326
+ .filter((nested) => nested.length > 0)
38327
+ .join(' - ');
38328
+ return title
38329
+ ? {
38330
+ id: `timeline-${itemIndex}`,
38331
+ title,
38332
+ }
38333
+ : null;
38334
+ }
38335
+ if (typeof entry !== 'object') {
38336
+ return null;
38337
+ }
38338
+ const record = entry;
38339
+ const title = this.getExpansionDetailTimelineObjectValue(record, this.getExpansionDetailTimelineFieldName(node, 'titleField'), ['label', 'title', 'name', 'value', 'event']);
38340
+ if (!title) {
38341
+ return null;
38342
+ }
38343
+ return {
38344
+ id: this.getExpansionDetailTimelineObjectValue(record, 'id', [])
38345
+ || `timeline-${itemIndex}`,
38346
+ title,
38347
+ subtitle: this.getExpansionDetailTimelineObjectValue(record, this.getExpansionDetailTimelineFieldName(node, 'subtitleField'), ['secondary', 'subtitle', 'description', 'detail']) || undefined,
38348
+ meta: this.getExpansionDetailTimelineObjectValue(record, this.getExpansionDetailTimelineFieldName(node, 'metaField'), ['status', 'time', 'timeAgo', 'date', 'when']) || undefined,
38349
+ icon: this.getExpansionDetailTimelineObjectValue(record, this.getExpansionDetailTimelineFieldName(node, 'iconField'), ['icon', 'symbol', 'kind']) || undefined,
38350
+ badge: this.getExpansionDetailTimelineObjectValue(record, this.getExpansionDetailTimelineFieldName(node, 'badgeField'), ['badge', 'category', 'tag', 'severity']) || undefined,
38351
+ };
38352
+ }
38353
+ getExpansionDetailTimelineObjectValue(entry, preferredKey, fallbackKeys) {
38354
+ const keys = [preferredKey, ...fallbackKeys].filter((key, index, all) => key && all.indexOf(key) === index);
38355
+ for (const key of keys) {
38356
+ const raw = entry[key];
38357
+ if (typeof raw === 'string'
38358
+ || typeof raw === 'number'
38359
+ || typeof raw === 'boolean'
38360
+ || typeof raw === 'bigint') {
38361
+ const normalized = String(raw).trim();
38362
+ if (normalized) {
38363
+ return normalized;
38364
+ }
38365
+ }
38366
+ }
38367
+ return null;
38368
+ }
38369
+ normalizeExpansionDetailCardGridCardContent(rawContent) {
38370
+ const content = Array.isArray(rawContent) ? rawContent : [];
38371
+ return content.filter((node) => this.isExpansionDetailCardGridContentNode(node));
38372
+ }
38373
+ normalizeExpansionDetailMediaBlockAvatar(candidate) {
38374
+ return candidate && typeof candidate === 'object' && candidate.type === 'avatar'
38375
+ ? candidate
38376
+ : null;
38377
+ }
38378
+ normalizeExpansionDetailMediaBlockText(candidate) {
38379
+ return candidate && typeof candidate === 'object' && candidate.type === 'text'
38380
+ ? candidate
38381
+ : null;
38382
+ }
38383
+ normalizeExpansionDetailMediaBlockMeta(candidate) {
38384
+ if (!candidate || typeof candidate !== 'object' || candidate.type !== 'compose') {
38385
+ return null;
38386
+ }
38387
+ const normalizedItems = this.normalizeExpansionDetailComposeItems(candidate.items);
38388
+ if (!normalizedItems.length) {
38389
+ return null;
38390
+ }
38391
+ return {
38392
+ ...candidate,
38393
+ items: normalizedItems,
38394
+ };
38395
+ }
38396
+ normalizeExpansionDetailMediaBlockTrailing(candidate) {
38397
+ return this.normalizeExpansionDetailCardGridCardContent(candidate);
38398
+ }
38399
+ normalizeExpansionDetailComposeItems(rawItems) {
38400
+ const items = Array.isArray(rawItems) ? rawItems : [];
38401
+ return items.filter((node) => this.isExpansionDetailComposeItemNode(node));
38402
+ }
38403
+ isExpansionDetailCardGridContentNode(node) {
38404
+ if (!node || typeof node !== 'object') {
38405
+ return false;
38406
+ }
38407
+ const type = String(node.type || '').trim();
38408
+ return (type === 'text'
38409
+ || type === 'icon'
38410
+ || type === 'image'
38411
+ || type === 'badge'
38412
+ || type === 'avatar'
38413
+ || type === 'metric'
38414
+ || type === 'progress'
38415
+ || type === 'compose');
38416
+ }
38417
+ isExpansionDetailComposeItemNode(node) {
38418
+ if (!node || typeof node !== 'object') {
38419
+ return false;
38420
+ }
38421
+ const type = String(node.type || '').trim();
38422
+ return (type === 'text'
38423
+ || type === 'icon'
38424
+ || type === 'image'
38425
+ || type === 'badge'
38426
+ || type === 'avatar'
38427
+ || type === 'metric'
38428
+ || type === 'progress');
38429
+ }
38430
+ getExpansionDetailRichListItemActionNodes(action) {
38431
+ const labelNode = {
38432
+ type: 'text',
38433
+ text: String(action?.label || action?.actionId || 'Ação').trim() || 'Ação',
38434
+ className: 'pfx-expansion-node-rich-list__item-action-text',
38435
+ };
38436
+ const icon = String(action?.icon || '').trim();
38437
+ if (!icon) {
38438
+ return [labelNode];
38439
+ }
38440
+ return [
38441
+ {
38442
+ type: 'compose',
38443
+ direction: 'row',
38444
+ gap: 'sm',
38445
+ items: [
38446
+ {
38447
+ type: 'icon',
38448
+ icon,
38449
+ ariaLabel: labelNode.text,
38450
+ className: 'pfx-expansion-node-rich-list__item-action-icon',
38451
+ },
38452
+ labelNode,
38453
+ ],
38454
+ },
38455
+ ];
38456
+ }
38457
+ isExpansionDetailRichListItemActionDisabled(action, row, rowIndex, node, entry, itemIndex) {
38458
+ const disabledWhen = action?.disabledWhen;
38459
+ if (!this.hasConditionalExpression(disabledWhen)) {
38460
+ return false;
38461
+ }
38462
+ return this.evaluateExpansionDetailRichListItemCondition(disabledWhen, this.getExpansionDetailRichListItemContext(row, rowIndex, node, entry, itemIndex), true);
38463
+ }
38464
+ onExpansionDetailRichListItemAction(action, row, rowIndex, node, entry, itemIndex, event) {
38465
+ const actionId = String(action?.actionId || '').trim();
38466
+ event.stopPropagation();
38467
+ if (!actionId) {
38468
+ return;
38469
+ }
38470
+ const context = this.getExpansionDetailRichListItemContext(row, rowIndex, node, entry, itemIndex);
38471
+ this.onRowAction(actionId, row, event, this.resolveRowActionRuntimeOptions({
38472
+ actionConfig: action,
38473
+ payload: this.evaluateExpansionDetailRichListItemPayload(action?.payloadExpr, context, entry),
38474
+ }, action));
38475
+ }
38476
+ getExpansionDetailListItemRichContentNodes(entry) {
38477
+ if (entry === null || entry === undefined) {
38478
+ return null;
38479
+ }
38480
+ if (typeof entry === 'string'
38481
+ || typeof entry === 'number'
38482
+ || typeof entry === 'boolean'
38483
+ || typeof entry === 'bigint') {
38484
+ const text = String(entry).trim();
38485
+ return text ? [{ type: 'text', text }] : null;
38486
+ }
38487
+ if (typeof entry !== 'object' || Array.isArray(entry)) {
38488
+ return null;
38489
+ }
38490
+ const record = entry;
38491
+ const label = this.getExpansionDetailObjectListLabel(record);
38492
+ if (!label) {
38493
+ return null;
38494
+ }
38495
+ const textNode = { type: 'text', text: label };
38496
+ const badgeLabel = this.getExpansionDetailObjectListBadgeLabel(record);
38497
+ if (!badgeLabel) {
38498
+ return [textNode];
38499
+ }
38500
+ const badgeNode = {
38501
+ type: 'badge',
38502
+ label: badgeLabel,
38503
+ className: 'pfx-expansion-node-list__badge',
38504
+ };
38505
+ return [
38506
+ {
38507
+ type: 'compose',
38508
+ direction: 'row',
38509
+ gap: 'sm',
38510
+ items: [textNode, badgeNode],
38511
+ },
38512
+ ];
38513
+ }
38514
+ getExpansionDetailActionId(node) {
38515
+ return String(node?.actionId || node?.id || node?.action || '').trim();
38516
+ }
38517
+ getExpansionDetailActionLabel(node) {
38518
+ return this.getExpansionDetailNodeTitle(node, this.getExpansionDetailActionId(node) || 'Action');
38519
+ }
38520
+ getExpansionDetailActionStatusText(node) {
38521
+ const raw = node?.statusText ?? node?.description ?? null;
38522
+ const normalized = String(raw ?? '').trim();
38523
+ return normalized || null;
38524
+ }
38525
+ getExpansionDetailActionBarTitle(node) {
38526
+ return (this.getExpansionDetailNodeTitle(node, this.resolveTableMessage('table.expansion.actionBar.title', 'Ações rápidas'))
38527
+ || this.resolveTableMessage('table.expansion.actionBar.title', 'Ações rápidas'));
38528
+ }
38529
+ getExpansionDetailActionBarActions(node, row, index) {
38530
+ const actions = Array.isArray(node?.actions) ? node.actions : [];
38531
+ const context = this.getExpansionDetailRichContentContext(row, index);
38532
+ return actions.filter((action) => this.isExpansionDetailActionBarActionVisible(action, context));
38533
+ }
38534
+ getExpansionDetailActionBarActionNodes(action) {
38535
+ return this.getExpansionDetailRichListItemActionNodes(action);
38536
+ }
38537
+ isExpansionDetailActionBarActionDisabled(action, row, index) {
38538
+ const disabledWhen = action?.disabledWhen;
38539
+ if (!this.hasConditionalExpression(disabledWhen)) {
38540
+ return false;
38541
+ }
38542
+ return this.evaluateExpansionDetailRichListItemCondition(disabledWhen, this.getExpansionDetailRichContentContext(row, index), true);
38543
+ }
38544
+ getExpansionDetailActionBarEmptyText(node) {
38545
+ return (String(node?.emptyText
38546
+ || this.resolveTableMessage('table.expansion.actionBar.empty', 'Nenhuma ação disponível.')).trim()
38547
+ || this.resolveTableMessage('table.expansion.actionBar.empty', 'Nenhuma ação disponível.'));
38548
+ }
38549
+ onExpansionDetailActionBarAction(action, row, index, event) {
38550
+ const actionId = String(action?.actionId || '').trim();
38551
+ event.stopPropagation();
38552
+ if (!actionId) {
38553
+ return;
38554
+ }
38555
+ const context = this.getExpansionDetailRichContentContext(row, index);
38556
+ this.onRowAction(actionId, row, event, this.resolveRowActionRuntimeOptions({
38557
+ actionConfig: action,
38558
+ payload: this.evaluateExpansionDetailRichListItemPayload(action?.payloadExpr, context, row),
38559
+ }, action));
38560
+ }
38561
+ getExpansionDetailEmbedHeaderNodes(node) {
38562
+ const items = [];
38563
+ const title = this.getExpansionDetailNodeTitle(node, this.getExpansionDetailEmbedLabel(node));
38564
+ const subtitle = String(node?.subtitle || '').trim();
38565
+ const description = String(node?.description || '').trim();
38566
+ const caption = String(node?.caption || '').trim();
38567
+ if (title) {
38568
+ items.push({ type: 'text', text: title, className: 'pfx-expansion-node-embed__title-text' });
38569
+ }
38570
+ if (subtitle) {
38571
+ items.push({ type: 'text', text: subtitle, className: 'pfx-expansion-node-embed__subtitle-text' });
38572
+ }
38573
+ if (description) {
38574
+ items.push({ type: 'text', text: description, className: 'pfx-expansion-node-embed__description-text' });
38575
+ }
38576
+ if (caption) {
38577
+ items.push({ type: 'text', text: caption, className: 'pfx-expansion-node-embed__caption-text' });
38578
+ }
38579
+ if (!items.length) {
38580
+ return [];
38581
+ }
38582
+ return [
38583
+ {
38584
+ type: 'compose',
38585
+ direction: 'column',
38586
+ gap: 'xs',
38587
+ items,
38588
+ },
38589
+ ];
38590
+ }
38591
+ getExpansionDetailEmbedMetaNodes(node) {
38592
+ const items = [
38593
+ {
38594
+ type: 'badge',
38595
+ label: this.getExpansionDetailEmbedLabel(node),
38596
+ className: 'pfx-expansion-node-embed__kind-badge',
38597
+ },
38598
+ ];
38599
+ const reference = this.getExpansionDetailEmbedReference(node);
38600
+ if (reference) {
38601
+ items.push({
38602
+ type: 'text',
38603
+ text: `${this.resolveTableMessage('table.expansion.embed.reference', 'Referência')}: ${reference}`,
38604
+ className: 'pfx-expansion-node-embed__meta-text',
38605
+ });
38606
+ }
38607
+ const provider = this.getExpansionDetailDiagramProvider(node);
38608
+ if (provider) {
38609
+ items.push({
38610
+ type: 'text',
38611
+ text: `${this.resolveTableMessage('table.expansion.embed.provider', 'Provedor')}: ${provider}`,
38612
+ className: 'pfx-expansion-node-embed__meta-text',
38613
+ });
38614
+ }
38615
+ const preset = this.getExpansionDetailEmbedPresetLabel(node);
38616
+ if (preset) {
38617
+ items.push({
38618
+ type: 'text',
38619
+ text: `${this.resolveTableMessage('table.expansion.embed.preset', 'Preset')}: ${preset}`,
38620
+ className: 'pfx-expansion-node-embed__meta-text',
38621
+ });
38622
+ }
38623
+ const inputSummary = this.getExpansionDetailEmbedInputsSummary(node);
38624
+ if (inputSummary) {
38625
+ items.push({
38626
+ type: 'text',
38627
+ text: `${this.resolveTableMessage('table.expansion.embed.inputs', 'Inputs')}: ${inputSummary}`,
38628
+ className: 'pfx-expansion-node-embed__meta-text',
38629
+ });
38630
+ }
38631
+ if (items.length === 1) {
38632
+ return [items[0]];
38633
+ }
38634
+ return [
38635
+ {
38636
+ type: 'compose',
38637
+ direction: 'row',
38638
+ gap: 'sm',
38639
+ wrap: true,
38640
+ items,
38641
+ },
38642
+ ];
38643
+ }
38644
+ getExpansionDetailEmbedActionNodes(action) {
38645
+ return this.getExpansionDetailRichListItemActionNodes(action);
38646
+ }
38647
+ getExpansionDetailEmbedAction(node, row, index) {
38648
+ const action = node?.action;
38649
+ if (!action || typeof action !== 'object') {
38650
+ return null;
38651
+ }
38652
+ const context = this.getExpansionDetailRichContentContext(row, index);
38653
+ return this.isExpansionDetailEmbedActionVisible(action, context) ? action : null;
38654
+ }
38655
+ isExpansionDetailEmbedActionDisabled(action, row, index) {
38656
+ const disabledWhen = action?.disabledWhen;
38657
+ if (!this.hasConditionalExpression(disabledWhen)) {
38658
+ return false;
38659
+ }
38660
+ return this.evaluateExpansionDetailRichListItemCondition(disabledWhen, this.getExpansionDetailRichContentContext(row, index), true);
38661
+ }
38662
+ getExpansionDetailEmbedEmptyText(node) {
38663
+ return (String(node?.emptyText
38664
+ || this.resolveTableMessage('table.expansion.embed.empty', 'Nenhum conteúdo de embed disponível.')).trim()
38665
+ || this.resolveTableMessage('table.expansion.embed.empty', 'Nenhum conteúdo de embed disponível.'));
38666
+ }
38667
+ getExpansionDetailDiagramSource(row, node) {
38668
+ const staticSource = String(node?.source || '').trim();
38669
+ if (staticSource) {
38670
+ return staticSource;
38671
+ }
38672
+ const field = String(node?.sourceField || '').trim();
38673
+ if (!field) {
38674
+ return null;
38675
+ }
38676
+ const resolved = this.getNestedPropertyValue(row, field);
38677
+ const normalized = String(resolved ?? '').trim();
38678
+ return normalized || null;
38679
+ }
38680
+ getExpansionDetailDiagramSourceLabel() {
38681
+ return this.resolveTableMessage('table.expansion.embed.diagramSource', 'Fonte do diagrama');
38682
+ }
38683
+ onExpansionDetailEmbedAction(action, row, index, event) {
38684
+ const actionId = String(action?.actionId || '').trim();
38685
+ event.stopPropagation();
38686
+ if (!actionId) {
38687
+ return;
38688
+ }
38689
+ const context = this.getExpansionDetailRichContentContext(row, index);
38690
+ this.onRowAction(actionId, row, event, this.resolveRowActionRuntimeOptions({
38691
+ actionConfig: action,
38692
+ payload: this.evaluateExpansionDetailRichListItemPayload(action?.payloadExpr, context, row),
38693
+ }, action));
38694
+ }
38695
+ isExpansionDetailEmbedActionVisible(action, context) {
38696
+ const visibleWhen = action?.visibleWhen;
38697
+ if (!this.hasConditionalExpression(visibleWhen)) {
38698
+ return true;
38699
+ }
38700
+ return this.evaluateExpansionDetailRichListItemCondition(visibleWhen, context, this.resolveRowVisibleWhenFailurePolicy() === 'fail-open');
38701
+ }
38702
+ getExpansionDetailEmbedLabel(node) {
38703
+ const type = this.getExpansionDetailNodeType(node);
38704
+ switch (type) {
38705
+ case 'formRef':
38706
+ return this.resolveTableMessage('table.expansion.embed.formRef.label', 'Formulário governado');
38707
+ case 'tableRef':
38708
+ return this.resolveTableMessage('table.expansion.embed.tableRef.label', 'Tabela governada');
38709
+ case 'chartRef':
38710
+ return this.resolveTableMessage('table.expansion.embed.chartRef.label', 'Gráfico governado');
38711
+ case 'templateRef':
38712
+ return this.resolveTableMessage('table.expansion.embed.templateRef.label', 'Template governado');
38713
+ case 'diagramEmbed':
38714
+ return this.resolveTableMessage('table.expansion.embed.diagramEmbed.label', 'Diagrama governado');
38715
+ default:
38716
+ return this.resolveTableMessage('table.expansion.embed.empty', 'Nenhum conteúdo de embed disponível.');
38717
+ }
38718
+ }
38719
+ getExpansionDetailEmbedReference(node) {
38720
+ const candidates = [
38721
+ node?.schemaId,
38722
+ node?.templateId,
38723
+ node?.tableId,
38724
+ node?.chartId,
38725
+ node?.formId,
38726
+ node?.id,
38727
+ ];
38728
+ for (const candidate of candidates) {
38729
+ const normalized = String(candidate ?? '').trim();
38730
+ if (normalized) {
38731
+ return normalized;
38732
+ }
38733
+ }
38734
+ return null;
38735
+ }
38736
+ getExpansionDetailDiagramProvider(node) {
38737
+ if (this.getExpansionDetailNodeType(node) !== 'diagramEmbed') {
38738
+ return null;
38739
+ }
38740
+ const normalized = String(node?.provider || '').trim();
38741
+ return normalized || null;
38742
+ }
38743
+ getExpansionDetailEmbedPresetLabel(node) {
38744
+ const presetRef = node?.presetRef;
38745
+ if (!presetRef || typeof presetRef !== 'object') {
38746
+ return null;
38747
+ }
38748
+ const namespace = String(presetRef.namespace || '').trim();
38749
+ const presetId = String(presetRef.presetId || '').trim();
38750
+ const version = String(presetRef.version || '').trim();
38751
+ if (!namespace || !presetId) {
38752
+ return null;
38753
+ }
38754
+ return version ? `${namespace}/${presetId}@${version}` : `${namespace}/${presetId}`;
38755
+ }
38756
+ getExpansionDetailEmbedInputsSummary(node) {
38757
+ const inputs = node?.inputs;
38758
+ if (!inputs || typeof inputs !== 'object' || Array.isArray(inputs)) {
38759
+ return null;
38760
+ }
38761
+ const keys = Object.keys(inputs).filter((key) => String(key || '').trim().length > 0);
38762
+ if (!keys.length) {
38763
+ return null;
38764
+ }
38765
+ if (keys.length <= 3) {
38766
+ return keys.join(', ');
38767
+ }
38768
+ return `${keys.slice(0, 3).join(', ')} +${keys.length - 3}`;
38769
+ }
38770
+ getExpansionDetailTextRichContentNodes(text, className) {
38771
+ const normalized = String(text ?? '').trim();
38772
+ if (!normalized) {
38773
+ return [];
38774
+ }
38775
+ return [
38776
+ {
38777
+ type: 'text',
38778
+ text: normalized,
38779
+ ...(className ? { className } : {}),
38780
+ },
38781
+ ];
38782
+ }
38783
+ getTableChromeTextRichContentNodes(text, className) {
38784
+ const normalized = String(text ?? '').trim();
38785
+ if (!normalized) {
38786
+ return [];
38787
+ }
38788
+ return [
38789
+ {
38790
+ type: 'text',
38791
+ text: normalized,
38792
+ ...(className ? { className } : {}),
38793
+ },
38794
+ ];
38795
+ }
38796
+ getRowActionRichContentNodes(action) {
38797
+ const label = String(action?.label || this.getActionId(action) || '').trim();
38798
+ const textNode = {
38799
+ type: 'text',
38800
+ text: label || this.getActionId(action),
38801
+ className: 'praxis-row-action__label',
38802
+ };
38803
+ const icon = String(action?.icon || '').trim();
38804
+ if (!icon) {
38805
+ return [textNode];
38806
+ }
38807
+ const iconNode = {
38808
+ type: 'icon',
38809
+ icon,
38810
+ ariaLabel: label || this.getActionId(action),
38811
+ className: 'praxis-row-action__icon',
38812
+ };
38813
+ return [
38814
+ {
38815
+ type: 'compose',
38816
+ direction: 'row',
38817
+ gap: 'sm',
38818
+ items: [iconNode, textNode],
38819
+ },
38820
+ ];
38821
+ }
38822
+ getRowActionIconRichContentNodes(action) {
38823
+ const icon = String(action?.icon || '').trim();
38824
+ if (!icon) {
38825
+ return [];
38826
+ }
38827
+ return [
38828
+ {
38829
+ type: 'icon',
38830
+ icon,
38831
+ ariaLabel: String(action?.label || this.getActionId(action) || '').trim() || this.getActionId(action),
38832
+ className: 'praxis-row-action__icon',
38833
+ },
38834
+ ];
38835
+ }
38836
+ getExpansionDetailActionRichContentNodes(node) {
38837
+ const labelNode = {
38838
+ type: 'text',
38839
+ text: this.getExpansionDetailActionLabel(node),
38840
+ };
38841
+ const icon = String(node?.icon || '').trim();
38842
+ if (!icon) {
38843
+ return [labelNode];
38844
+ }
38845
+ const iconNode = {
38846
+ type: 'icon',
38847
+ icon,
38848
+ ariaLabel: this.getExpansionDetailActionLabel(node),
38849
+ };
38850
+ return [
38851
+ {
38852
+ type: 'compose',
38853
+ direction: 'row',
38854
+ gap: 'sm',
38855
+ items: [iconNode, labelNode],
38856
+ },
38857
+ ];
38858
+ }
38859
+ isExpansionDetailActionBarActionVisible(action, context) {
38860
+ const visibleWhen = action?.visibleWhen;
38861
+ if (!this.hasConditionalExpression(visibleWhen)) {
38862
+ return true;
38863
+ }
38864
+ return this.evaluateExpansionDetailRichListItemCondition(visibleWhen, context, this.resolveRowVisibleWhenFailurePolicy() === 'fail-open');
38865
+ }
38866
+ getExpansionDetailTabButtonRichContentNodes(tab) {
38867
+ const titleNode = {
38868
+ type: 'text',
38869
+ text: this.getExpansionDetailNodeTitle(tab, 'Tab'),
38870
+ className: 'pfx-expansion-detail-tab-btn__title',
38871
+ };
38872
+ const items = [];
38873
+ const icon = String(tab?.icon ?? tab?.tabIcon ?? '').trim();
38874
+ if (icon) {
38875
+ items.push({
38876
+ type: 'icon',
38877
+ icon,
38878
+ ariaLabel: this.getExpansionDetailNodeTitle(tab, 'Tab'),
38879
+ className: 'pfx-expansion-detail-tab-btn__icon',
38880
+ });
38881
+ }
38882
+ items.push(titleNode);
38883
+ const badgeLabel = this.getExpansionDetailTabBadgeLabel(tab);
38884
+ if (badgeLabel) {
38885
+ items.push({
38886
+ type: 'badge',
38887
+ label: badgeLabel,
38888
+ className: 'pfx-expansion-detail-tab-btn__badge',
38889
+ });
38890
+ }
38891
+ if (items.length === 1) {
38892
+ return [titleNode];
38893
+ }
38894
+ return [
38895
+ {
38896
+ type: 'compose',
38897
+ direction: 'row',
38898
+ gap: 'sm',
38899
+ items,
38900
+ },
38901
+ ];
38902
+ }
38903
+ getExpansionDetailTabBadgeLabel(tab) {
38904
+ const rawBadge = tab?.badge
38905
+ ?? tab?.badgeLabel
38906
+ ?? tab?.badgeText
38907
+ ?? tab?.count
38908
+ ?? null;
38909
+ if (rawBadge === null || rawBadge === undefined) {
38910
+ return null;
38911
+ }
38912
+ if (typeof rawBadge === 'object' && !Array.isArray(rawBadge)) {
38913
+ const record = rawBadge;
38914
+ const nested = record['label']
38915
+ ?? record['text']
38916
+ ?? record['value']
38917
+ ?? null;
38918
+ const normalizedNested = String(nested ?? '').trim();
38919
+ return normalizedNested || null;
38920
+ }
38921
+ const normalized = String(rawBadge).trim();
38922
+ return normalized || null;
38923
+ }
38924
+ isExpansionDetailActionDisabled(node) {
38925
+ return node?.disabled === true;
38926
+ }
38927
+ onExpansionDetailAction(node, row, event) {
38928
+ const actionId = this.getExpansionDetailActionId(node);
38929
+ if (!actionId) {
38930
+ event.stopPropagation();
38931
+ return;
38932
+ }
38933
+ const runtimeOptions = this.resolveRowActionRuntimeOptions(this.buildRendererActionRuntimeOptions(node, row), node);
38934
+ this.onRowAction(actionId, row, event, runtimeOptions);
38935
+ }
37822
38936
  formatExpansionDetailListEntry(entry) {
37823
38937
  if (entry === null || entry === undefined) {
37824
38938
  return '';
@@ -37871,12 +38985,207 @@ class PraxisTable {
37871
38985
  }
37872
38986
  return null;
37873
38987
  }
38988
+ getExpansionDetailObjectListBadgeLabel(entry) {
38989
+ const preferredKeys = ['status', 'state', 'tag', 'badge'];
38990
+ for (const key of preferredKeys) {
38991
+ const raw = entry[key];
38992
+ if (typeof raw === 'string'
38993
+ || typeof raw === 'number'
38994
+ || typeof raw === 'boolean'
38995
+ || typeof raw === 'bigint') {
38996
+ const normalized = String(raw).trim();
38997
+ if (normalized)
38998
+ return normalized;
38999
+ }
39000
+ }
39001
+ return null;
39002
+ }
39003
+ evaluateExpansionDetailRichListItemPayload(raw, context, fallback) {
39004
+ if (raw === null || raw === undefined) {
39005
+ return fallback;
39006
+ }
39007
+ const normalized = String(raw).trim();
39008
+ if (!normalized) {
39009
+ return fallback;
39010
+ }
39011
+ const result = this.computedExpressionEvaluator.evaluate(normalized, context);
39012
+ if (result.error) {
39013
+ return fallback;
39014
+ }
39015
+ if (result.value === undefined && !normalized.startsWith('=')) {
39016
+ const nested = this.getNestedPropertyValue(context, normalized);
39017
+ return nested === undefined ? fallback : nested;
39018
+ }
39019
+ return result.value;
39020
+ }
39021
+ isExpansionDetailRichListItemActionVisible(action, context) {
39022
+ const visibleWhen = action?.visibleWhen;
39023
+ if (!this.hasConditionalExpression(visibleWhen)) {
39024
+ return true;
39025
+ }
39026
+ return this.evaluateExpansionDetailRichListItemCondition(visibleWhen, context, this.resolveRowVisibleWhenFailurePolicy() === 'fail-open');
39027
+ }
39028
+ evaluateExpansionDetailRichListItemCondition(expression, context, fallbackResult) {
39029
+ if (!this.isJsonLogicExpression(expression)) {
39030
+ return fallbackResult;
39031
+ }
39032
+ try {
39033
+ return this.jsonLogic.matches({
39034
+ condition: expression,
39035
+ data: context,
39036
+ });
39037
+ }
39038
+ catch {
39039
+ return fallbackResult;
39040
+ }
39041
+ }
37874
39042
  getExpansionDetailRichText(node) {
37875
39043
  const html = String(node?.html ?? node?.text ?? '').trim();
37876
39044
  if (!html)
37877
39045
  return '';
37878
39046
  return this.sanitizeStrictHtml(html);
37879
39047
  }
39048
+ getExpansionDetailRichContentContext(row, index) {
39049
+ return {
39050
+ row,
39051
+ computed: {
39052
+ rowId: this.getRowId(row),
39053
+ rowIndex: index,
39054
+ },
39055
+ detail: this.buildExpansionContextRecord(row, index),
39056
+ meta: (this.config?.meta || {}),
39057
+ };
39058
+ }
39059
+ getExpansionDetailRichContentNodes(node, row, index) {
39060
+ const mapped = this.mapExpansionDetailNodeToRichContent(node, row, index);
39061
+ return mapped ? [mapped] : null;
39062
+ }
39063
+ getExpansionDetailTabRichContentNodes(tab, row, index) {
39064
+ const mappedChildren = this.getExpansionDetailNodeChildren(tab)
39065
+ .map((childNode) => this.mapExpansionDetailNodeToRichContent(childNode, row, index));
39066
+ if (mappedChildren.some((childNode) => childNode == null)) {
39067
+ return null;
39068
+ }
39069
+ return mappedChildren;
39070
+ }
39071
+ mapExpansionDetailNodeToRichContent(node, row, index) {
39072
+ const type = this.getExpansionDetailNodeType(node);
39073
+ if (type === 'mediaBlock') {
39074
+ return this.mapExpansionDetailMediaBlockNodeToRichContent(node);
39075
+ }
39076
+ if (type === 'timeline') {
39077
+ return this.mapExpansionDetailTimelineNodeToRichContent(node, row);
39078
+ }
39079
+ if (type === 'card') {
39080
+ return this.mapExpansionDetailCardNodeToRichContent(node, row, index);
39081
+ }
39082
+ if (type === 'layout' || type === 'stack') {
39083
+ return this.mapExpansionDetailStackNodeToRichContent(node, row, index);
39084
+ }
39085
+ if (type === 'value') {
39086
+ return this.mapExpansionDetailValueNodeToRichContent(node, row);
39087
+ }
39088
+ return null;
39089
+ }
39090
+ mapExpansionDetailMediaBlockNodeToRichContent(node) {
39091
+ const avatar = this.normalizeExpansionDetailMediaBlockAvatar(node?.avatar);
39092
+ const title = this.normalizeExpansionDetailMediaBlockText(node?.title);
39093
+ const subtitle = this.normalizeExpansionDetailMediaBlockText(node?.subtitle);
39094
+ const meta = this.normalizeExpansionDetailMediaBlockMeta(node?.meta);
39095
+ const trailing = this.normalizeExpansionDetailMediaBlockTrailing(node?.trailing);
39096
+ if (!avatar && !title && !subtitle && !meta && trailing.length === 0) {
39097
+ return null;
39098
+ }
39099
+ return {
39100
+ type: 'mediaBlock',
39101
+ id: node?.id ? String(node.id) : undefined,
39102
+ className: 'pfx-expansion-node pfx-expansion-node-media-block',
39103
+ avatar: avatar ?? undefined,
39104
+ title: title ?? undefined,
39105
+ subtitle: subtitle ?? undefined,
39106
+ meta: meta ?? undefined,
39107
+ trailing: trailing.length ? trailing : undefined,
39108
+ };
39109
+ }
39110
+ mapExpansionDetailCardNodeToRichContent(node, row, index) {
39111
+ const mappedChildren = this.getExpansionDetailNodeChildren(node)
39112
+ .map((childNode) => this.mapExpansionDetailNodeToRichContent(childNode, row, index));
39113
+ const content = mappedChildren
39114
+ .filter((childNode) => childNode != null && childNode.type !== 'card' && childNode.type !== 'preset');
39115
+ if (content.length !== mappedChildren.length) {
39116
+ return null;
39117
+ }
39118
+ return {
39119
+ type: 'card',
39120
+ id: node?.id ? String(node.id) : undefined,
39121
+ className: 'pfx-expansion-node pfx-expansion-node-card',
39122
+ title: this.getExpansionDetailNodeTitle(node, 'Card'),
39123
+ subtitle: node?.subtitle ? String(node.subtitle) : undefined,
39124
+ content,
39125
+ };
39126
+ }
39127
+ mapExpansionDetailStackNodeToRichContent(node, row, index) {
39128
+ const mappedChildren = this.getExpansionDetailNodeChildren(node)
39129
+ .map((childNode) => this.mapExpansionDetailNodeToRichContent(childNode, row, index));
39130
+ const items = [];
39131
+ for (const childNode of mappedChildren) {
39132
+ if (!childNode
39133
+ || childNode.type === 'card'
39134
+ || childNode.type === 'preset'
39135
+ || childNode.type === 'mediaBlock'
39136
+ || childNode.type === 'timeline') {
39137
+ return null;
39138
+ }
39139
+ if (childNode.type === 'compose') {
39140
+ items.push(...childNode.items);
39141
+ continue;
39142
+ }
39143
+ if (!this.isExpansionDetailComposeItemNode(childNode)) {
39144
+ return null;
39145
+ }
39146
+ items.push(childNode);
39147
+ }
39148
+ if (!items.length) {
39149
+ return null;
39150
+ }
39151
+ return {
39152
+ type: 'compose',
39153
+ id: node?.id ? String(node.id) : undefined,
39154
+ className: 'pfx-expansion-node pfx-expansion-node-stack',
39155
+ direction: 'column',
39156
+ gap: 'sm',
39157
+ items,
39158
+ };
39159
+ }
39160
+ mapExpansionDetailValueNodeToRichContent(node, row) {
39161
+ const labelNode = {
39162
+ type: 'text',
39163
+ className: 'pfx-expansion-node-value__label',
39164
+ text: this.getExpansionDetailNodeTitle(node, 'Valor'),
39165
+ };
39166
+ const valueNode = {
39167
+ type: 'text',
39168
+ className: 'pfx-expansion-node-value__content',
39169
+ text: this.getExpansionDetailValue(row, node),
39170
+ };
39171
+ return {
39172
+ type: 'compose',
39173
+ className: 'pfx-expansion-node pfx-expansion-node-value',
39174
+ direction: 'column',
39175
+ gap: 'xs',
39176
+ items: [labelNode, valueNode],
39177
+ };
39178
+ }
39179
+ mapExpansionDetailTimelineNodeToRichContent(node, row) {
39180
+ const timelineNode = this.buildExpansionDetailTimelineRichNode(row, node) || {
39181
+ type: 'timeline',
39182
+ items: [],
39183
+ };
39184
+ return {
39185
+ ...timelineNode,
39186
+ className: 'pfx-expansion-node pfx-expansion-node-timeline',
39187
+ };
39188
+ }
37880
39189
  getExpansionDetailTabId(row, index, tab, tabIndex) {
37881
39190
  const rowKey = this.resolveRowExpansionKey(row, index) || `index-${Math.max(0, index)}`;
37882
39191
  const rawTabId = String(tab?.id || `tab-${tabIndex + 1}`).trim();
@@ -38442,6 +39751,49 @@ class PraxisTable {
38442
39751
  };
38443
39752
  }
38444
39753
  const clone = { ...node, type };
39754
+ if (type === 'mediaBlock') {
39755
+ const sanitizedMediaBlock = this.sanitizeExpansionDetailMediaBlockNode(clone, fallbackNodePolicy, path);
39756
+ if (!sanitizedMediaBlock.ok) {
39757
+ return {
39758
+ ok: false,
39759
+ node: null,
39760
+ message: sanitizedMediaBlock.message,
39761
+ };
39762
+ }
39763
+ return {
39764
+ ok: true,
39765
+ node: sanitizedMediaBlock.node,
39766
+ message: null,
39767
+ };
39768
+ }
39769
+ if (type === 'cardGrid') {
39770
+ const sanitizedCards = this.sanitizeExpansionDetailCardGridCards(clone.cards, fallbackNodePolicy, `${path}.cards`);
39771
+ if (!sanitizedCards.ok) {
39772
+ return {
39773
+ ok: false,
39774
+ node: null,
39775
+ message: sanitizedCards.message,
39776
+ };
39777
+ }
39778
+ clone.cards = sanitizedCards.cards;
39779
+ }
39780
+ if (type === 'diagramEmbed') {
39781
+ const provider = String(clone.provider || '').trim();
39782
+ if (provider !== 'mermaid' && provider !== 'bpmn' && provider !== 'custom') {
39783
+ if (fallbackNodePolicy === 'renderPlaceholder') {
39784
+ return {
39785
+ ok: true,
39786
+ node: this.buildExpansionBlockedPlaceholderNode(`provider inválido para diagramEmbed`, path),
39787
+ message: null,
39788
+ };
39789
+ }
39790
+ return {
39791
+ ok: false,
39792
+ node: null,
39793
+ message: `provider inválido para diagramEmbed em ${path}.`,
39794
+ };
39795
+ }
39796
+ }
38445
39797
  const childKeys = ['content', 'items'];
38446
39798
  for (const childKey of childKeys) {
38447
39799
  const rawChildren = clone[childKey];
@@ -38459,6 +39811,91 @@ class PraxisTable {
38459
39811
  }
38460
39812
  return { ok: true, node: clone, message: null };
38461
39813
  }
39814
+ sanitizeExpansionDetailMediaBlockNode(candidate, fallbackNodePolicy, path) {
39815
+ const avatar = this.normalizeExpansionDetailMediaBlockAvatar(candidate?.avatar);
39816
+ const title = this.normalizeExpansionDetailMediaBlockText(candidate?.title);
39817
+ const subtitle = this.normalizeExpansionDetailMediaBlockText(candidate?.subtitle);
39818
+ const meta = this.normalizeExpansionDetailMediaBlockMeta(candidate?.meta);
39819
+ const trailing = this.normalizeExpansionDetailMediaBlockTrailing(candidate?.trailing);
39820
+ if (!avatar && !title && !subtitle && !meta && trailing.length === 0) {
39821
+ if (fallbackNodePolicy === 'renderPlaceholder') {
39822
+ return {
39823
+ ok: true,
39824
+ node: {
39825
+ type: 'mediaBlock',
39826
+ title: { type: 'text', text: 'Node bloqueado' },
39827
+ subtitle: { type: 'text', text: `mediaBlock inválido (${path})` },
39828
+ },
39829
+ message: null,
39830
+ };
39831
+ }
39832
+ return { ok: false, node: null, message: `mediaBlock inválido em ${path}.` };
39833
+ }
39834
+ return {
39835
+ ok: true,
39836
+ node: {
39837
+ ...candidate,
39838
+ type: 'mediaBlock',
39839
+ avatar: avatar ?? undefined,
39840
+ title: title ?? undefined,
39841
+ subtitle: subtitle ?? undefined,
39842
+ meta: meta ?? undefined,
39843
+ trailing: trailing.length ? trailing : undefined,
39844
+ },
39845
+ message: null,
39846
+ };
39847
+ }
39848
+ sanitizeExpansionDetailCardGridCards(cards, fallbackNodePolicy, path) {
39849
+ if (!Array.isArray(cards)) {
39850
+ if (fallbackNodePolicy === 'renderPlaceholder') {
39851
+ return {
39852
+ ok: true,
39853
+ cards: [
39854
+ {
39855
+ title: 'Node bloqueado',
39856
+ subtitle: `cards inválidos (${path})`,
39857
+ content: [{ type: 'text', text: 'Node bloqueado' }],
39858
+ },
39859
+ ],
39860
+ message: null,
39861
+ };
39862
+ }
39863
+ return { ok: false, cards: [], message: `cards inválidos em ${path}.` };
39864
+ }
39865
+ const out = [];
39866
+ for (let i = 0; i < cards.length; i += 1) {
39867
+ const candidate = cards[i];
39868
+ if (!candidate || typeof candidate !== 'object') {
39869
+ if (fallbackNodePolicy === 'renderPlaceholder') {
39870
+ out.push({
39871
+ title: 'Node bloqueado',
39872
+ subtitle: `card inválido (${path}[${i}])`,
39873
+ content: [{ type: 'text', text: 'Node bloqueado' }],
39874
+ });
39875
+ continue;
39876
+ }
39877
+ return { ok: false, cards: [], message: `card inválido em ${path}[${i}].` };
39878
+ }
39879
+ const sanitizedContent = this.normalizeExpansionDetailCardGridCardContent(candidate.content);
39880
+ if (!sanitizedContent.length) {
39881
+ if (fallbackNodePolicy === 'renderPlaceholder') {
39882
+ out.push({
39883
+ id: candidate.id,
39884
+ title: candidate.title || 'Node bloqueado',
39885
+ subtitle: `content inválido (${path}[${i}].content)`,
39886
+ content: [{ type: 'text', text: 'Node bloqueado' }],
39887
+ });
39888
+ continue;
39889
+ }
39890
+ return { ok: false, cards: [], message: `content inválido em ${path}[${i}].content.` };
39891
+ }
39892
+ out.push({
39893
+ ...candidate,
39894
+ content: sanitizedContent,
39895
+ });
39896
+ }
39897
+ return { ok: true, cards: out, message: null };
39898
+ }
38462
39899
  buildExpansionBlockedPlaceholderNode(reason, path) {
38463
39900
  return {
38464
39901
  type: 'card',
@@ -38652,12 +40089,9 @@ class PraxisTable {
38652
40089
  items.push({
38653
40090
  type: 'card',
38654
40091
  title: this.getExpansionHypermediaMessage('table.expansion.hypermedia.actions.title', this.isEnglishLocale() ? 'Workflow actions' : 'Ações de workflow'),
38655
- content: workflowActionItems.map((item) => ({
38656
- type: 'value',
38657
- title: item.title,
38658
- value: item.value,
38659
- })),
40092
+ content: [],
38660
40093
  });
40094
+ items.push(...workflowActionItems);
38661
40095
  }
38662
40096
  if (!items.length) {
38663
40097
  return null;
@@ -38719,10 +40153,15 @@ class PraxisTable {
38719
40153
  .map((action) => {
38720
40154
  const title = action.title || action.id;
38721
40155
  return {
40156
+ type: 'action',
40157
+ id: action.id,
38722
40158
  title,
38723
- value: action.availability?.allowed === false
40159
+ disabled: action.availability?.allowed === false,
40160
+ statusText: action.availability?.allowed === false
38724
40161
  ? translateResourceAvailabilityReason(this.i18n, action.availability?.reason)
38725
40162
  : this.getExpansionHypermediaMessage('table.expansion.hypermedia.actions.status.ready', this.isEnglishLocale() ? 'Ready to use' : 'Pronta para uso'),
40163
+ actionId: action.id,
40164
+ __praxisDiscoveredAction: action,
38726
40165
  };
38727
40166
  });
38728
40167
  }
@@ -40097,6 +41536,14 @@ class PraxisTable {
40097
41536
  tableId: this.tableId,
40098
41537
  config: this.config,
40099
41538
  });
41539
+ if (this.crudContext && typeof this.crudContext.openAuthoring === 'function') {
41540
+ this.debugLog('[PraxisTable] Delegating settings entrypoint to CRUD authoring shell', {
41541
+ tableId: this.tableId,
41542
+ componentKeyId: this.componentKeyId(),
41543
+ });
41544
+ this.crudContext.openAuthoring();
41545
+ return;
41546
+ }
40100
41547
  const configCopy = JSON.parse(JSON.stringify(this.config));
40101
41548
  const componentKeyId = this.componentKeyId();
40102
41549
  const panelInputs = {
@@ -41411,7 +42858,7 @@ class PraxisTable {
41411
42858
  const existing = this.config?.columns ?? [];
41412
42859
  if (existing.length === 0) {
41413
42860
  this.config.columns = fields
41414
- .filter((f) => !f.tableHidden)
42861
+ .filter((f) => !f.tableHidden && !f.hidden)
41415
42862
  .map((f) => this.convertFieldToColumn(f));
41416
42863
  }
41417
42864
  const mountCtx = this.buildLoadingContext('mount', 'Montando colunas…', false);
@@ -41466,7 +42913,7 @@ class PraxisTable {
41466
42913
  order: field.order,
41467
42914
  width: field.width ?? undefined,
41468
42915
  sortable: field.sortable,
41469
- visible: field.tableHidden ? false : true,
42916
+ visible: field.tableHidden || field.hidden ? false : true,
41470
42917
  type: apiType,
41471
42918
  _originalApiType: apiType,
41472
42919
  _isApiField: true,
@@ -43353,6 +44800,19 @@ class PraxisTable {
43353
44800
  getIconAriaLabel(_row, column) {
43354
44801
  return this.getEffectiveRenderer(_row, column)?.icon?.ariaLabel || null;
43355
44802
  }
44803
+ getIconRichContentNodes(row, column) {
44804
+ const icon = this.getIconName(row, column);
44805
+ if (!icon)
44806
+ return [];
44807
+ return [
44808
+ {
44809
+ type: 'icon',
44810
+ icon,
44811
+ ariaLabel: this.getIconAriaLabel(row, column) || icon,
44812
+ className: 'pfx-icon-renderer__icon',
44813
+ },
44814
+ ];
44815
+ }
43356
44816
  /**
43357
44817
  * Lightweight, controlled evaluator for string/number expressions using row context.
43358
44818
  * Accepts values like '= row.status === "OK" ? "primary" : "warn"'.
@@ -43812,6 +45272,31 @@ class PraxisTable {
43812
45272
  return null;
43813
45273
  return (cfg.altField ? this.getNestedPropertyValue(row, cfg.altField) : cfg.alt) || null;
43814
45274
  }
45275
+ getImageRichContentNodes(row, column) {
45276
+ const shape = this.getImageShape(row, column);
45277
+ const width = this.getImageWidth(row, column);
45278
+ const height = this.getImageHeight(row, column);
45279
+ const fit = this.getImageFit(row, column);
45280
+ const style = {};
45281
+ if (width != null)
45282
+ style['width'] = `${width}px`;
45283
+ if (height != null)
45284
+ style['height'] = `${height}px`;
45285
+ if (fit)
45286
+ style['objectFit'] = fit;
45287
+ const node = {
45288
+ type: 'image',
45289
+ src: this.getImageSrc(row, column) ?? undefined,
45290
+ alt: this.getImageAlt(row, column) ?? '',
45291
+ className: shape === 'rounded'
45292
+ ? 'shape-rounded'
45293
+ : shape === 'circle'
45294
+ ? 'shape-circle'
45295
+ : undefined,
45296
+ style,
45297
+ };
45298
+ return [node];
45299
+ }
43815
45300
  getImageWidth(row, column) {
43816
45301
  if (this.isStrictCspStyleModeEnabled())
43817
45302
  return null;
@@ -43859,6 +45344,26 @@ class PraxisTable {
43859
45344
  out.push(`pfx-badge--filled-${color || 'primary'}`);
43860
45345
  return out;
43861
45346
  }
45347
+ getBadgeRichContentNodes(row, column) {
45348
+ const nodes = [];
45349
+ const icon = this.getBadgeIcon(row, column);
45350
+ const text = this.getBadgeText(row, column);
45351
+ if (icon) {
45352
+ nodes.push({
45353
+ type: 'icon',
45354
+ icon,
45355
+ className: 'pfx-badge-icon',
45356
+ });
45357
+ }
45358
+ if (text) {
45359
+ nodes.push({
45360
+ type: 'text',
45361
+ text,
45362
+ className: 'pfx-badge-text',
45363
+ });
45364
+ }
45365
+ return nodes;
45366
+ }
43862
45367
  // Resolve renderer type per row (supports conditional typeExpr)
43863
45368
  getEffectiveRendererType(row, column) {
43864
45369
  const eff = this.getEffectiveRenderer(row, column);
@@ -44115,6 +45620,39 @@ class PraxisTable {
44115
45620
  const text = cfg?.textField ? this.getNestedPropertyValue(row, cfg.textField) : cfg?.text;
44116
45621
  return (text != null ? String(text) : String(this.getCellValue(row, column) ?? '')) || '';
44117
45622
  }
45623
+ getLinkRichContentNodes(row, column) {
45624
+ const text = this.getLinkText(row, column);
45625
+ if (!text)
45626
+ return [];
45627
+ return [
45628
+ {
45629
+ type: 'text',
45630
+ text,
45631
+ className: 'pfx-link-text',
45632
+ },
45633
+ ];
45634
+ }
45635
+ getMenuItemRichContentNodes(item) {
45636
+ const nodes = [];
45637
+ const icon = String(item?.icon || '').trim();
45638
+ const label = String(item?.label || item?.id || '').trim();
45639
+ if (icon) {
45640
+ nodes.push({
45641
+ type: 'icon',
45642
+ icon,
45643
+ className: 'pfx-menu-item__icon',
45644
+ ariaLabel: label || icon,
45645
+ });
45646
+ }
45647
+ if (label) {
45648
+ nodes.push({
45649
+ type: 'text',
45650
+ text: label,
45651
+ className: 'pfx-menu-item__label',
45652
+ });
45653
+ }
45654
+ return nodes;
45655
+ }
44118
45656
  getLinkTarget(row, column) {
44119
45657
  const t = this.getEffectiveRenderer(row, column)?.link?.target || null;
44120
45658
  return t === '_blank' || t === '_self' || t === '_parent' || t === '_top' ? t : null;
@@ -44152,6 +45690,26 @@ class PraxisTable {
44152
45690
  getButtonAriaLabel(row, column) {
44153
45691
  return this.getEffectiveRenderer(row, column)?.button?.ariaLabel || this.getButtonLabel(row, column) || null;
44154
45692
  }
45693
+ getButtonRichContentNodes(row, column) {
45694
+ const nodes = [];
45695
+ const icon = this.getButtonIcon(row, column);
45696
+ const label = this.getButtonLabel(row, column);
45697
+ if (icon) {
45698
+ nodes.push({
45699
+ type: 'icon',
45700
+ icon,
45701
+ className: 'pfx-button-renderer__icon',
45702
+ });
45703
+ }
45704
+ if (label) {
45705
+ nodes.push({
45706
+ type: 'text',
45707
+ text: label,
45708
+ className: 'pfx-button-renderer__label',
45709
+ });
45710
+ }
45711
+ return nodes;
45712
+ }
44155
45713
  evaluateActionPayloadExpr(raw, row) {
44156
45714
  if (raw === undefined)
44157
45715
  return undefined;
@@ -44196,6 +45754,26 @@ class PraxisTable {
44196
45754
  const t = cfg.textField ? this.getNestedPropertyValue(row, cfg.textField) : cfg.text;
44197
45755
  return t != null ? String(t) : null;
44198
45756
  }
45757
+ getChipRichContentNodes(row, column) {
45758
+ const nodes = [];
45759
+ const icon = this.getChipIcon(row, column);
45760
+ const text = this.getChipText(row, column);
45761
+ if (icon) {
45762
+ nodes.push({
45763
+ type: 'icon',
45764
+ icon,
45765
+ className: 'pfx-chip-icon',
45766
+ });
45767
+ }
45768
+ if (text) {
45769
+ nodes.push({
45770
+ type: 'text',
45771
+ text,
45772
+ className: 'pfx-chip-text',
45773
+ });
45774
+ }
45775
+ return nodes;
45776
+ }
44199
45777
  getChipIcon(row, column) {
44200
45778
  return this.getEffectiveRenderer(row, column)?.chip?.icon || null;
44201
45779
  }
@@ -44241,6 +45819,24 @@ class PraxisTable {
44241
45819
  getProgressShowLabel(row, column) {
44242
45820
  return !!this.getEffectiveRenderer(row, column)?.progress?.showLabel;
44243
45821
  }
45822
+ getProgressRichContentContext(row, column) {
45823
+ const value = this.getProgressValue(row, column);
45824
+ return {
45825
+ value,
45826
+ label: this.getProgressShowLabel(row, column) ? `${value}%` : null,
45827
+ };
45828
+ }
45829
+ getProgressRichContentNodes(row, column) {
45830
+ return [
45831
+ {
45832
+ type: 'progress',
45833
+ valueExpr: 'value',
45834
+ labelExpr: 'label',
45835
+ max: 100,
45836
+ className: 'pfx-progress__node',
45837
+ },
45838
+ ];
45839
+ }
44244
45840
  // Rating
44245
45841
  getRatingValue(row, column) {
44246
45842
  const cfg = this.getEffectiveRenderer(row, column)?.rating;
@@ -44328,6 +45924,20 @@ class PraxisTable {
44328
45924
  }
44329
45925
  return '';
44330
45926
  }
45927
+ getAvatarRichContentNodes(row, column) {
45928
+ const imageSrc = this.getAvatarSrc(row, column);
45929
+ const initials = this.getAvatarInitials(row, column);
45930
+ const alt = this.getAvatarAlt(row, column) || undefined;
45931
+ return [
45932
+ {
45933
+ type: 'avatar',
45934
+ imageSrc: imageSrc || undefined,
45935
+ initials: initials || undefined,
45936
+ name: alt,
45937
+ className: 'pfx-avatar',
45938
+ },
45939
+ ];
45940
+ }
44331
45941
  getAvatarShape(row, column) {
44332
45942
  return this.getEffectiveRenderer(row, column)?.avatar?.shape || undefined;
44333
45943
  }
@@ -44364,6 +45974,14 @@ class PraxisTable {
44364
45974
  getMenuAriaLabel(row, column) {
44365
45975
  return this.getEffectiveRenderer(row, column)?.menu?.ariaLabel || null;
44366
45976
  }
45977
+ getMenuTriggerRichContentNodes() {
45978
+ const node = {
45979
+ type: 'icon',
45980
+ icon: 'more_vert',
45981
+ className: 'pfx-menu-trigger__icon',
45982
+ };
45983
+ return [node];
45984
+ }
44367
45985
  evaluateArrayExpr(raw, row) {
44368
45986
  const expr = this.extractExpression(raw, true);
44369
45987
  if (!expr)
@@ -44958,7 +46576,7 @@ class PraxisTable {
44958
46576
  this.resizeObserver = new ResizeObserver((entries) => {
44959
46577
  for (const entry of entries) {
44960
46578
  if (entry.target === this.hostRef.nativeElement) {
44961
- this.refreshResponsiveHorizontalScroll();
46579
+ this.scheduleResponsiveHorizontalScrollRefresh();
44962
46580
  }
44963
46581
  }
44964
46582
  });
@@ -45062,6 +46680,26 @@ class PraxisTable {
45062
46680
  const rowCfg = this.getRowActionsConfig();
45063
46681
  return rowCfg?.header?.label ?? rowCfg?.headerLabel;
45064
46682
  }
46683
+ getActionsHeaderRichContentNodes() {
46684
+ const nodes = [];
46685
+ const icon = this.getActionsHeaderIcon();
46686
+ const label = this.getActionsHeaderLabel();
46687
+ if (icon) {
46688
+ nodes.push({
46689
+ type: 'icon',
46690
+ icon,
46691
+ className: 'praxis-actions-header__icon',
46692
+ });
46693
+ }
46694
+ if (label) {
46695
+ nodes.push({
46696
+ type: 'text',
46697
+ text: label,
46698
+ className: 'praxis-actions-header__label',
46699
+ });
46700
+ }
46701
+ return nodes;
46702
+ }
45065
46703
  getRowActionsDisplay() {
45066
46704
  const display = this.getRowActionsConfig()?.display;
45067
46705
  return display === 'menu' || display === 'buttons' || display === 'icons'
@@ -45393,8 +47031,8 @@ class PraxisTable {
45393
47031
  }
45394
47032
  getRowMenuTooltip(row) {
45395
47033
  return this.isRowDiscoveryPending(row)
45396
- ? translateResourceDiscoveryText(this.i18n, 'rowActions.menu.loading', undefined, 'Carregando ações contextuais')
45397
- : translateResourceDiscoveryText(this.i18n, 'rowActions.menu.more', undefined, 'Mais ações');
47034
+ ? translateTableRuntimeText(this.i18n, 'table.chrome.rowActions.loading', 'Carregando ações contextuais')
47035
+ : translateTableRuntimeText(this.i18n, 'table.chrome.rowActions.more', 'Mais ações');
45398
47036
  }
45399
47037
  getRowMenuButtonColor() {
45400
47038
  return this.config.actions?.row?.menuButtonColor ?? undefined;
@@ -45428,6 +47066,7 @@ class PraxisTable {
45428
47066
  if (this.resizeObserver) {
45429
47067
  this.resizeObserver.disconnect();
45430
47068
  }
47069
+ this.cancelResponsiveHorizontalScrollRefresh();
45431
47070
  this.removeViewportChangeListeners?.();
45432
47071
  this.removeViewportChangeListeners = null;
45433
47072
  }
@@ -45439,7 +47078,7 @@ class PraxisTable {
45439
47078
  TableDefaultsProvider,
45440
47079
  FilterConfigService,
45441
47080
  DataFormattingService
45442
- ], queries: [{ propertyName: "toolbar", first: true, predicate: PraxisTableToolbar, descendants: true }, { propertyName: "projectedFilter", first: true, predicate: ["projectedFilter"], descendants: true }], viewQueries: [{ propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }, { propertyName: "sort", first: true, predicate: MatSort, descendants: true }, { propertyName: "materialTable", first: true, predicate: MatTable, descendants: true }, { propertyName: "internalFilter", first: true, predicate: PraxisFilter, descendants: true }], usesOnChanges: true, ngImport: i0, template: "@if (shouldShowEmptyState()) {\n <praxis-empty-state-card\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\n<!-- Error State with Quick Connect CTA -->\n@if (isRemoteMode() && (schemaError || dataError)) {\n<div class=\"ptable-error\" role=\"alert\" aria-live=\"assertive\">\n <mat-icon color=\"warn\" aria-hidden=\"true\">error</mat-icon>\n <div class=\"ptable-error__content\">\n <div class=\"ptable-error__title\">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 @if (!schemaError) { <button mat-stroked-button (click)=\"retryData()\">Tentar novamente</button> }\n @if (schemaError) { <button mat-stroked-button (click)=\"reloadSchema()\">Recarregar colunas</button> }\n </div>\n}\n\n<!-- Inline banner for schema change (only in edit mode) -->\n@if (shouldShowOutdatedInline()) {\n<div 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\n@if (shouldRenderDataSurface() && !schemaError && !dataError && toolbarV2) {\n <div class=\"praxis-table-header\" [class.edit-mode]=\"enableCustomization\" [class.stacked]=\"showToolbar\">\n @if (showToolbar && shouldShowToolbarTopPlacement()) {\n <praxis-table-toolbar\n [config]=\"config\"\n [backgroundColor]=\"getToolbarActionsBackgroundColor()\"\n [style.--pfx-filter-h]=\"getToolbarLayoutHeightHostStyle()\"\n [evaluationContext]=\"getToolbarEvaluationContext()\"\n [showActionsGroup]=\"shouldShowToolbarActionsTop()\"\n [showMobileActions]=\"shouldShowToolbarActionsTop()\"\n (toolbarAction)=\"onToolbarAction($event)\"\n (reset)=\"onResetPreferences()\"\n >\n @if (shouldRenderAdvancedFilter()) {\n <praxis-filter\n advancedFilter\n [resourcePath]=\"getAdvancedFilterResourcePath()\"\n [filterId]=\"tableId\"\n [formId]=\"tableId\"\n [persistenceKey]=\"getAdvancedFilterPersistenceKey()\"\n [fieldMetadata]=\"getAdvancedFilterFieldMetadata()\"\n [enableCustomization]=\"enableCustomization\"\n \n [alwaysVisibleFields]=\"config.behavior?.filtering?.advancedFilters?.settings?.alwaysVisibleFields\"\n [alwaysVisibleFieldMetadataOverrides]=\"config.behavior?.filtering?.advancedFilters?.settings?.alwaysVisibleFieldMetadataOverrides ?? {}\"\n [selectedFieldIds]=\"config.behavior?.filtering?.advancedFilters?.settings?.selectedFieldIds ?? []\"\n [allowSaveTags]=\"config.behavior?.filtering?.advancedFilters?.settings?.allowSaveTags\"\n [changeDebounceMs]=\"config.behavior?.filtering?.advancedFilters?.settings?.changeDebounceMs ?? 300\"\n [i18n]=\"getFilterI18n()\"\n [mode]=\"'filter'\"\n [showFilterSettings]=\"enableCustomization\"\n (change)=\"onAdvancedFilterChange($event)\"\n (requestSearch)=\"onAdvancedFilterSubmit($event)\"\n (clear)=\"onAdvancedFilterClear()\"\n ></praxis-filter>\n }\n <ng-content select=\"[advancedFilter]\" />\n <ng-content select=\"[toolbar]\" />\n \n <!-- AI Assistant in Toolbar -->\n @defer (on idle) {\n @if (aiAdapter) {\n <praxis-ai-assistant [adapter]=\"aiAdapter\" end-actions></praxis-ai-assistant>\n }\n }\n\n @if (enableCustomization) {\n <button end-actions mat-icon-button color=\"primary\" data-role=\"table-settings\" data-testid=\"table-settings-trigger\"\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 }\n </praxis-table-toolbar>\n }\n @if (!showToolbar && enableCustomization) {\n <div class=\"ptable-header-actions\">\n @defer (on idle) {\n @if (aiAdapter) {\n <praxis-ai-assistant [adapter]=\"aiAdapter\"></praxis-ai-assistant>\n }\n }\n <button mat-icon-button color=\"primary\" data-role=\"table-settings\" data-testid=\"table-settings-trigger\" (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 @if (isRemoteMode()) {\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 }\n </div>\n }\n </div>\n} @else {\n @if (shouldRenderDataSurface() && !schemaError && !dataError) {\n @if (showToolbar && shouldShowToolbarTopPlacement()) {\n <praxis-table-toolbar\n [config]=\"config\"\n [backgroundColor]=\"getToolbarActionsBackgroundColor()\"\n [style.--pfx-filter-h]=\"getToolbarLayoutHeightHostStyle()\"\n [evaluationContext]=\"getToolbarEvaluationContext()\"\n [showActionsGroup]=\"shouldShowToolbarActionsTop()\"\n [showMobileActions]=\"shouldShowToolbarActionsTop()\"\n (toolbarAction)=\"onToolbarAction($event)\"\n (reset)=\"onResetPreferences()\"\n >\n @if (shouldRenderAdvancedFilter()) {\n <praxis-filter\n advancedFilter\n [resourcePath]=\"getAdvancedFilterResourcePath()\"\n [filterId]=\"tableId\"\n [formId]=\"tableId\"\n [persistenceKey]=\"getAdvancedFilterPersistenceKey()\"\n [fieldMetadata]=\"getAdvancedFilterFieldMetadata()\"\n [enableCustomization]=\"enableCustomization\"\n \n [alwaysVisibleFields]=\"config.behavior?.filtering?.advancedFilters?.settings?.alwaysVisibleFields\"\n [alwaysVisibleFieldMetadataOverrides]=\"config.behavior?.filtering?.advancedFilters?.settings?.alwaysVisibleFieldMetadataOverrides ?? {}\"\n [selectedFieldIds]=\"config.behavior?.filtering?.advancedFilters?.settings?.selectedFieldIds ?? []\"\n [allowSaveTags]=\"config.behavior?.filtering?.advancedFilters?.settings?.allowSaveTags\"\n [changeDebounceMs]=\"config.behavior?.filtering?.advancedFilters?.settings?.changeDebounceMs ?? 300\"\n [i18n]=\"getFilterI18n()\"\n [mode]=\"'filter'\"\n [showFilterSettings]=\"enableCustomization\"\n (change)=\"onAdvancedFilterChange($event)\"\n (requestSearch)=\"onAdvancedFilterSubmit($event)\"\n (clear)=\"onAdvancedFilterClear()\"\n ></praxis-filter>\n }\n <ng-content select=\"[advancedFilter]\" />\n <ng-content select=\"[toolbar]\" />\n @defer (on idle) {\n @if (aiAdapter) {\n <praxis-ai-assistant [adapter]=\"aiAdapter\" end-actions></praxis-ai-assistant>\n }\n }\n @if (enableCustomization) {\n <button end-actions mat-icon-button color=\"primary\" data-role=\"table-settings\" data-testid=\"table-settings-trigger\"\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 }\n </praxis-table-toolbar>\n }\n @if (!showToolbar && enableCustomization) {\n <div class=\"ptable-header-actions\">\n <button mat-icon-button color=\"primary\" data-role=\"table-settings\" data-testid=\"table-settings-trigger\" (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 }\n }\n}\n<div class=\"px-scroll-viewport\"\n cdkScrollable\n [class.scroll-auto]=\"horizontalScroll === 'auto'\"\n [class.scroll-wrap]=\"horizontalScroll === 'wrap'\"\n [class.scroll-none]=\"horizontalScroll === 'none'\">\n\n@if (shouldRenderDataSurface() && !schemaError && !dataError) {\n<div class=\"praxis-visually-hidden-status\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n {{ columnReorderStatusMessage }}\n</div>\n@if (columnReorderVisualStatusMessage) {\n <div class=\"praxis-column-reorder-status\" role=\"note\">\n {{ columnReorderVisualStatusMessage }}\n </div>\n}\n<table\n mat-table\n data-testid=\"table-column-drag-drop-list\"\n [dataSource]=\"dataSource\"\n [multiTemplateDataRows]=\"isRowExpansionRuntimeEnabled()\"\n cdkDropList\n [cdkDropListDisabled]=\"!isColumnDraggingEnabled()\"\n [cdkDropListData]=\"visibleDataColumnsForDrag\"\n cdkDropListOrientation=\"horizontal\"\n (cdkDropListDropped)=\"onColumnDrop($event)\"\n matSort\n (matSortChange)=\"onSortChange($event)\"\n [matSortDisabled]=\"!getSortingEnabled()\"\n [ngClass]=\"getTableElevationClassName()\"\n [class.table-stack-top]=\"showToolbar\"\n [class.pfx-column-drag-enabled]=\"isColumnDraggingEnabled()\"\n [class.pfx-column-drag-indicator]=\"isColumnDragIndicatorEnabled()\"\n>\n @if (config.behavior?.selection?.enabled) {\n <ng-container\n matColumnDef=\"_select\"\n >\n <th mat-header-cell *matHeaderCellDef>\n @if (canSelectAll()) {\n <mat-checkbox\n (change)=\"masterToggle()\"\n [checked]=\"isAllSelected()\"\n [indeterminate]=\"selection.hasValue() && !isAllSelected()\"\n ></mat-checkbox>\n }\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 }\n @if (isRowExpansionRuntimeEnabled()) {\n <ng-container matColumnDef=\"_expander\">\n <th mat-header-cell *matHeaderCellDef class=\"pfx-expansion-col-header\">\n <span class=\"praxis-visually-hidden-status\">Expandir detalhes da linha</span>\n </th>\n <td mat-cell *matCellDef=\"let row; let i = index\" class=\"pfx-expansion-col-cell\">\n <button\n mat-icon-button\n class=\"pfx-expansion-toggle\"\n [disabled]=\"!isRowExpandable(row, i) || !isExpansionIconTriggerEnabled()\"\n [attr.aria-expanded]=\"isRowExpanded(row, i) ? 'true' : 'false'\"\n [attr.aria-controls]=\"getRowExpansionDetailId(row, i)\"\n [attr.aria-label]=\"getRowExpansionToggleAriaLabel(row, i)\"\n (click)=\"onExpansionToggleFromIcon(row, i, $event)\"\n (keydown)=\"onExpansionToggleKeydown($event, row, i)\"\n >\n <mat-icon [praxisIcon]=\"isRowExpanded(row, i)\n ? getExpansionExpandedIcon()\n : getExpansionCollapsedIcon()\"></mat-icon>\n </button>\n </td>\n </ng-container>\n }\n @for (column of visibleColumns; track column.field) {\n <ng-container\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 cdkDrag\n [cdkDragData]=\"column\"\n cdkDragLockAxis=\"x\"\n cdkDragPreviewClass=\"pfx-column-drag-preview\"\n (cdkDragStarted)=\"onColumnDragStarted(column)\"\n (cdkDragEnded)=\"onColumnDragEnded($event, column)\"\n (keydown)=\"onColumnDragHandleKeydown($event, column)\"\n [cdkDragDisabled]=\"!isColumnDraggingEnabled() || !isColumnDraggable(column)\"\n [class.praxis-header-draggable]=\"isColumnDraggingEnabled() && isColumnDraggable(column)\"\n [disabled]=\"!getSortingEnabled() || column.sortable === false\"\n [style.text-align]=\"getColumnTextAlignStyle(column)\"\n [style.width]=\"getColumnWidthStyle(column)\"\n [attr.style]=\"getColumnHeaderAttrStyle(column)\"\n [attr.aria-label]=\"isColumnDraggingEnabled() && isColumnDraggable(column) ? getColumnDragHandleAriaLabel(column) : null\"\n >\n <span class=\"praxis-header-label\" data-testid=\"column-header-label\">\n @if (isColumnDraggingEnabled() && isColumnDraggable(column)) {\n <span\n class=\"praxis-column-drag-handle\"\n data-testid=\"column-drag-handle\"\n [attr.data-column-field]=\"column.field\"\n aria-hidden=\"true\"\n >\n <mat-icon [praxisIcon]=\"'drag_indicator'\"></mat-icon>\n </span>\n }\n <span class=\"praxis-header-label-text\">{{ column.header }}</span>\n </span>\n </th>\n <td\n mat-cell\n *matCellDef=\"let element\"\n [style.text-align]=\"getColumnTextAlignStyle(column)\"\n [style.width]=\"getColumnWidthStyle(column)\"\n [attr.style]=\"getColumnCellAttrStyle(column)\"\n [ngClass]=\"getCellClasses(element, column)\"\n [ngStyle]=\"getCellNgStyle(element, column)\"\n >\n <div\n class=\"pfx-cell-content\"\n [ngClass]=\"getCellContentClasses(element, column)\"\n [ngStyle]=\"getCellContentNgStyle(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(element, column)\"\n [style.height.px]=\"getImageHeight(element, column)\"\n [class.shape-rounded]=\"getImageShape(element, column) === 'rounded'\"\n [class.shape-circle]=\"getImageShape(element, column) === 'circle'\"\n [style.object-fit]=\"getImageFit(element, column)\"\n />\n </ng-container>\n\n <!-- Badge renderer -->\n <ng-container *ngSwitchCase=\"'badge'\">\n <span class=\"pfx-badge\" [ngClass]=\"getBadgeClasses(element, column)\">\n @if (getBadgeIcon(element, column); as bi) { <mat-icon class=\"pfx-badge-icon\">{{ bi }}</mat-icon> }\n <span class=\"pfx-badge-text\">{{ getBadgeText(element, column) }}</span>\n </span>\n </ng-container>\n\n <!-- Link renderer -->\n <ng-container *ngSwitchCase=\"'link'\">\n <a\n class=\"pfx-link\"\n [attr.href]=\"getLinkHref(element, column) || null\"\n [attr.target]=\"getLinkTarget(element, column) || null\"\n [attr.rel]=\"getLinkRel(element, column) || null\"\n (click)=\"$event.stopPropagation()\"\n >{{ getLinkText(element, column) }}</a\n >\n </ng-container>\n\n <!-- Button renderer -->\n <ng-container *ngSwitchCase=\"'button'\">\n <ng-container [ngSwitch]=\"getButtonVariant(element, column)\">\n <button\n *ngSwitchCase=\"'outlined'\"\n mat-stroked-button\n [color]=\"getButtonColor(element, column) || null\"\n [disabled]=\"isButtonDisabled(element, column)\"\n [attr.aria-label]=\"getButtonAriaLabel(element, column) || getButtonLabel(element, column)\"\n (click)=\"onButtonClick(element, column, $event)\"\n >\n @if (getButtonIcon(element, column); as bi) { <mat-icon>{{ bi }}</mat-icon> }\n {{ getButtonLabel(element, column) }}\n </button>\n <button\n *ngSwitchCase=\"'text'\"\n mat-button\n [color]=\"getButtonColor(element, column) || null\"\n [disabled]=\"isButtonDisabled(element, column)\"\n [attr.aria-label]=\"getButtonAriaLabel(element, column) || getButtonLabel(element, column)\"\n (click)=\"onButtonClick(element, column, $event)\"\n >\n @if (getButtonIcon(element, column); as bi) { <mat-icon>{{ bi }}</mat-icon> }\n {{ getButtonLabel(element, column) }}\n </button>\n <button\n *ngSwitchDefault\n mat-flat-button\n [color]=\"getButtonColor(element, column) || null\"\n [disabled]=\"isButtonDisabled(element, column)\"\n [attr.aria-label]=\"getButtonAriaLabel(element, column) || getButtonLabel(element, column)\"\n (click)=\"onButtonClick(element, column, $event)\"\n >\n @if (getButtonIcon(element, column); as bi) { <mat-icon>{{ bi }}</mat-icon> }\n {{ getButtonLabel(element, column) }}\n </button>\n </ng-container>\n </ng-container>\n\n <!-- Chip renderer -->\n <ng-container *ngSwitchCase=\"'chip'\">\n <span class=\"pfx-chip\" [ngClass]=\"getChipClasses(element, column)\">\n @if (getChipIcon(element, column); as ci) { <mat-icon class=\"pfx-chip-icon\">{{ ci }}</mat-icon> }\n <span class=\"pfx-chip-text\">{{ getChipText(element, column) }}</span>\n </span>\n </ng-container>\n\n <!-- Progress renderer -->\n <ng-container *ngSwitchCase=\"'progress'\">\n <div class=\"pfx-progress\">\n <div class=\"pfx-progress-bar\" [style.width.%]=\"getProgressWidthPercentStyle(element, column)\" [style.background]=\"getProgressBackgroundStyle(element, column)\"></div>\n @if (getProgressShowLabel(element, column)) { <div class=\"pfx-progress-label\">{{ getProgressValue(element, column) }}%</div> }\n </div>\n </ng-container>\n\n <!-- Avatar renderer -->\n <ng-container *ngSwitchCase=\"'avatar'\">\n @if (getAvatarSrc(element, column); as asrc) {\n <img class=\"pfx-avatar\" [src]=\"asrc\" [attr.alt]=\"getAvatarAlt(element, column) || ''\" [ngStyle]=\"getAvatarStyle(element, column)\" [class.shape-rounded]=\"getAvatarShape(element, column) === 'rounded'\" [class.shape-circle]=\"getAvatarShape(element, column) === 'circle'\" loading=\"lazy\" />\n } @else {\n <span class=\"pfx-avatar pfx-avatar--initials\" [ngStyle]=\"getAvatarStyle(element, column)\" [class.shape-rounded]=\"getAvatarShape(element, column) === 'rounded'\" [class.shape-circle]=\"getAvatarShape(element, column) === 'circle'\">{{ getAvatarInitials(element, column) }}</span>\n }\n </ng-container>\n\n <!-- Toggle renderer -->\n <ng-container *ngSwitchCase=\"'toggle'\">\n <mat-slide-toggle\n [checked]=\"getToggleState(element, column)\"\n [disabled]=\"isToggleDisabled(element, column)\"\n [attr.aria-label]=\"getToggleAriaLabel(element, column) || 'Alternar'\"\n (change)=\"onToggleChange(element, column, $event)\"\n (click)=\"$event.stopPropagation()\"\n ></mat-slide-toggle>\n </ng-container>\n\n <!-- Menu renderer -->\n <ng-container *ngSwitchCase=\"'menu'\">\n <button mat-icon-button [matMenuTriggerFor]=\"menuRef\" (click)=\"$event.stopPropagation()\" [attr.aria-label]=\"getMenuAriaLabel(element, column) || 'Menu'\">\n <mat-icon>more_vert</mat-icon>\n </button>\n <mat-menu #menuRef=\"matMenu\">\n <button mat-menu-item *ngFor=\"let it of getMenuItems(element, column)\" (click)=\"onMenuItemClick(it, element, $event)\" [disabled]=\"!it.__visible\" >\n @if (it.icon) { <mat-icon>{{ it.icon }}</mat-icon> }\n <span>{{ it.label }}</span>\n </button>\n </mat-menu>\n </ng-container>\n\n <!-- Rating renderer -->\n <ng-container *ngSwitchCase=\"'rating'\">\n <praxis-table-rating\n class=\"pfx-rating-cell\"\n [itemsCount]=\"getRatingMax(element, column)\"\n [value]=\"getRatingValue(element, column)\"\n [size]=\"getRatingSize(element, column)\"\n [ratingColor]=\"getRatingColor(element, column)\"\n [outlineColor]=\"getRatingOutlineColor(element, column)\"\n [ariaLabel]=\"getRatingAriaLabel(element, column) || column.header\">\n </praxis-table-rating>\n </ng-container>\n\n <!-- HTML renderer (sanitizado) -->\n <ng-container *ngSwitchCase=\"'html'\">\n <span [innerHTML]=\"getSafeHtml(element, column)\"></span>\n </ng-container>\n\n <!-- Compose renderer -->\n <ng-container *ngSwitchCase=\"'compose'\">\n <span class=\"pfx-cell-compose\" [ngClass]=\"getComposeClasses(element, column)\" [ngStyle]=\"getComposeGapStyle(element, column)\">\n <ng-container *ngFor=\"let it of getComposeItems(element, column)\">\n <ng-container [ngSwitch]=\"getItemEffectiveType(element, column, it)\">\n <!-- Reuse helpers by projecting item as faux column -->\n <ng-container *ngSwitchCase=\"'icon'\">\n <mat-icon [color]=\"getIconColor(element, asItemColumn(column, it)) || null\" [ngStyle]=\"getIconStyle(element, asItemColumn(column, it))\" [attr.aria-label]=\"getIconAriaLabel(element, asItemColumn(column, it)) || null\">{{ getIconName(element, asItemColumn(column, it)) }}</mat-icon>\n </ng-container>\n <ng-container *ngSwitchCase=\"'image'\">\n <img class=\"pfx-cell-image\" [src]=\"getImageSrc(element, asItemColumn(column, it))\" [attr.alt]=\"getImageAlt(element, asItemColumn(column, it)) || ''\" [attr.loading]=\"getImageLazy(element, asItemColumn(column, it)) ? 'lazy' : null\" [style.width.px]=\"getImageWidth(element, asItemColumn(column, it))\" [style.height.px]=\"getImageHeight(element, asItemColumn(column, it))\" [class.shape-rounded]=\"getImageShape(element, asItemColumn(column, it)) === 'rounded'\" [class.shape-circle]=\"getImageShape(element, asItemColumn(column, it)) === 'circle'\" [style.object-fit]=\"getImageFit(element, asItemColumn(column, it))\" />\n </ng-container>\n <ng-container *ngSwitchCase=\"'badge'\">\n <span class=\"pfx-badge\" [ngClass]=\"getBadgeClasses(element, asItemColumn(column, it))\">@if (getBadgeIcon(element, asItemColumn(column, it)); as bi) { <mat-icon class=\"pfx-badge-icon\">{{ bi }}</mat-icon> }<span class=\"pfx-badge-text\">{{ getBadgeText(element, asItemColumn(column, it)) }}</span></span>\n </ng-container>\n <ng-container *ngSwitchCase=\"'link'\">\n <a class=\"pfx-link\" [attr.href]=\"getLinkHref(element, asItemColumn(column, it)) || null\" [attr.target]=\"getLinkTarget(element, asItemColumn(column, it)) || null\" [attr.rel]=\"getLinkRel(element, asItemColumn(column, it)) || null\" (click)=\"$event.stopPropagation()\">{{ getLinkText(element, asItemColumn(column, it)) }}</a>\n </ng-container>\n <ng-container *ngSwitchCase=\"'button'\">\n <ng-container [ngSwitch]=\"getButtonVariant(element, asItemColumn(column, it))\">\n <button *ngSwitchCase=\"'outlined'\" mat-stroked-button [color]=\"getButtonColor(element, asItemColumn(column, it)) || null\" [disabled]=\"isButtonDisabled(element, asItemColumn(column, it))\" [attr.aria-label]=\"getButtonAriaLabel(element, asItemColumn(column, it)) || getButtonLabel(element, asItemColumn(column, it))\" (click)=\"onButtonClick(element, asItemColumn(column, it), $event)\">@if (getButtonIcon(element, asItemColumn(column, it)); as bi) { <mat-icon>{{ bi }}</mat-icon> }{{ getButtonLabel(element, asItemColumn(column, it)) }}</button>\n <button *ngSwitchCase=\"'text'\" mat-button [color]=\"getButtonColor(element, asItemColumn(column, it)) || null\" [disabled]=\"isButtonDisabled(element, asItemColumn(column, it))\" [attr.aria-label]=\"getButtonAriaLabel(element, asItemColumn(column, it)) || getButtonLabel(element, asItemColumn(column, it))\" (click)=\"onButtonClick(element, asItemColumn(column, it), $event)\">@if (getButtonIcon(element, asItemColumn(column, it)); as bi) { <mat-icon>{{ bi }}</mat-icon> }{{ getButtonLabel(element, asItemColumn(column, it)) }}</button>\n <button *ngSwitchDefault mat-flat-button [color]=\"getButtonColor(element, asItemColumn(column, it)) || null\" [disabled]=\"isButtonDisabled(element, asItemColumn(column, it))\" [attr.aria-label]=\"getButtonAriaLabel(element, asItemColumn(column, it)) || getButtonLabel(element, asItemColumn(column, it))\" (click)=\"onButtonClick(element, asItemColumn(column, it), $event)\">@if (getButtonIcon(element, asItemColumn(column, it)); as bi) { <mat-icon>{{ bi }}</mat-icon> }{{ getButtonLabel(element, asItemColumn(column, it)) }}</button>\n </ng-container>\n </ng-container>\n <ng-container *ngSwitchCase=\"'chip'\">\n <span class=\"pfx-chip\" [ngClass]=\"getChipClasses(element, asItemColumn(column, it))\">@if (getChipIcon(element, asItemColumn(column, it)); as ci) { <mat-icon class=\"pfx-chip-icon\">{{ ci }}</mat-icon> }<span class=\"pfx-chip-text\">{{ getChipText(element, asItemColumn(column, it)) }}</span></span>\n </ng-container>\n <ng-container *ngSwitchCase=\"'progress'\">\n <div class=\"pfx-progress\"><div class=\"pfx-progress-bar\" [style.width.%]=\"getProgressWidthPercentStyle(element, asItemColumn(column, it))\" [style.background]=\"getProgressBackgroundStyle(element, asItemColumn(column, it))\"></div>@if (getProgressShowLabel(element, asItemColumn(column, it))) { <div class=\"pfx-progress-label\">{{ getProgressValue(element, asItemColumn(column, it)) }}%</div> }</div>\n </ng-container>\n <ng-container *ngSwitchCase=\"'avatar'\">\n @if (getAvatarSrc(element, asItemColumn(column, it)); as asrc) {\n <img class=\"pfx-avatar\" [src]=\"asrc\" [attr.alt]=\"getAvatarAlt(element, asItemColumn(column, it)) || ''\" [ngStyle]=\"getAvatarStyle(element, asItemColumn(column, it))\" [class.shape-rounded]=\"getAvatarShape(element, asItemColumn(column, it)) === 'rounded'\" [class.shape-circle]=\"getAvatarShape(element, asItemColumn(column, it)) === 'circle'\" loading=\"lazy\" />\n } @else {\n <span class=\"pfx-avatar pfx-avatar--initials\" [ngStyle]=\"getAvatarStyle(element, asItemColumn(column, it))\" [class.shape-rounded]=\"getAvatarShape(element, asItemColumn(column, it)) === 'rounded'\" [class.shape-circle]=\"getAvatarShape(element, asItemColumn(column, it)) === 'circle'\">{{ getAvatarInitials(element, asItemColumn(column, it)) }}</span>\n }\n </ng-container>\n <ng-container *ngSwitchCase=\"'toggle'\">\n <mat-slide-toggle [checked]=\"getToggleState(element, asItemColumn(column, it))\" [disabled]=\"isToggleDisabled(element, asItemColumn(column, it))\" [attr.aria-label]=\"getToggleAriaLabel(element, asItemColumn(column, it)) || 'Alternar'\" (change)=\"onToggleChange(element, asItemColumn(column, it), $event)\" (click)=\"$event.stopPropagation()\"></mat-slide-toggle>\n </ng-container>\n <ng-container *ngSwitchCase=\"'menu'\">\n <button mat-icon-button [matMenuTriggerFor]=\"menuRef\" (click)=\"$event.stopPropagation()\" [attr.aria-label]=\"getMenuAriaLabel(element, asItemColumn(column, it)) || 'Menu'\"><mat-icon>more_vert</mat-icon></button>\n <mat-menu #menuRef=\"matMenu\">\n <button mat-menu-item *ngFor=\"let mi of getMenuItems(element, asItemColumn(column, it))\" (click)=\"onMenuItemClick(mi, element, $event)\" [disabled]=\"!mi.__visible\">\n @if (mi.icon) { <mat-icon>{{ mi.icon }}</mat-icon> }\n <span>{{ mi.label }}</span>\n </button>\n </mat-menu>\n </ng-container>\n <ng-container *ngSwitchCase=\"'rating'\">\n <praxis-table-rating\n class=\"pfx-rating-cell\"\n [itemsCount]=\"getRatingMax(element, asItemColumn(column, it))\"\n [value]=\"getRatingValue(element, asItemColumn(column, it))\"\n [size]=\"getRatingSize(element, asItemColumn(column, it))\"\n [ratingColor]=\"getRatingColor(element, asItemColumn(column, it))\"\n [outlineColor]=\"getRatingOutlineColor(element, asItemColumn(column, it))\"\n [ariaLabel]=\"getRatingAriaLabel(element, asItemColumn(column, it)) || column.header\">\n </praxis-table-rating>\n </ng-container>\n <ng-container *ngSwitchCase=\"'html'\">\n <span [innerHTML]=\"getSafeHtml(element, asItemColumn(column, it))\"></span>\n </ng-container>\n <!-- Value item: render base cell text alongside visuals -->\n <ng-container *ngSwitchCase=\"'value'\">\n <span class=\"pfx-cell-value\">{{ getComposeItemValue(element, column, it) }}</span>\n </ng-container>\n </ng-container>\n </ng-container>\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 </div>\n </td>\n </ng-container>\n }\n @if (config.actions?.row?.enabled) {\n <ng-container 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]=\"getRowActionsWidthStyle()\" 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 @if (getActionsHeaderIcon(); as hi) { <mat-icon [praxisIcon]=\"hi\"></mat-icon> }\n @if (getActionsHeaderLabel(); as hl) { <span class=\"label\">{{ 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]=\"getRowActionsWidthStyle()\"\n >\n <div\n class=\"praxis-actions-cell__content\"\n [class.praxis-actions-cell__content--discovering]=\"isRowDiscoveryPending(row)\"\n [attr.aria-busy]=\"isRowDiscoveryPending(row) ? 'true' : null\"\n >\n <!-- A\u00E7\u00F5es inline -->\n <!-- Inline actions: icons mode -->\n @if (config.actions?.row?.display === 'icons' || !config.actions?.row?.display) {\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <span\n class=\"praxis-row-action-anchor\"\n [class.praxis-row-action-anchor--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-row-action-anchor--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [matTooltip]=\"getActionTooltip(a, row)\"\n [matTooltipDisabled]=\"!getActionTooltip(a, row)\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"praxis-tooltip\"\n >\n <button\n mat-icon-button\n class=\"praxis-icon-btn\"\n [class.praxis-icon-btn--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-icon-btn--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event, getRowActionRuntimeOptions(a, row))\"\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 </span>\n </ng-container>\n }\n\n <!-- Inline actions: buttons mode (show label + icon) -->\n @if (config.actions?.row?.display === 'buttons') {\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <span\n class=\"praxis-row-action-anchor\"\n [class.praxis-row-action-anchor--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-row-action-anchor--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [matTooltip]=\"getActionTooltip(a, row)\"\n [matTooltipDisabled]=\"!getActionTooltip(a, row)\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"praxis-tooltip\"\n >\n <button\n mat-flat-button\n class=\"praxis-row-btn\"\n [class.praxis-row-btn--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-row-btn--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event, getRowActionRuntimeOptions(a, row))\"\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 </span>\n </ng-container>\n }\n\n <!-- Menu de overflow -->\n @if (hasOverflowRowActions(row)) {\n <button\n mat-icon-button\n class=\"praxis-icon-btn praxis-more-btn\"\n [matMenuTriggerFor]=\"rowMoreMenu\"\n (menuOpened)=\"onRowOverflowMenuOpened(row)\"\n [color]=\"getRowMenuButtonColor() || null\"\n aria-label=\"Mais a\u00E7\u00F5es\"\n [matTooltip]=\"getRowMenuTooltip(row)\"\n matTooltipPosition=\"above\"\n >\n <mat-icon [praxisIcon]=\"getRowMenuIcon()\"></mat-icon>\n </button>\n }\n <mat-menu #rowMoreMenu=\"matMenu\" xPosition=\"before\">\n @if (hasRowOverflowMenuLoadingState(row)) {\n <button mat-menu-item disabled>\n <mat-icon>hourglass_empty</mat-icon>\n <span>{{ getRowOverflowMenuLoadingLabel() }}</span>\n </button>\n }\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, getRowActionRuntimeOptions(a, row))\"\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 @if (hasRowOverflowMenuEmptyState(row)) {\n <button mat-menu-item disabled>\n <mat-icon>info</mat-icon>\n <span>{{ getRowOverflowMenuEmptyLabel(row) }}</span>\n </button>\n }\n </mat-menu>\n </div>\n </td>\n </ng-container>\n }\n\n <tr\n mat-header-row\n *matHeaderRowDef=\"displayedColumns\"\n ></tr>\n @if (!isVirtualized()) {\n <tr\n mat-row\n *matRowDef=\"let row; let i = index; columns: displayedColumns\"\n [class.pfx-row-selected]=\"selection.isSelected(row)\"\n [class.pfx-row-expanded]=\"isRowExpansionRuntimeEnabled() && isRowExpanded(row, i)\"\n [attr.aria-selected]=\"config.behavior?.selection?.enabled ? (selection.isSelected(row) ? 'true' : 'false') : null\"\n [attr.aria-expanded]=\"isRowExpansionRuntimeEnabled() ? (isRowExpanded(row, i) ? 'true' : 'false') : null\"\n [ngClass]=\"getRowClasses(row)\"\n [ngStyle]=\"getRowNgStyle(row)\"\n [matTooltip]=\"getRowTooltip(row) || null\"\n [matTooltipDisabled]=\"!getRowTooltip(row)\"\n [matTooltipPosition]=\"getRowTooltipPosition(row)\"\n [matTooltipShowDelay]=\"getRowTooltipShowDelay(row)\"\n (click)=\"onRowClicked(row, i, $event)\"\n (dblclick)=\"onRowDoubleClicked(row, i)\"\n ></tr>\n @if (isRowExpansionRuntimeEnabled()) {\n <ng-container matColumnDef=\"_detail\">\n <td\n mat-cell\n *matCellDef=\"let row; let i = index\"\n class=\"pfx-expansion-detail-cell\"\n [attr.colspan]=\"displayedColumns.length\"\n >\n <section\n class=\"pfx-expansion-detail-panel\"\n [ngClass]=\"getExpansionMotionPresetClass()\"\n [ngStyle]=\"getExpansionMotionStyle()\"\n [attr.id]=\"getRowExpansionDetailId(row, i)\"\n role=\"region\"\n [attr.aria-label]=\"getRowExpansionRegionAriaLabel(row, i)\"\n [attr.aria-busy]=\"getExpansionDetailViewState(row, i).status === 'loading' ? 'true' : 'false'\"\n >\n @let detailState = getExpansionDetailViewState(row, i);\n @if (detailState.status === 'loading') {\n <div class=\"pfx-expansion-detail-message\" role=\"status\" aria-live=\"polite\">\n Carregando detail schema...\n </div>\n } @else if (detailState.status !== 'ready') {\n <div\n class=\"pfx-expansion-detail-message pfx-expansion-detail-message--error\"\n role=\"alert\"\n aria-live=\"assertive\"\n aria-atomic=\"true\"\n >\n {{ detailState.message || 'Detail indispon\u00EDvel para esta linha.' }}\n </div>\n } @else {\n @if (getExpansionDetailLayout(detailState.schema) === 'tabs') {\n @let detailTabs = getExpansionDetailTabs(detailState.schema);\n @if (detailTabs.length) {\n <div class=\"pfx-expansion-detail-tabs\" role=\"tablist\" aria-label=\"Se\u00E7\u00F5es do detail\">\n @for (tab of detailTabs; track $index; let tabIndex = $index) {\n <button\n type=\"button\"\n class=\"pfx-expansion-detail-tab-btn\"\n role=\"tab\"\n [attr.id]=\"getExpansionDetailTabId(row, i, tab, tabIndex)\"\n [attr.aria-controls]=\"getExpansionDetailPanelId(row, i, tab, tabIndex)\"\n [attr.aria-selected]=\"isExpansionDetailTabActive(row, i, tab, tabIndex, detailTabs) ? 'true' : 'false'\"\n [attr.tabindex]=\"isExpansionDetailTabActive(row, i, tab, tabIndex, detailTabs) ? '0' : '-1'\"\n [class.is-active]=\"isExpansionDetailTabActive(row, i, tab, tabIndex, detailTabs)\"\n (click)=\"onExpansionDetailTabSelect(row, i, tab, tabIndex, $event)\"\n (keydown)=\"onExpansionDetailTabKeydown($event, row, i, tabIndex, detailTabs)\"\n >\n {{ getExpansionDetailNodeTitle(tab, 'Tab') }}\n </button>\n }\n </div>\n @for (tab of detailTabs; track $index; let tabIndex = $index) {\n @if (isExpansionDetailTabActive(row, i, tab, tabIndex, detailTabs)) {\n <div\n class=\"pfx-expansion-detail-tab-panel\"\n role=\"tabpanel\"\n [attr.id]=\"getExpansionDetailPanelId(row, i, tab, tabIndex)\"\n [attr.aria-labelledby]=\"getExpansionDetailTabId(row, i, tab, tabIndex)\"\n >\n @for (childNode of getExpansionDetailNodeChildren(tab); track $index) {\n <ng-container\n *ngTemplateOutlet=\"\n expansionDetailNodeTemplate;\n context: { $implicit: childNode, row: row, index: i }\n \"\n ></ng-container>\n }\n </div>\n }\n }\n } @else {\n <div class=\"pfx-expansion-detail-message\">\n Schema em layout tabs sem abas v\u00E1lidas.\n </div>\n }\n } @else {\n <div class=\"pfx-expansion-detail-stack\">\n @for (node of getExpansionDetailItems(detailState.schema); track $index) {\n <ng-container\n *ngTemplateOutlet=\"\n expansionDetailNodeTemplate;\n context: { $implicit: node, row: row, index: i }\n \"\n ></ng-container>\n }\n </div>\n }\n }\n\n <ng-template #expansionDetailNodeTemplate let-node let-row=\"row\" let-index=\"index\">\n @switch (getExpansionDetailNodeType(node)) {\n @case ('card') {\n <article class=\"pfx-expansion-node pfx-expansion-node-card\">\n <header class=\"pfx-expansion-node-card__header\">\n <h5 class=\"pfx-expansion-node-card__title\">\n {{ getExpansionDetailNodeTitle(node, 'Card') }}\n </h5>\n @if (node?.subtitle) {\n <p class=\"pfx-expansion-node-card__subtitle\">{{ node?.subtitle }}</p>\n }\n </header>\n @for (childNode of getExpansionDetailNodeChildren(node); track $index) {\n <ng-container\n *ngTemplateOutlet=\"\n expansionDetailNodeTemplate;\n context: { $implicit: childNode, row: row, index: index }\n \"\n ></ng-container>\n }\n </article>\n }\n @case ('value') {\n <div class=\"pfx-expansion-node pfx-expansion-node-value\">\n <span class=\"pfx-expansion-node-value__label\">\n {{ getExpansionDetailNodeTitle(node, 'Valor') }}\n </span>\n <strong class=\"pfx-expansion-node-value__content\">\n {{ getExpansionDetailValue(row, node) }}\n </strong>\n </div>\n }\n @case ('list') {\n <section class=\"pfx-expansion-node pfx-expansion-node-list\">\n <h6 class=\"pfx-expansion-node-list__title\">\n {{ getExpansionDetailNodeTitle(node, 'Lista') }}\n </h6>\n @let listItems = getExpansionDetailListItems(row, node);\n @if (listItems.length) {\n <ul>\n @for (entry of listItems; track $index) {\n <li>{{ entry }}</li>\n }\n </ul>\n } @else {\n <p class=\"pfx-expansion-node-placeholder\">Sem itens.</p>\n }\n </section>\n }\n @case ('richText') {\n <div\n class=\"pfx-expansion-node pfx-expansion-node-richtext\"\n [innerHTML]=\"getExpansionDetailRichText(node)\"\n ></div>\n }\n @case ('formRef') {\n <div class=\"pfx-expansion-node pfx-expansion-node-placeholder\">\n Formul\u00E1rio referenciado: <code>{{ node?.schemaId || node?.id || 'sem-id' }}</code>\n </div>\n }\n @case ('tableRef') {\n <div class=\"pfx-expansion-node pfx-expansion-node-placeholder\">\n Tabela referenciada: <code>{{ node?.schemaId || node?.id || 'sem-id' }}</code>\n </div>\n }\n @case ('chartRef') {\n <div class=\"pfx-expansion-node pfx-expansion-node-placeholder\">\n Gr\u00E1fico referenciado: <code>{{ node?.schemaId || node?.id || 'sem-id' }}</code>\n </div>\n }\n @case ('templateRef') {\n <div class=\"pfx-expansion-node pfx-expansion-node-placeholder\">\n Template referenciado: <code>{{ node?.id || node?.templateId || 'sem-id' }}</code>\n </div>\n }\n @case ('action') {\n <div class=\"pfx-expansion-node pfx-expansion-node-placeholder\">\n A\u00E7\u00E3o declarada: <code>{{ node?.id || node?.actionId || 'sem-id' }}</code>\n </div>\n }\n @case ('tab') {\n <div class=\"pfx-expansion-node pfx-expansion-node-placeholder\">\n Node <code>tab</code> fora de contexto de tabs.\n </div>\n }\n @default {\n <div class=\"pfx-expansion-node pfx-expansion-node-placeholder\">\n Node n\u00E3o suportado: <code>{{ getExpansionDetailNodeType(node) }}</code>\n </div>\n }\n }\n </ng-template>\n </section>\n </td>\n </ng-container>\n <tr\n mat-row\n *matRowDef=\"let row; columns: expansionDetailRowColumns; when: isExpansionDetailRow\"\n class=\"pfx-expansion-detail-row\"\n ></tr>\n }\n }\n</table>\n}\n\n<!-- Virtual rows path (header preserved above) -->\n@if (shouldRenderDataSurface() && !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]=\"getVirtMinHeightHostStyle()\"\n >\n <table\n class=\"mat-mdc-table\"\n [ngClass]=\"getTableElevationClassName()\"\n [style.width]=\"getVirtualTableWidthStyle()\"\n >\n <tbody>\n <tr class=\"mat-mdc-row\"\n *cdkVirtualFor=\"let row of dataSource.data; let i = index; trackBy: trackByRow\"\n [class.pfx-row-selected]=\"selection.isSelected(row)\"\n [attr.aria-selected]=\"config.behavior?.selection?.enabled ? (selection.isSelected(row) ? 'true' : 'false') : null\"\n [ngClass]=\"getRowClasses(row)\"\n [ngStyle]=\"getRowNgStyle(row)\"\n [matTooltip]=\"getRowTooltip(row) || null\"\n [matTooltipDisabled]=\"!getRowTooltip(row)\"\n [matTooltipPosition]=\"getRowTooltipPosition(row)\"\n [matTooltipShowDelay]=\"getRowTooltipShowDelay(row)\"\n (click)=\"onRowClicked(row, i, $event)\"\n (dblclick)=\"onRowDoubleClicked(row, i)\">\n <!-- Selection column -->\n @if (config.behavior?.selection?.enabled) { <td class=\"mat-mdc-cell\">\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 @for (column of visibleColumns; track column.field) {\n <td class=\"mat-mdc-cell\"\n [style.text-align]=\"getColumnTextAlignStyle(column)\"\n [style.width]=\"getColumnWidthStyle(column)\"\n [attr.style]=\"getColumnCellAttrStyle(column)\"\n [ngClass]=\"getCellClasses(row, column)\"\n [ngStyle]=\"getCellNgStyle(row, column)\">\n <div\n class=\"pfx-cell-content\"\n [ngClass]=\"getCellContentClasses(row, column)\"\n [ngStyle]=\"getCellContentNgStyle(row, column)\"\n >\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(row, column)\"\n [style.height.px]=\"getImageHeight(row, column)\"\n [class.shape-rounded]=\"getImageShape(row, column) === 'rounded'\"\n [class.shape-circle]=\"getImageShape(row, column) === 'circle'\"\n [style.object-fit]=\"getImageFit(row, column)\" />\n </ng-container>\n <ng-container *ngSwitchCase=\"'badge'\">\n <span class=\"pfx-badge\" [ngClass]=\"getBadgeClasses(row, column)\">\n @if (getBadgeIcon(row, column); as bi) { <mat-icon 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 </div>\n </td>\n }\n\n <!-- Actions column -->\n @if (config.actions?.row?.enabled) {\n <td class=\"mat-mdc-cell praxis-actions-cell\" [class.dense]=\"dense\" [style.width]=\"getRowActionsWidthStyle()\">\n <div\n class=\"praxis-actions-cell__content\"\n [class.praxis-actions-cell__content--discovering]=\"isRowDiscoveryPending(row)\"\n [attr.aria-busy]=\"isRowDiscoveryPending(row) ? 'true' : null\"\n >\n @if (config.actions?.row?.display === 'icons' || !config.actions?.row?.display) {\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <span\n class=\"praxis-row-action-anchor\"\n [class.praxis-row-action-anchor--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-row-action-anchor--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [matTooltip]=\"getActionTooltip(a, row)\"\n [matTooltipDisabled]=\"!getActionTooltip(a, row)\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"praxis-tooltip\"\n >\n <button mat-icon-button class=\"praxis-icon-btn\"\n [class.praxis-icon-btn--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-icon-btn--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event, getRowActionRuntimeOptions(a, row))\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\">\n <mat-icon [praxisIcon]=\"a.icon\"></mat-icon>\n </button>\n </span>\n </ng-container>\n }\n @if (config.actions?.row?.display === 'buttons') {\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <span\n class=\"praxis-row-action-anchor\"\n [class.praxis-row-action-anchor--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-row-action-anchor--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [matTooltip]=\"getActionTooltip(a, row)\"\n [matTooltipDisabled]=\"!getActionTooltip(a, row)\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"praxis-tooltip\"\n >\n <button mat-flat-button class=\"praxis-row-btn\"\n [class.praxis-row-btn--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-row-btn--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event, getRowActionRuntimeOptions(a, row))\"\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 </span>\n </ng-container>\n }\n @if (hasOverflowRowActions(row)) {\n <button mat-icon-button class=\"praxis-icon-btn praxis-more-btn\"\n [matMenuTriggerFor]=\"rowMoreMenuV\"\n (menuOpened)=\"onRowOverflowMenuOpened(row)\"\n [color]=\"getRowMenuButtonColor() || null\"\n aria-label=\"Mais a\u00E7\u00F5es\"\n [matTooltip]=\"getRowMenuTooltip(row)\"\n matTooltipPosition=\"above\">\n <mat-icon [praxisIcon]=\"getRowMenuIcon()\"></mat-icon>\n </button>\n }\n <mat-menu #rowMoreMenuV=\"matMenu\" xPosition=\"before\">\n @if (hasRowOverflowMenuLoadingState(row)) {\n <button mat-menu-item disabled>\n <mat-icon>hourglass_empty</mat-icon>\n <span>{{ getRowOverflowMenuLoadingLabel() }}</span>\n </button>\n }\n <ng-container *ngFor=\"let a of getOverflowRowActions(row); trackBy: trackAction\">\n <button mat-menu-item (click)=\"onRowAction(getActionId(a), row, $event, getRowActionRuntimeOptions(a, row))\" [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 @if (hasRowOverflowMenuEmptyState(row)) {\n <button mat-menu-item disabled>\n <mat-icon>info</mat-icon>\n <span>{{ getRowOverflowMenuEmptyLabel(row) }}</span>\n </button>\n }\n </mat-menu>\n </div>\n </td>\n }\n </tr>\n </tbody>\n </table>\n </cdk-virtual-scroll-viewport>\n}\n\n</div>\n@if (\n shouldRenderDataSurface()\n && !schemaError\n && !dataError\n && shouldRenderFloatingBulkActions()\n && getFloatingBulkActions().length\n && !shouldHideFloatingBulkActions()\n) {\n <div [class]=\"'praxis-floating-bulk-actions ' + getFloatingBulkPositionClass()\">\n @for (action of getFloatingBulkActions(); track getActionId(action)) {\n <button\n mat-mini-fab\n [color]=\"action.color || 'primary'\"\n [disabled]=\"isFloatingBulkActionDisabled(action)\"\n (click)=\"onToolbarAction({ action: getActionId(action), actionConfig: action })\"\n [attr.aria-label]=\"action.label || getActionId(action)\"\n [matTooltip]=\"action.label || getActionId(action)\"\n matTooltipPosition=\"left\"\n >\n <mat-icon [praxisIcon]=\"action.icon || 'done_all'\"></mat-icon>\n </button>\n }\n </div>\n}\n<!-- Barra de a\u00E7\u00F5es no rodap\u00E9 (opcional) -->\n@if (shouldRenderDataSurface() && !schemaError && !dataError && showToolbar && shouldRenderFooterToolbar()) {\n <praxis-table-toolbar\n [config]=\"config\"\n [backgroundColor]=\"getToolbarActionsBackgroundColor()\"\n [style.--pfx-filter-h]=\"getToolbarLayoutHeightHostStyle()\"\n [class.footer-flat]=\"hasBottomPaginator()\"\n class=\"praxis-toolbar-footer\"\n placement=\"footer\"\n [showMain]=\"shouldShowFooterToolbarMain()\"\n [showEndActions]=\"shouldShowFooterToolbarEndActions()\"\n [showReset]=\"shouldShowFooterToolbarReset()\"\n [showActionsGroup]=\"shouldShowToolbarActionsBottom()\"\n [showMobileActions]=\"shouldShowToolbarActionsBottom()\"\n [evaluationContext]=\"getToolbarEvaluationContext()\"\n (toolbarAction)=\"onToolbarAction($event)\"\n >\n <praxis-filter\n *ngIf=\"shouldShowFooterToolbarMain() && shouldRenderAdvancedFilter()\"\n advancedFilter\n [resourcePath]=\"getAdvancedFilterResourcePath()\"\n [filterId]=\"tableId\"\n [formId]=\"tableId\"\n [persistenceKey]=\"getAdvancedFilterPersistenceKey()\"\n [fieldMetadata]=\"getAdvancedFilterFieldMetadata()\"\n [enableCustomization]=\"enableCustomization\"\n [alwaysVisibleFields]=\"config.behavior?.filtering?.advancedFilters?.settings?.alwaysVisibleFields\"\n [alwaysVisibleFieldMetadataOverrides]=\"config.behavior?.filtering?.advancedFilters?.settings?.alwaysVisibleFieldMetadataOverrides ?? {}\"\n [selectedFieldIds]=\"config.behavior?.filtering?.advancedFilters?.settings?.selectedFieldIds ?? []\"\n [allowSaveTags]=\"config.behavior?.filtering?.advancedFilters?.settings?.allowSaveTags\"\n [changeDebounceMs]=\"config.behavior?.filtering?.advancedFilters?.settings?.changeDebounceMs ?? 300\"\n [i18n]=\"getFilterI18n()\"\n [mode]=\"'filter'\"\n [showFilterSettings]=\"enableCustomization\"\n (change)=\"onAdvancedFilterChange($event)\"\n (requestSearch)=\"onAdvancedFilterSubmit($event)\"\n (clear)=\"onAdvancedFilterClear()\"\n ></praxis-filter>\n <ng-container *ngIf=\"shouldShowFooterToolbarMain() && shouldRenderAdvancedFilter()\">\n <ng-content select=\"[advancedFilter]\"></ng-content>\n </ng-container>\n <ng-container *ngIf=\"shouldShowFooterToolbarMain() && shouldRenderAdvancedFilter()\">\n <ng-content select=\"[toolbar]\"></ng-content>\n </ng-container>\n @if (shouldShowFooterToolbarMain() && shouldRenderAdvancedFilter()) {\n @defer (on idle) {\n @if (aiAdapter) {\n <praxis-ai-assistant\n [adapter]=\"aiAdapter\"\n end-actions\n ></praxis-ai-assistant>\n }\n }\n }\n <button\n *ngIf=\"shouldShowFooterToolbarMain() && shouldRenderAdvancedFilter() && enableCustomization\"\n end-actions\n mat-icon-button\n color=\"primary\"\n data-role=\"table-settings\"\n data-testid=\"table-settings-trigger\"\n (click)=\"openTableSettings()\"\n aria-label=\"Configura\u00E7\u00F5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\"\n matBadgeSize=\"small\"\n matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u2014 Reconciliar' : 'Configura\u00E7\u00F5es'\"\n matTooltipPosition=\"below\"\n >\n <mat-icon>settings</mat-icon>\n </button>\n </praxis-table-toolbar>\n}\n<!-- Paginadores (top/bottom) -->\n@if (shouldRenderDataSurface() && !schemaError && !dataError && getPaginationEnabled() && (config.behavior?.pagination?.position === 'top' || config.behavior?.pagination?.position === 'both')) {\n <mat-paginator\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\n@if (shouldRenderDataSurface() && !schemaError && !dataError && getPaginationEnabled() && (config.behavior?.pagination?.position === 'bottom' || config.behavior?.pagination?.position === 'both' || !config.behavior?.pagination?.position)) {\n <mat-paginator\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 [class.footer-stack]=\"shouldShowToolbarActionsBottom()\"\n >\n </mat-paginator>\n}\n", styles: ["@charset \"UTF-8\";table{width:100%}.praxis-visually-hidden-status{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.praxis-column-reorder-status{margin:8px 0;padding:10px 12px;border-radius:8px;border:1px solid var(--p-table-border-color);background:var(--p-table-row-hover-bg);color:var(--p-table-header-fg);font-size:12px;line-height:1.4;box-shadow:0 4px 12px #00000014;animation:pfx-column-reorder-status-in var(--p-table-drag-status-enter-duration, .18s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}@keyframes pfx-column-reorder-status-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.praxis-actions-cell{height:100%;padding-inline:12px;white-space:nowrap}.pfx-expansion-col-header,.pfx-expansion-col-cell{width:52px;min-width:52px;text-align:center}.pfx-expansion-toggle:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pfx-expansion-detail-row{background:var(--md-sys-color-surface-container-low)}.pfx-expansion-detail-cell{padding:0!important;border-bottom:1px solid var(--p-table-border-color)}.pfx-expansion-detail-panel{padding:12px 16px;border-left:3px solid var(--md-sys-color-primary)}.pfx-expansion-detail-panel.pfx-expansion-motion-none{animation:none;transition:none}.pfx-expansion-detail-panel.pfx-expansion-motion-subtle-slide{animation:pfx-expansion-subtle-slide-in var(--pfx-expansion-motion-duration, .16s) var(--pfx-expansion-motion-easing, cubic-bezier(.2, 0, 0, 1))}.pfx-expansion-detail-panel.pfx-expansion-motion-accordion{animation:pfx-expansion-accordion-in var(--pfx-expansion-motion-duration, .18s) var(--pfx-expansion-motion-easing, cubic-bezier(.2, 0, 0, 1));transform-origin:top center}.pfx-expansion-detail-panel.pfx-expansion-motion-fade-scale{animation:pfx-expansion-fade-scale-in var(--pfx-expansion-motion-duration, .16s) var(--pfx-expansion-motion-easing, cubic-bezier(.2, 0, 0, 1));transform-origin:top center}@keyframes pfx-expansion-subtle-slide-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}@keyframes pfx-expansion-accordion-in{0%{opacity:0;transform:scaleY(.96)}to{opacity:1;transform:scaleY(1)}}@keyframes pfx-expansion-fade-scale-in{0%{opacity:0;transform:translateY(-2px) scale(.985)}to{opacity:1;transform:translateY(0) scale(1)}}.pfx-expansion-detail-schema{margin:0;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;line-height:1.4;white-space:pre-wrap;word-break:break-word;color:var(--md-sys-color-on-surface-variant)}.pfx-expansion-detail-message{font-size:13px;color:var(--md-sys-color-on-surface-variant)}.pfx-expansion-detail-message--error{color:var(--md-sys-color-error)}.pfx-expansion-detail-stack{display:grid;gap:10px}.pfx-expansion-detail-tabs{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:10px}.pfx-expansion-detail-tab-btn{border:1px solid var(--p-table-border-color);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);border-radius:999px;padding:6px 12px;font-size:12px;line-height:1.2;cursor:pointer}.pfx-expansion-detail-tab-btn.is-active{border-color:var(--md-sys-color-primary);color:var(--md-sys-color-primary);font-weight:600}.pfx-expansion-detail-tab-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pfx-expansion-detail-tab-panel{display:grid;gap:10px}.pfx-expansion-node{border:1px solid var(--p-table-border-color);border-radius:8px;padding:10px 12px;background:var(--md-sys-color-surface)}.pfx-expansion-node-card__header{margin-bottom:8px}.pfx-expansion-node-card__title{margin:0;font-size:14px;line-height:1.3}.pfx-expansion-node-card__subtitle{margin:4px 0 0;font-size:12px;color:var(--md-sys-color-on-surface-variant)}.pfx-expansion-node-value{display:flex;align-items:baseline;gap:8px}.pfx-expansion-node-value__label{color:var(--md-sys-color-on-surface-variant);font-size:12px}.pfx-expansion-node-value__content{font-size:14px}.pfx-expansion-node-list__title{margin:0 0 6px;font-size:13px}.pfx-expansion-node-list ul{margin:0;padding-left:18px}.pfx-expansion-node-richtext :where(p,ul,ol,h1,h2,h3,h4,h5,h6){margin-top:0;margin-bottom:8px}.pfx-expansion-node-placeholder{font-size:12px;color:var(--md-sys-color-on-surface-variant)}:host.density-compact{--p-header-padding: 8px 12px;--p-cell-padding: 8px 12px;--p-actions-btn-size: 32px;--p-actions-icon-size: 18px}:host.density-comfortable{--p-header-padding: 12px 16px;--p-cell-padding: 12px 16px;--p-actions-btn-size: 40px;--p-actions-icon-size: 22px}:host.density-spacious{--p-header-padding: 16px 20px;--p-cell-padding: 16px 20px;--p-actions-btn-size: 44px;--p-actions-icon-size: 24px}:host.density-compact ::ng-deep .mat-mdc-cell{padding:var(--p-cell-padding, 8px 12px)}:host.density-comfortable ::ng-deep .mat-mdc-cell{padding:var(--p-cell-padding, 12px 16px)}:host.density-spacious ::ng-deep .mat-mdc-cell{padding:var(--p-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%;transition:opacity .12s ease}.praxis-actions-cell.dense .praxis-actions-cell__content{gap:6px}.praxis-actions-cell__content--discovering{opacity:.78}.praxis-row-action-anchor{display:inline-flex;align-items:center}.praxis-row-action-anchor--workflow{position:relative}.praxis-row-action-anchor--workflow:after{content:\"\";position:absolute;right:4px;top:4px;width:6px;height:6px;border-radius:999px;background:var(--md-sys-color-secondary);opacity:.9;pointer-events:none}.praxis-row-action-anchor--blocked:after{background:var(--md-sys-color-error)}.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)}.praxis-icon-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);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-icon-btn--workflow{background:color-mix(in srgb,var(--md-sys-color-secondary-container) 36%,transparent)}.praxis-icon-btn--blocked{background:color-mix(in srgb,var(--md-sys-color-error-container) 42%,transparent)}.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-row-btn--workflow{box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--md-sys-color-secondary) 28%,transparent)}.praxis-row-btn--blocked{box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--md-sys-color-error) 28%,transparent)}.praxis-icon-btn.destructive mat-icon{color:var(--md-sys-color-error)}.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.stacked{margin:0}.praxis-table-header>praxis-table-toolbar{flex:1 0 100%}.praxis-floating-bulk-actions{position:fixed;z-index:var(--praxis-layer-floating-local, 200);display:inline-flex;align-items:center;gap:8px;padding:8px;border-radius:999px;background:var(--md-sys-color-surface-container-highest);box-shadow:0 8px 20px #00000029}.praxis-floating-bulk-actions.pos-bottom-right{right:20px;bottom:20px}.praxis-floating-bulk-actions.pos-bottom-left{left:20px;bottom:20px}.praxis-floating-bulk-actions.pos-top-right{right:20px;top:20px}.praxis-floating-bulk-actions.pos-top-left{left:20px;top:20px}@media(max-width:768px){.praxis-floating-bulk-actions{gap:6px;padding:6px}.praxis-floating-bulk-actions.pos-bottom-right{right:12px;left:auto;bottom:12px}.praxis-floating-bulk-actions.pos-bottom-left{left:12px;right:auto;bottom:12px}.praxis-floating-bulk-actions.pos-top-right{right:12px;left:auto;top:12px}.praxis-floating-bulk-actions.pos-top-left{left:12px;right:auto;top:12px}}:host{display:block;width:100%;min-width:0;max-width:100%;--pfx-toolbar-pad-y: 6px;--pfx-toolbar-pad-x: 12px;--p-table-bg: var(--md-sys-color-surface-container-highest);--p-table-text-color: var(--md-sys-color-on-surface);--p-table-header-bg: var(--md-sys-color-surface-container-highest);--p-table-header-fg: var(--md-sys-color-on-surface);--p-table-border-color: var(--md-sys-color-outline-variant);--p-table-row-even-bg: var(--md-sys-color-surface-container);--p-table-row-hover-bg: var(--md-sys-color-surface-container-high);--p-table-row-selected-bg: var(--md-sys-color-primary-container);--p-table-badge-soft-primary-bg: var(--md-sys-color-primary-container);--p-table-badge-soft-primary-fg: var(--md-sys-color-on-primary-container);--p-table-badge-soft-accent-bg: var(--md-sys-color-secondary-container);--p-table-badge-soft-accent-fg: var(--md-sys-color-on-secondary-container);--p-table-badge-soft-warn-bg: var(--md-sys-color-error-container);--p-table-badge-soft-warn-fg: var(--md-sys-color-on-error-container);--p-table-state-success-bg: var(--md-sys-color-tertiary-container);--p-table-state-success-fg: var(--md-sys-color-on-tertiary-container);--p-table-state-warning-bg: var(--md-sys-color-secondary-container);--p-table-state-warning-fg: var(--md-sys-color-on-secondary-container);--p-table-state-danger-bg: var(--md-sys-color-error-container);--p-table-state-danger-fg: var(--md-sys-color-on-error-container);--p-table-state-highlight-bg: var(--md-sys-color-primary-container);--p-table-state-highlight-fg: var(--md-sys-color-on-primary-container);--p-table-drag-handle-size: 14px;--p-table-drag-handle-color: var(--md-sys-color-on-surface-variant);--p-table-drag-handle-hover-color: var(--md-sys-color-on-surface);--p-table-drag-handle-base-opacity: 0;--p-table-drag-handle-visible-opacity: .72;--p-table-drag-handle-active-opacity: 1;--p-table-drag-handle-transition-duration: .16s;--p-table-reorder-transition-duration: .16s;--p-table-drag-preview-scale: 1.01;--p-table-drag-status-enter-duration: .18s;--p-table-drag-preview-shadow: 0 14px 32px rgba(0, 0, 0, .28), 0 0 0 1px var(--p-table-border-color)}:host ::ng-deep .mat-mdc-table{background:var(--p-table-bg);color:var(--p-table-text-color);border-radius:12px 12px 0 0;width:100%;box-shadow:var(--p-table-surface-shadow);transition:box-shadow var(--p-table-selection-transition-duration, .18s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}:host ::ng-deep .mat-mdc-table:hover{box-shadow:var(--p-table-surface-shadow-hover, var(--p-table-surface-shadow))}:host ::ng-deep .mat-mdc-table.table-stack-top{border-top-left-radius:0;border-top-right-radius:0}:host ::ng-deep .praxis-toolbar-footer{border:0 solid var(--p-table-border-color);border-top:0;border-radius:0;background:var(--p-table-bg)}:host ::ng-deep .mat-mdc-paginator.footer-stack{border-top-left-radius:0;border-top-right-radius:0;border-top:0}:host ::ng-deep .mat-mdc-paginator.footer-stack .mat-mdc-paginator-container{padding:8px 12px}:host [data-role=table-settings].mat-mdc-icon-button{--mdc-icon-button-icon-color: var(--md-sys-color-primary);color:var(--md-sys-color-primary)}.pfx-link{color:var(--md-sys-color-primary);text-decoration:underline;cursor:pointer}.pfx-chip{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:10px;font-size:12px;line-height:1;border:1px solid transparent}.pfx-chip-icon{font-size:14px;width:14px;height:14px}.pfx-chip--outlined{background:transparent;border-color:var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface)}.pfx-chip--filled-primary{background:var(--md-sys-color-primary);color:var(--md-sys-color-on-primary)}.pfx-chip--filled-accent{background:var(--md-sys-color-secondary);color:var(--md-sys-color-on-secondary)}.pfx-chip--filled-warn{background:var(--md-sys-color-error);color:var(--md-sys-color-on-error)}.pfx-chip--soft-primary{background:var(--p-table-badge-soft-primary-bg);color:var(--p-table-badge-soft-primary-fg)}.pfx-chip--soft-accent{background:var(--p-table-badge-soft-accent-bg);color:var(--p-table-badge-soft-accent-fg)}.pfx-chip--soft-warn{background:var(--p-table-badge-soft-warn-bg);color:var(--p-table-badge-soft-warn-fg)}.pfx-progress{position:relative;width:100%;max-width:140px;height:8px;background:var(--md-sys-color-surface-container-highest);border-radius:4px;overflow:hidden;display:inline-block;vertical-align:middle}.pfx-progress-bar{height:100%;background:var(--md-sys-color-primary);transition:width .2s ease}.pfx-progress-label{margin-left:8px;font-size:12px;opacity:.8;display:inline-block;vertical-align:middle}.pfx-avatar{display:inline-flex;align-items:center;justify-content:center;background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface);border-radius:4px;overflow:hidden;font-weight:600}.pfx-avatar.shape-rounded{border-radius:8px}.pfx-avatar.shape-circle{border-radius:999px}.pfx-avatar--initials{text-transform:uppercase;font-size:12px}.pfx-cell-compose{display:inline-flex;align-items:center;gap:6px}.pfx-cell-compose.dir-col{flex-direction:column;align-items:stretch}.pfx-cell-compose.align-start{justify-content:flex-start}.pfx-cell-compose.align-center{justify-content:center}.pfx-cell-compose.align-end{justify-content:flex-end}.pfx-cell-compose.wrap{flex-wrap:wrap}.pfx-cell-compose.ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.px-scroll-viewport{width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;overscroll-behavior-x:contain}.px-scroll-viewport.scroll-none{overflow-x:visible}.px-scroll-viewport.scroll-auto ::ng-deep .mat-mdc-table{width:max-content;min-width:100%}.px-scroll-viewport.scroll-wrap ::ng-deep .mat-mdc-header-cell,.px-scroll-viewport.scroll-wrap ::ng-deep .mat-mdc-cell{white-space:normal;text-overflow:initial}.px-scroll-viewport.scroll-wrap ::ng-deep .mat-mdc-table{width:100%}:host ::ng-deep .mat-mdc-header-row{position:sticky;top:0;z-index:var(--praxis-layer-sticky-local, 100);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}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable .mat-sort-header-container{display:flex;align-items:center;width:100%;gap:4px;cursor:inherit}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable{-webkit-user-select:none;user-select:none;cursor:grab;padding-left:0!important}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable.cdk-drag-dragging{cursor:grabbing}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable .mat-sort-header-content{display:inline-flex;align-items:center;gap:4px;flex:1 1 auto;min-width:0}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-header-label{display:inline-flex;align-items:center;gap:4px;flex:1 1 auto;min-width:0}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-header-label-text{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-column-drag-handle{-webkit-appearance:none;appearance:none;border:0;background:transparent;color:var(--p-table-drag-handle-color);width:var(--p-table-drag-handle-size);min-width:var(--p-table-drag-handle-size);flex:0 0 var(--p-table-drag-handle-size);height:var(--p-table-drag-handle-size);padding:0;display:inline-flex;align-items:center;justify-content:center;border-radius:0;cursor:inherit;pointer-events:none;touch-action:none;opacity:var(--p-table-drag-handle-base-opacity, .42);transform:none;order:-1;margin-inline-end:0;transition:opacity var(--p-table-drag-handle-transition-duration, .16s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1)),color var(--p-table-drag-handle-transition-duration, .16s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable:hover .praxis-column-drag-handle,:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable:focus-within .praxis-column-drag-handle{opacity:var(--p-table-drag-handle-visible-opacity, .72);color:var(--p-table-drag-handle-hover-color, var(--p-table-drag-handle-color))}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-column-drag-handle:active{opacity:var(--p-table-drag-handle-active-opacity, 1);cursor:grabbing}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-column-drag-handle .mat-icon{font-size:14px;width:14px;height:14px;line-height:14px;transition:transform .18s var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable:hover .praxis-column-drag-handle .mat-icon{transform:none}:host ::ng-deep .pfx-column-drag-indicator .cdk-drop-list-dragging .mat-mdc-header-cell:not(.cdk-drag-placeholder){transition:transform var(--p-table-reorder-transition-duration, .18s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}:host ::ng-deep .pfx-column-drag-indicator .mat-mdc-header-cell.cdk-drag-animating{transition:transform var(--p-table-reorder-transition-duration, .16s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}.pfx-column-drag-preview{box-sizing:border-box;display:flex;align-items:center;border-radius:10px;border:1px solid var(--p-table-border-color);background:linear-gradient(135deg,var(--p-table-header-bg) 0%,var(--p-table-row-hover-bg) 100%);color:var(--p-table-header-fg);box-shadow:var(--p-table-drag-preview-shadow);transform:scale(var(--p-table-drag-preview-scale, 1.01));pointer-events:none;z-index:var(--praxis-layer-authoring-hover, 300)}.pfx-column-drag-preview .praxis-column-drag-handle,.pfx-column-drag-preview .mat-sort-header-arrow,.pfx-column-drag-preview .mat-sort-header-indicator,.pfx-column-drag-preview .mat-sort-header-stem,.pfx-column-drag-preview .mat-sort-header-pointer,.pfx-column-drag-preview .mat-sort-header-pointer-left,.pfx-column-drag-preview .mat-sort-header-pointer-right{display:none!important}.pfx-column-drag-preview .mat-sort-header-container{display:flex;align-items:center;width:100%;min-height:100%;padding-right:0!important}.pfx-column-drag-preview .mat-sort-header-content,.pfx-column-drag-preview .praxis-header-label{display:inline-flex;align-items:center;min-width:0;width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host ::ng-deep .pfx-column-drag-indicator .cdk-drag-placeholder{opacity:1;border:1px dashed var(--p-table-border-color);background:var(--p-table-row-hover-bg)}:host ::ng-deep .pfx-column-drag-indicator .cdk-drag-placeholder *{opacity:0}:host ::ng-deep .pfx-column-drag-indicator .mat-mdc-header-cell.cdk-drag-dragging{opacity:.58}@media(prefers-reduced-motion:reduce){:host ::ng-deep .pfx-column-drag-indicator .cdk-drop-list-dragging .mat-mdc-header-cell:not(.cdk-drag-placeholder){transition:none}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-column-drag-handle,:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-column-drag-handle .mat-icon,.pfx-column-drag-preview{transition:none;transform:none}.praxis-column-reorder-status{animation:none}.pfx-expansion-detail-panel{animation:none!important;transition:none!important;transform:none!important}:host ::ng-deep .mat-mdc-row{transition:none}:host ::ng-deep .mat-mdc-table{transition:none}}.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 .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable .mat-sort-header-container{padding-right:12px}@media(pointer:coarse){:host{--p-table-drag-handle-size: 18px;--p-table-drag-handle-base-opacity: .56;--p-table-drag-handle-visible-opacity: .92}}:host ::ng-deep .mat-mdc-cell{color:var(--p-table-text-color);font-size:var(--p-cell-font-size, inherit);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}:host ::ng-deep .mat-mdc-cell .pfx-cell-content,:host ::ng-deep .mat-cell .pfx-cell-content{display:inline-flex;align-items:center;gap:6px;width:100%;min-width:0;overflow:hidden}:host ::ng-deep .mat-mdc-row:hover{background:var(--p-table-row-hover-bg)}:host ::ng-deep .mat-mdc-row{transition:background-color var(--p-table-hover-transition-duration, .18s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1)),box-shadow var(--p-table-selection-transition-duration, .18s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}:host ::ng-deep .mat-mdc-row:nth-child(2n){background:var(--p-table-row-even-bg)}:host ::ng-deep .mat-mdc-row:nth-child(2n):hover{background:var(--p-table-row-hover-bg)}:host ::ng-deep .mat-mdc-row.pfx-row-selected,:host ::ng-deep .mat-mdc-row.pfx-row-selected:hover{background:var(--p-table-row-selected-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-error{display:flex;align-items:center;gap:12px;padding:12px;margin:8px 0;border:1px solid var(--md-sys-color-error);border-radius:8px}.ptable-error__content{flex:1}.ptable-error__title{font-weight:600}.ptable-info-banner{display:flex;gap:12px;align-items:center;padding:8px 12px;margin:8px 0;border-radius:8px;border:1px solid var(--md-sys-color-primary);background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.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);border:1px solid var(--md-sys-color-outline-variant)}.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(--md-sys-color-primary);color:var(--md-sys-color-on-primary)}.pfx-badge--filled-accent{background:var(--md-sys-color-secondary);color:var(--md-sys-color-on-secondary)}.pfx-badge--filled-warn{background:var(--md-sys-color-error);color:var(--md-sys-color-on-error)}.pfx-badge--outlined{background:transparent;border-color:var(--md-sys-color-outline-variant);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: i2$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2$1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i2$1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i2$1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i10$3.CdkScrollable, selector: "[cdk-scrollable], [cdkScrollable]" }, { kind: "directive", type: i11$1.ɵɵCdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i11$1.ɵɵCdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i11$1.ɵɵCdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i12.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i12.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "ngmodule", type: MatBadgeModule }, { kind: "directive", type: i15$2.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { 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.MatMiniFabButton, selector: "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", 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: 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: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i17.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: i17.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i17.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: MatPaginatorModule }, { kind: "component", type: i18.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i6$1.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "ngmodule", type: MatSortModule }, { kind: "directive", type: i20.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i20.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { 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: MatTooltipModule }, { kind: "directive", type: i10.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: PraxisFilter, selector: "praxis-filter", inputs: ["resourcePath", "fieldMetadata", "filterId", "formId", "componentInstanceId", "mode", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "enableCustomization", "value", "alwaysVisibleFields", "alwaysVisibleFieldMetadataOverrides", "selectedFieldIds", "tags", "allowSaveTags", "persistenceKey", "disablePersistence", "i18n", "changeDebounceMs", "showFilterSettings", "showAdvancedButton", "showAddButton", "showClearButton", "showSearchButton", "confirmTagDelete", "placeBooleansInActions", "showToggleLabels", "useInlineSelectVariant", "useInlineSearchableSelectVariant", "useInlineMultiSelectVariant", "useInlineInputVariant", "useInlineToggleVariant", "useInlineRangeVariant", "useInlineDateVariant", "useInlineDateRangeVariant", "useInlineTimeVariant", "useInlineTimeRangeVariant", "useInlineTreeSelectVariant", "alwaysMinWidth", "alwaysColsMd", "alwaysColsLg", "tagColor", "tagVariant", "tagButtonColor", "actionsButtonColor", "actionsVariant", "overlayVariant", "overlayBackdrop", "advancedOpenMode", "advancedClearButtonsEnabled"], outputs: ["submit", "change", "clear", "modeChange", "requestSearch", "tagsChange", "selectedFieldIdsChange", "metaChanged", "schemaStatusChange"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisTableToolbar, selector: "praxis-table-toolbar", inputs: ["config", "backgroundColor", "placement", "showMain", "showActionsGroup", "showEndActions", "showMobileActions", "showReset", "evaluationContext"], outputs: ["toolbarAction", "reset"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline", "tone"] }, { kind: "component", type: TableRatingCellComponent, selector: "praxis-table-rating", inputs: ["itemsCount", "value", "size", "ratingColor", "outlineColor", "ariaLabel"] }], deferBlockDependencies: [() => [import('@praxisui/ai').then(m => m.PraxisAiAssistantComponent)], () => [import('@praxisui/ai').then(m => m.PraxisAiAssistantComponent)], () => [import('@praxisui/ai').then(m => m.PraxisAiAssistantComponent)], () => [import('@praxisui/ai').then(m => m.PraxisAiAssistantComponent)]] });
47081
+ ], queries: [{ propertyName: "toolbar", first: true, predicate: PraxisTableToolbar, descendants: true }, { propertyName: "projectedFilter", first: true, predicate: ["projectedFilter"], descendants: true }], viewQueries: [{ propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }, { propertyName: "sort", first: true, predicate: MatSort, descendants: true }, { propertyName: "materialTable", first: true, predicate: MatTable, descendants: true }, { propertyName: "internalFilter", first: true, predicate: PraxisFilter, descendants: true }], usesOnChanges: true, ngImport: i0, template: "@if (shouldShowEmptyState()) {\n <praxis-empty-state-card\n icon=\"link\"\n [title]=\"'Conecte a tabela \u00C3\u00A0 fonte de dados'\"\n [description]=\"'Informe a rota do recurso da API para carregar colunas e dados automaticamente.'\"\n [primaryAction]=\"{ label: 'Conectar \u00C3\u00A0 fonte de dados', icon: 'bolt', action: openQuickConnect.bind(this) }\"\n ></praxis-empty-state-card>\n}\n\n<!-- Error State with Quick Connect CTA -->\n@if (isRemoteMode() && (schemaError || dataError)) {\n<div class=\"ptable-error\" role=\"alert\" aria-live=\"assertive\">\n <mat-icon color=\"warn\" aria-hidden=\"true\">error</mat-icon>\n <div class=\"ptable-error__content\">\n <praxis-rich-content\n rootClassName=\"ptable-error__title\"\n [nodes]=\"getTableChromeTextRichContentNodes('Erro', 'ptable-error__title-text')\"\n ></praxis-rich-content>\n <praxis-rich-content\n rootClassName=\"ptable-error__message\"\n [nodes]=\"getTableChromeTextRichContentNodes(errorMessage || 'Ocorreu um erro ao carregar a tabela.', 'ptable-error__message-text')\"\n ></praxis-rich-content>\n </div>\n <button mat-flat-button color=\"primary\" (click)=\"openQuickConnect()\">\n <mat-icon>bolt</mat-icon>\n <praxis-rich-content\n rootClassName=\"ptable-error__action-label\"\n [nodes]=\"getTableChromeTextRichContentNodes('Conectar a recurso', 'ptable-error__action-label-text')\"\n ></praxis-rich-content>\n </button>\n @if (enableCustomization) {\n <button\n mat-icon-button\n color=\"primary\"\n data-role=\"table-settings\"\n data-testid=\"table-settings-trigger\"\n (click)=\"openTableSettings()\"\n [attr.aria-label]=\"getTableSettingsLabel()\"\n [matTooltip]=\"getTableSettingsLabel()\"\n matTooltipPosition=\"below\"\n >\n <mat-icon>settings</mat-icon>\n </button>\n }\n @if (!schemaError) {\n <button mat-stroked-button (click)=\"retryData()\">\n <praxis-rich-content\n rootClassName=\"ptable-error__action-label\"\n [nodes]=\"getTableChromeTextRichContentNodes('Tentar novamente', 'ptable-error__action-label-text')\"\n ></praxis-rich-content>\n </button>\n }\n @if (schemaError) {\n <button mat-stroked-button (click)=\"reloadSchema()\">\n <praxis-rich-content\n rootClassName=\"ptable-error__action-label\"\n [nodes]=\"getTableChromeTextRichContentNodes('Recarregar colunas', 'ptable-error__action-label-text')\"\n ></praxis-rich-content>\n </button>\n }\n </div>\n}\n\n<!-- Inline banner for schema change (only in edit mode) -->\n@if (shouldShowOutdatedInline()) {\n<div class=\"ptable-info-banner\" role=\"status\" aria-live=\"polite\">\n <praxis-rich-content\n rootClassName=\"text\"\n [nodes]=\"getTableChromeTextRichContentNodes('O schema do servidor mudou. Reconciliar agora?', 'ptable-info-banner__text')\"\n ></praxis-rich-content>\n <div class=\"actions\">\n <button mat-stroked-button color=\"primary\" (click)=\"onReconcileRequested()\">\n <mat-icon>sync</mat-icon>\n <praxis-rich-content\n rootClassName=\"ptable-info-banner__action-label\"\n [nodes]=\"getTableChromeTextRichContentNodes('Reconciliar', 'ptable-info-banner__action-label-text')\"\n ></praxis-rich-content>\n </button>\n <button mat-button (click)=\"onSnoozeOutdated()\">\n <praxis-rich-content\n rootClassName=\"ptable-info-banner__action-label\"\n [nodes]=\"getTableChromeTextRichContentNodes('Lembrar depois', 'ptable-info-banner__action-label-text')\"\n ></praxis-rich-content>\n </button>\n <button mat-button (click)=\"onIgnoreOutdated()\">\n <praxis-rich-content\n rootClassName=\"ptable-info-banner__action-label\"\n [nodes]=\"getTableChromeTextRichContentNodes('Ignorar', 'ptable-info-banner__action-label-text')\"\n ></praxis-rich-content>\n </button>\n </div>\n </div>\n}\n\n@if (shouldRenderDataSurface() && !schemaError && !dataError && toolbarV2) {\n <div class=\"praxis-table-header\" [class.edit-mode]=\"enableCustomization\" [class.stacked]=\"showToolbar\">\n @if (showToolbar && shouldShowToolbarTopPlacement()) {\n <praxis-table-toolbar\n [config]=\"config\"\n [backgroundColor]=\"getToolbarActionsBackgroundColor()\"\n [style.--pfx-filter-h]=\"getToolbarLayoutHeightHostStyle()\"\n [evaluationContext]=\"getToolbarEvaluationContext()\"\n [showActionsGroup]=\"shouldShowToolbarActionsTop()\"\n [showMobileActions]=\"shouldShowToolbarActionsTop()\"\n (toolbarAction)=\"onToolbarAction($event)\"\n (reset)=\"onResetPreferences()\"\n >\n @if (shouldRenderAdvancedFilter()) {\n <praxis-filter\n advancedFilter\n [resourcePath]=\"getAdvancedFilterResourcePath()\"\n [filterId]=\"tableId\"\n [formId]=\"tableId\"\n [persistenceKey]=\"getAdvancedFilterPersistenceKey()\"\n [fieldMetadata]=\"getAdvancedFilterFieldMetadata()\"\n [enableCustomization]=\"enableCustomization\"\n \n [alwaysVisibleFields]=\"config.behavior?.filtering?.advancedFilters?.settings?.alwaysVisibleFields\"\n [alwaysVisibleFieldMetadataOverrides]=\"config.behavior?.filtering?.advancedFilters?.settings?.alwaysVisibleFieldMetadataOverrides ?? {}\"\n [selectedFieldIds]=\"config.behavior?.filtering?.advancedFilters?.settings?.selectedFieldIds ?? []\"\n [allowSaveTags]=\"config.behavior?.filtering?.advancedFilters?.settings?.allowSaveTags\"\n [changeDebounceMs]=\"config.behavior?.filtering?.advancedFilters?.settings?.changeDebounceMs ?? 300\"\n [i18n]=\"getFilterI18n()\"\n [mode]=\"'filter'\"\n [showFilterSettings]=\"enableCustomization\"\n (change)=\"onAdvancedFilterChange($event)\"\n (requestSearch)=\"onAdvancedFilterSubmit($event)\"\n (clear)=\"onAdvancedFilterClear()\"\n ></praxis-filter>\n }\n <ng-content select=\"[advancedFilter]\" />\n <ng-content select=\"[toolbar]\" />\n \n <!-- AI Assistant in Toolbar -->\n @defer (on idle) {\n @if (aiAdapter) {\n <praxis-ai-assistant [adapter]=\"aiAdapter\" end-actions></praxis-ai-assistant>\n }\n }\n\n @if (enableCustomization) {\n <button end-actions mat-icon-button color=\"primary\" data-role=\"table-settings\" data-testid=\"table-settings-trigger\"\n (click)=\"openTableSettings()\" [attr.aria-label]=\"getTableSettingsLabel()\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"getTableSettingsTooltip()\"\n matTooltipPosition=\"below\">\n <mat-icon>settings</mat-icon>\n </button>\n }\n </praxis-table-toolbar>\n }\n @if (!showToolbar && enableCustomization) {\n <div class=\"ptable-header-actions\">\n @defer (on idle) {\n @if (aiAdapter) {\n <praxis-ai-assistant [adapter]=\"aiAdapter\"></praxis-ai-assistant>\n }\n }\n <button mat-icon-button color=\"primary\" data-role=\"table-settings\" data-testid=\"table-settings-trigger\" (click)=\"openTableSettings()\" [attr.aria-label]=\"getTableSettingsLabel()\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"getTableSettingsTooltip()\"\n matTooltipPosition=\"below\">\n <mat-icon>settings</mat-icon>\n </button>\n @if (isRemoteMode()) {\n <button mat-icon-button (click)=\"disconnect()\" [attr.aria-label]=\"getDisconnectLabel()\" [matTooltip]=\"getDisconnectTooltip()\">\n <mat-icon>link_off</mat-icon>\n </button>\n }\n </div>\n }\n </div>\n} @else {\n @if (shouldRenderDataSurface() && !schemaError && !dataError) {\n @if (showToolbar && shouldShowToolbarTopPlacement()) {\n <praxis-table-toolbar\n [config]=\"config\"\n [backgroundColor]=\"getToolbarActionsBackgroundColor()\"\n [style.--pfx-filter-h]=\"getToolbarLayoutHeightHostStyle()\"\n [evaluationContext]=\"getToolbarEvaluationContext()\"\n [showActionsGroup]=\"shouldShowToolbarActionsTop()\"\n [showMobileActions]=\"shouldShowToolbarActionsTop()\"\n (toolbarAction)=\"onToolbarAction($event)\"\n (reset)=\"onResetPreferences()\"\n >\n @if (shouldRenderAdvancedFilter()) {\n <praxis-filter\n advancedFilter\n [resourcePath]=\"getAdvancedFilterResourcePath()\"\n [filterId]=\"tableId\"\n [formId]=\"tableId\"\n [persistenceKey]=\"getAdvancedFilterPersistenceKey()\"\n [fieldMetadata]=\"getAdvancedFilterFieldMetadata()\"\n [enableCustomization]=\"enableCustomization\"\n \n [alwaysVisibleFields]=\"config.behavior?.filtering?.advancedFilters?.settings?.alwaysVisibleFields\"\n [alwaysVisibleFieldMetadataOverrides]=\"config.behavior?.filtering?.advancedFilters?.settings?.alwaysVisibleFieldMetadataOverrides ?? {}\"\n [selectedFieldIds]=\"config.behavior?.filtering?.advancedFilters?.settings?.selectedFieldIds ?? []\"\n [allowSaveTags]=\"config.behavior?.filtering?.advancedFilters?.settings?.allowSaveTags\"\n [changeDebounceMs]=\"config.behavior?.filtering?.advancedFilters?.settings?.changeDebounceMs ?? 300\"\n [i18n]=\"getFilterI18n()\"\n [mode]=\"'filter'\"\n [showFilterSettings]=\"enableCustomization\"\n (change)=\"onAdvancedFilterChange($event)\"\n (requestSearch)=\"onAdvancedFilterSubmit($event)\"\n (clear)=\"onAdvancedFilterClear()\"\n ></praxis-filter>\n }\n <ng-content select=\"[advancedFilter]\" />\n <ng-content select=\"[toolbar]\" />\n @defer (on idle) {\n @if (aiAdapter) {\n <praxis-ai-assistant [adapter]=\"aiAdapter\" end-actions></praxis-ai-assistant>\n }\n }\n @if (enableCustomization) {\n <button end-actions mat-icon-button color=\"primary\" data-role=\"table-settings\" data-testid=\"table-settings-trigger\"\n (click)=\"openTableSettings()\" [attr.aria-label]=\"getTableSettingsLabel()\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"getTableSettingsTooltip()\"\n matTooltipPosition=\"below\">\n <mat-icon>settings</mat-icon>\n </button>\n }\n </praxis-table-toolbar>\n }\n @if (!showToolbar && enableCustomization) {\n <div class=\"ptable-header-actions\">\n <button mat-icon-button color=\"primary\" data-role=\"table-settings\" data-testid=\"table-settings-trigger\" (click)=\"openTableSettings()\" [attr.aria-label]=\"getTableSettingsLabel()\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"getTableSettingsTooltip()\"\n matTooltipPosition=\"below\">\n <mat-icon>settings</mat-icon>\n </button>\n </div>\n }\n }\n}\n<div class=\"px-scroll-viewport\"\n cdkScrollable\n [class.scroll-auto]=\"horizontalScroll === 'auto'\"\n [class.scroll-wrap]=\"horizontalScroll === 'wrap'\"\n [class.scroll-none]=\"horizontalScroll === 'none'\">\n\n@if (shouldRenderDataSurface() && !schemaError && !dataError) {\n<div class=\"praxis-visually-hidden-status\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n {{ columnReorderStatusMessage }}\n</div>\n@if (columnReorderVisualStatusMessage) {\n <div class=\"praxis-column-reorder-status\" role=\"note\">\n {{ columnReorderVisualStatusMessage }}\n </div>\n}\n<table\n mat-table\n data-testid=\"table-column-drag-drop-list\"\n [dataSource]=\"dataSource\"\n [multiTemplateDataRows]=\"isRowExpansionRuntimeEnabled()\"\n cdkDropList\n [cdkDropListDisabled]=\"!isColumnDraggingEnabled()\"\n [cdkDropListData]=\"visibleDataColumnsForDrag\"\n cdkDropListOrientation=\"horizontal\"\n (cdkDropListDropped)=\"onColumnDrop($event)\"\n matSort\n (matSortChange)=\"onSortChange($event)\"\n [matSortDisabled]=\"!getSortingEnabled()\"\n [ngClass]=\"getTableElevationClassName()\"\n [class.table-stack-top]=\"showToolbar\"\n [class.pfx-column-drag-enabled]=\"isColumnDraggingEnabled()\"\n [class.pfx-column-drag-indicator]=\"isColumnDragIndicatorEnabled()\"\n>\n @if (config.behavior?.selection?.enabled) {\n <ng-container\n matColumnDef=\"_select\"\n >\n <th mat-header-cell *matHeaderCellDef>\n @if (canSelectAll()) {\n <mat-checkbox\n (change)=\"masterToggle()\"\n [checked]=\"isAllSelected()\"\n [indeterminate]=\"selection.hasValue() && !isAllSelected()\"\n ></mat-checkbox>\n }\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 }\n @if (isRowExpansionRuntimeEnabled()) {\n <ng-container matColumnDef=\"_expander\">\n <th mat-header-cell *matHeaderCellDef class=\"pfx-expansion-col-header\">\n <span class=\"praxis-visually-hidden-status\">Expandir detalhes da linha</span>\n </th>\n <td mat-cell *matCellDef=\"let row; let i = index\" class=\"pfx-expansion-col-cell\">\n <button\n mat-icon-button\n class=\"pfx-expansion-toggle\"\n [disabled]=\"!isRowExpandable(row, i) || !isExpansionIconTriggerEnabled()\"\n [attr.aria-expanded]=\"isRowExpanded(row, i) ? 'true' : 'false'\"\n [attr.aria-controls]=\"getRowExpansionDetailId(row, i)\"\n [attr.aria-label]=\"getRowExpansionToggleAriaLabel(row, i)\"\n (click)=\"onExpansionToggleFromIcon(row, i, $event)\"\n (keydown)=\"onExpansionToggleKeydown($event, row, i)\"\n >\n <mat-icon [praxisIcon]=\"isRowExpanded(row, i)\n ? getExpansionExpandedIcon()\n : getExpansionCollapsedIcon()\"></mat-icon>\n </button>\n </td>\n </ng-container>\n }\n @for (column of visibleColumns; track column.field) {\n <ng-container\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 cdkDrag\n [cdkDragData]=\"column\"\n cdkDragLockAxis=\"x\"\n cdkDragPreviewClass=\"pfx-column-drag-preview\"\n (cdkDragStarted)=\"onColumnDragStarted(column)\"\n (cdkDragEnded)=\"onColumnDragEnded($event, column)\"\n (keydown)=\"onColumnDragHandleKeydown($event, column)\"\n [cdkDragDisabled]=\"!isColumnDraggingEnabled() || !isColumnDraggable(column)\"\n [class.praxis-header-draggable]=\"isColumnDraggingEnabled() && isColumnDraggable(column)\"\n [disabled]=\"!getSortingEnabled() || column.sortable === false\"\n [style.text-align]=\"getColumnTextAlignStyle(column)\"\n [style.width]=\"getColumnWidthStyle(column)\"\n [attr.style]=\"getColumnHeaderAttrStyle(column)\"\n [attr.aria-label]=\"isColumnDraggingEnabled() && isColumnDraggable(column) ? getColumnDragHandleAriaLabel(column) : null\"\n >\n <span class=\"praxis-header-label\" data-testid=\"column-header-label\">\n @if (isColumnDraggingEnabled() && isColumnDraggable(column)) {\n <span\n class=\"praxis-column-drag-handle\"\n data-testid=\"column-drag-handle\"\n [attr.data-column-field]=\"column.field\"\n aria-hidden=\"true\"\n >\n <mat-icon [praxisIcon]=\"'drag_indicator'\"></mat-icon>\n </span>\n }\n <span class=\"praxis-header-label-text\">{{ column.header }}</span>\n </span>\n </th>\n <td\n mat-cell\n *matCellDef=\"let element\"\n [style.text-align]=\"getColumnTextAlignStyle(column)\"\n [style.width]=\"getColumnWidthStyle(column)\"\n [attr.style]=\"getColumnCellAttrStyle(column)\"\n [ngClass]=\"getCellClasses(element, column)\"\n [ngStyle]=\"getCellNgStyle(element, column)\"\n >\n <div\n class=\"pfx-cell-content\"\n [ngClass]=\"getCellContentClasses(element, column)\"\n [ngStyle]=\"getCellContentNgStyle(element, column)\"\n >\n <ng-container [ngSwitch]=\"getEffectiveRendererType(element, column)\">\n <!-- Icon renderer -->\n <ng-container *ngSwitchCase=\"'icon'\">\n <span\n class=\"pfx-icon-renderer\"\n [style.color]=\"getIconColor(element, column) || null\"\n [ngStyle]=\"getIconStyle(element, column)\"\n [attr.aria-label]=\"getIconAriaLabel(element, column) || null\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-icon-renderer__content\"\n [nodes]=\"getIconRichContentNodes(element, column)\"\n ></praxis-rich-content>\n </span>\n </ng-container>\n\n <!-- Image renderer -->\n <ng-container *ngSwitchCase=\"'image'\">\n <span class=\"pfx-cell-image\">\n <praxis-rich-content\n rootClassName=\"pfx-cell-image__content\"\n [nodes]=\"getImageRichContentNodes(element, column)\"\n ></praxis-rich-content>\n </span>\n </ng-container>\n\n <!-- Badge renderer -->\n <ng-container *ngSwitchCase=\"'badge'\">\n <span class=\"pfx-badge\" [ngClass]=\"getBadgeClasses(element, column)\">\n <praxis-rich-content\n rootClassName=\"pfx-badge__content\"\n [nodes]=\"getBadgeRichContentNodes(element, column)\"\n ></praxis-rich-content>\n </span>\n </ng-container>\n\n <!-- Link renderer -->\n <ng-container *ngSwitchCase=\"'link'\">\n <a\n class=\"pfx-link\"\n [attr.href]=\"getLinkHref(element, column) || null\"\n [attr.target]=\"getLinkTarget(element, column) || null\"\n [attr.rel]=\"getLinkRel(element, column) || null\"\n (click)=\"$event.stopPropagation()\"\n ><praxis-rich-content\n rootClassName=\"pfx-link__content\"\n [nodes]=\"getLinkRichContentNodes(element, column)\"\n ></praxis-rich-content></a\n >\n </ng-container>\n\n <!-- Button renderer -->\n <ng-container *ngSwitchCase=\"'button'\">\n <ng-container [ngSwitch]=\"getButtonVariant(element, column)\">\n <button\n *ngSwitchCase=\"'outlined'\"\n mat-stroked-button\n [color]=\"getButtonColor(element, column) || null\"\n [disabled]=\"isButtonDisabled(element, column)\"\n [attr.aria-label]=\"getButtonAriaLabel(element, column) || getButtonLabel(element, column)\"\n (click)=\"onButtonClick(element, column, $event)\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-button-renderer__content\"\n [nodes]=\"getButtonRichContentNodes(element, column)\"\n ></praxis-rich-content>\n </button>\n <button\n *ngSwitchCase=\"'text'\"\n mat-button\n [color]=\"getButtonColor(element, column) || null\"\n [disabled]=\"isButtonDisabled(element, column)\"\n [attr.aria-label]=\"getButtonAriaLabel(element, column) || getButtonLabel(element, column)\"\n (click)=\"onButtonClick(element, column, $event)\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-button-renderer__content\"\n [nodes]=\"getButtonRichContentNodes(element, column)\"\n ></praxis-rich-content>\n </button>\n <button\n *ngSwitchDefault\n mat-flat-button\n [color]=\"getButtonColor(element, column) || null\"\n [disabled]=\"isButtonDisabled(element, column)\"\n [attr.aria-label]=\"getButtonAriaLabel(element, column) || getButtonLabel(element, column)\"\n (click)=\"onButtonClick(element, column, $event)\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-button-renderer__content\"\n [nodes]=\"getButtonRichContentNodes(element, column)\"\n ></praxis-rich-content>\n </button>\n </ng-container>\n </ng-container>\n\n <!-- Chip renderer -->\n <ng-container *ngSwitchCase=\"'chip'\">\n <span class=\"pfx-chip\" [ngClass]=\"getChipClasses(element, column)\">\n <praxis-rich-content\n rootClassName=\"pfx-chip__content\"\n [nodes]=\"getChipRichContentNodes(element, column)\"\n ></praxis-rich-content>\n </span>\n </ng-container>\n\n <!-- Progress renderer -->\n <ng-container *ngSwitchCase=\"'progress'\">\n <div class=\"pfx-progress\">\n <praxis-rich-content\n rootClassName=\"pfx-progress__content\"\n [context]=\"getProgressRichContentContext(element, column)\"\n [nodes]=\"getProgressRichContentNodes(element, column)\"\n ></praxis-rich-content>\n </div>\n </ng-container>\n\n <!-- Avatar renderer -->\n <ng-container *ngSwitchCase=\"'avatar'\">\n <span class=\"pfx-avatar-renderer\"\n [ngStyle]=\"getAvatarStyle(element, column)\"\n [class.shape-rounded]=\"getAvatarShape(element, column) === 'rounded'\"\n [class.shape-circle]=\"getAvatarShape(element, column) === 'circle'\">\n <praxis-rich-content\n rootClassName=\"pfx-avatar-renderer__content\"\n [nodes]=\"getAvatarRichContentNodes(element, column)\"\n ></praxis-rich-content>\n </span>\n </ng-container>\n\n <!-- Toggle renderer -->\n <ng-container *ngSwitchCase=\"'toggle'\">\n <mat-slide-toggle\n [checked]=\"getToggleState(element, column)\"\n [disabled]=\"isToggleDisabled(element, column)\"\n [attr.aria-label]=\"getToggleAriaLabel(element, column) || 'Alternar'\"\n (change)=\"onToggleChange(element, column, $event)\"\n (click)=\"$event.stopPropagation()\"\n ></mat-slide-toggle>\n </ng-container>\n\n <!-- Menu renderer -->\n <ng-container *ngSwitchCase=\"'menu'\">\n <button mat-icon-button [matMenuTriggerFor]=\"menuRef\" (click)=\"$event.stopPropagation()\" [attr.aria-label]=\"getMenuAriaLabel(element, column) || 'Menu'\">\n <praxis-rich-content rootClassName=\"pfx-menu-trigger__content\" [nodes]=\"getMenuTriggerRichContentNodes()\"></praxis-rich-content>\n </button>\n <mat-menu #menuRef=\"matMenu\">\n <button mat-menu-item *ngFor=\"let it of getMenuItems(element, column)\" (click)=\"onMenuItemClick(it, element, $event)\" [disabled]=\"!it.__visible\" >\n <praxis-rich-content\n rootClassName=\"pfx-menu-item__content\"\n [nodes]=\"getMenuItemRichContentNodes(it)\"\n ></praxis-rich-content>\n </button>\n </mat-menu>\n </ng-container>\n\n <!-- Rating renderer -->\n <ng-container *ngSwitchCase=\"'rating'\">\n <praxis-table-rating\n class=\"pfx-rating-cell\"\n [itemsCount]=\"getRatingMax(element, column)\"\n [value]=\"getRatingValue(element, column)\"\n [size]=\"getRatingSize(element, column)\"\n [ratingColor]=\"getRatingColor(element, column)\"\n [outlineColor]=\"getRatingOutlineColor(element, column)\"\n [ariaLabel]=\"getRatingAriaLabel(element, column) || column.header\">\n </praxis-table-rating>\n </ng-container>\n\n <!-- HTML renderer (sanitizado) -->\n <ng-container *ngSwitchCase=\"'html'\">\n <span [innerHTML]=\"getSafeHtml(element, column)\"></span>\n </ng-container>\n\n <!-- Compose renderer -->\n <ng-container *ngSwitchCase=\"'compose'\">\n <span class=\"pfx-cell-compose\" [ngClass]=\"getComposeClasses(element, column)\" [ngStyle]=\"getComposeGapStyle(element, column)\">\n <ng-container *ngFor=\"let it of getComposeItems(element, column)\">\n <ng-container [ngSwitch]=\"getItemEffectiveType(element, column, it)\">\n <!-- Reuse helpers by projecting item as faux column -->\n <ng-container *ngSwitchCase=\"'icon'\">\n <span class=\"pfx-icon-renderer\" [style.color]=\"getIconColor(element, asItemColumn(column, it)) || null\" [ngStyle]=\"getIconStyle(element, asItemColumn(column, it))\" [attr.aria-label]=\"getIconAriaLabel(element, asItemColumn(column, it)) || null\"><praxis-rich-content rootClassName=\"pfx-icon-renderer__content\" [nodes]=\"getIconRichContentNodes(element, asItemColumn(column, it))\"></praxis-rich-content></span>\n </ng-container>\n <ng-container *ngSwitchCase=\"'image'\">\n <span class=\"pfx-cell-image\"><praxis-rich-content rootClassName=\"pfx-cell-image__content\" [nodes]=\"getImageRichContentNodes(element, asItemColumn(column, it))\"></praxis-rich-content></span>\n </ng-container>\n <ng-container *ngSwitchCase=\"'badge'\">\n <span class=\"pfx-badge\" [ngClass]=\"getBadgeClasses(element, asItemColumn(column, it))\"><praxis-rich-content rootClassName=\"pfx-badge__content\" [nodes]=\"getBadgeRichContentNodes(element, asItemColumn(column, it))\"></praxis-rich-content></span>\n </ng-container>\n <ng-container *ngSwitchCase=\"'link'\">\n <a class=\"pfx-link\" [attr.href]=\"getLinkHref(element, asItemColumn(column, it)) || null\" [attr.target]=\"getLinkTarget(element, asItemColumn(column, it)) || null\" [attr.rel]=\"getLinkRel(element, asItemColumn(column, it)) || null\" (click)=\"$event.stopPropagation()\"><praxis-rich-content rootClassName=\"pfx-link__content\" [nodes]=\"getLinkRichContentNodes(element, asItemColumn(column, it))\"></praxis-rich-content></a>\n </ng-container>\n <ng-container *ngSwitchCase=\"'button'\">\n <ng-container [ngSwitch]=\"getButtonVariant(element, asItemColumn(column, it))\">\n <button *ngSwitchCase=\"'outlined'\" mat-stroked-button [color]=\"getButtonColor(element, asItemColumn(column, it)) || null\" [disabled]=\"isButtonDisabled(element, asItemColumn(column, it))\" [attr.aria-label]=\"getButtonAriaLabel(element, asItemColumn(column, it)) || getButtonLabel(element, asItemColumn(column, it))\" (click)=\"onButtonClick(element, asItemColumn(column, it), $event)\"><praxis-rich-content rootClassName=\"pfx-button-renderer__content\" [nodes]=\"getButtonRichContentNodes(element, asItemColumn(column, it))\"></praxis-rich-content></button>\n <button *ngSwitchCase=\"'text'\" mat-button [color]=\"getButtonColor(element, asItemColumn(column, it)) || null\" [disabled]=\"isButtonDisabled(element, asItemColumn(column, it))\" [attr.aria-label]=\"getButtonAriaLabel(element, asItemColumn(column, it)) || getButtonLabel(element, asItemColumn(column, it))\" (click)=\"onButtonClick(element, asItemColumn(column, it), $event)\"><praxis-rich-content rootClassName=\"pfx-button-renderer__content\" [nodes]=\"getButtonRichContentNodes(element, asItemColumn(column, it))\"></praxis-rich-content></button>\n <button *ngSwitchDefault mat-flat-button [color]=\"getButtonColor(element, asItemColumn(column, it)) || null\" [disabled]=\"isButtonDisabled(element, asItemColumn(column, it))\" [attr.aria-label]=\"getButtonAriaLabel(element, asItemColumn(column, it)) || getButtonLabel(element, asItemColumn(column, it))\" (click)=\"onButtonClick(element, asItemColumn(column, it), $event)\"><praxis-rich-content rootClassName=\"pfx-button-renderer__content\" [nodes]=\"getButtonRichContentNodes(element, asItemColumn(column, it))\"></praxis-rich-content></button>\n </ng-container>\n </ng-container>\n <ng-container *ngSwitchCase=\"'chip'\">\n <span class=\"pfx-chip\" [ngClass]=\"getChipClasses(element, asItemColumn(column, it))\"><praxis-rich-content rootClassName=\"pfx-chip__content\" [nodes]=\"getChipRichContentNodes(element, asItemColumn(column, it))\"></praxis-rich-content></span>\n </ng-container>\n <ng-container *ngSwitchCase=\"'progress'\">\n <div class=\"pfx-progress\">\n <praxis-rich-content\n rootClassName=\"pfx-progress__content\"\n [context]=\"getProgressRichContentContext(element, asItemColumn(column, it))\"\n [nodes]=\"getProgressRichContentNodes(element, asItemColumn(column, it))\"\n ></praxis-rich-content>\n </div>\n </ng-container>\n <ng-container *ngSwitchCase=\"'avatar'\">\n <span class=\"pfx-avatar-renderer\" [ngStyle]=\"getAvatarStyle(element, asItemColumn(column, it))\" [class.shape-rounded]=\"getAvatarShape(element, asItemColumn(column, it)) === 'rounded'\" [class.shape-circle]=\"getAvatarShape(element, asItemColumn(column, it)) === 'circle'\"><praxis-rich-content rootClassName=\"pfx-avatar-renderer__content\" [nodes]=\"getAvatarRichContentNodes(element, asItemColumn(column, it))\"></praxis-rich-content></span>\n </ng-container>\n <ng-container *ngSwitchCase=\"'toggle'\">\n <mat-slide-toggle [checked]=\"getToggleState(element, asItemColumn(column, it))\" [disabled]=\"isToggleDisabled(element, asItemColumn(column, it))\" [attr.aria-label]=\"getToggleAriaLabel(element, asItemColumn(column, it)) || 'Alternar'\" (change)=\"onToggleChange(element, asItemColumn(column, it), $event)\" (click)=\"$event.stopPropagation()\"></mat-slide-toggle>\n </ng-container>\n <ng-container *ngSwitchCase=\"'menu'\">\n <button mat-icon-button [matMenuTriggerFor]=\"menuRef\" (click)=\"$event.stopPropagation()\" [attr.aria-label]=\"getMenuAriaLabel(element, asItemColumn(column, it)) || 'Menu'\"><praxis-rich-content rootClassName=\"pfx-menu-trigger__content\" [nodes]=\"getMenuTriggerRichContentNodes()\"></praxis-rich-content></button>\n <mat-menu #menuRef=\"matMenu\">\n <button mat-menu-item *ngFor=\"let mi of getMenuItems(element, asItemColumn(column, it))\" (click)=\"onMenuItemClick(mi, element, $event)\" [disabled]=\"!mi.__visible\">\n <praxis-rich-content\n rootClassName=\"pfx-menu-item__content\"\n [nodes]=\"getMenuItemRichContentNodes(mi)\"\n ></praxis-rich-content>\n </button>\n </mat-menu>\n </ng-container>\n <ng-container *ngSwitchCase=\"'rating'\">\n <praxis-table-rating\n class=\"pfx-rating-cell\"\n [itemsCount]=\"getRatingMax(element, asItemColumn(column, it))\"\n [value]=\"getRatingValue(element, asItemColumn(column, it))\"\n [size]=\"getRatingSize(element, asItemColumn(column, it))\"\n [ratingColor]=\"getRatingColor(element, asItemColumn(column, it))\"\n [outlineColor]=\"getRatingOutlineColor(element, asItemColumn(column, it))\"\n [ariaLabel]=\"getRatingAriaLabel(element, asItemColumn(column, it)) || column.header\">\n </praxis-table-rating>\n </ng-container>\n <ng-container *ngSwitchCase=\"'html'\">\n <span [innerHTML]=\"getSafeHtml(element, asItemColumn(column, it))\"></span>\n </ng-container>\n <!-- Value item: render base cell text alongside visuals -->\n <ng-container *ngSwitchCase=\"'value'\">\n <span class=\"pfx-cell-value\">{{ getComposeItemValue(element, column, it) }}</span>\n </ng-container>\n </ng-container>\n </ng-container>\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 </div>\n </td>\n </ng-container>\n }\n @if (config.actions?.row?.enabled) {\n <ng-container 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]=\"getRowActionsWidthStyle()\" 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 <praxis-rich-content\n rootClassName=\"praxis-actions-header__nodes\"\n [nodes]=\"getActionsHeaderRichContentNodes()\"\n ></praxis-rich-content>\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]=\"getRowActionsWidthStyle()\"\n >\n <div\n class=\"praxis-actions-cell__content\"\n [class.praxis-actions-cell__content--discovering]=\"isRowDiscoveryPending(row)\"\n [attr.aria-busy]=\"isRowDiscoveryPending(row) ? 'true' : null\"\n >\n <!-- A\u00C3\u00A7\u00C3\u00B5es inline -->\n <!-- Inline actions: icons mode -->\n @if (config.actions?.row?.display === 'icons' || !config.actions?.row?.display) {\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <span\n class=\"praxis-row-action-anchor\"\n [class.praxis-row-action-anchor--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-row-action-anchor--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [matTooltip]=\"getActionTooltip(a, row)\"\n [matTooltipDisabled]=\"!getActionTooltip(a, row)\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"praxis-tooltip\"\n >\n <button\n mat-icon-button\n class=\"praxis-icon-btn\"\n [class.praxis-icon-btn--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-icon-btn--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event, getRowActionRuntimeOptions(a, row))\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\"\n >\n <praxis-rich-content\n rootClassName=\"praxis-row-action__content\"\n [nodes]=\"getRowActionIconRichContentNodes(a)\"\n ></praxis-rich-content>\n </button>\n </span>\n </ng-container>\n }\n\n <!-- Inline actions: buttons mode (show label + icon) -->\n @if (config.actions?.row?.display === 'buttons') {\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <span\n class=\"praxis-row-action-anchor\"\n [class.praxis-row-action-anchor--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-row-action-anchor--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [matTooltip]=\"getActionTooltip(a, row)\"\n [matTooltipDisabled]=\"!getActionTooltip(a, row)\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"praxis-tooltip\"\n >\n <button\n mat-flat-button\n class=\"praxis-row-btn\"\n [class.praxis-row-btn--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-row-btn--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event, getRowActionRuntimeOptions(a, row))\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\"\n >\n <praxis-rich-content\n rootClassName=\"praxis-row-action__content\"\n [nodes]=\"getRowActionRichContentNodes(a)\"\n ></praxis-rich-content>\n </button>\n </span>\n </ng-container>\n }\n\n <!-- Menu de overflow -->\n @if (hasOverflowRowActions(row)) {\n <button\n mat-icon-button\n class=\"praxis-icon-btn praxis-more-btn\"\n [matMenuTriggerFor]=\"rowMoreMenu\"\n (menuOpened)=\"onRowOverflowMenuOpened(row)\"\n [color]=\"getRowMenuButtonColor() || null\"\n [attr.aria-label]=\"getRowMenuTooltip(row)\"\n [matTooltip]=\"getRowMenuTooltip(row)\"\n matTooltipPosition=\"above\"\n >\n <mat-icon [praxisIcon]=\"getRowMenuIcon()\"></mat-icon>\n </button>\n }\n <mat-menu #rowMoreMenu=\"matMenu\" xPosition=\"before\">\n @if (hasRowOverflowMenuLoadingState(row)) {\n <button mat-menu-item disabled>\n <mat-icon>hourglass_empty</mat-icon>\n <praxis-rich-content\n rootClassName=\"praxis-row-overflow-menu__label\"\n [nodes]=\"getTableChromeTextRichContentNodes(getRowOverflowMenuLoadingLabel(), 'praxis-row-overflow-menu__label-text')\"\n ></praxis-rich-content>\n </button>\n }\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, getRowActionRuntimeOptions(a, row))\"\n [disabled]=\"isActionDisabled(a, row)\"\n >\n <mat-icon [color]=\"a.color || null\" [praxisIcon]=\"a.icon\"></mat-icon>\n <praxis-rich-content\n rootClassName=\"praxis-row-overflow-menu__label\"\n [nodes]=\"getTableChromeTextRichContentNodes(a.label || getActionId(a), 'praxis-row-overflow-menu__label-text')\"\n ></praxis-rich-content>\n </button>\n </ng-container>\n @if (hasRowOverflowMenuEmptyState(row)) {\n <button mat-menu-item disabled>\n <mat-icon>info</mat-icon>\n <praxis-rich-content\n rootClassName=\"praxis-row-overflow-menu__label\"\n [nodes]=\"getTableChromeTextRichContentNodes(getRowOverflowMenuEmptyLabel(row), 'praxis-row-overflow-menu__label-text')\"\n ></praxis-rich-content>\n </button>\n }\n </mat-menu>\n </div>\n </td>\n </ng-container>\n }\n\n <tr\n mat-header-row\n *matHeaderRowDef=\"displayedColumns\"\n ></tr>\n @if (!isVirtualized()) {\n <tr\n mat-row\n *matRowDef=\"let row; let i = index; columns: displayedColumns\"\n [class.pfx-row-selected]=\"selection.isSelected(row)\"\n [class.pfx-row-expanded]=\"isRowExpansionRuntimeEnabled() && isRowExpanded(row, i)\"\n [attr.aria-selected]=\"config.behavior?.selection?.enabled ? (selection.isSelected(row) ? 'true' : 'false') : null\"\n [attr.aria-expanded]=\"isRowExpansionRuntimeEnabled() ? (isRowExpanded(row, i) ? 'true' : 'false') : null\"\n [ngClass]=\"getRowClasses(row)\"\n [ngStyle]=\"getRowNgStyle(row)\"\n [matTooltip]=\"getRowTooltip(row) || null\"\n [matTooltipDisabled]=\"!getRowTooltip(row)\"\n [matTooltipPosition]=\"getRowTooltipPosition(row)\"\n [matTooltipShowDelay]=\"getRowTooltipShowDelay(row)\"\n (click)=\"onRowClicked(row, i, $event)\"\n (dblclick)=\"onRowDoubleClicked(row, i)\"\n ></tr>\n @if (isRowExpansionRuntimeEnabled()) {\n <ng-container matColumnDef=\"_detail\">\n <td\n mat-cell\n *matCellDef=\"let row; let i = index\"\n class=\"pfx-expansion-detail-cell\"\n [attr.colspan]=\"displayedColumns.length\"\n >\n <section\n class=\"pfx-expansion-detail-panel\"\n [ngClass]=\"getExpansionMotionPresetClass()\"\n [ngStyle]=\"getExpansionMotionStyle()\"\n [attr.id]=\"getRowExpansionDetailId(row, i)\"\n role=\"region\"\n [attr.aria-label]=\"getRowExpansionRegionAriaLabel(row, i)\"\n [attr.aria-busy]=\"getExpansionDetailViewState(row, i).status === 'loading' ? 'true' : 'false'\"\n >\n @let detailState = getExpansionDetailViewState(row, i);\n @if (detailState.status === 'loading') {\n <div class=\"pfx-expansion-detail-message\" role=\"status\" aria-live=\"polite\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-detail-message__content\"\n [nodes]=\"getExpansionDetailTextRichContentNodes('Carregando detail schema...', 'pfx-expansion-detail-message__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, i)\"\n ></praxis-rich-content>\n </div>\n } @else if (detailState.status !== 'ready') {\n <div\n class=\"pfx-expansion-detail-message pfx-expansion-detail-message--error\"\n role=\"alert\"\n aria-live=\"assertive\"\n aria-atomic=\"true\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-expansion-detail-message__content\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(detailState.message || 'Detail indispon\u00EDvel para esta linha.', 'pfx-expansion-detail-message__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, i)\"\n ></praxis-rich-content>\n </div>\n } @else {\n @if (getExpansionDetailLayout(detailState.schema) === 'tabs') {\n @let detailTabs = getExpansionDetailTabs(detailState.schema);\n @if (detailTabs.length) {\n <div class=\"pfx-expansion-detail-tabs\" role=\"tablist\" aria-label=\"Se\u00C3\u00A7\u00C3\u00B5es do detail\">\n @for (tab of detailTabs; track $index; let tabIndex = $index) {\n <button\n type=\"button\"\n class=\"pfx-expansion-detail-tab-btn\"\n role=\"tab\"\n [attr.id]=\"getExpansionDetailTabId(row, i, tab, tabIndex)\"\n [attr.aria-controls]=\"getExpansionDetailPanelId(row, i, tab, tabIndex)\"\n [attr.aria-selected]=\"isExpansionDetailTabActive(row, i, tab, tabIndex, detailTabs) ? 'true' : 'false'\"\n [attr.tabindex]=\"isExpansionDetailTabActive(row, i, tab, tabIndex, detailTabs) ? '0' : '-1'\"\n [class.is-active]=\"isExpansionDetailTabActive(row, i, tab, tabIndex, detailTabs)\"\n (click)=\"onExpansionDetailTabSelect(row, i, tab, tabIndex, $event)\"\n (keydown)=\"onExpansionDetailTabKeydown($event, row, i, tabIndex, detailTabs)\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-expansion-detail-tab-btn__content\"\n [nodes]=\"getExpansionDetailTabButtonRichContentNodes(tab)\"\n [context]=\"getExpansionDetailRichContentContext(row, i)\"\n ></praxis-rich-content>\n </button>\n }\n </div>\n @for (tab of detailTabs; track $index; let tabIndex = $index) {\n @if (isExpansionDetailTabActive(row, i, tab, tabIndex, detailTabs)) {\n <div\n class=\"pfx-expansion-detail-tab-panel\"\n role=\"tabpanel\"\n [attr.id]=\"getExpansionDetailPanelId(row, i, tab, tabIndex)\"\n [attr.aria-labelledby]=\"getExpansionDetailTabId(row, i, tab, tabIndex)\"\n >\n @if (getExpansionDetailTabRichContentNodes(tab, row, i); as richNodes) {\n <praxis-rich-content\n class=\"pfx-expansion-node-host pfx-expansion-node-host--tab\"\n [nodes]=\"richNodes\"\n [context]=\"getExpansionDetailRichContentContext(row, i)\"\n ></praxis-rich-content>\n } @else {\n @for (childNode of getExpansionDetailNodeChildren(tab); track $index) {\n <ng-container\n *ngTemplateOutlet=\"\n expansionDetailNodeTemplate;\n context: { $implicit: childNode, row: row, index: i }\n \"\n ></ng-container>\n }\n }\n </div>\n }\n }\n } @else {\n <div class=\"pfx-expansion-detail-message\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-detail-message__content\"\n [nodes]=\"getExpansionDetailTextRichContentNodes('Schema em layout tabs sem abas v\u00E1lidas.', 'pfx-expansion-detail-message__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, i)\"\n ></praxis-rich-content>\n </div>\n }\n } @else {\n <div class=\"pfx-expansion-detail-stack\">\n @for (node of getExpansionDetailItems(detailState.schema); track $index) {\n <ng-container\n *ngTemplateOutlet=\"\n expansionDetailNodeTemplate;\n context: { $implicit: node, row: row, index: i }\n \"\n ></ng-container>\n }\n </div>\n }\n }\n\n <ng-template #expansionDetailNodeTemplate let-node let-row=\"row\" let-index=\"index\">\n @if (getExpansionDetailRichContentNodes(node, row, index); as richNodes) {\n <praxis-rich-content\n class=\"pfx-expansion-node-host\"\n [nodes]=\"richNodes\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n } @else {\n @switch (getExpansionDetailNodeType(node)) {\n @case ('list') {\n <section class=\"pfx-expansion-node pfx-expansion-node-list\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-list__title\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(getExpansionDetailNodeTitle(node, 'Lista'), 'pfx-expansion-node-list__title-text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n @let listItems = getExpansionDetailListItems(row, node);\n @let listEntries = getExpansionDetailListEntries(row, node);\n @if (listItems.length) {\n <ul>\n @for (entry of listEntries; track $index) {\n <li>\n @if (getExpansionDetailListItemRichContentNodes(entry); as richNodes) {\n <praxis-rich-content\n class=\"pfx-expansion-node-list__item-host\"\n [nodes]=\"richNodes\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n } @else {\n {{ formatExpansionDetailListEntry(entry) }}\n }\n </li>\n }\n </ul>\n } @else {\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-placeholder\"\n [nodes]=\"getExpansionDetailTextRichContentNodes('Sem itens.', 'pfx-expansion-node-placeholder__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n }\n </section>\n }\n @case ('detailList') {\n <section class=\"pfx-expansion-node pfx-expansion-node-rich-list\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-rich-list__title\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(getExpansionDetailNodeTitle(node, 'Cole\u00E7\u00E3o rica'), 'pfx-expansion-node-rich-list__title-text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n @let richListEntries = getExpansionDetailRichListEntries(row, node);\n @if (richListEntries.length) {\n <div class=\"pfx-expansion-node-rich-list__items\">\n @for (entry of richListEntries; track $index) {\n <article [class]=\"getExpansionDetailRichListItemClassName(node)\">\n <praxis-rich-content\n class=\"pfx-expansion-node-rich-list__item-host\"\n [nodes]=\"getExpansionDetailRichListItemNodes(node)\"\n [context]=\"getExpansionDetailRichListItemContext(row, index, node, entry, $index)\"\n ></praxis-rich-content>\n @let itemActions = getExpansionDetailRichListItemActions(node, row, index, entry, $index);\n @if (itemActions.length) {\n <div class=\"pfx-expansion-node-rich-list__item-actions\">\n @for (action of itemActions; track action.actionId) {\n <button\n type=\"button\"\n mat-stroked-button\n class=\"pfx-expansion-node-rich-list__item-action-btn\"\n [disabled]=\"isExpansionDetailRichListItemActionDisabled(action, row, index, node, entry, $index)\"\n (click)=\"onExpansionDetailRichListItemAction(action, row, index, node, entry, $index, $event)\"\n >\n <praxis-rich-content\n class=\"pfx-expansion-node-rich-list__item-action-host\"\n [nodes]=\"getExpansionDetailRichListItemActionNodes(action)\"\n [context]=\"getExpansionDetailRichListItemContext(row, index, node, entry, $index)\"\n ></praxis-rich-content>\n </button>\n }\n </div>\n }\n </article>\n }\n </div>\n } @else {\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-placeholder\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(getExpansionDetailRichListEmptyText(node), 'pfx-expansion-node-placeholder__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n }\n </section>\n }\n @case ('cardGrid') {\n <section class=\"pfx-expansion-node pfx-expansion-node-card-grid\">\n @if (getExpansionDetailCardGridHeaderNodes(node); as cardGridHeaderNodes) {\n @if (cardGridHeaderNodes.length) {\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-card-grid__header\"\n [nodes]=\"cardGridHeaderNodes\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n }\n }\n @let gridCards = getExpansionDetailCardGridCards(node);\n @if (gridCards.length) {\n <div\n class=\"pfx-expansion-node-card-grid__cards\"\n [ngStyle]=\"getExpansionDetailCardGridStyles(node)\"\n >\n @for (card of gridCards; track $index) {\n <praxis-rich-content\n class=\"pfx-expansion-node-card-grid__card-host\"\n [nodes]=\"getExpansionDetailCardGridCardNodes(card)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n }\n </div>\n } @else {\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-placeholder\"\n [nodes]=\"getExpansionDetailTextRichContentNodes('Sem itens.', 'pfx-expansion-node-placeholder__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n }\n </section>\n }\n @case ('richText') {\n <div\n class=\"pfx-expansion-node pfx-expansion-node-richtext\"\n [innerHTML]=\"getExpansionDetailRichText(node)\"\n ></div>\n }\n @case ('formRef') {\n <section class=\"pfx-expansion-node pfx-expansion-node-embed\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__header\"\n [nodes]=\"getExpansionDetailEmbedHeaderNodes(node)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__meta\"\n [nodes]=\"getExpansionDetailEmbedMetaNodes(node)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__empty\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(getExpansionDetailEmbedEmptyText(node), 'pfx-expansion-node-embed__empty-text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n @if (getExpansionDetailEmbedAction(node, row, index); as embedAction) {\n <button\n type=\"button\"\n mat-stroked-button\n class=\"pfx-expansion-node-embed__action-btn\"\n [disabled]=\"isExpansionDetailEmbedActionDisabled(embedAction, row, index)\"\n (click)=\"onExpansionDetailEmbedAction(embedAction, row, index, $event)\"\n >\n <praxis-rich-content\n class=\"pfx-expansion-node-embed__action-host\"\n [nodes]=\"getExpansionDetailEmbedActionNodes(embedAction)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n </button>\n }\n </section>\n }\n @case ('tableRef') {\n <section class=\"pfx-expansion-node pfx-expansion-node-embed\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__header\"\n [nodes]=\"getExpansionDetailEmbedHeaderNodes(node)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__meta\"\n [nodes]=\"getExpansionDetailEmbedMetaNodes(node)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__empty\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(getExpansionDetailEmbedEmptyText(node), 'pfx-expansion-node-embed__empty-text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n @if (getExpansionDetailEmbedAction(node, row, index); as embedAction) {\n <button\n type=\"button\"\n mat-stroked-button\n class=\"pfx-expansion-node-embed__action-btn\"\n [disabled]=\"isExpansionDetailEmbedActionDisabled(embedAction, row, index)\"\n (click)=\"onExpansionDetailEmbedAction(embedAction, row, index, $event)\"\n >\n <praxis-rich-content\n class=\"pfx-expansion-node-embed__action-host\"\n [nodes]=\"getExpansionDetailEmbedActionNodes(embedAction)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n </button>\n }\n </section>\n }\n @case ('chartRef') {\n <section class=\"pfx-expansion-node pfx-expansion-node-embed\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__header\"\n [nodes]=\"getExpansionDetailEmbedHeaderNodes(node)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__meta\"\n [nodes]=\"getExpansionDetailEmbedMetaNodes(node)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__empty\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(getExpansionDetailEmbedEmptyText(node), 'pfx-expansion-node-embed__empty-text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n @if (getExpansionDetailEmbedAction(node, row, index); as embedAction) {\n <button\n type=\"button\"\n mat-stroked-button\n class=\"pfx-expansion-node-embed__action-btn\"\n [disabled]=\"isExpansionDetailEmbedActionDisabled(embedAction, row, index)\"\n (click)=\"onExpansionDetailEmbedAction(embedAction, row, index, $event)\"\n >\n <praxis-rich-content\n class=\"pfx-expansion-node-embed__action-host\"\n [nodes]=\"getExpansionDetailEmbedActionNodes(embedAction)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n </button>\n }\n </section>\n }\n @case ('templateRef') {\n <section class=\"pfx-expansion-node pfx-expansion-node-embed\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__header\"\n [nodes]=\"getExpansionDetailEmbedHeaderNodes(node)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__meta\"\n [nodes]=\"getExpansionDetailEmbedMetaNodes(node)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__empty\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(getExpansionDetailEmbedEmptyText(node), 'pfx-expansion-node-embed__empty-text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n @if (getExpansionDetailEmbedAction(node, row, index); as embedAction) {\n <button\n type=\"button\"\n mat-stroked-button\n class=\"pfx-expansion-node-embed__action-btn\"\n [disabled]=\"isExpansionDetailEmbedActionDisabled(embedAction, row, index)\"\n (click)=\"onExpansionDetailEmbedAction(embedAction, row, index, $event)\"\n >\n <praxis-rich-content\n class=\"pfx-expansion-node-embed__action-host\"\n [nodes]=\"getExpansionDetailEmbedActionNodes(embedAction)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n </button>\n }\n </section>\n }\n @case ('diagramEmbed') {\n <section class=\"pfx-expansion-node pfx-expansion-node-embed\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__header\"\n [nodes]=\"getExpansionDetailEmbedHeaderNodes(node)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__meta\"\n [nodes]=\"getExpansionDetailEmbedMetaNodes(node)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n @if (getExpansionDetailDiagramSource(row, node); as diagramSource) {\n <div class=\"pfx-expansion-node-embed__diagram\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__diagram-label\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(getExpansionDetailDiagramSourceLabel(), 'pfx-expansion-node-embed__diagram-label-text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <pre class=\"pfx-expansion-node-embed__diagram-source\">{{ diagramSource }}</pre>\n </div>\n } @else {\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__empty\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(getExpansionDetailEmbedEmptyText(node), 'pfx-expansion-node-embed__empty-text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n }\n @if (getExpansionDetailEmbedAction(node, row, index); as embedAction) {\n <button\n type=\"button\"\n mat-stroked-button\n class=\"pfx-expansion-node-embed__action-btn\"\n [disabled]=\"isExpansionDetailEmbedActionDisabled(embedAction, row, index)\"\n (click)=\"onExpansionDetailEmbedAction(embedAction, row, index, $event)\"\n >\n <praxis-rich-content\n class=\"pfx-expansion-node-embed__action-host\"\n [nodes]=\"getExpansionDetailEmbedActionNodes(embedAction)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n </button>\n }\n </section>\n }\n @case ('action') {\n <div class=\"pfx-expansion-node pfx-expansion-node-action\">\n <button\n type=\"button\"\n mat-stroked-button\n [disabled]=\"isExpansionDetailActionDisabled(node)\"\n (click)=\"onExpansionDetailAction(node, row, $event)\"\n >\n <praxis-rich-content\n class=\"pfx-expansion-node-action__content\"\n [nodes]=\"getExpansionDetailActionRichContentNodes(node)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n </button>\n @if (getExpansionDetailActionStatusText(node); as actionStatusText) {\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-action__status\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(actionStatusText, 'pfx-expansion-node-action__status-text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n }\n </div>\n }\n @case ('actionBar') {\n <section class=\"pfx-expansion-node pfx-expansion-node-action-bar\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-action-bar__title\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(getExpansionDetailActionBarTitle(node), 'pfx-expansion-node-action-bar__title-text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n @let actionBarActions = getExpansionDetailActionBarActions(node, row, index);\n @if (actionBarActions.length) {\n <div class=\"pfx-expansion-node-action-bar__actions\">\n @for (action of actionBarActions; track action.actionId) {\n <button\n type=\"button\"\n mat-stroked-button\n class=\"pfx-expansion-node-action-bar__action-btn\"\n [disabled]=\"isExpansionDetailActionBarActionDisabled(action, row, index)\"\n (click)=\"onExpansionDetailActionBarAction(action, row, index, $event)\"\n >\n <praxis-rich-content\n class=\"pfx-expansion-node-action-bar__action-host\"\n [nodes]=\"getExpansionDetailActionBarActionNodes(action)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n </button>\n }\n </div>\n } @else {\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-placeholder\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(getExpansionDetailActionBarEmptyText(node), 'pfx-expansion-node-placeholder__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n }\n </section>\n }\n @case ('tab') {\n <div class=\"pfx-expansion-node pfx-expansion-node-placeholder\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-placeholder__content\"\n [nodes]=\"getExpansionDetailTextRichContentNodes('Node', 'pfx-expansion-node-placeholder__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <code>tab</code>\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-placeholder__content\"\n [nodes]=\"getExpansionDetailTextRichContentNodes('fora de contexto de tabs.', 'pfx-expansion-node-placeholder__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n </div>\n }\n @default {\n <div class=\"pfx-expansion-node pfx-expansion-node-placeholder\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-placeholder__content\"\n [nodes]=\"getExpansionDetailTextRichContentNodes('Node n\u00E3o suportado:', 'pfx-expansion-node-placeholder__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <code>{{ getExpansionDetailNodeType(node) }}</code>\n </div>\n }\n }\n }\n </ng-template>\n </section>\n </td>\n </ng-container>\n <tr\n mat-row\n *matRowDef=\"let row; columns: expansionDetailRowColumns; when: isExpansionDetailRow\"\n class=\"pfx-expansion-detail-row\"\n ></tr>\n }\n }\n</table>\n}\n\n<!-- Virtual rows path (header preserved above) -->\n@if (shouldRenderDataSurface() && !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]=\"getVirtMinHeightHostStyle()\"\n >\n <table\n class=\"mat-mdc-table\"\n [ngClass]=\"getTableElevationClassName()\"\n [style.width]=\"getVirtualTableWidthStyle()\"\n >\n <tbody>\n <tr class=\"mat-mdc-row\"\n *cdkVirtualFor=\"let row of dataSource.data; let i = index; trackBy: trackByRow\"\n [class.pfx-row-selected]=\"selection.isSelected(row)\"\n [attr.aria-selected]=\"config.behavior?.selection?.enabled ? (selection.isSelected(row) ? 'true' : 'false') : null\"\n [ngClass]=\"getRowClasses(row)\"\n [ngStyle]=\"getRowNgStyle(row)\"\n [matTooltip]=\"getRowTooltip(row) || null\"\n [matTooltipDisabled]=\"!getRowTooltip(row)\"\n [matTooltipPosition]=\"getRowTooltipPosition(row)\"\n [matTooltipShowDelay]=\"getRowTooltipShowDelay(row)\"\n (click)=\"onRowClicked(row, i, $event)\"\n (dblclick)=\"onRowDoubleClicked(row, i)\">\n <!-- Selection column -->\n @if (config.behavior?.selection?.enabled) { <td class=\"mat-mdc-cell\">\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 @for (column of visibleColumns; track column.field) {\n <td class=\"mat-mdc-cell\"\n [style.text-align]=\"getColumnTextAlignStyle(column)\"\n [style.width]=\"getColumnWidthStyle(column)\"\n [attr.style]=\"getColumnCellAttrStyle(column)\"\n [ngClass]=\"getCellClasses(row, column)\"\n [ngStyle]=\"getCellNgStyle(row, column)\">\n <div\n class=\"pfx-cell-content\"\n [ngClass]=\"getCellContentClasses(row, column)\"\n [ngStyle]=\"getCellContentNgStyle(row, column)\"\n >\n <ng-container [ngSwitch]=\"getEffectiveRendererType(row, column)\">\n <ng-container *ngSwitchCase=\"'icon'\">\n <span class=\"pfx-icon-renderer\"\n [style.color]=\"getIconColor(row, column) || null\"\n [ngStyle]=\"getIconStyle(row, column)\"\n [attr.aria-label]=\"getIconAriaLabel(row, column) || null\">\n <praxis-rich-content\n rootClassName=\"pfx-icon-renderer__content\"\n [nodes]=\"getIconRichContentNodes(row, column)\"\n ></praxis-rich-content>\n </span>\n </ng-container>\n <ng-container *ngSwitchCase=\"'image'\">\n <span class=\"pfx-cell-image\">\n <praxis-rich-content\n rootClassName=\"pfx-cell-image__content\"\n [nodes]=\"getImageRichContentNodes(row, column)\"\n ></praxis-rich-content>\n </span>\n </ng-container>\n <ng-container *ngSwitchCase=\"'badge'\">\n <span class=\"pfx-badge\" [ngClass]=\"getBadgeClasses(row, column)\">\n <praxis-rich-content\n rootClassName=\"pfx-badge__content\"\n [nodes]=\"getBadgeRichContentNodes(row, column)\"\n ></praxis-rich-content>\n </span>\n </ng-container>\n <ng-container *ngSwitchCase=\"'chip'\">\n <span class=\"pfx-chip\" [ngClass]=\"getChipClasses(row, column)\">\n <praxis-rich-content\n rootClassName=\"pfx-chip__content\"\n [nodes]=\"getChipRichContentNodes(row, column)\"\n ></praxis-rich-content>\n </span>\n </ng-container>\n <ng-container *ngSwitchCase=\"'progress'\">\n <div class=\"pfx-progress\">\n <praxis-rich-content\n rootClassName=\"pfx-progress__content\"\n [context]=\"getProgressRichContentContext(row, column)\"\n [nodes]=\"getProgressRichContentNodes(row, column)\"\n ></praxis-rich-content>\n </div>\n </ng-container>\n <ng-container *ngSwitchCase=\"'avatar'\">\n <span\n class=\"pfx-avatar-renderer\"\n [ngStyle]=\"getAvatarStyle(row, column)\"\n [class.shape-rounded]=\"getAvatarShape(row, column) === 'rounded'\"\n [class.shape-circle]=\"getAvatarShape(row, column) === 'circle'\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-avatar-renderer__content\"\n [nodes]=\"getAvatarRichContentNodes(row, column)\"\n ></praxis-rich-content>\n </span>\n </ng-container>\n <ng-container *ngSwitchCase=\"'link'\">\n <a\n class=\"pfx-link\"\n [attr.href]=\"getLinkHref(row, column) || null\"\n [attr.target]=\"getLinkTarget(row, column) || null\"\n [attr.rel]=\"getLinkRel(row, column) || null\"\n (click)=\"$event.stopPropagation()\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-link__content\"\n [nodes]=\"getLinkRichContentNodes(row, column)\"\n ></praxis-rich-content>\n </a>\n </ng-container>\n <ng-container *ngSwitchCase=\"'button'\">\n <ng-container [ngSwitch]=\"getButtonVariant(row, column)\">\n <button\n *ngSwitchCase=\"'outlined'\"\n mat-stroked-button\n [color]=\"getButtonColor(row, column) || null\"\n [disabled]=\"isButtonDisabled(row, column)\"\n [attr.aria-label]=\"getButtonAriaLabel(row, column) || getButtonLabel(row, column)\"\n (click)=\"onButtonClick(row, column, $event)\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-button-renderer__content\"\n [nodes]=\"getButtonRichContentNodes(row, column)\"\n ></praxis-rich-content>\n </button>\n <button\n *ngSwitchCase=\"'text'\"\n mat-button\n [color]=\"getButtonColor(row, column) || null\"\n [disabled]=\"isButtonDisabled(row, column)\"\n [attr.aria-label]=\"getButtonAriaLabel(row, column) || getButtonLabel(row, column)\"\n (click)=\"onButtonClick(row, column, $event)\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-button-renderer__content\"\n [nodes]=\"getButtonRichContentNodes(row, column)\"\n ></praxis-rich-content>\n </button>\n <button\n *ngSwitchDefault\n mat-flat-button\n [color]=\"getButtonColor(row, column) || null\"\n [disabled]=\"isButtonDisabled(row, column)\"\n [attr.aria-label]=\"getButtonAriaLabel(row, column) || getButtonLabel(row, column)\"\n (click)=\"onButtonClick(row, column, $event)\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-button-renderer__content\"\n [nodes]=\"getButtonRichContentNodes(row, column)\"\n ></praxis-rich-content>\n </button>\n </ng-container>\n </ng-container>\n <ng-container *ngSwitchCase=\"'menu'\">\n <button\n mat-icon-button\n [matMenuTriggerFor]=\"menuRef\"\n (click)=\"$event.stopPropagation()\"\n [attr.aria-label]=\"getMenuAriaLabel(row, column) || 'Menu'\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-menu-trigger__content\"\n [nodes]=\"getMenuTriggerRichContentNodes()\"\n ></praxis-rich-content>\n </button>\n <mat-menu #menuRef=\"matMenu\">\n <button\n mat-menu-item\n *ngFor=\"let it of getMenuItems(row, column)\"\n (click)=\"onMenuItemClick(it, row, $event)\"\n [disabled]=\"!it.__visible\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-menu-item__content\"\n [nodes]=\"getMenuItemRichContentNodes(it)\"\n ></praxis-rich-content>\n </button>\n </mat-menu>\n </ng-container>\n <ng-container *ngSwitchDefault>\n {{ getCellValue(row, column) }}\n </ng-container>\n </ng-container>\n </div>\n </td>\n }\n\n <!-- Actions column -->\n @if (config.actions?.row?.enabled) {\n <td class=\"mat-mdc-cell praxis-actions-cell\" [class.dense]=\"dense\" [style.width]=\"getRowActionsWidthStyle()\">\n <div\n class=\"praxis-actions-cell__content\"\n [class.praxis-actions-cell__content--discovering]=\"isRowDiscoveryPending(row)\"\n [attr.aria-busy]=\"isRowDiscoveryPending(row) ? 'true' : null\"\n >\n @if (config.actions?.row?.display === 'icons' || !config.actions?.row?.display) {\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <span\n class=\"praxis-row-action-anchor\"\n [class.praxis-row-action-anchor--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-row-action-anchor--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [matTooltip]=\"getActionTooltip(a, row)\"\n [matTooltipDisabled]=\"!getActionTooltip(a, row)\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"praxis-tooltip\"\n >\n <button mat-icon-button class=\"praxis-icon-btn\"\n [class.praxis-icon-btn--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-icon-btn--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event, getRowActionRuntimeOptions(a, row))\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\">\n <praxis-rich-content\n rootClassName=\"praxis-row-action__content\"\n [nodes]=\"getRowActionIconRichContentNodes(a)\"\n ></praxis-rich-content>\n </button>\n </span>\n </ng-container>\n }\n @if (config.actions?.row?.display === 'buttons') {\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <span\n class=\"praxis-row-action-anchor\"\n [class.praxis-row-action-anchor--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-row-action-anchor--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [matTooltip]=\"getActionTooltip(a, row)\"\n [matTooltipDisabled]=\"!getActionTooltip(a, row)\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"praxis-tooltip\"\n >\n <button mat-flat-button class=\"praxis-row-btn\"\n [class.praxis-row-btn--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-row-btn--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event, getRowActionRuntimeOptions(a, row))\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\">\n <praxis-rich-content\n rootClassName=\"praxis-row-action__content\"\n [nodes]=\"getRowActionRichContentNodes(a)\"\n ></praxis-rich-content>\n </button>\n </span>\n </ng-container>\n }\n @if (hasOverflowRowActions(row)) {\n <button mat-icon-button class=\"praxis-icon-btn praxis-more-btn\"\n [matMenuTriggerFor]=\"rowMoreMenuV\"\n (menuOpened)=\"onRowOverflowMenuOpened(row)\"\n [color]=\"getRowMenuButtonColor() || null\"\n [attr.aria-label]=\"getRowMenuTooltip(row)\"\n [matTooltip]=\"getRowMenuTooltip(row)\"\n matTooltipPosition=\"above\">\n <mat-icon [praxisIcon]=\"getRowMenuIcon()\"></mat-icon>\n </button>\n }\n <mat-menu #rowMoreMenuV=\"matMenu\" xPosition=\"before\">\n @if (hasRowOverflowMenuLoadingState(row)) {\n <button mat-menu-item disabled>\n <mat-icon>hourglass_empty</mat-icon>\n <praxis-rich-content\n rootClassName=\"praxis-row-overflow-menu__label\"\n [nodes]=\"getTableChromeTextRichContentNodes(getRowOverflowMenuLoadingLabel(), 'praxis-row-overflow-menu__label-text')\"\n ></praxis-rich-content>\n </button>\n }\n <ng-container *ngFor=\"let a of getOverflowRowActions(row); trackBy: trackAction\">\n <button mat-menu-item (click)=\"onRowAction(getActionId(a), row, $event, getRowActionRuntimeOptions(a, row))\" [disabled]=\"isActionDisabled(a, row)\">\n <mat-icon [color]=\"a.color || null\" [praxisIcon]=\"a.icon\"></mat-icon>\n <praxis-rich-content\n rootClassName=\"praxis-row-overflow-menu__label\"\n [nodes]=\"getTableChromeTextRichContentNodes(a.label || getActionId(a), 'praxis-row-overflow-menu__label-text')\"\n ></praxis-rich-content>\n </button>\n </ng-container>\n @if (hasRowOverflowMenuEmptyState(row)) {\n <button mat-menu-item disabled>\n <mat-icon>info</mat-icon>\n <praxis-rich-content\n rootClassName=\"praxis-row-overflow-menu__label\"\n [nodes]=\"getTableChromeTextRichContentNodes(getRowOverflowMenuEmptyLabel(row), 'praxis-row-overflow-menu__label-text')\"\n ></praxis-rich-content>\n </button>\n }\n </mat-menu>\n </div>\n </td>\n }\n </tr>\n </tbody>\n </table>\n </cdk-virtual-scroll-viewport>\n}\n\n</div>\n@if (shouldShowLoadingSurface()) {\n <div class=\"ptable-loading-state\" role=\"status\" aria-live=\"polite\">\n <div class=\"ptable-loading-state__hero\" aria-hidden=\"true\">\n <span class=\"ptable-loading-state__line ptable-loading-state__line--wide\"></span>\n <span class=\"ptable-loading-state__line ptable-loading-state__line--short\"></span>\n <div class=\"ptable-loading-state__rows\">\n @for (placeholder of [1, 2, 3]; track placeholder) {\n <div class=\"ptable-loading-state__row\">\n <span class=\"ptable-loading-state__cell ptable-loading-state__cell--short\"></span>\n <span class=\"ptable-loading-state__cell\"></span>\n <span class=\"ptable-loading-state__cell ptable-loading-state__cell--wide\"></span>\n </div>\n }\n </div>\n </div>\n <div class=\"ptable-loading-state__message\">\n {{ getLoadingSurfaceMessage() }}\n </div>\n </div>\n}\n@if (shouldShowNoDataState()) {\n <div class=\"ptable-no-data-state\" role=\"status\" aria-live=\"polite\">\n <praxis-empty-state-card\n [icon]=\"getNoDataStateIcon()\"\n [title]=\"getNoDataStateTitle()\"\n [description]=\"getNoDataStateDescription()\"\n [primaryAction]=\"getNoDataStatePrimaryAction()\"\n [secondaryActions]=\"getNoDataStateSecondaryActions()\"\n [inline]=\"true\"\n ></praxis-empty-state-card>\n </div>\n}\n@if (\n shouldRenderDataSurface()\n && !schemaError\n && !dataError\n && shouldRenderFloatingBulkActions()\n && getFloatingBulkActions().length\n && !shouldHideFloatingBulkActions()\n) {\n <div [class]=\"'praxis-floating-bulk-actions ' + getFloatingBulkPositionClass()\">\n @for (action of getFloatingBulkActions(); track getActionId(action)) {\n <button\n mat-mini-fab\n [color]=\"action.color || 'primary'\"\n [disabled]=\"isFloatingBulkActionDisabled(action)\"\n (click)=\"onToolbarAction({ action: getActionId(action), actionConfig: action })\"\n [attr.aria-label]=\"action.label || getActionId(action)\"\n [matTooltip]=\"action.label || getActionId(action)\"\n matTooltipPosition=\"left\"\n >\n <praxis-rich-content\n rootClassName=\"praxis-floating-bulk-actions__content\"\n [nodes]=\"getFloatingBulkActionRichContentNodes(action)\"\n ></praxis-rich-content>\n </button>\n }\n </div>\n}\n<!-- Barra de a\u00C3\u00A7\u00C3\u00B5es no rodap\u00C3\u00A9 (opcional) -->\n@if (shouldRenderDataSurface() && !schemaError && !dataError && showToolbar && shouldRenderFooterToolbar()) {\n <praxis-table-toolbar\n [config]=\"config\"\n [backgroundColor]=\"getToolbarActionsBackgroundColor()\"\n [style.--pfx-filter-h]=\"getToolbarLayoutHeightHostStyle()\"\n [class.footer-flat]=\"hasBottomPaginator()\"\n class=\"praxis-toolbar-footer\"\n placement=\"footer\"\n [showMain]=\"shouldShowFooterToolbarMain()\"\n [showEndActions]=\"shouldShowFooterToolbarEndActions()\"\n [showReset]=\"shouldShowFooterToolbarReset()\"\n [showActionsGroup]=\"shouldShowToolbarActionsBottom()\"\n [showMobileActions]=\"shouldShowToolbarActionsBottom()\"\n [evaluationContext]=\"getToolbarEvaluationContext()\"\n (toolbarAction)=\"onToolbarAction($event)\"\n >\n <praxis-filter\n *ngIf=\"shouldShowFooterToolbarMain() && shouldRenderAdvancedFilter()\"\n advancedFilter\n [resourcePath]=\"getAdvancedFilterResourcePath()\"\n [filterId]=\"tableId\"\n [formId]=\"tableId\"\n [persistenceKey]=\"getAdvancedFilterPersistenceKey()\"\n [fieldMetadata]=\"getAdvancedFilterFieldMetadata()\"\n [enableCustomization]=\"enableCustomization\"\n [alwaysVisibleFields]=\"config.behavior?.filtering?.advancedFilters?.settings?.alwaysVisibleFields\"\n [alwaysVisibleFieldMetadataOverrides]=\"config.behavior?.filtering?.advancedFilters?.settings?.alwaysVisibleFieldMetadataOverrides ?? {}\"\n [selectedFieldIds]=\"config.behavior?.filtering?.advancedFilters?.settings?.selectedFieldIds ?? []\"\n [allowSaveTags]=\"config.behavior?.filtering?.advancedFilters?.settings?.allowSaveTags\"\n [changeDebounceMs]=\"config.behavior?.filtering?.advancedFilters?.settings?.changeDebounceMs ?? 300\"\n [i18n]=\"getFilterI18n()\"\n [mode]=\"'filter'\"\n [showFilterSettings]=\"enableCustomization\"\n (change)=\"onAdvancedFilterChange($event)\"\n (requestSearch)=\"onAdvancedFilterSubmit($event)\"\n (clear)=\"onAdvancedFilterClear()\"\n ></praxis-filter>\n <ng-container *ngIf=\"shouldShowFooterToolbarMain() && shouldRenderAdvancedFilter()\">\n <ng-content select=\"[advancedFilter]\"></ng-content>\n </ng-container>\n <ng-container *ngIf=\"shouldShowFooterToolbarMain() && shouldRenderAdvancedFilter()\">\n <ng-content select=\"[toolbar]\"></ng-content>\n </ng-container>\n @if (shouldShowFooterToolbarMain() && shouldRenderAdvancedFilter()) {\n @defer (on idle) {\n @if (aiAdapter) {\n <praxis-ai-assistant\n [adapter]=\"aiAdapter\"\n end-actions\n ></praxis-ai-assistant>\n }\n }\n }\n <button\n *ngIf=\"shouldShowFooterToolbarMain() && shouldRenderAdvancedFilter() && enableCustomization\"\n end-actions\n mat-icon-button\n color=\"primary\"\n data-role=\"table-settings\"\n data-testid=\"table-settings-trigger\"\n (click)=\"openTableSettings()\"\n [attr.aria-label]=\"getTableSettingsLabel()\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\"\n matBadgeSize=\"small\"\n matBadgePosition=\"above after\"\n [matTooltip]=\"getTableSettingsTooltip()\"\n matTooltipPosition=\"below\"\n >\n <mat-icon>settings</mat-icon>\n </button>\n </praxis-table-toolbar>\n}\n<!-- Paginadores (top/bottom) -->\n@if (shouldRenderDataSurface() && !schemaError && !dataError && getPaginationEnabled() && (config.behavior?.pagination?.position === 'top' || config.behavior?.pagination?.position === 'both')) {\n <mat-paginator\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\n@if (shouldRenderDataSurface() && !schemaError && !dataError && getPaginationEnabled() && (config.behavior?.pagination?.position === 'bottom' || config.behavior?.pagination?.position === 'both' || !config.behavior?.pagination?.position)) {\n <mat-paginator\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 [class.footer-stack]=\"shouldShowToolbarActionsBottom()\"\n >\n </mat-paginator>\n}\n", styles: ["@charset \"UTF-8\";table{width:100%}.praxis-visually-hidden-status{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.praxis-column-reorder-status{margin:8px 0;padding:10px 12px;border-radius:8px;border:1px solid var(--p-table-border-color);background:var(--p-table-row-hover-bg);color:var(--p-table-header-fg);font-size:12px;line-height:1.4;box-shadow:0 4px 12px #00000014;animation:pfx-column-reorder-status-in var(--p-table-drag-status-enter-duration, .18s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}@keyframes pfx-column-reorder-status-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.praxis-actions-cell{height:100%;padding-inline:12px;white-space:nowrap}.pfx-expansion-col-header,.pfx-expansion-col-cell{width:52px;min-width:52px;text-align:center}.pfx-expansion-toggle:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pfx-expansion-detail-row{background:var(--md-sys-color-surface-container-low)}.pfx-expansion-detail-cell{padding:0!important;border-bottom:1px solid var(--p-table-border-color)}.pfx-expansion-detail-panel{padding:12px 16px;border-left:3px solid var(--md-sys-color-primary)}.pfx-expansion-detail-panel.pfx-expansion-motion-none{animation:none;transition:none}.pfx-expansion-detail-panel.pfx-expansion-motion-subtle-slide{animation:pfx-expansion-subtle-slide-in var(--pfx-expansion-motion-duration, .16s) var(--pfx-expansion-motion-easing, cubic-bezier(.2, 0, 0, 1))}.pfx-expansion-detail-panel.pfx-expansion-motion-accordion{animation:pfx-expansion-accordion-in var(--pfx-expansion-motion-duration, .18s) var(--pfx-expansion-motion-easing, cubic-bezier(.2, 0, 0, 1));transform-origin:top center}.pfx-expansion-detail-panel.pfx-expansion-motion-fade-scale{animation:pfx-expansion-fade-scale-in var(--pfx-expansion-motion-duration, .16s) var(--pfx-expansion-motion-easing, cubic-bezier(.2, 0, 0, 1));transform-origin:top center}@keyframes pfx-expansion-subtle-slide-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}@keyframes pfx-expansion-accordion-in{0%{opacity:0;transform:scaleY(.96)}to{opacity:1;transform:scaleY(1)}}@keyframes pfx-expansion-fade-scale-in{0%{opacity:0;transform:translateY(-2px) scale(.985)}to{opacity:1;transform:translateY(0) scale(1)}}.pfx-expansion-detail-schema{margin:0;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;line-height:1.4;white-space:pre-wrap;word-break:break-word;color:var(--md-sys-color-on-surface-variant)}.pfx-expansion-detail-message{font-size:13px;color:var(--md-sys-color-on-surface-variant)}.pfx-expansion-detail-message--error{color:var(--md-sys-color-error)}.pfx-expansion-detail-stack{display:grid;gap:10px}.pfx-expansion-detail-tabs{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:10px}.pfx-expansion-detail-tab-btn{border:1px solid var(--p-table-border-color);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);border-radius:999px;padding:6px 12px;font-size:12px;line-height:1.2;cursor:pointer}.pfx-expansion-detail-tab-btn__content{display:inline-flex;align-items:center;gap:6px;min-width:0}.pfx-expansion-detail-tab-btn__icon .prx-rich-icon{font-size:16px;line-height:1}.pfx-expansion-detail-tab-btn__title{white-space:nowrap}.pfx-expansion-detail-tab-btn__badge .prx-rich-badge{padding:0 8px;min-height:18px;background:color-mix(in srgb,currentColor 12%,transparent);color:inherit}.pfx-expansion-detail-tab-btn.is-active{border-color:var(--md-sys-color-primary);color:var(--md-sys-color-primary);font-weight:600}.pfx-expansion-detail-tab-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pfx-expansion-detail-tab-panel{display:grid;gap:10px}.pfx-expansion-node{border:1px solid var(--p-table-border-color);border-radius:8px;padding:10px 12px;background:var(--md-sys-color-surface)}.pfx-expansion-node-card__header{margin-bottom:8px}.pfx-expansion-node-card__title{margin:0;font-size:14px;line-height:1.3}.pfx-expansion-node-card__subtitle{margin:4px 0 0;font-size:12px;color:var(--md-sys-color-on-surface-variant)}.pfx-expansion-node-value{display:flex;align-items:baseline;gap:8px}.pfx-expansion-node-value__label{color:var(--md-sys-color-on-surface-variant);font-size:12px}.pfx-expansion-node-value__content{font-size:14px}.pfx-expansion-node-list__title{margin:0 0 6px;font-size:13px}.pfx-expansion-node-list ul{margin:0;padding-left:18px}.pfx-expansion-node-rich-list__title{margin:0 0 8px;font-size:13px}.pfx-expansion-node-rich-list__items{display:grid;gap:12px}.pfx-expansion-node-rich-list__item{display:grid;gap:10px;padding:12px;border:1px solid var(--md-sys-color-outline-variant, #d7dbe5);border-radius:14px;background:var(--md-sys-color-surface-container-lowest, #fff)}.pfx-expansion-node-rich-list__item--row{grid-template-columns:minmax(0,1fr) auto;align-items:center}.pfx-expansion-node-rich-list__item--stack{grid-template-columns:minmax(0,1fr)}.pfx-expansion-node-rich-list__item--card-list{grid-template-columns:minmax(0,1fr);box-shadow:0 4px 14px #0f172a0a}.pfx-expansion-node-rich-list__item-host{min-width:0}.pfx-expansion-node-rich-list__item-actions{display:flex;flex-wrap:wrap;gap:8px;align-items:center}.pfx-expansion-node-rich-list__item-action-btn{min-width:0}.pfx-expansion-node-rich-list__item-action-host{display:inline-flex;align-items:center}.pfx-expansion-node-action-bar__title{margin:0 0 12px}.pfx-expansion-node-action-bar__actions{display:flex;flex-wrap:wrap;gap:8px;align-items:center}.pfx-expansion-node-action-bar__action-btn{min-width:0}.pfx-expansion-node-action-bar__action-host{display:inline-flex;align-items:center}.pfx-expansion-node-card-grid__header{margin:0 0 12px}.pfx-expansion-node-card-grid__cards{display:grid;gap:12px;grid-template-columns:var(--pfx-expansion-card-grid-columns, repeat(auto-fit, minmax(var(--pfx-expansion-card-grid-min-width, 220px), 1fr)))}.pfx-expansion-node-card-grid__card-host{min-width:0}.pfx-expansion-node-card-grid__card-host [data-rich-node-type=card]{height:100%}.pfx-expansion-node-richtext :where(p,ul,ol,h1,h2,h3,h4,h5,h6){margin-top:0;margin-bottom:8px}.pfx-expansion-node-placeholder{font-size:12px;color:var(--md-sys-color-on-surface-variant)}:host.density-compact{--p-header-padding: 6px 10px;--p-cell-padding: 6px 10px;--p-actions-btn-size: 30px;--p-actions-icon-size: 16px}:host.density-comfortable{--p-header-padding: 10px 14px;--p-cell-padding: 10px 14px;--p-actions-btn-size: 36px;--p-actions-icon-size: 18px}:host.density-spacious{--p-header-padding: 16px 20px;--p-cell-padding: 16px 20px;--p-actions-btn-size: 44px;--p-actions-icon-size: 24px}:host.density-compact ::ng-deep .mat-mdc-cell{padding:var(--p-cell-padding, 8px 12px)}:host.density-comfortable ::ng-deep .mat-mdc-cell{padding:var(--p-cell-padding, 12px 16px)}:host.density-spacious ::ng-deep .mat-mdc-cell{padding:var(--p-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:6px;width:100%;transition:opacity .12s ease}.praxis-actions-cell.dense .praxis-actions-cell__content{gap:6px}.praxis-actions-cell__content--discovering{opacity:.78}.praxis-row-action-anchor{display:inline-flex;align-items:center}.praxis-row-action-anchor--workflow{position:relative}.praxis-row-action-anchor--workflow:after{content:\"\";position:absolute;right:4px;top:4px;width:6px;height:6px;border-radius:999px;background:var(--md-sys-color-secondary);opacity:.9;pointer-events:none}.praxis-row-action-anchor--blocked:after{background:var(--md-sys-color-error)}.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)}.praxis-icon-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);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-icon-btn--workflow{background:color-mix(in srgb,var(--md-sys-color-secondary-container) 36%,transparent)}.praxis-icon-btn--blocked{background:color-mix(in srgb,var(--md-sys-color-error-container) 42%,transparent)}.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-row-btn--workflow{box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--md-sys-color-secondary) 28%,transparent)}.praxis-row-btn--blocked{box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--md-sys-color-error) 28%,transparent)}.praxis-icon-btn.destructive mat-icon{color:var(--md-sys-color-error)}.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:6px;margin:10px 0 8px;width:100%;clear:both;position:relative}.praxis-table-header.stacked{margin:0}.praxis-table-header>praxis-table-toolbar{flex:1 0 100%}.praxis-floating-bulk-actions{position:fixed;z-index:var(--praxis-layer-floating-local, 200);display:inline-flex;align-items:center;gap:8px;padding:8px;border-radius:999px;background:var(--md-sys-color-surface-container-highest);box-shadow:0 8px 20px #00000029}.praxis-floating-bulk-actions.pos-bottom-right{right:20px;bottom:20px}.praxis-floating-bulk-actions.pos-bottom-left{left:20px;bottom:20px}.praxis-floating-bulk-actions.pos-top-right{right:20px;top:20px}.praxis-floating-bulk-actions.pos-top-left{left:20px;top:20px}@media(max-width:768px){.praxis-floating-bulk-actions{gap:6px;padding:6px}.praxis-floating-bulk-actions.pos-bottom-right{right:12px;left:auto;bottom:12px}.praxis-floating-bulk-actions.pos-bottom-left{left:12px;right:auto;bottom:12px}.praxis-floating-bulk-actions.pos-top-right{right:12px;left:auto;top:12px}.praxis-floating-bulk-actions.pos-top-left{left:12px;right:auto;top:12px}}:host{display:block;width:100%;min-width:0;max-width:100%;--pfx-toolbar-pad-y: 6px;--pfx-toolbar-pad-x: 12px;--p-table-bg: var(--md-sys-color-surface-container-highest);--p-table-text-color: var(--md-sys-color-on-surface);--p-table-header-bg: var(--md-sys-color-surface-container-highest);--p-table-header-fg: var(--md-sys-color-on-surface);--p-table-border-color: var(--md-sys-color-outline-variant);--p-table-row-even-bg: var(--md-sys-color-surface-container);--p-table-row-hover-bg: var(--md-sys-color-surface-container-high);--p-table-row-selected-bg: var(--md-sys-color-primary-container);--p-table-badge-soft-primary-bg: var(--md-sys-color-primary-container);--p-table-badge-soft-primary-fg: var(--md-sys-color-on-primary-container);--p-table-badge-soft-accent-bg: var(--md-sys-color-secondary-container);--p-table-badge-soft-accent-fg: var(--md-sys-color-on-secondary-container);--p-table-badge-soft-warn-bg: var(--md-sys-color-error-container);--p-table-badge-soft-warn-fg: var(--md-sys-color-on-error-container);--p-table-state-success-bg: var(--md-sys-color-tertiary-container);--p-table-state-success-fg: var(--md-sys-color-on-tertiary-container);--p-table-state-warning-bg: var(--md-sys-color-secondary-container);--p-table-state-warning-fg: var(--md-sys-color-on-secondary-container);--p-table-state-danger-bg: var(--md-sys-color-error-container);--p-table-state-danger-fg: var(--md-sys-color-on-error-container);--p-table-state-highlight-bg: var(--md-sys-color-primary-container);--p-table-state-highlight-fg: var(--md-sys-color-on-primary-container);--p-table-drag-handle-size: 14px;--p-table-drag-handle-color: var(--md-sys-color-on-surface-variant);--p-table-drag-handle-hover-color: var(--md-sys-color-on-surface);--p-table-drag-handle-base-opacity: 0;--p-table-drag-handle-visible-opacity: .72;--p-table-drag-handle-active-opacity: 1;--p-table-drag-handle-transition-duration: .16s;--p-table-reorder-transition-duration: .16s;--p-table-drag-preview-scale: 1.01;--p-table-drag-status-enter-duration: .18s;--p-table-drag-preview-shadow: 0 14px 32px rgba(0, 0, 0, .28), 0 0 0 1px var(--p-table-border-color)}:host ::ng-deep .mat-mdc-table{background:var(--p-table-bg);color:var(--p-table-text-color);border-radius:12px 12px 0 0;width:100%;box-shadow:var(--p-table-surface-shadow);transition:box-shadow var(--p-table-selection-transition-duration, .18s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}:host ::ng-deep .mat-mdc-table:hover{box-shadow:var(--p-table-surface-shadow-hover, var(--p-table-surface-shadow))}:host ::ng-deep .mat-mdc-table.table-stack-top{border-top-left-radius:0;border-top-right-radius:0}:host ::ng-deep .praxis-toolbar-footer{border:0 solid var(--p-table-border-color);border-top:0;border-radius:0;background:var(--p-table-bg)}:host ::ng-deep .mat-mdc-paginator.footer-stack{border-top-left-radius:0;border-top-right-radius:0;border-top:0}:host ::ng-deep .mat-mdc-paginator.footer-stack .mat-mdc-paginator-container{padding:6px 10px}:host ::ng-deep .mat-mdc-paginator{border-top:1px solid color-mix(in srgb,var(--p-table-border-color) 82%,transparent);background:color-mix(in srgb,var(--p-table-bg) 94%,white)}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-container{min-height:40px;padding:4px 8px;gap:6px}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-range-label,:host ::ng-deep .mat-mdc-paginator .mat-mdc-select-value-text{font-size:12px;color:color-mix(in srgb,var(--p-table-text-color) 76%,white)}:host [data-role=table-settings].mat-mdc-icon-button{--mdc-icon-button-icon-color: var(--md-sys-color-primary);color:var(--md-sys-color-primary)}.pfx-link{color:var(--md-sys-color-primary);text-decoration:underline;cursor:pointer}.pfx-chip{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:10px;font-size:12px;line-height:1;border:1px solid transparent}.pfx-chip-icon{font-size:14px;width:14px;height:14px}.pfx-chip--outlined{background:transparent;border-color:var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface)}.pfx-chip--filled-primary{background:var(--md-sys-color-primary);color:var(--md-sys-color-on-primary)}.pfx-chip--filled-accent{background:var(--md-sys-color-secondary);color:var(--md-sys-color-on-secondary)}.pfx-chip--filled-warn{background:var(--md-sys-color-error);color:var(--md-sys-color-on-error)}.pfx-chip--soft-primary{background:var(--p-table-badge-soft-primary-bg);color:var(--p-table-badge-soft-primary-fg)}.pfx-chip--soft-accent{background:var(--p-table-badge-soft-accent-bg);color:var(--p-table-badge-soft-accent-fg)}.pfx-chip--soft-warn{background:var(--p-table-badge-soft-warn-bg);color:var(--p-table-badge-soft-warn-fg)}.pfx-progress{position:relative;width:100%;max-width:140px;height:8px;background:var(--md-sys-color-surface-container-highest);border-radius:4px;overflow:hidden;display:inline-block;vertical-align:middle}.pfx-progress-bar{height:100%;background:var(--md-sys-color-primary);transition:width .2s ease}.pfx-progress-label{margin-left:8px;font-size:12px;opacity:.8;display:inline-block;vertical-align:middle}.pfx-avatar{display:inline-flex;align-items:center;justify-content:center;background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface);border-radius:4px;overflow:hidden;font-weight:600}.pfx-avatar.shape-rounded{border-radius:8px}.pfx-avatar.shape-circle{border-radius:999px}.pfx-avatar--initials{text-transform:uppercase;font-size:12px}.pfx-cell-compose{display:inline-flex;align-items:center;gap:6px}.pfx-cell-compose.dir-col{flex-direction:column;align-items:stretch}.pfx-cell-compose.align-start{justify-content:flex-start}.pfx-cell-compose.align-center{justify-content:center}.pfx-cell-compose.align-end{justify-content:flex-end}.pfx-cell-compose.wrap{flex-wrap:wrap}.pfx-cell-compose.ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.px-scroll-viewport{width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;overscroll-behavior-x:contain}.px-scroll-viewport.scroll-none{overflow-x:visible}.px-scroll-viewport.scroll-auto ::ng-deep .mat-mdc-table{width:max-content;min-width:100%}.px-scroll-viewport.scroll-wrap ::ng-deep .mat-mdc-header-cell,.px-scroll-viewport.scroll-wrap ::ng-deep .mat-mdc-cell{white-space:normal;text-overflow:initial}.px-scroll-viewport.scroll-wrap ::ng-deep .mat-mdc-table{width:100%}:host ::ng-deep .mat-mdc-header-row{position:sticky;top:0;z-index:var(--praxis-layer-sticky-local, 100);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}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable .mat-sort-header-container{display:flex;align-items:center;width:100%;gap:4px;cursor:inherit}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable{-webkit-user-select:none;user-select:none;cursor:grab;padding-left:0!important}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable.cdk-drag-dragging{cursor:grabbing}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable .mat-sort-header-content{display:inline-flex;align-items:center;gap:4px;flex:1 1 auto;min-width:0}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-header-label{display:inline-flex;align-items:center;gap:4px;flex:1 1 auto;min-width:0}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-header-label-text{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-column-drag-handle{-webkit-appearance:none;appearance:none;border:0;background:transparent;color:var(--p-table-drag-handle-color);width:var(--p-table-drag-handle-size);min-width:var(--p-table-drag-handle-size);flex:0 0 var(--p-table-drag-handle-size);height:var(--p-table-drag-handle-size);padding:0;display:inline-flex;align-items:center;justify-content:center;border-radius:0;cursor:inherit;pointer-events:none;touch-action:none;opacity:var(--p-table-drag-handle-base-opacity, .42);transform:none;order:-1;margin-inline-end:0;transition:opacity var(--p-table-drag-handle-transition-duration, .16s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1)),color var(--p-table-drag-handle-transition-duration, .16s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable:hover .praxis-column-drag-handle,:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable:focus-within .praxis-column-drag-handle{opacity:var(--p-table-drag-handle-visible-opacity, .72);color:var(--p-table-drag-handle-hover-color, var(--p-table-drag-handle-color))}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-column-drag-handle:active{opacity:var(--p-table-drag-handle-active-opacity, 1);cursor:grabbing}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-column-drag-handle .mat-icon{font-size:14px;width:14px;height:14px;line-height:14px;transition:transform .18s var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable:hover .praxis-column-drag-handle .mat-icon{transform:none}:host ::ng-deep .pfx-column-drag-indicator .cdk-drop-list-dragging .mat-mdc-header-cell:not(.cdk-drag-placeholder){transition:transform var(--p-table-reorder-transition-duration, .18s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}:host ::ng-deep .pfx-column-drag-indicator .mat-mdc-header-cell.cdk-drag-animating{transition:transform var(--p-table-reorder-transition-duration, .16s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}.pfx-column-drag-preview{box-sizing:border-box;display:flex;align-items:center;border-radius:10px;border:1px solid var(--p-table-border-color);background:linear-gradient(135deg,var(--p-table-header-bg) 0%,var(--p-table-row-hover-bg) 100%);color:var(--p-table-header-fg);box-shadow:var(--p-table-drag-preview-shadow);transform:scale(var(--p-table-drag-preview-scale, 1.01));pointer-events:none;z-index:var(--praxis-layer-authoring-hover, 300)}.pfx-column-drag-preview .praxis-column-drag-handle,.pfx-column-drag-preview .mat-sort-header-arrow,.pfx-column-drag-preview .mat-sort-header-indicator,.pfx-column-drag-preview .mat-sort-header-stem,.pfx-column-drag-preview .mat-sort-header-pointer,.pfx-column-drag-preview .mat-sort-header-pointer-left,.pfx-column-drag-preview .mat-sort-header-pointer-right{display:none!important}.pfx-column-drag-preview .mat-sort-header-container{display:flex;align-items:center;width:100%;min-height:100%;padding-right:0!important}.pfx-column-drag-preview .mat-sort-header-content,.pfx-column-drag-preview .praxis-header-label{display:inline-flex;align-items:center;min-width:0;width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host ::ng-deep .pfx-column-drag-indicator .cdk-drag-placeholder{opacity:1;border:1px dashed var(--p-table-border-color);background:var(--p-table-row-hover-bg)}:host ::ng-deep .pfx-column-drag-indicator .cdk-drag-placeholder *{opacity:0}:host ::ng-deep .pfx-column-drag-indicator .mat-mdc-header-cell.cdk-drag-dragging{opacity:.58}@media(prefers-reduced-motion:reduce){:host ::ng-deep .pfx-column-drag-indicator .cdk-drop-list-dragging .mat-mdc-header-cell:not(.cdk-drag-placeholder){transition:none}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-column-drag-handle,:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-column-drag-handle .mat-icon,.pfx-column-drag-preview{transition:none;transform:none}.praxis-column-reorder-status{animation:none}.pfx-expansion-detail-panel{animation:none!important;transition:none!important;transform:none!important}:host ::ng-deep .mat-mdc-row{transition:none}:host ::ng-deep .mat-mdc-table{transition:none}}.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 .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable .mat-sort-header-container{padding-right:12px}@media(pointer:coarse){:host{--p-table-drag-handle-size: 18px;--p-table-drag-handle-base-opacity: .56;--p-table-drag-handle-visible-opacity: .92}}:host ::ng-deep .mat-mdc-cell{color:var(--p-table-text-color);font-size:var(--p-cell-font-size, inherit);line-height:1.25;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}:host ::ng-deep .mat-mdc-cell .pfx-cell-content,:host ::ng-deep .mat-cell .pfx-cell-content{display:inline-flex;align-items:center;gap:6px;width:100%;min-width:0;overflow:hidden}:host ::ng-deep .mat-mdc-row:hover{background:var(--p-table-row-hover-bg)}:host ::ng-deep .mat-mdc-row{transition:background-color var(--p-table-hover-transition-duration, .18s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1)),box-shadow var(--p-table-selection-transition-duration, .18s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}:host ::ng-deep .mat-mdc-row:nth-child(2n){background:var(--p-table-row-even-bg)}:host ::ng-deep .mat-mdc-row:nth-child(2n):hover{background:var(--p-table-row-hover-bg)}:host ::ng-deep .mat-mdc-row.pfx-row-selected,:host ::ng-deep .mat-mdc-row.pfx-row-selected:hover{background:var(--p-table-row-selected-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-error{display:flex;align-items:center;gap:12px;padding:12px;margin:8px 0;border:1px solid var(--md-sys-color-error);border-radius:8px}.ptable-error__content{flex:1}.ptable-error__title{font-weight:600}.ptable-info-banner{display:flex;gap:12px;align-items:center;padding:8px 12px;margin:8px 0;border-radius:8px;border:1px solid var(--md-sys-color-primary);background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.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);border:1px solid var(--md-sys-color-outline-variant)}.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(--md-sys-color-primary);color:var(--md-sys-color-on-primary)}.pfx-badge--filled-accent{background:var(--md-sys-color-secondary);color:var(--md-sys-color-on-secondary)}.pfx-badge--filled-warn{background:var(--md-sys-color-error);color:var(--md-sys-color-on-error)}.pfx-badge--outlined{background:transparent;border-color:var(--md-sys-color-outline-variant);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)}.pfx-expansion-node-embed{display:flex;flex-direction:column;gap:12px;padding:16px;border:1px solid var(--md-sys-color-outline-variant);border-radius:16px;background:var(--md-sys-color-surface-container-low, var(--md-sys-color-surface))}.ptable-loading-state{padding:8px 12px 4px;display:grid;gap:10px}.ptable-loading-state__hero{display:grid;gap:10px;padding:14px;border-radius:8px;border:1px solid rgba(18,99,180,.08);background:linear-gradient(180deg,#ffffffa6,#ffffff47),linear-gradient(0deg,#1263b408,#1263b400)}.ptable-loading-state__rows{display:grid;gap:8px}.ptable-loading-state__row{display:grid;grid-template-columns:96px minmax(0,1fr) minmax(140px,1.2fr);gap:8px}.ptable-loading-state__line,.ptable-loading-state__cell{display:block;height:12px;border-radius:999px;background:linear-gradient(90deg,#1263b414,#1263b42e,#1263b414);background-size:200% 100%;animation:ptable-loading-wave 1.2s ease-in-out infinite}.ptable-loading-state__line--wide{width:180px}.ptable-loading-state__line--short{width:96px}.ptable-loading-state__cell--short{width:100%;max-width:84px}.ptable-loading-state__cell--wide{width:100%}.ptable-loading-state__message{font-size:.85rem;color:var(--md-sys-color-on-surface-variant, #5a5d67);padding-inline:4px}.pfx-expansion-node-embed__header,.pfx-expansion-node-embed__meta,.pfx-expansion-node-embed__empty,.pfx-expansion-node-embed__diagram-label{display:block}.ptable-no-data-state{padding:8px 12px 4px}.ptable-no-data-state ::ng-deep .empty-card{margin:0;border-radius:8px}@keyframes ptable-loading-wave{0%{background-position:100% 0}to{background-position:-100% 0}}.pfx-expansion-node-embed__diagram{display:flex;flex-direction:column;gap:8px}.pfx-expansion-node-embed__diagram-source{margin:0;padding:12px;overflow:auto;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-highest, var(--md-sys-color-surface));color:var(--md-sys-color-on-surface);font-family:Consolas,Courier New,monospace;font-size:12px;line-height:1.4;white-space:pre-wrap}.pfx-expansion-node-embed__action-btn{align-self:flex-start}.pfx-expansion-node-embed__action-host{display:inline-flex}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2$1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i2$1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i2$1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i10$3.CdkScrollable, selector: "[cdk-scrollable], [cdkScrollable]" }, { kind: "directive", type: i11$1.ɵɵCdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i11$1.ɵɵCdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i11$1.ɵɵCdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i12.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i12.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "ngmodule", type: MatBadgeModule }, { kind: "directive", type: i15$2.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { 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.MatMiniFabButton, selector: "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", 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: 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: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i17.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: i17.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i17.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: MatPaginatorModule }, { kind: "component", type: i18.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i6$1.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "ngmodule", type: MatSortModule }, { kind: "directive", type: i20.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i20.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { 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: MatTooltipModule }, { kind: "directive", type: i10.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: PraxisFilter, selector: "praxis-filter", inputs: ["resourcePath", "fieldMetadata", "filterId", "formId", "componentInstanceId", "mode", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "enableCustomization", "value", "alwaysVisibleFields", "alwaysVisibleFieldMetadataOverrides", "selectedFieldIds", "tags", "allowSaveTags", "persistenceKey", "disablePersistence", "i18n", "changeDebounceMs", "showFilterSettings", "showAdvancedButton", "showAddButton", "showClearButton", "showSearchButton", "confirmTagDelete", "placeBooleansInActions", "showToggleLabels", "useInlineSelectVariant", "useInlineSearchableSelectVariant", "useInlineMultiSelectVariant", "useInlineInputVariant", "useInlineToggleVariant", "useInlineRangeVariant", "useInlineDateVariant", "useInlineDateRangeVariant", "useInlineTimeVariant", "useInlineTimeRangeVariant", "useInlineTreeSelectVariant", "alwaysMinWidth", "alwaysColsMd", "alwaysColsLg", "tagColor", "tagVariant", "tagButtonColor", "actionsButtonColor", "actionsVariant", "overlayVariant", "overlayBackdrop", "advancedOpenMode", "advancedClearButtonsEnabled"], outputs: ["submit", "change", "clear", "modeChange", "requestSearch", "tagsChange", "selectedFieldIdsChange", "metaChanged", "schemaStatusChange"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisTableToolbar, selector: "praxis-table-toolbar", inputs: ["config", "backgroundColor", "placement", "showMain", "showActionsGroup", "showEndActions", "showMobileActions", "showReset", "evaluationContext"], outputs: ["toolbarAction", "reset"] }, { kind: "component", type: PraxisRichContent, selector: "praxis-rich-content", inputs: ["document", "nodes", "context", "hostCapabilities", "rootClassName"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline", "tone"] }, { kind: "component", type: TableRatingCellComponent, selector: "praxis-table-rating", inputs: ["itemsCount", "value", "size", "ratingColor", "outlineColor", "ariaLabel"] }], deferBlockDependencies: [() => [import('@praxisui/ai').then(m => m.PraxisAiAssistantComponent)], () => [import('@praxisui/ai').then(m => m.PraxisAiAssistantComponent)], () => [import('@praxisui/ai').then(m => m.PraxisAiAssistantComponent)], () => [import('@praxisui/ai').then(m => m.PraxisAiAssistantComponent)]] });
45443
47082
  }
45444
47083
  i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "20.3.17", ngImport: i0, type: PraxisTable, resolveDeferredDeps: () => [import('@praxisui/ai').then(m => m.PraxisAiAssistantComponent)], resolveMetadata: PraxisAiAssistantComponent => ({ decorators: [{
45445
47084
  type: Component,
@@ -45463,6 +47102,7 @@ i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "20.3.17", n
45463
47102
  PraxisFilter,
45464
47103
  PraxisIconDirective,
45465
47104
  PraxisTableToolbar,
47105
+ PraxisRichContent,
45466
47106
  EmptyStateCardComponent,
45467
47107
  TableRatingCellComponent,
45468
47108
  ], providers: [
@@ -45472,7 +47112,7 @@ i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "20.3.17", n
45472
47112
  TableDefaultsProvider,
45473
47113
  FilterConfigService,
45474
47114
  DataFormattingService
45475
- ], template: "@if (shouldShowEmptyState()) {\n <praxis-empty-state-card\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\n<!-- Error State with Quick Connect CTA -->\n@if (isRemoteMode() && (schemaError || dataError)) {\n<div class=\"ptable-error\" role=\"alert\" aria-live=\"assertive\">\n <mat-icon color=\"warn\" aria-hidden=\"true\">error</mat-icon>\n <div class=\"ptable-error__content\">\n <div class=\"ptable-error__title\">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 @if (!schemaError) { <button mat-stroked-button (click)=\"retryData()\">Tentar novamente</button> }\n @if (schemaError) { <button mat-stroked-button (click)=\"reloadSchema()\">Recarregar colunas</button> }\n </div>\n}\n\n<!-- Inline banner for schema change (only in edit mode) -->\n@if (shouldShowOutdatedInline()) {\n<div 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\n@if (shouldRenderDataSurface() && !schemaError && !dataError && toolbarV2) {\n <div class=\"praxis-table-header\" [class.edit-mode]=\"enableCustomization\" [class.stacked]=\"showToolbar\">\n @if (showToolbar && shouldShowToolbarTopPlacement()) {\n <praxis-table-toolbar\n [config]=\"config\"\n [backgroundColor]=\"getToolbarActionsBackgroundColor()\"\n [style.--pfx-filter-h]=\"getToolbarLayoutHeightHostStyle()\"\n [evaluationContext]=\"getToolbarEvaluationContext()\"\n [showActionsGroup]=\"shouldShowToolbarActionsTop()\"\n [showMobileActions]=\"shouldShowToolbarActionsTop()\"\n (toolbarAction)=\"onToolbarAction($event)\"\n (reset)=\"onResetPreferences()\"\n >\n @if (shouldRenderAdvancedFilter()) {\n <praxis-filter\n advancedFilter\n [resourcePath]=\"getAdvancedFilterResourcePath()\"\n [filterId]=\"tableId\"\n [formId]=\"tableId\"\n [persistenceKey]=\"getAdvancedFilterPersistenceKey()\"\n [fieldMetadata]=\"getAdvancedFilterFieldMetadata()\"\n [enableCustomization]=\"enableCustomization\"\n \n [alwaysVisibleFields]=\"config.behavior?.filtering?.advancedFilters?.settings?.alwaysVisibleFields\"\n [alwaysVisibleFieldMetadataOverrides]=\"config.behavior?.filtering?.advancedFilters?.settings?.alwaysVisibleFieldMetadataOverrides ?? {}\"\n [selectedFieldIds]=\"config.behavior?.filtering?.advancedFilters?.settings?.selectedFieldIds ?? []\"\n [allowSaveTags]=\"config.behavior?.filtering?.advancedFilters?.settings?.allowSaveTags\"\n [changeDebounceMs]=\"config.behavior?.filtering?.advancedFilters?.settings?.changeDebounceMs ?? 300\"\n [i18n]=\"getFilterI18n()\"\n [mode]=\"'filter'\"\n [showFilterSettings]=\"enableCustomization\"\n (change)=\"onAdvancedFilterChange($event)\"\n (requestSearch)=\"onAdvancedFilterSubmit($event)\"\n (clear)=\"onAdvancedFilterClear()\"\n ></praxis-filter>\n }\n <ng-content select=\"[advancedFilter]\" />\n <ng-content select=\"[toolbar]\" />\n \n <!-- AI Assistant in Toolbar -->\n @defer (on idle) {\n @if (aiAdapter) {\n <praxis-ai-assistant [adapter]=\"aiAdapter\" end-actions></praxis-ai-assistant>\n }\n }\n\n @if (enableCustomization) {\n <button end-actions mat-icon-button color=\"primary\" data-role=\"table-settings\" data-testid=\"table-settings-trigger\"\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 }\n </praxis-table-toolbar>\n }\n @if (!showToolbar && enableCustomization) {\n <div class=\"ptable-header-actions\">\n @defer (on idle) {\n @if (aiAdapter) {\n <praxis-ai-assistant [adapter]=\"aiAdapter\"></praxis-ai-assistant>\n }\n }\n <button mat-icon-button color=\"primary\" data-role=\"table-settings\" data-testid=\"table-settings-trigger\" (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 @if (isRemoteMode()) {\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 }\n </div>\n }\n </div>\n} @else {\n @if (shouldRenderDataSurface() && !schemaError && !dataError) {\n @if (showToolbar && shouldShowToolbarTopPlacement()) {\n <praxis-table-toolbar\n [config]=\"config\"\n [backgroundColor]=\"getToolbarActionsBackgroundColor()\"\n [style.--pfx-filter-h]=\"getToolbarLayoutHeightHostStyle()\"\n [evaluationContext]=\"getToolbarEvaluationContext()\"\n [showActionsGroup]=\"shouldShowToolbarActionsTop()\"\n [showMobileActions]=\"shouldShowToolbarActionsTop()\"\n (toolbarAction)=\"onToolbarAction($event)\"\n (reset)=\"onResetPreferences()\"\n >\n @if (shouldRenderAdvancedFilter()) {\n <praxis-filter\n advancedFilter\n [resourcePath]=\"getAdvancedFilterResourcePath()\"\n [filterId]=\"tableId\"\n [formId]=\"tableId\"\n [persistenceKey]=\"getAdvancedFilterPersistenceKey()\"\n [fieldMetadata]=\"getAdvancedFilterFieldMetadata()\"\n [enableCustomization]=\"enableCustomization\"\n \n [alwaysVisibleFields]=\"config.behavior?.filtering?.advancedFilters?.settings?.alwaysVisibleFields\"\n [alwaysVisibleFieldMetadataOverrides]=\"config.behavior?.filtering?.advancedFilters?.settings?.alwaysVisibleFieldMetadataOverrides ?? {}\"\n [selectedFieldIds]=\"config.behavior?.filtering?.advancedFilters?.settings?.selectedFieldIds ?? []\"\n [allowSaveTags]=\"config.behavior?.filtering?.advancedFilters?.settings?.allowSaveTags\"\n [changeDebounceMs]=\"config.behavior?.filtering?.advancedFilters?.settings?.changeDebounceMs ?? 300\"\n [i18n]=\"getFilterI18n()\"\n [mode]=\"'filter'\"\n [showFilterSettings]=\"enableCustomization\"\n (change)=\"onAdvancedFilterChange($event)\"\n (requestSearch)=\"onAdvancedFilterSubmit($event)\"\n (clear)=\"onAdvancedFilterClear()\"\n ></praxis-filter>\n }\n <ng-content select=\"[advancedFilter]\" />\n <ng-content select=\"[toolbar]\" />\n @defer (on idle) {\n @if (aiAdapter) {\n <praxis-ai-assistant [adapter]=\"aiAdapter\" end-actions></praxis-ai-assistant>\n }\n }\n @if (enableCustomization) {\n <button end-actions mat-icon-button color=\"primary\" data-role=\"table-settings\" data-testid=\"table-settings-trigger\"\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 }\n </praxis-table-toolbar>\n }\n @if (!showToolbar && enableCustomization) {\n <div class=\"ptable-header-actions\">\n <button mat-icon-button color=\"primary\" data-role=\"table-settings\" data-testid=\"table-settings-trigger\" (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 }\n }\n}\n<div class=\"px-scroll-viewport\"\n cdkScrollable\n [class.scroll-auto]=\"horizontalScroll === 'auto'\"\n [class.scroll-wrap]=\"horizontalScroll === 'wrap'\"\n [class.scroll-none]=\"horizontalScroll === 'none'\">\n\n@if (shouldRenderDataSurface() && !schemaError && !dataError) {\n<div class=\"praxis-visually-hidden-status\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n {{ columnReorderStatusMessage }}\n</div>\n@if (columnReorderVisualStatusMessage) {\n <div class=\"praxis-column-reorder-status\" role=\"note\">\n {{ columnReorderVisualStatusMessage }}\n </div>\n}\n<table\n mat-table\n data-testid=\"table-column-drag-drop-list\"\n [dataSource]=\"dataSource\"\n [multiTemplateDataRows]=\"isRowExpansionRuntimeEnabled()\"\n cdkDropList\n [cdkDropListDisabled]=\"!isColumnDraggingEnabled()\"\n [cdkDropListData]=\"visibleDataColumnsForDrag\"\n cdkDropListOrientation=\"horizontal\"\n (cdkDropListDropped)=\"onColumnDrop($event)\"\n matSort\n (matSortChange)=\"onSortChange($event)\"\n [matSortDisabled]=\"!getSortingEnabled()\"\n [ngClass]=\"getTableElevationClassName()\"\n [class.table-stack-top]=\"showToolbar\"\n [class.pfx-column-drag-enabled]=\"isColumnDraggingEnabled()\"\n [class.pfx-column-drag-indicator]=\"isColumnDragIndicatorEnabled()\"\n>\n @if (config.behavior?.selection?.enabled) {\n <ng-container\n matColumnDef=\"_select\"\n >\n <th mat-header-cell *matHeaderCellDef>\n @if (canSelectAll()) {\n <mat-checkbox\n (change)=\"masterToggle()\"\n [checked]=\"isAllSelected()\"\n [indeterminate]=\"selection.hasValue() && !isAllSelected()\"\n ></mat-checkbox>\n }\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 }\n @if (isRowExpansionRuntimeEnabled()) {\n <ng-container matColumnDef=\"_expander\">\n <th mat-header-cell *matHeaderCellDef class=\"pfx-expansion-col-header\">\n <span class=\"praxis-visually-hidden-status\">Expandir detalhes da linha</span>\n </th>\n <td mat-cell *matCellDef=\"let row; let i = index\" class=\"pfx-expansion-col-cell\">\n <button\n mat-icon-button\n class=\"pfx-expansion-toggle\"\n [disabled]=\"!isRowExpandable(row, i) || !isExpansionIconTriggerEnabled()\"\n [attr.aria-expanded]=\"isRowExpanded(row, i) ? 'true' : 'false'\"\n [attr.aria-controls]=\"getRowExpansionDetailId(row, i)\"\n [attr.aria-label]=\"getRowExpansionToggleAriaLabel(row, i)\"\n (click)=\"onExpansionToggleFromIcon(row, i, $event)\"\n (keydown)=\"onExpansionToggleKeydown($event, row, i)\"\n >\n <mat-icon [praxisIcon]=\"isRowExpanded(row, i)\n ? getExpansionExpandedIcon()\n : getExpansionCollapsedIcon()\"></mat-icon>\n </button>\n </td>\n </ng-container>\n }\n @for (column of visibleColumns; track column.field) {\n <ng-container\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 cdkDrag\n [cdkDragData]=\"column\"\n cdkDragLockAxis=\"x\"\n cdkDragPreviewClass=\"pfx-column-drag-preview\"\n (cdkDragStarted)=\"onColumnDragStarted(column)\"\n (cdkDragEnded)=\"onColumnDragEnded($event, column)\"\n (keydown)=\"onColumnDragHandleKeydown($event, column)\"\n [cdkDragDisabled]=\"!isColumnDraggingEnabled() || !isColumnDraggable(column)\"\n [class.praxis-header-draggable]=\"isColumnDraggingEnabled() && isColumnDraggable(column)\"\n [disabled]=\"!getSortingEnabled() || column.sortable === false\"\n [style.text-align]=\"getColumnTextAlignStyle(column)\"\n [style.width]=\"getColumnWidthStyle(column)\"\n [attr.style]=\"getColumnHeaderAttrStyle(column)\"\n [attr.aria-label]=\"isColumnDraggingEnabled() && isColumnDraggable(column) ? getColumnDragHandleAriaLabel(column) : null\"\n >\n <span class=\"praxis-header-label\" data-testid=\"column-header-label\">\n @if (isColumnDraggingEnabled() && isColumnDraggable(column)) {\n <span\n class=\"praxis-column-drag-handle\"\n data-testid=\"column-drag-handle\"\n [attr.data-column-field]=\"column.field\"\n aria-hidden=\"true\"\n >\n <mat-icon [praxisIcon]=\"'drag_indicator'\"></mat-icon>\n </span>\n }\n <span class=\"praxis-header-label-text\">{{ column.header }}</span>\n </span>\n </th>\n <td\n mat-cell\n *matCellDef=\"let element\"\n [style.text-align]=\"getColumnTextAlignStyle(column)\"\n [style.width]=\"getColumnWidthStyle(column)\"\n [attr.style]=\"getColumnCellAttrStyle(column)\"\n [ngClass]=\"getCellClasses(element, column)\"\n [ngStyle]=\"getCellNgStyle(element, column)\"\n >\n <div\n class=\"pfx-cell-content\"\n [ngClass]=\"getCellContentClasses(element, column)\"\n [ngStyle]=\"getCellContentNgStyle(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(element, column)\"\n [style.height.px]=\"getImageHeight(element, column)\"\n [class.shape-rounded]=\"getImageShape(element, column) === 'rounded'\"\n [class.shape-circle]=\"getImageShape(element, column) === 'circle'\"\n [style.object-fit]=\"getImageFit(element, column)\"\n />\n </ng-container>\n\n <!-- Badge renderer -->\n <ng-container *ngSwitchCase=\"'badge'\">\n <span class=\"pfx-badge\" [ngClass]=\"getBadgeClasses(element, column)\">\n @if (getBadgeIcon(element, column); as bi) { <mat-icon class=\"pfx-badge-icon\">{{ bi }}</mat-icon> }\n <span class=\"pfx-badge-text\">{{ getBadgeText(element, column) }}</span>\n </span>\n </ng-container>\n\n <!-- Link renderer -->\n <ng-container *ngSwitchCase=\"'link'\">\n <a\n class=\"pfx-link\"\n [attr.href]=\"getLinkHref(element, column) || null\"\n [attr.target]=\"getLinkTarget(element, column) || null\"\n [attr.rel]=\"getLinkRel(element, column) || null\"\n (click)=\"$event.stopPropagation()\"\n >{{ getLinkText(element, column) }}</a\n >\n </ng-container>\n\n <!-- Button renderer -->\n <ng-container *ngSwitchCase=\"'button'\">\n <ng-container [ngSwitch]=\"getButtonVariant(element, column)\">\n <button\n *ngSwitchCase=\"'outlined'\"\n mat-stroked-button\n [color]=\"getButtonColor(element, column) || null\"\n [disabled]=\"isButtonDisabled(element, column)\"\n [attr.aria-label]=\"getButtonAriaLabel(element, column) || getButtonLabel(element, column)\"\n (click)=\"onButtonClick(element, column, $event)\"\n >\n @if (getButtonIcon(element, column); as bi) { <mat-icon>{{ bi }}</mat-icon> }\n {{ getButtonLabel(element, column) }}\n </button>\n <button\n *ngSwitchCase=\"'text'\"\n mat-button\n [color]=\"getButtonColor(element, column) || null\"\n [disabled]=\"isButtonDisabled(element, column)\"\n [attr.aria-label]=\"getButtonAriaLabel(element, column) || getButtonLabel(element, column)\"\n (click)=\"onButtonClick(element, column, $event)\"\n >\n @if (getButtonIcon(element, column); as bi) { <mat-icon>{{ bi }}</mat-icon> }\n {{ getButtonLabel(element, column) }}\n </button>\n <button\n *ngSwitchDefault\n mat-flat-button\n [color]=\"getButtonColor(element, column) || null\"\n [disabled]=\"isButtonDisabled(element, column)\"\n [attr.aria-label]=\"getButtonAriaLabel(element, column) || getButtonLabel(element, column)\"\n (click)=\"onButtonClick(element, column, $event)\"\n >\n @if (getButtonIcon(element, column); as bi) { <mat-icon>{{ bi }}</mat-icon> }\n {{ getButtonLabel(element, column) }}\n </button>\n </ng-container>\n </ng-container>\n\n <!-- Chip renderer -->\n <ng-container *ngSwitchCase=\"'chip'\">\n <span class=\"pfx-chip\" [ngClass]=\"getChipClasses(element, column)\">\n @if (getChipIcon(element, column); as ci) { <mat-icon class=\"pfx-chip-icon\">{{ ci }}</mat-icon> }\n <span class=\"pfx-chip-text\">{{ getChipText(element, column) }}</span>\n </span>\n </ng-container>\n\n <!-- Progress renderer -->\n <ng-container *ngSwitchCase=\"'progress'\">\n <div class=\"pfx-progress\">\n <div class=\"pfx-progress-bar\" [style.width.%]=\"getProgressWidthPercentStyle(element, column)\" [style.background]=\"getProgressBackgroundStyle(element, column)\"></div>\n @if (getProgressShowLabel(element, column)) { <div class=\"pfx-progress-label\">{{ getProgressValue(element, column) }}%</div> }\n </div>\n </ng-container>\n\n <!-- Avatar renderer -->\n <ng-container *ngSwitchCase=\"'avatar'\">\n @if (getAvatarSrc(element, column); as asrc) {\n <img class=\"pfx-avatar\" [src]=\"asrc\" [attr.alt]=\"getAvatarAlt(element, column) || ''\" [ngStyle]=\"getAvatarStyle(element, column)\" [class.shape-rounded]=\"getAvatarShape(element, column) === 'rounded'\" [class.shape-circle]=\"getAvatarShape(element, column) === 'circle'\" loading=\"lazy\" />\n } @else {\n <span class=\"pfx-avatar pfx-avatar--initials\" [ngStyle]=\"getAvatarStyle(element, column)\" [class.shape-rounded]=\"getAvatarShape(element, column) === 'rounded'\" [class.shape-circle]=\"getAvatarShape(element, column) === 'circle'\">{{ getAvatarInitials(element, column) }}</span>\n }\n </ng-container>\n\n <!-- Toggle renderer -->\n <ng-container *ngSwitchCase=\"'toggle'\">\n <mat-slide-toggle\n [checked]=\"getToggleState(element, column)\"\n [disabled]=\"isToggleDisabled(element, column)\"\n [attr.aria-label]=\"getToggleAriaLabel(element, column) || 'Alternar'\"\n (change)=\"onToggleChange(element, column, $event)\"\n (click)=\"$event.stopPropagation()\"\n ></mat-slide-toggle>\n </ng-container>\n\n <!-- Menu renderer -->\n <ng-container *ngSwitchCase=\"'menu'\">\n <button mat-icon-button [matMenuTriggerFor]=\"menuRef\" (click)=\"$event.stopPropagation()\" [attr.aria-label]=\"getMenuAriaLabel(element, column) || 'Menu'\">\n <mat-icon>more_vert</mat-icon>\n </button>\n <mat-menu #menuRef=\"matMenu\">\n <button mat-menu-item *ngFor=\"let it of getMenuItems(element, column)\" (click)=\"onMenuItemClick(it, element, $event)\" [disabled]=\"!it.__visible\" >\n @if (it.icon) { <mat-icon>{{ it.icon }}</mat-icon> }\n <span>{{ it.label }}</span>\n </button>\n </mat-menu>\n </ng-container>\n\n <!-- Rating renderer -->\n <ng-container *ngSwitchCase=\"'rating'\">\n <praxis-table-rating\n class=\"pfx-rating-cell\"\n [itemsCount]=\"getRatingMax(element, column)\"\n [value]=\"getRatingValue(element, column)\"\n [size]=\"getRatingSize(element, column)\"\n [ratingColor]=\"getRatingColor(element, column)\"\n [outlineColor]=\"getRatingOutlineColor(element, column)\"\n [ariaLabel]=\"getRatingAriaLabel(element, column) || column.header\">\n </praxis-table-rating>\n </ng-container>\n\n <!-- HTML renderer (sanitizado) -->\n <ng-container *ngSwitchCase=\"'html'\">\n <span [innerHTML]=\"getSafeHtml(element, column)\"></span>\n </ng-container>\n\n <!-- Compose renderer -->\n <ng-container *ngSwitchCase=\"'compose'\">\n <span class=\"pfx-cell-compose\" [ngClass]=\"getComposeClasses(element, column)\" [ngStyle]=\"getComposeGapStyle(element, column)\">\n <ng-container *ngFor=\"let it of getComposeItems(element, column)\">\n <ng-container [ngSwitch]=\"getItemEffectiveType(element, column, it)\">\n <!-- Reuse helpers by projecting item as faux column -->\n <ng-container *ngSwitchCase=\"'icon'\">\n <mat-icon [color]=\"getIconColor(element, asItemColumn(column, it)) || null\" [ngStyle]=\"getIconStyle(element, asItemColumn(column, it))\" [attr.aria-label]=\"getIconAriaLabel(element, asItemColumn(column, it)) || null\">{{ getIconName(element, asItemColumn(column, it)) }}</mat-icon>\n </ng-container>\n <ng-container *ngSwitchCase=\"'image'\">\n <img class=\"pfx-cell-image\" [src]=\"getImageSrc(element, asItemColumn(column, it))\" [attr.alt]=\"getImageAlt(element, asItemColumn(column, it)) || ''\" [attr.loading]=\"getImageLazy(element, asItemColumn(column, it)) ? 'lazy' : null\" [style.width.px]=\"getImageWidth(element, asItemColumn(column, it))\" [style.height.px]=\"getImageHeight(element, asItemColumn(column, it))\" [class.shape-rounded]=\"getImageShape(element, asItemColumn(column, it)) === 'rounded'\" [class.shape-circle]=\"getImageShape(element, asItemColumn(column, it)) === 'circle'\" [style.object-fit]=\"getImageFit(element, asItemColumn(column, it))\" />\n </ng-container>\n <ng-container *ngSwitchCase=\"'badge'\">\n <span class=\"pfx-badge\" [ngClass]=\"getBadgeClasses(element, asItemColumn(column, it))\">@if (getBadgeIcon(element, asItemColumn(column, it)); as bi) { <mat-icon class=\"pfx-badge-icon\">{{ bi }}</mat-icon> }<span class=\"pfx-badge-text\">{{ getBadgeText(element, asItemColumn(column, it)) }}</span></span>\n </ng-container>\n <ng-container *ngSwitchCase=\"'link'\">\n <a class=\"pfx-link\" [attr.href]=\"getLinkHref(element, asItemColumn(column, it)) || null\" [attr.target]=\"getLinkTarget(element, asItemColumn(column, it)) || null\" [attr.rel]=\"getLinkRel(element, asItemColumn(column, it)) || null\" (click)=\"$event.stopPropagation()\">{{ getLinkText(element, asItemColumn(column, it)) }}</a>\n </ng-container>\n <ng-container *ngSwitchCase=\"'button'\">\n <ng-container [ngSwitch]=\"getButtonVariant(element, asItemColumn(column, it))\">\n <button *ngSwitchCase=\"'outlined'\" mat-stroked-button [color]=\"getButtonColor(element, asItemColumn(column, it)) || null\" [disabled]=\"isButtonDisabled(element, asItemColumn(column, it))\" [attr.aria-label]=\"getButtonAriaLabel(element, asItemColumn(column, it)) || getButtonLabel(element, asItemColumn(column, it))\" (click)=\"onButtonClick(element, asItemColumn(column, it), $event)\">@if (getButtonIcon(element, asItemColumn(column, it)); as bi) { <mat-icon>{{ bi }}</mat-icon> }{{ getButtonLabel(element, asItemColumn(column, it)) }}</button>\n <button *ngSwitchCase=\"'text'\" mat-button [color]=\"getButtonColor(element, asItemColumn(column, it)) || null\" [disabled]=\"isButtonDisabled(element, asItemColumn(column, it))\" [attr.aria-label]=\"getButtonAriaLabel(element, asItemColumn(column, it)) || getButtonLabel(element, asItemColumn(column, it))\" (click)=\"onButtonClick(element, asItemColumn(column, it), $event)\">@if (getButtonIcon(element, asItemColumn(column, it)); as bi) { <mat-icon>{{ bi }}</mat-icon> }{{ getButtonLabel(element, asItemColumn(column, it)) }}</button>\n <button *ngSwitchDefault mat-flat-button [color]=\"getButtonColor(element, asItemColumn(column, it)) || null\" [disabled]=\"isButtonDisabled(element, asItemColumn(column, it))\" [attr.aria-label]=\"getButtonAriaLabel(element, asItemColumn(column, it)) || getButtonLabel(element, asItemColumn(column, it))\" (click)=\"onButtonClick(element, asItemColumn(column, it), $event)\">@if (getButtonIcon(element, asItemColumn(column, it)); as bi) { <mat-icon>{{ bi }}</mat-icon> }{{ getButtonLabel(element, asItemColumn(column, it)) }}</button>\n </ng-container>\n </ng-container>\n <ng-container *ngSwitchCase=\"'chip'\">\n <span class=\"pfx-chip\" [ngClass]=\"getChipClasses(element, asItemColumn(column, it))\">@if (getChipIcon(element, asItemColumn(column, it)); as ci) { <mat-icon class=\"pfx-chip-icon\">{{ ci }}</mat-icon> }<span class=\"pfx-chip-text\">{{ getChipText(element, asItemColumn(column, it)) }}</span></span>\n </ng-container>\n <ng-container *ngSwitchCase=\"'progress'\">\n <div class=\"pfx-progress\"><div class=\"pfx-progress-bar\" [style.width.%]=\"getProgressWidthPercentStyle(element, asItemColumn(column, it))\" [style.background]=\"getProgressBackgroundStyle(element, asItemColumn(column, it))\"></div>@if (getProgressShowLabel(element, asItemColumn(column, it))) { <div class=\"pfx-progress-label\">{{ getProgressValue(element, asItemColumn(column, it)) }}%</div> }</div>\n </ng-container>\n <ng-container *ngSwitchCase=\"'avatar'\">\n @if (getAvatarSrc(element, asItemColumn(column, it)); as asrc) {\n <img class=\"pfx-avatar\" [src]=\"asrc\" [attr.alt]=\"getAvatarAlt(element, asItemColumn(column, it)) || ''\" [ngStyle]=\"getAvatarStyle(element, asItemColumn(column, it))\" [class.shape-rounded]=\"getAvatarShape(element, asItemColumn(column, it)) === 'rounded'\" [class.shape-circle]=\"getAvatarShape(element, asItemColumn(column, it)) === 'circle'\" loading=\"lazy\" />\n } @else {\n <span class=\"pfx-avatar pfx-avatar--initials\" [ngStyle]=\"getAvatarStyle(element, asItemColumn(column, it))\" [class.shape-rounded]=\"getAvatarShape(element, asItemColumn(column, it)) === 'rounded'\" [class.shape-circle]=\"getAvatarShape(element, asItemColumn(column, it)) === 'circle'\">{{ getAvatarInitials(element, asItemColumn(column, it)) }}</span>\n }\n </ng-container>\n <ng-container *ngSwitchCase=\"'toggle'\">\n <mat-slide-toggle [checked]=\"getToggleState(element, asItemColumn(column, it))\" [disabled]=\"isToggleDisabled(element, asItemColumn(column, it))\" [attr.aria-label]=\"getToggleAriaLabel(element, asItemColumn(column, it)) || 'Alternar'\" (change)=\"onToggleChange(element, asItemColumn(column, it), $event)\" (click)=\"$event.stopPropagation()\"></mat-slide-toggle>\n </ng-container>\n <ng-container *ngSwitchCase=\"'menu'\">\n <button mat-icon-button [matMenuTriggerFor]=\"menuRef\" (click)=\"$event.stopPropagation()\" [attr.aria-label]=\"getMenuAriaLabel(element, asItemColumn(column, it)) || 'Menu'\"><mat-icon>more_vert</mat-icon></button>\n <mat-menu #menuRef=\"matMenu\">\n <button mat-menu-item *ngFor=\"let mi of getMenuItems(element, asItemColumn(column, it))\" (click)=\"onMenuItemClick(mi, element, $event)\" [disabled]=\"!mi.__visible\">\n @if (mi.icon) { <mat-icon>{{ mi.icon }}</mat-icon> }\n <span>{{ mi.label }}</span>\n </button>\n </mat-menu>\n </ng-container>\n <ng-container *ngSwitchCase=\"'rating'\">\n <praxis-table-rating\n class=\"pfx-rating-cell\"\n [itemsCount]=\"getRatingMax(element, asItemColumn(column, it))\"\n [value]=\"getRatingValue(element, asItemColumn(column, it))\"\n [size]=\"getRatingSize(element, asItemColumn(column, it))\"\n [ratingColor]=\"getRatingColor(element, asItemColumn(column, it))\"\n [outlineColor]=\"getRatingOutlineColor(element, asItemColumn(column, it))\"\n [ariaLabel]=\"getRatingAriaLabel(element, asItemColumn(column, it)) || column.header\">\n </praxis-table-rating>\n </ng-container>\n <ng-container *ngSwitchCase=\"'html'\">\n <span [innerHTML]=\"getSafeHtml(element, asItemColumn(column, it))\"></span>\n </ng-container>\n <!-- Value item: render base cell text alongside visuals -->\n <ng-container *ngSwitchCase=\"'value'\">\n <span class=\"pfx-cell-value\">{{ getComposeItemValue(element, column, it) }}</span>\n </ng-container>\n </ng-container>\n </ng-container>\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 </div>\n </td>\n </ng-container>\n }\n @if (config.actions?.row?.enabled) {\n <ng-container 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]=\"getRowActionsWidthStyle()\" 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 @if (getActionsHeaderIcon(); as hi) { <mat-icon [praxisIcon]=\"hi\"></mat-icon> }\n @if (getActionsHeaderLabel(); as hl) { <span class=\"label\">{{ 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]=\"getRowActionsWidthStyle()\"\n >\n <div\n class=\"praxis-actions-cell__content\"\n [class.praxis-actions-cell__content--discovering]=\"isRowDiscoveryPending(row)\"\n [attr.aria-busy]=\"isRowDiscoveryPending(row) ? 'true' : null\"\n >\n <!-- A\u00E7\u00F5es inline -->\n <!-- Inline actions: icons mode -->\n @if (config.actions?.row?.display === 'icons' || !config.actions?.row?.display) {\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <span\n class=\"praxis-row-action-anchor\"\n [class.praxis-row-action-anchor--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-row-action-anchor--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [matTooltip]=\"getActionTooltip(a, row)\"\n [matTooltipDisabled]=\"!getActionTooltip(a, row)\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"praxis-tooltip\"\n >\n <button\n mat-icon-button\n class=\"praxis-icon-btn\"\n [class.praxis-icon-btn--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-icon-btn--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event, getRowActionRuntimeOptions(a, row))\"\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 </span>\n </ng-container>\n }\n\n <!-- Inline actions: buttons mode (show label + icon) -->\n @if (config.actions?.row?.display === 'buttons') {\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <span\n class=\"praxis-row-action-anchor\"\n [class.praxis-row-action-anchor--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-row-action-anchor--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [matTooltip]=\"getActionTooltip(a, row)\"\n [matTooltipDisabled]=\"!getActionTooltip(a, row)\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"praxis-tooltip\"\n >\n <button\n mat-flat-button\n class=\"praxis-row-btn\"\n [class.praxis-row-btn--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-row-btn--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event, getRowActionRuntimeOptions(a, row))\"\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 </span>\n </ng-container>\n }\n\n <!-- Menu de overflow -->\n @if (hasOverflowRowActions(row)) {\n <button\n mat-icon-button\n class=\"praxis-icon-btn praxis-more-btn\"\n [matMenuTriggerFor]=\"rowMoreMenu\"\n (menuOpened)=\"onRowOverflowMenuOpened(row)\"\n [color]=\"getRowMenuButtonColor() || null\"\n aria-label=\"Mais a\u00E7\u00F5es\"\n [matTooltip]=\"getRowMenuTooltip(row)\"\n matTooltipPosition=\"above\"\n >\n <mat-icon [praxisIcon]=\"getRowMenuIcon()\"></mat-icon>\n </button>\n }\n <mat-menu #rowMoreMenu=\"matMenu\" xPosition=\"before\">\n @if (hasRowOverflowMenuLoadingState(row)) {\n <button mat-menu-item disabled>\n <mat-icon>hourglass_empty</mat-icon>\n <span>{{ getRowOverflowMenuLoadingLabel() }}</span>\n </button>\n }\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, getRowActionRuntimeOptions(a, row))\"\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 @if (hasRowOverflowMenuEmptyState(row)) {\n <button mat-menu-item disabled>\n <mat-icon>info</mat-icon>\n <span>{{ getRowOverflowMenuEmptyLabel(row) }}</span>\n </button>\n }\n </mat-menu>\n </div>\n </td>\n </ng-container>\n }\n\n <tr\n mat-header-row\n *matHeaderRowDef=\"displayedColumns\"\n ></tr>\n @if (!isVirtualized()) {\n <tr\n mat-row\n *matRowDef=\"let row; let i = index; columns: displayedColumns\"\n [class.pfx-row-selected]=\"selection.isSelected(row)\"\n [class.pfx-row-expanded]=\"isRowExpansionRuntimeEnabled() && isRowExpanded(row, i)\"\n [attr.aria-selected]=\"config.behavior?.selection?.enabled ? (selection.isSelected(row) ? 'true' : 'false') : null\"\n [attr.aria-expanded]=\"isRowExpansionRuntimeEnabled() ? (isRowExpanded(row, i) ? 'true' : 'false') : null\"\n [ngClass]=\"getRowClasses(row)\"\n [ngStyle]=\"getRowNgStyle(row)\"\n [matTooltip]=\"getRowTooltip(row) || null\"\n [matTooltipDisabled]=\"!getRowTooltip(row)\"\n [matTooltipPosition]=\"getRowTooltipPosition(row)\"\n [matTooltipShowDelay]=\"getRowTooltipShowDelay(row)\"\n (click)=\"onRowClicked(row, i, $event)\"\n (dblclick)=\"onRowDoubleClicked(row, i)\"\n ></tr>\n @if (isRowExpansionRuntimeEnabled()) {\n <ng-container matColumnDef=\"_detail\">\n <td\n mat-cell\n *matCellDef=\"let row; let i = index\"\n class=\"pfx-expansion-detail-cell\"\n [attr.colspan]=\"displayedColumns.length\"\n >\n <section\n class=\"pfx-expansion-detail-panel\"\n [ngClass]=\"getExpansionMotionPresetClass()\"\n [ngStyle]=\"getExpansionMotionStyle()\"\n [attr.id]=\"getRowExpansionDetailId(row, i)\"\n role=\"region\"\n [attr.aria-label]=\"getRowExpansionRegionAriaLabel(row, i)\"\n [attr.aria-busy]=\"getExpansionDetailViewState(row, i).status === 'loading' ? 'true' : 'false'\"\n >\n @let detailState = getExpansionDetailViewState(row, i);\n @if (detailState.status === 'loading') {\n <div class=\"pfx-expansion-detail-message\" role=\"status\" aria-live=\"polite\">\n Carregando detail schema...\n </div>\n } @else if (detailState.status !== 'ready') {\n <div\n class=\"pfx-expansion-detail-message pfx-expansion-detail-message--error\"\n role=\"alert\"\n aria-live=\"assertive\"\n aria-atomic=\"true\"\n >\n {{ detailState.message || 'Detail indispon\u00EDvel para esta linha.' }}\n </div>\n } @else {\n @if (getExpansionDetailLayout(detailState.schema) === 'tabs') {\n @let detailTabs = getExpansionDetailTabs(detailState.schema);\n @if (detailTabs.length) {\n <div class=\"pfx-expansion-detail-tabs\" role=\"tablist\" aria-label=\"Se\u00E7\u00F5es do detail\">\n @for (tab of detailTabs; track $index; let tabIndex = $index) {\n <button\n type=\"button\"\n class=\"pfx-expansion-detail-tab-btn\"\n role=\"tab\"\n [attr.id]=\"getExpansionDetailTabId(row, i, tab, tabIndex)\"\n [attr.aria-controls]=\"getExpansionDetailPanelId(row, i, tab, tabIndex)\"\n [attr.aria-selected]=\"isExpansionDetailTabActive(row, i, tab, tabIndex, detailTabs) ? 'true' : 'false'\"\n [attr.tabindex]=\"isExpansionDetailTabActive(row, i, tab, tabIndex, detailTabs) ? '0' : '-1'\"\n [class.is-active]=\"isExpansionDetailTabActive(row, i, tab, tabIndex, detailTabs)\"\n (click)=\"onExpansionDetailTabSelect(row, i, tab, tabIndex, $event)\"\n (keydown)=\"onExpansionDetailTabKeydown($event, row, i, tabIndex, detailTabs)\"\n >\n {{ getExpansionDetailNodeTitle(tab, 'Tab') }}\n </button>\n }\n </div>\n @for (tab of detailTabs; track $index; let tabIndex = $index) {\n @if (isExpansionDetailTabActive(row, i, tab, tabIndex, detailTabs)) {\n <div\n class=\"pfx-expansion-detail-tab-panel\"\n role=\"tabpanel\"\n [attr.id]=\"getExpansionDetailPanelId(row, i, tab, tabIndex)\"\n [attr.aria-labelledby]=\"getExpansionDetailTabId(row, i, tab, tabIndex)\"\n >\n @for (childNode of getExpansionDetailNodeChildren(tab); track $index) {\n <ng-container\n *ngTemplateOutlet=\"\n expansionDetailNodeTemplate;\n context: { $implicit: childNode, row: row, index: i }\n \"\n ></ng-container>\n }\n </div>\n }\n }\n } @else {\n <div class=\"pfx-expansion-detail-message\">\n Schema em layout tabs sem abas v\u00E1lidas.\n </div>\n }\n } @else {\n <div class=\"pfx-expansion-detail-stack\">\n @for (node of getExpansionDetailItems(detailState.schema); track $index) {\n <ng-container\n *ngTemplateOutlet=\"\n expansionDetailNodeTemplate;\n context: { $implicit: node, row: row, index: i }\n \"\n ></ng-container>\n }\n </div>\n }\n }\n\n <ng-template #expansionDetailNodeTemplate let-node let-row=\"row\" let-index=\"index\">\n @switch (getExpansionDetailNodeType(node)) {\n @case ('card') {\n <article class=\"pfx-expansion-node pfx-expansion-node-card\">\n <header class=\"pfx-expansion-node-card__header\">\n <h5 class=\"pfx-expansion-node-card__title\">\n {{ getExpansionDetailNodeTitle(node, 'Card') }}\n </h5>\n @if (node?.subtitle) {\n <p class=\"pfx-expansion-node-card__subtitle\">{{ node?.subtitle }}</p>\n }\n </header>\n @for (childNode of getExpansionDetailNodeChildren(node); track $index) {\n <ng-container\n *ngTemplateOutlet=\"\n expansionDetailNodeTemplate;\n context: { $implicit: childNode, row: row, index: index }\n \"\n ></ng-container>\n }\n </article>\n }\n @case ('value') {\n <div class=\"pfx-expansion-node pfx-expansion-node-value\">\n <span class=\"pfx-expansion-node-value__label\">\n {{ getExpansionDetailNodeTitle(node, 'Valor') }}\n </span>\n <strong class=\"pfx-expansion-node-value__content\">\n {{ getExpansionDetailValue(row, node) }}\n </strong>\n </div>\n }\n @case ('list') {\n <section class=\"pfx-expansion-node pfx-expansion-node-list\">\n <h6 class=\"pfx-expansion-node-list__title\">\n {{ getExpansionDetailNodeTitle(node, 'Lista') }}\n </h6>\n @let listItems = getExpansionDetailListItems(row, node);\n @if (listItems.length) {\n <ul>\n @for (entry of listItems; track $index) {\n <li>{{ entry }}</li>\n }\n </ul>\n } @else {\n <p class=\"pfx-expansion-node-placeholder\">Sem itens.</p>\n }\n </section>\n }\n @case ('richText') {\n <div\n class=\"pfx-expansion-node pfx-expansion-node-richtext\"\n [innerHTML]=\"getExpansionDetailRichText(node)\"\n ></div>\n }\n @case ('formRef') {\n <div class=\"pfx-expansion-node pfx-expansion-node-placeholder\">\n Formul\u00E1rio referenciado: <code>{{ node?.schemaId || node?.id || 'sem-id' }}</code>\n </div>\n }\n @case ('tableRef') {\n <div class=\"pfx-expansion-node pfx-expansion-node-placeholder\">\n Tabela referenciada: <code>{{ node?.schemaId || node?.id || 'sem-id' }}</code>\n </div>\n }\n @case ('chartRef') {\n <div class=\"pfx-expansion-node pfx-expansion-node-placeholder\">\n Gr\u00E1fico referenciado: <code>{{ node?.schemaId || node?.id || 'sem-id' }}</code>\n </div>\n }\n @case ('templateRef') {\n <div class=\"pfx-expansion-node pfx-expansion-node-placeholder\">\n Template referenciado: <code>{{ node?.id || node?.templateId || 'sem-id' }}</code>\n </div>\n }\n @case ('action') {\n <div class=\"pfx-expansion-node pfx-expansion-node-placeholder\">\n A\u00E7\u00E3o declarada: <code>{{ node?.id || node?.actionId || 'sem-id' }}</code>\n </div>\n }\n @case ('tab') {\n <div class=\"pfx-expansion-node pfx-expansion-node-placeholder\">\n Node <code>tab</code> fora de contexto de tabs.\n </div>\n }\n @default {\n <div class=\"pfx-expansion-node pfx-expansion-node-placeholder\">\n Node n\u00E3o suportado: <code>{{ getExpansionDetailNodeType(node) }}</code>\n </div>\n }\n }\n </ng-template>\n </section>\n </td>\n </ng-container>\n <tr\n mat-row\n *matRowDef=\"let row; columns: expansionDetailRowColumns; when: isExpansionDetailRow\"\n class=\"pfx-expansion-detail-row\"\n ></tr>\n }\n }\n</table>\n}\n\n<!-- Virtual rows path (header preserved above) -->\n@if (shouldRenderDataSurface() && !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]=\"getVirtMinHeightHostStyle()\"\n >\n <table\n class=\"mat-mdc-table\"\n [ngClass]=\"getTableElevationClassName()\"\n [style.width]=\"getVirtualTableWidthStyle()\"\n >\n <tbody>\n <tr class=\"mat-mdc-row\"\n *cdkVirtualFor=\"let row of dataSource.data; let i = index; trackBy: trackByRow\"\n [class.pfx-row-selected]=\"selection.isSelected(row)\"\n [attr.aria-selected]=\"config.behavior?.selection?.enabled ? (selection.isSelected(row) ? 'true' : 'false') : null\"\n [ngClass]=\"getRowClasses(row)\"\n [ngStyle]=\"getRowNgStyle(row)\"\n [matTooltip]=\"getRowTooltip(row) || null\"\n [matTooltipDisabled]=\"!getRowTooltip(row)\"\n [matTooltipPosition]=\"getRowTooltipPosition(row)\"\n [matTooltipShowDelay]=\"getRowTooltipShowDelay(row)\"\n (click)=\"onRowClicked(row, i, $event)\"\n (dblclick)=\"onRowDoubleClicked(row, i)\">\n <!-- Selection column -->\n @if (config.behavior?.selection?.enabled) { <td class=\"mat-mdc-cell\">\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 @for (column of visibleColumns; track column.field) {\n <td class=\"mat-mdc-cell\"\n [style.text-align]=\"getColumnTextAlignStyle(column)\"\n [style.width]=\"getColumnWidthStyle(column)\"\n [attr.style]=\"getColumnCellAttrStyle(column)\"\n [ngClass]=\"getCellClasses(row, column)\"\n [ngStyle]=\"getCellNgStyle(row, column)\">\n <div\n class=\"pfx-cell-content\"\n [ngClass]=\"getCellContentClasses(row, column)\"\n [ngStyle]=\"getCellContentNgStyle(row, column)\"\n >\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(row, column)\"\n [style.height.px]=\"getImageHeight(row, column)\"\n [class.shape-rounded]=\"getImageShape(row, column) === 'rounded'\"\n [class.shape-circle]=\"getImageShape(row, column) === 'circle'\"\n [style.object-fit]=\"getImageFit(row, column)\" />\n </ng-container>\n <ng-container *ngSwitchCase=\"'badge'\">\n <span class=\"pfx-badge\" [ngClass]=\"getBadgeClasses(row, column)\">\n @if (getBadgeIcon(row, column); as bi) { <mat-icon 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 </div>\n </td>\n }\n\n <!-- Actions column -->\n @if (config.actions?.row?.enabled) {\n <td class=\"mat-mdc-cell praxis-actions-cell\" [class.dense]=\"dense\" [style.width]=\"getRowActionsWidthStyle()\">\n <div\n class=\"praxis-actions-cell__content\"\n [class.praxis-actions-cell__content--discovering]=\"isRowDiscoveryPending(row)\"\n [attr.aria-busy]=\"isRowDiscoveryPending(row) ? 'true' : null\"\n >\n @if (config.actions?.row?.display === 'icons' || !config.actions?.row?.display) {\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <span\n class=\"praxis-row-action-anchor\"\n [class.praxis-row-action-anchor--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-row-action-anchor--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [matTooltip]=\"getActionTooltip(a, row)\"\n [matTooltipDisabled]=\"!getActionTooltip(a, row)\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"praxis-tooltip\"\n >\n <button mat-icon-button class=\"praxis-icon-btn\"\n [class.praxis-icon-btn--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-icon-btn--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event, getRowActionRuntimeOptions(a, row))\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\">\n <mat-icon [praxisIcon]=\"a.icon\"></mat-icon>\n </button>\n </span>\n </ng-container>\n }\n @if (config.actions?.row?.display === 'buttons') {\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <span\n class=\"praxis-row-action-anchor\"\n [class.praxis-row-action-anchor--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-row-action-anchor--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [matTooltip]=\"getActionTooltip(a, row)\"\n [matTooltipDisabled]=\"!getActionTooltip(a, row)\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"praxis-tooltip\"\n >\n <button mat-flat-button class=\"praxis-row-btn\"\n [class.praxis-row-btn--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-row-btn--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event, getRowActionRuntimeOptions(a, row))\"\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 </span>\n </ng-container>\n }\n @if (hasOverflowRowActions(row)) {\n <button mat-icon-button class=\"praxis-icon-btn praxis-more-btn\"\n [matMenuTriggerFor]=\"rowMoreMenuV\"\n (menuOpened)=\"onRowOverflowMenuOpened(row)\"\n [color]=\"getRowMenuButtonColor() || null\"\n aria-label=\"Mais a\u00E7\u00F5es\"\n [matTooltip]=\"getRowMenuTooltip(row)\"\n matTooltipPosition=\"above\">\n <mat-icon [praxisIcon]=\"getRowMenuIcon()\"></mat-icon>\n </button>\n }\n <mat-menu #rowMoreMenuV=\"matMenu\" xPosition=\"before\">\n @if (hasRowOverflowMenuLoadingState(row)) {\n <button mat-menu-item disabled>\n <mat-icon>hourglass_empty</mat-icon>\n <span>{{ getRowOverflowMenuLoadingLabel() }}</span>\n </button>\n }\n <ng-container *ngFor=\"let a of getOverflowRowActions(row); trackBy: trackAction\">\n <button mat-menu-item (click)=\"onRowAction(getActionId(a), row, $event, getRowActionRuntimeOptions(a, row))\" [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 @if (hasRowOverflowMenuEmptyState(row)) {\n <button mat-menu-item disabled>\n <mat-icon>info</mat-icon>\n <span>{{ getRowOverflowMenuEmptyLabel(row) }}</span>\n </button>\n }\n </mat-menu>\n </div>\n </td>\n }\n </tr>\n </tbody>\n </table>\n </cdk-virtual-scroll-viewport>\n}\n\n</div>\n@if (\n shouldRenderDataSurface()\n && !schemaError\n && !dataError\n && shouldRenderFloatingBulkActions()\n && getFloatingBulkActions().length\n && !shouldHideFloatingBulkActions()\n) {\n <div [class]=\"'praxis-floating-bulk-actions ' + getFloatingBulkPositionClass()\">\n @for (action of getFloatingBulkActions(); track getActionId(action)) {\n <button\n mat-mini-fab\n [color]=\"action.color || 'primary'\"\n [disabled]=\"isFloatingBulkActionDisabled(action)\"\n (click)=\"onToolbarAction({ action: getActionId(action), actionConfig: action })\"\n [attr.aria-label]=\"action.label || getActionId(action)\"\n [matTooltip]=\"action.label || getActionId(action)\"\n matTooltipPosition=\"left\"\n >\n <mat-icon [praxisIcon]=\"action.icon || 'done_all'\"></mat-icon>\n </button>\n }\n </div>\n}\n<!-- Barra de a\u00E7\u00F5es no rodap\u00E9 (opcional) -->\n@if (shouldRenderDataSurface() && !schemaError && !dataError && showToolbar && shouldRenderFooterToolbar()) {\n <praxis-table-toolbar\n [config]=\"config\"\n [backgroundColor]=\"getToolbarActionsBackgroundColor()\"\n [style.--pfx-filter-h]=\"getToolbarLayoutHeightHostStyle()\"\n [class.footer-flat]=\"hasBottomPaginator()\"\n class=\"praxis-toolbar-footer\"\n placement=\"footer\"\n [showMain]=\"shouldShowFooterToolbarMain()\"\n [showEndActions]=\"shouldShowFooterToolbarEndActions()\"\n [showReset]=\"shouldShowFooterToolbarReset()\"\n [showActionsGroup]=\"shouldShowToolbarActionsBottom()\"\n [showMobileActions]=\"shouldShowToolbarActionsBottom()\"\n [evaluationContext]=\"getToolbarEvaluationContext()\"\n (toolbarAction)=\"onToolbarAction($event)\"\n >\n <praxis-filter\n *ngIf=\"shouldShowFooterToolbarMain() && shouldRenderAdvancedFilter()\"\n advancedFilter\n [resourcePath]=\"getAdvancedFilterResourcePath()\"\n [filterId]=\"tableId\"\n [formId]=\"tableId\"\n [persistenceKey]=\"getAdvancedFilterPersistenceKey()\"\n [fieldMetadata]=\"getAdvancedFilterFieldMetadata()\"\n [enableCustomization]=\"enableCustomization\"\n [alwaysVisibleFields]=\"config.behavior?.filtering?.advancedFilters?.settings?.alwaysVisibleFields\"\n [alwaysVisibleFieldMetadataOverrides]=\"config.behavior?.filtering?.advancedFilters?.settings?.alwaysVisibleFieldMetadataOverrides ?? {}\"\n [selectedFieldIds]=\"config.behavior?.filtering?.advancedFilters?.settings?.selectedFieldIds ?? []\"\n [allowSaveTags]=\"config.behavior?.filtering?.advancedFilters?.settings?.allowSaveTags\"\n [changeDebounceMs]=\"config.behavior?.filtering?.advancedFilters?.settings?.changeDebounceMs ?? 300\"\n [i18n]=\"getFilterI18n()\"\n [mode]=\"'filter'\"\n [showFilterSettings]=\"enableCustomization\"\n (change)=\"onAdvancedFilterChange($event)\"\n (requestSearch)=\"onAdvancedFilterSubmit($event)\"\n (clear)=\"onAdvancedFilterClear()\"\n ></praxis-filter>\n <ng-container *ngIf=\"shouldShowFooterToolbarMain() && shouldRenderAdvancedFilter()\">\n <ng-content select=\"[advancedFilter]\"></ng-content>\n </ng-container>\n <ng-container *ngIf=\"shouldShowFooterToolbarMain() && shouldRenderAdvancedFilter()\">\n <ng-content select=\"[toolbar]\"></ng-content>\n </ng-container>\n @if (shouldShowFooterToolbarMain() && shouldRenderAdvancedFilter()) {\n @defer (on idle) {\n @if (aiAdapter) {\n <praxis-ai-assistant\n [adapter]=\"aiAdapter\"\n end-actions\n ></praxis-ai-assistant>\n }\n }\n }\n <button\n *ngIf=\"shouldShowFooterToolbarMain() && shouldRenderAdvancedFilter() && enableCustomization\"\n end-actions\n mat-icon-button\n color=\"primary\"\n data-role=\"table-settings\"\n data-testid=\"table-settings-trigger\"\n (click)=\"openTableSettings()\"\n aria-label=\"Configura\u00E7\u00F5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\"\n matBadgeSize=\"small\"\n matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u2014 Reconciliar' : 'Configura\u00E7\u00F5es'\"\n matTooltipPosition=\"below\"\n >\n <mat-icon>settings</mat-icon>\n </button>\n </praxis-table-toolbar>\n}\n<!-- Paginadores (top/bottom) -->\n@if (shouldRenderDataSurface() && !schemaError && !dataError && getPaginationEnabled() && (config.behavior?.pagination?.position === 'top' || config.behavior?.pagination?.position === 'both')) {\n <mat-paginator\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\n@if (shouldRenderDataSurface() && !schemaError && !dataError && getPaginationEnabled() && (config.behavior?.pagination?.position === 'bottom' || config.behavior?.pagination?.position === 'both' || !config.behavior?.pagination?.position)) {\n <mat-paginator\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 [class.footer-stack]=\"shouldShowToolbarActionsBottom()\"\n >\n </mat-paginator>\n}\n", styles: ["@charset \"UTF-8\";table{width:100%}.praxis-visually-hidden-status{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.praxis-column-reorder-status{margin:8px 0;padding:10px 12px;border-radius:8px;border:1px solid var(--p-table-border-color);background:var(--p-table-row-hover-bg);color:var(--p-table-header-fg);font-size:12px;line-height:1.4;box-shadow:0 4px 12px #00000014;animation:pfx-column-reorder-status-in var(--p-table-drag-status-enter-duration, .18s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}@keyframes pfx-column-reorder-status-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.praxis-actions-cell{height:100%;padding-inline:12px;white-space:nowrap}.pfx-expansion-col-header,.pfx-expansion-col-cell{width:52px;min-width:52px;text-align:center}.pfx-expansion-toggle:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pfx-expansion-detail-row{background:var(--md-sys-color-surface-container-low)}.pfx-expansion-detail-cell{padding:0!important;border-bottom:1px solid var(--p-table-border-color)}.pfx-expansion-detail-panel{padding:12px 16px;border-left:3px solid var(--md-sys-color-primary)}.pfx-expansion-detail-panel.pfx-expansion-motion-none{animation:none;transition:none}.pfx-expansion-detail-panel.pfx-expansion-motion-subtle-slide{animation:pfx-expansion-subtle-slide-in var(--pfx-expansion-motion-duration, .16s) var(--pfx-expansion-motion-easing, cubic-bezier(.2, 0, 0, 1))}.pfx-expansion-detail-panel.pfx-expansion-motion-accordion{animation:pfx-expansion-accordion-in var(--pfx-expansion-motion-duration, .18s) var(--pfx-expansion-motion-easing, cubic-bezier(.2, 0, 0, 1));transform-origin:top center}.pfx-expansion-detail-panel.pfx-expansion-motion-fade-scale{animation:pfx-expansion-fade-scale-in var(--pfx-expansion-motion-duration, .16s) var(--pfx-expansion-motion-easing, cubic-bezier(.2, 0, 0, 1));transform-origin:top center}@keyframes pfx-expansion-subtle-slide-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}@keyframes pfx-expansion-accordion-in{0%{opacity:0;transform:scaleY(.96)}to{opacity:1;transform:scaleY(1)}}@keyframes pfx-expansion-fade-scale-in{0%{opacity:0;transform:translateY(-2px) scale(.985)}to{opacity:1;transform:translateY(0) scale(1)}}.pfx-expansion-detail-schema{margin:0;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;line-height:1.4;white-space:pre-wrap;word-break:break-word;color:var(--md-sys-color-on-surface-variant)}.pfx-expansion-detail-message{font-size:13px;color:var(--md-sys-color-on-surface-variant)}.pfx-expansion-detail-message--error{color:var(--md-sys-color-error)}.pfx-expansion-detail-stack{display:grid;gap:10px}.pfx-expansion-detail-tabs{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:10px}.pfx-expansion-detail-tab-btn{border:1px solid var(--p-table-border-color);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);border-radius:999px;padding:6px 12px;font-size:12px;line-height:1.2;cursor:pointer}.pfx-expansion-detail-tab-btn.is-active{border-color:var(--md-sys-color-primary);color:var(--md-sys-color-primary);font-weight:600}.pfx-expansion-detail-tab-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pfx-expansion-detail-tab-panel{display:grid;gap:10px}.pfx-expansion-node{border:1px solid var(--p-table-border-color);border-radius:8px;padding:10px 12px;background:var(--md-sys-color-surface)}.pfx-expansion-node-card__header{margin-bottom:8px}.pfx-expansion-node-card__title{margin:0;font-size:14px;line-height:1.3}.pfx-expansion-node-card__subtitle{margin:4px 0 0;font-size:12px;color:var(--md-sys-color-on-surface-variant)}.pfx-expansion-node-value{display:flex;align-items:baseline;gap:8px}.pfx-expansion-node-value__label{color:var(--md-sys-color-on-surface-variant);font-size:12px}.pfx-expansion-node-value__content{font-size:14px}.pfx-expansion-node-list__title{margin:0 0 6px;font-size:13px}.pfx-expansion-node-list ul{margin:0;padding-left:18px}.pfx-expansion-node-richtext :where(p,ul,ol,h1,h2,h3,h4,h5,h6){margin-top:0;margin-bottom:8px}.pfx-expansion-node-placeholder{font-size:12px;color:var(--md-sys-color-on-surface-variant)}:host.density-compact{--p-header-padding: 8px 12px;--p-cell-padding: 8px 12px;--p-actions-btn-size: 32px;--p-actions-icon-size: 18px}:host.density-comfortable{--p-header-padding: 12px 16px;--p-cell-padding: 12px 16px;--p-actions-btn-size: 40px;--p-actions-icon-size: 22px}:host.density-spacious{--p-header-padding: 16px 20px;--p-cell-padding: 16px 20px;--p-actions-btn-size: 44px;--p-actions-icon-size: 24px}:host.density-compact ::ng-deep .mat-mdc-cell{padding:var(--p-cell-padding, 8px 12px)}:host.density-comfortable ::ng-deep .mat-mdc-cell{padding:var(--p-cell-padding, 12px 16px)}:host.density-spacious ::ng-deep .mat-mdc-cell{padding:var(--p-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%;transition:opacity .12s ease}.praxis-actions-cell.dense .praxis-actions-cell__content{gap:6px}.praxis-actions-cell__content--discovering{opacity:.78}.praxis-row-action-anchor{display:inline-flex;align-items:center}.praxis-row-action-anchor--workflow{position:relative}.praxis-row-action-anchor--workflow:after{content:\"\";position:absolute;right:4px;top:4px;width:6px;height:6px;border-radius:999px;background:var(--md-sys-color-secondary);opacity:.9;pointer-events:none}.praxis-row-action-anchor--blocked:after{background:var(--md-sys-color-error)}.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)}.praxis-icon-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);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-icon-btn--workflow{background:color-mix(in srgb,var(--md-sys-color-secondary-container) 36%,transparent)}.praxis-icon-btn--blocked{background:color-mix(in srgb,var(--md-sys-color-error-container) 42%,transparent)}.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-row-btn--workflow{box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--md-sys-color-secondary) 28%,transparent)}.praxis-row-btn--blocked{box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--md-sys-color-error) 28%,transparent)}.praxis-icon-btn.destructive mat-icon{color:var(--md-sys-color-error)}.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.stacked{margin:0}.praxis-table-header>praxis-table-toolbar{flex:1 0 100%}.praxis-floating-bulk-actions{position:fixed;z-index:var(--praxis-layer-floating-local, 200);display:inline-flex;align-items:center;gap:8px;padding:8px;border-radius:999px;background:var(--md-sys-color-surface-container-highest);box-shadow:0 8px 20px #00000029}.praxis-floating-bulk-actions.pos-bottom-right{right:20px;bottom:20px}.praxis-floating-bulk-actions.pos-bottom-left{left:20px;bottom:20px}.praxis-floating-bulk-actions.pos-top-right{right:20px;top:20px}.praxis-floating-bulk-actions.pos-top-left{left:20px;top:20px}@media(max-width:768px){.praxis-floating-bulk-actions{gap:6px;padding:6px}.praxis-floating-bulk-actions.pos-bottom-right{right:12px;left:auto;bottom:12px}.praxis-floating-bulk-actions.pos-bottom-left{left:12px;right:auto;bottom:12px}.praxis-floating-bulk-actions.pos-top-right{right:12px;left:auto;top:12px}.praxis-floating-bulk-actions.pos-top-left{left:12px;right:auto;top:12px}}:host{display:block;width:100%;min-width:0;max-width:100%;--pfx-toolbar-pad-y: 6px;--pfx-toolbar-pad-x: 12px;--p-table-bg: var(--md-sys-color-surface-container-highest);--p-table-text-color: var(--md-sys-color-on-surface);--p-table-header-bg: var(--md-sys-color-surface-container-highest);--p-table-header-fg: var(--md-sys-color-on-surface);--p-table-border-color: var(--md-sys-color-outline-variant);--p-table-row-even-bg: var(--md-sys-color-surface-container);--p-table-row-hover-bg: var(--md-sys-color-surface-container-high);--p-table-row-selected-bg: var(--md-sys-color-primary-container);--p-table-badge-soft-primary-bg: var(--md-sys-color-primary-container);--p-table-badge-soft-primary-fg: var(--md-sys-color-on-primary-container);--p-table-badge-soft-accent-bg: var(--md-sys-color-secondary-container);--p-table-badge-soft-accent-fg: var(--md-sys-color-on-secondary-container);--p-table-badge-soft-warn-bg: var(--md-sys-color-error-container);--p-table-badge-soft-warn-fg: var(--md-sys-color-on-error-container);--p-table-state-success-bg: var(--md-sys-color-tertiary-container);--p-table-state-success-fg: var(--md-sys-color-on-tertiary-container);--p-table-state-warning-bg: var(--md-sys-color-secondary-container);--p-table-state-warning-fg: var(--md-sys-color-on-secondary-container);--p-table-state-danger-bg: var(--md-sys-color-error-container);--p-table-state-danger-fg: var(--md-sys-color-on-error-container);--p-table-state-highlight-bg: var(--md-sys-color-primary-container);--p-table-state-highlight-fg: var(--md-sys-color-on-primary-container);--p-table-drag-handle-size: 14px;--p-table-drag-handle-color: var(--md-sys-color-on-surface-variant);--p-table-drag-handle-hover-color: var(--md-sys-color-on-surface);--p-table-drag-handle-base-opacity: 0;--p-table-drag-handle-visible-opacity: .72;--p-table-drag-handle-active-opacity: 1;--p-table-drag-handle-transition-duration: .16s;--p-table-reorder-transition-duration: .16s;--p-table-drag-preview-scale: 1.01;--p-table-drag-status-enter-duration: .18s;--p-table-drag-preview-shadow: 0 14px 32px rgba(0, 0, 0, .28), 0 0 0 1px var(--p-table-border-color)}:host ::ng-deep .mat-mdc-table{background:var(--p-table-bg);color:var(--p-table-text-color);border-radius:12px 12px 0 0;width:100%;box-shadow:var(--p-table-surface-shadow);transition:box-shadow var(--p-table-selection-transition-duration, .18s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}:host ::ng-deep .mat-mdc-table:hover{box-shadow:var(--p-table-surface-shadow-hover, var(--p-table-surface-shadow))}:host ::ng-deep .mat-mdc-table.table-stack-top{border-top-left-radius:0;border-top-right-radius:0}:host ::ng-deep .praxis-toolbar-footer{border:0 solid var(--p-table-border-color);border-top:0;border-radius:0;background:var(--p-table-bg)}:host ::ng-deep .mat-mdc-paginator.footer-stack{border-top-left-radius:0;border-top-right-radius:0;border-top:0}:host ::ng-deep .mat-mdc-paginator.footer-stack .mat-mdc-paginator-container{padding:8px 12px}:host [data-role=table-settings].mat-mdc-icon-button{--mdc-icon-button-icon-color: var(--md-sys-color-primary);color:var(--md-sys-color-primary)}.pfx-link{color:var(--md-sys-color-primary);text-decoration:underline;cursor:pointer}.pfx-chip{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:10px;font-size:12px;line-height:1;border:1px solid transparent}.pfx-chip-icon{font-size:14px;width:14px;height:14px}.pfx-chip--outlined{background:transparent;border-color:var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface)}.pfx-chip--filled-primary{background:var(--md-sys-color-primary);color:var(--md-sys-color-on-primary)}.pfx-chip--filled-accent{background:var(--md-sys-color-secondary);color:var(--md-sys-color-on-secondary)}.pfx-chip--filled-warn{background:var(--md-sys-color-error);color:var(--md-sys-color-on-error)}.pfx-chip--soft-primary{background:var(--p-table-badge-soft-primary-bg);color:var(--p-table-badge-soft-primary-fg)}.pfx-chip--soft-accent{background:var(--p-table-badge-soft-accent-bg);color:var(--p-table-badge-soft-accent-fg)}.pfx-chip--soft-warn{background:var(--p-table-badge-soft-warn-bg);color:var(--p-table-badge-soft-warn-fg)}.pfx-progress{position:relative;width:100%;max-width:140px;height:8px;background:var(--md-sys-color-surface-container-highest);border-radius:4px;overflow:hidden;display:inline-block;vertical-align:middle}.pfx-progress-bar{height:100%;background:var(--md-sys-color-primary);transition:width .2s ease}.pfx-progress-label{margin-left:8px;font-size:12px;opacity:.8;display:inline-block;vertical-align:middle}.pfx-avatar{display:inline-flex;align-items:center;justify-content:center;background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface);border-radius:4px;overflow:hidden;font-weight:600}.pfx-avatar.shape-rounded{border-radius:8px}.pfx-avatar.shape-circle{border-radius:999px}.pfx-avatar--initials{text-transform:uppercase;font-size:12px}.pfx-cell-compose{display:inline-flex;align-items:center;gap:6px}.pfx-cell-compose.dir-col{flex-direction:column;align-items:stretch}.pfx-cell-compose.align-start{justify-content:flex-start}.pfx-cell-compose.align-center{justify-content:center}.pfx-cell-compose.align-end{justify-content:flex-end}.pfx-cell-compose.wrap{flex-wrap:wrap}.pfx-cell-compose.ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.px-scroll-viewport{width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;overscroll-behavior-x:contain}.px-scroll-viewport.scroll-none{overflow-x:visible}.px-scroll-viewport.scroll-auto ::ng-deep .mat-mdc-table{width:max-content;min-width:100%}.px-scroll-viewport.scroll-wrap ::ng-deep .mat-mdc-header-cell,.px-scroll-viewport.scroll-wrap ::ng-deep .mat-mdc-cell{white-space:normal;text-overflow:initial}.px-scroll-viewport.scroll-wrap ::ng-deep .mat-mdc-table{width:100%}:host ::ng-deep .mat-mdc-header-row{position:sticky;top:0;z-index:var(--praxis-layer-sticky-local, 100);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}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable .mat-sort-header-container{display:flex;align-items:center;width:100%;gap:4px;cursor:inherit}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable{-webkit-user-select:none;user-select:none;cursor:grab;padding-left:0!important}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable.cdk-drag-dragging{cursor:grabbing}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable .mat-sort-header-content{display:inline-flex;align-items:center;gap:4px;flex:1 1 auto;min-width:0}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-header-label{display:inline-flex;align-items:center;gap:4px;flex:1 1 auto;min-width:0}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-header-label-text{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-column-drag-handle{-webkit-appearance:none;appearance:none;border:0;background:transparent;color:var(--p-table-drag-handle-color);width:var(--p-table-drag-handle-size);min-width:var(--p-table-drag-handle-size);flex:0 0 var(--p-table-drag-handle-size);height:var(--p-table-drag-handle-size);padding:0;display:inline-flex;align-items:center;justify-content:center;border-radius:0;cursor:inherit;pointer-events:none;touch-action:none;opacity:var(--p-table-drag-handle-base-opacity, .42);transform:none;order:-1;margin-inline-end:0;transition:opacity var(--p-table-drag-handle-transition-duration, .16s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1)),color var(--p-table-drag-handle-transition-duration, .16s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable:hover .praxis-column-drag-handle,:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable:focus-within .praxis-column-drag-handle{opacity:var(--p-table-drag-handle-visible-opacity, .72);color:var(--p-table-drag-handle-hover-color, var(--p-table-drag-handle-color))}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-column-drag-handle:active{opacity:var(--p-table-drag-handle-active-opacity, 1);cursor:grabbing}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-column-drag-handle .mat-icon{font-size:14px;width:14px;height:14px;line-height:14px;transition:transform .18s var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable:hover .praxis-column-drag-handle .mat-icon{transform:none}:host ::ng-deep .pfx-column-drag-indicator .cdk-drop-list-dragging .mat-mdc-header-cell:not(.cdk-drag-placeholder){transition:transform var(--p-table-reorder-transition-duration, .18s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}:host ::ng-deep .pfx-column-drag-indicator .mat-mdc-header-cell.cdk-drag-animating{transition:transform var(--p-table-reorder-transition-duration, .16s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}.pfx-column-drag-preview{box-sizing:border-box;display:flex;align-items:center;border-radius:10px;border:1px solid var(--p-table-border-color);background:linear-gradient(135deg,var(--p-table-header-bg) 0%,var(--p-table-row-hover-bg) 100%);color:var(--p-table-header-fg);box-shadow:var(--p-table-drag-preview-shadow);transform:scale(var(--p-table-drag-preview-scale, 1.01));pointer-events:none;z-index:var(--praxis-layer-authoring-hover, 300)}.pfx-column-drag-preview .praxis-column-drag-handle,.pfx-column-drag-preview .mat-sort-header-arrow,.pfx-column-drag-preview .mat-sort-header-indicator,.pfx-column-drag-preview .mat-sort-header-stem,.pfx-column-drag-preview .mat-sort-header-pointer,.pfx-column-drag-preview .mat-sort-header-pointer-left,.pfx-column-drag-preview .mat-sort-header-pointer-right{display:none!important}.pfx-column-drag-preview .mat-sort-header-container{display:flex;align-items:center;width:100%;min-height:100%;padding-right:0!important}.pfx-column-drag-preview .mat-sort-header-content,.pfx-column-drag-preview .praxis-header-label{display:inline-flex;align-items:center;min-width:0;width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host ::ng-deep .pfx-column-drag-indicator .cdk-drag-placeholder{opacity:1;border:1px dashed var(--p-table-border-color);background:var(--p-table-row-hover-bg)}:host ::ng-deep .pfx-column-drag-indicator .cdk-drag-placeholder *{opacity:0}:host ::ng-deep .pfx-column-drag-indicator .mat-mdc-header-cell.cdk-drag-dragging{opacity:.58}@media(prefers-reduced-motion:reduce){:host ::ng-deep .pfx-column-drag-indicator .cdk-drop-list-dragging .mat-mdc-header-cell:not(.cdk-drag-placeholder){transition:none}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-column-drag-handle,:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-column-drag-handle .mat-icon,.pfx-column-drag-preview{transition:none;transform:none}.praxis-column-reorder-status{animation:none}.pfx-expansion-detail-panel{animation:none!important;transition:none!important;transform:none!important}:host ::ng-deep .mat-mdc-row{transition:none}:host ::ng-deep .mat-mdc-table{transition:none}}.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 .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable .mat-sort-header-container{padding-right:12px}@media(pointer:coarse){:host{--p-table-drag-handle-size: 18px;--p-table-drag-handle-base-opacity: .56;--p-table-drag-handle-visible-opacity: .92}}:host ::ng-deep .mat-mdc-cell{color:var(--p-table-text-color);font-size:var(--p-cell-font-size, inherit);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}:host ::ng-deep .mat-mdc-cell .pfx-cell-content,:host ::ng-deep .mat-cell .pfx-cell-content{display:inline-flex;align-items:center;gap:6px;width:100%;min-width:0;overflow:hidden}:host ::ng-deep .mat-mdc-row:hover{background:var(--p-table-row-hover-bg)}:host ::ng-deep .mat-mdc-row{transition:background-color var(--p-table-hover-transition-duration, .18s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1)),box-shadow var(--p-table-selection-transition-duration, .18s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}:host ::ng-deep .mat-mdc-row:nth-child(2n){background:var(--p-table-row-even-bg)}:host ::ng-deep .mat-mdc-row:nth-child(2n):hover{background:var(--p-table-row-hover-bg)}:host ::ng-deep .mat-mdc-row.pfx-row-selected,:host ::ng-deep .mat-mdc-row.pfx-row-selected:hover{background:var(--p-table-row-selected-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-error{display:flex;align-items:center;gap:12px;padding:12px;margin:8px 0;border:1px solid var(--md-sys-color-error);border-radius:8px}.ptable-error__content{flex:1}.ptable-error__title{font-weight:600}.ptable-info-banner{display:flex;gap:12px;align-items:center;padding:8px 12px;margin:8px 0;border-radius:8px;border:1px solid var(--md-sys-color-primary);background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.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);border:1px solid var(--md-sys-color-outline-variant)}.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(--md-sys-color-primary);color:var(--md-sys-color-on-primary)}.pfx-badge--filled-accent{background:var(--md-sys-color-secondary);color:var(--md-sys-color-on-secondary)}.pfx-badge--filled-warn{background:var(--md-sys-color-error);color:var(--md-sys-color-on-error)}.pfx-badge--outlined{background:transparent;border-color:var(--md-sys-color-outline-variant);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"] }]
47115
+ ], template: "@if (shouldShowEmptyState()) {\n <praxis-empty-state-card\n icon=\"link\"\n [title]=\"'Conecte a tabela \u00C3\u00A0 fonte de dados'\"\n [description]=\"'Informe a rota do recurso da API para carregar colunas e dados automaticamente.'\"\n [primaryAction]=\"{ label: 'Conectar \u00C3\u00A0 fonte de dados', icon: 'bolt', action: openQuickConnect.bind(this) }\"\n ></praxis-empty-state-card>\n}\n\n<!-- Error State with Quick Connect CTA -->\n@if (isRemoteMode() && (schemaError || dataError)) {\n<div class=\"ptable-error\" role=\"alert\" aria-live=\"assertive\">\n <mat-icon color=\"warn\" aria-hidden=\"true\">error</mat-icon>\n <div class=\"ptable-error__content\">\n <praxis-rich-content\n rootClassName=\"ptable-error__title\"\n [nodes]=\"getTableChromeTextRichContentNodes('Erro', 'ptable-error__title-text')\"\n ></praxis-rich-content>\n <praxis-rich-content\n rootClassName=\"ptable-error__message\"\n [nodes]=\"getTableChromeTextRichContentNodes(errorMessage || 'Ocorreu um erro ao carregar a tabela.', 'ptable-error__message-text')\"\n ></praxis-rich-content>\n </div>\n <button mat-flat-button color=\"primary\" (click)=\"openQuickConnect()\">\n <mat-icon>bolt</mat-icon>\n <praxis-rich-content\n rootClassName=\"ptable-error__action-label\"\n [nodes]=\"getTableChromeTextRichContentNodes('Conectar a recurso', 'ptable-error__action-label-text')\"\n ></praxis-rich-content>\n </button>\n @if (enableCustomization) {\n <button\n mat-icon-button\n color=\"primary\"\n data-role=\"table-settings\"\n data-testid=\"table-settings-trigger\"\n (click)=\"openTableSettings()\"\n [attr.aria-label]=\"getTableSettingsLabel()\"\n [matTooltip]=\"getTableSettingsLabel()\"\n matTooltipPosition=\"below\"\n >\n <mat-icon>settings</mat-icon>\n </button>\n }\n @if (!schemaError) {\n <button mat-stroked-button (click)=\"retryData()\">\n <praxis-rich-content\n rootClassName=\"ptable-error__action-label\"\n [nodes]=\"getTableChromeTextRichContentNodes('Tentar novamente', 'ptable-error__action-label-text')\"\n ></praxis-rich-content>\n </button>\n }\n @if (schemaError) {\n <button mat-stroked-button (click)=\"reloadSchema()\">\n <praxis-rich-content\n rootClassName=\"ptable-error__action-label\"\n [nodes]=\"getTableChromeTextRichContentNodes('Recarregar colunas', 'ptable-error__action-label-text')\"\n ></praxis-rich-content>\n </button>\n }\n </div>\n}\n\n<!-- Inline banner for schema change (only in edit mode) -->\n@if (shouldShowOutdatedInline()) {\n<div class=\"ptable-info-banner\" role=\"status\" aria-live=\"polite\">\n <praxis-rich-content\n rootClassName=\"text\"\n [nodes]=\"getTableChromeTextRichContentNodes('O schema do servidor mudou. Reconciliar agora?', 'ptable-info-banner__text')\"\n ></praxis-rich-content>\n <div class=\"actions\">\n <button mat-stroked-button color=\"primary\" (click)=\"onReconcileRequested()\">\n <mat-icon>sync</mat-icon>\n <praxis-rich-content\n rootClassName=\"ptable-info-banner__action-label\"\n [nodes]=\"getTableChromeTextRichContentNodes('Reconciliar', 'ptable-info-banner__action-label-text')\"\n ></praxis-rich-content>\n </button>\n <button mat-button (click)=\"onSnoozeOutdated()\">\n <praxis-rich-content\n rootClassName=\"ptable-info-banner__action-label\"\n [nodes]=\"getTableChromeTextRichContentNodes('Lembrar depois', 'ptable-info-banner__action-label-text')\"\n ></praxis-rich-content>\n </button>\n <button mat-button (click)=\"onIgnoreOutdated()\">\n <praxis-rich-content\n rootClassName=\"ptable-info-banner__action-label\"\n [nodes]=\"getTableChromeTextRichContentNodes('Ignorar', 'ptable-info-banner__action-label-text')\"\n ></praxis-rich-content>\n </button>\n </div>\n </div>\n}\n\n@if (shouldRenderDataSurface() && !schemaError && !dataError && toolbarV2) {\n <div class=\"praxis-table-header\" [class.edit-mode]=\"enableCustomization\" [class.stacked]=\"showToolbar\">\n @if (showToolbar && shouldShowToolbarTopPlacement()) {\n <praxis-table-toolbar\n [config]=\"config\"\n [backgroundColor]=\"getToolbarActionsBackgroundColor()\"\n [style.--pfx-filter-h]=\"getToolbarLayoutHeightHostStyle()\"\n [evaluationContext]=\"getToolbarEvaluationContext()\"\n [showActionsGroup]=\"shouldShowToolbarActionsTop()\"\n [showMobileActions]=\"shouldShowToolbarActionsTop()\"\n (toolbarAction)=\"onToolbarAction($event)\"\n (reset)=\"onResetPreferences()\"\n >\n @if (shouldRenderAdvancedFilter()) {\n <praxis-filter\n advancedFilter\n [resourcePath]=\"getAdvancedFilterResourcePath()\"\n [filterId]=\"tableId\"\n [formId]=\"tableId\"\n [persistenceKey]=\"getAdvancedFilterPersistenceKey()\"\n [fieldMetadata]=\"getAdvancedFilterFieldMetadata()\"\n [enableCustomization]=\"enableCustomization\"\n \n [alwaysVisibleFields]=\"config.behavior?.filtering?.advancedFilters?.settings?.alwaysVisibleFields\"\n [alwaysVisibleFieldMetadataOverrides]=\"config.behavior?.filtering?.advancedFilters?.settings?.alwaysVisibleFieldMetadataOverrides ?? {}\"\n [selectedFieldIds]=\"config.behavior?.filtering?.advancedFilters?.settings?.selectedFieldIds ?? []\"\n [allowSaveTags]=\"config.behavior?.filtering?.advancedFilters?.settings?.allowSaveTags\"\n [changeDebounceMs]=\"config.behavior?.filtering?.advancedFilters?.settings?.changeDebounceMs ?? 300\"\n [i18n]=\"getFilterI18n()\"\n [mode]=\"'filter'\"\n [showFilterSettings]=\"enableCustomization\"\n (change)=\"onAdvancedFilterChange($event)\"\n (requestSearch)=\"onAdvancedFilterSubmit($event)\"\n (clear)=\"onAdvancedFilterClear()\"\n ></praxis-filter>\n }\n <ng-content select=\"[advancedFilter]\" />\n <ng-content select=\"[toolbar]\" />\n \n <!-- AI Assistant in Toolbar -->\n @defer (on idle) {\n @if (aiAdapter) {\n <praxis-ai-assistant [adapter]=\"aiAdapter\" end-actions></praxis-ai-assistant>\n }\n }\n\n @if (enableCustomization) {\n <button end-actions mat-icon-button color=\"primary\" data-role=\"table-settings\" data-testid=\"table-settings-trigger\"\n (click)=\"openTableSettings()\" [attr.aria-label]=\"getTableSettingsLabel()\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"getTableSettingsTooltip()\"\n matTooltipPosition=\"below\">\n <mat-icon>settings</mat-icon>\n </button>\n }\n </praxis-table-toolbar>\n }\n @if (!showToolbar && enableCustomization) {\n <div class=\"ptable-header-actions\">\n @defer (on idle) {\n @if (aiAdapter) {\n <praxis-ai-assistant [adapter]=\"aiAdapter\"></praxis-ai-assistant>\n }\n }\n <button mat-icon-button color=\"primary\" data-role=\"table-settings\" data-testid=\"table-settings-trigger\" (click)=\"openTableSettings()\" [attr.aria-label]=\"getTableSettingsLabel()\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"getTableSettingsTooltip()\"\n matTooltipPosition=\"below\">\n <mat-icon>settings</mat-icon>\n </button>\n @if (isRemoteMode()) {\n <button mat-icon-button (click)=\"disconnect()\" [attr.aria-label]=\"getDisconnectLabel()\" [matTooltip]=\"getDisconnectTooltip()\">\n <mat-icon>link_off</mat-icon>\n </button>\n }\n </div>\n }\n </div>\n} @else {\n @if (shouldRenderDataSurface() && !schemaError && !dataError) {\n @if (showToolbar && shouldShowToolbarTopPlacement()) {\n <praxis-table-toolbar\n [config]=\"config\"\n [backgroundColor]=\"getToolbarActionsBackgroundColor()\"\n [style.--pfx-filter-h]=\"getToolbarLayoutHeightHostStyle()\"\n [evaluationContext]=\"getToolbarEvaluationContext()\"\n [showActionsGroup]=\"shouldShowToolbarActionsTop()\"\n [showMobileActions]=\"shouldShowToolbarActionsTop()\"\n (toolbarAction)=\"onToolbarAction($event)\"\n (reset)=\"onResetPreferences()\"\n >\n @if (shouldRenderAdvancedFilter()) {\n <praxis-filter\n advancedFilter\n [resourcePath]=\"getAdvancedFilterResourcePath()\"\n [filterId]=\"tableId\"\n [formId]=\"tableId\"\n [persistenceKey]=\"getAdvancedFilterPersistenceKey()\"\n [fieldMetadata]=\"getAdvancedFilterFieldMetadata()\"\n [enableCustomization]=\"enableCustomization\"\n \n [alwaysVisibleFields]=\"config.behavior?.filtering?.advancedFilters?.settings?.alwaysVisibleFields\"\n [alwaysVisibleFieldMetadataOverrides]=\"config.behavior?.filtering?.advancedFilters?.settings?.alwaysVisibleFieldMetadataOverrides ?? {}\"\n [selectedFieldIds]=\"config.behavior?.filtering?.advancedFilters?.settings?.selectedFieldIds ?? []\"\n [allowSaveTags]=\"config.behavior?.filtering?.advancedFilters?.settings?.allowSaveTags\"\n [changeDebounceMs]=\"config.behavior?.filtering?.advancedFilters?.settings?.changeDebounceMs ?? 300\"\n [i18n]=\"getFilterI18n()\"\n [mode]=\"'filter'\"\n [showFilterSettings]=\"enableCustomization\"\n (change)=\"onAdvancedFilterChange($event)\"\n (requestSearch)=\"onAdvancedFilterSubmit($event)\"\n (clear)=\"onAdvancedFilterClear()\"\n ></praxis-filter>\n }\n <ng-content select=\"[advancedFilter]\" />\n <ng-content select=\"[toolbar]\" />\n @defer (on idle) {\n @if (aiAdapter) {\n <praxis-ai-assistant [adapter]=\"aiAdapter\" end-actions></praxis-ai-assistant>\n }\n }\n @if (enableCustomization) {\n <button end-actions mat-icon-button color=\"primary\" data-role=\"table-settings\" data-testid=\"table-settings-trigger\"\n (click)=\"openTableSettings()\" [attr.aria-label]=\"getTableSettingsLabel()\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"getTableSettingsTooltip()\"\n matTooltipPosition=\"below\">\n <mat-icon>settings</mat-icon>\n </button>\n }\n </praxis-table-toolbar>\n }\n @if (!showToolbar && enableCustomization) {\n <div class=\"ptable-header-actions\">\n <button mat-icon-button color=\"primary\" data-role=\"table-settings\" data-testid=\"table-settings-trigger\" (click)=\"openTableSettings()\" [attr.aria-label]=\"getTableSettingsLabel()\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"getTableSettingsTooltip()\"\n matTooltipPosition=\"below\">\n <mat-icon>settings</mat-icon>\n </button>\n </div>\n }\n }\n}\n<div class=\"px-scroll-viewport\"\n cdkScrollable\n [class.scroll-auto]=\"horizontalScroll === 'auto'\"\n [class.scroll-wrap]=\"horizontalScroll === 'wrap'\"\n [class.scroll-none]=\"horizontalScroll === 'none'\">\n\n@if (shouldRenderDataSurface() && !schemaError && !dataError) {\n<div class=\"praxis-visually-hidden-status\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n {{ columnReorderStatusMessage }}\n</div>\n@if (columnReorderVisualStatusMessage) {\n <div class=\"praxis-column-reorder-status\" role=\"note\">\n {{ columnReorderVisualStatusMessage }}\n </div>\n}\n<table\n mat-table\n data-testid=\"table-column-drag-drop-list\"\n [dataSource]=\"dataSource\"\n [multiTemplateDataRows]=\"isRowExpansionRuntimeEnabled()\"\n cdkDropList\n [cdkDropListDisabled]=\"!isColumnDraggingEnabled()\"\n [cdkDropListData]=\"visibleDataColumnsForDrag\"\n cdkDropListOrientation=\"horizontal\"\n (cdkDropListDropped)=\"onColumnDrop($event)\"\n matSort\n (matSortChange)=\"onSortChange($event)\"\n [matSortDisabled]=\"!getSortingEnabled()\"\n [ngClass]=\"getTableElevationClassName()\"\n [class.table-stack-top]=\"showToolbar\"\n [class.pfx-column-drag-enabled]=\"isColumnDraggingEnabled()\"\n [class.pfx-column-drag-indicator]=\"isColumnDragIndicatorEnabled()\"\n>\n @if (config.behavior?.selection?.enabled) {\n <ng-container\n matColumnDef=\"_select\"\n >\n <th mat-header-cell *matHeaderCellDef>\n @if (canSelectAll()) {\n <mat-checkbox\n (change)=\"masterToggle()\"\n [checked]=\"isAllSelected()\"\n [indeterminate]=\"selection.hasValue() && !isAllSelected()\"\n ></mat-checkbox>\n }\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 }\n @if (isRowExpansionRuntimeEnabled()) {\n <ng-container matColumnDef=\"_expander\">\n <th mat-header-cell *matHeaderCellDef class=\"pfx-expansion-col-header\">\n <span class=\"praxis-visually-hidden-status\">Expandir detalhes da linha</span>\n </th>\n <td mat-cell *matCellDef=\"let row; let i = index\" class=\"pfx-expansion-col-cell\">\n <button\n mat-icon-button\n class=\"pfx-expansion-toggle\"\n [disabled]=\"!isRowExpandable(row, i) || !isExpansionIconTriggerEnabled()\"\n [attr.aria-expanded]=\"isRowExpanded(row, i) ? 'true' : 'false'\"\n [attr.aria-controls]=\"getRowExpansionDetailId(row, i)\"\n [attr.aria-label]=\"getRowExpansionToggleAriaLabel(row, i)\"\n (click)=\"onExpansionToggleFromIcon(row, i, $event)\"\n (keydown)=\"onExpansionToggleKeydown($event, row, i)\"\n >\n <mat-icon [praxisIcon]=\"isRowExpanded(row, i)\n ? getExpansionExpandedIcon()\n : getExpansionCollapsedIcon()\"></mat-icon>\n </button>\n </td>\n </ng-container>\n }\n @for (column of visibleColumns; track column.field) {\n <ng-container\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 cdkDrag\n [cdkDragData]=\"column\"\n cdkDragLockAxis=\"x\"\n cdkDragPreviewClass=\"pfx-column-drag-preview\"\n (cdkDragStarted)=\"onColumnDragStarted(column)\"\n (cdkDragEnded)=\"onColumnDragEnded($event, column)\"\n (keydown)=\"onColumnDragHandleKeydown($event, column)\"\n [cdkDragDisabled]=\"!isColumnDraggingEnabled() || !isColumnDraggable(column)\"\n [class.praxis-header-draggable]=\"isColumnDraggingEnabled() && isColumnDraggable(column)\"\n [disabled]=\"!getSortingEnabled() || column.sortable === false\"\n [style.text-align]=\"getColumnTextAlignStyle(column)\"\n [style.width]=\"getColumnWidthStyle(column)\"\n [attr.style]=\"getColumnHeaderAttrStyle(column)\"\n [attr.aria-label]=\"isColumnDraggingEnabled() && isColumnDraggable(column) ? getColumnDragHandleAriaLabel(column) : null\"\n >\n <span class=\"praxis-header-label\" data-testid=\"column-header-label\">\n @if (isColumnDraggingEnabled() && isColumnDraggable(column)) {\n <span\n class=\"praxis-column-drag-handle\"\n data-testid=\"column-drag-handle\"\n [attr.data-column-field]=\"column.field\"\n aria-hidden=\"true\"\n >\n <mat-icon [praxisIcon]=\"'drag_indicator'\"></mat-icon>\n </span>\n }\n <span class=\"praxis-header-label-text\">{{ column.header }}</span>\n </span>\n </th>\n <td\n mat-cell\n *matCellDef=\"let element\"\n [style.text-align]=\"getColumnTextAlignStyle(column)\"\n [style.width]=\"getColumnWidthStyle(column)\"\n [attr.style]=\"getColumnCellAttrStyle(column)\"\n [ngClass]=\"getCellClasses(element, column)\"\n [ngStyle]=\"getCellNgStyle(element, column)\"\n >\n <div\n class=\"pfx-cell-content\"\n [ngClass]=\"getCellContentClasses(element, column)\"\n [ngStyle]=\"getCellContentNgStyle(element, column)\"\n >\n <ng-container [ngSwitch]=\"getEffectiveRendererType(element, column)\">\n <!-- Icon renderer -->\n <ng-container *ngSwitchCase=\"'icon'\">\n <span\n class=\"pfx-icon-renderer\"\n [style.color]=\"getIconColor(element, column) || null\"\n [ngStyle]=\"getIconStyle(element, column)\"\n [attr.aria-label]=\"getIconAriaLabel(element, column) || null\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-icon-renderer__content\"\n [nodes]=\"getIconRichContentNodes(element, column)\"\n ></praxis-rich-content>\n </span>\n </ng-container>\n\n <!-- Image renderer -->\n <ng-container *ngSwitchCase=\"'image'\">\n <span class=\"pfx-cell-image\">\n <praxis-rich-content\n rootClassName=\"pfx-cell-image__content\"\n [nodes]=\"getImageRichContentNodes(element, column)\"\n ></praxis-rich-content>\n </span>\n </ng-container>\n\n <!-- Badge renderer -->\n <ng-container *ngSwitchCase=\"'badge'\">\n <span class=\"pfx-badge\" [ngClass]=\"getBadgeClasses(element, column)\">\n <praxis-rich-content\n rootClassName=\"pfx-badge__content\"\n [nodes]=\"getBadgeRichContentNodes(element, column)\"\n ></praxis-rich-content>\n </span>\n </ng-container>\n\n <!-- Link renderer -->\n <ng-container *ngSwitchCase=\"'link'\">\n <a\n class=\"pfx-link\"\n [attr.href]=\"getLinkHref(element, column) || null\"\n [attr.target]=\"getLinkTarget(element, column) || null\"\n [attr.rel]=\"getLinkRel(element, column) || null\"\n (click)=\"$event.stopPropagation()\"\n ><praxis-rich-content\n rootClassName=\"pfx-link__content\"\n [nodes]=\"getLinkRichContentNodes(element, column)\"\n ></praxis-rich-content></a\n >\n </ng-container>\n\n <!-- Button renderer -->\n <ng-container *ngSwitchCase=\"'button'\">\n <ng-container [ngSwitch]=\"getButtonVariant(element, column)\">\n <button\n *ngSwitchCase=\"'outlined'\"\n mat-stroked-button\n [color]=\"getButtonColor(element, column) || null\"\n [disabled]=\"isButtonDisabled(element, column)\"\n [attr.aria-label]=\"getButtonAriaLabel(element, column) || getButtonLabel(element, column)\"\n (click)=\"onButtonClick(element, column, $event)\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-button-renderer__content\"\n [nodes]=\"getButtonRichContentNodes(element, column)\"\n ></praxis-rich-content>\n </button>\n <button\n *ngSwitchCase=\"'text'\"\n mat-button\n [color]=\"getButtonColor(element, column) || null\"\n [disabled]=\"isButtonDisabled(element, column)\"\n [attr.aria-label]=\"getButtonAriaLabel(element, column) || getButtonLabel(element, column)\"\n (click)=\"onButtonClick(element, column, $event)\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-button-renderer__content\"\n [nodes]=\"getButtonRichContentNodes(element, column)\"\n ></praxis-rich-content>\n </button>\n <button\n *ngSwitchDefault\n mat-flat-button\n [color]=\"getButtonColor(element, column) || null\"\n [disabled]=\"isButtonDisabled(element, column)\"\n [attr.aria-label]=\"getButtonAriaLabel(element, column) || getButtonLabel(element, column)\"\n (click)=\"onButtonClick(element, column, $event)\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-button-renderer__content\"\n [nodes]=\"getButtonRichContentNodes(element, column)\"\n ></praxis-rich-content>\n </button>\n </ng-container>\n </ng-container>\n\n <!-- Chip renderer -->\n <ng-container *ngSwitchCase=\"'chip'\">\n <span class=\"pfx-chip\" [ngClass]=\"getChipClasses(element, column)\">\n <praxis-rich-content\n rootClassName=\"pfx-chip__content\"\n [nodes]=\"getChipRichContentNodes(element, column)\"\n ></praxis-rich-content>\n </span>\n </ng-container>\n\n <!-- Progress renderer -->\n <ng-container *ngSwitchCase=\"'progress'\">\n <div class=\"pfx-progress\">\n <praxis-rich-content\n rootClassName=\"pfx-progress__content\"\n [context]=\"getProgressRichContentContext(element, column)\"\n [nodes]=\"getProgressRichContentNodes(element, column)\"\n ></praxis-rich-content>\n </div>\n </ng-container>\n\n <!-- Avatar renderer -->\n <ng-container *ngSwitchCase=\"'avatar'\">\n <span class=\"pfx-avatar-renderer\"\n [ngStyle]=\"getAvatarStyle(element, column)\"\n [class.shape-rounded]=\"getAvatarShape(element, column) === 'rounded'\"\n [class.shape-circle]=\"getAvatarShape(element, column) === 'circle'\">\n <praxis-rich-content\n rootClassName=\"pfx-avatar-renderer__content\"\n [nodes]=\"getAvatarRichContentNodes(element, column)\"\n ></praxis-rich-content>\n </span>\n </ng-container>\n\n <!-- Toggle renderer -->\n <ng-container *ngSwitchCase=\"'toggle'\">\n <mat-slide-toggle\n [checked]=\"getToggleState(element, column)\"\n [disabled]=\"isToggleDisabled(element, column)\"\n [attr.aria-label]=\"getToggleAriaLabel(element, column) || 'Alternar'\"\n (change)=\"onToggleChange(element, column, $event)\"\n (click)=\"$event.stopPropagation()\"\n ></mat-slide-toggle>\n </ng-container>\n\n <!-- Menu renderer -->\n <ng-container *ngSwitchCase=\"'menu'\">\n <button mat-icon-button [matMenuTriggerFor]=\"menuRef\" (click)=\"$event.stopPropagation()\" [attr.aria-label]=\"getMenuAriaLabel(element, column) || 'Menu'\">\n <praxis-rich-content rootClassName=\"pfx-menu-trigger__content\" [nodes]=\"getMenuTriggerRichContentNodes()\"></praxis-rich-content>\n </button>\n <mat-menu #menuRef=\"matMenu\">\n <button mat-menu-item *ngFor=\"let it of getMenuItems(element, column)\" (click)=\"onMenuItemClick(it, element, $event)\" [disabled]=\"!it.__visible\" >\n <praxis-rich-content\n rootClassName=\"pfx-menu-item__content\"\n [nodes]=\"getMenuItemRichContentNodes(it)\"\n ></praxis-rich-content>\n </button>\n </mat-menu>\n </ng-container>\n\n <!-- Rating renderer -->\n <ng-container *ngSwitchCase=\"'rating'\">\n <praxis-table-rating\n class=\"pfx-rating-cell\"\n [itemsCount]=\"getRatingMax(element, column)\"\n [value]=\"getRatingValue(element, column)\"\n [size]=\"getRatingSize(element, column)\"\n [ratingColor]=\"getRatingColor(element, column)\"\n [outlineColor]=\"getRatingOutlineColor(element, column)\"\n [ariaLabel]=\"getRatingAriaLabel(element, column) || column.header\">\n </praxis-table-rating>\n </ng-container>\n\n <!-- HTML renderer (sanitizado) -->\n <ng-container *ngSwitchCase=\"'html'\">\n <span [innerHTML]=\"getSafeHtml(element, column)\"></span>\n </ng-container>\n\n <!-- Compose renderer -->\n <ng-container *ngSwitchCase=\"'compose'\">\n <span class=\"pfx-cell-compose\" [ngClass]=\"getComposeClasses(element, column)\" [ngStyle]=\"getComposeGapStyle(element, column)\">\n <ng-container *ngFor=\"let it of getComposeItems(element, column)\">\n <ng-container [ngSwitch]=\"getItemEffectiveType(element, column, it)\">\n <!-- Reuse helpers by projecting item as faux column -->\n <ng-container *ngSwitchCase=\"'icon'\">\n <span class=\"pfx-icon-renderer\" [style.color]=\"getIconColor(element, asItemColumn(column, it)) || null\" [ngStyle]=\"getIconStyle(element, asItemColumn(column, it))\" [attr.aria-label]=\"getIconAriaLabel(element, asItemColumn(column, it)) || null\"><praxis-rich-content rootClassName=\"pfx-icon-renderer__content\" [nodes]=\"getIconRichContentNodes(element, asItemColumn(column, it))\"></praxis-rich-content></span>\n </ng-container>\n <ng-container *ngSwitchCase=\"'image'\">\n <span class=\"pfx-cell-image\"><praxis-rich-content rootClassName=\"pfx-cell-image__content\" [nodes]=\"getImageRichContentNodes(element, asItemColumn(column, it))\"></praxis-rich-content></span>\n </ng-container>\n <ng-container *ngSwitchCase=\"'badge'\">\n <span class=\"pfx-badge\" [ngClass]=\"getBadgeClasses(element, asItemColumn(column, it))\"><praxis-rich-content rootClassName=\"pfx-badge__content\" [nodes]=\"getBadgeRichContentNodes(element, asItemColumn(column, it))\"></praxis-rich-content></span>\n </ng-container>\n <ng-container *ngSwitchCase=\"'link'\">\n <a class=\"pfx-link\" [attr.href]=\"getLinkHref(element, asItemColumn(column, it)) || null\" [attr.target]=\"getLinkTarget(element, asItemColumn(column, it)) || null\" [attr.rel]=\"getLinkRel(element, asItemColumn(column, it)) || null\" (click)=\"$event.stopPropagation()\"><praxis-rich-content rootClassName=\"pfx-link__content\" [nodes]=\"getLinkRichContentNodes(element, asItemColumn(column, it))\"></praxis-rich-content></a>\n </ng-container>\n <ng-container *ngSwitchCase=\"'button'\">\n <ng-container [ngSwitch]=\"getButtonVariant(element, asItemColumn(column, it))\">\n <button *ngSwitchCase=\"'outlined'\" mat-stroked-button [color]=\"getButtonColor(element, asItemColumn(column, it)) || null\" [disabled]=\"isButtonDisabled(element, asItemColumn(column, it))\" [attr.aria-label]=\"getButtonAriaLabel(element, asItemColumn(column, it)) || getButtonLabel(element, asItemColumn(column, it))\" (click)=\"onButtonClick(element, asItemColumn(column, it), $event)\"><praxis-rich-content rootClassName=\"pfx-button-renderer__content\" [nodes]=\"getButtonRichContentNodes(element, asItemColumn(column, it))\"></praxis-rich-content></button>\n <button *ngSwitchCase=\"'text'\" mat-button [color]=\"getButtonColor(element, asItemColumn(column, it)) || null\" [disabled]=\"isButtonDisabled(element, asItemColumn(column, it))\" [attr.aria-label]=\"getButtonAriaLabel(element, asItemColumn(column, it)) || getButtonLabel(element, asItemColumn(column, it))\" (click)=\"onButtonClick(element, asItemColumn(column, it), $event)\"><praxis-rich-content rootClassName=\"pfx-button-renderer__content\" [nodes]=\"getButtonRichContentNodes(element, asItemColumn(column, it))\"></praxis-rich-content></button>\n <button *ngSwitchDefault mat-flat-button [color]=\"getButtonColor(element, asItemColumn(column, it)) || null\" [disabled]=\"isButtonDisabled(element, asItemColumn(column, it))\" [attr.aria-label]=\"getButtonAriaLabel(element, asItemColumn(column, it)) || getButtonLabel(element, asItemColumn(column, it))\" (click)=\"onButtonClick(element, asItemColumn(column, it), $event)\"><praxis-rich-content rootClassName=\"pfx-button-renderer__content\" [nodes]=\"getButtonRichContentNodes(element, asItemColumn(column, it))\"></praxis-rich-content></button>\n </ng-container>\n </ng-container>\n <ng-container *ngSwitchCase=\"'chip'\">\n <span class=\"pfx-chip\" [ngClass]=\"getChipClasses(element, asItemColumn(column, it))\"><praxis-rich-content rootClassName=\"pfx-chip__content\" [nodes]=\"getChipRichContentNodes(element, asItemColumn(column, it))\"></praxis-rich-content></span>\n </ng-container>\n <ng-container *ngSwitchCase=\"'progress'\">\n <div class=\"pfx-progress\">\n <praxis-rich-content\n rootClassName=\"pfx-progress__content\"\n [context]=\"getProgressRichContentContext(element, asItemColumn(column, it))\"\n [nodes]=\"getProgressRichContentNodes(element, asItemColumn(column, it))\"\n ></praxis-rich-content>\n </div>\n </ng-container>\n <ng-container *ngSwitchCase=\"'avatar'\">\n <span class=\"pfx-avatar-renderer\" [ngStyle]=\"getAvatarStyle(element, asItemColumn(column, it))\" [class.shape-rounded]=\"getAvatarShape(element, asItemColumn(column, it)) === 'rounded'\" [class.shape-circle]=\"getAvatarShape(element, asItemColumn(column, it)) === 'circle'\"><praxis-rich-content rootClassName=\"pfx-avatar-renderer__content\" [nodes]=\"getAvatarRichContentNodes(element, asItemColumn(column, it))\"></praxis-rich-content></span>\n </ng-container>\n <ng-container *ngSwitchCase=\"'toggle'\">\n <mat-slide-toggle [checked]=\"getToggleState(element, asItemColumn(column, it))\" [disabled]=\"isToggleDisabled(element, asItemColumn(column, it))\" [attr.aria-label]=\"getToggleAriaLabel(element, asItemColumn(column, it)) || 'Alternar'\" (change)=\"onToggleChange(element, asItemColumn(column, it), $event)\" (click)=\"$event.stopPropagation()\"></mat-slide-toggle>\n </ng-container>\n <ng-container *ngSwitchCase=\"'menu'\">\n <button mat-icon-button [matMenuTriggerFor]=\"menuRef\" (click)=\"$event.stopPropagation()\" [attr.aria-label]=\"getMenuAriaLabel(element, asItemColumn(column, it)) || 'Menu'\"><praxis-rich-content rootClassName=\"pfx-menu-trigger__content\" [nodes]=\"getMenuTriggerRichContentNodes()\"></praxis-rich-content></button>\n <mat-menu #menuRef=\"matMenu\">\n <button mat-menu-item *ngFor=\"let mi of getMenuItems(element, asItemColumn(column, it))\" (click)=\"onMenuItemClick(mi, element, $event)\" [disabled]=\"!mi.__visible\">\n <praxis-rich-content\n rootClassName=\"pfx-menu-item__content\"\n [nodes]=\"getMenuItemRichContentNodes(mi)\"\n ></praxis-rich-content>\n </button>\n </mat-menu>\n </ng-container>\n <ng-container *ngSwitchCase=\"'rating'\">\n <praxis-table-rating\n class=\"pfx-rating-cell\"\n [itemsCount]=\"getRatingMax(element, asItemColumn(column, it))\"\n [value]=\"getRatingValue(element, asItemColumn(column, it))\"\n [size]=\"getRatingSize(element, asItemColumn(column, it))\"\n [ratingColor]=\"getRatingColor(element, asItemColumn(column, it))\"\n [outlineColor]=\"getRatingOutlineColor(element, asItemColumn(column, it))\"\n [ariaLabel]=\"getRatingAriaLabel(element, asItemColumn(column, it)) || column.header\">\n </praxis-table-rating>\n </ng-container>\n <ng-container *ngSwitchCase=\"'html'\">\n <span [innerHTML]=\"getSafeHtml(element, asItemColumn(column, it))\"></span>\n </ng-container>\n <!-- Value item: render base cell text alongside visuals -->\n <ng-container *ngSwitchCase=\"'value'\">\n <span class=\"pfx-cell-value\">{{ getComposeItemValue(element, column, it) }}</span>\n </ng-container>\n </ng-container>\n </ng-container>\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 </div>\n </td>\n </ng-container>\n }\n @if (config.actions?.row?.enabled) {\n <ng-container 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]=\"getRowActionsWidthStyle()\" 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 <praxis-rich-content\n rootClassName=\"praxis-actions-header__nodes\"\n [nodes]=\"getActionsHeaderRichContentNodes()\"\n ></praxis-rich-content>\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]=\"getRowActionsWidthStyle()\"\n >\n <div\n class=\"praxis-actions-cell__content\"\n [class.praxis-actions-cell__content--discovering]=\"isRowDiscoveryPending(row)\"\n [attr.aria-busy]=\"isRowDiscoveryPending(row) ? 'true' : null\"\n >\n <!-- A\u00C3\u00A7\u00C3\u00B5es inline -->\n <!-- Inline actions: icons mode -->\n @if (config.actions?.row?.display === 'icons' || !config.actions?.row?.display) {\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <span\n class=\"praxis-row-action-anchor\"\n [class.praxis-row-action-anchor--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-row-action-anchor--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [matTooltip]=\"getActionTooltip(a, row)\"\n [matTooltipDisabled]=\"!getActionTooltip(a, row)\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"praxis-tooltip\"\n >\n <button\n mat-icon-button\n class=\"praxis-icon-btn\"\n [class.praxis-icon-btn--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-icon-btn--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event, getRowActionRuntimeOptions(a, row))\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\"\n >\n <praxis-rich-content\n rootClassName=\"praxis-row-action__content\"\n [nodes]=\"getRowActionIconRichContentNodes(a)\"\n ></praxis-rich-content>\n </button>\n </span>\n </ng-container>\n }\n\n <!-- Inline actions: buttons mode (show label + icon) -->\n @if (config.actions?.row?.display === 'buttons') {\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <span\n class=\"praxis-row-action-anchor\"\n [class.praxis-row-action-anchor--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-row-action-anchor--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [matTooltip]=\"getActionTooltip(a, row)\"\n [matTooltipDisabled]=\"!getActionTooltip(a, row)\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"praxis-tooltip\"\n >\n <button\n mat-flat-button\n class=\"praxis-row-btn\"\n [class.praxis-row-btn--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-row-btn--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event, getRowActionRuntimeOptions(a, row))\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\"\n >\n <praxis-rich-content\n rootClassName=\"praxis-row-action__content\"\n [nodes]=\"getRowActionRichContentNodes(a)\"\n ></praxis-rich-content>\n </button>\n </span>\n </ng-container>\n }\n\n <!-- Menu de overflow -->\n @if (hasOverflowRowActions(row)) {\n <button\n mat-icon-button\n class=\"praxis-icon-btn praxis-more-btn\"\n [matMenuTriggerFor]=\"rowMoreMenu\"\n (menuOpened)=\"onRowOverflowMenuOpened(row)\"\n [color]=\"getRowMenuButtonColor() || null\"\n [attr.aria-label]=\"getRowMenuTooltip(row)\"\n [matTooltip]=\"getRowMenuTooltip(row)\"\n matTooltipPosition=\"above\"\n >\n <mat-icon [praxisIcon]=\"getRowMenuIcon()\"></mat-icon>\n </button>\n }\n <mat-menu #rowMoreMenu=\"matMenu\" xPosition=\"before\">\n @if (hasRowOverflowMenuLoadingState(row)) {\n <button mat-menu-item disabled>\n <mat-icon>hourglass_empty</mat-icon>\n <praxis-rich-content\n rootClassName=\"praxis-row-overflow-menu__label\"\n [nodes]=\"getTableChromeTextRichContentNodes(getRowOverflowMenuLoadingLabel(), 'praxis-row-overflow-menu__label-text')\"\n ></praxis-rich-content>\n </button>\n }\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, getRowActionRuntimeOptions(a, row))\"\n [disabled]=\"isActionDisabled(a, row)\"\n >\n <mat-icon [color]=\"a.color || null\" [praxisIcon]=\"a.icon\"></mat-icon>\n <praxis-rich-content\n rootClassName=\"praxis-row-overflow-menu__label\"\n [nodes]=\"getTableChromeTextRichContentNodes(a.label || getActionId(a), 'praxis-row-overflow-menu__label-text')\"\n ></praxis-rich-content>\n </button>\n </ng-container>\n @if (hasRowOverflowMenuEmptyState(row)) {\n <button mat-menu-item disabled>\n <mat-icon>info</mat-icon>\n <praxis-rich-content\n rootClassName=\"praxis-row-overflow-menu__label\"\n [nodes]=\"getTableChromeTextRichContentNodes(getRowOverflowMenuEmptyLabel(row), 'praxis-row-overflow-menu__label-text')\"\n ></praxis-rich-content>\n </button>\n }\n </mat-menu>\n </div>\n </td>\n </ng-container>\n }\n\n <tr\n mat-header-row\n *matHeaderRowDef=\"displayedColumns\"\n ></tr>\n @if (!isVirtualized()) {\n <tr\n mat-row\n *matRowDef=\"let row; let i = index; columns: displayedColumns\"\n [class.pfx-row-selected]=\"selection.isSelected(row)\"\n [class.pfx-row-expanded]=\"isRowExpansionRuntimeEnabled() && isRowExpanded(row, i)\"\n [attr.aria-selected]=\"config.behavior?.selection?.enabled ? (selection.isSelected(row) ? 'true' : 'false') : null\"\n [attr.aria-expanded]=\"isRowExpansionRuntimeEnabled() ? (isRowExpanded(row, i) ? 'true' : 'false') : null\"\n [ngClass]=\"getRowClasses(row)\"\n [ngStyle]=\"getRowNgStyle(row)\"\n [matTooltip]=\"getRowTooltip(row) || null\"\n [matTooltipDisabled]=\"!getRowTooltip(row)\"\n [matTooltipPosition]=\"getRowTooltipPosition(row)\"\n [matTooltipShowDelay]=\"getRowTooltipShowDelay(row)\"\n (click)=\"onRowClicked(row, i, $event)\"\n (dblclick)=\"onRowDoubleClicked(row, i)\"\n ></tr>\n @if (isRowExpansionRuntimeEnabled()) {\n <ng-container matColumnDef=\"_detail\">\n <td\n mat-cell\n *matCellDef=\"let row; let i = index\"\n class=\"pfx-expansion-detail-cell\"\n [attr.colspan]=\"displayedColumns.length\"\n >\n <section\n class=\"pfx-expansion-detail-panel\"\n [ngClass]=\"getExpansionMotionPresetClass()\"\n [ngStyle]=\"getExpansionMotionStyle()\"\n [attr.id]=\"getRowExpansionDetailId(row, i)\"\n role=\"region\"\n [attr.aria-label]=\"getRowExpansionRegionAriaLabel(row, i)\"\n [attr.aria-busy]=\"getExpansionDetailViewState(row, i).status === 'loading' ? 'true' : 'false'\"\n >\n @let detailState = getExpansionDetailViewState(row, i);\n @if (detailState.status === 'loading') {\n <div class=\"pfx-expansion-detail-message\" role=\"status\" aria-live=\"polite\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-detail-message__content\"\n [nodes]=\"getExpansionDetailTextRichContentNodes('Carregando detail schema...', 'pfx-expansion-detail-message__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, i)\"\n ></praxis-rich-content>\n </div>\n } @else if (detailState.status !== 'ready') {\n <div\n class=\"pfx-expansion-detail-message pfx-expansion-detail-message--error\"\n role=\"alert\"\n aria-live=\"assertive\"\n aria-atomic=\"true\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-expansion-detail-message__content\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(detailState.message || 'Detail indispon\u00EDvel para esta linha.', 'pfx-expansion-detail-message__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, i)\"\n ></praxis-rich-content>\n </div>\n } @else {\n @if (getExpansionDetailLayout(detailState.schema) === 'tabs') {\n @let detailTabs = getExpansionDetailTabs(detailState.schema);\n @if (detailTabs.length) {\n <div class=\"pfx-expansion-detail-tabs\" role=\"tablist\" aria-label=\"Se\u00C3\u00A7\u00C3\u00B5es do detail\">\n @for (tab of detailTabs; track $index; let tabIndex = $index) {\n <button\n type=\"button\"\n class=\"pfx-expansion-detail-tab-btn\"\n role=\"tab\"\n [attr.id]=\"getExpansionDetailTabId(row, i, tab, tabIndex)\"\n [attr.aria-controls]=\"getExpansionDetailPanelId(row, i, tab, tabIndex)\"\n [attr.aria-selected]=\"isExpansionDetailTabActive(row, i, tab, tabIndex, detailTabs) ? 'true' : 'false'\"\n [attr.tabindex]=\"isExpansionDetailTabActive(row, i, tab, tabIndex, detailTabs) ? '0' : '-1'\"\n [class.is-active]=\"isExpansionDetailTabActive(row, i, tab, tabIndex, detailTabs)\"\n (click)=\"onExpansionDetailTabSelect(row, i, tab, tabIndex, $event)\"\n (keydown)=\"onExpansionDetailTabKeydown($event, row, i, tabIndex, detailTabs)\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-expansion-detail-tab-btn__content\"\n [nodes]=\"getExpansionDetailTabButtonRichContentNodes(tab)\"\n [context]=\"getExpansionDetailRichContentContext(row, i)\"\n ></praxis-rich-content>\n </button>\n }\n </div>\n @for (tab of detailTabs; track $index; let tabIndex = $index) {\n @if (isExpansionDetailTabActive(row, i, tab, tabIndex, detailTabs)) {\n <div\n class=\"pfx-expansion-detail-tab-panel\"\n role=\"tabpanel\"\n [attr.id]=\"getExpansionDetailPanelId(row, i, tab, tabIndex)\"\n [attr.aria-labelledby]=\"getExpansionDetailTabId(row, i, tab, tabIndex)\"\n >\n @if (getExpansionDetailTabRichContentNodes(tab, row, i); as richNodes) {\n <praxis-rich-content\n class=\"pfx-expansion-node-host pfx-expansion-node-host--tab\"\n [nodes]=\"richNodes\"\n [context]=\"getExpansionDetailRichContentContext(row, i)\"\n ></praxis-rich-content>\n } @else {\n @for (childNode of getExpansionDetailNodeChildren(tab); track $index) {\n <ng-container\n *ngTemplateOutlet=\"\n expansionDetailNodeTemplate;\n context: { $implicit: childNode, row: row, index: i }\n \"\n ></ng-container>\n }\n }\n </div>\n }\n }\n } @else {\n <div class=\"pfx-expansion-detail-message\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-detail-message__content\"\n [nodes]=\"getExpansionDetailTextRichContentNodes('Schema em layout tabs sem abas v\u00E1lidas.', 'pfx-expansion-detail-message__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, i)\"\n ></praxis-rich-content>\n </div>\n }\n } @else {\n <div class=\"pfx-expansion-detail-stack\">\n @for (node of getExpansionDetailItems(detailState.schema); track $index) {\n <ng-container\n *ngTemplateOutlet=\"\n expansionDetailNodeTemplate;\n context: { $implicit: node, row: row, index: i }\n \"\n ></ng-container>\n }\n </div>\n }\n }\n\n <ng-template #expansionDetailNodeTemplate let-node let-row=\"row\" let-index=\"index\">\n @if (getExpansionDetailRichContentNodes(node, row, index); as richNodes) {\n <praxis-rich-content\n class=\"pfx-expansion-node-host\"\n [nodes]=\"richNodes\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n } @else {\n @switch (getExpansionDetailNodeType(node)) {\n @case ('list') {\n <section class=\"pfx-expansion-node pfx-expansion-node-list\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-list__title\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(getExpansionDetailNodeTitle(node, 'Lista'), 'pfx-expansion-node-list__title-text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n @let listItems = getExpansionDetailListItems(row, node);\n @let listEntries = getExpansionDetailListEntries(row, node);\n @if (listItems.length) {\n <ul>\n @for (entry of listEntries; track $index) {\n <li>\n @if (getExpansionDetailListItemRichContentNodes(entry); as richNodes) {\n <praxis-rich-content\n class=\"pfx-expansion-node-list__item-host\"\n [nodes]=\"richNodes\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n } @else {\n {{ formatExpansionDetailListEntry(entry) }}\n }\n </li>\n }\n </ul>\n } @else {\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-placeholder\"\n [nodes]=\"getExpansionDetailTextRichContentNodes('Sem itens.', 'pfx-expansion-node-placeholder__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n }\n </section>\n }\n @case ('detailList') {\n <section class=\"pfx-expansion-node pfx-expansion-node-rich-list\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-rich-list__title\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(getExpansionDetailNodeTitle(node, 'Cole\u00E7\u00E3o rica'), 'pfx-expansion-node-rich-list__title-text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n @let richListEntries = getExpansionDetailRichListEntries(row, node);\n @if (richListEntries.length) {\n <div class=\"pfx-expansion-node-rich-list__items\">\n @for (entry of richListEntries; track $index) {\n <article [class]=\"getExpansionDetailRichListItemClassName(node)\">\n <praxis-rich-content\n class=\"pfx-expansion-node-rich-list__item-host\"\n [nodes]=\"getExpansionDetailRichListItemNodes(node)\"\n [context]=\"getExpansionDetailRichListItemContext(row, index, node, entry, $index)\"\n ></praxis-rich-content>\n @let itemActions = getExpansionDetailRichListItemActions(node, row, index, entry, $index);\n @if (itemActions.length) {\n <div class=\"pfx-expansion-node-rich-list__item-actions\">\n @for (action of itemActions; track action.actionId) {\n <button\n type=\"button\"\n mat-stroked-button\n class=\"pfx-expansion-node-rich-list__item-action-btn\"\n [disabled]=\"isExpansionDetailRichListItemActionDisabled(action, row, index, node, entry, $index)\"\n (click)=\"onExpansionDetailRichListItemAction(action, row, index, node, entry, $index, $event)\"\n >\n <praxis-rich-content\n class=\"pfx-expansion-node-rich-list__item-action-host\"\n [nodes]=\"getExpansionDetailRichListItemActionNodes(action)\"\n [context]=\"getExpansionDetailRichListItemContext(row, index, node, entry, $index)\"\n ></praxis-rich-content>\n </button>\n }\n </div>\n }\n </article>\n }\n </div>\n } @else {\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-placeholder\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(getExpansionDetailRichListEmptyText(node), 'pfx-expansion-node-placeholder__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n }\n </section>\n }\n @case ('cardGrid') {\n <section class=\"pfx-expansion-node pfx-expansion-node-card-grid\">\n @if (getExpansionDetailCardGridHeaderNodes(node); as cardGridHeaderNodes) {\n @if (cardGridHeaderNodes.length) {\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-card-grid__header\"\n [nodes]=\"cardGridHeaderNodes\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n }\n }\n @let gridCards = getExpansionDetailCardGridCards(node);\n @if (gridCards.length) {\n <div\n class=\"pfx-expansion-node-card-grid__cards\"\n [ngStyle]=\"getExpansionDetailCardGridStyles(node)\"\n >\n @for (card of gridCards; track $index) {\n <praxis-rich-content\n class=\"pfx-expansion-node-card-grid__card-host\"\n [nodes]=\"getExpansionDetailCardGridCardNodes(card)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n }\n </div>\n } @else {\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-placeholder\"\n [nodes]=\"getExpansionDetailTextRichContentNodes('Sem itens.', 'pfx-expansion-node-placeholder__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n }\n </section>\n }\n @case ('richText') {\n <div\n class=\"pfx-expansion-node pfx-expansion-node-richtext\"\n [innerHTML]=\"getExpansionDetailRichText(node)\"\n ></div>\n }\n @case ('formRef') {\n <section class=\"pfx-expansion-node pfx-expansion-node-embed\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__header\"\n [nodes]=\"getExpansionDetailEmbedHeaderNodes(node)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__meta\"\n [nodes]=\"getExpansionDetailEmbedMetaNodes(node)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__empty\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(getExpansionDetailEmbedEmptyText(node), 'pfx-expansion-node-embed__empty-text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n @if (getExpansionDetailEmbedAction(node, row, index); as embedAction) {\n <button\n type=\"button\"\n mat-stroked-button\n class=\"pfx-expansion-node-embed__action-btn\"\n [disabled]=\"isExpansionDetailEmbedActionDisabled(embedAction, row, index)\"\n (click)=\"onExpansionDetailEmbedAction(embedAction, row, index, $event)\"\n >\n <praxis-rich-content\n class=\"pfx-expansion-node-embed__action-host\"\n [nodes]=\"getExpansionDetailEmbedActionNodes(embedAction)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n </button>\n }\n </section>\n }\n @case ('tableRef') {\n <section class=\"pfx-expansion-node pfx-expansion-node-embed\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__header\"\n [nodes]=\"getExpansionDetailEmbedHeaderNodes(node)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__meta\"\n [nodes]=\"getExpansionDetailEmbedMetaNodes(node)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__empty\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(getExpansionDetailEmbedEmptyText(node), 'pfx-expansion-node-embed__empty-text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n @if (getExpansionDetailEmbedAction(node, row, index); as embedAction) {\n <button\n type=\"button\"\n mat-stroked-button\n class=\"pfx-expansion-node-embed__action-btn\"\n [disabled]=\"isExpansionDetailEmbedActionDisabled(embedAction, row, index)\"\n (click)=\"onExpansionDetailEmbedAction(embedAction, row, index, $event)\"\n >\n <praxis-rich-content\n class=\"pfx-expansion-node-embed__action-host\"\n [nodes]=\"getExpansionDetailEmbedActionNodes(embedAction)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n </button>\n }\n </section>\n }\n @case ('chartRef') {\n <section class=\"pfx-expansion-node pfx-expansion-node-embed\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__header\"\n [nodes]=\"getExpansionDetailEmbedHeaderNodes(node)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__meta\"\n [nodes]=\"getExpansionDetailEmbedMetaNodes(node)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__empty\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(getExpansionDetailEmbedEmptyText(node), 'pfx-expansion-node-embed__empty-text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n @if (getExpansionDetailEmbedAction(node, row, index); as embedAction) {\n <button\n type=\"button\"\n mat-stroked-button\n class=\"pfx-expansion-node-embed__action-btn\"\n [disabled]=\"isExpansionDetailEmbedActionDisabled(embedAction, row, index)\"\n (click)=\"onExpansionDetailEmbedAction(embedAction, row, index, $event)\"\n >\n <praxis-rich-content\n class=\"pfx-expansion-node-embed__action-host\"\n [nodes]=\"getExpansionDetailEmbedActionNodes(embedAction)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n </button>\n }\n </section>\n }\n @case ('templateRef') {\n <section class=\"pfx-expansion-node pfx-expansion-node-embed\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__header\"\n [nodes]=\"getExpansionDetailEmbedHeaderNodes(node)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__meta\"\n [nodes]=\"getExpansionDetailEmbedMetaNodes(node)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__empty\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(getExpansionDetailEmbedEmptyText(node), 'pfx-expansion-node-embed__empty-text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n @if (getExpansionDetailEmbedAction(node, row, index); as embedAction) {\n <button\n type=\"button\"\n mat-stroked-button\n class=\"pfx-expansion-node-embed__action-btn\"\n [disabled]=\"isExpansionDetailEmbedActionDisabled(embedAction, row, index)\"\n (click)=\"onExpansionDetailEmbedAction(embedAction, row, index, $event)\"\n >\n <praxis-rich-content\n class=\"pfx-expansion-node-embed__action-host\"\n [nodes]=\"getExpansionDetailEmbedActionNodes(embedAction)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n </button>\n }\n </section>\n }\n @case ('diagramEmbed') {\n <section class=\"pfx-expansion-node pfx-expansion-node-embed\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__header\"\n [nodes]=\"getExpansionDetailEmbedHeaderNodes(node)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__meta\"\n [nodes]=\"getExpansionDetailEmbedMetaNodes(node)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n @if (getExpansionDetailDiagramSource(row, node); as diagramSource) {\n <div class=\"pfx-expansion-node-embed__diagram\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__diagram-label\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(getExpansionDetailDiagramSourceLabel(), 'pfx-expansion-node-embed__diagram-label-text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <pre class=\"pfx-expansion-node-embed__diagram-source\">{{ diagramSource }}</pre>\n </div>\n } @else {\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-embed__empty\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(getExpansionDetailEmbedEmptyText(node), 'pfx-expansion-node-embed__empty-text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n }\n @if (getExpansionDetailEmbedAction(node, row, index); as embedAction) {\n <button\n type=\"button\"\n mat-stroked-button\n class=\"pfx-expansion-node-embed__action-btn\"\n [disabled]=\"isExpansionDetailEmbedActionDisabled(embedAction, row, index)\"\n (click)=\"onExpansionDetailEmbedAction(embedAction, row, index, $event)\"\n >\n <praxis-rich-content\n class=\"pfx-expansion-node-embed__action-host\"\n [nodes]=\"getExpansionDetailEmbedActionNodes(embedAction)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n </button>\n }\n </section>\n }\n @case ('action') {\n <div class=\"pfx-expansion-node pfx-expansion-node-action\">\n <button\n type=\"button\"\n mat-stroked-button\n [disabled]=\"isExpansionDetailActionDisabled(node)\"\n (click)=\"onExpansionDetailAction(node, row, $event)\"\n >\n <praxis-rich-content\n class=\"pfx-expansion-node-action__content\"\n [nodes]=\"getExpansionDetailActionRichContentNodes(node)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n </button>\n @if (getExpansionDetailActionStatusText(node); as actionStatusText) {\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-action__status\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(actionStatusText, 'pfx-expansion-node-action__status-text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n }\n </div>\n }\n @case ('actionBar') {\n <section class=\"pfx-expansion-node pfx-expansion-node-action-bar\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-action-bar__title\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(getExpansionDetailActionBarTitle(node), 'pfx-expansion-node-action-bar__title-text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n @let actionBarActions = getExpansionDetailActionBarActions(node, row, index);\n @if (actionBarActions.length) {\n <div class=\"pfx-expansion-node-action-bar__actions\">\n @for (action of actionBarActions; track action.actionId) {\n <button\n type=\"button\"\n mat-stroked-button\n class=\"pfx-expansion-node-action-bar__action-btn\"\n [disabled]=\"isExpansionDetailActionBarActionDisabled(action, row, index)\"\n (click)=\"onExpansionDetailActionBarAction(action, row, index, $event)\"\n >\n <praxis-rich-content\n class=\"pfx-expansion-node-action-bar__action-host\"\n [nodes]=\"getExpansionDetailActionBarActionNodes(action)\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n </button>\n }\n </div>\n } @else {\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-placeholder\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(getExpansionDetailActionBarEmptyText(node), 'pfx-expansion-node-placeholder__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n }\n </section>\n }\n @case ('tab') {\n <div class=\"pfx-expansion-node pfx-expansion-node-placeholder\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-placeholder__content\"\n [nodes]=\"getExpansionDetailTextRichContentNodes('Node', 'pfx-expansion-node-placeholder__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <code>tab</code>\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-placeholder__content\"\n [nodes]=\"getExpansionDetailTextRichContentNodes('fora de contexto de tabs.', 'pfx-expansion-node-placeholder__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n </div>\n }\n @default {\n <div class=\"pfx-expansion-node pfx-expansion-node-placeholder\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-placeholder__content\"\n [nodes]=\"getExpansionDetailTextRichContentNodes('Node n\u00E3o suportado:', 'pfx-expansion-node-placeholder__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <code>{{ getExpansionDetailNodeType(node) }}</code>\n </div>\n }\n }\n }\n </ng-template>\n </section>\n </td>\n </ng-container>\n <tr\n mat-row\n *matRowDef=\"let row; columns: expansionDetailRowColumns; when: isExpansionDetailRow\"\n class=\"pfx-expansion-detail-row\"\n ></tr>\n }\n }\n</table>\n}\n\n<!-- Virtual rows path (header preserved above) -->\n@if (shouldRenderDataSurface() && !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]=\"getVirtMinHeightHostStyle()\"\n >\n <table\n class=\"mat-mdc-table\"\n [ngClass]=\"getTableElevationClassName()\"\n [style.width]=\"getVirtualTableWidthStyle()\"\n >\n <tbody>\n <tr class=\"mat-mdc-row\"\n *cdkVirtualFor=\"let row of dataSource.data; let i = index; trackBy: trackByRow\"\n [class.pfx-row-selected]=\"selection.isSelected(row)\"\n [attr.aria-selected]=\"config.behavior?.selection?.enabled ? (selection.isSelected(row) ? 'true' : 'false') : null\"\n [ngClass]=\"getRowClasses(row)\"\n [ngStyle]=\"getRowNgStyle(row)\"\n [matTooltip]=\"getRowTooltip(row) || null\"\n [matTooltipDisabled]=\"!getRowTooltip(row)\"\n [matTooltipPosition]=\"getRowTooltipPosition(row)\"\n [matTooltipShowDelay]=\"getRowTooltipShowDelay(row)\"\n (click)=\"onRowClicked(row, i, $event)\"\n (dblclick)=\"onRowDoubleClicked(row, i)\">\n <!-- Selection column -->\n @if (config.behavior?.selection?.enabled) { <td class=\"mat-mdc-cell\">\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 @for (column of visibleColumns; track column.field) {\n <td class=\"mat-mdc-cell\"\n [style.text-align]=\"getColumnTextAlignStyle(column)\"\n [style.width]=\"getColumnWidthStyle(column)\"\n [attr.style]=\"getColumnCellAttrStyle(column)\"\n [ngClass]=\"getCellClasses(row, column)\"\n [ngStyle]=\"getCellNgStyle(row, column)\">\n <div\n class=\"pfx-cell-content\"\n [ngClass]=\"getCellContentClasses(row, column)\"\n [ngStyle]=\"getCellContentNgStyle(row, column)\"\n >\n <ng-container [ngSwitch]=\"getEffectiveRendererType(row, column)\">\n <ng-container *ngSwitchCase=\"'icon'\">\n <span class=\"pfx-icon-renderer\"\n [style.color]=\"getIconColor(row, column) || null\"\n [ngStyle]=\"getIconStyle(row, column)\"\n [attr.aria-label]=\"getIconAriaLabel(row, column) || null\">\n <praxis-rich-content\n rootClassName=\"pfx-icon-renderer__content\"\n [nodes]=\"getIconRichContentNodes(row, column)\"\n ></praxis-rich-content>\n </span>\n </ng-container>\n <ng-container *ngSwitchCase=\"'image'\">\n <span class=\"pfx-cell-image\">\n <praxis-rich-content\n rootClassName=\"pfx-cell-image__content\"\n [nodes]=\"getImageRichContentNodes(row, column)\"\n ></praxis-rich-content>\n </span>\n </ng-container>\n <ng-container *ngSwitchCase=\"'badge'\">\n <span class=\"pfx-badge\" [ngClass]=\"getBadgeClasses(row, column)\">\n <praxis-rich-content\n rootClassName=\"pfx-badge__content\"\n [nodes]=\"getBadgeRichContentNodes(row, column)\"\n ></praxis-rich-content>\n </span>\n </ng-container>\n <ng-container *ngSwitchCase=\"'chip'\">\n <span class=\"pfx-chip\" [ngClass]=\"getChipClasses(row, column)\">\n <praxis-rich-content\n rootClassName=\"pfx-chip__content\"\n [nodes]=\"getChipRichContentNodes(row, column)\"\n ></praxis-rich-content>\n </span>\n </ng-container>\n <ng-container *ngSwitchCase=\"'progress'\">\n <div class=\"pfx-progress\">\n <praxis-rich-content\n rootClassName=\"pfx-progress__content\"\n [context]=\"getProgressRichContentContext(row, column)\"\n [nodes]=\"getProgressRichContentNodes(row, column)\"\n ></praxis-rich-content>\n </div>\n </ng-container>\n <ng-container *ngSwitchCase=\"'avatar'\">\n <span\n class=\"pfx-avatar-renderer\"\n [ngStyle]=\"getAvatarStyle(row, column)\"\n [class.shape-rounded]=\"getAvatarShape(row, column) === 'rounded'\"\n [class.shape-circle]=\"getAvatarShape(row, column) === 'circle'\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-avatar-renderer__content\"\n [nodes]=\"getAvatarRichContentNodes(row, column)\"\n ></praxis-rich-content>\n </span>\n </ng-container>\n <ng-container *ngSwitchCase=\"'link'\">\n <a\n class=\"pfx-link\"\n [attr.href]=\"getLinkHref(row, column) || null\"\n [attr.target]=\"getLinkTarget(row, column) || null\"\n [attr.rel]=\"getLinkRel(row, column) || null\"\n (click)=\"$event.stopPropagation()\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-link__content\"\n [nodes]=\"getLinkRichContentNodes(row, column)\"\n ></praxis-rich-content>\n </a>\n </ng-container>\n <ng-container *ngSwitchCase=\"'button'\">\n <ng-container [ngSwitch]=\"getButtonVariant(row, column)\">\n <button\n *ngSwitchCase=\"'outlined'\"\n mat-stroked-button\n [color]=\"getButtonColor(row, column) || null\"\n [disabled]=\"isButtonDisabled(row, column)\"\n [attr.aria-label]=\"getButtonAriaLabel(row, column) || getButtonLabel(row, column)\"\n (click)=\"onButtonClick(row, column, $event)\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-button-renderer__content\"\n [nodes]=\"getButtonRichContentNodes(row, column)\"\n ></praxis-rich-content>\n </button>\n <button\n *ngSwitchCase=\"'text'\"\n mat-button\n [color]=\"getButtonColor(row, column) || null\"\n [disabled]=\"isButtonDisabled(row, column)\"\n [attr.aria-label]=\"getButtonAriaLabel(row, column) || getButtonLabel(row, column)\"\n (click)=\"onButtonClick(row, column, $event)\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-button-renderer__content\"\n [nodes]=\"getButtonRichContentNodes(row, column)\"\n ></praxis-rich-content>\n </button>\n <button\n *ngSwitchDefault\n mat-flat-button\n [color]=\"getButtonColor(row, column) || null\"\n [disabled]=\"isButtonDisabled(row, column)\"\n [attr.aria-label]=\"getButtonAriaLabel(row, column) || getButtonLabel(row, column)\"\n (click)=\"onButtonClick(row, column, $event)\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-button-renderer__content\"\n [nodes]=\"getButtonRichContentNodes(row, column)\"\n ></praxis-rich-content>\n </button>\n </ng-container>\n </ng-container>\n <ng-container *ngSwitchCase=\"'menu'\">\n <button\n mat-icon-button\n [matMenuTriggerFor]=\"menuRef\"\n (click)=\"$event.stopPropagation()\"\n [attr.aria-label]=\"getMenuAriaLabel(row, column) || 'Menu'\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-menu-trigger__content\"\n [nodes]=\"getMenuTriggerRichContentNodes()\"\n ></praxis-rich-content>\n </button>\n <mat-menu #menuRef=\"matMenu\">\n <button\n mat-menu-item\n *ngFor=\"let it of getMenuItems(row, column)\"\n (click)=\"onMenuItemClick(it, row, $event)\"\n [disabled]=\"!it.__visible\"\n >\n <praxis-rich-content\n rootClassName=\"pfx-menu-item__content\"\n [nodes]=\"getMenuItemRichContentNodes(it)\"\n ></praxis-rich-content>\n </button>\n </mat-menu>\n </ng-container>\n <ng-container *ngSwitchDefault>\n {{ getCellValue(row, column) }}\n </ng-container>\n </ng-container>\n </div>\n </td>\n }\n\n <!-- Actions column -->\n @if (config.actions?.row?.enabled) {\n <td class=\"mat-mdc-cell praxis-actions-cell\" [class.dense]=\"dense\" [style.width]=\"getRowActionsWidthStyle()\">\n <div\n class=\"praxis-actions-cell__content\"\n [class.praxis-actions-cell__content--discovering]=\"isRowDiscoveryPending(row)\"\n [attr.aria-busy]=\"isRowDiscoveryPending(row) ? 'true' : null\"\n >\n @if (config.actions?.row?.display === 'icons' || !config.actions?.row?.display) {\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <span\n class=\"praxis-row-action-anchor\"\n [class.praxis-row-action-anchor--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-row-action-anchor--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [matTooltip]=\"getActionTooltip(a, row)\"\n [matTooltipDisabled]=\"!getActionTooltip(a, row)\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"praxis-tooltip\"\n >\n <button mat-icon-button class=\"praxis-icon-btn\"\n [class.praxis-icon-btn--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-icon-btn--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event, getRowActionRuntimeOptions(a, row))\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\">\n <praxis-rich-content\n rootClassName=\"praxis-row-action__content\"\n [nodes]=\"getRowActionIconRichContentNodes(a)\"\n ></praxis-rich-content>\n </button>\n </span>\n </ng-container>\n }\n @if (config.actions?.row?.display === 'buttons') {\n <ng-container *ngFor=\"let a of getInlineRowActions(row); trackBy: trackAction\">\n <span\n class=\"praxis-row-action-anchor\"\n [class.praxis-row-action-anchor--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-row-action-anchor--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [matTooltip]=\"getActionTooltip(a, row)\"\n [matTooltipDisabled]=\"!getActionTooltip(a, row)\"\n matTooltipPosition=\"above\"\n matTooltipClass=\"praxis-tooltip\"\n >\n <button mat-flat-button class=\"praxis-row-btn\"\n [class.praxis-row-btn--workflow]=\"isWorkflowRowAction(a)\"\n [class.praxis-row-btn--blocked]=\"isBlockedWorkflowRowAction(a, row)\"\n [disabled]=\"isActionDisabled(a, row)\"\n (click)=\"onRowAction(getActionId(a), row, $event, getRowActionRuntimeOptions(a, row))\"\n [attr.aria-label]=\"a.label || getActionId(a)\"\n [color]=\"a.color || null\">\n <praxis-rich-content\n rootClassName=\"praxis-row-action__content\"\n [nodes]=\"getRowActionRichContentNodes(a)\"\n ></praxis-rich-content>\n </button>\n </span>\n </ng-container>\n }\n @if (hasOverflowRowActions(row)) {\n <button mat-icon-button class=\"praxis-icon-btn praxis-more-btn\"\n [matMenuTriggerFor]=\"rowMoreMenuV\"\n (menuOpened)=\"onRowOverflowMenuOpened(row)\"\n [color]=\"getRowMenuButtonColor() || null\"\n [attr.aria-label]=\"getRowMenuTooltip(row)\"\n [matTooltip]=\"getRowMenuTooltip(row)\"\n matTooltipPosition=\"above\">\n <mat-icon [praxisIcon]=\"getRowMenuIcon()\"></mat-icon>\n </button>\n }\n <mat-menu #rowMoreMenuV=\"matMenu\" xPosition=\"before\">\n @if (hasRowOverflowMenuLoadingState(row)) {\n <button mat-menu-item disabled>\n <mat-icon>hourglass_empty</mat-icon>\n <praxis-rich-content\n rootClassName=\"praxis-row-overflow-menu__label\"\n [nodes]=\"getTableChromeTextRichContentNodes(getRowOverflowMenuLoadingLabel(), 'praxis-row-overflow-menu__label-text')\"\n ></praxis-rich-content>\n </button>\n }\n <ng-container *ngFor=\"let a of getOverflowRowActions(row); trackBy: trackAction\">\n <button mat-menu-item (click)=\"onRowAction(getActionId(a), row, $event, getRowActionRuntimeOptions(a, row))\" [disabled]=\"isActionDisabled(a, row)\">\n <mat-icon [color]=\"a.color || null\" [praxisIcon]=\"a.icon\"></mat-icon>\n <praxis-rich-content\n rootClassName=\"praxis-row-overflow-menu__label\"\n [nodes]=\"getTableChromeTextRichContentNodes(a.label || getActionId(a), 'praxis-row-overflow-menu__label-text')\"\n ></praxis-rich-content>\n </button>\n </ng-container>\n @if (hasRowOverflowMenuEmptyState(row)) {\n <button mat-menu-item disabled>\n <mat-icon>info</mat-icon>\n <praxis-rich-content\n rootClassName=\"praxis-row-overflow-menu__label\"\n [nodes]=\"getTableChromeTextRichContentNodes(getRowOverflowMenuEmptyLabel(row), 'praxis-row-overflow-menu__label-text')\"\n ></praxis-rich-content>\n </button>\n }\n </mat-menu>\n </div>\n </td>\n }\n </tr>\n </tbody>\n </table>\n </cdk-virtual-scroll-viewport>\n}\n\n</div>\n@if (shouldShowLoadingSurface()) {\n <div class=\"ptable-loading-state\" role=\"status\" aria-live=\"polite\">\n <div class=\"ptable-loading-state__hero\" aria-hidden=\"true\">\n <span class=\"ptable-loading-state__line ptable-loading-state__line--wide\"></span>\n <span class=\"ptable-loading-state__line ptable-loading-state__line--short\"></span>\n <div class=\"ptable-loading-state__rows\">\n @for (placeholder of [1, 2, 3]; track placeholder) {\n <div class=\"ptable-loading-state__row\">\n <span class=\"ptable-loading-state__cell ptable-loading-state__cell--short\"></span>\n <span class=\"ptable-loading-state__cell\"></span>\n <span class=\"ptable-loading-state__cell ptable-loading-state__cell--wide\"></span>\n </div>\n }\n </div>\n </div>\n <div class=\"ptable-loading-state__message\">\n {{ getLoadingSurfaceMessage() }}\n </div>\n </div>\n}\n@if (shouldShowNoDataState()) {\n <div class=\"ptable-no-data-state\" role=\"status\" aria-live=\"polite\">\n <praxis-empty-state-card\n [icon]=\"getNoDataStateIcon()\"\n [title]=\"getNoDataStateTitle()\"\n [description]=\"getNoDataStateDescription()\"\n [primaryAction]=\"getNoDataStatePrimaryAction()\"\n [secondaryActions]=\"getNoDataStateSecondaryActions()\"\n [inline]=\"true\"\n ></praxis-empty-state-card>\n </div>\n}\n@if (\n shouldRenderDataSurface()\n && !schemaError\n && !dataError\n && shouldRenderFloatingBulkActions()\n && getFloatingBulkActions().length\n && !shouldHideFloatingBulkActions()\n) {\n <div [class]=\"'praxis-floating-bulk-actions ' + getFloatingBulkPositionClass()\">\n @for (action of getFloatingBulkActions(); track getActionId(action)) {\n <button\n mat-mini-fab\n [color]=\"action.color || 'primary'\"\n [disabled]=\"isFloatingBulkActionDisabled(action)\"\n (click)=\"onToolbarAction({ action: getActionId(action), actionConfig: action })\"\n [attr.aria-label]=\"action.label || getActionId(action)\"\n [matTooltip]=\"action.label || getActionId(action)\"\n matTooltipPosition=\"left\"\n >\n <praxis-rich-content\n rootClassName=\"praxis-floating-bulk-actions__content\"\n [nodes]=\"getFloatingBulkActionRichContentNodes(action)\"\n ></praxis-rich-content>\n </button>\n }\n </div>\n}\n<!-- Barra de a\u00C3\u00A7\u00C3\u00B5es no rodap\u00C3\u00A9 (opcional) -->\n@if (shouldRenderDataSurface() && !schemaError && !dataError && showToolbar && shouldRenderFooterToolbar()) {\n <praxis-table-toolbar\n [config]=\"config\"\n [backgroundColor]=\"getToolbarActionsBackgroundColor()\"\n [style.--pfx-filter-h]=\"getToolbarLayoutHeightHostStyle()\"\n [class.footer-flat]=\"hasBottomPaginator()\"\n class=\"praxis-toolbar-footer\"\n placement=\"footer\"\n [showMain]=\"shouldShowFooterToolbarMain()\"\n [showEndActions]=\"shouldShowFooterToolbarEndActions()\"\n [showReset]=\"shouldShowFooterToolbarReset()\"\n [showActionsGroup]=\"shouldShowToolbarActionsBottom()\"\n [showMobileActions]=\"shouldShowToolbarActionsBottom()\"\n [evaluationContext]=\"getToolbarEvaluationContext()\"\n (toolbarAction)=\"onToolbarAction($event)\"\n >\n <praxis-filter\n *ngIf=\"shouldShowFooterToolbarMain() && shouldRenderAdvancedFilter()\"\n advancedFilter\n [resourcePath]=\"getAdvancedFilterResourcePath()\"\n [filterId]=\"tableId\"\n [formId]=\"tableId\"\n [persistenceKey]=\"getAdvancedFilterPersistenceKey()\"\n [fieldMetadata]=\"getAdvancedFilterFieldMetadata()\"\n [enableCustomization]=\"enableCustomization\"\n [alwaysVisibleFields]=\"config.behavior?.filtering?.advancedFilters?.settings?.alwaysVisibleFields\"\n [alwaysVisibleFieldMetadataOverrides]=\"config.behavior?.filtering?.advancedFilters?.settings?.alwaysVisibleFieldMetadataOverrides ?? {}\"\n [selectedFieldIds]=\"config.behavior?.filtering?.advancedFilters?.settings?.selectedFieldIds ?? []\"\n [allowSaveTags]=\"config.behavior?.filtering?.advancedFilters?.settings?.allowSaveTags\"\n [changeDebounceMs]=\"config.behavior?.filtering?.advancedFilters?.settings?.changeDebounceMs ?? 300\"\n [i18n]=\"getFilterI18n()\"\n [mode]=\"'filter'\"\n [showFilterSettings]=\"enableCustomization\"\n (change)=\"onAdvancedFilterChange($event)\"\n (requestSearch)=\"onAdvancedFilterSubmit($event)\"\n (clear)=\"onAdvancedFilterClear()\"\n ></praxis-filter>\n <ng-container *ngIf=\"shouldShowFooterToolbarMain() && shouldRenderAdvancedFilter()\">\n <ng-content select=\"[advancedFilter]\"></ng-content>\n </ng-container>\n <ng-container *ngIf=\"shouldShowFooterToolbarMain() && shouldRenderAdvancedFilter()\">\n <ng-content select=\"[toolbar]\"></ng-content>\n </ng-container>\n @if (shouldShowFooterToolbarMain() && shouldRenderAdvancedFilter()) {\n @defer (on idle) {\n @if (aiAdapter) {\n <praxis-ai-assistant\n [adapter]=\"aiAdapter\"\n end-actions\n ></praxis-ai-assistant>\n }\n }\n }\n <button\n *ngIf=\"shouldShowFooterToolbarMain() && shouldRenderAdvancedFilter() && enableCustomization\"\n end-actions\n mat-icon-button\n color=\"primary\"\n data-role=\"table-settings\"\n data-testid=\"table-settings-trigger\"\n (click)=\"openTableSettings()\"\n [attr.aria-label]=\"getTableSettingsLabel()\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\"\n matBadgeSize=\"small\"\n matBadgePosition=\"above after\"\n [matTooltip]=\"getTableSettingsTooltip()\"\n matTooltipPosition=\"below\"\n >\n <mat-icon>settings</mat-icon>\n </button>\n </praxis-table-toolbar>\n}\n<!-- Paginadores (top/bottom) -->\n@if (shouldRenderDataSurface() && !schemaError && !dataError && getPaginationEnabled() && (config.behavior?.pagination?.position === 'top' || config.behavior?.pagination?.position === 'both')) {\n <mat-paginator\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\n@if (shouldRenderDataSurface() && !schemaError && !dataError && getPaginationEnabled() && (config.behavior?.pagination?.position === 'bottom' || config.behavior?.pagination?.position === 'both' || !config.behavior?.pagination?.position)) {\n <mat-paginator\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 [class.footer-stack]=\"shouldShowToolbarActionsBottom()\"\n >\n </mat-paginator>\n}\n", styles: ["@charset \"UTF-8\";table{width:100%}.praxis-visually-hidden-status{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.praxis-column-reorder-status{margin:8px 0;padding:10px 12px;border-radius:8px;border:1px solid var(--p-table-border-color);background:var(--p-table-row-hover-bg);color:var(--p-table-header-fg);font-size:12px;line-height:1.4;box-shadow:0 4px 12px #00000014;animation:pfx-column-reorder-status-in var(--p-table-drag-status-enter-duration, .18s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}@keyframes pfx-column-reorder-status-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.praxis-actions-cell{height:100%;padding-inline:12px;white-space:nowrap}.pfx-expansion-col-header,.pfx-expansion-col-cell{width:52px;min-width:52px;text-align:center}.pfx-expansion-toggle:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pfx-expansion-detail-row{background:var(--md-sys-color-surface-container-low)}.pfx-expansion-detail-cell{padding:0!important;border-bottom:1px solid var(--p-table-border-color)}.pfx-expansion-detail-panel{padding:12px 16px;border-left:3px solid var(--md-sys-color-primary)}.pfx-expansion-detail-panel.pfx-expansion-motion-none{animation:none;transition:none}.pfx-expansion-detail-panel.pfx-expansion-motion-subtle-slide{animation:pfx-expansion-subtle-slide-in var(--pfx-expansion-motion-duration, .16s) var(--pfx-expansion-motion-easing, cubic-bezier(.2, 0, 0, 1))}.pfx-expansion-detail-panel.pfx-expansion-motion-accordion{animation:pfx-expansion-accordion-in var(--pfx-expansion-motion-duration, .18s) var(--pfx-expansion-motion-easing, cubic-bezier(.2, 0, 0, 1));transform-origin:top center}.pfx-expansion-detail-panel.pfx-expansion-motion-fade-scale{animation:pfx-expansion-fade-scale-in var(--pfx-expansion-motion-duration, .16s) var(--pfx-expansion-motion-easing, cubic-bezier(.2, 0, 0, 1));transform-origin:top center}@keyframes pfx-expansion-subtle-slide-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}@keyframes pfx-expansion-accordion-in{0%{opacity:0;transform:scaleY(.96)}to{opacity:1;transform:scaleY(1)}}@keyframes pfx-expansion-fade-scale-in{0%{opacity:0;transform:translateY(-2px) scale(.985)}to{opacity:1;transform:translateY(0) scale(1)}}.pfx-expansion-detail-schema{margin:0;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;line-height:1.4;white-space:pre-wrap;word-break:break-word;color:var(--md-sys-color-on-surface-variant)}.pfx-expansion-detail-message{font-size:13px;color:var(--md-sys-color-on-surface-variant)}.pfx-expansion-detail-message--error{color:var(--md-sys-color-error)}.pfx-expansion-detail-stack{display:grid;gap:10px}.pfx-expansion-detail-tabs{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:10px}.pfx-expansion-detail-tab-btn{border:1px solid var(--p-table-border-color);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);border-radius:999px;padding:6px 12px;font-size:12px;line-height:1.2;cursor:pointer}.pfx-expansion-detail-tab-btn__content{display:inline-flex;align-items:center;gap:6px;min-width:0}.pfx-expansion-detail-tab-btn__icon .prx-rich-icon{font-size:16px;line-height:1}.pfx-expansion-detail-tab-btn__title{white-space:nowrap}.pfx-expansion-detail-tab-btn__badge .prx-rich-badge{padding:0 8px;min-height:18px;background:color-mix(in srgb,currentColor 12%,transparent);color:inherit}.pfx-expansion-detail-tab-btn.is-active{border-color:var(--md-sys-color-primary);color:var(--md-sys-color-primary);font-weight:600}.pfx-expansion-detail-tab-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pfx-expansion-detail-tab-panel{display:grid;gap:10px}.pfx-expansion-node{border:1px solid var(--p-table-border-color);border-radius:8px;padding:10px 12px;background:var(--md-sys-color-surface)}.pfx-expansion-node-card__header{margin-bottom:8px}.pfx-expansion-node-card__title{margin:0;font-size:14px;line-height:1.3}.pfx-expansion-node-card__subtitle{margin:4px 0 0;font-size:12px;color:var(--md-sys-color-on-surface-variant)}.pfx-expansion-node-value{display:flex;align-items:baseline;gap:8px}.pfx-expansion-node-value__label{color:var(--md-sys-color-on-surface-variant);font-size:12px}.pfx-expansion-node-value__content{font-size:14px}.pfx-expansion-node-list__title{margin:0 0 6px;font-size:13px}.pfx-expansion-node-list ul{margin:0;padding-left:18px}.pfx-expansion-node-rich-list__title{margin:0 0 8px;font-size:13px}.pfx-expansion-node-rich-list__items{display:grid;gap:12px}.pfx-expansion-node-rich-list__item{display:grid;gap:10px;padding:12px;border:1px solid var(--md-sys-color-outline-variant, #d7dbe5);border-radius:14px;background:var(--md-sys-color-surface-container-lowest, #fff)}.pfx-expansion-node-rich-list__item--row{grid-template-columns:minmax(0,1fr) auto;align-items:center}.pfx-expansion-node-rich-list__item--stack{grid-template-columns:minmax(0,1fr)}.pfx-expansion-node-rich-list__item--card-list{grid-template-columns:minmax(0,1fr);box-shadow:0 4px 14px #0f172a0a}.pfx-expansion-node-rich-list__item-host{min-width:0}.pfx-expansion-node-rich-list__item-actions{display:flex;flex-wrap:wrap;gap:8px;align-items:center}.pfx-expansion-node-rich-list__item-action-btn{min-width:0}.pfx-expansion-node-rich-list__item-action-host{display:inline-flex;align-items:center}.pfx-expansion-node-action-bar__title{margin:0 0 12px}.pfx-expansion-node-action-bar__actions{display:flex;flex-wrap:wrap;gap:8px;align-items:center}.pfx-expansion-node-action-bar__action-btn{min-width:0}.pfx-expansion-node-action-bar__action-host{display:inline-flex;align-items:center}.pfx-expansion-node-card-grid__header{margin:0 0 12px}.pfx-expansion-node-card-grid__cards{display:grid;gap:12px;grid-template-columns:var(--pfx-expansion-card-grid-columns, repeat(auto-fit, minmax(var(--pfx-expansion-card-grid-min-width, 220px), 1fr)))}.pfx-expansion-node-card-grid__card-host{min-width:0}.pfx-expansion-node-card-grid__card-host [data-rich-node-type=card]{height:100%}.pfx-expansion-node-richtext :where(p,ul,ol,h1,h2,h3,h4,h5,h6){margin-top:0;margin-bottom:8px}.pfx-expansion-node-placeholder{font-size:12px;color:var(--md-sys-color-on-surface-variant)}:host.density-compact{--p-header-padding: 6px 10px;--p-cell-padding: 6px 10px;--p-actions-btn-size: 30px;--p-actions-icon-size: 16px}:host.density-comfortable{--p-header-padding: 10px 14px;--p-cell-padding: 10px 14px;--p-actions-btn-size: 36px;--p-actions-icon-size: 18px}:host.density-spacious{--p-header-padding: 16px 20px;--p-cell-padding: 16px 20px;--p-actions-btn-size: 44px;--p-actions-icon-size: 24px}:host.density-compact ::ng-deep .mat-mdc-cell{padding:var(--p-cell-padding, 8px 12px)}:host.density-comfortable ::ng-deep .mat-mdc-cell{padding:var(--p-cell-padding, 12px 16px)}:host.density-spacious ::ng-deep .mat-mdc-cell{padding:var(--p-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:6px;width:100%;transition:opacity .12s ease}.praxis-actions-cell.dense .praxis-actions-cell__content{gap:6px}.praxis-actions-cell__content--discovering{opacity:.78}.praxis-row-action-anchor{display:inline-flex;align-items:center}.praxis-row-action-anchor--workflow{position:relative}.praxis-row-action-anchor--workflow:after{content:\"\";position:absolute;right:4px;top:4px;width:6px;height:6px;border-radius:999px;background:var(--md-sys-color-secondary);opacity:.9;pointer-events:none}.praxis-row-action-anchor--blocked:after{background:var(--md-sys-color-error)}.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)}.praxis-icon-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);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-icon-btn--workflow{background:color-mix(in srgb,var(--md-sys-color-secondary-container) 36%,transparent)}.praxis-icon-btn--blocked{background:color-mix(in srgb,var(--md-sys-color-error-container) 42%,transparent)}.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-row-btn--workflow{box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--md-sys-color-secondary) 28%,transparent)}.praxis-row-btn--blocked{box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--md-sys-color-error) 28%,transparent)}.praxis-icon-btn.destructive mat-icon{color:var(--md-sys-color-error)}.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:6px;margin:10px 0 8px;width:100%;clear:both;position:relative}.praxis-table-header.stacked{margin:0}.praxis-table-header>praxis-table-toolbar{flex:1 0 100%}.praxis-floating-bulk-actions{position:fixed;z-index:var(--praxis-layer-floating-local, 200);display:inline-flex;align-items:center;gap:8px;padding:8px;border-radius:999px;background:var(--md-sys-color-surface-container-highest);box-shadow:0 8px 20px #00000029}.praxis-floating-bulk-actions.pos-bottom-right{right:20px;bottom:20px}.praxis-floating-bulk-actions.pos-bottom-left{left:20px;bottom:20px}.praxis-floating-bulk-actions.pos-top-right{right:20px;top:20px}.praxis-floating-bulk-actions.pos-top-left{left:20px;top:20px}@media(max-width:768px){.praxis-floating-bulk-actions{gap:6px;padding:6px}.praxis-floating-bulk-actions.pos-bottom-right{right:12px;left:auto;bottom:12px}.praxis-floating-bulk-actions.pos-bottom-left{left:12px;right:auto;bottom:12px}.praxis-floating-bulk-actions.pos-top-right{right:12px;left:auto;top:12px}.praxis-floating-bulk-actions.pos-top-left{left:12px;right:auto;top:12px}}:host{display:block;width:100%;min-width:0;max-width:100%;--pfx-toolbar-pad-y: 6px;--pfx-toolbar-pad-x: 12px;--p-table-bg: var(--md-sys-color-surface-container-highest);--p-table-text-color: var(--md-sys-color-on-surface);--p-table-header-bg: var(--md-sys-color-surface-container-highest);--p-table-header-fg: var(--md-sys-color-on-surface);--p-table-border-color: var(--md-sys-color-outline-variant);--p-table-row-even-bg: var(--md-sys-color-surface-container);--p-table-row-hover-bg: var(--md-sys-color-surface-container-high);--p-table-row-selected-bg: var(--md-sys-color-primary-container);--p-table-badge-soft-primary-bg: var(--md-sys-color-primary-container);--p-table-badge-soft-primary-fg: var(--md-sys-color-on-primary-container);--p-table-badge-soft-accent-bg: var(--md-sys-color-secondary-container);--p-table-badge-soft-accent-fg: var(--md-sys-color-on-secondary-container);--p-table-badge-soft-warn-bg: var(--md-sys-color-error-container);--p-table-badge-soft-warn-fg: var(--md-sys-color-on-error-container);--p-table-state-success-bg: var(--md-sys-color-tertiary-container);--p-table-state-success-fg: var(--md-sys-color-on-tertiary-container);--p-table-state-warning-bg: var(--md-sys-color-secondary-container);--p-table-state-warning-fg: var(--md-sys-color-on-secondary-container);--p-table-state-danger-bg: var(--md-sys-color-error-container);--p-table-state-danger-fg: var(--md-sys-color-on-error-container);--p-table-state-highlight-bg: var(--md-sys-color-primary-container);--p-table-state-highlight-fg: var(--md-sys-color-on-primary-container);--p-table-drag-handle-size: 14px;--p-table-drag-handle-color: var(--md-sys-color-on-surface-variant);--p-table-drag-handle-hover-color: var(--md-sys-color-on-surface);--p-table-drag-handle-base-opacity: 0;--p-table-drag-handle-visible-opacity: .72;--p-table-drag-handle-active-opacity: 1;--p-table-drag-handle-transition-duration: .16s;--p-table-reorder-transition-duration: .16s;--p-table-drag-preview-scale: 1.01;--p-table-drag-status-enter-duration: .18s;--p-table-drag-preview-shadow: 0 14px 32px rgba(0, 0, 0, .28), 0 0 0 1px var(--p-table-border-color)}:host ::ng-deep .mat-mdc-table{background:var(--p-table-bg);color:var(--p-table-text-color);border-radius:12px 12px 0 0;width:100%;box-shadow:var(--p-table-surface-shadow);transition:box-shadow var(--p-table-selection-transition-duration, .18s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}:host ::ng-deep .mat-mdc-table:hover{box-shadow:var(--p-table-surface-shadow-hover, var(--p-table-surface-shadow))}:host ::ng-deep .mat-mdc-table.table-stack-top{border-top-left-radius:0;border-top-right-radius:0}:host ::ng-deep .praxis-toolbar-footer{border:0 solid var(--p-table-border-color);border-top:0;border-radius:0;background:var(--p-table-bg)}:host ::ng-deep .mat-mdc-paginator.footer-stack{border-top-left-radius:0;border-top-right-radius:0;border-top:0}:host ::ng-deep .mat-mdc-paginator.footer-stack .mat-mdc-paginator-container{padding:6px 10px}:host ::ng-deep .mat-mdc-paginator{border-top:1px solid color-mix(in srgb,var(--p-table-border-color) 82%,transparent);background:color-mix(in srgb,var(--p-table-bg) 94%,white)}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-container{min-height:40px;padding:4px 8px;gap:6px}:host ::ng-deep .mat-mdc-paginator .mat-mdc-paginator-range-label,:host ::ng-deep .mat-mdc-paginator .mat-mdc-select-value-text{font-size:12px;color:color-mix(in srgb,var(--p-table-text-color) 76%,white)}:host [data-role=table-settings].mat-mdc-icon-button{--mdc-icon-button-icon-color: var(--md-sys-color-primary);color:var(--md-sys-color-primary)}.pfx-link{color:var(--md-sys-color-primary);text-decoration:underline;cursor:pointer}.pfx-chip{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:10px;font-size:12px;line-height:1;border:1px solid transparent}.pfx-chip-icon{font-size:14px;width:14px;height:14px}.pfx-chip--outlined{background:transparent;border-color:var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface)}.pfx-chip--filled-primary{background:var(--md-sys-color-primary);color:var(--md-sys-color-on-primary)}.pfx-chip--filled-accent{background:var(--md-sys-color-secondary);color:var(--md-sys-color-on-secondary)}.pfx-chip--filled-warn{background:var(--md-sys-color-error);color:var(--md-sys-color-on-error)}.pfx-chip--soft-primary{background:var(--p-table-badge-soft-primary-bg);color:var(--p-table-badge-soft-primary-fg)}.pfx-chip--soft-accent{background:var(--p-table-badge-soft-accent-bg);color:var(--p-table-badge-soft-accent-fg)}.pfx-chip--soft-warn{background:var(--p-table-badge-soft-warn-bg);color:var(--p-table-badge-soft-warn-fg)}.pfx-progress{position:relative;width:100%;max-width:140px;height:8px;background:var(--md-sys-color-surface-container-highest);border-radius:4px;overflow:hidden;display:inline-block;vertical-align:middle}.pfx-progress-bar{height:100%;background:var(--md-sys-color-primary);transition:width .2s ease}.pfx-progress-label{margin-left:8px;font-size:12px;opacity:.8;display:inline-block;vertical-align:middle}.pfx-avatar{display:inline-flex;align-items:center;justify-content:center;background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface);border-radius:4px;overflow:hidden;font-weight:600}.pfx-avatar.shape-rounded{border-radius:8px}.pfx-avatar.shape-circle{border-radius:999px}.pfx-avatar--initials{text-transform:uppercase;font-size:12px}.pfx-cell-compose{display:inline-flex;align-items:center;gap:6px}.pfx-cell-compose.dir-col{flex-direction:column;align-items:stretch}.pfx-cell-compose.align-start{justify-content:flex-start}.pfx-cell-compose.align-center{justify-content:center}.pfx-cell-compose.align-end{justify-content:flex-end}.pfx-cell-compose.wrap{flex-wrap:wrap}.pfx-cell-compose.ellipsis{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.px-scroll-viewport{width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;overscroll-behavior-x:contain}.px-scroll-viewport.scroll-none{overflow-x:visible}.px-scroll-viewport.scroll-auto ::ng-deep .mat-mdc-table{width:max-content;min-width:100%}.px-scroll-viewport.scroll-wrap ::ng-deep .mat-mdc-header-cell,.px-scroll-viewport.scroll-wrap ::ng-deep .mat-mdc-cell{white-space:normal;text-overflow:initial}.px-scroll-viewport.scroll-wrap ::ng-deep .mat-mdc-table{width:100%}:host ::ng-deep .mat-mdc-header-row{position:sticky;top:0;z-index:var(--praxis-layer-sticky-local, 100);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}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable .mat-sort-header-container{display:flex;align-items:center;width:100%;gap:4px;cursor:inherit}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable{-webkit-user-select:none;user-select:none;cursor:grab;padding-left:0!important}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable.cdk-drag-dragging{cursor:grabbing}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable .mat-sort-header-content{display:inline-flex;align-items:center;gap:4px;flex:1 1 auto;min-width:0}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-header-label{display:inline-flex;align-items:center;gap:4px;flex:1 1 auto;min-width:0}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-header-label-text{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-column-drag-handle{-webkit-appearance:none;appearance:none;border:0;background:transparent;color:var(--p-table-drag-handle-color);width:var(--p-table-drag-handle-size);min-width:var(--p-table-drag-handle-size);flex:0 0 var(--p-table-drag-handle-size);height:var(--p-table-drag-handle-size);padding:0;display:inline-flex;align-items:center;justify-content:center;border-radius:0;cursor:inherit;pointer-events:none;touch-action:none;opacity:var(--p-table-drag-handle-base-opacity, .42);transform:none;order:-1;margin-inline-end:0;transition:opacity var(--p-table-drag-handle-transition-duration, .16s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1)),color var(--p-table-drag-handle-transition-duration, .16s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable:hover .praxis-column-drag-handle,:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable:focus-within .praxis-column-drag-handle{opacity:var(--p-table-drag-handle-visible-opacity, .72);color:var(--p-table-drag-handle-hover-color, var(--p-table-drag-handle-color))}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-column-drag-handle:active{opacity:var(--p-table-drag-handle-active-opacity, 1);cursor:grabbing}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-column-drag-handle .mat-icon{font-size:14px;width:14px;height:14px;line-height:14px;transition:transform .18s var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable:hover .praxis-column-drag-handle .mat-icon{transform:none}:host ::ng-deep .pfx-column-drag-indicator .cdk-drop-list-dragging .mat-mdc-header-cell:not(.cdk-drag-placeholder){transition:transform var(--p-table-reorder-transition-duration, .18s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}:host ::ng-deep .pfx-column-drag-indicator .mat-mdc-header-cell.cdk-drag-animating{transition:transform var(--p-table-reorder-transition-duration, .16s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}.pfx-column-drag-preview{box-sizing:border-box;display:flex;align-items:center;border-radius:10px;border:1px solid var(--p-table-border-color);background:linear-gradient(135deg,var(--p-table-header-bg) 0%,var(--p-table-row-hover-bg) 100%);color:var(--p-table-header-fg);box-shadow:var(--p-table-drag-preview-shadow);transform:scale(var(--p-table-drag-preview-scale, 1.01));pointer-events:none;z-index:var(--praxis-layer-authoring-hover, 300)}.pfx-column-drag-preview .praxis-column-drag-handle,.pfx-column-drag-preview .mat-sort-header-arrow,.pfx-column-drag-preview .mat-sort-header-indicator,.pfx-column-drag-preview .mat-sort-header-stem,.pfx-column-drag-preview .mat-sort-header-pointer,.pfx-column-drag-preview .mat-sort-header-pointer-left,.pfx-column-drag-preview .mat-sort-header-pointer-right{display:none!important}.pfx-column-drag-preview .mat-sort-header-container{display:flex;align-items:center;width:100%;min-height:100%;padding-right:0!important}.pfx-column-drag-preview .mat-sort-header-content,.pfx-column-drag-preview .praxis-header-label{display:inline-flex;align-items:center;min-width:0;width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host ::ng-deep .pfx-column-drag-indicator .cdk-drag-placeholder{opacity:1;border:1px dashed var(--p-table-border-color);background:var(--p-table-row-hover-bg)}:host ::ng-deep .pfx-column-drag-indicator .cdk-drag-placeholder *{opacity:0}:host ::ng-deep .pfx-column-drag-indicator .mat-mdc-header-cell.cdk-drag-dragging{opacity:.58}@media(prefers-reduced-motion:reduce){:host ::ng-deep .pfx-column-drag-indicator .cdk-drop-list-dragging .mat-mdc-header-cell:not(.cdk-drag-placeholder){transition:none}:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-column-drag-handle,:host ::ng-deep .pfx-column-drag-enabled .mat-mdc-header-cell .praxis-column-drag-handle .mat-icon,.pfx-column-drag-preview{transition:none;transform:none}.praxis-column-reorder-status{animation:none}.pfx-expansion-detail-panel{animation:none!important;transition:none!important;transform:none!important}:host ::ng-deep .mat-mdc-row{transition:none}:host ::ng-deep .mat-mdc-table{transition:none}}.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 .pfx-column-drag-enabled .mat-mdc-header-cell.praxis-header-draggable .mat-sort-header-container{padding-right:12px}@media(pointer:coarse){:host{--p-table-drag-handle-size: 18px;--p-table-drag-handle-base-opacity: .56;--p-table-drag-handle-visible-opacity: .92}}:host ::ng-deep .mat-mdc-cell{color:var(--p-table-text-color);font-size:var(--p-cell-font-size, inherit);line-height:1.25;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}:host ::ng-deep .mat-mdc-cell .pfx-cell-content,:host ::ng-deep .mat-cell .pfx-cell-content{display:inline-flex;align-items:center;gap:6px;width:100%;min-width:0;overflow:hidden}:host ::ng-deep .mat-mdc-row:hover{background:var(--p-table-row-hover-bg)}:host ::ng-deep .mat-mdc-row{transition:background-color var(--p-table-hover-transition-duration, .18s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1)),box-shadow var(--p-table-selection-transition-duration, .18s) var(--p-table-motion-easing, cubic-bezier(0, 0, .2, 1))}:host ::ng-deep .mat-mdc-row:nth-child(2n){background:var(--p-table-row-even-bg)}:host ::ng-deep .mat-mdc-row:nth-child(2n):hover{background:var(--p-table-row-hover-bg)}:host ::ng-deep .mat-mdc-row.pfx-row-selected,:host ::ng-deep .mat-mdc-row.pfx-row-selected:hover{background:var(--p-table-row-selected-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-error{display:flex;align-items:center;gap:12px;padding:12px;margin:8px 0;border:1px solid var(--md-sys-color-error);border-radius:8px}.ptable-error__content{flex:1}.ptable-error__title{font-weight:600}.ptable-info-banner{display:flex;gap:12px;align-items:center;padding:8px 12px;margin:8px 0;border-radius:8px;border:1px solid var(--md-sys-color-primary);background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.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);border:1px solid var(--md-sys-color-outline-variant)}.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(--md-sys-color-primary);color:var(--md-sys-color-on-primary)}.pfx-badge--filled-accent{background:var(--md-sys-color-secondary);color:var(--md-sys-color-on-secondary)}.pfx-badge--filled-warn{background:var(--md-sys-color-error);color:var(--md-sys-color-on-error)}.pfx-badge--outlined{background:transparent;border-color:var(--md-sys-color-outline-variant);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)}.pfx-expansion-node-embed{display:flex;flex-direction:column;gap:12px;padding:16px;border:1px solid var(--md-sys-color-outline-variant);border-radius:16px;background:var(--md-sys-color-surface-container-low, var(--md-sys-color-surface))}.ptable-loading-state{padding:8px 12px 4px;display:grid;gap:10px}.ptable-loading-state__hero{display:grid;gap:10px;padding:14px;border-radius:8px;border:1px solid rgba(18,99,180,.08);background:linear-gradient(180deg,#ffffffa6,#ffffff47),linear-gradient(0deg,#1263b408,#1263b400)}.ptable-loading-state__rows{display:grid;gap:8px}.ptable-loading-state__row{display:grid;grid-template-columns:96px minmax(0,1fr) minmax(140px,1.2fr);gap:8px}.ptable-loading-state__line,.ptable-loading-state__cell{display:block;height:12px;border-radius:999px;background:linear-gradient(90deg,#1263b414,#1263b42e,#1263b414);background-size:200% 100%;animation:ptable-loading-wave 1.2s ease-in-out infinite}.ptable-loading-state__line--wide{width:180px}.ptable-loading-state__line--short{width:96px}.ptable-loading-state__cell--short{width:100%;max-width:84px}.ptable-loading-state__cell--wide{width:100%}.ptable-loading-state__message{font-size:.85rem;color:var(--md-sys-color-on-surface-variant, #5a5d67);padding-inline:4px}.pfx-expansion-node-embed__header,.pfx-expansion-node-embed__meta,.pfx-expansion-node-embed__empty,.pfx-expansion-node-embed__diagram-label{display:block}.ptable-no-data-state{padding:8px 12px 4px}.ptable-no-data-state ::ng-deep .empty-card{margin:0;border-radius:8px}@keyframes ptable-loading-wave{0%{background-position:100% 0}to{background-position:-100% 0}}.pfx-expansion-node-embed__diagram{display:flex;flex-direction:column;gap:8px}.pfx-expansion-node-embed__diagram-source{margin:0;padding:12px;overflow:auto;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-highest, var(--md-sys-color-surface));color:var(--md-sys-color-on-surface);font-family:Consolas,Courier New,monospace;font-size:12px;line-height:1.4;white-space:pre-wrap}.pfx-expansion-node-embed__action-btn{align-self:flex-start}.pfx-expansion-node-embed__action-host{display:inline-flex}\n"] }]
45476
47116
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i3$1.SettingsPanelService }, { type: i1.GenericCrudService }, { type: TableDefaultsProvider }, { type: FilterConfigService }, { type: DataFormattingService }, { type: i6$3.PraxisDialog }, { type: i2$2.MatSnackBar }, { type: undefined, decorators: [{
45477
47117
  type: Inject,
45478
47118
  args: [ASYNC_CONFIG_STORAGE]
@@ -45909,6 +47549,1406 @@ function normalizeError(error) {
45909
47549
  return 'Unknown analytics table contract error.';
45910
47550
  }
45911
47551
 
47552
+ function isTableRendererSupportedByRichContentP0(renderer) {
47553
+ if (!renderer) {
47554
+ return false;
47555
+ }
47556
+ switch (renderer.type) {
47557
+ case 'icon':
47558
+ case 'image':
47559
+ case 'badge':
47560
+ case 'chip':
47561
+ case 'progress':
47562
+ case 'avatar':
47563
+ case 'compose':
47564
+ return true;
47565
+ default:
47566
+ return false;
47567
+ }
47568
+ }
47569
+ function mapTableRendererToRichContentP0(renderer) {
47570
+ if (!renderer) {
47571
+ return null;
47572
+ }
47573
+ switch (renderer.type) {
47574
+ case 'icon':
47575
+ return mapIconRenderer(renderer);
47576
+ case 'image':
47577
+ return mapImageRenderer(renderer);
47578
+ case 'badge':
47579
+ return mapBadgeRenderer(renderer);
47580
+ case 'chip':
47581
+ return mapChipRenderer(renderer);
47582
+ case 'progress':
47583
+ return mapProgressRenderer(renderer);
47584
+ case 'avatar':
47585
+ return mapAvatarRenderer(renderer);
47586
+ case 'compose':
47587
+ return mapComposeRenderer(renderer);
47588
+ default:
47589
+ return null;
47590
+ }
47591
+ }
47592
+ function mapIconRenderer(renderer) {
47593
+ return {
47594
+ type: 'icon',
47595
+ icon: renderer.icon?.name || renderer.icon?.nameField || '',
47596
+ };
47597
+ }
47598
+ function mapImageRenderer(renderer) {
47599
+ return {
47600
+ type: 'image',
47601
+ src: renderer.image?.src,
47602
+ srcExpr: asRowExpr(renderer.image?.srcField),
47603
+ alt: renderer.image?.alt,
47604
+ altExpr: asRowExpr(renderer.image?.altField),
47605
+ };
47606
+ }
47607
+ function mapBadgeRenderer(renderer) {
47608
+ return {
47609
+ type: 'badge',
47610
+ label: renderer.badge?.text,
47611
+ labelExpr: asRowExpr(renderer.badge?.textField),
47612
+ icon: renderer.badge?.icon,
47613
+ };
47614
+ }
47615
+ function mapChipRenderer(renderer) {
47616
+ return {
47617
+ type: 'badge',
47618
+ label: renderer.chip?.text,
47619
+ labelExpr: asRowExpr(renderer.chip?.textField),
47620
+ icon: renderer.chip?.icon,
47621
+ };
47622
+ }
47623
+ function mapProgressRenderer(renderer) {
47624
+ return {
47625
+ type: 'progress',
47626
+ valueExpr: renderer.progress?.valueExpr || '${row.value}',
47627
+ showPercent: renderer.progress?.showLabel,
47628
+ };
47629
+ }
47630
+ function mapAvatarRenderer(renderer) {
47631
+ return {
47632
+ type: 'avatar',
47633
+ imageSrc: renderer.avatar?.src,
47634
+ imageSrcExpr: asRowExpr(renderer.avatar?.srcField),
47635
+ nameExpr: asRowExpr(renderer.avatar?.altField),
47636
+ initialsExpr: renderer.avatar?.initialsExpr,
47637
+ };
47638
+ }
47639
+ function mapComposeRenderer(renderer) {
47640
+ const items = (renderer.compose?.items || [])
47641
+ .map(item => mapComposeItemToRichContent(item))
47642
+ .filter(isRichPresenterNode);
47643
+ return {
47644
+ type: 'compose',
47645
+ direction: renderer.compose?.layout?.direction || 'row',
47646
+ gap: normalizeGap(renderer.compose?.layout?.gap),
47647
+ wrap: renderer.compose?.layout?.wrap,
47648
+ items,
47649
+ };
47650
+ }
47651
+ function mapComposeItemToRichContent(item) {
47652
+ switch (item.type) {
47653
+ case 'icon':
47654
+ return mapTableRendererToRichContentP0({
47655
+ type: 'icon',
47656
+ icon: item.icon,
47657
+ });
47658
+ case 'image':
47659
+ return mapTableRendererToRichContentP0({
47660
+ type: 'image',
47661
+ image: item.image,
47662
+ });
47663
+ case 'badge':
47664
+ return mapTableRendererToRichContentP0({
47665
+ type: 'badge',
47666
+ badge: item.badge,
47667
+ });
47668
+ case 'chip':
47669
+ return mapTableRendererToRichContentP0({
47670
+ type: 'chip',
47671
+ chip: item.chip,
47672
+ });
47673
+ case 'progress':
47674
+ return mapTableRendererToRichContentP0({
47675
+ type: 'progress',
47676
+ progress: item.progress,
47677
+ });
47678
+ case 'avatar':
47679
+ return mapTableRendererToRichContentP0({
47680
+ type: 'avatar',
47681
+ avatar: item.avatar,
47682
+ });
47683
+ default:
47684
+ return null;
47685
+ }
47686
+ }
47687
+ function isRichPresenterNode(node) {
47688
+ return !!node && node.type !== 'compose';
47689
+ }
47690
+ function asRowExpr(field) {
47691
+ return field ? `\${row.${field}}` : undefined;
47692
+ }
47693
+ function normalizeGap(gap) {
47694
+ switch (gap) {
47695
+ case 4:
47696
+ return 'xs';
47697
+ case 8:
47698
+ return 'sm';
47699
+ case 16:
47700
+ return 'lg';
47701
+ case 20:
47702
+ return 'xl';
47703
+ default:
47704
+ return gap == null ? undefined : 'md';
47705
+ }
47706
+ }
47707
+
47708
+ class PraxisTableInlineAuthoringEditorComponent {
47709
+ i18n;
47710
+ config = { columns: [] };
47711
+ readonly = false;
47712
+ configChange = new EventEmitter();
47713
+ workingConfig = { columns: [] };
47714
+ toolbarTitle = '';
47715
+ toolbarSubtitle = '';
47716
+ toolbarVisible = true;
47717
+ density = 'comfortable';
47718
+ toolbarActionsPosition = 'top';
47719
+ toolbarHeight = 64;
47720
+ toolbarActionsBackgroundColor = '';
47721
+ paginationEnabled = true;
47722
+ pageSize = 10;
47723
+ pageSizeOptionsText = '5, 10, 25, 50';
47724
+ showFirstLastButtons = true;
47725
+ paginationStrategy = 'client';
47726
+ paginationPosition = 'bottom';
47727
+ paginationStyle = 'default';
47728
+ sortingEnabled = true;
47729
+ sortingStrategy = 'client';
47730
+ allowClearSort = true;
47731
+ defaultSortField = '';
47732
+ defaultSortDirection = 'asc';
47733
+ loadingMessage = '';
47734
+ emptyMessage = '';
47735
+ noResultsMessage = '';
47736
+ errorMessage = '';
47737
+ loadingMoreMessage = '';
47738
+ lastInputSignature = '';
47739
+ constructor(i18n) {
47740
+ this.i18n = i18n;
47741
+ }
47742
+ ngOnChanges() {
47743
+ const signature = JSON.stringify(this.config || { columns: [] });
47744
+ if (signature === this.lastInputSignature) {
47745
+ return;
47746
+ }
47747
+ this.lastInputSignature = signature;
47748
+ this.workingConfig = cloneTableConfig(this.config);
47749
+ this.toolbarTitle = this.workingConfig.toolbar?.title || '';
47750
+ this.toolbarSubtitle = this.workingConfig.toolbar?.subtitle || '';
47751
+ this.toolbarVisible = this.workingConfig.toolbar?.visible !== false;
47752
+ this.density = this.workingConfig.appearance?.density || 'comfortable';
47753
+ this.toolbarActionsPosition =
47754
+ this.workingConfig.toolbar?.actionsPosition
47755
+ || this.workingConfig.toolbar?.position
47756
+ || 'top';
47757
+ this.toolbarHeight = this.workingConfig.toolbar?.layout?.height || 64;
47758
+ this.toolbarActionsBackgroundColor = this.workingConfig.toolbar?.actionsBackgroundColor || '';
47759
+ this.paginationEnabled = this.workingConfig.behavior?.pagination?.enabled !== false;
47760
+ this.pageSize = this.workingConfig.behavior?.pagination?.pageSize || 10;
47761
+ this.pageSizeOptionsText = this.arrayToString(this.workingConfig.behavior?.pagination?.pageSizeOptions || [5, 10, 25, 50]);
47762
+ this.showFirstLastButtons =
47763
+ this.workingConfig.behavior?.pagination?.showFirstLastButtons !== false;
47764
+ this.paginationStrategy = this.workingConfig.behavior?.pagination?.strategy || 'client';
47765
+ this.paginationPosition = this.workingConfig.behavior?.pagination?.position || 'bottom';
47766
+ this.paginationStyle = this.workingConfig.behavior?.pagination?.style || 'default';
47767
+ this.sortingEnabled = this.workingConfig.behavior?.sorting?.enabled !== false;
47768
+ this.sortingStrategy = this.workingConfig.behavior?.sorting?.strategy || 'client';
47769
+ this.allowClearSort = this.workingConfig.behavior?.sorting?.allowClearSort !== false;
47770
+ this.defaultSortField = this.getDefaultSortField(this.workingConfig);
47771
+ this.defaultSortDirection = this.getDefaultSortDirection(this.workingConfig);
47772
+ this.loadingMessage =
47773
+ this.workingConfig.messages?.states?.loading
47774
+ || this.tx('messages.loading.placeholder', 'Loading...');
47775
+ this.emptyMessage =
47776
+ this.workingConfig.messages?.states?.empty
47777
+ || this.tx('messages.empty.placeholder', 'No data available');
47778
+ this.noResultsMessage =
47779
+ this.workingConfig.messages?.states?.noResults
47780
+ || this.tx('messages.noResults.placeholder', 'No results found');
47781
+ this.errorMessage =
47782
+ this.workingConfig.messages?.states?.error
47783
+ || this.tx('messages.error.placeholder', 'Error loading data');
47784
+ this.loadingMoreMessage =
47785
+ this.workingConfig.messages?.states?.loadingMore
47786
+ || this.tx('messages.loadingMore.placeholder', 'Loading more data...');
47787
+ }
47788
+ setToolbarTitle(value) {
47789
+ this.toolbarTitle = value || '';
47790
+ this.patchToolbar({ title: this.toolbarTitle || undefined });
47791
+ }
47792
+ setToolbarSubtitle(value) {
47793
+ this.toolbarSubtitle = value || '';
47794
+ this.patchToolbar({ subtitle: this.toolbarSubtitle || undefined });
47795
+ }
47796
+ setToolbarVisible(value) {
47797
+ this.toolbarVisible = !!value;
47798
+ this.patchToolbar({ visible: this.toolbarVisible });
47799
+ }
47800
+ setDensity(value) {
47801
+ this.density = value || 'comfortable';
47802
+ this.workingConfig = {
47803
+ ...this.workingConfig,
47804
+ appearance: {
47805
+ ...(this.workingConfig.appearance || {}),
47806
+ density: this.density,
47807
+ },
47808
+ };
47809
+ this.emitConfig();
47810
+ }
47811
+ setToolbarActionsPosition(value) {
47812
+ this.toolbarActionsPosition = value || 'top';
47813
+ this.patchToolbar({
47814
+ actionsPosition: this.toolbarActionsPosition,
47815
+ position: this.toolbarActionsPosition,
47816
+ });
47817
+ }
47818
+ setToolbarHeight(value) {
47819
+ const nextHeight = clampNumber(value, 40, 120, 64);
47820
+ this.toolbarHeight = nextHeight;
47821
+ this.patchToolbar({
47822
+ layout: {
47823
+ alignment: this.workingConfig.toolbar?.layout?.alignment || 'space-between',
47824
+ showSeparator: this.workingConfig.toolbar?.layout?.showSeparator ?? false,
47825
+ ...(this.workingConfig.toolbar?.layout || {}),
47826
+ height: nextHeight,
47827
+ },
47828
+ });
47829
+ }
47830
+ setToolbarActionsBackgroundColor(value) {
47831
+ this.toolbarActionsBackgroundColor = value || '';
47832
+ this.patchToolbar({
47833
+ actionsBackgroundColor: this.toolbarActionsBackgroundColor || undefined,
47834
+ });
47835
+ }
47836
+ setPaginationEnabled(value) {
47837
+ this.paginationEnabled = !!value;
47838
+ this.patchPagination();
47839
+ }
47840
+ setPaginationPageSize(value) {
47841
+ this.pageSize = clampNumber(value, 1, 500, 10);
47842
+ this.patchPagination();
47843
+ }
47844
+ setPaginationPageSizeOptions(value) {
47845
+ this.pageSizeOptionsText = value || '';
47846
+ this.patchPagination();
47847
+ }
47848
+ setPaginationShowFirstLastButtons(value) {
47849
+ this.showFirstLastButtons = !!value;
47850
+ this.patchPagination();
47851
+ }
47852
+ setPaginationStrategy(value) {
47853
+ this.paginationStrategy = value || 'client';
47854
+ this.patchPagination();
47855
+ }
47856
+ setPaginationPosition(value) {
47857
+ this.paginationPosition = value || 'bottom';
47858
+ this.patchPagination();
47859
+ }
47860
+ setPaginationStyle(value) {
47861
+ this.paginationStyle = value || 'default';
47862
+ this.patchPagination();
47863
+ }
47864
+ setSortingEnabled(value) {
47865
+ this.sortingEnabled = !!value;
47866
+ this.patchSorting();
47867
+ }
47868
+ setSortingStrategy(value) {
47869
+ this.sortingStrategy = value || 'client';
47870
+ this.patchSorting();
47871
+ }
47872
+ setSortingAllowClear(value) {
47873
+ this.allowClearSort = !!value;
47874
+ this.patchSorting();
47875
+ }
47876
+ setSortingDefaultField(value) {
47877
+ this.defaultSortField = value || '';
47878
+ this.patchSorting();
47879
+ }
47880
+ setSortingDefaultDirection(value) {
47881
+ this.defaultSortDirection = value || 'asc';
47882
+ this.patchSorting();
47883
+ }
47884
+ setStateMessage(key, value) {
47885
+ const nextValue = value || '';
47886
+ if (key === 'loading') {
47887
+ this.loadingMessage = nextValue;
47888
+ }
47889
+ else if (key === 'empty') {
47890
+ this.emptyMessage = nextValue;
47891
+ }
47892
+ else if (key === 'noResults') {
47893
+ this.noResultsMessage = nextValue;
47894
+ }
47895
+ else {
47896
+ this.errorMessage = nextValue;
47897
+ }
47898
+ this.patchMessages({
47899
+ states: {
47900
+ ...(this.workingConfig.messages?.states || {}),
47901
+ loading: this.loadingMessage,
47902
+ empty: this.emptyMessage,
47903
+ noResults: this.noResultsMessage,
47904
+ error: this.errorMessage,
47905
+ loadingMore: this.loadingMoreMessage,
47906
+ },
47907
+ });
47908
+ }
47909
+ onColumnsConfigChange(config) {
47910
+ this.workingConfig = cloneTableConfig(config);
47911
+ this.toolbarTitle = this.workingConfig.toolbar?.title || '';
47912
+ this.toolbarSubtitle = this.workingConfig.toolbar?.subtitle || '';
47913
+ this.toolbarVisible = this.workingConfig.toolbar?.visible !== false;
47914
+ this.density = this.workingConfig.appearance?.density || 'comfortable';
47915
+ this.toolbarActionsPosition =
47916
+ this.workingConfig.toolbar?.actionsPosition
47917
+ || this.workingConfig.toolbar?.position
47918
+ || 'top';
47919
+ this.toolbarHeight = this.workingConfig.toolbar?.layout?.height || 64;
47920
+ this.toolbarActionsBackgroundColor = this.workingConfig.toolbar?.actionsBackgroundColor || '';
47921
+ this.paginationEnabled = this.workingConfig.behavior?.pagination?.enabled !== false;
47922
+ this.pageSize = this.workingConfig.behavior?.pagination?.pageSize || 10;
47923
+ this.pageSizeOptionsText = this.arrayToString(this.workingConfig.behavior?.pagination?.pageSizeOptions || [5, 10, 25, 50]);
47924
+ this.showFirstLastButtons =
47925
+ this.workingConfig.behavior?.pagination?.showFirstLastButtons !== false;
47926
+ this.paginationStrategy = this.workingConfig.behavior?.pagination?.strategy || 'client';
47927
+ this.paginationPosition = this.workingConfig.behavior?.pagination?.position || 'bottom';
47928
+ this.paginationStyle = this.workingConfig.behavior?.pagination?.style || 'default';
47929
+ this.sortingEnabled = this.workingConfig.behavior?.sorting?.enabled !== false;
47930
+ this.sortingStrategy = this.workingConfig.behavior?.sorting?.strategy || 'client';
47931
+ this.allowClearSort = this.workingConfig.behavior?.sorting?.allowClearSort !== false;
47932
+ this.defaultSortField = this.getDefaultSortField(this.workingConfig);
47933
+ this.defaultSortDirection = this.getDefaultSortDirection(this.workingConfig);
47934
+ this.loadingMessage =
47935
+ this.workingConfig.messages?.states?.loading
47936
+ || this.tx('messages.loading.placeholder', 'Loading...');
47937
+ this.emptyMessage =
47938
+ this.workingConfig.messages?.states?.empty
47939
+ || this.tx('messages.empty.placeholder', 'No data available');
47940
+ this.noResultsMessage =
47941
+ this.workingConfig.messages?.states?.noResults
47942
+ || this.tx('messages.noResults.placeholder', 'No results found');
47943
+ this.errorMessage =
47944
+ this.workingConfig.messages?.states?.error
47945
+ || this.tx('messages.error.placeholder', 'Error loading data');
47946
+ this.loadingMoreMessage =
47947
+ this.workingConfig.messages?.states?.loadingMore
47948
+ || this.tx('messages.loadingMore.placeholder', 'Loading more data...');
47949
+ this.emitConfig();
47950
+ }
47951
+ columnCountLabel() {
47952
+ const count = this.workingConfig.columns?.length || 0;
47953
+ return this.tx('inlineAuthoring.columnCount', '{count} columns').replace('{count}', String(count));
47954
+ }
47955
+ toolbarSummary() {
47956
+ if (!this.toolbarVisible) {
47957
+ return this.tx('inlineAuthoring.summary.toolbarHidden', 'Hidden');
47958
+ }
47959
+ return this.toolbarTitle || this.tx('inlineAuthoring.summary.toolbarDefault', 'Visible without custom title');
47960
+ }
47961
+ densitySummary() {
47962
+ return {
47963
+ compact: this.tx('behavior.appearance.density.compact', 'Compact'),
47964
+ comfortable: this.tx('behavior.appearance.density.comfortable', 'Comfortable'),
47965
+ spacious: this.tx('behavior.appearance.density.spacious', 'Spacious'),
47966
+ }[this.density];
47967
+ }
47968
+ actionsPositionSummary() {
47969
+ return {
47970
+ top: this.tx('toolbar.actionsPosition.top', 'Above the table'),
47971
+ bottom: this.tx('toolbar.actionsPosition.bottom', 'Below the table'),
47972
+ both: this.tx('toolbar.actionsPosition.both', 'Top and bottom'),
47973
+ }[this.toolbarActionsPosition];
47974
+ }
47975
+ behaviorSummary() {
47976
+ const parts = [];
47977
+ if (this.paginationEnabled) {
47978
+ parts.push(`${this.tx('behavior.pagination.pageSize', 'Items per page')}: ${this.pageSize}`);
47979
+ }
47980
+ else {
47981
+ parts.push(this.tx('inlineAuthoring.summary.paginationOff', 'Pagination off'));
47982
+ }
47983
+ if (this.sortingEnabled) {
47984
+ parts.push(this.sortingStrategy === 'server'
47985
+ ? this.tx('behavior.sorting.strategy.server', 'Server (remote sorting)')
47986
+ : this.tx('behavior.sorting.strategy.client', 'Client (local sorting)'));
47987
+ if (this.defaultSortField) {
47988
+ parts.push(`${this.defaultSortField} ${this.defaultSortDirection}`);
47989
+ }
47990
+ }
47991
+ else {
47992
+ parts.push(this.tx('inlineAuthoring.summary.sortingOff', 'Sorting off'));
47993
+ }
47994
+ return parts.join(' • ');
47995
+ }
47996
+ statesSummary() {
47997
+ const customized = [
47998
+ this.loadingMessage !== this.tx('messages.loading.placeholder', 'Loading...'),
47999
+ this.emptyMessage !== this.tx('messages.empty.placeholder', 'No data available'),
48000
+ this.noResultsMessage !== this.tx('messages.noResults.placeholder', 'No results found'),
48001
+ this.errorMessage !== this.tx('messages.error.placeholder', 'Error loading data'),
48002
+ ].filter(Boolean).length;
48003
+ if (!customized) {
48004
+ return this.tx('inlineAuthoring.summary.statesDefault', 'Default copy');
48005
+ }
48006
+ return this.tx('inlineAuthoring.summary.statesCustom', '{count} customized states')
48007
+ .replace('{count}', String(customized));
48008
+ }
48009
+ columnsPreviewSummary() {
48010
+ const columns = this.workingConfig.columns || [];
48011
+ if (!columns.length) {
48012
+ return this.tx('inlineAuthoring.summary.columnsEmpty', 'No columns configured yet');
48013
+ }
48014
+ const preview = columns
48015
+ .slice(0, 3)
48016
+ .map((column) => column.header || column.field || '?')
48017
+ .join(', ');
48018
+ if (columns.length <= 3) {
48019
+ return preview;
48020
+ }
48021
+ return `${preview} +${columns.length - 3}`;
48022
+ }
48023
+ serializedConfig() {
48024
+ return JSON.stringify(this.workingConfig || { columns: [] }, null, 2);
48025
+ }
48026
+ tx(key, fallback) {
48027
+ return this.i18n.t(key, undefined, fallback, PRAXIS_TABLE_EDITOR_I18N_NAMESPACE);
48028
+ }
48029
+ emitConfig() {
48030
+ const next = cloneTableConfig(this.workingConfig);
48031
+ this.lastInputSignature = JSON.stringify(next);
48032
+ this.configChange.emit(next);
48033
+ }
48034
+ patchToolbar(patch) {
48035
+ this.workingConfig = {
48036
+ ...this.workingConfig,
48037
+ toolbar: {
48038
+ visible: this.toolbarVisible,
48039
+ ...(this.workingConfig.toolbar || {}),
48040
+ ...patch,
48041
+ },
48042
+ };
48043
+ this.emitConfig();
48044
+ }
48045
+ patchBehavior(patch) {
48046
+ this.workingConfig = {
48047
+ ...this.workingConfig,
48048
+ behavior: {
48049
+ ...(this.workingConfig.behavior || {}),
48050
+ ...patch,
48051
+ },
48052
+ };
48053
+ this.emitConfig();
48054
+ }
48055
+ patchPagination() {
48056
+ this.patchBehavior({
48057
+ pagination: {
48058
+ ...(this.workingConfig.behavior?.pagination || {}),
48059
+ enabled: this.paginationEnabled,
48060
+ pageSize: this.pageSize,
48061
+ pageSizeOptions: this.parseNumberList(this.pageSizeOptionsText, [5, 10, 25, 50]),
48062
+ showFirstLastButtons: this.showFirstLastButtons,
48063
+ showPageNumbers: this.workingConfig.behavior?.pagination?.showPageNumbers ?? true,
48064
+ showPageInfo: this.workingConfig.behavior?.pagination?.showPageInfo ?? true,
48065
+ position: this.paginationPosition,
48066
+ style: this.paginationStyle,
48067
+ strategy: this.paginationStrategy,
48068
+ },
48069
+ });
48070
+ }
48071
+ patchSorting() {
48072
+ this.patchBehavior({
48073
+ sorting: {
48074
+ ...(this.workingConfig.behavior?.sorting || {}),
48075
+ enabled: this.sortingEnabled,
48076
+ strategy: this.sortingStrategy,
48077
+ allowClearSort: this.allowClearSort,
48078
+ multiSort: this.workingConfig.behavior?.sorting?.multiSort ?? false,
48079
+ showSortIndicators: this.workingConfig.behavior?.sorting?.showSortIndicators ?? true,
48080
+ indicatorPosition: this.workingConfig.behavior?.sorting?.indicatorPosition || 'end',
48081
+ defaultSort: this.buildDefaultSort(),
48082
+ },
48083
+ });
48084
+ }
48085
+ patchMessages(patch) {
48086
+ this.workingConfig = {
48087
+ ...this.workingConfig,
48088
+ messages: {
48089
+ ...(this.workingConfig.messages || {}),
48090
+ ...patch,
48091
+ },
48092
+ };
48093
+ this.emitConfig();
48094
+ }
48095
+ arrayToString(values) {
48096
+ return (values || []).join(', ');
48097
+ }
48098
+ parseNumberList(value, fallback) {
48099
+ const parsed = (value || '')
48100
+ .split(',')
48101
+ .map((item) => Number(item.trim()))
48102
+ .filter((item) => Number.isFinite(item) && item > 0);
48103
+ return parsed.length ? parsed : fallback;
48104
+ }
48105
+ buildDefaultSort() {
48106
+ if (!this.defaultSortField) {
48107
+ return undefined;
48108
+ }
48109
+ return {
48110
+ column: this.defaultSortField,
48111
+ direction: this.defaultSortDirection,
48112
+ };
48113
+ }
48114
+ getDefaultSortField(config) {
48115
+ const defaultSort = config.behavior?.sorting?.defaultSort;
48116
+ if (Array.isArray(defaultSort)) {
48117
+ return defaultSort[0]?.column || '';
48118
+ }
48119
+ return defaultSort?.column || '';
48120
+ }
48121
+ getDefaultSortDirection(config) {
48122
+ const defaultSort = config.behavior?.sorting?.defaultSort;
48123
+ const direction = Array.isArray(defaultSort)
48124
+ ? defaultSort[0]?.direction
48125
+ : defaultSort?.direction;
48126
+ return direction === 'desc' ? 'desc' : 'asc';
48127
+ }
48128
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisTableInlineAuthoringEditorComponent, deps: [{ token: i1.PraxisI18nService }], target: i0.ɵɵFactoryTarget.Component });
48129
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: PraxisTableInlineAuthoringEditorComponent, isStandalone: true, selector: "praxis-table-inline-authoring-editor", inputs: { config: "config", readonly: "readonly" }, outputs: { configChange: "configChange" }, providers: [providePraxisI18nConfig(PRAXIS_TABLE_EDITOR_I18N_CONFIG)], usesOnChanges: true, ngImport: i0, template: `
48130
+ <section class="inline-authoring" data-testid="table-inline-authoring-editor">
48131
+ <header class="inline-authoring__header">
48132
+ <div>
48133
+ <h4>{{ tx('inlineAuthoring.title', 'Table structure') }}</h4>
48134
+ <p>{{ tx('inlineAuthoring.description', 'Edit the canonical TableConfig used by this CRUD surface.') }}</p>
48135
+ </div>
48136
+ <span class="inline-authoring__chip" data-testid="table-inline-editor-column-count">
48137
+ {{ columnCountLabel() }}
48138
+ </span>
48139
+ </header>
48140
+
48141
+ <mat-card class="inline-authoring__card">
48142
+ <section class="inline-authoring__summary" data-testid="table-inline-editor-summary">
48143
+ <div class="inline-authoring__summary-card">
48144
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.summary.toolbar', 'Toolbar') }}</span>
48145
+ <strong data-testid="table-inline-editor-summary-toolbar">{{ toolbarSummary() }}</strong>
48146
+ </div>
48147
+ <div class="inline-authoring__summary-card">
48148
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.summary.density', 'Density') }}</span>
48149
+ <strong data-testid="table-inline-editor-summary-density">{{ densitySummary() }}</strong>
48150
+ </div>
48151
+ <div class="inline-authoring__summary-card">
48152
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.summary.actions', 'Actions') }}</span>
48153
+ <strong data-testid="table-inline-editor-summary-actions">{{ actionsPositionSummary() }}</strong>
48154
+ </div>
48155
+ <div class="inline-authoring__summary-card">
48156
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.summary.behavior', 'List behavior') }}</span>
48157
+ <strong data-testid="table-inline-editor-summary-behavior">{{ behaviorSummary() }}</strong>
48158
+ </div>
48159
+ <div class="inline-authoring__summary-card">
48160
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.summary.states', 'State messages') }}</span>
48161
+ <strong data-testid="table-inline-editor-summary-states">{{ statesSummary() }}</strong>
48162
+ </div>
48163
+ <div class="inline-authoring__summary-card inline-authoring__summary-card--wide">
48164
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.summary.columns', 'Visible structure') }}</span>
48165
+ <strong data-testid="table-inline-editor-summary-columns">{{ columnsPreviewSummary() }}</strong>
48166
+ </div>
48167
+ </section>
48168
+
48169
+ <section class="inline-authoring__essentials">
48170
+ <div class="inline-authoring__section-copy">
48171
+ <h5>{{ tx('inlineAuthoring.essentialsTitle', 'Table essentials') }}</h5>
48172
+ <p>{{ tx('inlineAuthoring.essentialsDescription', 'Define the toolbar presence and the texts users see first.') }}</p>
48173
+ </div>
48174
+
48175
+ <div class="inline-authoring__grid">
48176
+ <mat-slide-toggle
48177
+ data-testid="table-inline-editor-toolbar-visible"
48178
+ [ngModel]="toolbarVisible"
48179
+ (ngModelChange)="setToolbarVisible($event)"
48180
+ [disabled]="readonly"
48181
+ >
48182
+ {{ tx('toolbar.visible', 'Show toolbar') }}
48183
+ </mat-slide-toggle>
48184
+
48185
+ <mat-form-field appearance="outline">
48186
+ <mat-label>{{ tx('toolbar.title.label', 'Toolbar title') }}</mat-label>
48187
+ <input
48188
+ matInput
48189
+ data-testid="table-inline-editor-toolbar-title"
48190
+ [ngModel]="toolbarTitle"
48191
+ (ngModelChange)="setToolbarTitle($event)"
48192
+ [disabled]="readonly"
48193
+ />
48194
+ </mat-form-field>
48195
+
48196
+ <mat-form-field appearance="outline">
48197
+ <mat-label>{{ tx('toolbar.subtitle.label', 'Subtitle') }}</mat-label>
48198
+ <input
48199
+ matInput
48200
+ data-testid="table-inline-editor-toolbar-subtitle"
48201
+ [ngModel]="toolbarSubtitle"
48202
+ (ngModelChange)="setToolbarSubtitle($event)"
48203
+ [disabled]="readonly"
48204
+ />
48205
+ </mat-form-field>
48206
+
48207
+ <mat-form-field appearance="outline">
48208
+ <mat-label>{{ tx('behavior.appearance.density', 'Density') }}</mat-label>
48209
+ <mat-select
48210
+ data-testid="table-inline-editor-density"
48211
+ [ngModel]="density"
48212
+ (ngModelChange)="setDensity($event)"
48213
+ [disabled]="readonly"
48214
+ >
48215
+ <mat-option value="compact">{{ tx('behavior.appearance.density.compact', 'Compact') }}</mat-option>
48216
+ <mat-option value="comfortable">{{ tx('behavior.appearance.density.comfortable', 'Comfortable') }}</mat-option>
48217
+ <mat-option value="spacious">{{ tx('behavior.appearance.density.spacious', 'Spacious') }}</mat-option>
48218
+ </mat-select>
48219
+ </mat-form-field>
48220
+
48221
+ <mat-form-field appearance="outline">
48222
+ <mat-label>{{ tx('toolbar.actionsPosition.label', 'Actions position') }}</mat-label>
48223
+ <mat-select
48224
+ data-testid="table-inline-editor-toolbar-actions-position"
48225
+ [ngModel]="toolbarActionsPosition"
48226
+ (ngModelChange)="setToolbarActionsPosition($event)"
48227
+ [disabled]="readonly"
48228
+ >
48229
+ <mat-option value="top">{{ tx('toolbar.actionsPosition.top', 'Above the table') }}</mat-option>
48230
+ <mat-option value="bottom">{{ tx('toolbar.actionsPosition.bottom', 'Below the table') }}</mat-option>
48231
+ <mat-option value="both">{{ tx('toolbar.actionsPosition.both', 'Top and bottom') }}</mat-option>
48232
+ </mat-select>
48233
+ </mat-form-field>
48234
+
48235
+ <mat-form-field appearance="outline">
48236
+ <mat-label>{{ tx('toolbar.height.label', 'Toolbar height (px)') }}</mat-label>
48237
+ <input
48238
+ matInput
48239
+ type="number"
48240
+ data-testid="table-inline-editor-toolbar-height"
48241
+ [ngModel]="toolbarHeight"
48242
+ (ngModelChange)="setToolbarHeight($event)"
48243
+ [disabled]="readonly"
48244
+ min="40"
48245
+ max="120"
48246
+ />
48247
+ </mat-form-field>
48248
+
48249
+ <mat-form-field appearance="outline">
48250
+ <mat-label>{{ tx('toolbar.backgroundPreset.label', 'Actions background color') }}</mat-label>
48251
+ <mat-select
48252
+ data-testid="table-inline-editor-toolbar-background-preset"
48253
+ [ngModel]="toolbarActionsBackgroundColor"
48254
+ (ngModelChange)="setToolbarActionsBackgroundColor($event)"
48255
+ [disabled]="readonly"
48256
+ >
48257
+ <mat-option value="">{{ tx('toolbar.backgroundPreset.default', 'Theme default') }}</mat-option>
48258
+ <mat-option value="var(--p-table-header-bg)">{{ tx('toolbar.backgroundPreset.header', 'Table header') }}</mat-option>
48259
+ <mat-option value="var(--md-sys-color-surface)">{{ tx('toolbar.backgroundPreset.surface', 'Surface') }}</mat-option>
48260
+ <mat-option value="var(--md-sys-color-surface-container)">{{ tx('toolbar.backgroundPreset.surfaceContainer', 'Surface container') }}</mat-option>
48261
+ <mat-option value="var(--md-sys-color-surface-container-high)">{{ tx('toolbar.backgroundPreset.surfaceContainerHigh', 'Surface container high') }}</mat-option>
48262
+ <mat-option value="var(--md-sys-color-surface-container-highest)">{{ tx('toolbar.backgroundPreset.surfaceContainerHighest', 'Surface container highest') }}</mat-option>
48263
+ </mat-select>
48264
+ </mat-form-field>
48265
+ </div>
48266
+ </section>
48267
+
48268
+ <section class="inline-authoring__advanced" data-testid="table-inline-editor-advanced-intro">
48269
+ <div class="inline-authoring__section-copy">
48270
+ <h5>{{ tx('inlineAuthoring.advancedTitle', 'Advanced table details') }}</h5>
48271
+ <p>{{ tx('inlineAuthoring.advancedDescription', 'Open the next panels only when you need to tune list behavior, state copy, or visible columns.') }}</p>
48272
+ </div>
48273
+
48274
+ <section class="inline-authoring__advanced-summary">
48275
+ <div class="inline-authoring__advanced-card">
48276
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.advanced.behavior', 'Behavior') }}</span>
48277
+ <strong data-testid="table-inline-editor-advanced-summary-behavior">{{ behaviorSummary() }}</strong>
48278
+ </div>
48279
+ <div class="inline-authoring__advanced-card">
48280
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.advanced.states', 'State copy') }}</span>
48281
+ <strong data-testid="table-inline-editor-advanced-summary-states">{{ statesSummary() }}</strong>
48282
+ </div>
48283
+ <div class="inline-authoring__advanced-card">
48284
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.advanced.columns', 'Columns') }}</span>
48285
+ <strong data-testid="table-inline-editor-advanced-summary-columns">{{ columnsPreviewSummary() }}</strong>
48286
+ </div>
48287
+ </section>
48288
+ </section>
48289
+
48290
+ @if (readonly) {
48291
+ <p class="inline-authoring__readonly" data-testid="table-inline-editor-readonly-hint">
48292
+ {{ tx('inlineAuthoring.readonlyHint', 'This authoring surface is read-only in the current context.') }}
48293
+ </p>
48294
+ <pre data-testid="table-inline-editor-json">{{ serializedConfig() }}</pre>
48295
+ } @else {
48296
+ <section class="inline-authoring__advanced-panels" data-testid="table-inline-editor-advanced-panels">
48297
+ <mat-expansion-panel class="inline-authoring__columns-panel" [expanded]="false" data-testid="table-inline-editor-behavior-panel">
48298
+ <mat-expansion-panel-header>
48299
+ <mat-panel-title>{{ tx('inlineAuthoring.behaviorTitle', 'List behavior') }}</mat-panel-title>
48300
+ <mat-panel-description>{{ behaviorSummary() }}</mat-panel-description>
48301
+ </mat-expansion-panel-header>
48302
+ <p class="inline-authoring__panel-description">
48303
+ {{ tx('inlineAuthoring.behaviorDescription', 'Control pagination and sorting with the same runtime-backed semantics used by the full table editor.') }}
48304
+ </p>
48305
+ <div class="inline-authoring__grid">
48306
+ <mat-slide-toggle
48307
+ data-testid="table-inline-editor-pagination-enabled"
48308
+ [ngModel]="paginationEnabled"
48309
+ (ngModelChange)="setPaginationEnabled($event)"
48310
+ [disabled]="readonly"
48311
+ >
48312
+ {{ tx('behavior.pagination.enabled', 'Enable pagination') }}
48313
+ </mat-slide-toggle>
48314
+
48315
+ <mat-form-field appearance="outline">
48316
+ <mat-label>{{ tx('behavior.pagination.pageSize', 'Items per page') }}</mat-label>
48317
+ <input
48318
+ matInput
48319
+ type="number"
48320
+ data-testid="table-inline-editor-pagination-page-size"
48321
+ [ngModel]="pageSize"
48322
+ (ngModelChange)="setPaginationPageSize($event)"
48323
+ [disabled]="readonly || !paginationEnabled"
48324
+ min="1"
48325
+ />
48326
+ </mat-form-field>
48327
+
48328
+ <mat-form-field appearance="outline">
48329
+ <mat-label>{{ tx('behavior.pagination.pageSizeOptions', 'Page size options') }}</mat-label>
48330
+ <input
48331
+ matInput
48332
+ data-testid="table-inline-editor-pagination-page-size-options"
48333
+ [ngModel]="pageSizeOptionsText"
48334
+ (ngModelChange)="setPaginationPageSizeOptions($event)"
48335
+ [disabled]="readonly || !paginationEnabled"
48336
+ placeholder="5, 10, 25, 50"
48337
+ />
48338
+ </mat-form-field>
48339
+
48340
+ <mat-slide-toggle
48341
+ data-testid="table-inline-editor-pagination-first-last"
48342
+ [ngModel]="showFirstLastButtons"
48343
+ (ngModelChange)="setPaginationShowFirstLastButtons($event)"
48344
+ [disabled]="readonly || !paginationEnabled"
48345
+ >
48346
+ {{ tx('behavior.pagination.firstLast', 'Show first/last page buttons') }}
48347
+ </mat-slide-toggle>
48348
+
48349
+ <mat-form-field appearance="outline">
48350
+ <mat-label>{{ tx('behavior.pagination.strategy', 'Pagination strategy') }}</mat-label>
48351
+ <mat-select
48352
+ data-testid="table-inline-editor-pagination-strategy"
48353
+ [ngModel]="paginationStrategy"
48354
+ (ngModelChange)="setPaginationStrategy($event)"
48355
+ [disabled]="readonly || !paginationEnabled"
48356
+ >
48357
+ <mat-option value="client">{{ tx('behavior.pagination.strategy.client', 'Client (local data)') }}</mat-option>
48358
+ <mat-option value="server">{{ tx('behavior.pagination.strategy.server', 'Server (remote pagination)') }}</mat-option>
48359
+ </mat-select>
48360
+ </mat-form-field>
48361
+
48362
+ <mat-form-field appearance="outline">
48363
+ <mat-label>{{ tx('behavior.pagination.position', 'Paginator position') }}</mat-label>
48364
+ <mat-select
48365
+ data-testid="table-inline-editor-pagination-position"
48366
+ [ngModel]="paginationPosition"
48367
+ (ngModelChange)="setPaginationPosition($event)"
48368
+ [disabled]="readonly || !paginationEnabled"
48369
+ >
48370
+ <mat-option value="top">{{ tx('behavior.pagination.position.top', 'Top') }}</mat-option>
48371
+ <mat-option value="bottom">{{ tx('behavior.pagination.position.bottom', 'Bottom') }}</mat-option>
48372
+ <mat-option value="both">{{ tx('behavior.pagination.position.both', 'Both') }}</mat-option>
48373
+ </mat-select>
48374
+ </mat-form-field>
48375
+
48376
+ <mat-form-field appearance="outline">
48377
+ <mat-label>{{ tx('behavior.pagination.style', 'Paginator style') }}</mat-label>
48378
+ <mat-select
48379
+ data-testid="table-inline-editor-pagination-style"
48380
+ [ngModel]="paginationStyle"
48381
+ (ngModelChange)="setPaginationStyle($event)"
48382
+ [disabled]="readonly || !paginationEnabled"
48383
+ >
48384
+ <mat-option value="default">{{ tx('behavior.pagination.style.default', 'Default') }}</mat-option>
48385
+ <mat-option value="simple">{{ tx('behavior.pagination.style.simple', 'Simple') }}</mat-option>
48386
+ <mat-option value="compact">{{ tx('behavior.pagination.style.compact', 'Compact') }}</mat-option>
48387
+ <mat-option value="advanced">{{ tx('behavior.pagination.style.advanced', 'Advanced') }}</mat-option>
48388
+ </mat-select>
48389
+ </mat-form-field>
48390
+
48391
+ <mat-slide-toggle
48392
+ data-testid="table-inline-editor-sorting-enabled"
48393
+ [ngModel]="sortingEnabled"
48394
+ (ngModelChange)="setSortingEnabled($event)"
48395
+ [disabled]="readonly"
48396
+ >
48397
+ {{ tx('behavior.sorting.enabled', 'Enable sorting') }}
48398
+ </mat-slide-toggle>
48399
+
48400
+ <mat-form-field appearance="outline">
48401
+ <mat-label>{{ tx('behavior.sorting.strategy', 'Sorting strategy') }}</mat-label>
48402
+ <mat-select
48403
+ data-testid="table-inline-editor-sorting-strategy"
48404
+ [ngModel]="sortingStrategy"
48405
+ (ngModelChange)="setSortingStrategy($event)"
48406
+ [disabled]="readonly || !sortingEnabled"
48407
+ >
48408
+ <mat-option value="client">{{ tx('behavior.sorting.strategy.client', 'Client (local sorting)') }}</mat-option>
48409
+ <mat-option value="server">{{ tx('behavior.sorting.strategy.server', 'Server (remote sorting)') }}</mat-option>
48410
+ </mat-select>
48411
+ </mat-form-field>
48412
+
48413
+ <mat-slide-toggle
48414
+ data-testid="table-inline-editor-sorting-allow-clear"
48415
+ [ngModel]="allowClearSort"
48416
+ (ngModelChange)="setSortingAllowClear($event)"
48417
+ [disabled]="readonly || !sortingEnabled"
48418
+ >
48419
+ {{ tx('behavior.sorting.allowClear', 'Allow clearing sort') }}
48420
+ </mat-slide-toggle>
48421
+
48422
+ <mat-form-field appearance="outline">
48423
+ <mat-label>{{ tx('behavior.sorting.defaultField', 'Initial sort - column') }}</mat-label>
48424
+ <mat-select
48425
+ data-testid="table-inline-editor-sorting-default-field"
48426
+ [ngModel]="defaultSortField"
48427
+ (ngModelChange)="setSortingDefaultField($event)"
48428
+ [disabled]="readonly || !sortingEnabled"
48429
+ >
48430
+ <mat-option value="">{{ tx('inlineAuthoring.noneOption', 'None') }}</mat-option>
48431
+ @for (column of workingConfig.columns || []; track column.field) {
48432
+ <mat-option [value]="column.field">{{ column.header || column.field }}</mat-option>
48433
+ }
48434
+ </mat-select>
48435
+ </mat-form-field>
48436
+
48437
+ <mat-form-field appearance="outline">
48438
+ <mat-label>{{ tx('behavior.sorting.direction', 'Direction') }}</mat-label>
48439
+ <mat-select
48440
+ data-testid="table-inline-editor-sorting-default-direction"
48441
+ [ngModel]="defaultSortDirection"
48442
+ (ngModelChange)="setSortingDefaultDirection($event)"
48443
+ [disabled]="readonly || !sortingEnabled || !defaultSortField"
48444
+ >
48445
+ <mat-option value="asc">{{ tx('behavior.sorting.direction.asc', 'Ascending') }}</mat-option>
48446
+ <mat-option value="desc">{{ tx('behavior.sorting.direction.desc', 'Descending') }}</mat-option>
48447
+ </mat-select>
48448
+ </mat-form-field>
48449
+ </div>
48450
+ </mat-expansion-panel>
48451
+
48452
+ <mat-expansion-panel class="inline-authoring__columns-panel" [expanded]="false" data-testid="table-inline-editor-states-panel">
48453
+ <mat-expansion-panel-header>
48454
+ <mat-panel-title>{{ tx('inlineAuthoring.statesTitle', 'State messages') }}</mat-panel-title>
48455
+ <mat-panel-description>{{ statesSummary() }}</mat-panel-description>
48456
+ </mat-expansion-panel-header>
48457
+ <p class="inline-authoring__panel-description">
48458
+ {{ tx('inlineAuthoring.statesDescription', 'Adjust the first messages users read when the list is loading, empty, filtered, or broken.') }}
48459
+ </p>
48460
+ <div class="inline-authoring__grid">
48461
+ <mat-form-field appearance="outline">
48462
+ <mat-label>{{ tx('messages.loading.label', 'Loading data') }}</mat-label>
48463
+ <input
48464
+ matInput
48465
+ data-testid="table-inline-editor-message-loading"
48466
+ [ngModel]="loadingMessage"
48467
+ (ngModelChange)="setStateMessage('loading', $event)"
48468
+ [disabled]="readonly"
48469
+ />
48470
+ </mat-form-field>
48471
+
48472
+ <mat-form-field appearance="outline">
48473
+ <mat-label>{{ tx('messages.empty.label', 'No data found') }}</mat-label>
48474
+ <input
48475
+ matInput
48476
+ data-testid="table-inline-editor-message-empty"
48477
+ [ngModel]="emptyMessage"
48478
+ (ngModelChange)="setStateMessage('empty', $event)"
48479
+ [disabled]="readonly"
48480
+ />
48481
+ </mat-form-field>
48482
+
48483
+ <mat-form-field appearance="outline">
48484
+ <mat-label>{{ tx('messages.noResults.label', 'No results') }}</mat-label>
48485
+ <input
48486
+ matInput
48487
+ data-testid="table-inline-editor-message-no-results"
48488
+ [ngModel]="noResultsMessage"
48489
+ (ngModelChange)="setStateMessage('noResults', $event)"
48490
+ [disabled]="readonly"
48491
+ />
48492
+ </mat-form-field>
48493
+
48494
+ <mat-form-field appearance="outline">
48495
+ <mat-label>{{ tx('messages.error.label', 'Load error') }}</mat-label>
48496
+ <input
48497
+ matInput
48498
+ data-testid="table-inline-editor-message-error"
48499
+ [ngModel]="errorMessage"
48500
+ (ngModelChange)="setStateMessage('error', $event)"
48501
+ [disabled]="readonly"
48502
+ />
48503
+ </mat-form-field>
48504
+ </div>
48505
+ </mat-expansion-panel>
48506
+
48507
+ <mat-expansion-panel class="inline-authoring__columns-panel" [expanded]="false" data-testid="table-inline-editor-columns-panel">
48508
+ <mat-expansion-panel-header>
48509
+ <mat-panel-title>{{ tx('inlineAuthoring.columnsTitle', 'Columns') }}</mat-panel-title>
48510
+ <mat-panel-description>{{ columnCountLabel() }}</mat-panel-description>
48511
+ </mat-expansion-panel-header>
48512
+ <p class="inline-authoring__panel-description">
48513
+ {{ tx('inlineAuthoring.columnsDescription', 'Open only when you need to adjust visible fields, labels, and renderers.') }}
48514
+ </p>
48515
+ <columns-config-editor
48516
+ [config]="workingConfig"
48517
+ (configChange)="onColumnsConfigChange($event)"
48518
+ />
48519
+ </mat-expansion-panel>
48520
+ </section>
48521
+ }
48522
+ </mat-card>
48523
+ </section>
48524
+ `, isInline: true, styles: [":host{display:block}.inline-authoring{display:grid;gap:12px}.inline-authoring__header{display:flex;align-items:start;justify-content:space-between;gap:12px}.inline-authoring__header h4{margin:0;font-size:16px}.inline-authoring__header p{margin:4px 0 0;color:var(--md-sys-color-on-surface-variant,#5f6368)}.inline-authoring__chip{border-radius:999px;padding:6px 10px;font-size:12px;background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent);white-space:nowrap}.inline-authoring__card{display:grid;gap:12px;padding:14px;border-radius:18px}.inline-authoring__summary{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.inline-authoring__summary-card{display:grid;gap:4px;padding:12px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent);min-width:0}.inline-authoring__summary-card--wide{grid-column:span 2}.inline-authoring__summary-label{font-size:12px;color:var(--md-sys-color-on-surface-variant,#5f6368)}.inline-authoring__summary-card strong{font-size:13px;line-height:1.4;overflow:hidden;text-overflow:ellipsis}.inline-authoring__essentials{display:grid;gap:12px}.inline-authoring__advanced{display:grid;gap:12px;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container,#f5f7fb) 88%,transparent)}.inline-authoring__advanced-summary{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.inline-authoring__advanced-card{display:grid;gap:4px;padding:12px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-surface,#fff) 92%,transparent);min-width:0}.inline-authoring__advanced-card strong{font-size:13px;line-height:1.4;overflow:hidden;text-overflow:ellipsis}.inline-authoring__advanced-panels{display:grid;gap:12px}.inline-authoring__section-copy{display:grid;gap:4px}.inline-authoring__section-copy h5{margin:0;font-size:14px}.inline-authoring__section-copy p{margin:0;color:var(--md-sys-color-on-surface-variant,#5f6368)}.inline-authoring__grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));align-items:start}.inline-authoring__columns-panel{border:1px solid color-mix(in srgb,var(--md-sys-color-outline,#c5c7ce) 65%,transparent);border-radius:16px}.inline-authoring__panel-description{margin:0 0 12px;color:var(--md-sys-color-on-surface-variant,#5f6368)}.inline-authoring__readonly{margin:0;color:var(--md-sys-color-on-surface-variant,#5f6368)}pre{margin:0;overflow:auto;max-height:240px;padding:12px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$1.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i10$1.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i7$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i7$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i7$1.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "directive", type: i7$1.MatExpansionPanelDescription, selector: "mat-panel-description" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i6.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i6.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i5$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i6$1.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "component", type: ColumnsConfigEditorComponent, selector: "columns-config-editor", inputs: ["config"], outputs: ["configChange", "columnChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
48525
+ }
48526
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisTableInlineAuthoringEditorComponent, decorators: [{
48527
+ type: Component,
48528
+ args: [{ selector: 'praxis-table-inline-authoring-editor', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
48529
+ CommonModule,
48530
+ FormsModule,
48531
+ MatCardModule,
48532
+ MatExpansionModule,
48533
+ MatFormFieldModule,
48534
+ MatInputModule,
48535
+ MatSelectModule,
48536
+ MatSlideToggleModule,
48537
+ ColumnsConfigEditorComponent,
48538
+ ], providers: [providePraxisI18nConfig(PRAXIS_TABLE_EDITOR_I18N_CONFIG)], template: `
48539
+ <section class="inline-authoring" data-testid="table-inline-authoring-editor">
48540
+ <header class="inline-authoring__header">
48541
+ <div>
48542
+ <h4>{{ tx('inlineAuthoring.title', 'Table structure') }}</h4>
48543
+ <p>{{ tx('inlineAuthoring.description', 'Edit the canonical TableConfig used by this CRUD surface.') }}</p>
48544
+ </div>
48545
+ <span class="inline-authoring__chip" data-testid="table-inline-editor-column-count">
48546
+ {{ columnCountLabel() }}
48547
+ </span>
48548
+ </header>
48549
+
48550
+ <mat-card class="inline-authoring__card">
48551
+ <section class="inline-authoring__summary" data-testid="table-inline-editor-summary">
48552
+ <div class="inline-authoring__summary-card">
48553
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.summary.toolbar', 'Toolbar') }}</span>
48554
+ <strong data-testid="table-inline-editor-summary-toolbar">{{ toolbarSummary() }}</strong>
48555
+ </div>
48556
+ <div class="inline-authoring__summary-card">
48557
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.summary.density', 'Density') }}</span>
48558
+ <strong data-testid="table-inline-editor-summary-density">{{ densitySummary() }}</strong>
48559
+ </div>
48560
+ <div class="inline-authoring__summary-card">
48561
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.summary.actions', 'Actions') }}</span>
48562
+ <strong data-testid="table-inline-editor-summary-actions">{{ actionsPositionSummary() }}</strong>
48563
+ </div>
48564
+ <div class="inline-authoring__summary-card">
48565
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.summary.behavior', 'List behavior') }}</span>
48566
+ <strong data-testid="table-inline-editor-summary-behavior">{{ behaviorSummary() }}</strong>
48567
+ </div>
48568
+ <div class="inline-authoring__summary-card">
48569
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.summary.states', 'State messages') }}</span>
48570
+ <strong data-testid="table-inline-editor-summary-states">{{ statesSummary() }}</strong>
48571
+ </div>
48572
+ <div class="inline-authoring__summary-card inline-authoring__summary-card--wide">
48573
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.summary.columns', 'Visible structure') }}</span>
48574
+ <strong data-testid="table-inline-editor-summary-columns">{{ columnsPreviewSummary() }}</strong>
48575
+ </div>
48576
+ </section>
48577
+
48578
+ <section class="inline-authoring__essentials">
48579
+ <div class="inline-authoring__section-copy">
48580
+ <h5>{{ tx('inlineAuthoring.essentialsTitle', 'Table essentials') }}</h5>
48581
+ <p>{{ tx('inlineAuthoring.essentialsDescription', 'Define the toolbar presence and the texts users see first.') }}</p>
48582
+ </div>
48583
+
48584
+ <div class="inline-authoring__grid">
48585
+ <mat-slide-toggle
48586
+ data-testid="table-inline-editor-toolbar-visible"
48587
+ [ngModel]="toolbarVisible"
48588
+ (ngModelChange)="setToolbarVisible($event)"
48589
+ [disabled]="readonly"
48590
+ >
48591
+ {{ tx('toolbar.visible', 'Show toolbar') }}
48592
+ </mat-slide-toggle>
48593
+
48594
+ <mat-form-field appearance="outline">
48595
+ <mat-label>{{ tx('toolbar.title.label', 'Toolbar title') }}</mat-label>
48596
+ <input
48597
+ matInput
48598
+ data-testid="table-inline-editor-toolbar-title"
48599
+ [ngModel]="toolbarTitle"
48600
+ (ngModelChange)="setToolbarTitle($event)"
48601
+ [disabled]="readonly"
48602
+ />
48603
+ </mat-form-field>
48604
+
48605
+ <mat-form-field appearance="outline">
48606
+ <mat-label>{{ tx('toolbar.subtitle.label', 'Subtitle') }}</mat-label>
48607
+ <input
48608
+ matInput
48609
+ data-testid="table-inline-editor-toolbar-subtitle"
48610
+ [ngModel]="toolbarSubtitle"
48611
+ (ngModelChange)="setToolbarSubtitle($event)"
48612
+ [disabled]="readonly"
48613
+ />
48614
+ </mat-form-field>
48615
+
48616
+ <mat-form-field appearance="outline">
48617
+ <mat-label>{{ tx('behavior.appearance.density', 'Density') }}</mat-label>
48618
+ <mat-select
48619
+ data-testid="table-inline-editor-density"
48620
+ [ngModel]="density"
48621
+ (ngModelChange)="setDensity($event)"
48622
+ [disabled]="readonly"
48623
+ >
48624
+ <mat-option value="compact">{{ tx('behavior.appearance.density.compact', 'Compact') }}</mat-option>
48625
+ <mat-option value="comfortable">{{ tx('behavior.appearance.density.comfortable', 'Comfortable') }}</mat-option>
48626
+ <mat-option value="spacious">{{ tx('behavior.appearance.density.spacious', 'Spacious') }}</mat-option>
48627
+ </mat-select>
48628
+ </mat-form-field>
48629
+
48630
+ <mat-form-field appearance="outline">
48631
+ <mat-label>{{ tx('toolbar.actionsPosition.label', 'Actions position') }}</mat-label>
48632
+ <mat-select
48633
+ data-testid="table-inline-editor-toolbar-actions-position"
48634
+ [ngModel]="toolbarActionsPosition"
48635
+ (ngModelChange)="setToolbarActionsPosition($event)"
48636
+ [disabled]="readonly"
48637
+ >
48638
+ <mat-option value="top">{{ tx('toolbar.actionsPosition.top', 'Above the table') }}</mat-option>
48639
+ <mat-option value="bottom">{{ tx('toolbar.actionsPosition.bottom', 'Below the table') }}</mat-option>
48640
+ <mat-option value="both">{{ tx('toolbar.actionsPosition.both', 'Top and bottom') }}</mat-option>
48641
+ </mat-select>
48642
+ </mat-form-field>
48643
+
48644
+ <mat-form-field appearance="outline">
48645
+ <mat-label>{{ tx('toolbar.height.label', 'Toolbar height (px)') }}</mat-label>
48646
+ <input
48647
+ matInput
48648
+ type="number"
48649
+ data-testid="table-inline-editor-toolbar-height"
48650
+ [ngModel]="toolbarHeight"
48651
+ (ngModelChange)="setToolbarHeight($event)"
48652
+ [disabled]="readonly"
48653
+ min="40"
48654
+ max="120"
48655
+ />
48656
+ </mat-form-field>
48657
+
48658
+ <mat-form-field appearance="outline">
48659
+ <mat-label>{{ tx('toolbar.backgroundPreset.label', 'Actions background color') }}</mat-label>
48660
+ <mat-select
48661
+ data-testid="table-inline-editor-toolbar-background-preset"
48662
+ [ngModel]="toolbarActionsBackgroundColor"
48663
+ (ngModelChange)="setToolbarActionsBackgroundColor($event)"
48664
+ [disabled]="readonly"
48665
+ >
48666
+ <mat-option value="">{{ tx('toolbar.backgroundPreset.default', 'Theme default') }}</mat-option>
48667
+ <mat-option value="var(--p-table-header-bg)">{{ tx('toolbar.backgroundPreset.header', 'Table header') }}</mat-option>
48668
+ <mat-option value="var(--md-sys-color-surface)">{{ tx('toolbar.backgroundPreset.surface', 'Surface') }}</mat-option>
48669
+ <mat-option value="var(--md-sys-color-surface-container)">{{ tx('toolbar.backgroundPreset.surfaceContainer', 'Surface container') }}</mat-option>
48670
+ <mat-option value="var(--md-sys-color-surface-container-high)">{{ tx('toolbar.backgroundPreset.surfaceContainerHigh', 'Surface container high') }}</mat-option>
48671
+ <mat-option value="var(--md-sys-color-surface-container-highest)">{{ tx('toolbar.backgroundPreset.surfaceContainerHighest', 'Surface container highest') }}</mat-option>
48672
+ </mat-select>
48673
+ </mat-form-field>
48674
+ </div>
48675
+ </section>
48676
+
48677
+ <section class="inline-authoring__advanced" data-testid="table-inline-editor-advanced-intro">
48678
+ <div class="inline-authoring__section-copy">
48679
+ <h5>{{ tx('inlineAuthoring.advancedTitle', 'Advanced table details') }}</h5>
48680
+ <p>{{ tx('inlineAuthoring.advancedDescription', 'Open the next panels only when you need to tune list behavior, state copy, or visible columns.') }}</p>
48681
+ </div>
48682
+
48683
+ <section class="inline-authoring__advanced-summary">
48684
+ <div class="inline-authoring__advanced-card">
48685
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.advanced.behavior', 'Behavior') }}</span>
48686
+ <strong data-testid="table-inline-editor-advanced-summary-behavior">{{ behaviorSummary() }}</strong>
48687
+ </div>
48688
+ <div class="inline-authoring__advanced-card">
48689
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.advanced.states', 'State copy') }}</span>
48690
+ <strong data-testid="table-inline-editor-advanced-summary-states">{{ statesSummary() }}</strong>
48691
+ </div>
48692
+ <div class="inline-authoring__advanced-card">
48693
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.advanced.columns', 'Columns') }}</span>
48694
+ <strong data-testid="table-inline-editor-advanced-summary-columns">{{ columnsPreviewSummary() }}</strong>
48695
+ </div>
48696
+ </section>
48697
+ </section>
48698
+
48699
+ @if (readonly) {
48700
+ <p class="inline-authoring__readonly" data-testid="table-inline-editor-readonly-hint">
48701
+ {{ tx('inlineAuthoring.readonlyHint', 'This authoring surface is read-only in the current context.') }}
48702
+ </p>
48703
+ <pre data-testid="table-inline-editor-json">{{ serializedConfig() }}</pre>
48704
+ } @else {
48705
+ <section class="inline-authoring__advanced-panels" data-testid="table-inline-editor-advanced-panels">
48706
+ <mat-expansion-panel class="inline-authoring__columns-panel" [expanded]="false" data-testid="table-inline-editor-behavior-panel">
48707
+ <mat-expansion-panel-header>
48708
+ <mat-panel-title>{{ tx('inlineAuthoring.behaviorTitle', 'List behavior') }}</mat-panel-title>
48709
+ <mat-panel-description>{{ behaviorSummary() }}</mat-panel-description>
48710
+ </mat-expansion-panel-header>
48711
+ <p class="inline-authoring__panel-description">
48712
+ {{ tx('inlineAuthoring.behaviorDescription', 'Control pagination and sorting with the same runtime-backed semantics used by the full table editor.') }}
48713
+ </p>
48714
+ <div class="inline-authoring__grid">
48715
+ <mat-slide-toggle
48716
+ data-testid="table-inline-editor-pagination-enabled"
48717
+ [ngModel]="paginationEnabled"
48718
+ (ngModelChange)="setPaginationEnabled($event)"
48719
+ [disabled]="readonly"
48720
+ >
48721
+ {{ tx('behavior.pagination.enabled', 'Enable pagination') }}
48722
+ </mat-slide-toggle>
48723
+
48724
+ <mat-form-field appearance="outline">
48725
+ <mat-label>{{ tx('behavior.pagination.pageSize', 'Items per page') }}</mat-label>
48726
+ <input
48727
+ matInput
48728
+ type="number"
48729
+ data-testid="table-inline-editor-pagination-page-size"
48730
+ [ngModel]="pageSize"
48731
+ (ngModelChange)="setPaginationPageSize($event)"
48732
+ [disabled]="readonly || !paginationEnabled"
48733
+ min="1"
48734
+ />
48735
+ </mat-form-field>
48736
+
48737
+ <mat-form-field appearance="outline">
48738
+ <mat-label>{{ tx('behavior.pagination.pageSizeOptions', 'Page size options') }}</mat-label>
48739
+ <input
48740
+ matInput
48741
+ data-testid="table-inline-editor-pagination-page-size-options"
48742
+ [ngModel]="pageSizeOptionsText"
48743
+ (ngModelChange)="setPaginationPageSizeOptions($event)"
48744
+ [disabled]="readonly || !paginationEnabled"
48745
+ placeholder="5, 10, 25, 50"
48746
+ />
48747
+ </mat-form-field>
48748
+
48749
+ <mat-slide-toggle
48750
+ data-testid="table-inline-editor-pagination-first-last"
48751
+ [ngModel]="showFirstLastButtons"
48752
+ (ngModelChange)="setPaginationShowFirstLastButtons($event)"
48753
+ [disabled]="readonly || !paginationEnabled"
48754
+ >
48755
+ {{ tx('behavior.pagination.firstLast', 'Show first/last page buttons') }}
48756
+ </mat-slide-toggle>
48757
+
48758
+ <mat-form-field appearance="outline">
48759
+ <mat-label>{{ tx('behavior.pagination.strategy', 'Pagination strategy') }}</mat-label>
48760
+ <mat-select
48761
+ data-testid="table-inline-editor-pagination-strategy"
48762
+ [ngModel]="paginationStrategy"
48763
+ (ngModelChange)="setPaginationStrategy($event)"
48764
+ [disabled]="readonly || !paginationEnabled"
48765
+ >
48766
+ <mat-option value="client">{{ tx('behavior.pagination.strategy.client', 'Client (local data)') }}</mat-option>
48767
+ <mat-option value="server">{{ tx('behavior.pagination.strategy.server', 'Server (remote pagination)') }}</mat-option>
48768
+ </mat-select>
48769
+ </mat-form-field>
48770
+
48771
+ <mat-form-field appearance="outline">
48772
+ <mat-label>{{ tx('behavior.pagination.position', 'Paginator position') }}</mat-label>
48773
+ <mat-select
48774
+ data-testid="table-inline-editor-pagination-position"
48775
+ [ngModel]="paginationPosition"
48776
+ (ngModelChange)="setPaginationPosition($event)"
48777
+ [disabled]="readonly || !paginationEnabled"
48778
+ >
48779
+ <mat-option value="top">{{ tx('behavior.pagination.position.top', 'Top') }}</mat-option>
48780
+ <mat-option value="bottom">{{ tx('behavior.pagination.position.bottom', 'Bottom') }}</mat-option>
48781
+ <mat-option value="both">{{ tx('behavior.pagination.position.both', 'Both') }}</mat-option>
48782
+ </mat-select>
48783
+ </mat-form-field>
48784
+
48785
+ <mat-form-field appearance="outline">
48786
+ <mat-label>{{ tx('behavior.pagination.style', 'Paginator style') }}</mat-label>
48787
+ <mat-select
48788
+ data-testid="table-inline-editor-pagination-style"
48789
+ [ngModel]="paginationStyle"
48790
+ (ngModelChange)="setPaginationStyle($event)"
48791
+ [disabled]="readonly || !paginationEnabled"
48792
+ >
48793
+ <mat-option value="default">{{ tx('behavior.pagination.style.default', 'Default') }}</mat-option>
48794
+ <mat-option value="simple">{{ tx('behavior.pagination.style.simple', 'Simple') }}</mat-option>
48795
+ <mat-option value="compact">{{ tx('behavior.pagination.style.compact', 'Compact') }}</mat-option>
48796
+ <mat-option value="advanced">{{ tx('behavior.pagination.style.advanced', 'Advanced') }}</mat-option>
48797
+ </mat-select>
48798
+ </mat-form-field>
48799
+
48800
+ <mat-slide-toggle
48801
+ data-testid="table-inline-editor-sorting-enabled"
48802
+ [ngModel]="sortingEnabled"
48803
+ (ngModelChange)="setSortingEnabled($event)"
48804
+ [disabled]="readonly"
48805
+ >
48806
+ {{ tx('behavior.sorting.enabled', 'Enable sorting') }}
48807
+ </mat-slide-toggle>
48808
+
48809
+ <mat-form-field appearance="outline">
48810
+ <mat-label>{{ tx('behavior.sorting.strategy', 'Sorting strategy') }}</mat-label>
48811
+ <mat-select
48812
+ data-testid="table-inline-editor-sorting-strategy"
48813
+ [ngModel]="sortingStrategy"
48814
+ (ngModelChange)="setSortingStrategy($event)"
48815
+ [disabled]="readonly || !sortingEnabled"
48816
+ >
48817
+ <mat-option value="client">{{ tx('behavior.sorting.strategy.client', 'Client (local sorting)') }}</mat-option>
48818
+ <mat-option value="server">{{ tx('behavior.sorting.strategy.server', 'Server (remote sorting)') }}</mat-option>
48819
+ </mat-select>
48820
+ </mat-form-field>
48821
+
48822
+ <mat-slide-toggle
48823
+ data-testid="table-inline-editor-sorting-allow-clear"
48824
+ [ngModel]="allowClearSort"
48825
+ (ngModelChange)="setSortingAllowClear($event)"
48826
+ [disabled]="readonly || !sortingEnabled"
48827
+ >
48828
+ {{ tx('behavior.sorting.allowClear', 'Allow clearing sort') }}
48829
+ </mat-slide-toggle>
48830
+
48831
+ <mat-form-field appearance="outline">
48832
+ <mat-label>{{ tx('behavior.sorting.defaultField', 'Initial sort - column') }}</mat-label>
48833
+ <mat-select
48834
+ data-testid="table-inline-editor-sorting-default-field"
48835
+ [ngModel]="defaultSortField"
48836
+ (ngModelChange)="setSortingDefaultField($event)"
48837
+ [disabled]="readonly || !sortingEnabled"
48838
+ >
48839
+ <mat-option value="">{{ tx('inlineAuthoring.noneOption', 'None') }}</mat-option>
48840
+ @for (column of workingConfig.columns || []; track column.field) {
48841
+ <mat-option [value]="column.field">{{ column.header || column.field }}</mat-option>
48842
+ }
48843
+ </mat-select>
48844
+ </mat-form-field>
48845
+
48846
+ <mat-form-field appearance="outline">
48847
+ <mat-label>{{ tx('behavior.sorting.direction', 'Direction') }}</mat-label>
48848
+ <mat-select
48849
+ data-testid="table-inline-editor-sorting-default-direction"
48850
+ [ngModel]="defaultSortDirection"
48851
+ (ngModelChange)="setSortingDefaultDirection($event)"
48852
+ [disabled]="readonly || !sortingEnabled || !defaultSortField"
48853
+ >
48854
+ <mat-option value="asc">{{ tx('behavior.sorting.direction.asc', 'Ascending') }}</mat-option>
48855
+ <mat-option value="desc">{{ tx('behavior.sorting.direction.desc', 'Descending') }}</mat-option>
48856
+ </mat-select>
48857
+ </mat-form-field>
48858
+ </div>
48859
+ </mat-expansion-panel>
48860
+
48861
+ <mat-expansion-panel class="inline-authoring__columns-panel" [expanded]="false" data-testid="table-inline-editor-states-panel">
48862
+ <mat-expansion-panel-header>
48863
+ <mat-panel-title>{{ tx('inlineAuthoring.statesTitle', 'State messages') }}</mat-panel-title>
48864
+ <mat-panel-description>{{ statesSummary() }}</mat-panel-description>
48865
+ </mat-expansion-panel-header>
48866
+ <p class="inline-authoring__panel-description">
48867
+ {{ tx('inlineAuthoring.statesDescription', 'Adjust the first messages users read when the list is loading, empty, filtered, or broken.') }}
48868
+ </p>
48869
+ <div class="inline-authoring__grid">
48870
+ <mat-form-field appearance="outline">
48871
+ <mat-label>{{ tx('messages.loading.label', 'Loading data') }}</mat-label>
48872
+ <input
48873
+ matInput
48874
+ data-testid="table-inline-editor-message-loading"
48875
+ [ngModel]="loadingMessage"
48876
+ (ngModelChange)="setStateMessage('loading', $event)"
48877
+ [disabled]="readonly"
48878
+ />
48879
+ </mat-form-field>
48880
+
48881
+ <mat-form-field appearance="outline">
48882
+ <mat-label>{{ tx('messages.empty.label', 'No data found') }}</mat-label>
48883
+ <input
48884
+ matInput
48885
+ data-testid="table-inline-editor-message-empty"
48886
+ [ngModel]="emptyMessage"
48887
+ (ngModelChange)="setStateMessage('empty', $event)"
48888
+ [disabled]="readonly"
48889
+ />
48890
+ </mat-form-field>
48891
+
48892
+ <mat-form-field appearance="outline">
48893
+ <mat-label>{{ tx('messages.noResults.label', 'No results') }}</mat-label>
48894
+ <input
48895
+ matInput
48896
+ data-testid="table-inline-editor-message-no-results"
48897
+ [ngModel]="noResultsMessage"
48898
+ (ngModelChange)="setStateMessage('noResults', $event)"
48899
+ [disabled]="readonly"
48900
+ />
48901
+ </mat-form-field>
48902
+
48903
+ <mat-form-field appearance="outline">
48904
+ <mat-label>{{ tx('messages.error.label', 'Load error') }}</mat-label>
48905
+ <input
48906
+ matInput
48907
+ data-testid="table-inline-editor-message-error"
48908
+ [ngModel]="errorMessage"
48909
+ (ngModelChange)="setStateMessage('error', $event)"
48910
+ [disabled]="readonly"
48911
+ />
48912
+ </mat-form-field>
48913
+ </div>
48914
+ </mat-expansion-panel>
48915
+
48916
+ <mat-expansion-panel class="inline-authoring__columns-panel" [expanded]="false" data-testid="table-inline-editor-columns-panel">
48917
+ <mat-expansion-panel-header>
48918
+ <mat-panel-title>{{ tx('inlineAuthoring.columnsTitle', 'Columns') }}</mat-panel-title>
48919
+ <mat-panel-description>{{ columnCountLabel() }}</mat-panel-description>
48920
+ </mat-expansion-panel-header>
48921
+ <p class="inline-authoring__panel-description">
48922
+ {{ tx('inlineAuthoring.columnsDescription', 'Open only when you need to adjust visible fields, labels, and renderers.') }}
48923
+ </p>
48924
+ <columns-config-editor
48925
+ [config]="workingConfig"
48926
+ (configChange)="onColumnsConfigChange($event)"
48927
+ />
48928
+ </mat-expansion-panel>
48929
+ </section>
48930
+ }
48931
+ </mat-card>
48932
+ </section>
48933
+ `, styles: [":host{display:block}.inline-authoring{display:grid;gap:12px}.inline-authoring__header{display:flex;align-items:start;justify-content:space-between;gap:12px}.inline-authoring__header h4{margin:0;font-size:16px}.inline-authoring__header p{margin:4px 0 0;color:var(--md-sys-color-on-surface-variant,#5f6368)}.inline-authoring__chip{border-radius:999px;padding:6px 10px;font-size:12px;background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent);white-space:nowrap}.inline-authoring__card{display:grid;gap:12px;padding:14px;border-radius:18px}.inline-authoring__summary{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.inline-authoring__summary-card{display:grid;gap:4px;padding:12px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent);min-width:0}.inline-authoring__summary-card--wide{grid-column:span 2}.inline-authoring__summary-label{font-size:12px;color:var(--md-sys-color-on-surface-variant,#5f6368)}.inline-authoring__summary-card strong{font-size:13px;line-height:1.4;overflow:hidden;text-overflow:ellipsis}.inline-authoring__essentials{display:grid;gap:12px}.inline-authoring__advanced{display:grid;gap:12px;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container,#f5f7fb) 88%,transparent)}.inline-authoring__advanced-summary{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.inline-authoring__advanced-card{display:grid;gap:4px;padding:12px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-surface,#fff) 92%,transparent);min-width:0}.inline-authoring__advanced-card strong{font-size:13px;line-height:1.4;overflow:hidden;text-overflow:ellipsis}.inline-authoring__advanced-panels{display:grid;gap:12px}.inline-authoring__section-copy{display:grid;gap:4px}.inline-authoring__section-copy h5{margin:0;font-size:14px}.inline-authoring__section-copy p{margin:0;color:var(--md-sys-color-on-surface-variant,#5f6368)}.inline-authoring__grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));align-items:start}.inline-authoring__columns-panel{border:1px solid color-mix(in srgb,var(--md-sys-color-outline,#c5c7ce) 65%,transparent);border-radius:16px}.inline-authoring__panel-description{margin:0 0 12px;color:var(--md-sys-color-on-surface-variant,#5f6368)}.inline-authoring__readonly{margin:0;color:var(--md-sys-color-on-surface-variant,#5f6368)}pre{margin:0;overflow:auto;max-height:240px;padding:12px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}\n"] }]
48934
+ }], ctorParameters: () => [{ type: i1.PraxisI18nService }], propDecorators: { config: [{
48935
+ type: Input
48936
+ }], readonly: [{
48937
+ type: Input
48938
+ }], configChange: [{
48939
+ type: Output
48940
+ }] } });
48941
+ function cloneTableConfig(config) {
48942
+ return JSON.parse(JSON.stringify(config || { columns: [] }));
48943
+ }
48944
+ function clampNumber(value, min, max, fallback) {
48945
+ const parsed = Number(value);
48946
+ if (!Number.isFinite(parsed)) {
48947
+ return fallback;
48948
+ }
48949
+ return Math.min(max, Math.max(min, parsed));
48950
+ }
48951
+
45912
48952
  const PRAXIS_TABLE_PORTS = [
45913
48953
  {
45914
48954
  id: 'queryContext',
@@ -47166,4 +50206,4 @@ const TABLE_COMPONENT_AI_CAPABILITIES = {
47166
50206
  * Generated bundle index. Do not edit.
47167
50207
  */
47168
50208
 
47169
- export { AnalyticsTableConfigAdapterService, AnalyticsTableContractService, AnalyticsTableStatsApiService, BOOLEAN_PRESETS, BehaviorConfigEditorComponent, CURRENCY_PRESETS, ColumnsConfigEditorComponent, DATE_PRESETS, DataFormatterComponent, DataFormattingService, FORMULA_TEMPLATES, FilterConfigService, FilterSettingsComponent, FormulaGeneratorService, JsonConfigEditorComponent, MessagesLocalizationEditorComponent, NUMBER_PRESETS, PERCENTAGE_PRESETS, PRAXIS_FILTER_COMPONENT_METADATA, PRAXIS_TABLE_COMPONENT_METADATA, PraxisFilter, PraxisTable, PraxisTableConfigEditor, PraxisTableToolbar, STRING_PRESETS, TABLE_AI_CAPABILITIES, TABLE_COMPONENT_AI_CAPABILITIES, TASK_PRESETS, TableDefaultsProvider, TableRulesEditorComponent, ToolbarActionsEditorComponent, ValueMappingEditorComponent, VisualFormulaBuilderComponent, buildTableApplyPlan, createTableAuthoringDocument, getActionId, getEnum, getTableCapabilities, normalizeTableAuthoringDocument, parseLegacyOrTableDocument, providePraxisFilterMetadata, providePraxisTableMetadata, serializeTableAuthoringDocument, toCanonicalTableConfig, validateTableAuthoringDocument };
50209
+ export { AnalyticsTableConfigAdapterService, AnalyticsTableContractService, AnalyticsTableStatsApiService, BOOLEAN_PRESETS, BehaviorConfigEditorComponent, CURRENCY_PRESETS, ColumnsConfigEditorComponent, DATE_PRESETS, DataFormatterComponent, DataFormattingService, FORMULA_TEMPLATES, FilterConfigService, FilterSettingsComponent, FormulaGeneratorService, JsonConfigEditorComponent, MessagesLocalizationEditorComponent, NUMBER_PRESETS, PERCENTAGE_PRESETS, PRAXIS_FILTER_COMPONENT_METADATA, PRAXIS_TABLE_COMPONENT_METADATA, PraxisFilter, PraxisTable, PraxisTableConfigEditor, PraxisTableInlineAuthoringEditorComponent, PraxisTableToolbar, STRING_PRESETS, TABLE_AI_CAPABILITIES, TABLE_COMPONENT_AI_CAPABILITIES, TASK_PRESETS, TableDefaultsProvider, TableRulesEditorComponent, ToolbarActionsEditorComponent, ValueMappingEditorComponent, VisualFormulaBuilderComponent, buildTableApplyPlan, createTableAuthoringDocument, getActionId, getEnum, getTableCapabilities, isTableRendererSupportedByRichContentP0, mapTableRendererToRichContentP0, normalizeTableAuthoringDocument, parseLegacyOrTableDocument, providePraxisFilterMetadata, providePraxisTableMetadata, serializeTableAuthoringDocument, toCanonicalTableConfig, validateTableAuthoringDocument };