@praxisui/table 6.0.0-beta.0 → 8.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.
@@ -1169,7 +1169,7 @@ class PraxisTableToolbar {
1169
1169
  }
1170
1170
  </div>
1171
1171
  </mat-toolbar>
1172
- `, 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"] }] });
1173
1173
  }
1174
1174
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisTableToolbar, decorators: [{
1175
1175
  type: Component,
@@ -1542,7 +1542,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
1542
1542
  `, host: {
1543
1543
  '[class.praxis-toolbar-footer]': "placement === 'footer'",
1544
1544
  '(document:keydown)': 'onDocumentKeydown($event)',
1545
- }, 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"] }]
1546
1546
  }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i1.LoggerService, decorators: [{
1547
1547
  type: Optional
1548
1548
  }] }], propDecorators: { config: [{
@@ -7032,6 +7032,38 @@ const PRAXIS_TABLE_EDITOR_I18N_CONFIG = {
7032
7032
  'sections.messages': 'Mensagens e localização',
7033
7033
  'sections.dialogs': 'Diálogos',
7034
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.',
7035
7067
  'connect.resource': 'Recurso',
7036
7068
  'connect.resource.placeholder': 'ex.: employees',
7037
7069
  'connect.primaryKey': 'Chave primária',
@@ -7607,7 +7639,9 @@ const PRAXIS_TABLE_EDITOR_I18N_CONFIG = {
7607
7639
  'behavior.pagination.positionHint': 'Paginator no topo/baixo/ambos.',
7608
7640
  'behavior.pagination.style': 'Estilo do paginator',
7609
7641
  'behavior.pagination.style.default': 'Padrao',
7642
+ 'behavior.pagination.style.simple': 'Simples',
7610
7643
  'behavior.pagination.style.compact': 'Compacto',
7644
+ 'behavior.pagination.style.advanced': 'Avancado',
7611
7645
  'behavior.pagination.styleHint': 'Compacto para densidade alta.',
7612
7646
  'behavior.sorting.title': 'Ordenacao',
7613
7647
  'behavior.sorting.description': 'Controle como as colunas podem ser ordenadas',
@@ -7727,7 +7761,7 @@ const PRAXIS_TABLE_EDITOR_I18N_CONFIG = {
7727
7761
  'behavior.expansion.detailSource.resourcePath': 'Resource Path',
7728
7762
  'behavior.expansion.detailSource.hypermedia': 'Hypermedia',
7729
7763
  'behavior.expansion.inlineSchema': 'Inline schema (JSON)',
7730
- 'behavior.expansion.inlineSchemaHint': 'Aceita nos como card/formRef/tableRef/chartRef/richText/tabs/list, conforme schemaContract.allowedNodes.',
7764
+ 'behavior.expansion.inlineSchemaHint': 'Aceita nós como text/icon/image/badge/avatar/metric/progress/compose/card/mediaBlock/cardGrid/detailList/timeline/actionBar/formRef/tableRef/chartRef/diagramEmbed/richText/tabs/list, conforme schemaContract.allowedNodes.',
7731
7765
  'behavior.expansion.inlineSchemaError': 'JSON invalido: o contrato atual nao foi aplicado para',
7732
7766
  'behavior.expansion.allowList': 'Resource allow list',
7733
7767
  'behavior.expansion.resourceKind': 'Tipo de recurso',
@@ -7774,8 +7808,8 @@ const PRAXIS_TABLE_EDITOR_I18N_CONFIG = {
7774
7808
  'behavior.appearance.density.compact': 'Compacta',
7775
7809
  'behavior.appearance.density.comfortable': 'Confortavel',
7776
7810
  'behavior.appearance.density.spacious': 'Espacosa',
7777
- 'crud.defaults.title': 'Padroes do CRUD',
7778
- '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.',
7779
7813
  'crud.defaults.openMode': 'Modo de abertura',
7780
7814
  'crud.mode.modal': 'Modal',
7781
7815
  'crud.mode.route': 'Rota',
@@ -8024,6 +8058,38 @@ const PRAXIS_TABLE_EDITOR_I18N_CONFIG = {
8024
8058
  'sections.messages': 'Messages and localization',
8025
8059
  'sections.dialogs': 'Dialogs',
8026
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.',
8027
8093
  'connect.resource': 'Resource',
8028
8094
  'connect.resource.placeholder': 'e.g. employees',
8029
8095
  'connect.primaryKey': 'Primary key',
@@ -8599,7 +8665,9 @@ const PRAXIS_TABLE_EDITOR_I18N_CONFIG = {
8599
8665
  'behavior.pagination.positionHint': 'Paginator on top/bottom/both.',
8600
8666
  'behavior.pagination.style': 'Paginator style',
8601
8667
  'behavior.pagination.style.default': 'Default',
8668
+ 'behavior.pagination.style.simple': 'Simple',
8602
8669
  'behavior.pagination.style.compact': 'Compact',
8670
+ 'behavior.pagination.style.advanced': 'Advanced',
8603
8671
  'behavior.pagination.styleHint': 'Compact for high density.',
8604
8672
  'behavior.sorting.title': 'Sorting',
8605
8673
  'behavior.sorting.description': 'Control how columns can be sorted',
@@ -8719,7 +8787,7 @@ const PRAXIS_TABLE_EDITOR_I18N_CONFIG = {
8719
8787
  'behavior.expansion.detailSource.resourcePath': 'Resource Path',
8720
8788
  'behavior.expansion.detailSource.hypermedia': 'Hypermedia',
8721
8789
  'behavior.expansion.inlineSchema': 'Inline schema (JSON)',
8722
- '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 text/icon/image/badge/avatar/metric/progress/compose/card/mediaBlock/cardGrid/detailList/timeline/actionBar/formRef/tableRef/chartRef/diagramEmbed/richText/tabs/list, according to schemaContract.allowedNodes.',
8723
8791
  'behavior.expansion.inlineSchemaError': 'Invalid JSON: the current contract was not applied to',
8724
8792
  'behavior.expansion.allowList': 'Resource allow list',
8725
8793
  'behavior.expansion.resourceKind': 'Resource kind',
@@ -8766,8 +8834,8 @@ const PRAXIS_TABLE_EDITOR_I18N_CONFIG = {
8766
8834
  'behavior.appearance.density.compact': 'Compact',
8767
8835
  'behavior.appearance.density.comfortable': 'Comfortable',
8768
8836
  'behavior.appearance.density.spacious': 'Spacious',
8769
- 'crud.defaults.title': 'CRUD defaults',
8770
- '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.',
8771
8839
  'crud.defaults.openMode': 'Open mode',
8772
8840
  'crud.mode.modal': 'Modal',
8773
8841
  'crud.mode.route': 'Route',
@@ -14414,13 +14482,27 @@ const DEFAULT_EXPANSION_ALLOWED_NODES$1 = [
14414
14482
  'stack',
14415
14483
  'tabs',
14416
14484
  'tab',
14485
+ 'text',
14486
+ 'icon',
14487
+ 'image',
14488
+ 'badge',
14489
+ 'avatar',
14490
+ 'metric',
14491
+ 'progress',
14492
+ 'compose',
14417
14493
  'card',
14494
+ 'mediaBlock',
14495
+ 'cardGrid',
14496
+ 'timeline',
14418
14497
  'value',
14419
14498
  'action',
14499
+ 'actionBar',
14420
14500
  'list',
14501
+ 'detailList',
14421
14502
  'formRef',
14422
14503
  'tableRef',
14423
14504
  'chartRef',
14505
+ 'diagramEmbed',
14424
14506
  'richText',
14425
14507
  'templateRef',
14426
14508
  ];
@@ -16089,7 +16171,7 @@ class BehaviorConfigEditorComponent {
16089
16171
  matSuffix
16090
16172
  type="button"
16091
16173
  class="help-icon-button"
16092
- [matTooltip]="tx('behavior.expansion.inlineSchemaHint', 'Accepts nodes such as card/formRef/tableRef/chartRef/richText/tabs/list, according to schemaContract.allowedNodes.')"
16174
+ [matTooltip]="tx('behavior.expansion.inlineSchemaHint', 'Accepts nodes such as text/icon/image/badge/avatar/metric/progress/compose/card/mediaBlock/cardGrid/timeline/actionBar/detailList/formRef/tableRef/chartRef/templateRef/diagramEmbed/richText/tabs/list, according to schemaContract.allowedNodes.')"
16093
16175
  >
16094
16176
  <mat-icon>help_outline</mat-icon>
16095
16177
  </button>
@@ -17081,7 +17163,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
17081
17163
  matSuffix
17082
17164
  type="button"
17083
17165
  class="help-icon-button"
17084
- [matTooltip]="tx('behavior.expansion.inlineSchemaHint', 'Accepts nodes such as card/formRef/tableRef/chartRef/richText/tabs/list, according to schemaContract.allowedNodes.')"
17166
+ [matTooltip]="tx('behavior.expansion.inlineSchemaHint', 'Accepts nodes such as text/icon/image/badge/avatar/metric/progress/compose/card/mediaBlock/cardGrid/timeline/actionBar/detailList/formRef/tableRef/chartRef/templateRef/diagramEmbed/richText/tabs/list, according to schemaContract.allowedNodes.')"
17085
17167
  >
17086
17168
  <mat-icon>help_outline</mat-icon>
17087
17169
  </button>
@@ -23454,6 +23536,10 @@ const CONTROL_TYPE_LABELS = {
23454
23536
  [INLINE_TIME_CONTROL_TYPE$1]: 'Horario',
23455
23537
  [INLINE_TIME_RANGE_CONTROL_TYPE$1]: 'Intervalo de horario',
23456
23538
  [INLINE_TREE_SELECT_CONTROL_TYPE$1]: 'Arvore inline',
23539
+ inlineselect: 'Select',
23540
+ inlinesearchableselect: 'Select buscável',
23541
+ inlinedate: 'date',
23542
+ inlinedaterange: 'Intervalo de datas',
23457
23543
  [String(FieldControlType.TIME_PICKER).toLowerCase()]: 'Horario',
23458
23544
  [String(FieldControlType.TIME_RANGE).toLowerCase()]: 'Intervalo de horario',
23459
23545
  [String(FieldControlType.TREE_SELECT).toLowerCase()]: 'Arvore',
@@ -24674,6 +24760,11 @@ class FilterSettingsComponent {
24674
24760
  return this.normalizeAlwaysVisibleMetadataOverrides(this.extraSettings.alwaysVisibleFieldMetadataOverrides);
24675
24761
  }
24676
24762
  normalizeControlTypeForEditor(controlType) {
24763
+ const raw = String(controlType || '').trim();
24764
+ const token = normalizeControlTypeToken(raw);
24765
+ if (INLINE_FILTER_CONTROL_TYPE_VALUES.some((value) => normalizeControlTypeToken(value) === token)) {
24766
+ return raw;
24767
+ }
24677
24768
  return resolveControlTypeAlias(controlType, FieldControlType.INPUT);
24678
24769
  }
24679
24770
  inferControlTypeFromMetadata(meta) {
@@ -36112,6 +36203,12 @@ const PRAXIS_TABLE_RUNTIME_I18N_CONFIG = {
36112
36203
  namespaces: {
36113
36204
  [PRAXIS_TABLE_RUNTIME_I18N_NAMESPACE]: {
36114
36205
  'pt-BR': {
36206
+ 'table.chrome.settings': 'Configurações',
36207
+ 'table.chrome.settings.reconcile': 'Schema do servidor mudou - reconciliar',
36208
+ 'table.chrome.disconnect': 'Desconectar',
36209
+ 'table.chrome.disconnectTooltip': 'Desconectar da fonte de dados',
36210
+ 'table.chrome.rowActions.more': 'Mais ações',
36211
+ 'table.chrome.rowActions.loading': 'Carregando ações contextuais',
36115
36212
  'table.expansion.toggle.ariaLabel.expand': 'Expandir detalhes do item',
36116
36213
  'table.expansion.toggle.ariaLabel.collapse': 'Recolher detalhes do item',
36117
36214
  'table.expansion.region.ariaLabel': 'Detalhes do item expandido (linha {position})',
@@ -36158,8 +36255,29 @@ const PRAXIS_TABLE_RUNTIME_I18N_CONFIG = {
36158
36255
  'table.expansion.hypermedia.operations.create': 'Criar',
36159
36256
  'table.expansion.hypermedia.operations.edit': 'Editar',
36160
36257
  'table.expansion.hypermedia.operations.delete': 'Excluir',
36258
+ 'table.expansion.actionBar.title': 'Ações rápidas',
36259
+ 'table.expansion.actionBar.empty': 'Nenhuma ação disponível.',
36260
+ 'table.expansion.timeline.title': 'Linha do tempo',
36261
+ 'table.expansion.timeline.empty': 'Sem eventos recentes.',
36262
+ 'table.expansion.embed.formRef.label': 'Formulário governado',
36263
+ 'table.expansion.embed.tableRef.label': 'Tabela governada',
36264
+ 'table.expansion.embed.chartRef.label': 'Gráfico governado',
36265
+ 'table.expansion.embed.templateRef.label': 'Template governado',
36266
+ 'table.expansion.embed.diagramEmbed.label': 'Diagrama governado',
36267
+ 'table.expansion.embed.empty': 'Nenhum conteúdo de embed disponível.',
36268
+ 'table.expansion.embed.reference': 'Referência',
36269
+ 'table.expansion.embed.provider': 'Provedor',
36270
+ 'table.expansion.embed.preset': 'Preset',
36271
+ 'table.expansion.embed.inputs': 'Inputs',
36272
+ 'table.expansion.embed.diagramSource': 'Fonte do diagrama',
36161
36273
  },
36162
36274
  'en-US': {
36275
+ 'table.chrome.settings': 'Settings',
36276
+ 'table.chrome.settings.reconcile': 'Server schema changed - reconcile',
36277
+ 'table.chrome.disconnect': 'Disconnect',
36278
+ 'table.chrome.disconnectTooltip': 'Disconnect from the data source',
36279
+ 'table.chrome.rowActions.more': 'More actions',
36280
+ 'table.chrome.rowActions.loading': 'Loading contextual actions',
36163
36281
  'table.expansion.toggle.ariaLabel.expand': 'Expand item details',
36164
36282
  'table.expansion.toggle.ariaLabel.collapse': 'Collapse item details',
36165
36283
  'table.expansion.region.ariaLabel': 'Expanded item details (row {position})',
@@ -36206,6 +36324,21 @@ const PRAXIS_TABLE_RUNTIME_I18N_CONFIG = {
36206
36324
  'table.expansion.hypermedia.operations.create': 'Create',
36207
36325
  'table.expansion.hypermedia.operations.edit': 'Edit',
36208
36326
  'table.expansion.hypermedia.operations.delete': 'Delete',
36327
+ 'table.expansion.actionBar.title': 'Quick actions',
36328
+ 'table.expansion.actionBar.empty': 'No actions available.',
36329
+ 'table.expansion.timeline.title': 'Timeline',
36330
+ 'table.expansion.timeline.empty': 'No recent events.',
36331
+ 'table.expansion.embed.formRef.label': 'Governed form',
36332
+ 'table.expansion.embed.tableRef.label': 'Governed table',
36333
+ 'table.expansion.embed.chartRef.label': 'Governed chart',
36334
+ 'table.expansion.embed.templateRef.label': 'Governed template',
36335
+ 'table.expansion.embed.diagramEmbed.label': 'Governed diagram',
36336
+ 'table.expansion.embed.empty': 'No embed content available.',
36337
+ 'table.expansion.embed.reference': 'Reference',
36338
+ 'table.expansion.embed.provider': 'Provider',
36339
+ 'table.expansion.embed.preset': 'Preset',
36340
+ 'table.expansion.embed.inputs': 'Inputs',
36341
+ 'table.expansion.embed.diagramSource': 'Diagram source',
36209
36342
  },
36210
36343
  },
36211
36344
  },
@@ -36252,12 +36385,18 @@ const DEFAULT_EXPANSION_ALLOWED_NODES = [
36252
36385
  'tabs',
36253
36386
  'tab',
36254
36387
  'card',
36388
+ 'mediaBlock',
36389
+ 'cardGrid',
36255
36390
  'value',
36256
36391
  'action',
36392
+ 'actionBar',
36257
36393
  'list',
36394
+ 'detailList',
36395
+ 'timeline',
36258
36396
  'formRef',
36259
36397
  'tableRef',
36260
36398
  'chartRef',
36399
+ 'diagramEmbed',
36261
36400
  'richText',
36262
36401
  'templateRef',
36263
36402
  ];
@@ -36379,6 +36518,7 @@ class PraxisTable {
36379
36518
  metadataChange = new EventEmitter();
36380
36519
  loadingStateChange = new EventEmitter();
36381
36520
  collectionLinksChange = new EventEmitter();
36521
+ latestLoadingState = null;
36382
36522
  paginator;
36383
36523
  sort;
36384
36524
  materialTable;
@@ -36702,6 +36842,31 @@ class PraxisTable {
36702
36842
  this.cdr.markForCheck();
36703
36843
  }
36704
36844
  }
36845
+ scheduleResponsiveHorizontalScrollRefresh() {
36846
+ const scheduler = typeof globalThis.requestAnimationFrame === 'function'
36847
+ ? globalThis.requestAnimationFrame.bind(globalThis)
36848
+ : null;
36849
+ if (!scheduler) {
36850
+ this.refreshResponsiveHorizontalScroll();
36851
+ return;
36852
+ }
36853
+ if (this.resizeObserverRefreshFrameId !== null) {
36854
+ return;
36855
+ }
36856
+ this.resizeObserverRefreshFrameId = scheduler(() => {
36857
+ this.resizeObserverRefreshFrameId = null;
36858
+ this.refreshResponsiveHorizontalScroll();
36859
+ });
36860
+ }
36861
+ cancelResponsiveHorizontalScrollRefresh() {
36862
+ if (this.resizeObserverRefreshFrameId === null) {
36863
+ return;
36864
+ }
36865
+ if (typeof globalThis.cancelAnimationFrame === 'function') {
36866
+ globalThis.cancelAnimationFrame(this.resizeObserverRefreshFrameId);
36867
+ }
36868
+ this.resizeObserverRefreshFrameId = null;
36869
+ }
36705
36870
  hasBottomPaginator() {
36706
36871
  if (!this.getPaginationEnabled())
36707
36872
  return false;
@@ -36745,6 +36910,82 @@ class PraxisTable {
36745
36910
  return true;
36746
36911
  return this.isLocalMode() && !this.isLocalDataModeFeatureEnabled();
36747
36912
  }
36913
+ shouldShowNoDataState() {
36914
+ if (this.shouldShowEmptyState() ||
36915
+ !this.shouldRenderDataSurface() ||
36916
+ this.schemaError ||
36917
+ this.dataError ||
36918
+ this.isTableDataLoading()) {
36919
+ return false;
36920
+ }
36921
+ if (this.isRemoteMode() && !this.hasResolvedRemoteDataLoad()) {
36922
+ return false;
36923
+ }
36924
+ return this.getRenderedRowCount() === 0;
36925
+ }
36926
+ shouldShowLoadingSurface() {
36927
+ if (this.shouldShowEmptyState() ||
36928
+ !this.shouldRenderDataSurface() ||
36929
+ this.schemaError ||
36930
+ this.dataError) {
36931
+ return false;
36932
+ }
36933
+ return this.isTableDataLoading() && this.getRenderedRowCount() === 0;
36934
+ }
36935
+ getLoadingSurfaceMessage() {
36936
+ return this.config?.messages?.states?.loading || 'Carregando dados...';
36937
+ }
36938
+ getNoDataStateTitle() {
36939
+ const emptyState = this.resolveNoDataStateConfig();
36940
+ return emptyState.message;
36941
+ }
36942
+ getNoDataStateDescription() {
36943
+ const emptyState = this.resolveNoDataStateConfig();
36944
+ const actions = emptyState.actions ?? [];
36945
+ if (!actions.length) {
36946
+ return undefined;
36947
+ }
36948
+ return this.hasActiveDataFilters()
36949
+ ? 'Ajuste os filtros ou abra outra carteira operacional.'
36950
+ : 'Use a ação principal para iniciar a próxima operação.';
36951
+ }
36952
+ getTableSettingsLabel() {
36953
+ return translateTableRuntimeText(this.i18n, 'table.chrome.settings', 'Configurações');
36954
+ }
36955
+ getTableSettingsTooltip() {
36956
+ return this.schemaOutdated
36957
+ ? translateTableRuntimeText(this.i18n, 'table.chrome.settings.reconcile', 'Schema do servidor mudou - reconciliar')
36958
+ : this.getTableSettingsLabel();
36959
+ }
36960
+ getDisconnectLabel() {
36961
+ return translateTableRuntimeText(this.i18n, 'table.chrome.disconnect', 'Desconectar');
36962
+ }
36963
+ getDisconnectTooltip() {
36964
+ return translateTableRuntimeText(this.i18n, 'table.chrome.disconnectTooltip', 'Desconectar da fonte de dados');
36965
+ }
36966
+ getNoDataStateIcon() {
36967
+ const emptyState = this.resolveNoDataStateConfig();
36968
+ return (emptyState.icon || '').trim();
36969
+ }
36970
+ getNoDataStatePrimaryAction() {
36971
+ const actions = this.resolveNoDataStateConfig().actions ?? [];
36972
+ const action = actions.find((entry) => entry?.primary) ?? actions[0];
36973
+ return action ? this.mapEmptyStateAction(action) : null;
36974
+ }
36975
+ getNoDataStateSecondaryActions() {
36976
+ const actions = this.resolveNoDataStateConfig().actions ?? [];
36977
+ const primary = this.getNoDataStatePrimaryAction();
36978
+ const primaryId = primary?.label ? primary.label : null;
36979
+ return actions
36980
+ .filter((entry) => {
36981
+ if (!entry)
36982
+ return false;
36983
+ if (entry.primary && primaryId)
36984
+ return false;
36985
+ return true;
36986
+ })
36987
+ .map((entry) => this.mapEmptyStateAction(entry));
36988
+ }
36748
36989
  shouldRenderAdvancedFilter() {
36749
36990
  if (!this.config?.behavior?.filtering?.advancedFilters?.enabled) {
36750
36991
  return false;
@@ -36912,6 +37153,7 @@ class PraxisTable {
36912
37153
  allowComparisons: true,
36913
37154
  });
36914
37155
  resizeObserver;
37156
+ resizeObserverRefreshFrameId = null;
36915
37157
  removeViewportChangeListeners = null;
36916
37158
  warnedUnsupportedFeatures = new Set();
36917
37159
  warnedStrictCspStyleMode = false;
@@ -37335,7 +37577,7 @@ class PraxisTable {
37335
37577
  });
37336
37578
  }
37337
37579
  emitLoadingState(phase, status, message, error) {
37338
- this.loadingStateChange.emit({
37580
+ this.latestLoadingState = {
37339
37581
  phase,
37340
37582
  status,
37341
37583
  message,
@@ -37346,8 +37588,71 @@ class PraxisTable {
37346
37588
  resourcePath: this.resourcePath,
37347
37589
  },
37348
37590
  timestamp: new Date().toISOString(),
37591
+ };
37592
+ this.loadingStateChange.emit({
37593
+ ...this.latestLoadingState,
37349
37594
  });
37350
37595
  }
37596
+ hasResolvedRemoteDataLoad() {
37597
+ return this.latestLoadingState?.status === 'success';
37598
+ }
37599
+ isTableDataLoading() {
37600
+ return this.latestLoadingState?.status === 'loading';
37601
+ }
37602
+ getRenderedRowCount() {
37603
+ return Array.isArray(this.dataSource?.data) ? this.dataSource.data.length : 0;
37604
+ }
37605
+ hasActiveDataFilters() {
37606
+ const criteria = this.getEffectiveFilterCriteria();
37607
+ const values = Object.values(criteria || {});
37608
+ return values.some((value) => this.hasMeaningfulFilterValue(value));
37609
+ }
37610
+ hasMeaningfulFilterValue(value) {
37611
+ if (value == null)
37612
+ return false;
37613
+ if (typeof value === 'string')
37614
+ return value.trim().length > 0;
37615
+ if (typeof value === 'number' || typeof value === 'boolean')
37616
+ return true;
37617
+ if (Array.isArray(value)) {
37618
+ return value.some((entry) => this.hasMeaningfulFilterValue(entry));
37619
+ }
37620
+ if (typeof value === 'object') {
37621
+ return Object.values(value).some((entry) => this.hasMeaningfulFilterValue(entry));
37622
+ }
37623
+ return false;
37624
+ }
37625
+ resolveNoDataStateConfig() {
37626
+ const baseEmptyState = this.config?.behavior?.emptyState;
37627
+ const contextualEmptyState = this.hasActiveDataFilters()
37628
+ ? (baseEmptyState?.contexts?.filtered ?? baseEmptyState?.contexts?.searched)
37629
+ : baseEmptyState?.contexts?.initial;
37630
+ const merged = {
37631
+ ...(baseEmptyState || {}),
37632
+ ...(contextualEmptyState || {}),
37633
+ };
37634
+ const fallbackMessage = this.hasActiveDataFilters()
37635
+ ? this.config?.messages?.states?.noResults || 'Nenhum resultado encontrado.'
37636
+ : this.config?.messages?.states?.empty || 'Nenhum dado disponivel.';
37637
+ return {
37638
+ message: String(merged.message || fallbackMessage),
37639
+ icon: typeof merged.icon === 'string' ? merged.icon : undefined,
37640
+ actions: Array.isArray(merged.actions) ? merged.actions : [],
37641
+ };
37642
+ }
37643
+ mapEmptyStateAction(action) {
37644
+ return {
37645
+ label: action.label,
37646
+ icon: action.icon,
37647
+ action: () => this.onEmptyStateAction(action.action),
37648
+ };
37649
+ }
37650
+ onEmptyStateAction(actionId) {
37651
+ const action = String(actionId || '').trim();
37652
+ if (!action)
37653
+ return;
37654
+ this.onToolbarAction({ action });
37655
+ }
37351
37656
  buildLoadingContext(phase, label, blocking) {
37352
37657
  const componentId = this.tableId || this.componentInstanceId || 'table';
37353
37658
  return {
@@ -37831,6 +38136,392 @@ class PraxisTable {
37831
38136
  .map((entry) => this.formatExpansionDetailListEntry(entry))
37832
38137
  .filter((entry) => entry.length > 0);
37833
38138
  }
38139
+ getExpansionDetailRichListEntries(row, node) {
38140
+ const field = String(node?.field || '').trim();
38141
+ const staticItems = Array.isArray(node?.items) ? node.items : null;
38142
+ if (field) {
38143
+ const resolved = this.getNestedPropertyValue(row, field);
38144
+ if (Array.isArray(resolved)) {
38145
+ return resolved;
38146
+ }
38147
+ }
38148
+ if (staticItems) {
38149
+ return staticItems;
38150
+ }
38151
+ return [];
38152
+ }
38153
+ getExpansionDetailRichListEmptyText(node) {
38154
+ return String(node?.emptyText || 'Sem itens.').trim() || 'Sem itens.';
38155
+ }
38156
+ getExpansionDetailRichListItemNodes(node) {
38157
+ return Array.isArray(node?.itemSchema?.nodes)
38158
+ ? node.itemSchema.nodes
38159
+ : [];
38160
+ }
38161
+ getExpansionDetailCardGridHeaderNodes(node) {
38162
+ const title = String(node?.title || '').trim();
38163
+ const subtitle = String(node?.subtitle || '').trim();
38164
+ const items = [];
38165
+ if (title) {
38166
+ items.push({
38167
+ type: 'text',
38168
+ text: title,
38169
+ className: 'pfx-expansion-node-card-grid__title-text',
38170
+ });
38171
+ }
38172
+ if (subtitle) {
38173
+ items.push({
38174
+ type: 'text',
38175
+ text: subtitle,
38176
+ className: 'pfx-expansion-node-card-grid__subtitle-text',
38177
+ });
38178
+ }
38179
+ if (!items.length) {
38180
+ return [];
38181
+ }
38182
+ return [
38183
+ {
38184
+ type: 'compose',
38185
+ direction: 'column',
38186
+ gap: 'xs',
38187
+ items,
38188
+ },
38189
+ ];
38190
+ }
38191
+ getExpansionDetailCardGridCards(node) {
38192
+ return Array.isArray(node?.cards) ? node.cards : [];
38193
+ }
38194
+ getExpansionDetailCardGridStyles(node) {
38195
+ const columns = node?.columns === 'auto'
38196
+ ? 'repeat(auto-fit, minmax(var(--pfx-expansion-card-grid-min-width, 220px), 1fr))'
38197
+ : `repeat(${this.normalizeExpansionDetailCardGridColumns(node?.columns)}, minmax(0, 1fr))`;
38198
+ const minCardWidth = this.normalizeExpansionDetailCardGridMinWidth(node?.minCardWidth);
38199
+ return {
38200
+ '--pfx-expansion-card-grid-columns': columns,
38201
+ '--pfx-expansion-card-grid-min-width': `${minCardWidth}px`,
38202
+ };
38203
+ }
38204
+ getExpansionDetailCardGridCardNodes(card) {
38205
+ const content = this.normalizeExpansionDetailCardGridCardContent(card?.content);
38206
+ return [
38207
+ {
38208
+ type: 'card',
38209
+ id: card?.id ? String(card.id) : undefined,
38210
+ title: card?.title ? String(card.title) : undefined,
38211
+ subtitle: card?.subtitle ? String(card.subtitle) : undefined,
38212
+ content,
38213
+ className: 'pfx-expansion-node-card-grid__card',
38214
+ },
38215
+ ];
38216
+ }
38217
+ getExpansionDetailTimelineEntries(row, node) {
38218
+ const field = String(node?.field || '').trim();
38219
+ const staticItems = Array.isArray(node?.items) ? node.items : null;
38220
+ if (field) {
38221
+ const resolved = this.getNestedPropertyValue(row, field);
38222
+ if (Array.isArray(resolved)) {
38223
+ return resolved;
38224
+ }
38225
+ }
38226
+ if (staticItems) {
38227
+ return staticItems;
38228
+ }
38229
+ return [];
38230
+ }
38231
+ getExpansionDetailTimelineEmptyText(node) {
38232
+ return (String(node?.emptyText
38233
+ || this.resolveTableMessage('table.expansion.timeline.empty', 'Sem eventos recentes.')).trim()
38234
+ || this.resolveTableMessage('table.expansion.timeline.empty', 'Sem eventos recentes.'));
38235
+ }
38236
+ getExpansionDetailTimelineNodes(row, node) {
38237
+ const timelineNode = this.buildExpansionDetailTimelineRichNode(row, node);
38238
+ return timelineNode ? [timelineNode] : [];
38239
+ }
38240
+ getExpansionDetailRichListItemContext(row, rowIndex, node, entry, itemIndex) {
38241
+ const baseContext = this.getExpansionDetailRichContentContext(row, rowIndex);
38242
+ const canonicalAlias = 'detailItem';
38243
+ const canonicalIndexAlias = 'detailIndex';
38244
+ const itemAlias = String(node?.itemContext?.itemAlias || canonicalAlias).trim() || canonicalAlias;
38245
+ const indexAlias = String(node?.itemContext?.indexAlias || canonicalIndexAlias).trim() || canonicalIndexAlias;
38246
+ const extendedContext = {
38247
+ ...baseContext,
38248
+ [canonicalAlias]: entry,
38249
+ [canonicalIndexAlias]: itemIndex,
38250
+ };
38251
+ if (itemAlias !== canonicalAlias) {
38252
+ extendedContext[itemAlias] = entry;
38253
+ }
38254
+ if (indexAlias !== canonicalIndexAlias) {
38255
+ extendedContext[indexAlias] = itemIndex;
38256
+ }
38257
+ return extendedContext;
38258
+ }
38259
+ getExpansionDetailRichListItemClassName(node) {
38260
+ const layout = String(node?.itemSchema?.layout || 'stack').trim().toLowerCase();
38261
+ if (layout === 'row') {
38262
+ return 'pfx-expansion-node-rich-list__item pfx-expansion-node-rich-list__item--row';
38263
+ }
38264
+ if (layout === 'card-list') {
38265
+ return 'pfx-expansion-node-rich-list__item pfx-expansion-node-rich-list__item--card-list';
38266
+ }
38267
+ return 'pfx-expansion-node-rich-list__item pfx-expansion-node-rich-list__item--stack';
38268
+ }
38269
+ getExpansionDetailRichListItemKey(node, entry, itemIndex) {
38270
+ const keyField = String(node?.itemKeyField || '').trim();
38271
+ if (keyField && entry && typeof entry === 'object' && !Array.isArray(entry)) {
38272
+ const resolved = this.getNestedPropertyValue(entry, keyField);
38273
+ const normalized = String(resolved ?? '').trim();
38274
+ if (normalized) {
38275
+ return normalized;
38276
+ }
38277
+ }
38278
+ const label = this.formatExpansionDetailListEntry(entry);
38279
+ return label ? `${itemIndex}:${label}` : `item-${itemIndex}`;
38280
+ }
38281
+ getExpansionDetailRichListItemActions(node, row, rowIndex, entry, itemIndex) {
38282
+ const actions = Array.isArray(node?.itemActions) ? node.itemActions : [];
38283
+ const context = this.getExpansionDetailRichListItemContext(row, rowIndex, node, entry, itemIndex);
38284
+ return actions.filter((action) => this.isExpansionDetailRichListItemActionVisible(action, context));
38285
+ }
38286
+ normalizeExpansionDetailCardGridColumns(columns) {
38287
+ const normalized = Number(columns);
38288
+ if (normalized === 4 || normalized === 3 || normalized === 2) {
38289
+ return normalized;
38290
+ }
38291
+ return 1;
38292
+ }
38293
+ normalizeExpansionDetailCardGridMinWidth(minCardWidth) {
38294
+ const normalized = Number(minCardWidth);
38295
+ return Number.isFinite(normalized) && normalized >= 160 ? normalized : 220;
38296
+ }
38297
+ getExpansionDetailTimelineFieldName(node, fieldName) {
38298
+ const explicit = String(node?.itemSchema?.[fieldName] || '').trim();
38299
+ if (explicit) {
38300
+ return explicit;
38301
+ }
38302
+ const defaults = {
38303
+ titleField: 'label',
38304
+ subtitleField: 'secondary',
38305
+ metaField: 'status',
38306
+ iconField: 'icon',
38307
+ badgeField: 'badge',
38308
+ };
38309
+ return defaults[fieldName];
38310
+ }
38311
+ buildExpansionDetailTimelineRichNode(row, node) {
38312
+ const items = this.getExpansionDetailTimelineEntries(row, node)
38313
+ .map((entry, itemIndex) => this.mapExpansionDetailTimelineItem(row, entry, node, itemIndex))
38314
+ .filter((item) => item != null);
38315
+ return {
38316
+ type: 'timeline',
38317
+ id: node?.id ? String(node.id) : undefined,
38318
+ className: 'pfx-expansion-node pfx-expansion-node-timeline',
38319
+ title: this.getExpansionDetailNodeTitle(node, this.resolveTableMessage('table.expansion.timeline.title', 'Linha do tempo')) || undefined,
38320
+ emptyText: this.getExpansionDetailTimelineEmptyText(node),
38321
+ items,
38322
+ };
38323
+ }
38324
+ mapExpansionDetailTimelineItem(row, entry, node, itemIndex) {
38325
+ if (entry === null || entry === undefined) {
38326
+ return null;
38327
+ }
38328
+ if (typeof entry === 'string'
38329
+ || typeof entry === 'number'
38330
+ || typeof entry === 'boolean'
38331
+ || typeof entry === 'bigint') {
38332
+ const title = String(entry).trim();
38333
+ return title
38334
+ ? {
38335
+ id: `timeline-${itemIndex}`,
38336
+ title,
38337
+ }
38338
+ : null;
38339
+ }
38340
+ if (Array.isArray(entry)) {
38341
+ const title = entry
38342
+ .map((nested) => this.formatExpansionDetailListEntry(nested))
38343
+ .filter((nested) => nested.length > 0)
38344
+ .join(' - ');
38345
+ return title
38346
+ ? {
38347
+ id: `timeline-${itemIndex}`,
38348
+ title,
38349
+ }
38350
+ : null;
38351
+ }
38352
+ if (typeof entry !== 'object') {
38353
+ return null;
38354
+ }
38355
+ const record = entry;
38356
+ const title = this.resolveExpansionDetailTimelineObjectValue(row, record, this.getExpansionDetailTimelineFieldName(node, 'titleField'), ['label', 'title', 'name', 'value', 'event'], 'titleExpr');
38357
+ if (!title) {
38358
+ return null;
38359
+ }
38360
+ return {
38361
+ id: this.getExpansionDetailTimelineObjectValue(record, 'id', [])
38362
+ || `timeline-${itemIndex}`,
38363
+ title,
38364
+ subtitle: this.resolveExpansionDetailTimelineObjectValue(row, record, this.getExpansionDetailTimelineFieldName(node, 'subtitleField'), ['secondary', 'subtitle', 'description', 'detail'], 'subtitleExpr') || undefined,
38365
+ meta: this.resolveExpansionDetailTimelineObjectValue(row, record, this.getExpansionDetailTimelineFieldName(node, 'metaField'), ['status', 'time', 'timeAgo', 'date', 'when'], 'metaExpr') || undefined,
38366
+ icon: this.resolveExpansionDetailTimelineObjectValue(row, record, this.getExpansionDetailTimelineFieldName(node, 'iconField'), ['icon', 'symbol', 'kind'], 'iconExpr') || undefined,
38367
+ badge: this.resolveExpansionDetailTimelineObjectValue(row, record, this.getExpansionDetailTimelineFieldName(node, 'badgeField'), ['badge', 'category', 'tag', 'severity'], 'badgeExpr') || undefined,
38368
+ };
38369
+ }
38370
+ resolveExpansionDetailTimelineObjectValue(row, entry, preferredKey, fallbackKeys, expressionKey) {
38371
+ const expressionValue = this.resolveExpansionDetailTimelineExpressionValue(row, entry, expressionKey);
38372
+ if (expressionValue) {
38373
+ return expressionValue;
38374
+ }
38375
+ return this.getExpansionDetailTimelineObjectValue(entry, preferredKey, fallbackKeys);
38376
+ }
38377
+ resolveExpansionDetailTimelineExpressionValue(row, entry, expressionKey) {
38378
+ const expression = entry[expressionKey];
38379
+ if (typeof expression !== 'string') {
38380
+ return null;
38381
+ }
38382
+ const path = expression.trim();
38383
+ if (!path) {
38384
+ return null;
38385
+ }
38386
+ const resolved = path.startsWith('row.')
38387
+ || path.startsWith('entry.')
38388
+ || path.startsWith('detailItem.')
38389
+ ? this.getNestedPropertyValue({ row, entry, detailItem: entry }, path)
38390
+ : this.getNestedPropertyValue(row, path);
38391
+ if (typeof resolved === 'string'
38392
+ || typeof resolved === 'number'
38393
+ || typeof resolved === 'boolean'
38394
+ || typeof resolved === 'bigint') {
38395
+ const normalized = String(resolved).trim();
38396
+ if (normalized) {
38397
+ return normalized;
38398
+ }
38399
+ }
38400
+ return null;
38401
+ }
38402
+ getExpansionDetailTimelineObjectValue(entry, preferredKey, fallbackKeys) {
38403
+ const keys = [preferredKey, ...fallbackKeys].filter((key, index, all) => key && all.indexOf(key) === index);
38404
+ for (const key of keys) {
38405
+ const raw = entry[key];
38406
+ if (typeof raw === 'string'
38407
+ || typeof raw === 'number'
38408
+ || typeof raw === 'boolean'
38409
+ || typeof raw === 'bigint') {
38410
+ const normalized = String(raw).trim();
38411
+ if (normalized) {
38412
+ return normalized;
38413
+ }
38414
+ }
38415
+ }
38416
+ return null;
38417
+ }
38418
+ normalizeExpansionDetailCardGridCardContent(rawContent) {
38419
+ const content = Array.isArray(rawContent) ? rawContent : [];
38420
+ return content.filter((node) => this.isExpansionDetailCardGridContentNode(node));
38421
+ }
38422
+ normalizeExpansionDetailMediaBlockAvatar(candidate) {
38423
+ return candidate && typeof candidate === 'object' && candidate.type === 'avatar'
38424
+ ? candidate
38425
+ : null;
38426
+ }
38427
+ normalizeExpansionDetailMediaBlockText(candidate) {
38428
+ return candidate && typeof candidate === 'object' && candidate.type === 'text'
38429
+ ? candidate
38430
+ : null;
38431
+ }
38432
+ normalizeExpansionDetailMediaBlockMeta(candidate) {
38433
+ if (!candidate || typeof candidate !== 'object' || candidate.type !== 'compose') {
38434
+ return null;
38435
+ }
38436
+ const normalizedItems = this.normalizeExpansionDetailComposeItems(candidate.items);
38437
+ if (!normalizedItems.length) {
38438
+ return null;
38439
+ }
38440
+ return {
38441
+ ...candidate,
38442
+ items: normalizedItems,
38443
+ };
38444
+ }
38445
+ normalizeExpansionDetailMediaBlockTrailing(candidate) {
38446
+ return this.normalizeExpansionDetailCardGridCardContent(candidate);
38447
+ }
38448
+ normalizeExpansionDetailComposeItems(rawItems) {
38449
+ const items = Array.isArray(rawItems) ? rawItems : [];
38450
+ return items.filter((node) => this.isExpansionDetailComposeItemNode(node));
38451
+ }
38452
+ isExpansionDetailCardGridContentNode(node) {
38453
+ if (!node || typeof node !== 'object') {
38454
+ return false;
38455
+ }
38456
+ const type = String(node.type || '').trim();
38457
+ return (type === 'text'
38458
+ || type === 'icon'
38459
+ || type === 'image'
38460
+ || type === 'badge'
38461
+ || type === 'avatar'
38462
+ || type === 'metric'
38463
+ || type === 'progress'
38464
+ || type === 'compose');
38465
+ }
38466
+ isExpansionDetailComposeItemNode(node) {
38467
+ if (!node || typeof node !== 'object') {
38468
+ return false;
38469
+ }
38470
+ const type = String(node.type || '').trim();
38471
+ return (type === 'text'
38472
+ || type === 'icon'
38473
+ || type === 'image'
38474
+ || type === 'badge'
38475
+ || type === 'avatar'
38476
+ || type === 'metric'
38477
+ || type === 'progress');
38478
+ }
38479
+ getExpansionDetailRichListItemActionNodes(action) {
38480
+ const labelNode = {
38481
+ type: 'text',
38482
+ text: String(action?.label || action?.actionId || 'Ação').trim() || 'Ação',
38483
+ className: 'pfx-expansion-node-rich-list__item-action-text',
38484
+ };
38485
+ const icon = String(action?.icon || '').trim();
38486
+ if (!icon) {
38487
+ return [labelNode];
38488
+ }
38489
+ return [
38490
+ {
38491
+ type: 'compose',
38492
+ direction: 'row',
38493
+ gap: 'sm',
38494
+ items: [
38495
+ {
38496
+ type: 'icon',
38497
+ icon,
38498
+ ariaLabel: labelNode.text,
38499
+ className: 'pfx-expansion-node-rich-list__item-action-icon',
38500
+ },
38501
+ labelNode,
38502
+ ],
38503
+ },
38504
+ ];
38505
+ }
38506
+ isExpansionDetailRichListItemActionDisabled(action, row, rowIndex, node, entry, itemIndex) {
38507
+ const disabledWhen = action?.disabledWhen;
38508
+ if (!this.hasConditionalExpression(disabledWhen)) {
38509
+ return false;
38510
+ }
38511
+ return this.evaluateExpansionDetailRichListItemCondition(disabledWhen, this.getExpansionDetailRichListItemContext(row, rowIndex, node, entry, itemIndex), true);
38512
+ }
38513
+ onExpansionDetailRichListItemAction(action, row, rowIndex, node, entry, itemIndex, event) {
38514
+ const actionId = String(action?.actionId || '').trim();
38515
+ event.stopPropagation();
38516
+ if (!actionId) {
38517
+ return;
38518
+ }
38519
+ const context = this.getExpansionDetailRichListItemContext(row, rowIndex, node, entry, itemIndex);
38520
+ this.onRowAction(actionId, row, event, this.resolveRowActionRuntimeOptions({
38521
+ actionConfig: action,
38522
+ payload: this.evaluateExpansionDetailRichListItemPayload(action?.payloadExpr, context, entry),
38523
+ }, action));
38524
+ }
37834
38525
  getExpansionDetailListItemRichContentNodes(entry) {
37835
38526
  if (entry === null || entry === undefined) {
37836
38527
  return null;
@@ -37880,6 +38571,251 @@ class PraxisTable {
37880
38571
  const normalized = String(raw ?? '').trim();
37881
38572
  return normalized || null;
37882
38573
  }
38574
+ getExpansionDetailActionBarTitle(node) {
38575
+ return (this.getExpansionDetailNodeTitle(node, this.resolveTableMessage('table.expansion.actionBar.title', 'Ações rápidas'))
38576
+ || this.resolveTableMessage('table.expansion.actionBar.title', 'Ações rápidas'));
38577
+ }
38578
+ getExpansionDetailActionBarActions(node, row, index) {
38579
+ const actions = Array.isArray(node?.actions) ? node.actions : [];
38580
+ const context = this.getExpansionDetailRichContentContext(row, index);
38581
+ return actions.filter((action) => this.isExpansionDetailActionBarActionVisible(action, context));
38582
+ }
38583
+ getExpansionDetailActionBarActionNodes(action) {
38584
+ return this.getExpansionDetailRichListItemActionNodes(action);
38585
+ }
38586
+ isExpansionDetailActionBarActionDisabled(action, row, index) {
38587
+ const disabledWhen = action?.disabledWhen;
38588
+ if (!this.hasConditionalExpression(disabledWhen)) {
38589
+ return false;
38590
+ }
38591
+ return this.evaluateExpansionDetailRichListItemCondition(disabledWhen, this.getExpansionDetailRichContentContext(row, index), true);
38592
+ }
38593
+ getExpansionDetailActionBarEmptyText(node) {
38594
+ return (String(node?.emptyText
38595
+ || this.resolveTableMessage('table.expansion.actionBar.empty', 'Nenhuma ação disponível.')).trim()
38596
+ || this.resolveTableMessage('table.expansion.actionBar.empty', 'Nenhuma ação disponível.'));
38597
+ }
38598
+ onExpansionDetailActionBarAction(action, row, index, event) {
38599
+ const actionId = String(action?.actionId || '').trim();
38600
+ event.stopPropagation();
38601
+ if (!actionId) {
38602
+ return;
38603
+ }
38604
+ const context = this.getExpansionDetailRichContentContext(row, index);
38605
+ this.onRowAction(actionId, row, event, this.resolveRowActionRuntimeOptions({
38606
+ actionConfig: action,
38607
+ payload: this.evaluateExpansionDetailRichListItemPayload(action?.payloadExpr, context, row),
38608
+ }, action));
38609
+ }
38610
+ getExpansionDetailEmbedHeaderNodes(node) {
38611
+ const items = [];
38612
+ const title = this.getExpansionDetailNodeTitle(node, this.getExpansionDetailEmbedLabel(node));
38613
+ const subtitle = String(node?.subtitle || '').trim();
38614
+ const description = String(node?.description || '').trim();
38615
+ const caption = String(node?.caption || '').trim();
38616
+ if (title) {
38617
+ items.push({ type: 'text', text: title, className: 'pfx-expansion-node-embed__title-text' });
38618
+ }
38619
+ if (subtitle) {
38620
+ items.push({ type: 'text', text: subtitle, className: 'pfx-expansion-node-embed__subtitle-text' });
38621
+ }
38622
+ if (description) {
38623
+ items.push({ type: 'text', text: description, className: 'pfx-expansion-node-embed__description-text' });
38624
+ }
38625
+ if (caption) {
38626
+ items.push({ type: 'text', text: caption, className: 'pfx-expansion-node-embed__caption-text' });
38627
+ }
38628
+ if (!items.length) {
38629
+ return [];
38630
+ }
38631
+ return [
38632
+ {
38633
+ type: 'compose',
38634
+ direction: 'column',
38635
+ gap: 'xs',
38636
+ items,
38637
+ },
38638
+ ];
38639
+ }
38640
+ getExpansionDetailEmbedMetaNodes(node) {
38641
+ const items = [
38642
+ {
38643
+ type: 'badge',
38644
+ label: this.getExpansionDetailEmbedLabel(node),
38645
+ className: 'pfx-expansion-node-embed__kind-badge',
38646
+ },
38647
+ ];
38648
+ const reference = this.getExpansionDetailEmbedReference(node);
38649
+ if (reference) {
38650
+ items.push({
38651
+ type: 'text',
38652
+ text: `${this.resolveTableMessage('table.expansion.embed.reference', 'Referência')}: ${reference}`,
38653
+ className: 'pfx-expansion-node-embed__meta-text',
38654
+ });
38655
+ }
38656
+ const provider = this.getExpansionDetailDiagramProvider(node);
38657
+ if (provider) {
38658
+ items.push({
38659
+ type: 'text',
38660
+ text: `${this.resolveTableMessage('table.expansion.embed.provider', 'Provedor')}: ${provider}`,
38661
+ className: 'pfx-expansion-node-embed__meta-text',
38662
+ });
38663
+ }
38664
+ const preset = this.getExpansionDetailEmbedPresetLabel(node);
38665
+ if (preset) {
38666
+ items.push({
38667
+ type: 'text',
38668
+ text: `${this.resolveTableMessage('table.expansion.embed.preset', 'Preset')}: ${preset}`,
38669
+ className: 'pfx-expansion-node-embed__meta-text',
38670
+ });
38671
+ }
38672
+ const inputSummary = this.getExpansionDetailEmbedInputsSummary(node);
38673
+ if (inputSummary) {
38674
+ items.push({
38675
+ type: 'text',
38676
+ text: `${this.resolveTableMessage('table.expansion.embed.inputs', 'Inputs')}: ${inputSummary}`,
38677
+ className: 'pfx-expansion-node-embed__meta-text',
38678
+ });
38679
+ }
38680
+ if (items.length === 1) {
38681
+ return [items[0]];
38682
+ }
38683
+ return [
38684
+ {
38685
+ type: 'compose',
38686
+ direction: 'row',
38687
+ gap: 'sm',
38688
+ wrap: true,
38689
+ items,
38690
+ },
38691
+ ];
38692
+ }
38693
+ getExpansionDetailEmbedActionNodes(action) {
38694
+ return this.getExpansionDetailRichListItemActionNodes(action);
38695
+ }
38696
+ getExpansionDetailEmbedAction(node, row, index) {
38697
+ const action = node?.action;
38698
+ if (!action || typeof action !== 'object') {
38699
+ return null;
38700
+ }
38701
+ const context = this.getExpansionDetailRichContentContext(row, index);
38702
+ return this.isExpansionDetailEmbedActionVisible(action, context) ? action : null;
38703
+ }
38704
+ isExpansionDetailEmbedActionDisabled(action, row, index) {
38705
+ const disabledWhen = action?.disabledWhen;
38706
+ if (!this.hasConditionalExpression(disabledWhen)) {
38707
+ return false;
38708
+ }
38709
+ return this.evaluateExpansionDetailRichListItemCondition(disabledWhen, this.getExpansionDetailRichContentContext(row, index), true);
38710
+ }
38711
+ getExpansionDetailEmbedEmptyText(node) {
38712
+ return (String(node?.emptyText
38713
+ || this.resolveTableMessage('table.expansion.embed.empty', 'Nenhum conteúdo de embed disponível.')).trim()
38714
+ || this.resolveTableMessage('table.expansion.embed.empty', 'Nenhum conteúdo de embed disponível.'));
38715
+ }
38716
+ getExpansionDetailDiagramSource(row, node) {
38717
+ const staticSource = String(node?.source || '').trim();
38718
+ if (staticSource) {
38719
+ return staticSource;
38720
+ }
38721
+ const field = String(node?.sourceField || '').trim();
38722
+ if (!field) {
38723
+ return null;
38724
+ }
38725
+ const resolved = this.getNestedPropertyValue(row, field);
38726
+ const normalized = String(resolved ?? '').trim();
38727
+ return normalized || null;
38728
+ }
38729
+ getExpansionDetailDiagramSourceLabel() {
38730
+ return this.resolveTableMessage('table.expansion.embed.diagramSource', 'Fonte do diagrama');
38731
+ }
38732
+ onExpansionDetailEmbedAction(action, row, index, event) {
38733
+ const actionId = String(action?.actionId || '').trim();
38734
+ event.stopPropagation();
38735
+ if (!actionId) {
38736
+ return;
38737
+ }
38738
+ const context = this.getExpansionDetailRichContentContext(row, index);
38739
+ this.onRowAction(actionId, row, event, this.resolveRowActionRuntimeOptions({
38740
+ actionConfig: action,
38741
+ payload: this.evaluateExpansionDetailRichListItemPayload(action?.payloadExpr, context, row),
38742
+ }, action));
38743
+ }
38744
+ isExpansionDetailEmbedActionVisible(action, context) {
38745
+ const visibleWhen = action?.visibleWhen;
38746
+ if (!this.hasConditionalExpression(visibleWhen)) {
38747
+ return true;
38748
+ }
38749
+ return this.evaluateExpansionDetailRichListItemCondition(visibleWhen, context, this.resolveRowVisibleWhenFailurePolicy() === 'fail-open');
38750
+ }
38751
+ getExpansionDetailEmbedLabel(node) {
38752
+ const type = this.getExpansionDetailNodeType(node);
38753
+ switch (type) {
38754
+ case 'formRef':
38755
+ return this.resolveTableMessage('table.expansion.embed.formRef.label', 'Formulário governado');
38756
+ case 'tableRef':
38757
+ return this.resolveTableMessage('table.expansion.embed.tableRef.label', 'Tabela governada');
38758
+ case 'chartRef':
38759
+ return this.resolveTableMessage('table.expansion.embed.chartRef.label', 'Gráfico governado');
38760
+ case 'templateRef':
38761
+ return this.resolveTableMessage('table.expansion.embed.templateRef.label', 'Template governado');
38762
+ case 'diagramEmbed':
38763
+ return this.resolveTableMessage('table.expansion.embed.diagramEmbed.label', 'Diagrama governado');
38764
+ default:
38765
+ return this.resolveTableMessage('table.expansion.embed.empty', 'Nenhum conteúdo de embed disponível.');
38766
+ }
38767
+ }
38768
+ getExpansionDetailEmbedReference(node) {
38769
+ const candidates = [
38770
+ node?.schemaId,
38771
+ node?.templateId,
38772
+ node?.tableId,
38773
+ node?.chartId,
38774
+ node?.formId,
38775
+ node?.id,
38776
+ ];
38777
+ for (const candidate of candidates) {
38778
+ const normalized = String(candidate ?? '').trim();
38779
+ if (normalized) {
38780
+ return normalized;
38781
+ }
38782
+ }
38783
+ return null;
38784
+ }
38785
+ getExpansionDetailDiagramProvider(node) {
38786
+ if (this.getExpansionDetailNodeType(node) !== 'diagramEmbed') {
38787
+ return null;
38788
+ }
38789
+ const normalized = String(node?.provider || '').trim();
38790
+ return normalized || null;
38791
+ }
38792
+ getExpansionDetailEmbedPresetLabel(node) {
38793
+ const presetRef = node?.presetRef;
38794
+ if (!presetRef || typeof presetRef !== 'object') {
38795
+ return null;
38796
+ }
38797
+ const namespace = String(presetRef.namespace || '').trim();
38798
+ const presetId = String(presetRef.presetId || '').trim();
38799
+ const version = String(presetRef.version || '').trim();
38800
+ if (!namespace || !presetId) {
38801
+ return null;
38802
+ }
38803
+ return version ? `${namespace}/${presetId}@${version}` : `${namespace}/${presetId}`;
38804
+ }
38805
+ getExpansionDetailEmbedInputsSummary(node) {
38806
+ const inputs = node?.inputs;
38807
+ if (!inputs || typeof inputs !== 'object' || Array.isArray(inputs)) {
38808
+ return null;
38809
+ }
38810
+ const keys = Object.keys(inputs).filter((key) => String(key || '').trim().length > 0);
38811
+ if (!keys.length) {
38812
+ return null;
38813
+ }
38814
+ if (keys.length <= 3) {
38815
+ return keys.join(', ');
38816
+ }
38817
+ return `${keys.slice(0, 3).join(', ')} +${keys.length - 3}`;
38818
+ }
37883
38819
  getExpansionDetailTextRichContentNodes(text, className) {
37884
38820
  const normalized = String(text ?? '').trim();
37885
38821
  if (!normalized) {
@@ -37969,6 +38905,13 @@ class PraxisTable {
37969
38905
  },
37970
38906
  ];
37971
38907
  }
38908
+ isExpansionDetailActionBarActionVisible(action, context) {
38909
+ const visibleWhen = action?.visibleWhen;
38910
+ if (!this.hasConditionalExpression(visibleWhen)) {
38911
+ return true;
38912
+ }
38913
+ return this.evaluateExpansionDetailRichListItemCondition(visibleWhen, context, this.resolveRowVisibleWhenFailurePolicy() === 'fail-open');
38914
+ }
37972
38915
  getExpansionDetailTabButtonRichContentNodes(tab) {
37973
38916
  const titleNode = {
37974
38917
  type: 'text',
@@ -38106,6 +39049,46 @@ class PraxisTable {
38106
39049
  }
38107
39050
  return null;
38108
39051
  }
39052
+ evaluateExpansionDetailRichListItemPayload(raw, context, fallback) {
39053
+ if (raw === null || raw === undefined) {
39054
+ return fallback;
39055
+ }
39056
+ const normalized = String(raw).trim();
39057
+ if (!normalized) {
39058
+ return fallback;
39059
+ }
39060
+ const result = this.computedExpressionEvaluator.evaluate(normalized, context);
39061
+ if (result.error) {
39062
+ return fallback;
39063
+ }
39064
+ if (result.value === undefined && !normalized.startsWith('=')) {
39065
+ const nested = this.getNestedPropertyValue(context, normalized);
39066
+ return nested === undefined ? fallback : nested;
39067
+ }
39068
+ return result.value;
39069
+ }
39070
+ isExpansionDetailRichListItemActionVisible(action, context) {
39071
+ const visibleWhen = action?.visibleWhen;
39072
+ if (!this.hasConditionalExpression(visibleWhen)) {
39073
+ return true;
39074
+ }
39075
+ return this.evaluateExpansionDetailRichListItemCondition(visibleWhen, context, this.resolveRowVisibleWhenFailurePolicy() === 'fail-open');
39076
+ }
39077
+ evaluateExpansionDetailRichListItemCondition(expression, context, fallbackResult) {
39078
+ if (!this.isJsonLogicExpression(expression)) {
39079
+ return fallbackResult;
39080
+ }
39081
+ try {
39082
+ return this.jsonLogic.matches({
39083
+ condition: expression,
39084
+ data: context,
39085
+ options: this.getJsonLogicEvaluationOptions(),
39086
+ });
39087
+ }
39088
+ catch {
39089
+ return fallbackResult;
39090
+ }
39091
+ }
38109
39092
  getExpansionDetailRichText(node) {
38110
39093
  const html = String(node?.html ?? node?.text ?? '').trim();
38111
39094
  if (!html)
@@ -38137,6 +39120,12 @@ class PraxisTable {
38137
39120
  }
38138
39121
  mapExpansionDetailNodeToRichContent(node, row, index) {
38139
39122
  const type = this.getExpansionDetailNodeType(node);
39123
+ if (type === 'mediaBlock') {
39124
+ return this.mapExpansionDetailMediaBlockNodeToRichContent(node);
39125
+ }
39126
+ if (type === 'timeline') {
39127
+ return this.mapExpansionDetailTimelineNodeToRichContent(node, row);
39128
+ }
38140
39129
  if (type === 'card') {
38141
39130
  return this.mapExpansionDetailCardNodeToRichContent(node, row, index);
38142
39131
  }
@@ -38148,6 +39137,26 @@ class PraxisTable {
38148
39137
  }
38149
39138
  return null;
38150
39139
  }
39140
+ mapExpansionDetailMediaBlockNodeToRichContent(node) {
39141
+ const avatar = this.normalizeExpansionDetailMediaBlockAvatar(node?.avatar);
39142
+ const title = this.normalizeExpansionDetailMediaBlockText(node?.title);
39143
+ const subtitle = this.normalizeExpansionDetailMediaBlockText(node?.subtitle);
39144
+ const meta = this.normalizeExpansionDetailMediaBlockMeta(node?.meta);
39145
+ const trailing = this.normalizeExpansionDetailMediaBlockTrailing(node?.trailing);
39146
+ if (!avatar && !title && !subtitle && !meta && trailing.length === 0) {
39147
+ return null;
39148
+ }
39149
+ return {
39150
+ type: 'mediaBlock',
39151
+ id: node?.id ? String(node.id) : undefined,
39152
+ className: 'pfx-expansion-node pfx-expansion-node-media-block',
39153
+ avatar: avatar ?? undefined,
39154
+ title: title ?? undefined,
39155
+ subtitle: subtitle ?? undefined,
39156
+ meta: meta ?? undefined,
39157
+ trailing: trailing.length ? trailing : undefined,
39158
+ };
39159
+ }
38151
39160
  mapExpansionDetailCardNodeToRichContent(node, row, index) {
38152
39161
  const mappedChildren = this.getExpansionDetailNodeChildren(node)
38153
39162
  .map((childNode) => this.mapExpansionDetailNodeToRichContent(childNode, row, index));
@@ -38170,13 +39179,20 @@ class PraxisTable {
38170
39179
  .map((childNode) => this.mapExpansionDetailNodeToRichContent(childNode, row, index));
38171
39180
  const items = [];
38172
39181
  for (const childNode of mappedChildren) {
38173
- if (!childNode || childNode.type === 'card' || childNode.type === 'preset') {
39182
+ if (!childNode
39183
+ || childNode.type === 'card'
39184
+ || childNode.type === 'preset'
39185
+ || childNode.type === 'mediaBlock'
39186
+ || childNode.type === 'timeline') {
38174
39187
  return null;
38175
39188
  }
38176
39189
  if (childNode.type === 'compose') {
38177
39190
  items.push(...childNode.items);
38178
39191
  continue;
38179
39192
  }
39193
+ if (!this.isExpansionDetailComposeItemNode(childNode)) {
39194
+ return null;
39195
+ }
38180
39196
  items.push(childNode);
38181
39197
  }
38182
39198
  if (!items.length) {
@@ -38210,6 +39226,16 @@ class PraxisTable {
38210
39226
  items: [labelNode, valueNode],
38211
39227
  };
38212
39228
  }
39229
+ mapExpansionDetailTimelineNodeToRichContent(node, row) {
39230
+ const timelineNode = this.buildExpansionDetailTimelineRichNode(row, node) || {
39231
+ type: 'timeline',
39232
+ items: [],
39233
+ };
39234
+ return {
39235
+ ...timelineNode,
39236
+ className: 'pfx-expansion-node pfx-expansion-node-timeline',
39237
+ };
39238
+ }
38213
39239
  getExpansionDetailTabId(row, index, tab, tabIndex) {
38214
39240
  const rowKey = this.resolveRowExpansionKey(row, index) || `index-${Math.max(0, index)}`;
38215
39241
  const rawTabId = String(tab?.id || `tab-${tabIndex + 1}`).trim();
@@ -38732,11 +39758,11 @@ class PraxisTable {
38732
39758
  message: null,
38733
39759
  };
38734
39760
  }
38735
- sanitizeExpansionDetailNodes(nodes, allowedNodes, fallbackNodePolicy, path) {
39761
+ sanitizeExpansionDetailNodes(nodes, allowedNodes, fallbackNodePolicy, path, permittedTypes) {
38736
39762
  const out = [];
38737
39763
  for (let i = 0; i < nodes.length; i += 1) {
38738
39764
  const node = nodes[i];
38739
- const sanitized = this.sanitizeExpansionDetailNode(node, allowedNodes, fallbackNodePolicy, `${path}[${i}]`);
39765
+ const sanitized = this.sanitizeExpansionDetailNode(node, allowedNodes, fallbackNodePolicy, `${path}[${i}]`, permittedTypes);
38740
39766
  if (!sanitized.ok) {
38741
39767
  return { ok: false, nodes: [], message: sanitized.message };
38742
39768
  }
@@ -38746,7 +39772,7 @@ class PraxisTable {
38746
39772
  }
38747
39773
  return { ok: true, nodes: out, message: null };
38748
39774
  }
38749
- sanitizeExpansionDetailNode(node, allowedNodes, fallbackNodePolicy, path) {
39775
+ sanitizeExpansionDetailNode(node, allowedNodes, fallbackNodePolicy, path, permittedTypes) {
38750
39776
  if (!node || typeof node !== 'object') {
38751
39777
  if (fallbackNodePolicy === 'renderPlaceholder') {
38752
39778
  return { ok: true, node: this.buildExpansionBlockedPlaceholderNode('node inválido', path), message: null };
@@ -38774,8 +39800,82 @@ class PraxisTable {
38774
39800
  message: `Node "${type}" não permitido por schemaContract.allowedNodes.`,
38775
39801
  };
38776
39802
  }
39803
+ if (permittedTypes && !permittedTypes.has(type)) {
39804
+ if (fallbackNodePolicy === 'renderPlaceholder') {
39805
+ return {
39806
+ ok: true,
39807
+ node: this.buildExpansionBlockedPlaceholderNode(`type "${type}" nao permitido neste contexto`, path),
39808
+ message: null,
39809
+ };
39810
+ }
39811
+ return {
39812
+ ok: false,
39813
+ node: null,
39814
+ message: `Node "${type}" nao permitido em ${path}.`,
39815
+ };
39816
+ }
38777
39817
  const clone = { ...node, type };
38778
- const childKeys = ['content', 'items'];
39818
+ if (type === 'compose') {
39819
+ const sanitizedItems = this.sanitizeExpansionDetailNodes(Array.isArray(clone.items) ? clone.items : [], allowedNodes, fallbackNodePolicy, `${path}.items`, this.getExpansionDetailComposeItemTypes());
39820
+ if (!sanitizedItems.ok) {
39821
+ return {
39822
+ ok: false,
39823
+ node: null,
39824
+ message: sanitizedItems.message,
39825
+ };
39826
+ }
39827
+ clone.items = sanitizedItems.nodes;
39828
+ }
39829
+ if (type === 'mediaBlock') {
39830
+ const sanitizedMediaBlock = this.sanitizeExpansionDetailMediaBlockNode(clone, fallbackNodePolicy, path);
39831
+ if (!sanitizedMediaBlock.ok) {
39832
+ return {
39833
+ ok: false,
39834
+ node: null,
39835
+ message: sanitizedMediaBlock.message,
39836
+ };
39837
+ }
39838
+ return {
39839
+ ok: true,
39840
+ node: sanitizedMediaBlock.node,
39841
+ message: null,
39842
+ };
39843
+ }
39844
+ if (type === 'cardGrid') {
39845
+ const sanitizedCards = this.sanitizeExpansionDetailCardGridCards(clone.cards, allowedNodes, fallbackNodePolicy, `${path}.cards`);
39846
+ if (!sanitizedCards.ok) {
39847
+ return {
39848
+ ok: false,
39849
+ node: null,
39850
+ message: sanitizedCards.message,
39851
+ };
39852
+ }
39853
+ clone.cards = sanitizedCards.cards;
39854
+ }
39855
+ if (type === 'diagramEmbed') {
39856
+ const provider = String(clone.provider || '').trim();
39857
+ if (provider !== 'mermaid' && provider !== 'bpmn' && provider !== 'custom') {
39858
+ if (fallbackNodePolicy === 'renderPlaceholder') {
39859
+ return {
39860
+ ok: true,
39861
+ node: this.buildExpansionBlockedPlaceholderNode(`provider inválido para diagramEmbed`, path),
39862
+ message: null,
39863
+ };
39864
+ }
39865
+ return {
39866
+ ok: false,
39867
+ node: null,
39868
+ message: `provider inválido para diagramEmbed em ${path}.`,
39869
+ };
39870
+ }
39871
+ }
39872
+ const childKeys = [];
39873
+ if (type === 'layout' || type === 'stack' || type === 'tabs') {
39874
+ childKeys.push('items');
39875
+ }
39876
+ if (type === 'tab' || type === 'card') {
39877
+ childKeys.push('content');
39878
+ }
38779
39879
  for (const childKey of childKeys) {
38780
39880
  const rawChildren = clone[childKey];
38781
39881
  if (!Array.isArray(rawChildren))
@@ -38792,6 +39892,121 @@ class PraxisTable {
38792
39892
  }
38793
39893
  return { ok: true, node: clone, message: null };
38794
39894
  }
39895
+ sanitizeExpansionDetailMediaBlockNode(candidate, fallbackNodePolicy, path) {
39896
+ const avatar = this.normalizeExpansionDetailMediaBlockAvatar(candidate?.avatar);
39897
+ const title = this.normalizeExpansionDetailMediaBlockText(candidate?.title);
39898
+ const subtitle = this.normalizeExpansionDetailMediaBlockText(candidate?.subtitle);
39899
+ const meta = this.normalizeExpansionDetailMediaBlockMeta(candidate?.meta);
39900
+ const trailing = this.normalizeExpansionDetailMediaBlockTrailing(candidate?.trailing);
39901
+ if (!avatar && !title && !subtitle && !meta && trailing.length === 0) {
39902
+ if (fallbackNodePolicy === 'renderPlaceholder') {
39903
+ return {
39904
+ ok: true,
39905
+ node: {
39906
+ type: 'mediaBlock',
39907
+ title: { type: 'text', text: 'Node bloqueado' },
39908
+ subtitle: { type: 'text', text: `mediaBlock inválido (${path})` },
39909
+ },
39910
+ message: null,
39911
+ };
39912
+ }
39913
+ return { ok: false, node: null, message: `mediaBlock inválido em ${path}.` };
39914
+ }
39915
+ return {
39916
+ ok: true,
39917
+ node: {
39918
+ ...candidate,
39919
+ type: 'mediaBlock',
39920
+ avatar: avatar ?? undefined,
39921
+ title: title ?? undefined,
39922
+ subtitle: subtitle ?? undefined,
39923
+ meta: meta ?? undefined,
39924
+ trailing: trailing.length ? trailing : undefined,
39925
+ },
39926
+ message: null,
39927
+ };
39928
+ }
39929
+ sanitizeExpansionDetailCardGridCards(cards, allowedNodes, fallbackNodePolicy, path) {
39930
+ if (!Array.isArray(cards)) {
39931
+ if (fallbackNodePolicy === 'renderPlaceholder') {
39932
+ return {
39933
+ ok: true,
39934
+ cards: [
39935
+ {
39936
+ title: 'Node bloqueado',
39937
+ subtitle: `cards inválidos (${path})`,
39938
+ content: [{ type: 'text', text: 'Node bloqueado' }],
39939
+ },
39940
+ ],
39941
+ message: null,
39942
+ };
39943
+ }
39944
+ return { ok: false, cards: [], message: `cards inválidos em ${path}.` };
39945
+ }
39946
+ const out = [];
39947
+ for (let i = 0; i < cards.length; i += 1) {
39948
+ const candidate = cards[i];
39949
+ if (!candidate || typeof candidate !== 'object') {
39950
+ if (fallbackNodePolicy === 'renderPlaceholder') {
39951
+ out.push({
39952
+ title: 'Node bloqueado',
39953
+ subtitle: `card inválido (${path}[${i}])`,
39954
+ content: [{ type: 'text', text: 'Node bloqueado' }],
39955
+ });
39956
+ continue;
39957
+ }
39958
+ return { ok: false, cards: [], message: `card inválido em ${path}[${i}].` };
39959
+ }
39960
+ const sanitizedContent = this.sanitizeExpansionDetailNodes(Array.isArray(candidate.content) ? candidate.content : [], allowedNodes, fallbackNodePolicy, `${path}[${i}].content`, this.getExpansionDetailCardGridContentTypes());
39961
+ if (!sanitizedContent.ok) {
39962
+ return {
39963
+ ok: false,
39964
+ cards: [],
39965
+ message: sanitizedContent.message,
39966
+ };
39967
+ }
39968
+ if (!sanitizedContent.nodes.length) {
39969
+ if (fallbackNodePolicy === 'renderPlaceholder') {
39970
+ out.push({
39971
+ id: candidate.id,
39972
+ title: candidate.title || 'Node bloqueado',
39973
+ subtitle: `content inválido (${path}[${i}].content)`,
39974
+ content: [{ type: 'text', text: 'Node bloqueado' }],
39975
+ });
39976
+ continue;
39977
+ }
39978
+ return { ok: false, cards: [], message: `content inválido em ${path}[${i}].content.` };
39979
+ }
39980
+ out.push({
39981
+ ...candidate,
39982
+ content: sanitizedContent.nodes,
39983
+ });
39984
+ }
39985
+ return { ok: true, cards: out, message: null };
39986
+ }
39987
+ getExpansionDetailCardGridContentTypes() {
39988
+ return new Set([
39989
+ 'text',
39990
+ 'icon',
39991
+ 'image',
39992
+ 'badge',
39993
+ 'avatar',
39994
+ 'metric',
39995
+ 'progress',
39996
+ 'compose',
39997
+ ]);
39998
+ }
39999
+ getExpansionDetailComposeItemTypes() {
40000
+ return new Set([
40001
+ 'text',
40002
+ 'icon',
40003
+ 'image',
40004
+ 'badge',
40005
+ 'avatar',
40006
+ 'metric',
40007
+ 'progress',
40008
+ ]);
40009
+ }
38795
40010
  buildExpansionBlockedPlaceholderNode(reason, path) {
38796
40011
  return {
38797
40012
  type: 'card',
@@ -40432,6 +41647,14 @@ class PraxisTable {
40432
41647
  tableId: this.tableId,
40433
41648
  config: this.config,
40434
41649
  });
41650
+ if (this.crudContext && typeof this.crudContext.openAuthoring === 'function') {
41651
+ this.debugLog('[PraxisTable] Delegating settings entrypoint to CRUD authoring shell', {
41652
+ tableId: this.tableId,
41653
+ componentKeyId: this.componentKeyId(),
41654
+ });
41655
+ this.crudContext.openAuthoring();
41656
+ return;
41657
+ }
40435
41658
  const configCopy = JSON.parse(JSON.stringify(this.config));
40436
41659
  const componentKeyId = this.componentKeyId();
40437
41660
  const panelInputs = {
@@ -42903,6 +44126,45 @@ class PraxisTable {
42903
44126
  }
42904
44127
  return ctx;
42905
44128
  }
44129
+ getJsonLogicEvaluationOptions() {
44130
+ const userTimeZone = this.resolveJsonLogicUserTimeZone();
44131
+ return userTimeZone ? { userTimeZone } : {};
44132
+ }
44133
+ resolveJsonLogicUserTimeZone() {
44134
+ const configAny = this.config;
44135
+ const candidates = [
44136
+ configAny?.jsonLogic?.userTimeZone,
44137
+ configAny?.jsonLogic?.timeZone,
44138
+ configAny?.rules?.userTimeZone,
44139
+ configAny?.rules?.timeZone,
44140
+ configAny?.localization?.timeZone,
44141
+ configAny?.localization?.timezone,
44142
+ configAny?.localization?.dateTime?.timeZone,
44143
+ configAny?.localization?.dateTime?.timezone,
44144
+ ];
44145
+ try {
44146
+ candidates.push(this.global.get('table.jsonLogic.userTimeZone'));
44147
+ candidates.push(this.global.get('table.jsonLogic.timeZone'));
44148
+ candidates.push(this.global.get('table.localization.timeZone'));
44149
+ candidates.push(this.global.get('table.localization.timezone'));
44150
+ candidates.push(this.global.get('table.localization.dateTime.timeZone'));
44151
+ candidates.push(this.global.get('table.localization.dateTime.timezone'));
44152
+ }
44153
+ catch { }
44154
+ for (const candidate of candidates) {
44155
+ if (typeof candidate !== 'string')
44156
+ continue;
44157
+ const normalized = candidate.trim();
44158
+ if (normalized)
44159
+ return normalized;
44160
+ }
44161
+ try {
44162
+ return Intl.DateTimeFormat().resolvedOptions().timeZone || undefined;
44163
+ }
44164
+ catch {
44165
+ return undefined;
44166
+ }
44167
+ }
42906
44168
  isUnsafeComputedField(field) {
42907
44169
  const blocked = ['__proto__', 'prototype', 'constructor'];
42908
44170
  return field.split('.').some((seg) => blocked.includes(seg));
@@ -42917,6 +44179,7 @@ class PraxisTable {
42917
44179
  return this.jsonLogic.matches({
42918
44180
  condition: expression,
42919
44181
  data: this.buildEvaluationContext(rowData),
44182
+ options: this.getJsonLogicEvaluationOptions(),
42920
44183
  });
42921
44184
  }
42922
44185
  catch (err) {
@@ -42951,6 +44214,7 @@ class PraxisTable {
42951
44214
  return this.jsonLogic.matches({
42952
44215
  condition: expression,
42953
44216
  data: this.buildEvaluationContext(rowData),
44217
+ options: this.getJsonLogicEvaluationOptions(),
42954
44218
  });
42955
44219
  }
42956
44220
  catch (err) {
@@ -43725,6 +44989,7 @@ class PraxisTable {
43725
44989
  availableRoots: ['row', 'computed'],
43726
44990
  defaultRoot: 'row',
43727
44991
  allowImplicitRoot: true,
44992
+ ...this.getJsonLogicEvaluationOptions(),
43728
44993
  });
43729
44994
  }
43730
44995
  catch (error) {
@@ -44160,6 +45425,31 @@ class PraxisTable {
44160
45425
  return null;
44161
45426
  return (cfg.altField ? this.getNestedPropertyValue(row, cfg.altField) : cfg.alt) || null;
44162
45427
  }
45428
+ getImageRichContentNodes(row, column) {
45429
+ const shape = this.getImageShape(row, column);
45430
+ const width = this.getImageWidth(row, column);
45431
+ const height = this.getImageHeight(row, column);
45432
+ const fit = this.getImageFit(row, column);
45433
+ const style = {};
45434
+ if (width != null)
45435
+ style['width'] = `${width}px`;
45436
+ if (height != null)
45437
+ style['height'] = `${height}px`;
45438
+ if (fit)
45439
+ style['objectFit'] = fit;
45440
+ const node = {
45441
+ type: 'image',
45442
+ src: this.getImageSrc(row, column) ?? undefined,
45443
+ alt: this.getImageAlt(row, column) ?? '',
45444
+ className: shape === 'rounded'
45445
+ ? 'shape-rounded'
45446
+ : shape === 'circle'
45447
+ ? 'shape-circle'
45448
+ : undefined,
45449
+ style,
45450
+ };
45451
+ return [node];
45452
+ }
44163
45453
  getImageWidth(row, column) {
44164
45454
  if (this.isStrictCspStyleModeEnabled())
44165
45455
  return null;
@@ -44682,6 +45972,24 @@ class PraxisTable {
44682
45972
  getProgressShowLabel(row, column) {
44683
45973
  return !!this.getEffectiveRenderer(row, column)?.progress?.showLabel;
44684
45974
  }
45975
+ getProgressRichContentContext(row, column) {
45976
+ const value = this.getProgressValue(row, column);
45977
+ return {
45978
+ value,
45979
+ label: this.getProgressShowLabel(row, column) ? `${value}%` : null,
45980
+ };
45981
+ }
45982
+ getProgressRichContentNodes(row, column) {
45983
+ return [
45984
+ {
45985
+ type: 'progress',
45986
+ valueExpr: 'value',
45987
+ labelExpr: 'label',
45988
+ max: 100,
45989
+ className: 'pfx-progress__node',
45990
+ },
45991
+ ];
45992
+ }
44685
45993
  // Rating
44686
45994
  getRatingValue(row, column) {
44687
45995
  const cfg = this.getEffectiveRenderer(row, column)?.rating;
@@ -44819,6 +46127,14 @@ class PraxisTable {
44819
46127
  getMenuAriaLabel(row, column) {
44820
46128
  return this.getEffectiveRenderer(row, column)?.menu?.ariaLabel || null;
44821
46129
  }
46130
+ getMenuTriggerRichContentNodes() {
46131
+ const node = {
46132
+ type: 'icon',
46133
+ icon: 'more_vert',
46134
+ className: 'pfx-menu-trigger__icon',
46135
+ };
46136
+ return [node];
46137
+ }
44822
46138
  evaluateArrayExpr(raw, row) {
44823
46139
  const expr = this.extractExpression(raw, true);
44824
46140
  if (!expr)
@@ -45413,7 +46729,7 @@ class PraxisTable {
45413
46729
  this.resizeObserver = new ResizeObserver((entries) => {
45414
46730
  for (const entry of entries) {
45415
46731
  if (entry.target === this.hostRef.nativeElement) {
45416
- this.refreshResponsiveHorizontalScroll();
46732
+ this.scheduleResponsiveHorizontalScrollRefresh();
45417
46733
  }
45418
46734
  }
45419
46735
  });
@@ -45868,8 +47184,8 @@ class PraxisTable {
45868
47184
  }
45869
47185
  getRowMenuTooltip(row) {
45870
47186
  return this.isRowDiscoveryPending(row)
45871
- ? translateResourceDiscoveryText(this.i18n, 'rowActions.menu.loading', undefined, 'Carregando ações contextuais')
45872
- : translateResourceDiscoveryText(this.i18n, 'rowActions.menu.more', undefined, 'Mais ações');
47187
+ ? translateTableRuntimeText(this.i18n, 'table.chrome.rowActions.loading', 'Carregando ações contextuais')
47188
+ : translateTableRuntimeText(this.i18n, 'table.chrome.rowActions.more', 'Mais ações');
45873
47189
  }
45874
47190
  getRowMenuButtonColor() {
45875
47191
  return this.config.actions?.row?.menuButtonColor ?? undefined;
@@ -45903,6 +47219,7 @@ class PraxisTable {
45903
47219
  if (this.resizeObserver) {
45904
47220
  this.resizeObserver.disconnect();
45905
47221
  }
47222
+ this.cancelResponsiveHorizontalScrollRefresh();
45906
47223
  this.removeViewportChangeListeners?.();
45907
47224
  this.removeViewportChangeListeners = null;
45908
47225
  }
@@ -45914,7 +47231,7 @@ class PraxisTable {
45914
47231
  TableDefaultsProvider,
45915
47232
  FilterConfigService,
45916
47233
  DataFormattingService
45917
- ], 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 (!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()\" aria-label=\"Configura\u00C3\u00A7\u00C3\u00B5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u00E2\u20AC\u201D Reconciliar' : 'Configura\u00C3\u00A7\u00C3\u00B5es'\"\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\u00C3\u00A7\u00C3\u00B5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u00E2\u20AC\u201D Reconciliar' : 'Configura\u00C3\u00A7\u00C3\u00B5es'\"\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\u00C3\u00A7\u00C3\u00B5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u00E2\u20AC\u201D Reconciliar' : 'Configura\u00C3\u00A7\u00C3\u00B5es'\"\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\u00C3\u00A7\u00C3\u00B5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u00E2\u20AC\u201D Reconciliar' : 'Configura\u00C3\u00A7\u00C3\u00B5es'\"\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 <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 <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 <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 <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 <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 <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 <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))\"><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\"><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 <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'\"><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 <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 aria-label=\"Mais a\u00C3\u00A7\u00C3\u00B5es\"\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 ('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 <praxis-rich-content\n rootClassName=\"pfx-expansion-node-placeholder__content\"\n [nodes]=\"getExpansionDetailTextRichContentNodes('Formul\u00E1rio referenciado:', 'pfx-expansion-node-placeholder__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <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 <praxis-rich-content\n rootClassName=\"pfx-expansion-node-placeholder__content\"\n [nodes]=\"getExpansionDetailTextRichContentNodes('Tabela referenciada:', 'pfx-expansion-node-placeholder__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <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 <praxis-rich-content\n rootClassName=\"pfx-expansion-node-placeholder__content\"\n [nodes]=\"getExpansionDetailTextRichContentNodes('Gr\u00E1fico referenciado:', 'pfx-expansion-node-placeholder__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <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 <praxis-rich-content\n rootClassName=\"pfx-expansion-node-placeholder__content\"\n [nodes]=\"getExpansionDetailTextRichContentNodes('Template referenciado:', 'pfx-expansion-node-placeholder__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <code>{{ node?.id || node?.templateId || 'sem-id' }}</code>\n </div>\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 ('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 <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 <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 aria-label=\"Mais a\u00C3\u00A7\u00C3\u00B5es\"\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 (\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 aria-label=\"Configura\u00C3\u00A7\u00C3\u00B5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\"\n matBadgeSize=\"small\"\n matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u00E2\u20AC\u201D Reconciliar' : 'Configura\u00C3\u00A7\u00C3\u00B5es'\"\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-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: 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)]] });
47234
+ ], 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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\" 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 [layout]=\"'inline'\" 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 [layout]=\"'inline'\" 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 [layout]=\"'inline'\" 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 [layout]=\"'inline'\" 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 [layout]=\"'inline'\" 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 [layout]=\"'inline'\" 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 ('card') {\n <section class=\"pfx-expansion-node pfx-expansion-node-card\">\n <header class=\"pfx-expansion-node-card__header\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-card__title\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(getExpansionDetailNodeTitle(node, 'Card'), 'pfx-expansion-node-card__title-text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n @if (node?.subtitle) {\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-card__subtitle\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(node.subtitle, 'pfx-expansion-node-card__subtitle-text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n }\n </header>\n @if (getExpansionDetailNodeChildren(node).length) {\n <div class=\"pfx-expansion-node-card__content\">\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 </div>\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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-card__content{display:grid;gap:10px}.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;white-space:nowrap;--prx-rich-content-inline-gap: 6px}.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;white-space:nowrap;--prx-rich-content-inline-gap: 6px}.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", "layout", "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)]] });
45918
47235
  }
45919
47236
  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: [{
45920
47237
  type: Component,
@@ -45948,7 +47265,7 @@ i0.ɵɵngDeclareClassMetadataAsync({ minVersion: "18.0.0", version: "20.3.17", n
45948
47265
  TableDefaultsProvider,
45949
47266
  FilterConfigService,
45950
47267
  DataFormattingService
45951
- ], 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 (!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()\" aria-label=\"Configura\u00C3\u00A7\u00C3\u00B5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u00E2\u20AC\u201D Reconciliar' : 'Configura\u00C3\u00A7\u00C3\u00B5es'\"\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\u00C3\u00A7\u00C3\u00B5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u00E2\u20AC\u201D Reconciliar' : 'Configura\u00C3\u00A7\u00C3\u00B5es'\"\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\u00C3\u00A7\u00C3\u00B5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u00E2\u20AC\u201D Reconciliar' : 'Configura\u00C3\u00A7\u00C3\u00B5es'\"\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\u00C3\u00A7\u00C3\u00B5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\" matBadgeSize=\"small\" matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u00E2\u20AC\u201D Reconciliar' : 'Configura\u00C3\u00A7\u00C3\u00B5es'\"\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 <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 <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 <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 <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 <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 <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 <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))\"><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\"><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 <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'\"><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 <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 aria-label=\"Mais a\u00C3\u00A7\u00C3\u00B5es\"\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 ('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 <praxis-rich-content\n rootClassName=\"pfx-expansion-node-placeholder__content\"\n [nodes]=\"getExpansionDetailTextRichContentNodes('Formul\u00E1rio referenciado:', 'pfx-expansion-node-placeholder__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <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 <praxis-rich-content\n rootClassName=\"pfx-expansion-node-placeholder__content\"\n [nodes]=\"getExpansionDetailTextRichContentNodes('Tabela referenciada:', 'pfx-expansion-node-placeholder__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <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 <praxis-rich-content\n rootClassName=\"pfx-expansion-node-placeholder__content\"\n [nodes]=\"getExpansionDetailTextRichContentNodes('Gr\u00E1fico referenciado:', 'pfx-expansion-node-placeholder__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <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 <praxis-rich-content\n rootClassName=\"pfx-expansion-node-placeholder__content\"\n [nodes]=\"getExpansionDetailTextRichContentNodes('Template referenciado:', 'pfx-expansion-node-placeholder__text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n <code>{{ node?.id || node?.templateId || 'sem-id' }}</code>\n </div>\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 ('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 <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 <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 aria-label=\"Mais a\u00C3\u00A7\u00C3\u00B5es\"\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 (\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 aria-label=\"Configura\u00C3\u00A7\u00C3\u00B5es\"\n [matBadge]=\"schemaOutdated ? '!' : ''\"\n [matBadgeHidden]=\"!schemaOutdated\"\n matBadgeColor=\"warn\"\n matBadgeSize=\"small\"\n matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u00E2\u20AC\u201D Reconciliar' : 'Configura\u00C3\u00A7\u00C3\u00B5es'\"\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-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"] }]
47268
+ ], 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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\" 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 [layout]=\"'inline'\" 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 [layout]=\"'inline'\" 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 [layout]=\"'inline'\" 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 [layout]=\"'inline'\" 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 [layout]=\"'inline'\" 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 [layout]=\"'inline'\" 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 ('card') {\n <section class=\"pfx-expansion-node pfx-expansion-node-card\">\n <header class=\"pfx-expansion-node-card__header\">\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-card__title\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(getExpansionDetailNodeTitle(node, 'Card'), 'pfx-expansion-node-card__title-text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n @if (node?.subtitle) {\n <praxis-rich-content\n rootClassName=\"pfx-expansion-node-card__subtitle\"\n [nodes]=\"getExpansionDetailTextRichContentNodes(node.subtitle, 'pfx-expansion-node-card__subtitle-text')\"\n [context]=\"getExpansionDetailRichContentContext(row, index)\"\n ></praxis-rich-content>\n }\n </header>\n @if (getExpansionDetailNodeChildren(node).length) {\n <div class=\"pfx-expansion-node-card__content\">\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 </div>\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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 [layout]=\"'inline'\"\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-card__content{display:grid;gap:10px}.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;white-space:nowrap;--prx-rich-content-inline-gap: 6px}.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;white-space:nowrap;--prx-rich-content-inline-gap: 6px}.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"] }]
45952
47269
  }], 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: [{
45953
47270
  type: Inject,
45954
47271
  args: [ASYNC_CONFIG_STORAGE]
@@ -46541,6 +47858,1250 @@ function normalizeGap(gap) {
46541
47858
  }
46542
47859
  }
46543
47860
 
47861
+ class PraxisTableInlineAuthoringEditorComponent {
47862
+ i18n;
47863
+ config = { columns: [] };
47864
+ readonly = false;
47865
+ configChange = new EventEmitter();
47866
+ workingConfig = { columns: [] };
47867
+ toolbarTitle = '';
47868
+ toolbarSubtitle = '';
47869
+ toolbarVisible = true;
47870
+ density = 'comfortable';
47871
+ toolbarActionsPosition = 'top';
47872
+ toolbarHeight = 64;
47873
+ toolbarActionsBackgroundColor = '';
47874
+ paginationEnabled = true;
47875
+ pageSize = 10;
47876
+ pageSizeOptionsText = '5, 10, 25, 50';
47877
+ showFirstLastButtons = true;
47878
+ paginationStrategy = 'client';
47879
+ paginationPosition = 'bottom';
47880
+ paginationStyle = 'default';
47881
+ sortingEnabled = true;
47882
+ sortingStrategy = 'client';
47883
+ allowClearSort = true;
47884
+ defaultSortField = '';
47885
+ defaultSortDirection = 'asc';
47886
+ loadingMessage = '';
47887
+ emptyMessage = '';
47888
+ noResultsMessage = '';
47889
+ errorMessage = '';
47890
+ loadingMoreMessage = '';
47891
+ lastInputSignature = '';
47892
+ constructor(i18n) {
47893
+ this.i18n = i18n;
47894
+ }
47895
+ ngOnChanges() {
47896
+ const signature = JSON.stringify(this.config || { columns: [] });
47897
+ if (signature === this.lastInputSignature) {
47898
+ return;
47899
+ }
47900
+ this.lastInputSignature = signature;
47901
+ this.workingConfig = cloneTableConfig(this.config);
47902
+ this.toolbarTitle = this.workingConfig.toolbar?.title || '';
47903
+ this.toolbarSubtitle = this.workingConfig.toolbar?.subtitle || '';
47904
+ this.toolbarVisible = this.workingConfig.toolbar?.visible !== false;
47905
+ this.density = this.workingConfig.appearance?.density || 'comfortable';
47906
+ this.toolbarActionsPosition =
47907
+ this.workingConfig.toolbar?.actionsPosition
47908
+ || this.workingConfig.toolbar?.position
47909
+ || 'top';
47910
+ this.toolbarHeight = this.workingConfig.toolbar?.layout?.height || 64;
47911
+ this.toolbarActionsBackgroundColor = this.workingConfig.toolbar?.actionsBackgroundColor || '';
47912
+ this.paginationEnabled = this.workingConfig.behavior?.pagination?.enabled !== false;
47913
+ this.pageSize = this.workingConfig.behavior?.pagination?.pageSize || 10;
47914
+ this.pageSizeOptionsText = this.arrayToString(this.workingConfig.behavior?.pagination?.pageSizeOptions || [5, 10, 25, 50]);
47915
+ this.showFirstLastButtons =
47916
+ this.workingConfig.behavior?.pagination?.showFirstLastButtons !== false;
47917
+ this.paginationStrategy = this.workingConfig.behavior?.pagination?.strategy || 'client';
47918
+ this.paginationPosition = this.workingConfig.behavior?.pagination?.position || 'bottom';
47919
+ this.paginationStyle = this.workingConfig.behavior?.pagination?.style || 'default';
47920
+ this.sortingEnabled = this.workingConfig.behavior?.sorting?.enabled !== false;
47921
+ this.sortingStrategy = this.workingConfig.behavior?.sorting?.strategy || 'client';
47922
+ this.allowClearSort = this.workingConfig.behavior?.sorting?.allowClearSort !== false;
47923
+ this.defaultSortField = this.getDefaultSortField(this.workingConfig);
47924
+ this.defaultSortDirection = this.getDefaultSortDirection(this.workingConfig);
47925
+ this.loadingMessage =
47926
+ this.workingConfig.messages?.states?.loading
47927
+ || this.tx('messages.loading.placeholder', 'Loading...');
47928
+ this.emptyMessage =
47929
+ this.workingConfig.messages?.states?.empty
47930
+ || this.tx('messages.empty.placeholder', 'No data available');
47931
+ this.noResultsMessage =
47932
+ this.workingConfig.messages?.states?.noResults
47933
+ || this.tx('messages.noResults.placeholder', 'No results found');
47934
+ this.errorMessage =
47935
+ this.workingConfig.messages?.states?.error
47936
+ || this.tx('messages.error.placeholder', 'Error loading data');
47937
+ this.loadingMoreMessage =
47938
+ this.workingConfig.messages?.states?.loadingMore
47939
+ || this.tx('messages.loadingMore.placeholder', 'Loading more data...');
47940
+ }
47941
+ setToolbarTitle(value) {
47942
+ this.toolbarTitle = value || '';
47943
+ this.patchToolbar({ title: this.toolbarTitle || undefined });
47944
+ }
47945
+ setToolbarSubtitle(value) {
47946
+ this.toolbarSubtitle = value || '';
47947
+ this.patchToolbar({ subtitle: this.toolbarSubtitle || undefined });
47948
+ }
47949
+ setToolbarVisible(value) {
47950
+ this.toolbarVisible = !!value;
47951
+ this.patchToolbar({ visible: this.toolbarVisible });
47952
+ }
47953
+ setDensity(value) {
47954
+ this.density = value || 'comfortable';
47955
+ this.workingConfig = {
47956
+ ...this.workingConfig,
47957
+ appearance: {
47958
+ ...(this.workingConfig.appearance || {}),
47959
+ density: this.density,
47960
+ },
47961
+ };
47962
+ this.emitConfig();
47963
+ }
47964
+ setToolbarActionsPosition(value) {
47965
+ this.toolbarActionsPosition = value || 'top';
47966
+ this.patchToolbar({
47967
+ actionsPosition: this.toolbarActionsPosition,
47968
+ position: this.toolbarActionsPosition,
47969
+ });
47970
+ }
47971
+ setToolbarHeight(value) {
47972
+ const nextHeight = clampNumber(value, 40, 120, 64);
47973
+ this.toolbarHeight = nextHeight;
47974
+ this.patchToolbar({
47975
+ layout: {
47976
+ alignment: this.workingConfig.toolbar?.layout?.alignment || 'space-between',
47977
+ showSeparator: this.workingConfig.toolbar?.layout?.showSeparator ?? false,
47978
+ ...(this.workingConfig.toolbar?.layout || {}),
47979
+ height: nextHeight,
47980
+ },
47981
+ });
47982
+ }
47983
+ setToolbarActionsBackgroundColor(value) {
47984
+ this.toolbarActionsBackgroundColor = value || '';
47985
+ this.patchToolbar({
47986
+ actionsBackgroundColor: this.toolbarActionsBackgroundColor || undefined,
47987
+ });
47988
+ }
47989
+ setPaginationEnabled(value) {
47990
+ this.paginationEnabled = !!value;
47991
+ this.patchPagination();
47992
+ }
47993
+ setPaginationPageSize(value) {
47994
+ this.pageSize = clampNumber(value, 1, 500, 10);
47995
+ this.patchPagination();
47996
+ }
47997
+ setPaginationPageSizeOptions(value) {
47998
+ this.pageSizeOptionsText = value || '';
47999
+ this.patchPagination();
48000
+ }
48001
+ setPaginationShowFirstLastButtons(value) {
48002
+ this.showFirstLastButtons = !!value;
48003
+ this.patchPagination();
48004
+ }
48005
+ setPaginationStrategy(value) {
48006
+ this.paginationStrategy = value || 'client';
48007
+ this.patchPagination();
48008
+ }
48009
+ setPaginationPosition(value) {
48010
+ this.paginationPosition = value || 'bottom';
48011
+ this.patchPagination();
48012
+ }
48013
+ setPaginationStyle(value) {
48014
+ this.paginationStyle = value || 'default';
48015
+ this.patchPagination();
48016
+ }
48017
+ setSortingEnabled(value) {
48018
+ this.sortingEnabled = !!value;
48019
+ this.patchSorting();
48020
+ }
48021
+ setSortingStrategy(value) {
48022
+ this.sortingStrategy = value || 'client';
48023
+ this.patchSorting();
48024
+ }
48025
+ setSortingAllowClear(value) {
48026
+ this.allowClearSort = !!value;
48027
+ this.patchSorting();
48028
+ }
48029
+ setSortingDefaultField(value) {
48030
+ this.defaultSortField = value || '';
48031
+ this.patchSorting();
48032
+ }
48033
+ setSortingDefaultDirection(value) {
48034
+ this.defaultSortDirection = value || 'asc';
48035
+ this.patchSorting();
48036
+ }
48037
+ setStateMessage(key, value) {
48038
+ const nextValue = value || '';
48039
+ if (key === 'loading') {
48040
+ this.loadingMessage = nextValue;
48041
+ }
48042
+ else if (key === 'empty') {
48043
+ this.emptyMessage = nextValue;
48044
+ }
48045
+ else if (key === 'noResults') {
48046
+ this.noResultsMessage = nextValue;
48047
+ }
48048
+ else {
48049
+ this.errorMessage = nextValue;
48050
+ }
48051
+ this.patchMessages({
48052
+ states: {
48053
+ ...(this.workingConfig.messages?.states || {}),
48054
+ loading: this.loadingMessage,
48055
+ empty: this.emptyMessage,
48056
+ noResults: this.noResultsMessage,
48057
+ error: this.errorMessage,
48058
+ loadingMore: this.loadingMoreMessage,
48059
+ },
48060
+ });
48061
+ }
48062
+ onColumnsConfigChange(config) {
48063
+ this.workingConfig = cloneTableConfig(config);
48064
+ this.toolbarTitle = this.workingConfig.toolbar?.title || '';
48065
+ this.toolbarSubtitle = this.workingConfig.toolbar?.subtitle || '';
48066
+ this.toolbarVisible = this.workingConfig.toolbar?.visible !== false;
48067
+ this.density = this.workingConfig.appearance?.density || 'comfortable';
48068
+ this.toolbarActionsPosition =
48069
+ this.workingConfig.toolbar?.actionsPosition
48070
+ || this.workingConfig.toolbar?.position
48071
+ || 'top';
48072
+ this.toolbarHeight = this.workingConfig.toolbar?.layout?.height || 64;
48073
+ this.toolbarActionsBackgroundColor = this.workingConfig.toolbar?.actionsBackgroundColor || '';
48074
+ this.paginationEnabled = this.workingConfig.behavior?.pagination?.enabled !== false;
48075
+ this.pageSize = this.workingConfig.behavior?.pagination?.pageSize || 10;
48076
+ this.pageSizeOptionsText = this.arrayToString(this.workingConfig.behavior?.pagination?.pageSizeOptions || [5, 10, 25, 50]);
48077
+ this.showFirstLastButtons =
48078
+ this.workingConfig.behavior?.pagination?.showFirstLastButtons !== false;
48079
+ this.paginationStrategy = this.workingConfig.behavior?.pagination?.strategy || 'client';
48080
+ this.paginationPosition = this.workingConfig.behavior?.pagination?.position || 'bottom';
48081
+ this.paginationStyle = this.workingConfig.behavior?.pagination?.style || 'default';
48082
+ this.sortingEnabled = this.workingConfig.behavior?.sorting?.enabled !== false;
48083
+ this.sortingStrategy = this.workingConfig.behavior?.sorting?.strategy || 'client';
48084
+ this.allowClearSort = this.workingConfig.behavior?.sorting?.allowClearSort !== false;
48085
+ this.defaultSortField = this.getDefaultSortField(this.workingConfig);
48086
+ this.defaultSortDirection = this.getDefaultSortDirection(this.workingConfig);
48087
+ this.loadingMessage =
48088
+ this.workingConfig.messages?.states?.loading
48089
+ || this.tx('messages.loading.placeholder', 'Loading...');
48090
+ this.emptyMessage =
48091
+ this.workingConfig.messages?.states?.empty
48092
+ || this.tx('messages.empty.placeholder', 'No data available');
48093
+ this.noResultsMessage =
48094
+ this.workingConfig.messages?.states?.noResults
48095
+ || this.tx('messages.noResults.placeholder', 'No results found');
48096
+ this.errorMessage =
48097
+ this.workingConfig.messages?.states?.error
48098
+ || this.tx('messages.error.placeholder', 'Error loading data');
48099
+ this.loadingMoreMessage =
48100
+ this.workingConfig.messages?.states?.loadingMore
48101
+ || this.tx('messages.loadingMore.placeholder', 'Loading more data...');
48102
+ this.emitConfig();
48103
+ }
48104
+ columnCountLabel() {
48105
+ const count = this.workingConfig.columns?.length || 0;
48106
+ return this.tx('inlineAuthoring.columnCount', '{count} columns').replace('{count}', String(count));
48107
+ }
48108
+ toolbarSummary() {
48109
+ if (!this.toolbarVisible) {
48110
+ return this.tx('inlineAuthoring.summary.toolbarHidden', 'Hidden');
48111
+ }
48112
+ return this.toolbarTitle || this.tx('inlineAuthoring.summary.toolbarDefault', 'Visible without custom title');
48113
+ }
48114
+ densitySummary() {
48115
+ return {
48116
+ compact: this.tx('behavior.appearance.density.compact', 'Compact'),
48117
+ comfortable: this.tx('behavior.appearance.density.comfortable', 'Comfortable'),
48118
+ spacious: this.tx('behavior.appearance.density.spacious', 'Spacious'),
48119
+ }[this.density];
48120
+ }
48121
+ actionsPositionSummary() {
48122
+ return {
48123
+ top: this.tx('toolbar.actionsPosition.top', 'Above the table'),
48124
+ bottom: this.tx('toolbar.actionsPosition.bottom', 'Below the table'),
48125
+ both: this.tx('toolbar.actionsPosition.both', 'Top and bottom'),
48126
+ }[this.toolbarActionsPosition];
48127
+ }
48128
+ behaviorSummary() {
48129
+ const parts = [];
48130
+ if (this.paginationEnabled) {
48131
+ parts.push(`${this.tx('behavior.pagination.pageSize', 'Items per page')}: ${this.pageSize}`);
48132
+ }
48133
+ else {
48134
+ parts.push(this.tx('inlineAuthoring.summary.paginationOff', 'Pagination off'));
48135
+ }
48136
+ if (this.sortingEnabled) {
48137
+ parts.push(this.sortingStrategy === 'server'
48138
+ ? this.tx('behavior.sorting.strategy.server', 'Server (remote sorting)')
48139
+ : this.tx('behavior.sorting.strategy.client', 'Client (local sorting)'));
48140
+ if (this.defaultSortField) {
48141
+ parts.push(`${this.defaultSortField} ${this.defaultSortDirection}`);
48142
+ }
48143
+ }
48144
+ else {
48145
+ parts.push(this.tx('inlineAuthoring.summary.sortingOff', 'Sorting off'));
48146
+ }
48147
+ return parts.join(' • ');
48148
+ }
48149
+ statesSummary() {
48150
+ const customized = [
48151
+ this.loadingMessage !== this.tx('messages.loading.placeholder', 'Loading...'),
48152
+ this.emptyMessage !== this.tx('messages.empty.placeholder', 'No data available'),
48153
+ this.noResultsMessage !== this.tx('messages.noResults.placeholder', 'No results found'),
48154
+ this.errorMessage !== this.tx('messages.error.placeholder', 'Error loading data'),
48155
+ ].filter(Boolean).length;
48156
+ if (!customized) {
48157
+ return this.tx('inlineAuthoring.summary.statesDefault', 'Default copy');
48158
+ }
48159
+ return this.tx('inlineAuthoring.summary.statesCustom', '{count} customized states')
48160
+ .replace('{count}', String(customized));
48161
+ }
48162
+ columnsPreviewSummary() {
48163
+ const columns = this.workingConfig.columns || [];
48164
+ if (!columns.length) {
48165
+ return this.tx('inlineAuthoring.summary.columnsEmpty', 'No columns configured yet');
48166
+ }
48167
+ const preview = columns
48168
+ .slice(0, 3)
48169
+ .map((column) => column.header || column.field || '?')
48170
+ .join(', ');
48171
+ if (columns.length <= 3) {
48172
+ return preview;
48173
+ }
48174
+ return `${preview} +${columns.length - 3}`;
48175
+ }
48176
+ serializedConfig() {
48177
+ return JSON.stringify(this.workingConfig || { columns: [] }, null, 2);
48178
+ }
48179
+ tx(key, fallback) {
48180
+ return this.i18n.t(key, undefined, fallback, PRAXIS_TABLE_EDITOR_I18N_NAMESPACE);
48181
+ }
48182
+ emitConfig() {
48183
+ const next = cloneTableConfig(this.workingConfig);
48184
+ this.lastInputSignature = JSON.stringify(next);
48185
+ this.configChange.emit(next);
48186
+ }
48187
+ patchToolbar(patch) {
48188
+ this.workingConfig = {
48189
+ ...this.workingConfig,
48190
+ toolbar: {
48191
+ visible: this.toolbarVisible,
48192
+ ...(this.workingConfig.toolbar || {}),
48193
+ ...patch,
48194
+ },
48195
+ };
48196
+ this.emitConfig();
48197
+ }
48198
+ patchBehavior(patch) {
48199
+ this.workingConfig = {
48200
+ ...this.workingConfig,
48201
+ behavior: {
48202
+ ...(this.workingConfig.behavior || {}),
48203
+ ...patch,
48204
+ },
48205
+ };
48206
+ this.emitConfig();
48207
+ }
48208
+ patchPagination() {
48209
+ this.patchBehavior({
48210
+ pagination: {
48211
+ ...(this.workingConfig.behavior?.pagination || {}),
48212
+ enabled: this.paginationEnabled,
48213
+ pageSize: this.pageSize,
48214
+ pageSizeOptions: this.parseNumberList(this.pageSizeOptionsText, [5, 10, 25, 50]),
48215
+ showFirstLastButtons: this.showFirstLastButtons,
48216
+ showPageNumbers: this.workingConfig.behavior?.pagination?.showPageNumbers ?? true,
48217
+ showPageInfo: this.workingConfig.behavior?.pagination?.showPageInfo ?? true,
48218
+ position: this.paginationPosition,
48219
+ style: this.paginationStyle,
48220
+ strategy: this.paginationStrategy,
48221
+ },
48222
+ });
48223
+ }
48224
+ patchSorting() {
48225
+ this.patchBehavior({
48226
+ sorting: {
48227
+ ...(this.workingConfig.behavior?.sorting || {}),
48228
+ enabled: this.sortingEnabled,
48229
+ strategy: this.sortingStrategy,
48230
+ allowClearSort: this.allowClearSort,
48231
+ multiSort: this.workingConfig.behavior?.sorting?.multiSort ?? false,
48232
+ showSortIndicators: this.workingConfig.behavior?.sorting?.showSortIndicators ?? true,
48233
+ indicatorPosition: this.workingConfig.behavior?.sorting?.indicatorPosition || 'end',
48234
+ defaultSort: this.buildDefaultSort(),
48235
+ },
48236
+ });
48237
+ }
48238
+ patchMessages(patch) {
48239
+ this.workingConfig = {
48240
+ ...this.workingConfig,
48241
+ messages: {
48242
+ ...(this.workingConfig.messages || {}),
48243
+ ...patch,
48244
+ },
48245
+ };
48246
+ this.emitConfig();
48247
+ }
48248
+ arrayToString(values) {
48249
+ return (values || []).join(', ');
48250
+ }
48251
+ parseNumberList(value, fallback) {
48252
+ const parsed = (value || '')
48253
+ .split(',')
48254
+ .map((item) => Number(item.trim()))
48255
+ .filter((item) => Number.isFinite(item) && item > 0);
48256
+ return parsed.length ? parsed : fallback;
48257
+ }
48258
+ buildDefaultSort() {
48259
+ if (!this.defaultSortField) {
48260
+ return undefined;
48261
+ }
48262
+ return {
48263
+ column: this.defaultSortField,
48264
+ direction: this.defaultSortDirection,
48265
+ };
48266
+ }
48267
+ getDefaultSortField(config) {
48268
+ const defaultSort = config.behavior?.sorting?.defaultSort;
48269
+ if (Array.isArray(defaultSort)) {
48270
+ return defaultSort[0]?.column || '';
48271
+ }
48272
+ return defaultSort?.column || '';
48273
+ }
48274
+ getDefaultSortDirection(config) {
48275
+ const defaultSort = config.behavior?.sorting?.defaultSort;
48276
+ const direction = Array.isArray(defaultSort)
48277
+ ? defaultSort[0]?.direction
48278
+ : defaultSort?.direction;
48279
+ return direction === 'desc' ? 'desc' : 'asc';
48280
+ }
48281
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisTableInlineAuthoringEditorComponent, deps: [{ token: i1.PraxisI18nService }], target: i0.ɵɵFactoryTarget.Component });
48282
+ 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: `
48283
+ <section class="inline-authoring" data-testid="table-inline-authoring-editor">
48284
+ <header class="inline-authoring__header">
48285
+ <div>
48286
+ <h4>{{ tx('inlineAuthoring.title', 'Table structure') }}</h4>
48287
+ <p>{{ tx('inlineAuthoring.description', 'Edit the canonical TableConfig used by this CRUD surface.') }}</p>
48288
+ </div>
48289
+ <span class="inline-authoring__chip" data-testid="table-inline-editor-column-count">
48290
+ {{ columnCountLabel() }}
48291
+ </span>
48292
+ </header>
48293
+
48294
+ <mat-card class="inline-authoring__card">
48295
+ <section class="inline-authoring__summary" data-testid="table-inline-editor-summary">
48296
+ <div class="inline-authoring__summary-card">
48297
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.summary.toolbar', 'Toolbar') }}</span>
48298
+ <strong data-testid="table-inline-editor-summary-toolbar">{{ toolbarSummary() }}</strong>
48299
+ </div>
48300
+ <div class="inline-authoring__summary-card">
48301
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.summary.density', 'Density') }}</span>
48302
+ <strong data-testid="table-inline-editor-summary-density">{{ densitySummary() }}</strong>
48303
+ </div>
48304
+ <div class="inline-authoring__summary-card">
48305
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.summary.actions', 'Actions') }}</span>
48306
+ <strong data-testid="table-inline-editor-summary-actions">{{ actionsPositionSummary() }}</strong>
48307
+ </div>
48308
+ <div class="inline-authoring__summary-card">
48309
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.summary.behavior', 'List behavior') }}</span>
48310
+ <strong data-testid="table-inline-editor-summary-behavior">{{ behaviorSummary() }}</strong>
48311
+ </div>
48312
+ <div class="inline-authoring__summary-card">
48313
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.summary.states', 'State messages') }}</span>
48314
+ <strong data-testid="table-inline-editor-summary-states">{{ statesSummary() }}</strong>
48315
+ </div>
48316
+ <div class="inline-authoring__summary-card inline-authoring__summary-card--wide">
48317
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.summary.columns', 'Visible structure') }}</span>
48318
+ <strong data-testid="table-inline-editor-summary-columns">{{ columnsPreviewSummary() }}</strong>
48319
+ </div>
48320
+ </section>
48321
+
48322
+ <section class="inline-authoring__essentials">
48323
+ <div class="inline-authoring__section-copy">
48324
+ <h5>{{ tx('inlineAuthoring.essentialsTitle', 'Table essentials') }}</h5>
48325
+ <p>{{ tx('inlineAuthoring.essentialsDescription', 'Define the toolbar presence and the texts users see first.') }}</p>
48326
+ </div>
48327
+
48328
+ <div class="inline-authoring__grid">
48329
+ <mat-slide-toggle
48330
+ data-testid="table-inline-editor-toolbar-visible"
48331
+ [ngModel]="toolbarVisible"
48332
+ (ngModelChange)="setToolbarVisible($event)"
48333
+ [disabled]="readonly"
48334
+ >
48335
+ {{ tx('toolbar.visible', 'Show toolbar') }}
48336
+ </mat-slide-toggle>
48337
+
48338
+ <mat-form-field appearance="outline">
48339
+ <mat-label>{{ tx('toolbar.title.label', 'Toolbar title') }}</mat-label>
48340
+ <input
48341
+ matInput
48342
+ data-testid="table-inline-editor-toolbar-title"
48343
+ [ngModel]="toolbarTitle"
48344
+ (ngModelChange)="setToolbarTitle($event)"
48345
+ [disabled]="readonly"
48346
+ />
48347
+ </mat-form-field>
48348
+
48349
+ <mat-form-field appearance="outline">
48350
+ <mat-label>{{ tx('toolbar.subtitle.label', 'Subtitle') }}</mat-label>
48351
+ <input
48352
+ matInput
48353
+ data-testid="table-inline-editor-toolbar-subtitle"
48354
+ [ngModel]="toolbarSubtitle"
48355
+ (ngModelChange)="setToolbarSubtitle($event)"
48356
+ [disabled]="readonly"
48357
+ />
48358
+ </mat-form-field>
48359
+
48360
+ <mat-form-field appearance="outline">
48361
+ <mat-label>{{ tx('behavior.appearance.density', 'Density') }}</mat-label>
48362
+ <mat-select
48363
+ data-testid="table-inline-editor-density"
48364
+ [ngModel]="density"
48365
+ (ngModelChange)="setDensity($event)"
48366
+ [disabled]="readonly"
48367
+ >
48368
+ <mat-option value="compact">{{ tx('behavior.appearance.density.compact', 'Compact') }}</mat-option>
48369
+ <mat-option value="comfortable">{{ tx('behavior.appearance.density.comfortable', 'Comfortable') }}</mat-option>
48370
+ <mat-option value="spacious">{{ tx('behavior.appearance.density.spacious', 'Spacious') }}</mat-option>
48371
+ </mat-select>
48372
+ </mat-form-field>
48373
+
48374
+ <mat-form-field appearance="outline">
48375
+ <mat-label>{{ tx('toolbar.actionsPosition.label', 'Actions position') }}</mat-label>
48376
+ <mat-select
48377
+ data-testid="table-inline-editor-toolbar-actions-position"
48378
+ [ngModel]="toolbarActionsPosition"
48379
+ (ngModelChange)="setToolbarActionsPosition($event)"
48380
+ [disabled]="readonly"
48381
+ >
48382
+ <mat-option value="top">{{ tx('toolbar.actionsPosition.top', 'Above the table') }}</mat-option>
48383
+ <mat-option value="bottom">{{ tx('toolbar.actionsPosition.bottom', 'Below the table') }}</mat-option>
48384
+ <mat-option value="both">{{ tx('toolbar.actionsPosition.both', 'Top and bottom') }}</mat-option>
48385
+ </mat-select>
48386
+ </mat-form-field>
48387
+
48388
+ <mat-form-field appearance="outline">
48389
+ <mat-label>{{ tx('toolbar.height.label', 'Toolbar height (px)') }}</mat-label>
48390
+ <input
48391
+ matInput
48392
+ type="number"
48393
+ data-testid="table-inline-editor-toolbar-height"
48394
+ [ngModel]="toolbarHeight"
48395
+ (ngModelChange)="setToolbarHeight($event)"
48396
+ [disabled]="readonly"
48397
+ min="40"
48398
+ max="120"
48399
+ />
48400
+ </mat-form-field>
48401
+
48402
+ <mat-form-field appearance="outline">
48403
+ <mat-label>{{ tx('toolbar.backgroundPreset.label', 'Actions background color') }}</mat-label>
48404
+ <mat-select
48405
+ data-testid="table-inline-editor-toolbar-background-preset"
48406
+ [ngModel]="toolbarActionsBackgroundColor"
48407
+ (ngModelChange)="setToolbarActionsBackgroundColor($event)"
48408
+ [disabled]="readonly"
48409
+ >
48410
+ <mat-option value="">{{ tx('toolbar.backgroundPreset.default', 'Theme default') }}</mat-option>
48411
+ <mat-option value="var(--p-table-header-bg)">{{ tx('toolbar.backgroundPreset.header', 'Table header') }}</mat-option>
48412
+ <mat-option value="var(--md-sys-color-surface)">{{ tx('toolbar.backgroundPreset.surface', 'Surface') }}</mat-option>
48413
+ <mat-option value="var(--md-sys-color-surface-container)">{{ tx('toolbar.backgroundPreset.surfaceContainer', 'Surface container') }}</mat-option>
48414
+ <mat-option value="var(--md-sys-color-surface-container-high)">{{ tx('toolbar.backgroundPreset.surfaceContainerHigh', 'Surface container high') }}</mat-option>
48415
+ <mat-option value="var(--md-sys-color-surface-container-highest)">{{ tx('toolbar.backgroundPreset.surfaceContainerHighest', 'Surface container highest') }}</mat-option>
48416
+ </mat-select>
48417
+ </mat-form-field>
48418
+ </div>
48419
+ </section>
48420
+
48421
+ <section class="inline-authoring__advanced" data-testid="table-inline-editor-advanced-intro">
48422
+ <div class="inline-authoring__section-copy">
48423
+ <h5>{{ tx('inlineAuthoring.advancedTitle', 'Advanced table details') }}</h5>
48424
+ <p>{{ tx('inlineAuthoring.advancedDescription', 'Open the next panels only when you need to tune list behavior, state copy, or visible columns.') }}</p>
48425
+ </div>
48426
+
48427
+ <section class="inline-authoring__advanced-summary">
48428
+ <div class="inline-authoring__advanced-card">
48429
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.advanced.behavior', 'Behavior') }}</span>
48430
+ <strong data-testid="table-inline-editor-advanced-summary-behavior">{{ behaviorSummary() }}</strong>
48431
+ </div>
48432
+ <div class="inline-authoring__advanced-card">
48433
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.advanced.states', 'State copy') }}</span>
48434
+ <strong data-testid="table-inline-editor-advanced-summary-states">{{ statesSummary() }}</strong>
48435
+ </div>
48436
+ <div class="inline-authoring__advanced-card">
48437
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.advanced.columns', 'Columns') }}</span>
48438
+ <strong data-testid="table-inline-editor-advanced-summary-columns">{{ columnsPreviewSummary() }}</strong>
48439
+ </div>
48440
+ </section>
48441
+ </section>
48442
+
48443
+ @if (readonly) {
48444
+ <p class="inline-authoring__readonly" data-testid="table-inline-editor-readonly-hint">
48445
+ {{ tx('inlineAuthoring.readonlyHint', 'This authoring surface is read-only in the current context.') }}
48446
+ </p>
48447
+ <pre data-testid="table-inline-editor-json">{{ serializedConfig() }}</pre>
48448
+ } @else {
48449
+ <section class="inline-authoring__advanced-panels" data-testid="table-inline-editor-advanced-panels">
48450
+ <mat-expansion-panel class="inline-authoring__columns-panel" [expanded]="false" data-testid="table-inline-editor-behavior-panel">
48451
+ <mat-expansion-panel-header>
48452
+ <mat-panel-title>{{ tx('inlineAuthoring.behaviorTitle', 'List behavior') }}</mat-panel-title>
48453
+ <mat-panel-description>{{ behaviorSummary() }}</mat-panel-description>
48454
+ </mat-expansion-panel-header>
48455
+ <p class="inline-authoring__panel-description">
48456
+ {{ tx('inlineAuthoring.behaviorDescription', 'Control pagination and sorting with the same runtime-backed semantics used by the full table editor.') }}
48457
+ </p>
48458
+ <div class="inline-authoring__grid">
48459
+ <mat-slide-toggle
48460
+ data-testid="table-inline-editor-pagination-enabled"
48461
+ [ngModel]="paginationEnabled"
48462
+ (ngModelChange)="setPaginationEnabled($event)"
48463
+ [disabled]="readonly"
48464
+ >
48465
+ {{ tx('behavior.pagination.enabled', 'Enable pagination') }}
48466
+ </mat-slide-toggle>
48467
+
48468
+ <mat-form-field appearance="outline">
48469
+ <mat-label>{{ tx('behavior.pagination.pageSize', 'Items per page') }}</mat-label>
48470
+ <input
48471
+ matInput
48472
+ type="number"
48473
+ data-testid="table-inline-editor-pagination-page-size"
48474
+ [ngModel]="pageSize"
48475
+ (ngModelChange)="setPaginationPageSize($event)"
48476
+ [disabled]="readonly || !paginationEnabled"
48477
+ min="1"
48478
+ />
48479
+ </mat-form-field>
48480
+
48481
+ <mat-form-field appearance="outline">
48482
+ <mat-label>{{ tx('behavior.pagination.pageSizeOptions', 'Page size options') }}</mat-label>
48483
+ <input
48484
+ matInput
48485
+ data-testid="table-inline-editor-pagination-page-size-options"
48486
+ [ngModel]="pageSizeOptionsText"
48487
+ (ngModelChange)="setPaginationPageSizeOptions($event)"
48488
+ [disabled]="readonly || !paginationEnabled"
48489
+ placeholder="5, 10, 25, 50"
48490
+ />
48491
+ </mat-form-field>
48492
+
48493
+ <mat-slide-toggle
48494
+ data-testid="table-inline-editor-pagination-first-last"
48495
+ [ngModel]="showFirstLastButtons"
48496
+ (ngModelChange)="setPaginationShowFirstLastButtons($event)"
48497
+ [disabled]="readonly || !paginationEnabled"
48498
+ >
48499
+ {{ tx('behavior.pagination.firstLast', 'Show first/last page buttons') }}
48500
+ </mat-slide-toggle>
48501
+
48502
+ <mat-form-field appearance="outline">
48503
+ <mat-label>{{ tx('behavior.pagination.strategy', 'Pagination strategy') }}</mat-label>
48504
+ <mat-select
48505
+ data-testid="table-inline-editor-pagination-strategy"
48506
+ [ngModel]="paginationStrategy"
48507
+ (ngModelChange)="setPaginationStrategy($event)"
48508
+ [disabled]="readonly || !paginationEnabled"
48509
+ >
48510
+ <mat-option value="client">{{ tx('behavior.pagination.strategy.client', 'Client (local data)') }}</mat-option>
48511
+ <mat-option value="server">{{ tx('behavior.pagination.strategy.server', 'Server (remote pagination)') }}</mat-option>
48512
+ </mat-select>
48513
+ </mat-form-field>
48514
+
48515
+ <mat-form-field appearance="outline">
48516
+ <mat-label>{{ tx('behavior.pagination.position', 'Paginator position') }}</mat-label>
48517
+ <mat-select
48518
+ data-testid="table-inline-editor-pagination-position"
48519
+ [ngModel]="paginationPosition"
48520
+ (ngModelChange)="setPaginationPosition($event)"
48521
+ [disabled]="readonly || !paginationEnabled"
48522
+ >
48523
+ <mat-option value="top">{{ tx('behavior.pagination.position.top', 'Top') }}</mat-option>
48524
+ <mat-option value="bottom">{{ tx('behavior.pagination.position.bottom', 'Bottom') }}</mat-option>
48525
+ <mat-option value="both">{{ tx('behavior.pagination.position.both', 'Both') }}</mat-option>
48526
+ </mat-select>
48527
+ </mat-form-field>
48528
+
48529
+ <mat-form-field appearance="outline">
48530
+ <mat-label>{{ tx('behavior.pagination.style', 'Paginator style') }}</mat-label>
48531
+ <mat-select
48532
+ data-testid="table-inline-editor-pagination-style"
48533
+ [ngModel]="paginationStyle"
48534
+ (ngModelChange)="setPaginationStyle($event)"
48535
+ [disabled]="readonly || !paginationEnabled"
48536
+ >
48537
+ <mat-option value="default">{{ tx('behavior.pagination.style.default', 'Default') }}</mat-option>
48538
+ <mat-option value="simple">{{ tx('behavior.pagination.style.simple', 'Simple') }}</mat-option>
48539
+ <mat-option value="compact">{{ tx('behavior.pagination.style.compact', 'Compact') }}</mat-option>
48540
+ <mat-option value="advanced">{{ tx('behavior.pagination.style.advanced', 'Advanced') }}</mat-option>
48541
+ </mat-select>
48542
+ </mat-form-field>
48543
+
48544
+ <mat-slide-toggle
48545
+ data-testid="table-inline-editor-sorting-enabled"
48546
+ [ngModel]="sortingEnabled"
48547
+ (ngModelChange)="setSortingEnabled($event)"
48548
+ [disabled]="readonly"
48549
+ >
48550
+ {{ tx('behavior.sorting.enabled', 'Enable sorting') }}
48551
+ </mat-slide-toggle>
48552
+
48553
+ <mat-form-field appearance="outline">
48554
+ <mat-label>{{ tx('behavior.sorting.strategy', 'Sorting strategy') }}</mat-label>
48555
+ <mat-select
48556
+ data-testid="table-inline-editor-sorting-strategy"
48557
+ [ngModel]="sortingStrategy"
48558
+ (ngModelChange)="setSortingStrategy($event)"
48559
+ [disabled]="readonly || !sortingEnabled"
48560
+ >
48561
+ <mat-option value="client">{{ tx('behavior.sorting.strategy.client', 'Client (local sorting)') }}</mat-option>
48562
+ <mat-option value="server">{{ tx('behavior.sorting.strategy.server', 'Server (remote sorting)') }}</mat-option>
48563
+ </mat-select>
48564
+ </mat-form-field>
48565
+
48566
+ <mat-slide-toggle
48567
+ data-testid="table-inline-editor-sorting-allow-clear"
48568
+ [ngModel]="allowClearSort"
48569
+ (ngModelChange)="setSortingAllowClear($event)"
48570
+ [disabled]="readonly || !sortingEnabled"
48571
+ >
48572
+ {{ tx('behavior.sorting.allowClear', 'Allow clearing sort') }}
48573
+ </mat-slide-toggle>
48574
+
48575
+ <mat-form-field appearance="outline">
48576
+ <mat-label>{{ tx('behavior.sorting.defaultField', 'Initial sort - column') }}</mat-label>
48577
+ <mat-select
48578
+ data-testid="table-inline-editor-sorting-default-field"
48579
+ [ngModel]="defaultSortField"
48580
+ (ngModelChange)="setSortingDefaultField($event)"
48581
+ [disabled]="readonly || !sortingEnabled"
48582
+ >
48583
+ <mat-option value="">{{ tx('inlineAuthoring.noneOption', 'None') }}</mat-option>
48584
+ @for (column of workingConfig.columns || []; track column.field) {
48585
+ <mat-option [value]="column.field">{{ column.header || column.field }}</mat-option>
48586
+ }
48587
+ </mat-select>
48588
+ </mat-form-field>
48589
+
48590
+ <mat-form-field appearance="outline">
48591
+ <mat-label>{{ tx('behavior.sorting.direction', 'Direction') }}</mat-label>
48592
+ <mat-select
48593
+ data-testid="table-inline-editor-sorting-default-direction"
48594
+ [ngModel]="defaultSortDirection"
48595
+ (ngModelChange)="setSortingDefaultDirection($event)"
48596
+ [disabled]="readonly || !sortingEnabled || !defaultSortField"
48597
+ >
48598
+ <mat-option value="asc">{{ tx('behavior.sorting.direction.asc', 'Ascending') }}</mat-option>
48599
+ <mat-option value="desc">{{ tx('behavior.sorting.direction.desc', 'Descending') }}</mat-option>
48600
+ </mat-select>
48601
+ </mat-form-field>
48602
+ </div>
48603
+ </mat-expansion-panel>
48604
+
48605
+ <mat-expansion-panel class="inline-authoring__columns-panel" [expanded]="false" data-testid="table-inline-editor-states-panel">
48606
+ <mat-expansion-panel-header>
48607
+ <mat-panel-title>{{ tx('inlineAuthoring.statesTitle', 'State messages') }}</mat-panel-title>
48608
+ <mat-panel-description>{{ statesSummary() }}</mat-panel-description>
48609
+ </mat-expansion-panel-header>
48610
+ <p class="inline-authoring__panel-description">
48611
+ {{ tx('inlineAuthoring.statesDescription', 'Adjust the first messages users read when the list is loading, empty, filtered, or broken.') }}
48612
+ </p>
48613
+ <div class="inline-authoring__grid">
48614
+ <mat-form-field appearance="outline">
48615
+ <mat-label>{{ tx('messages.loading.label', 'Loading data') }}</mat-label>
48616
+ <input
48617
+ matInput
48618
+ data-testid="table-inline-editor-message-loading"
48619
+ [ngModel]="loadingMessage"
48620
+ (ngModelChange)="setStateMessage('loading', $event)"
48621
+ [disabled]="readonly"
48622
+ />
48623
+ </mat-form-field>
48624
+
48625
+ <mat-form-field appearance="outline">
48626
+ <mat-label>{{ tx('messages.empty.label', 'No data found') }}</mat-label>
48627
+ <input
48628
+ matInput
48629
+ data-testid="table-inline-editor-message-empty"
48630
+ [ngModel]="emptyMessage"
48631
+ (ngModelChange)="setStateMessage('empty', $event)"
48632
+ [disabled]="readonly"
48633
+ />
48634
+ </mat-form-field>
48635
+
48636
+ <mat-form-field appearance="outline">
48637
+ <mat-label>{{ tx('messages.noResults.label', 'No results') }}</mat-label>
48638
+ <input
48639
+ matInput
48640
+ data-testid="table-inline-editor-message-no-results"
48641
+ [ngModel]="noResultsMessage"
48642
+ (ngModelChange)="setStateMessage('noResults', $event)"
48643
+ [disabled]="readonly"
48644
+ />
48645
+ </mat-form-field>
48646
+
48647
+ <mat-form-field appearance="outline">
48648
+ <mat-label>{{ tx('messages.error.label', 'Load error') }}</mat-label>
48649
+ <input
48650
+ matInput
48651
+ data-testid="table-inline-editor-message-error"
48652
+ [ngModel]="errorMessage"
48653
+ (ngModelChange)="setStateMessage('error', $event)"
48654
+ [disabled]="readonly"
48655
+ />
48656
+ </mat-form-field>
48657
+ </div>
48658
+ </mat-expansion-panel>
48659
+
48660
+ <mat-expansion-panel class="inline-authoring__columns-panel" [expanded]="false" data-testid="table-inline-editor-columns-panel">
48661
+ <mat-expansion-panel-header>
48662
+ <mat-panel-title>{{ tx('inlineAuthoring.columnsTitle', 'Columns') }}</mat-panel-title>
48663
+ <mat-panel-description>{{ columnCountLabel() }}</mat-panel-description>
48664
+ </mat-expansion-panel-header>
48665
+ <p class="inline-authoring__panel-description">
48666
+ {{ tx('inlineAuthoring.columnsDescription', 'Open only when you need to adjust visible fields, labels, and renderers.') }}
48667
+ </p>
48668
+ <columns-config-editor
48669
+ [config]="workingConfig"
48670
+ (configChange)="onColumnsConfigChange($event)"
48671
+ />
48672
+ </mat-expansion-panel>
48673
+ </section>
48674
+ }
48675
+ </mat-card>
48676
+ </section>
48677
+ `, 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 });
48678
+ }
48679
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisTableInlineAuthoringEditorComponent, decorators: [{
48680
+ type: Component,
48681
+ args: [{ selector: 'praxis-table-inline-authoring-editor', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
48682
+ CommonModule,
48683
+ FormsModule,
48684
+ MatCardModule,
48685
+ MatExpansionModule,
48686
+ MatFormFieldModule,
48687
+ MatInputModule,
48688
+ MatSelectModule,
48689
+ MatSlideToggleModule,
48690
+ ColumnsConfigEditorComponent,
48691
+ ], providers: [providePraxisI18nConfig(PRAXIS_TABLE_EDITOR_I18N_CONFIG)], template: `
48692
+ <section class="inline-authoring" data-testid="table-inline-authoring-editor">
48693
+ <header class="inline-authoring__header">
48694
+ <div>
48695
+ <h4>{{ tx('inlineAuthoring.title', 'Table structure') }}</h4>
48696
+ <p>{{ tx('inlineAuthoring.description', 'Edit the canonical TableConfig used by this CRUD surface.') }}</p>
48697
+ </div>
48698
+ <span class="inline-authoring__chip" data-testid="table-inline-editor-column-count">
48699
+ {{ columnCountLabel() }}
48700
+ </span>
48701
+ </header>
48702
+
48703
+ <mat-card class="inline-authoring__card">
48704
+ <section class="inline-authoring__summary" data-testid="table-inline-editor-summary">
48705
+ <div class="inline-authoring__summary-card">
48706
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.summary.toolbar', 'Toolbar') }}</span>
48707
+ <strong data-testid="table-inline-editor-summary-toolbar">{{ toolbarSummary() }}</strong>
48708
+ </div>
48709
+ <div class="inline-authoring__summary-card">
48710
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.summary.density', 'Density') }}</span>
48711
+ <strong data-testid="table-inline-editor-summary-density">{{ densitySummary() }}</strong>
48712
+ </div>
48713
+ <div class="inline-authoring__summary-card">
48714
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.summary.actions', 'Actions') }}</span>
48715
+ <strong data-testid="table-inline-editor-summary-actions">{{ actionsPositionSummary() }}</strong>
48716
+ </div>
48717
+ <div class="inline-authoring__summary-card">
48718
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.summary.behavior', 'List behavior') }}</span>
48719
+ <strong data-testid="table-inline-editor-summary-behavior">{{ behaviorSummary() }}</strong>
48720
+ </div>
48721
+ <div class="inline-authoring__summary-card">
48722
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.summary.states', 'State messages') }}</span>
48723
+ <strong data-testid="table-inline-editor-summary-states">{{ statesSummary() }}</strong>
48724
+ </div>
48725
+ <div class="inline-authoring__summary-card inline-authoring__summary-card--wide">
48726
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.summary.columns', 'Visible structure') }}</span>
48727
+ <strong data-testid="table-inline-editor-summary-columns">{{ columnsPreviewSummary() }}</strong>
48728
+ </div>
48729
+ </section>
48730
+
48731
+ <section class="inline-authoring__essentials">
48732
+ <div class="inline-authoring__section-copy">
48733
+ <h5>{{ tx('inlineAuthoring.essentialsTitle', 'Table essentials') }}</h5>
48734
+ <p>{{ tx('inlineAuthoring.essentialsDescription', 'Define the toolbar presence and the texts users see first.') }}</p>
48735
+ </div>
48736
+
48737
+ <div class="inline-authoring__grid">
48738
+ <mat-slide-toggle
48739
+ data-testid="table-inline-editor-toolbar-visible"
48740
+ [ngModel]="toolbarVisible"
48741
+ (ngModelChange)="setToolbarVisible($event)"
48742
+ [disabled]="readonly"
48743
+ >
48744
+ {{ tx('toolbar.visible', 'Show toolbar') }}
48745
+ </mat-slide-toggle>
48746
+
48747
+ <mat-form-field appearance="outline">
48748
+ <mat-label>{{ tx('toolbar.title.label', 'Toolbar title') }}</mat-label>
48749
+ <input
48750
+ matInput
48751
+ data-testid="table-inline-editor-toolbar-title"
48752
+ [ngModel]="toolbarTitle"
48753
+ (ngModelChange)="setToolbarTitle($event)"
48754
+ [disabled]="readonly"
48755
+ />
48756
+ </mat-form-field>
48757
+
48758
+ <mat-form-field appearance="outline">
48759
+ <mat-label>{{ tx('toolbar.subtitle.label', 'Subtitle') }}</mat-label>
48760
+ <input
48761
+ matInput
48762
+ data-testid="table-inline-editor-toolbar-subtitle"
48763
+ [ngModel]="toolbarSubtitle"
48764
+ (ngModelChange)="setToolbarSubtitle($event)"
48765
+ [disabled]="readonly"
48766
+ />
48767
+ </mat-form-field>
48768
+
48769
+ <mat-form-field appearance="outline">
48770
+ <mat-label>{{ tx('behavior.appearance.density', 'Density') }}</mat-label>
48771
+ <mat-select
48772
+ data-testid="table-inline-editor-density"
48773
+ [ngModel]="density"
48774
+ (ngModelChange)="setDensity($event)"
48775
+ [disabled]="readonly"
48776
+ >
48777
+ <mat-option value="compact">{{ tx('behavior.appearance.density.compact', 'Compact') }}</mat-option>
48778
+ <mat-option value="comfortable">{{ tx('behavior.appearance.density.comfortable', 'Comfortable') }}</mat-option>
48779
+ <mat-option value="spacious">{{ tx('behavior.appearance.density.spacious', 'Spacious') }}</mat-option>
48780
+ </mat-select>
48781
+ </mat-form-field>
48782
+
48783
+ <mat-form-field appearance="outline">
48784
+ <mat-label>{{ tx('toolbar.actionsPosition.label', 'Actions position') }}</mat-label>
48785
+ <mat-select
48786
+ data-testid="table-inline-editor-toolbar-actions-position"
48787
+ [ngModel]="toolbarActionsPosition"
48788
+ (ngModelChange)="setToolbarActionsPosition($event)"
48789
+ [disabled]="readonly"
48790
+ >
48791
+ <mat-option value="top">{{ tx('toolbar.actionsPosition.top', 'Above the table') }}</mat-option>
48792
+ <mat-option value="bottom">{{ tx('toolbar.actionsPosition.bottom', 'Below the table') }}</mat-option>
48793
+ <mat-option value="both">{{ tx('toolbar.actionsPosition.both', 'Top and bottom') }}</mat-option>
48794
+ </mat-select>
48795
+ </mat-form-field>
48796
+
48797
+ <mat-form-field appearance="outline">
48798
+ <mat-label>{{ tx('toolbar.height.label', 'Toolbar height (px)') }}</mat-label>
48799
+ <input
48800
+ matInput
48801
+ type="number"
48802
+ data-testid="table-inline-editor-toolbar-height"
48803
+ [ngModel]="toolbarHeight"
48804
+ (ngModelChange)="setToolbarHeight($event)"
48805
+ [disabled]="readonly"
48806
+ min="40"
48807
+ max="120"
48808
+ />
48809
+ </mat-form-field>
48810
+
48811
+ <mat-form-field appearance="outline">
48812
+ <mat-label>{{ tx('toolbar.backgroundPreset.label', 'Actions background color') }}</mat-label>
48813
+ <mat-select
48814
+ data-testid="table-inline-editor-toolbar-background-preset"
48815
+ [ngModel]="toolbarActionsBackgroundColor"
48816
+ (ngModelChange)="setToolbarActionsBackgroundColor($event)"
48817
+ [disabled]="readonly"
48818
+ >
48819
+ <mat-option value="">{{ tx('toolbar.backgroundPreset.default', 'Theme default') }}</mat-option>
48820
+ <mat-option value="var(--p-table-header-bg)">{{ tx('toolbar.backgroundPreset.header', 'Table header') }}</mat-option>
48821
+ <mat-option value="var(--md-sys-color-surface)">{{ tx('toolbar.backgroundPreset.surface', 'Surface') }}</mat-option>
48822
+ <mat-option value="var(--md-sys-color-surface-container)">{{ tx('toolbar.backgroundPreset.surfaceContainer', 'Surface container') }}</mat-option>
48823
+ <mat-option value="var(--md-sys-color-surface-container-high)">{{ tx('toolbar.backgroundPreset.surfaceContainerHigh', 'Surface container high') }}</mat-option>
48824
+ <mat-option value="var(--md-sys-color-surface-container-highest)">{{ tx('toolbar.backgroundPreset.surfaceContainerHighest', 'Surface container highest') }}</mat-option>
48825
+ </mat-select>
48826
+ </mat-form-field>
48827
+ </div>
48828
+ </section>
48829
+
48830
+ <section class="inline-authoring__advanced" data-testid="table-inline-editor-advanced-intro">
48831
+ <div class="inline-authoring__section-copy">
48832
+ <h5>{{ tx('inlineAuthoring.advancedTitle', 'Advanced table details') }}</h5>
48833
+ <p>{{ tx('inlineAuthoring.advancedDescription', 'Open the next panels only when you need to tune list behavior, state copy, or visible columns.') }}</p>
48834
+ </div>
48835
+
48836
+ <section class="inline-authoring__advanced-summary">
48837
+ <div class="inline-authoring__advanced-card">
48838
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.advanced.behavior', 'Behavior') }}</span>
48839
+ <strong data-testid="table-inline-editor-advanced-summary-behavior">{{ behaviorSummary() }}</strong>
48840
+ </div>
48841
+ <div class="inline-authoring__advanced-card">
48842
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.advanced.states', 'State copy') }}</span>
48843
+ <strong data-testid="table-inline-editor-advanced-summary-states">{{ statesSummary() }}</strong>
48844
+ </div>
48845
+ <div class="inline-authoring__advanced-card">
48846
+ <span class="inline-authoring__summary-label">{{ tx('inlineAuthoring.advanced.columns', 'Columns') }}</span>
48847
+ <strong data-testid="table-inline-editor-advanced-summary-columns">{{ columnsPreviewSummary() }}</strong>
48848
+ </div>
48849
+ </section>
48850
+ </section>
48851
+
48852
+ @if (readonly) {
48853
+ <p class="inline-authoring__readonly" data-testid="table-inline-editor-readonly-hint">
48854
+ {{ tx('inlineAuthoring.readonlyHint', 'This authoring surface is read-only in the current context.') }}
48855
+ </p>
48856
+ <pre data-testid="table-inline-editor-json">{{ serializedConfig() }}</pre>
48857
+ } @else {
48858
+ <section class="inline-authoring__advanced-panels" data-testid="table-inline-editor-advanced-panels">
48859
+ <mat-expansion-panel class="inline-authoring__columns-panel" [expanded]="false" data-testid="table-inline-editor-behavior-panel">
48860
+ <mat-expansion-panel-header>
48861
+ <mat-panel-title>{{ tx('inlineAuthoring.behaviorTitle', 'List behavior') }}</mat-panel-title>
48862
+ <mat-panel-description>{{ behaviorSummary() }}</mat-panel-description>
48863
+ </mat-expansion-panel-header>
48864
+ <p class="inline-authoring__panel-description">
48865
+ {{ tx('inlineAuthoring.behaviorDescription', 'Control pagination and sorting with the same runtime-backed semantics used by the full table editor.') }}
48866
+ </p>
48867
+ <div class="inline-authoring__grid">
48868
+ <mat-slide-toggle
48869
+ data-testid="table-inline-editor-pagination-enabled"
48870
+ [ngModel]="paginationEnabled"
48871
+ (ngModelChange)="setPaginationEnabled($event)"
48872
+ [disabled]="readonly"
48873
+ >
48874
+ {{ tx('behavior.pagination.enabled', 'Enable pagination') }}
48875
+ </mat-slide-toggle>
48876
+
48877
+ <mat-form-field appearance="outline">
48878
+ <mat-label>{{ tx('behavior.pagination.pageSize', 'Items per page') }}</mat-label>
48879
+ <input
48880
+ matInput
48881
+ type="number"
48882
+ data-testid="table-inline-editor-pagination-page-size"
48883
+ [ngModel]="pageSize"
48884
+ (ngModelChange)="setPaginationPageSize($event)"
48885
+ [disabled]="readonly || !paginationEnabled"
48886
+ min="1"
48887
+ />
48888
+ </mat-form-field>
48889
+
48890
+ <mat-form-field appearance="outline">
48891
+ <mat-label>{{ tx('behavior.pagination.pageSizeOptions', 'Page size options') }}</mat-label>
48892
+ <input
48893
+ matInput
48894
+ data-testid="table-inline-editor-pagination-page-size-options"
48895
+ [ngModel]="pageSizeOptionsText"
48896
+ (ngModelChange)="setPaginationPageSizeOptions($event)"
48897
+ [disabled]="readonly || !paginationEnabled"
48898
+ placeholder="5, 10, 25, 50"
48899
+ />
48900
+ </mat-form-field>
48901
+
48902
+ <mat-slide-toggle
48903
+ data-testid="table-inline-editor-pagination-first-last"
48904
+ [ngModel]="showFirstLastButtons"
48905
+ (ngModelChange)="setPaginationShowFirstLastButtons($event)"
48906
+ [disabled]="readonly || !paginationEnabled"
48907
+ >
48908
+ {{ tx('behavior.pagination.firstLast', 'Show first/last page buttons') }}
48909
+ </mat-slide-toggle>
48910
+
48911
+ <mat-form-field appearance="outline">
48912
+ <mat-label>{{ tx('behavior.pagination.strategy', 'Pagination strategy') }}</mat-label>
48913
+ <mat-select
48914
+ data-testid="table-inline-editor-pagination-strategy"
48915
+ [ngModel]="paginationStrategy"
48916
+ (ngModelChange)="setPaginationStrategy($event)"
48917
+ [disabled]="readonly || !paginationEnabled"
48918
+ >
48919
+ <mat-option value="client">{{ tx('behavior.pagination.strategy.client', 'Client (local data)') }}</mat-option>
48920
+ <mat-option value="server">{{ tx('behavior.pagination.strategy.server', 'Server (remote pagination)') }}</mat-option>
48921
+ </mat-select>
48922
+ </mat-form-field>
48923
+
48924
+ <mat-form-field appearance="outline">
48925
+ <mat-label>{{ tx('behavior.pagination.position', 'Paginator position') }}</mat-label>
48926
+ <mat-select
48927
+ data-testid="table-inline-editor-pagination-position"
48928
+ [ngModel]="paginationPosition"
48929
+ (ngModelChange)="setPaginationPosition($event)"
48930
+ [disabled]="readonly || !paginationEnabled"
48931
+ >
48932
+ <mat-option value="top">{{ tx('behavior.pagination.position.top', 'Top') }}</mat-option>
48933
+ <mat-option value="bottom">{{ tx('behavior.pagination.position.bottom', 'Bottom') }}</mat-option>
48934
+ <mat-option value="both">{{ tx('behavior.pagination.position.both', 'Both') }}</mat-option>
48935
+ </mat-select>
48936
+ </mat-form-field>
48937
+
48938
+ <mat-form-field appearance="outline">
48939
+ <mat-label>{{ tx('behavior.pagination.style', 'Paginator style') }}</mat-label>
48940
+ <mat-select
48941
+ data-testid="table-inline-editor-pagination-style"
48942
+ [ngModel]="paginationStyle"
48943
+ (ngModelChange)="setPaginationStyle($event)"
48944
+ [disabled]="readonly || !paginationEnabled"
48945
+ >
48946
+ <mat-option value="default">{{ tx('behavior.pagination.style.default', 'Default') }}</mat-option>
48947
+ <mat-option value="simple">{{ tx('behavior.pagination.style.simple', 'Simple') }}</mat-option>
48948
+ <mat-option value="compact">{{ tx('behavior.pagination.style.compact', 'Compact') }}</mat-option>
48949
+ <mat-option value="advanced">{{ tx('behavior.pagination.style.advanced', 'Advanced') }}</mat-option>
48950
+ </mat-select>
48951
+ </mat-form-field>
48952
+
48953
+ <mat-slide-toggle
48954
+ data-testid="table-inline-editor-sorting-enabled"
48955
+ [ngModel]="sortingEnabled"
48956
+ (ngModelChange)="setSortingEnabled($event)"
48957
+ [disabled]="readonly"
48958
+ >
48959
+ {{ tx('behavior.sorting.enabled', 'Enable sorting') }}
48960
+ </mat-slide-toggle>
48961
+
48962
+ <mat-form-field appearance="outline">
48963
+ <mat-label>{{ tx('behavior.sorting.strategy', 'Sorting strategy') }}</mat-label>
48964
+ <mat-select
48965
+ data-testid="table-inline-editor-sorting-strategy"
48966
+ [ngModel]="sortingStrategy"
48967
+ (ngModelChange)="setSortingStrategy($event)"
48968
+ [disabled]="readonly || !sortingEnabled"
48969
+ >
48970
+ <mat-option value="client">{{ tx('behavior.sorting.strategy.client', 'Client (local sorting)') }}</mat-option>
48971
+ <mat-option value="server">{{ tx('behavior.sorting.strategy.server', 'Server (remote sorting)') }}</mat-option>
48972
+ </mat-select>
48973
+ </mat-form-field>
48974
+
48975
+ <mat-slide-toggle
48976
+ data-testid="table-inline-editor-sorting-allow-clear"
48977
+ [ngModel]="allowClearSort"
48978
+ (ngModelChange)="setSortingAllowClear($event)"
48979
+ [disabled]="readonly || !sortingEnabled"
48980
+ >
48981
+ {{ tx('behavior.sorting.allowClear', 'Allow clearing sort') }}
48982
+ </mat-slide-toggle>
48983
+
48984
+ <mat-form-field appearance="outline">
48985
+ <mat-label>{{ tx('behavior.sorting.defaultField', 'Initial sort - column') }}</mat-label>
48986
+ <mat-select
48987
+ data-testid="table-inline-editor-sorting-default-field"
48988
+ [ngModel]="defaultSortField"
48989
+ (ngModelChange)="setSortingDefaultField($event)"
48990
+ [disabled]="readonly || !sortingEnabled"
48991
+ >
48992
+ <mat-option value="">{{ tx('inlineAuthoring.noneOption', 'None') }}</mat-option>
48993
+ @for (column of workingConfig.columns || []; track column.field) {
48994
+ <mat-option [value]="column.field">{{ column.header || column.field }}</mat-option>
48995
+ }
48996
+ </mat-select>
48997
+ </mat-form-field>
48998
+
48999
+ <mat-form-field appearance="outline">
49000
+ <mat-label>{{ tx('behavior.sorting.direction', 'Direction') }}</mat-label>
49001
+ <mat-select
49002
+ data-testid="table-inline-editor-sorting-default-direction"
49003
+ [ngModel]="defaultSortDirection"
49004
+ (ngModelChange)="setSortingDefaultDirection($event)"
49005
+ [disabled]="readonly || !sortingEnabled || !defaultSortField"
49006
+ >
49007
+ <mat-option value="asc">{{ tx('behavior.sorting.direction.asc', 'Ascending') }}</mat-option>
49008
+ <mat-option value="desc">{{ tx('behavior.sorting.direction.desc', 'Descending') }}</mat-option>
49009
+ </mat-select>
49010
+ </mat-form-field>
49011
+ </div>
49012
+ </mat-expansion-panel>
49013
+
49014
+ <mat-expansion-panel class="inline-authoring__columns-panel" [expanded]="false" data-testid="table-inline-editor-states-panel">
49015
+ <mat-expansion-panel-header>
49016
+ <mat-panel-title>{{ tx('inlineAuthoring.statesTitle', 'State messages') }}</mat-panel-title>
49017
+ <mat-panel-description>{{ statesSummary() }}</mat-panel-description>
49018
+ </mat-expansion-panel-header>
49019
+ <p class="inline-authoring__panel-description">
49020
+ {{ tx('inlineAuthoring.statesDescription', 'Adjust the first messages users read when the list is loading, empty, filtered, or broken.') }}
49021
+ </p>
49022
+ <div class="inline-authoring__grid">
49023
+ <mat-form-field appearance="outline">
49024
+ <mat-label>{{ tx('messages.loading.label', 'Loading data') }}</mat-label>
49025
+ <input
49026
+ matInput
49027
+ data-testid="table-inline-editor-message-loading"
49028
+ [ngModel]="loadingMessage"
49029
+ (ngModelChange)="setStateMessage('loading', $event)"
49030
+ [disabled]="readonly"
49031
+ />
49032
+ </mat-form-field>
49033
+
49034
+ <mat-form-field appearance="outline">
49035
+ <mat-label>{{ tx('messages.empty.label', 'No data found') }}</mat-label>
49036
+ <input
49037
+ matInput
49038
+ data-testid="table-inline-editor-message-empty"
49039
+ [ngModel]="emptyMessage"
49040
+ (ngModelChange)="setStateMessage('empty', $event)"
49041
+ [disabled]="readonly"
49042
+ />
49043
+ </mat-form-field>
49044
+
49045
+ <mat-form-field appearance="outline">
49046
+ <mat-label>{{ tx('messages.noResults.label', 'No results') }}</mat-label>
49047
+ <input
49048
+ matInput
49049
+ data-testid="table-inline-editor-message-no-results"
49050
+ [ngModel]="noResultsMessage"
49051
+ (ngModelChange)="setStateMessage('noResults', $event)"
49052
+ [disabled]="readonly"
49053
+ />
49054
+ </mat-form-field>
49055
+
49056
+ <mat-form-field appearance="outline">
49057
+ <mat-label>{{ tx('messages.error.label', 'Load error') }}</mat-label>
49058
+ <input
49059
+ matInput
49060
+ data-testid="table-inline-editor-message-error"
49061
+ [ngModel]="errorMessage"
49062
+ (ngModelChange)="setStateMessage('error', $event)"
49063
+ [disabled]="readonly"
49064
+ />
49065
+ </mat-form-field>
49066
+ </div>
49067
+ </mat-expansion-panel>
49068
+
49069
+ <mat-expansion-panel class="inline-authoring__columns-panel" [expanded]="false" data-testid="table-inline-editor-columns-panel">
49070
+ <mat-expansion-panel-header>
49071
+ <mat-panel-title>{{ tx('inlineAuthoring.columnsTitle', 'Columns') }}</mat-panel-title>
49072
+ <mat-panel-description>{{ columnCountLabel() }}</mat-panel-description>
49073
+ </mat-expansion-panel-header>
49074
+ <p class="inline-authoring__panel-description">
49075
+ {{ tx('inlineAuthoring.columnsDescription', 'Open only when you need to adjust visible fields, labels, and renderers.') }}
49076
+ </p>
49077
+ <columns-config-editor
49078
+ [config]="workingConfig"
49079
+ (configChange)="onColumnsConfigChange($event)"
49080
+ />
49081
+ </mat-expansion-panel>
49082
+ </section>
49083
+ }
49084
+ </mat-card>
49085
+ </section>
49086
+ `, 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"] }]
49087
+ }], ctorParameters: () => [{ type: i1.PraxisI18nService }], propDecorators: { config: [{
49088
+ type: Input
49089
+ }], readonly: [{
49090
+ type: Input
49091
+ }], configChange: [{
49092
+ type: Output
49093
+ }] } });
49094
+ function cloneTableConfig(config) {
49095
+ return JSON.parse(JSON.stringify(config || { columns: [] }));
49096
+ }
49097
+ function clampNumber(value, min, max, fallback) {
49098
+ const parsed = Number(value);
49099
+ if (!Number.isFinite(parsed)) {
49100
+ return fallback;
49101
+ }
49102
+ return Math.min(max, Math.max(min, parsed));
49103
+ }
49104
+
46544
49105
  const PRAXIS_TABLE_PORTS = [
46545
49106
  {
46546
49107
  id: 'queryContext',
@@ -47798,4 +50359,4 @@ const TABLE_COMPONENT_AI_CAPABILITIES = {
47798
50359
  * Generated bundle index. Do not edit.
47799
50360
  */
47800
50361
 
47801
- 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, isTableRendererSupportedByRichContentP0, mapTableRendererToRichContentP0, normalizeTableAuthoringDocument, parseLegacyOrTableDocument, providePraxisFilterMetadata, providePraxisTableMetadata, serializeTableAuthoringDocument, toCanonicalTableConfig, validateTableAuthoringDocument };
50362
+ 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 };