@praxisui/table 10.0.0-rc.30 → 10.0.0-rc.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": "1.0.0",
3
- "generatedAt": "2026-10-16T15:16:17.774Z",
3
+ "generatedAt": "2026-10-17T14:44:09.509Z",
4
4
  "packageName": "@praxisui/table",
5
- "packageVersion": "10.0.0-rc.30",
5
+ "packageVersion": "10.0.0-rc.32",
6
6
  "sourceRegistry": "praxis-component-registry-ingestion",
7
7
  "sourceRegistryVersion": "1.0.0",
8
8
  "componentCount": 3,
@@ -423,7 +423,13 @@ function toTableDetailCardGridContentNode(node) {
423
423
  return node;
424
424
  const type = String(node?.['type'] || '').trim();
425
425
  if (type === 'cardGrid') {
426
- const first = Array.isArray(node['cards']) ? node['cards'][0]?.content?.[0] : undefined;
426
+ const firstCard = Array.isArray(node['cards']) ? node['cards'][0] : undefined;
427
+ const firstList = Array.isArray(firstCard?.content)
428
+ ? firstCard.content
429
+ : Array.isArray(firstCard?.items)
430
+ ? firstCard.items
431
+ : undefined;
432
+ const first = firstList?.[0];
427
433
  return first ? toTableDetailCardGridContentNode(structuredClone(first)) : null;
428
434
  }
429
435
  if (type !== 'value')
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { input, inject, computed, ChangeDetectionStrategy, Component, output, signal, InjectionToken, EventEmitter, ElementRef, Output, Input, ViewChild, Optional, Inject, LOCALE_ID, Injectable, Injector, HostListener, Directive, ChangeDetectorRef, DestroyRef, ENVIRONMENT_INITIALIZER, effect, booleanAttribute, HostBinding, ContentChild, TemplateRef, ViewContainerRef, numberAttribute } from '@angular/core';
3
3
  import * as i1 from '@praxisui/core';
4
- import { PraxisI18nService, BulkOperationClientService, isBulkExecutionTerminal, LoggerService, createCorporateLoggerConfig, ConsoleLoggerSink, PraxisJsonLogicService, resolveResourceAvailabilityReasonKey, RESOURCE_DISCOVERY_I18N_CONFIG, RESOURCE_DISCOVERY_I18N_NAMESPACE, PraxisIconDirective, PraxisIconButtonComponent, SafeExpressionEvaluator, API_URL, EnterpriseRuntimeContextService, buildApiUrl, ResourceDiscoveryService, renderPraxisPresentationVisualizationHtml, createDefaultTableConfig, fillUndefined, deepMerge as deepMerge$1, ASYNC_CONFIG_STORAGE, providePraxisI18nConfig, INLINE_FILTER_CONTROL_TYPES, INLINE_FILTER_CONTROL_TYPE_VALUES, FieldControlType, normalizeControlTypeToken, resolveControlTypeAlias, INLINE_FILTER_ALIAS_TOKENS, SCHEMA_METADATA_CLIENT_OPTIONS, GlobalConfigService, buildSchemaId, MemoryCacheAdapter, LocalStorageCacheAdapter, SchemaMetadataClient, mapFieldDefinitionsToMetadata, resolveInlineFilterControlType, fetchWithETag, serializeEntityLookupValueForPayload, GenericCrudService, SchemaNormalizerService, ResourceQuickConnectComponent, ComponentMetadataRegistry, AnalyticsStatsRequestBuilderService, normalizePraxisDataQueryContext, normalizeUnknownError, PraxisRuntimeComponentObservationRegistryService, PRAXIS_TABLE_DETAIL_INLINE_RENDERERS, BulkSyncStateService, PRAXIS_TABLE_DETAIL_INLINE_NODE_RESOLVERS, PRAXIS_TABLE_DETAIL_RESOURCE_RESOLVER, PraxisWidgetEventBus, PraxisCollectionExportService, translateResourceAvailabilityReason, ResourceSurfaceOpenAdapterService, ResourceActionOpenAdapterService, CrudOperationResolutionService, SURFACE_OPEN_PRESETS, SURFACE_DRAWER_REF, assertPraxisCollectionExportArtifact, PRAXIS_LOADING_CTX, resolveColumnTypeFromFieldDefinition as resolveColumnTypeFromFieldDefinition$1, isPraxisPresentationVisualizationTableSafe as isPraxisPresentationVisualizationTableSafe$1, resolveTextMaskFormatFromFieldDefinition as resolveTextMaskFormatFromFieldDefinition$1, translateResourceDiscoveryText, supportsImplicitValuePresentation, resolveValuePresentation, resolvePraxisMicroVisualizationTone, resolveResourceIdentityContract, materializeResourceIdentity, translateUnavailableWorkflowMessage, CONNECTION_STORAGE, PRAXIS_LOADING_RENDERER, EmptyStateCardComponent, getPraxisTableCellVisualizationGuidance, getPraxisTableCellVisualizationConstraint, isTableConfigV2, normalizePraxisPresentationVisualization, PRAXIS_GLOBAL_ACTION_CATALOG, SURFACE_OPEN_I18N_NAMESPACE, isRequiredGlobalActionPayloadMissing, getGlobalActionUiSchema, getRequiredGlobalActionPayloadKeys, hasMeaningfulGlobalActionPayloadValue, SurfaceOpenActionEditorComponent, SURFACE_OPEN_I18N_CONFIG, PraxisJsonLogicError, GLOBAL_ACTION_CATALOG, validateGlobalActionRefs, getGlobalActionCatalog, TableConfigService } from '@praxisui/core';
4
+ import { PraxisI18nService, BulkOperationClientService, isBulkExecutionTerminal, LoggerService, createCorporateLoggerConfig, ConsoleLoggerSink, PraxisJsonLogicService, resolveResourceAvailabilityReasonKey, RESOURCE_DISCOVERY_I18N_CONFIG, RESOURCE_DISCOVERY_I18N_NAMESPACE, PraxisIconDirective, PraxisIconButtonComponent, SafeExpressionEvaluator, API_URL, EnterpriseRuntimeContextService, buildApiUrl, ResourceDiscoveryService, renderPraxisPresentationVisualizationHtml, createDefaultTableConfig, deepMerge as deepMerge$1, ASYNC_CONFIG_STORAGE, providePraxisI18nConfig, INLINE_FILTER_CONTROL_TYPES, INLINE_FILTER_CONTROL_TYPE_VALUES, FieldControlType, normalizeControlTypeToken, resolveControlTypeAlias, INLINE_FILTER_ALIAS_TOKENS, SCHEMA_METADATA_CLIENT_OPTIONS, GlobalConfigService, buildSchemaId, MemoryCacheAdapter, LocalStorageCacheAdapter, SchemaMetadataClient, mapFieldDefinitionsToMetadata, resolveInlineFilterControlType, fetchWithETag, serializeEntityLookupValueForPayload, GenericCrudService, SchemaNormalizerService, ResourceQuickConnectComponent, ComponentMetadataRegistry, AnalyticsStatsRequestBuilderService, normalizePraxisDataQueryContext, normalizeUnknownError, PraxisRuntimeComponentObservationRegistryService, PRAXIS_TABLE_DETAIL_INLINE_RENDERERS, BulkSyncStateService, PRAXIS_TABLE_DETAIL_INLINE_NODE_RESOLVERS, PRAXIS_TABLE_DETAIL_RESOURCE_RESOLVER, PraxisWidgetEventBus, PraxisCollectionExportService, fillUndefined, translateResourceAvailabilityReason, ResourceSurfaceOpenAdapterService, ResourceActionOpenAdapterService, CrudOperationResolutionService, SURFACE_OPEN_PRESETS, SURFACE_DRAWER_REF, assertPraxisCollectionExportArtifact, PRAXIS_LOADING_CTX, resolveColumnTypeFromFieldDefinition as resolveColumnTypeFromFieldDefinition$1, isPraxisPresentationVisualizationTableSafe as isPraxisPresentationVisualizationTableSafe$1, resolveTextMaskFormatFromFieldDefinition as resolveTextMaskFormatFromFieldDefinition$1, translateResourceDiscoveryText, supportsImplicitValuePresentation, resolveValuePresentation, resolvePraxisMicroVisualizationTone, resolveResourceIdentityContract, materializeResourceIdentity, translateUnavailableWorkflowMessage, CONNECTION_STORAGE, PRAXIS_LOADING_RENDERER, EmptyStateCardComponent, getPraxisTableCellVisualizationGuidance, getPraxisTableCellVisualizationConstraint, isTableConfigV2, normalizePraxisPresentationVisualization, PRAXIS_GLOBAL_ACTION_CATALOG, SURFACE_OPEN_I18N_NAMESPACE, isRequiredGlobalActionPayloadMissing, getGlobalActionUiSchema, getRequiredGlobalActionPayloadKeys, hasMeaningfulGlobalActionPayloadValue, SurfaceOpenActionEditorComponent, SURFACE_OPEN_I18N_CONFIG, PraxisJsonLogicError, GLOBAL_ACTION_CATALOG, validateGlobalActionRefs, getGlobalActionCatalog, TableConfigService } from '@praxisui/core';
5
5
  import * as i1$3 from '@angular/common/http';
6
6
  import { HttpErrorResponse, HttpClient, HttpContext } from '@angular/common/http';
7
7
  import * as i3 from '@angular/material/button';
@@ -7649,6 +7649,7 @@ class TableDefaultsProvider {
7649
7649
  }
7650
7650
  /**
7651
7651
  * Returns default configuration for a given table identifier.
7652
+ * Precedence inside defaults: GlobalConfig.table > createDefaultTableConfig().
7652
7653
  * @param tableId Table identifier
7653
7654
  */
7654
7655
  getDefaults(_tableId) {
@@ -7664,11 +7665,17 @@ class TableDefaultsProvider {
7664
7665
  const g = this.global.getTable();
7665
7666
  if (!g)
7666
7667
  return base;
7667
- // Preserve existing provider semantics for non-appearance domains, but let global appearance
7668
- // override hardcoded table defaults so density/spacing/typography can act as tenant-wide fallback.
7669
- const merged = fillUndefined(base, g);
7670
- if (g.appearance) {
7671
- merged.appearance = deepMerge$1(base.appearance, g.appearance);
7668
+ const merged = deepMerge$1(base, g);
7669
+ if (g.filteringUi) {
7670
+ merged.behavior = merged.behavior || {};
7671
+ merged.behavior.filtering = merged.behavior.filtering || {};
7672
+ merged.behavior.filtering.advancedFilters =
7673
+ merged.behavior.filtering.advancedFilters || { enabled: false };
7674
+ const currentSettings = (merged.behavior.filtering.advancedFilters.settings || {});
7675
+ merged.behavior.filtering.advancedFilters.settings = {
7676
+ ...g.filteringUi,
7677
+ ...currentSettings,
7678
+ };
7672
7679
  }
7673
7680
  return merged;
7674
7681
  }
@@ -19981,7 +19988,13 @@ function toTableDetailCardGridContentNode(node) {
19981
19988
  return node;
19982
19989
  const type = String(node?.['type'] || '').trim();
19983
19990
  if (type === 'cardGrid') {
19984
- const first = Array.isArray(node['cards']) ? node['cards'][0]?.content?.[0] : undefined;
19991
+ const firstCard = Array.isArray(node['cards']) ? node['cards'][0] : undefined;
19992
+ const firstList = Array.isArray(firstCard?.content)
19993
+ ? firstCard.content
19994
+ : Array.isArray(firstCard?.items)
19995
+ ? firstCard.items
19996
+ : undefined;
19997
+ const first = firstList?.[0];
19985
19998
  return first ? toTableDetailCardGridContentNode(structuredClone(first)) : null;
19986
19999
  }
19987
20000
  if (type !== 'value')
@@ -21035,7 +21048,10 @@ class PraxisTable {
21035
21048
  'audit-warning': 'audit-review',
21036
21049
  'sync-warning': 'sync-pending',
21037
21050
  };
21038
- config = createDefaultTableConfig();
21051
+ static ngAcceptInputType_config;
21052
+ config = { columns: [] };
21053
+ hostInputConfig;
21054
+ persistedUserConfig;
21039
21055
  resourcePath;
21040
21056
  data = null;
21041
21057
  tableId;
@@ -21050,11 +21066,29 @@ class PraxisTable {
21050
21066
  autoOpenSettingsOnOutdated = false;
21051
21067
  crudContext;
21052
21068
  filterCriteria = {};
21053
- filterTeleport = false;
21069
+ filterCriteriaChange = new EventEmitter();
21070
+ _filterTeleport = false;
21071
+ set filterTeleport(value) {
21072
+ if (this._filterTeleport === value)
21073
+ return;
21074
+ this._filterTeleport = value;
21075
+ this.setShowToolbar(this.shouldExposeToolbar(), true);
21076
+ }
21077
+ get filterTeleport() {
21078
+ return this._filterTeleport;
21079
+ }
21054
21080
  widgetId;
21055
- filterBarTemplate;
21081
+ filterTemplateSignal = signal(undefined, ...(ngDevMode ? [{ debugName: "filterTemplateSignal" }] : /* istanbul ignore next */ []));
21082
+ _filterBarTemplate;
21083
+ set filterBarTemplate(tpl) {
21084
+ this._filterBarTemplate = tpl;
21085
+ this.filterTemplateSignal.set(tpl);
21086
+ }
21087
+ get filterBarTemplate() {
21088
+ return this._filterBarTemplate;
21089
+ }
21056
21090
  get filterTemplate() {
21057
- return this.filterBarTemplate;
21091
+ return this.filterTemplateSignal();
21058
21092
  }
21059
21093
  widgetEventBus = inject(PraxisWidgetEventBus, { optional: true });
21060
21094
  queryContext = null;
@@ -23056,6 +23090,40 @@ class PraxisTable {
23056
23090
  }
23057
23091
  catch { }
23058
23092
  }
23093
+ recomposeEffectiveTableConfig() {
23094
+ const existingColumns = Array.isArray(this.config?.columns)
23095
+ ? [...this.config.columns]
23096
+ : [];
23097
+ const baseInput = this.hostInputConfig
23098
+ ? this.clonePlainObject(this.hostInputConfig)
23099
+ : { columns: [] };
23100
+ if (this.persistedUserConfig) {
23101
+ const storedClone = this.clonePlainObject(this.persistedUserConfig);
23102
+ this.config = this.mergePersistedTableConfigWithInput(storedClone, baseInput);
23103
+ }
23104
+ else {
23105
+ this.config = baseInput;
23106
+ }
23107
+ const globalTable = this.global?.getTable?.();
23108
+ this.hostDeclaredAppearanceDensity =
23109
+ this.hasDeclaredAppearanceDensity(this.persistedUserConfig) ||
23110
+ this.hasDeclaredAppearanceDensity(this.hostInputConfig) ||
23111
+ this.hasDeclaredAppearanceDensity(globalTable);
23112
+ this.hostDeclaredAppearanceSpacing =
23113
+ this.hasDeclaredAppearanceSection(this.persistedUserConfig, 'spacing') ||
23114
+ this.hasDeclaredAppearanceSection(this.hostInputConfig, 'spacing') ||
23115
+ this.hasDeclaredAppearanceSection(globalTable, 'spacing');
23116
+ this.hostDeclaredAppearanceTypography =
23117
+ this.hasDeclaredAppearanceSection(this.persistedUserConfig, 'typography') ||
23118
+ this.hasDeclaredAppearanceSection(this.hostInputConfig, 'typography') ||
23119
+ this.hasDeclaredAppearanceSection(globalTable, 'typography');
23120
+ this.ensureConfigDefaults();
23121
+ if ((!Array.isArray(this.config.columns) || this.config.columns.length === 0) &&
23122
+ existingColumns.length > 0 &&
23123
+ (this.usesSchemaColumnProjection(this.config) || this.isRemoteMode())) {
23124
+ this.config.columns = existingColumns;
23125
+ }
23126
+ }
23059
23127
  hasDeclaredAppearanceDensity(config) {
23060
23128
  const appearance = config?.appearance;
23061
23129
  return !!appearance && Object.prototype.hasOwnProperty.call(appearance, 'density');
@@ -23301,11 +23369,19 @@ class PraxisTable {
23301
23369
  return false;
23302
23370
  if (config.toolbar?.visible)
23303
23371
  return true;
23372
+ if (typeof config.toolbar?.title === 'string' && config.toolbar.title.trim().length > 0)
23373
+ return true;
23374
+ if (typeof config.toolbar?.subtitle === 'string' && config.toolbar.subtitle.trim().length > 0)
23375
+ return true;
23304
23376
  if (config.export?.enabled)
23305
23377
  return true;
23306
23378
  if (config.actions?.bulk?.enabled)
23307
23379
  return true;
23308
- if (this.isAdvancedFiltersEffectivelyEnabled(config))
23380
+ if (!this.filterTeleport && this.isAdvancedFiltersEffectivelyEnabled(config))
23381
+ return true;
23382
+ if (this.getConfiguredQuickFilters().length > 0)
23383
+ return true;
23384
+ if ((this.activeCrossFilters?.length ?? 0) > 0)
23309
23385
  return true;
23310
23386
  return (config.toolbar?.actions?.length ?? 0) > 0;
23311
23387
  }
@@ -23959,7 +24035,7 @@ class PraxisTable {
23959
24035
  if (this.aiAdapter || this.aiAdapterLoadStarted)
23960
24036
  return;
23961
24037
  this.aiAdapterLoadStarted = true;
23962
- import('./praxisui-table-table-ai.adapter-D0gEqzlb.mjs')
24038
+ import('./praxisui-table-table-ai.adapter-DqlbvUHe.mjs')
23963
24039
  .then(({ TableAiAdapter }) => {
23964
24040
  if (!this.isAiAssistantEnabled()) {
23965
24041
  this.aiAssistantOpenAfterAdapterLoad = false;
@@ -24731,7 +24807,7 @@ class PraxisTable {
24731
24807
  initializeAiAssistantController() {
24732
24808
  if (!this.aiAdapter || this.aiAssistantController)
24733
24809
  return;
24734
- import('./praxisui-table-table-agentic-authoring-turn-flow-B2nHomHJ.mjs')
24810
+ import('./praxisui-table-table-agentic-authoring-turn-flow-EvaZNCNd.mjs')
24735
24811
  .then(({ TableAgenticAuthoringTurnFlow }) => {
24736
24812
  if (this.aiAssistantController || !this.aiAdapter)
24737
24813
  return;
@@ -25601,6 +25677,8 @@ class PraxisTable {
25601
25677
  };
25602
25678
  this.queryContextClear.emit(clearEvent);
25603
25679
  this.queryContextChange.emit(this.queryContext);
25680
+ this.filterCriteriaChange.emit({ ...this.getEffectiveEditableFilterCriteria() });
25681
+ this.setShowToolbar(this.shouldExposeToolbar(), true);
25604
25682
  // Host-mediated widgetEvent for composition runtime
25605
25683
  this.widgetEvent.emit({
25606
25684
  ownerWidgetKey: this.componentInstanceId || this.tableId || undefined,
@@ -25659,6 +25737,8 @@ class PraxisTable {
25659
25737
  };
25660
25738
  this.queryContextClear.emit(clearEvent);
25661
25739
  this.queryContextChange.emit(this.queryContext);
25740
+ this.filterCriteriaChange.emit({ ...this.getEffectiveEditableFilterCriteria() });
25741
+ this.setShowToolbar(this.shouldExposeToolbar(), true);
25662
25742
  this.widgetEvent.emit({
25663
25743
  ownerWidgetKey: this.componentInstanceId || this.tableId || undefined,
25664
25744
  sourceComponentId: 'praxis-table',
@@ -25804,8 +25884,11 @@ class PraxisTable {
25804
25884
  ref.saved$.subscribe(apply);
25805
25885
  }
25806
25886
  async ngOnInit() {
25887
+ if (!this.hostInputConfig && this.config) {
25888
+ this.hostInputConfig = this.clonePlainObject(this.config);
25889
+ }
25807
25890
  // Ensure config exists before any awaited operations to avoid template errors
25808
- this.ensureConfigDefaults();
25891
+ this.recomposeEffectiveTableConfig();
25809
25892
  this.enforceUnsupportedFeatureGuards();
25810
25893
  this.syncPaginatorIntl();
25811
25894
  this.setShowToolbar(this.shouldExposeToolbar(), true);
@@ -25821,6 +25904,21 @@ class PraxisTable {
25821
25904
  }
25822
25905
  }));
25823
25906
  }
25907
+ if (this.global?.configChanges$) {
25908
+ this.subscriptions.push(this.global.configChanges$.subscribe(() => {
25909
+ this.recomposeEffectiveTableConfig();
25910
+ this.enforceUnsupportedFeatureGuards();
25911
+ this.syncPaginatorIntl();
25912
+ this.syncHorizontalScrollFromAppearanceConfig();
25913
+ this.setShowToolbar(this.shouldExposeToolbar(), true);
25914
+ this.applyAppearanceVariables();
25915
+ this.cdr.markForCheck();
25916
+ }));
25917
+ }
25918
+ try {
25919
+ await this.global?.ready?.();
25920
+ }
25921
+ catch { }
25824
25922
  // Resolve global fallback for schema prefs
25825
25923
  try {
25826
25924
  await this.resolveSchemaPrefs();
@@ -25851,14 +25949,8 @@ class PraxisTable {
25851
25949
  try {
25852
25950
  const storedConfig = await firstValueFrom(this.asyncConfigStorage.loadConfig(cfgKey));
25853
25951
  if (storedConfig) {
25854
- this.hostDeclaredAppearanceDensity =
25855
- this.hostDeclaredAppearanceDensity || this.hasDeclaredAppearanceDensity(storedConfig);
25856
- this.hostDeclaredAppearanceSpacing =
25857
- this.hostDeclaredAppearanceSpacing || this.hasDeclaredAppearanceSection(storedConfig, 'spacing');
25858
- this.hostDeclaredAppearanceTypography =
25859
- this.hostDeclaredAppearanceTypography || this.hasDeclaredAppearanceSection(storedConfig, 'typography');
25860
- this.config = this.mergePersistedTableConfigWithInput(storedConfig, this.config);
25861
- this.ensureConfigDefaults();
25952
+ this.persistedUserConfig = this.clonePlainObject(storedConfig);
25953
+ this.recomposeEffectiveTableConfig();
25862
25954
  this.enforceUnsupportedFeatureGuards();
25863
25955
  this.setupColumns();
25864
25956
  this.cdr.markForCheck();
@@ -25883,8 +25975,8 @@ class PraxisTable {
25883
25975
  }
25884
25976
  }
25885
25977
  catch { }
25886
- // Apply global defaults as fallback only for missing properties
25887
- this.ensureConfigDefaults();
25978
+ // Recompose effective config after GlobalConfig.ready() and persisted user config hydration
25979
+ this.recomposeEffectiveTableConfig();
25888
25980
  this.resetDetailRuntimeForConfig();
25889
25981
  this.syncPaginatorIntl();
25890
25982
  // ngOnInit is async and Angular does not await it before ngAfterContentInit.
@@ -25908,6 +26000,7 @@ class PraxisTable {
25908
26000
  resetPageSizeOverride: true,
25909
26001
  });
25910
26002
  this.applyDataSourceSettings();
26003
+ this.applyAppearanceVariables();
25911
26004
  if (this.isAiAssistantEnabled()) {
25912
26005
  this.ensureAiAdapterLoaded();
25913
26006
  }
@@ -25938,10 +26031,24 @@ class PraxisTable {
25938
26031
  this.localPageSizeRuntimeOverride = false;
25939
26032
  }
25940
26033
  if (changes['config']?.currentValue && this.config) {
25941
- this.hostDeclaredAppearanceDensity = this.hasDeclaredAppearanceDensity(changes['config'].currentValue);
25942
- this.hostDeclaredAppearanceSpacing = this.hasDeclaredAppearanceSection(changes['config'].currentValue, 'spacing');
25943
- this.hostDeclaredAppearanceTypography = this.hasDeclaredAppearanceSection(changes['config'].currentValue, 'typography');
26034
+ this.hostInputConfig = this.clonePlainObject(changes['config'].currentValue);
26035
+ const globalTable = this.global?.getTable?.();
26036
+ this.hostDeclaredAppearanceDensity =
26037
+ this.hasDeclaredAppearanceDensity(this.persistedUserConfig) ||
26038
+ this.hasDeclaredAppearanceDensity(changes['config'].currentValue) ||
26039
+ this.hasDeclaredAppearanceDensity(globalTable);
26040
+ this.hostDeclaredAppearanceSpacing =
26041
+ this.hasDeclaredAppearanceSection(this.persistedUserConfig, 'spacing') ||
26042
+ this.hasDeclaredAppearanceSection(changes['config'].currentValue, 'spacing') ||
26043
+ this.hasDeclaredAppearanceSection(globalTable, 'spacing');
26044
+ this.hostDeclaredAppearanceTypography =
26045
+ this.hasDeclaredAppearanceSection(this.persistedUserConfig, 'typography') ||
26046
+ this.hasDeclaredAppearanceSection(changes['config'].currentValue, 'typography') ||
26047
+ this.hasDeclaredAppearanceSection(globalTable, 'typography');
25944
26048
  this.config = this.reconcileTableConfigInputChange(changes['config'].previousValue, changes['config'].currentValue);
26049
+ if (this.persistedUserConfig) {
26050
+ this.persistedUserConfig = this.clonePlainObject(this.config);
26051
+ }
25945
26052
  }
25946
26053
  this.ensureConfigDefaults();
25947
26054
  this.enforceUnsupportedFeatureGuards();
@@ -26081,6 +26188,16 @@ class PraxisTable {
26081
26188
  shouldFetchRemoteDataForQueryChanges(changes) {
26082
26189
  if (!this.isRemoteMode())
26083
26190
  return false;
26191
+ if (changes['filterCriteria'] &&
26192
+ !changes['queryContext'] &&
26193
+ this.lastAppliedFilterCriteria !== null) {
26194
+ const incoming = normalizePraxisFilterCriteriaRecord(changes['filterCriteria'].currentValue);
26195
+ const prevApplied = this.lastAppliedFilterCriteria;
26196
+ this.lastAppliedFilterCriteria = null;
26197
+ if (this.hasEquivalentFilterCriteria(incoming, prevApplied)) {
26198
+ return false;
26199
+ }
26200
+ }
26084
26201
  if (this.hasInitialized)
26085
26202
  return true;
26086
26203
  const queryChanges = [changes['filterCriteria'], changes['queryContext']];
@@ -28362,8 +28479,13 @@ class PraxisTable {
28362
28479
  return false;
28363
28480
  if (visibleFieldNames.has(name))
28364
28481
  return false;
28365
- if (name.startsWith('_') || name === 'id' || name === '__v')
28482
+ if (name.startsWith('_')
28483
+ || name === 'id'
28484
+ || name === '__v'
28485
+ || name === 'version'
28486
+ || name === 'resourceVersion') {
28366
28487
  return false;
28488
+ }
28367
28489
  if (this.isSensitiveAiAssistantField(name))
28368
28490
  return false;
28369
28491
  return true;
@@ -29055,7 +29177,7 @@ class PraxisTable {
29055
29177
  childKeys.push('items');
29056
29178
  }
29057
29179
  if (type === 'tab' || type === 'card') {
29058
- childKeys.push('content');
29180
+ childKeys.push('content', 'items');
29059
29181
  }
29060
29182
  for (const childKey of childKeys) {
29061
29183
  const rawChildren = clone[childKey];
@@ -32289,6 +32411,7 @@ class PraxisTable {
32289
32411
  this.clearQuickFilterRuntimeState();
32290
32412
  this.applyAdvancedFilterCriteria(editableCriteria, 'advanced-filter-submit');
32291
32413
  this.syncQuickFilterStateFromEffectiveCriteria();
32414
+ this.filterCriteriaChange.emit({ ...this.filterCriteria });
32292
32415
  if (this.widgetEventBus) {
32293
32416
  this.widgetEventBus.publish({
32294
32417
  sourceWidgetId: this.widgetId || this.tableId,
@@ -32305,6 +32428,7 @@ class PraxisTable {
32305
32428
  this.clearQuickFilterRuntimeState();
32306
32429
  this.applyAdvancedFilterCriteria({}, 'advanced-filter-clear');
32307
32430
  this.syncQuickFilterStateFromEffectiveCriteria();
32431
+ this.filterCriteriaChange.emit({});
32308
32432
  if (this.widgetEventBus) {
32309
32433
  this.widgetEventBus.publish({
32310
32434
  sourceWidgetId: this.widgetId || this.tableId,
@@ -32336,6 +32460,7 @@ class PraxisTable {
32336
32460
  this.activeQuickFilterId = id;
32337
32461
  this.activeQuickFilterCriteria = {};
32338
32462
  this.applyAdvancedFilterCriteria({}, 'quick-filter-apply');
32463
+ this.filterCriteriaChange.emit({});
32339
32464
  return;
32340
32465
  }
32341
32466
  const current = normalizePraxisFilterCriteriaRecord(this.filterCriteria);
@@ -32353,12 +32478,14 @@ class PraxisTable {
32353
32478
  this.clearQuickFilterRuntimeState();
32354
32479
  this.applyAdvancedFilterCriteria(cleanedBase, 'quick-filter-clear');
32355
32480
  this.syncQuickFilterStateFromEffectiveCriteria();
32481
+ this.filterCriteriaChange.emit({ ...this.filterCriteria });
32356
32482
  return;
32357
32483
  }
32358
32484
  this.quickFilterBaseCriteria = cleanedBase;
32359
32485
  this.activeQuickFilterId = id;
32360
32486
  this.activeQuickFilterCriteria = { ...cleanedBase, ...patch };
32361
32487
  this.applyAdvancedFilterCriteria(this.activeQuickFilterCriteria, 'quick-filter-apply');
32488
+ this.filterCriteriaChange.emit({ ...this.filterCriteria });
32362
32489
  }
32363
32490
  getConfiguredQuickFilters() {
32364
32491
  const filters = this.config?.toolbar?.filters;
@@ -32501,6 +32628,7 @@ class PraxisTable {
32501
32628
  };
32502
32629
  this.queryContextClear?.emit(clearEvent);
32503
32630
  this.queryContextChange?.emit(this.queryContext);
32631
+ this.filterCriteriaChange.emit({ ...this.getEffectiveEditableFilterCriteria() });
32504
32632
  this.widgetEvent?.emit({
32505
32633
  ownerWidgetKey: this.componentInstanceId || this.tableId || undefined,
32506
32634
  sourceComponentId: 'praxis-table',
@@ -32556,8 +32684,15 @@ class PraxisTable {
32556
32684
  }
32557
32685
  hasEquivalentFilterCriteria(left, right) {
32558
32686
  const canonicalize = (value) => {
32559
- if (Array.isArray(value))
32560
- return value.map(canonicalize);
32687
+ if (Array.isArray(value)) {
32688
+ const items = value.map(canonicalize);
32689
+ if (items.length === 2 &&
32690
+ typeof items[0] === 'number' &&
32691
+ typeof items[1] === 'number') {
32692
+ return { end: items[1], start: items[0] };
32693
+ }
32694
+ return items;
32695
+ }
32561
32696
  if (!value || typeof value !== 'object')
32562
32697
  return value;
32563
32698
  return Object.keys(value)
@@ -32569,8 +32704,10 @@ class PraxisTable {
32569
32704
  };
32570
32705
  return JSON.stringify(canonicalize(left)) === JSON.stringify(canonicalize(right));
32571
32706
  }
32707
+ lastAppliedFilterCriteria = null;
32572
32708
  applyAdvancedFilterCriteria(criteria, reason = 'external-api') {
32573
32709
  this.setAdvancedFilterCriteria(criteria);
32710
+ this.lastAppliedFilterCriteria = { ...this.filterCriteria };
32574
32711
  this.applyExpansionCollapsePolicy('filterChange');
32575
32712
  this.clearCellAnimationChangeState();
32576
32713
  this.pageIndex = 0;
@@ -33056,6 +33193,7 @@ class PraxisTable {
33056
33193
  const compactConfig = this.toPortableTableConfig(plan.canonicalConfig);
33057
33194
  const cfg = this.attachRuntimeMetadataToEditorConfig(compactConfig, plan.metadata?.attachServerMeta === true && !shouldReloadSchema, plan.persistence?.saveConfig === true);
33058
33195
  if (plan.persistence?.saveConfig) {
33196
+ this.persistedUserConfig = this.clonePlainObject(cfg);
33059
33197
  const cfgKey = this.tableConfigKey();
33060
33198
  if (cfgKey) {
33061
33199
  this.saveConfigWithAck(cfgKey, cfg, 'table-settings-save', {
@@ -33224,8 +33362,27 @@ class PraxisTable {
33224
33362
  onResetPersistenceSettled(false, 'reset-filter-config');
33225
33363
  });
33226
33364
  }
33365
+ this.persistedUserConfig = undefined;
33227
33366
  const defaults = this.tableDefaultsProvider.getDefaults(this.tableId);
33228
- this.applyTableConfig(defaults);
33367
+ const restoredConfig = this.hostInputConfig
33368
+ ? fillUndefined(this.clonePlainObject(this.hostInputConfig), defaults)
33369
+ : defaults;
33370
+ if ((!Array.isArray(restoredConfig.columns) || restoredConfig.columns.length === 0) &&
33371
+ this.schemaColumnFieldsSnapshot.length > 0 &&
33372
+ (this.usesSchemaColumnProjection(restoredConfig) || this.isRemoteMode())) {
33373
+ restoredConfig.columns = this.projectSchemaColumns(this.schemaColumnFieldsSnapshot, restoredConfig.columnProjection);
33374
+ }
33375
+ const globalTable = this.global?.getTable?.();
33376
+ this.hostDeclaredAppearanceDensity =
33377
+ this.hasDeclaredAppearanceDensity(this.hostInputConfig) ||
33378
+ this.hasDeclaredAppearanceDensity(globalTable);
33379
+ this.hostDeclaredAppearanceSpacing =
33380
+ this.hasDeclaredAppearanceSection(this.hostInputConfig, 'spacing') ||
33381
+ this.hasDeclaredAppearanceSection(globalTable, 'spacing');
33382
+ this.hostDeclaredAppearanceTypography =
33383
+ this.hasDeclaredAppearanceSection(this.hostInputConfig, 'typography') ||
33384
+ this.hasDeclaredAppearanceSection(globalTable, 'typography');
33385
+ this.applyTableConfig(restoredConfig);
33229
33386
  if (operationCount === 0) {
33230
33387
  this.showResetSuccessFeedback(successTrigger);
33231
33388
  }
@@ -34799,6 +34956,7 @@ class PraxisTable {
34799
34956
  .subscribe({
34800
34957
  next: () => {
34801
34958
  acknowledged = true;
34959
+ this.persistedUserConfig = this.clonePlainObject(payload);
34802
34960
  if (applyReconciledConfigOnSuccess) {
34803
34961
  this.config = payload;
34804
34962
  this.runColumnResizeLayoutMutation(() => this.setupColumns());
@@ -35098,7 +35256,11 @@ class PraxisTable {
35098
35256
  this.config?.toolbar?.filters?.enabled !== false &&
35099
35257
  (!this.config?.toolbar?.filters?.quickFilters || this.config.toolbar.filters.quickFilters.length === 0)) {
35100
35258
  const toolbar = this.ensureToolbarConfigForRuntime();
35101
- toolbar.filters = toolbar.filters || { enabled: true, showAdvancedButton: true };
35259
+ const globalShowAdvanced = this.global?.getTable?.()?.toolbar?.filters?.showAdvancedButton;
35260
+ toolbar.filters = toolbar.filters || {
35261
+ enabled: true,
35262
+ showAdvancedButton: typeof globalShowAdvanced === 'boolean' ? globalShowAdvanced : true,
35263
+ };
35102
35264
  toolbar.filters.quickFilters = [...discoveredQuickFilters];
35103
35265
  if (toolbar.filters.enabled === undefined) {
35104
35266
  toolbar.filters.enabled = true;
@@ -38300,8 +38462,56 @@ class PraxisTable {
38300
38462
  }, { dedupeKey: 'praxis-table:expansion:virtualization-fail-closed' });
38301
38463
  return false;
38302
38464
  }
38465
+ if (!this.hasComplementarySchemaExpansionFields()) {
38466
+ return false;
38467
+ }
38303
38468
  return true;
38304
38469
  }
38470
+ hasComplementarySchemaExpansionFields() {
38471
+ const rawMode = String(this.getDetailConfig()?.source?.mode
38472
+ || this.getExpansionConfig()?.source
38473
+ || this.getExpansionConfig()?.mode
38474
+ || (this.getExpansionConfig()?.enabled && !this.getDetailConfig()?.source?.inlineSchema ? 'schema' : 'inline')).trim();
38475
+ if (rawMode !== 'schema')
38476
+ return true;
38477
+ if (this.getExpansionFallbackMode() !== 'none')
38478
+ return true;
38479
+ const schemaFields = this.schemaColumnFieldsSnapshot && this.schemaColumnFieldsSnapshot.length > 0
38480
+ ? this.schemaColumnFieldsSnapshot
38481
+ : this.schemaFieldsSnapshot && this.schemaFieldsSnapshot.length > 0
38482
+ ? this.schemaFieldsSnapshot
38483
+ : null;
38484
+ if (!schemaFields || schemaFields.length === 0)
38485
+ return true;
38486
+ const visibleFieldNames = new Set();
38487
+ const activeColumns = this.visibleColumns && this.visibleColumns.length > 0
38488
+ ? this.visibleColumns
38489
+ : (this.config?.columns || []).filter((col) => col && col.visible !== false);
38490
+ for (const col of activeColumns) {
38491
+ if (col?.field)
38492
+ visibleFieldNames.add(col.field);
38493
+ }
38494
+ const idField = this.getIdField();
38495
+ if (idField)
38496
+ visibleFieldNames.add(idField);
38497
+ return schemaFields.some((f) => {
38498
+ const name = String(f?.name || '').trim();
38499
+ if (!name)
38500
+ return false;
38501
+ if (visibleFieldNames.has(name))
38502
+ return false;
38503
+ if (name.startsWith('_')
38504
+ || name === 'id'
38505
+ || name === '__v'
38506
+ || name === 'version'
38507
+ || name === 'resourceVersion') {
38508
+ return false;
38509
+ }
38510
+ if (this.isSensitiveAiAssistantField(name))
38511
+ return false;
38512
+ return true;
38513
+ });
38514
+ }
38305
38515
  enforceAppearanceSchemaGuards() {
38306
38516
  const appearance = (this.config?.appearance || {});
38307
38517
  if (!appearance || typeof appearance !== 'object')
@@ -42266,7 +42476,7 @@ class PraxisTable {
42266
42476
  this.removeViewportChangeListeners = null;
42267
42477
  }
42268
42478
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisTable, deps: [{ token: i0.ChangeDetectorRef }, { token: i3$2.SettingsPanelService }, { token: i1.GenericCrudService }, { token: TableDefaultsProvider }, { token: FilterConfigService }, { token: DataFormattingService }, { token: i6$1.PraxisDialog }, { token: i4$2.MatSnackBar }, { token: ASYNC_CONFIG_STORAGE }, { token: CONNECTION_STORAGE }, { token: i0.ElementRef }, { token: i1.GlobalConfigService }, { token: i0.Injector }, { token: i1.ComponentKeyService }, { token: i1.LoadingOrchestrator }, { token: i1.GlobalActionService }, { token: i1.EnterpriseRuntimeContextService, optional: true }, { token: PRAXIS_LOADING_RENDERER, optional: true }, { token: i8$1.ActivatedRoute, optional: true }, { token: i1.LoggerService, optional: true }, { token: AnalyticsTableStatsApiService, optional: true }], target: i0.ɵɵFactoryTarget.Component });
42269
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisTable, isStandalone: true, selector: "praxis-table", inputs: { config: "config", resourcePath: "resourcePath", data: "data", tableId: "tableId", componentInstanceId: "componentInstanceId", configPersistenceStrategy: "configPersistenceStrategy", title: "title", subtitle: "subtitle", icon: "icon", autoDelete: "autoDelete", notifyIfOutdated: "notifyIfOutdated", snoozeMs: "snoozeMs", autoOpenSettingsOnOutdated: "autoOpenSettingsOnOutdated", crudContext: "crudContext", filterCriteria: "filterCriteria", filterTeleport: ["filterTeleport", "filterTeleport", booleanAttribute], widgetId: "widgetId", queryContext: "queryContext", aiContext: "aiContext", aiAssistantVoiceInputMode: "aiAssistantVoiceInputMode", aiAssistantVoiceLanguage: "aiAssistantVoiceLanguage", horizontalScroll: "horizontalScroll", enableCustomization: ["enableCustomization", "enableCustomization", booleanAttribute], authoringCapability: "authoringCapability", dense: "dense" }, outputs: { queryContextClear: "queryContextClear", queryContextChange: "queryContextChange", rowClick: "rowClick", widgetEvent: "widgetEvent", resourceEvent: "resourceEvent", rowDoubleClick: "rowDoubleClick", rowExpansionChange: "rowExpansionChange", rowAction: "rowAction", toolbarAction: "toolbarAction", bulkAction: "bulkAction", exportAction: "exportAction", columnReorder: "columnReorder", columnReorderAttempt: "columnReorderAttempt", columnResize: "columnResize", beforeDelete: "beforeDelete", afterDelete: "afterDelete", deleteError: "deleteError", beforeBulkDelete: "beforeBulkDelete", afterBulkDelete: "afterBulkDelete", bulkDeleteError: "bulkDeleteError", schemaStatusChange: "schemaStatusChange", configChange: "configChange", metadataChange: "metadataChange", loadingStateChange: "loadingStateChange", collectionLinksChange: "collectionLinksChange", selectionChange: "selectionChange", drawerOpen: "drawerOpen", drawerClose: "drawerClose", drawerTabChange: "drawerTabChange" }, host: { properties: { "class.density-compact": "this.hostDensityCompactClass", "class.density-comfortable": "this.hostDensityComfortableClass", "class.density-spacious": "this.hostDensitySpaciousClass", "class.row-borders": "this.hostRowBordersClass", "class.col-borders": "this.hostColumnBordersClass" } }, providers: [
42479
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisTable, isStandalone: true, selector: "praxis-table", inputs: { config: "config", resourcePath: "resourcePath", data: "data", tableId: "tableId", componentInstanceId: "componentInstanceId", configPersistenceStrategy: "configPersistenceStrategy", title: "title", subtitle: "subtitle", icon: "icon", autoDelete: "autoDelete", notifyIfOutdated: "notifyIfOutdated", snoozeMs: "snoozeMs", autoOpenSettingsOnOutdated: "autoOpenSettingsOnOutdated", crudContext: "crudContext", filterCriteria: "filterCriteria", filterTeleport: ["filterTeleport", "filterTeleport", booleanAttribute], widgetId: "widgetId", queryContext: "queryContext", aiContext: "aiContext", aiAssistantVoiceInputMode: "aiAssistantVoiceInputMode", aiAssistantVoiceLanguage: "aiAssistantVoiceLanguage", horizontalScroll: "horizontalScroll", enableCustomization: ["enableCustomization", "enableCustomization", booleanAttribute], authoringCapability: "authoringCapability", dense: "dense" }, outputs: { filterCriteriaChange: "filterCriteriaChange", queryContextClear: "queryContextClear", queryContextChange: "queryContextChange", rowClick: "rowClick", widgetEvent: "widgetEvent", resourceEvent: "resourceEvent", rowDoubleClick: "rowDoubleClick", rowExpansionChange: "rowExpansionChange", rowAction: "rowAction", toolbarAction: "toolbarAction", bulkAction: "bulkAction", exportAction: "exportAction", columnReorder: "columnReorder", columnReorderAttempt: "columnReorderAttempt", columnResize: "columnResize", beforeDelete: "beforeDelete", afterDelete: "afterDelete", deleteError: "deleteError", beforeBulkDelete: "beforeBulkDelete", afterBulkDelete: "afterBulkDelete", bulkDeleteError: "bulkDeleteError", schemaStatusChange: "schemaStatusChange", configChange: "configChange", metadataChange: "metadataChange", loadingStateChange: "loadingStateChange", collectionLinksChange: "collectionLinksChange", selectionChange: "selectionChange", drawerOpen: "drawerOpen", drawerClose: "drawerClose", drawerTabChange: "drawerTabChange" }, host: { properties: { "class.density-compact": "this.hostDensityCompactClass", "class.density-comfortable": "this.hostDensityComfortableClass", "class.density-spacious": "this.hostDensitySpaciousClass", "class.row-borders": "this.hostRowBordersClass", "class.col-borders": "this.hostColumnBordersClass" } }, providers: [
42270
42480
  providePraxisI18nConfig(RESOURCE_DISCOVERY_I18N_CONFIG),
42271
42481
  providePraxisI18nConfig(PRAXIS_TABLE_RUNTIME_I18N_CONFIG),
42272
42482
  MatPaginatorIntl,
@@ -42365,6 +42575,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
42365
42575
  type: Input
42366
42576
  }], filterCriteria: [{
42367
42577
  type: Input
42578
+ }], filterCriteriaChange: [{
42579
+ type: Output
42368
42580
  }], filterTeleport: [{
42369
42581
  type: Input,
42370
42582
  args: [{ transform: booleanAttribute }]
@@ -73679,7 +73891,7 @@ class PraxisTableConfigEditor {
73679
73891
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisTableConfigEditor, isStandalone: true, selector: "praxis-table-config-editor", providers: [
73680
73892
  TableConfigService,
73681
73893
  providePraxisI18nConfig(PRAXIS_TABLE_EDITOR_I18N_CONFIG),
73682
- ], viewQueries: [{ propertyName: "filterSettingsEditor", first: true, predicate: ["tableFilterSettingsRef"], descendants: true }, { propertyName: "behaviorEditor", first: true, predicate: BehaviorConfigEditorComponent, descendants: true }, { propertyName: "detailEditor", first: true, predicate: TableDetailEditorComponent, descendants: true }, { propertyName: "rulesEditor", first: true, predicate: TableRulesEditorComponent, descendants: true }, { propertyName: "crudEditorSetter", first: true, predicate: ["crudEditorRef"], descendants: true }], ngImport: i0, template: "<div class=\"editor-section-selector\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('sections.selector', 'Editor section') }}</mat-label>\n <mat-select [(ngModel)]=\"activeSectionIndex\" data-testid=\"table-config-editor-sections\">\n @for (section of sections; track section.id; let index = $index) {\n <mat-option [value]=\"index\">{{ section.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n</div>\n<mat-tab-group [preserveContent]=\"true\" class=\"config-tabs\" [(selectedIndex)]=\"activeSectionIndex\">\n @for (section of sections; track section.id) {\n <mat-tab>\n <ng-template mat-tab-label>\n @if (section.icon) { <mat-icon [praxisIcon]=\"section.icon\"></mat-icon> }\n <span [attr.data-testid]=\"'config-tab-' + section.id\">{{ section.label }}</span>\n </ng-template>\n <div class=\"tab-content\">\n @switch (section.id) {\n @case ('connect') {\n <div class=\"connect-grid\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('connect.resource', 'Resource') }}</mat-label>\n <input matInput [(ngModel)]=\"resourcePath\" (ngModelChange)=\"onResourcePathChange($event)\" [placeholder]=\"tx('connect.resource.placeholder', 'e.g. employees')\" />\n <mat-icon matSuffix>link</mat-icon>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('connect.primaryKey', 'Primary key') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"idField\"\n (ngModelChange)=\"onIdFieldChange($event)\"\n [matAutocomplete]=\"idFieldPicker\"\n [placeholder]=\"tx('connect.primaryKey.placeholder', 'e.g. id, uuid, code')\"\n />\n <mat-icon matSuffix>fingerprint</mat-icon>\n <mat-autocomplete\n #idFieldPicker=\"matAutocomplete\"\n (optionSelected)=\"onIdFieldOptionSelected($event.option.value)\"\n >\n @for (option of filteredIdFieldOptions; track option.name) {\n <mat-option [value]=\"option.name\">\n <span>{{ option.name }}</span>\n @if (option.label && option.label !== option.name) {\n <small> - {{ option.label }}</small>\n }\n </mat-option>\n }\n @if (filteredIdFieldOptions.length === 0) {\n <mat-option disabled>\n {{ tx('connect.primaryKey.empty', 'No matching field found.') }}\n </mat-option>\n }\n </mat-autocomplete>\n </mat-form-field>\n <p class=\"connect-note\">{{ tx('connect.note', 'Define the API resource and, when needed, the primary key.') }}</p>\n <!-- Diverg\u00EAncia: idField -->\n @if (idFieldDiverges) { <div class=\"alert alert--warning connect-alert\">\n <mat-icon class=\"alert__icon\">warning</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n {{ tx('connect.serverPrimaryKeyDiverges', 'The primary key on the server differs from the current configuration.') }}\n <span class=\"connect-alert__meta\">(config: {{ idField || 'id' }} | servidor: {{ serverIdField || 'id' }})</span>\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onReconcileIdField()\">{{ tx('connect.reconcile', 'Reconcile') }}</button>\n </div> }\n <!-- Diverg\u00EAncia: serverHash -->\n @if (schemaHashDiverges) { <div class=\"alert alert--info connect-alert\">\n <mat-icon class=\"alert__icon\">info</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n {{ tx('connect.schemaSnapshotDiverges', 'The saved schema snapshot differs from the current server schema.') }}\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onAcceptServerHash()\">{{ tx('connect.updateMetadata', 'Update metadata') }}</button>\n </div> }\n @if (!schemaHashDiverges && schemaSnapshotMissing) { <div class=\"alert alert--info connect-alert\">\n <mat-icon class=\"alert__icon\">info</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n {{ tx('connect.schemaSnapshotMissing', 'A current schema exists on the server, but there is still no persisted snapshot for reconciliation.') }}\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onAcceptServerHash()\">{{ tx('connect.saveSnapshot', 'Save snapshot') }}</button>\n </div> }\n </div>\n }\n @case ('overview') {\n <div class=\"overview-grid\">\n <section class=\"responsive-settings\" aria-labelledby=\"table-responsive-title\">\n <div class=\"responsive-settings__heading\">\n <mat-icon aria-hidden=\"true\">devices</mat-icon>\n <h3 id=\"table-responsive-title\">{{ tx('overview.responsive.title', 'Narrow screens') }}</h3>\n </div>\n <p>{{ tx('overview.responsive.description', 'Choose how records appear when the table has less space.') }}</p>\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('overview.responsive.mode', 'Record layout') }}</mat-label>\n <mat-select [ngModel]=\"mobileCardMode\" (ngModelChange)=\"onMobileCardModeChange($event)\">\n <mat-option [value]=\"false\">{{ tx('overview.responsive.table', 'Table') }}</mat-option>\n <mat-option [value]=\"true\">{{ tx('overview.responsive.cards', 'Cards') }}</mat-option>\n </mat-select>\n </mat-form-field>\n @if (mobileCardMode) {\n <p class=\"responsive-settings__hint\">{{ tx('overview.responsive.cardsHint', 'Each record becomes a card with its visible columns. Selection and row actions remain available. Wider areas keep the table layout.') }}</p>\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('overview.responsive.breakpoint', 'Show cards at widths up to') }}</mat-label>\n <input matInput type=\"number\" min=\"1\" step=\"1\" required\n [ngModel]=\"mobileBreakpoint\" (ngModelChange)=\"onMobileBreakpointChange($event)\"\n [attr.aria-invalid]=\"mobileBreakpointInvalid\"\n aria-describedby=\"table-responsive-width-help\" />\n <span matTextSuffix>px</span>\n </mat-form-field>\n @if (mobileBreakpointInvalid) {\n <p class=\"responsive-settings__error\" role=\"alert\">{{ tx('overview.responsive.invalidWidth', 'Enter a positive whole number.') }}</p>\n }\n <p id=\"table-responsive-width-help\" class=\"responsive-settings__hint\">{{ tx('overview.responsive.widthHint', 'Uses the smaller width of the screen and the table area. Default: 768 px.') }}</p>\n <p class=\"responsive-settings__hint\">{{ tx('overview.responsive.virtualizationHint', 'Cards require standard rows. With virtual scrolling enabled, records remain in a table.') }}</p>\n }\n </section>\n <div class=\"overview-row\">\n <mat-form-field appearance=\"outline\" class=\"hs-field\">\n <mat-label>{{ tx('overview.horizontalScroll', 'Horizontal scroll') }}</mat-label>\n <mat-select [(ngModel)]=\"horizontalScroll\" (ngModelChange)=\"onHorizontalScrollChange($event)\">\n <mat-option value=\"auto\">{{ tx('overview.horizontalScroll.auto', 'Auto (default)') }}</mat-option>\n <mat-option value=\"wrap\">{{ tx('overview.horizontalScroll.wrap', 'Wrap') }}</mat-option>\n <mat-option value=\"none\">{{ tx('overview.horizontalScroll.none', 'Host controls') }}</mat-option>\n </mat-select>\n </mat-form-field>\n <div class=\"help small\">{{ tx('overview.horizontalScroll.help', 'Controls how the table handles horizontal width and scrollbar behavior.') }}</div>\n </div>\n @if (isEffectiveLocalMode()) { <div class=\"alert alert--info overview-mode-alert\">\n <mat-icon class=\"alert__icon\">info</mat-icon>\n <div class=\"alert__text\">\n {{ tx('overview.localModeActive', 'Effective local mode is active: server pagination and sorting strategies are blocked and persisted as client mode.') }}\n </div>\n </div> }\n <behavior-config-editor\n [config]=\"editedConfig\"\n [resourcePath]=\"resourcePath\"\n [forceClientStrategies]=\"isEffectiveLocalMode()\"\n (configChange)=\"onBehaviorConfigChange($event)\"\n (behaviorChange)=\"onBehaviorChange($event)\"\n ></behavior-config-editor>\n </div>\n }\n @case ('columns') {\n <columns-config-editor\n [config]=\"editedConfig\"\n [fieldMetadata]=\"getColumnAuthoringMetas()\"\n (configChange)=\"onColumnsConfigChange($event)\"\n (columnChange)=\"onColumnChange($event)\"\n ></columns-config-editor>\n }\n @case ('detail') {\n <praxis-table-detail-editor [config]=\"editedConfig\"\n (configChange)=\"onDetailConfigChange($event)\"\n (draftStateChange)=\"onDetailDraftStateChange($event)\"\n ></praxis-table-detail-editor>\n }\n @case ('rules') {\n <table-rules-editor\n [config]=\"editedConfig\"\n [resourcePath]=\"resourcePath\"\n (configChange)=\"onRulesConfigChange($event)\"\n ></table-rules-editor>\n }\n @case ('header') {\n <header-appearance-editor\n [config]=\"editedConfig\"\n (configChange)=\"onColumnsConfigChange($event)\"\n ></header-appearance-editor>\n }\n @case ('toolbar') {\n <toolbar-actions-editor\n [config]=\"editedConfig\"\n (configChange)=\"onToolbarActionsConfigChange($event)\"\n (toolbarActionsChange)=\"onToolbarActionsChange($event)\"\n (validationChange)=\"onSurfaceDraftValidationChange($event)\"\n ></toolbar-actions-editor>\n }\n <!-- Aba extra para integra\u00E7\u00F5es CRUD (vis\u00EDvel quando em contexto CRUD) -->\n @case ('crud') {\n <crud-integration-editor\n #crudEditorRef\n [tableId]=\"crudContext?.tableId || 'default'\"\n [crudContext]=\"crudContext\"\n [componentKeyId]=\"crudContext?.componentKeyId\"\n ></crud-integration-editor>\n }\n @case ('filters') {\n <div class=\"filters-authoring-stack\">\n <section\n class=\"responsive-settings toolbar-filters-region\"\n data-testid=\"table-unified-filter-region-section\"\n aria-labelledby=\"table-unified-filter-region-title\"\n >\n <div class=\"responsive-settings__heading\">\n <mat-icon aria-hidden=\"true\">filter_list</mat-icon>\n <h3 id=\"table-unified-filter-region-title\">\n {{ tx('filters.region.title', 'Regi\u00E3o \u00FAnica de filtros e escopos da toolbar') }}\n </h3>\n </div>\n <p class=\"responsive-settings__hint\">\n {{\n tx(\n 'filters.region.description',\n 'Centralize escopos r\u00E1pidos (Todos / Ativos / Inativos), busca e gaveta avan\u00E7ada na toolbar da tabela sem controles duplicados.'\n )\n }}\n </p>\n\n <div class=\"toolbar-filters-toggles\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.region.enabled', 'Regi\u00E3o de filtros na toolbar') }}</mat-label>\n <mat-select\n [ngModel]=\"toolbarFiltersEnabled\"\n (ngModelChange)=\"onToolbarFiltersEnabledChange($event)\"\n data-testid=\"table-toolbar-filters-enabled-select\"\n >\n <mat-option [value]=\"true\">{{ tx('common.enabled', 'Ativada') }}</mat-option>\n <mat-option [value]=\"false\">{{ tx('common.disabled', 'Desativada') }}</mat-option>\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.region.showQuickFilters', 'Exibir escopos r\u00E1pidos') }}</mat-label>\n <mat-select\n [ngModel]=\"toolbarShowQuickFilters\"\n (ngModelChange)=\"onToolbarShowQuickFiltersChange($event)\"\n data-testid=\"table-toolbar-show-quick-filters-select\"\n >\n <mat-option [value]=\"true\">{{ tx('common.yes', 'Sim') }}</mat-option>\n <mat-option [value]=\"false\">{{ tx('common.no', 'N\u00E3o') }}</mat-option>\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.region.showAdvancedButton', 'Bot\u00E3o de gaveta avan\u00E7ada') }}</mat-label>\n <mat-select\n [ngModel]=\"toolbarShowAdvancedButton\"\n (ngModelChange)=\"onToolbarShowAdvancedButtonChange($event)\"\n data-testid=\"table-toolbar-show-advanced-button-select\"\n >\n <mat-option [value]=\"true\">{{ tx('common.yes', 'Sim') }}</mat-option>\n <mat-option [value]=\"false\">{{ tx('common.no', 'N\u00E3o') }}</mat-option>\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.region.showAddButton', 'Bot\u00E3o \"Gerenciar campos\"') }}</mat-label>\n <mat-select\n [ngModel]=\"advancedFilterShowAddButton\"\n (ngModelChange)=\"onAdvancedFilterShowAddButtonChange($event)\"\n data-testid=\"table-toolbar-show-add-button-select\"\n >\n <mat-option [value]=\"true\">{{ tx('common.yes', 'Sim') }}</mat-option>\n <mat-option [value]=\"false\">{{ tx('common.no', 'N\u00E3o') }}</mat-option>\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.region.showFilterSettings', 'Bot\u00E3o \"Configura\u00E7\u00F5es do filtro\"') }}</mat-label>\n <mat-select\n [ngModel]=\"advancedFilterShowFilterSettings\"\n (ngModelChange)=\"onAdvancedFilterShowFilterSettingsChange($event)\"\n data-testid=\"table-toolbar-show-filter-settings-select\"\n >\n <mat-option [value]=\"true\">{{ tx('common.yes', 'Sim') }}</mat-option>\n <mat-option [value]=\"false\">{{ tx('common.no', 'N\u00E3o') }}</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n @if (duplicatedQuickFilterInlineFields.length > 0) {\n <div\n class=\"alert alert--warning connect-alert\"\n data-testid=\"table-filter-duplicate-scope-warning\"\n >\n <mat-icon class=\"alert__icon\">warning</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n {{\n tx(\n 'filters.region.duplicateWarning',\n 'Os campos abaixo j\u00E1 possuem escopos r\u00E1pidos na toolbar e tamb\u00E9m est\u00E3o nos filtros inline:'\n )\n }}\n <strong>{{ duplicatedQuickFilterInlineFields.join(', ') }}</strong>\n </div>\n <button\n mat-stroked-button\n color=\"primary\"\n type=\"button\"\n data-testid=\"table-filter-consolidate-duplicates-btn\"\n (click)=\"consolidateDuplicateInlineFilterFields()\"\n >\n {{ tx('filters.region.consolidateBtn', 'Consolidar nos escopos r\u00E1pidos') }}\n </button>\n </div>\n }\n\n <div class=\"quick-filters-header\">\n <strong>{{ tx('filters.quickFilters.heading', 'Escopos r\u00E1pidos (quickFilters)') }}</strong>\n <div class=\"quick-filters-actions\">\n <button\n mat-stroked-button\n type=\"button\"\n data-testid=\"table-quick-filters-preset-boolean-btn\"\n (click)=\"applyBooleanQuickFilterPreset()\"\n >\n {{ tx('filters.quickFilters.presetBoolean', 'Preset Todos / Ativos / Inativos') }}\n </button>\n <button\n mat-stroked-button\n color=\"primary\"\n type=\"button\"\n data-testid=\"table-quick-filters-add-btn\"\n (click)=\"addQuickFilterScope()\"\n >\n {{ tx('filters.quickFilters.add', 'Adicionar escopo') }}\n </button>\n </div>\n </div>\n\n @if (toolbarQuickFilters.length > 0) {\n <div class=\"quick-filters-list\" data-testid=\"table-quick-filters-list\">\n @for (qf of toolbarQuickFilters; track $index; let idx = $index) {\n <div class=\"quick-filter-row\" [attr.data-testid]=\"'table-quick-filter-row-' + idx\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.quickFilters.id', 'ID') }}</mat-label>\n <input\n matInput\n [ngModel]=\"qf.id\"\n (ngModelChange)=\"updateQuickFilterMeta(idx, { id: $event })\"\n [attr.data-testid]=\"'table-quick-filter-id-' + idx\"\n />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.quickFilters.label', 'R\u00F3tulo') }}</mat-label>\n <input\n matInput\n [ngModel]=\"qf.label\"\n (ngModelChange)=\"updateQuickFilterMeta(idx, { label: $event })\"\n [attr.data-testid]=\"'table-quick-filter-label-' + idx\"\n />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.quickFilters.mode', 'Tipo de escopo') }}</mat-label>\n <mat-select\n [ngModel]=\"getQuickFilterMode(qf)\"\n (ngModelChange)=\"onQuickFilterModeChange(idx, $event)\"\n [attr.data-testid]=\"'table-quick-filter-mode-' + idx\"\n >\n <mat-option value=\"all\">{{ tx('filters.quickFilters.mode.all', 'Todos (limpar filtro)') }}</mat-option>\n <mat-option value=\"boolean\">{{ tx('filters.quickFilters.mode.boolean', 'Status booleano (Sim/N\u00E3o)') }}</mat-option>\n <mat-option value=\"equals\">{{ tx('filters.quickFilters.mode.equals', 'Campo igual a valor') }}</mat-option>\n </mat-select>\n </mat-form-field>\n\n @if (getQuickFilterMode(qf) !== 'all') {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.quickFilters.field', 'Campo') }}</mat-label>\n <input\n matInput\n [ngModel]=\"getQuickFilterField(qf)\"\n (ngModelChange)=\"onQuickFilterFieldChange(idx, $event)\"\n [attr.data-testid]=\"'table-quick-filter-field-' + idx\"\n />\n </mat-form-field>\n\n @if (getQuickFilterMode(qf) === 'boolean') {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.quickFilters.value', 'Valor') }}</mat-label>\n <mat-select\n [ngModel]=\"getQuickFilterValue(qf)\"\n (ngModelChange)=\"onQuickFilterValueChange(idx, $event)\"\n [attr.data-testid]=\"'table-quick-filter-bool-value-' + idx\"\n >\n <mat-option [value]=\"true\">{{ tx('common.boolean.true', 'Verdadeiro (true)') }}</mat-option>\n <mat-option [value]=\"false\">{{ tx('common.boolean.false', 'Falso (false)') }}</mat-option>\n </mat-select>\n </mat-form-field>\n } @else {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.quickFilters.value', 'Valor') }}</mat-label>\n <input\n matInput\n [ngModel]=\"getQuickFilterValue(qf)\"\n (ngModelChange)=\"onQuickFilterValueChange(idx, $event)\"\n [attr.data-testid]=\"'table-quick-filter-value-' + idx\"\n />\n </mat-form-field>\n }\n }\n\n <button\n mat-stroked-button\n color=\"warn\"\n type=\"button\"\n [attr.data-testid]=\"'table-quick-filter-remove-' + idx\"\n (click)=\"removeQuickFilterScope(idx)\"\n >\n {{ tx('common.remove', 'Remover') }}\n </button>\n </div>\n }\n </div>\n }\n </section>\n\n <filter-settings\n #tableFilterSettingsRef\n [metadata]=\"getFilterSettingsMetas()\"\n [metadataSource]=\"isUsingFilterDtoMetas() ? 'filter-dto' : 'columns'\"\n [metadataLoading]=\"filterDtoMetasLoading\"\n [metadataErrorMsg]=\"filterDtoMetasErrorMsg\"\n [settings]=\"\n editedConfig.behavior?.filtering?.advancedFilters?.settings\n \"\n (settingsChange)=\"onFilterSettingsChange($event)\"\n ></filter-settings>\n </div>\n }\n @case ('messages') {\n <messages-localization-editor\n [config]=\"editedConfig\"\n (configChange)=\"onMessagesLocalizationConfigChange($event)\"\n (messagesLocalizationChange)=\"\n onMessagesLocalizationChange($event)\n \"\n ></messages-localization-editor>\n }\n @case ('dialogs') {\n <confirm-dialog-appearance-editor\n [config]=\"editedConfig\"\n (configChange)=\"onJsonConfigChange($event)\"\n ></confirm-dialog-appearance-editor>\n }\n @case ('json') {\n <json-config-editor\n [document]=\"buildJsonAuthoringDocument()\"\n (documentChange)=\"onJsonConfigChange($event)\"\n (validationChange)=\"onJsonValidationChange($event)\"\n (editorEvent)=\"onJsonEditorEvent($event)\"\n ></json-config-editor>\n }\n }\n </div>\n </mat-tab>\n }\n</mat-tab-group>\n@if (statusMessage) { <div class=\"config-editor-status\">\n <span\n class=\"status-text\"\n [class.error]=\"hasErrors\"\n [class.success]=\"hasSuccess\"\n >{{ statusMessage }}</span\n >\n</div> }\n", styles: ["@charset \"UTF-8\";:host{display:block;min-width:0;container:praxis-authoring-editor/inline-size}.editor-section-selector{display:none;padding:var(--pfx-spacing-2, 8px)}.editor-section-selector mat-form-field{width:100%}@container praxis-authoring-editor (max-width: 768px){.editor-section-selector{display:block}::ng-deep .config-tabs>.mat-mdc-tab-header{display:none}}.config-tabs{flex:1 1 auto;display:flex;flex-direction:column}.config-tabs .mat-mdc-tab{min-width:120px}.config-tabs .mat-mdc-tab-body-wrapper,.config-tabs .mat-mdc-tab-group-container{flex:1 1 auto;min-height:0}.config-tabs .mat-mdc-tab-body-content{height:100%;min-height:0;overflow:visible}.tab-content{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;padding:8px 8px 56px;box-sizing:border-box;overflow:visible}.overview-grid{display:grid;min-width:0;grid-template-columns:minmax(0,1fr);gap:12px}.overview-grid .overview-row{display:grid;grid-template-columns:280px 1fr;align-items:end;gap:12px}.overview-grid .overview-row .help.small{opacity:.75;font-size:12px}.overview-mode-alert{align-items:center}.connect-grid{display:grid;gap:12px;padding:8px;grid-template-columns:minmax(0,1fr);align-items:start}.connect-note{grid-column:1/-1;margin:0;font-size:12px;color:var(--md-sys-color-on-surface-variant)}.connect-alert{grid-column:1/-1;align-items:center}.connect-alert__text{flex:1}.connect-alert__meta{opacity:.85}.educational-card{margin-bottom:24px;background:var(--md-sys-color-surface-container-low);border-left:4px solid var(--md-sys-color-primary);flex-shrink:0}.config-editor-status{padding:12px 16px;margin-top:auto;border-top:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container);flex-shrink:0;position:relative}.status-text{font-size:.875rem;line-height:1.2}.status-text.error{color:var(--md-sys-color-error)}.status-text.success{color:var(--md-sys-color-primary)}@media(max-width:768px){.tab-content{padding:8px 8px 72px}.config-editor-status{padding:12px 16px}.connect-grid{grid-template-columns:1fr}.overview-grid .overview-row{grid-template-columns:1fr;align-items:start}:host{display:flex;flex-direction:column;flex:1 1 auto;min-height:0}}.responsive-settings{display:grid;grid-template-columns:minmax(0,1fr);gap:12px;min-width:0;padding:16px;border:1px solid var(--md-sys-color-outline-variant);border-radius:var(--md-sys-shape-corner-medium, 12px);background:var(--md-sys-color-surface-container-low)}.responsive-settings__heading{display:flex;align-items:center;gap:8px}.responsive-settings h3,.responsive-settings p{margin:0}.responsive-settings h3{font-size:1rem;font-weight:600}.responsive-settings p{font-size:.875rem;line-height:1.5}.responsive-settings__hint{color:var(--md-sys-color-on-surface-variant)}.responsive-settings__error{color:var(--md-sys-color-error)}.responsive-settings mat-form-field{width:100%;min-width:0}.filters-authoring-stack{display:grid;grid-template-columns:minmax(0,1fr);gap:16px}.toolbar-filters-toggles{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px}.quick-filters-header{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:8px}.quick-filters-actions{display:flex;flex-wrap:wrap;gap:8px}.quick-filters-list{display:grid;gap:10px}.quick-filter-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));align-items:center;gap:8px;padding:10px;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;background:var(--md-sys-color-surface)}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i7.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { 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: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i4$1.MatTabLabel, selector: "[mat-tab-label], [matTabLabel]" }, { kind: "component", type: i4$1.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i4$1.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.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.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { 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.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i17$1.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i17$1.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4.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: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: JsonConfigEditorComponent, selector: "json-config-editor", inputs: ["document"], outputs: ["documentChange", "validationChange", "editorEvent"] }, { kind: "component", type: ColumnsConfigEditorComponent, selector: "columns-config-editor", inputs: ["config", "readonly", "fieldMetadata"], outputs: ["configChange", "columnChange"] }, { kind: "component", type: BehaviorConfigEditorComponent, selector: "behavior-config-editor", inputs: ["config", "resourcePath", "forceClientStrategies"], outputs: ["configChange", "behaviorChange"] }, { kind: "component", type: HeaderAppearanceEditorComponent, selector: "header-appearance-editor", inputs: ["config"], outputs: ["configChange"] }, { kind: "component", type: ToolbarActionsEditorComponent, selector: "toolbar-actions-editor", inputs: ["config"], outputs: ["configChange", "validationChange", "toolbarActionsChange"] }, { kind: "component", type: MessagesLocalizationEditorComponent, selector: "messages-localization-editor", inputs: ["config"], outputs: ["configChange", "messagesLocalizationChange"] }, { kind: "component", type: FilterSettingsComponent, selector: "filter-settings", inputs: ["metadata", "metadataSource", "metadataLoading", "metadataErrorMsg", "settings", "configKey"], outputs: ["settingsChange"] }, { kind: "component", type: CrudIntegrationEditorComponent, selector: "crud-integration-editor", inputs: ["tableId", "crudContext", "componentKeyId"] }, { kind: "component", type: ConfirmDialogAppearanceEditorComponent, selector: "confirm-dialog-appearance-editor", inputs: ["config"], outputs: ["configChange"] }, { kind: "component", type: TableRulesEditorComponent, selector: "table-rules-editor", inputs: ["config", "resourcePath", "fields", "i18nRules", "debugLogs", "debugLevel"], outputs: ["configChange"] }, { kind: "component", type: TableDetailEditorComponent, selector: "praxis-table-detail-editor", inputs: ["config"], outputs: ["configChange", "draftStateChange"] }] });
73894
+ ], viewQueries: [{ propertyName: "filterSettingsEditor", first: true, predicate: ["tableFilterSettingsRef"], descendants: true }, { propertyName: "behaviorEditor", first: true, predicate: BehaviorConfigEditorComponent, descendants: true }, { propertyName: "detailEditor", first: true, predicate: TableDetailEditorComponent, descendants: true }, { propertyName: "rulesEditor", first: true, predicate: TableRulesEditorComponent, descendants: true }, { propertyName: "crudEditorSetter", first: true, predicate: ["crudEditorRef"], descendants: true }], ngImport: i0, template: "<div class=\"editor-section-selector\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('sections.selector', 'Editor section') }}</mat-label>\n <mat-select [(ngModel)]=\"activeSectionIndex\" data-testid=\"table-config-editor-sections\">\n @for (section of sections; track section.id; let index = $index) {\n <mat-option [value]=\"index\">{{ section.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n</div>\n<mat-tab-group [preserveContent]=\"true\" class=\"config-tabs\" [(selectedIndex)]=\"activeSectionIndex\">\n @for (section of sections; track section.id) {\n <mat-tab>\n <ng-template mat-tab-label>\n @if (section.icon) { <mat-icon [praxisIcon]=\"section.icon\"></mat-icon> }\n <span [attr.data-testid]=\"'config-tab-' + section.id\">{{ section.label }}</span>\n </ng-template>\n <div class=\"tab-content\">\n @switch (section.id) {\n @case ('connect') {\n <div class=\"connect-grid\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('connect.resource', 'Resource') }}</mat-label>\n <input matInput [(ngModel)]=\"resourcePath\" (ngModelChange)=\"onResourcePathChange($event)\" [placeholder]=\"tx('connect.resource.placeholder', 'e.g. employees')\" />\n <mat-icon matSuffix>link</mat-icon>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('connect.primaryKey', 'Primary key') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"idField\"\n (ngModelChange)=\"onIdFieldChange($event)\"\n [matAutocomplete]=\"idFieldPicker\"\n [placeholder]=\"tx('connect.primaryKey.placeholder', 'e.g. id, uuid, code')\"\n />\n <mat-icon matSuffix>fingerprint</mat-icon>\n <mat-autocomplete\n #idFieldPicker=\"matAutocomplete\"\n (optionSelected)=\"onIdFieldOptionSelected($event.option.value)\"\n >\n @for (option of filteredIdFieldOptions; track option.name) {\n <mat-option [value]=\"option.name\">\n <span>{{ option.name }}</span>\n @if (option.label && option.label !== option.name) {\n <small> - {{ option.label }}</small>\n }\n </mat-option>\n }\n @if (filteredIdFieldOptions.length === 0) {\n <mat-option disabled>\n {{ tx('connect.primaryKey.empty', 'No matching field found.') }}\n </mat-option>\n }\n </mat-autocomplete>\n </mat-form-field>\n <p class=\"connect-note\">{{ tx('connect.note', 'Define the API resource and, when needed, the primary key.') }}</p>\n <!-- Diverg\u00EAncia: idField -->\n @if (idFieldDiverges) { <div class=\"alert alert--warning connect-alert\">\n <mat-icon class=\"alert__icon\">warning</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n {{ tx('connect.serverPrimaryKeyDiverges', 'The primary key on the server differs from the current configuration.') }}\n <span class=\"connect-alert__meta\">(config: {{ idField || 'id' }} | servidor: {{ serverIdField || 'id' }})</span>\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onReconcileIdField()\">{{ tx('connect.reconcile', 'Reconcile') }}</button>\n </div> }\n <!-- Diverg\u00EAncia: serverHash -->\n @if (schemaHashDiverges) { <div class=\"alert alert--info connect-alert\">\n <mat-icon class=\"alert__icon\">info</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n {{ tx('connect.schemaSnapshotDiverges', 'The saved schema snapshot differs from the current server schema.') }}\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onAcceptServerHash()\">{{ tx('connect.updateMetadata', 'Update metadata') }}</button>\n </div> }\n @if (!schemaHashDiverges && schemaSnapshotMissing) { <div class=\"alert alert--info connect-alert\">\n <mat-icon class=\"alert__icon\">info</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n {{ tx('connect.schemaSnapshotMissing', 'A current schema exists on the server, but there is still no persisted snapshot for reconciliation.') }}\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onAcceptServerHash()\">{{ tx('connect.saveSnapshot', 'Save snapshot') }}</button>\n </div> }\n </div>\n }\n @case ('overview') {\n <div class=\"overview-grid\">\n <section class=\"responsive-settings\" aria-labelledby=\"table-responsive-title\">\n <div class=\"responsive-settings__heading\">\n <mat-icon aria-hidden=\"true\">devices</mat-icon>\n <h3 id=\"table-responsive-title\">{{ tx('overview.responsive.title', 'Narrow screens') }}</h3>\n </div>\n <p>{{ tx('overview.responsive.description', 'Choose how records appear when the table has less space.') }}</p>\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('overview.responsive.mode', 'Record layout') }}</mat-label>\n <mat-select [ngModel]=\"mobileCardMode\" (ngModelChange)=\"onMobileCardModeChange($event)\">\n <mat-option [value]=\"false\">{{ tx('overview.responsive.table', 'Table') }}</mat-option>\n <mat-option [value]=\"true\">{{ tx('overview.responsive.cards', 'Cards') }}</mat-option>\n </mat-select>\n </mat-form-field>\n @if (mobileCardMode) {\n <p class=\"responsive-settings__hint\">{{ tx('overview.responsive.cardsHint', 'Each record becomes a card with its visible columns. Selection and row actions remain available. Wider areas keep the table layout.') }}</p>\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('overview.responsive.breakpoint', 'Show cards at widths up to') }}</mat-label>\n <input matInput type=\"number\" min=\"1\" step=\"1\" required\n [ngModel]=\"mobileBreakpoint\" (ngModelChange)=\"onMobileBreakpointChange($event)\"\n [attr.aria-invalid]=\"mobileBreakpointInvalid\"\n aria-describedby=\"table-responsive-width-help\" />\n <span matTextSuffix>px</span>\n </mat-form-field>\n @if (mobileBreakpointInvalid) {\n <p class=\"responsive-settings__error\" role=\"alert\">{{ tx('overview.responsive.invalidWidth', 'Enter a positive whole number.') }}</p>\n }\n <p id=\"table-responsive-width-help\" class=\"responsive-settings__hint\">{{ tx('overview.responsive.widthHint', 'Uses the smaller width of the screen and the table area. Default: 768 px.') }}</p>\n <p class=\"responsive-settings__hint\">{{ tx('overview.responsive.virtualizationHint', 'Cards require standard rows. With virtual scrolling enabled, records remain in a table.') }}</p>\n }\n </section>\n <div class=\"overview-row\">\n <mat-form-field appearance=\"outline\" class=\"hs-field\">\n <mat-label>{{ tx('overview.horizontalScroll', 'Horizontal scroll') }}</mat-label>\n <mat-select [(ngModel)]=\"horizontalScroll\" (ngModelChange)=\"onHorizontalScrollChange($event)\">\n <mat-option value=\"auto\">{{ tx('overview.horizontalScroll.auto', 'Auto (default)') }}</mat-option>\n <mat-option value=\"wrap\">{{ tx('overview.horizontalScroll.wrap', 'Wrap') }}</mat-option>\n <mat-option value=\"none\">{{ tx('overview.horizontalScroll.none', 'Host controls') }}</mat-option>\n </mat-select>\n </mat-form-field>\n <div class=\"help small\">{{ tx('overview.horizontalScroll.help', 'Controls how the table handles horizontal width and scrollbar behavior.') }}</div>\n </div>\n @if (isEffectiveLocalMode()) { <div class=\"alert alert--info overview-mode-alert\">\n <mat-icon class=\"alert__icon\">info</mat-icon>\n <div class=\"alert__text\">\n {{ tx('overview.localModeActive', 'Effective local mode is active: server pagination and sorting strategies are blocked and persisted as client mode.') }}\n </div>\n </div> }\n <behavior-config-editor\n [config]=\"editedConfig\"\n [resourcePath]=\"resourcePath\"\n [forceClientStrategies]=\"isEffectiveLocalMode()\"\n (configChange)=\"onBehaviorConfigChange($event)\"\n (behaviorChange)=\"onBehaviorChange($event)\"\n ></behavior-config-editor>\n </div>\n }\n @case ('columns') {\n <columns-config-editor\n [config]=\"editedConfig\"\n [fieldMetadata]=\"getColumnAuthoringMetas()\"\n (configChange)=\"onColumnsConfigChange($event)\"\n (columnChange)=\"onColumnChange($event)\"\n ></columns-config-editor>\n }\n @case ('detail') {\n <praxis-table-detail-editor [config]=\"editedConfig\"\n (configChange)=\"onDetailConfigChange($event)\"\n (draftStateChange)=\"onDetailDraftStateChange($event)\"\n ></praxis-table-detail-editor>\n }\n @case ('rules') {\n <table-rules-editor\n [config]=\"editedConfig\"\n [resourcePath]=\"resourcePath\"\n (configChange)=\"onRulesConfigChange($event)\"\n ></table-rules-editor>\n }\n @case ('header') {\n <header-appearance-editor\n [config]=\"editedConfig\"\n (configChange)=\"onColumnsConfigChange($event)\"\n ></header-appearance-editor>\n }\n @case ('toolbar') {\n <toolbar-actions-editor\n [config]=\"editedConfig\"\n (configChange)=\"onToolbarActionsConfigChange($event)\"\n (toolbarActionsChange)=\"onToolbarActionsChange($event)\"\n (validationChange)=\"onSurfaceDraftValidationChange($event)\"\n ></toolbar-actions-editor>\n }\n <!-- Aba extra para integra\u00E7\u00F5es CRUD (vis\u00EDvel quando em contexto CRUD) -->\n @case ('crud') {\n <crud-integration-editor\n #crudEditorRef\n [tableId]=\"crudContext?.tableId || 'default'\"\n [crudContext]=\"crudContext\"\n [componentKeyId]=\"crudContext?.componentKeyId\"\n ></crud-integration-editor>\n }\n @case ('filters') {\n <div class=\"filters-authoring-stack\">\n <section\n class=\"responsive-settings toolbar-filters-region\"\n data-testid=\"table-unified-filter-region-section\"\n aria-labelledby=\"table-unified-filter-region-title\"\n >\n <div class=\"responsive-settings__heading\">\n <mat-icon aria-hidden=\"true\">filter_list</mat-icon>\n <h3 id=\"table-unified-filter-region-title\">\n {{ tx('filters.region.title', 'Regi\u00E3o \u00FAnica de filtros e escopos da toolbar') }}\n </h3>\n </div>\n <p class=\"responsive-settings__hint\">\n {{\n tx(\n 'filters.region.description',\n 'Centralize escopos r\u00E1pidos (Todos / Ativos / Inativos), busca e gaveta avan\u00E7ada na toolbar da tabela sem controles duplicados.'\n )\n }}\n </p>\n\n <div class=\"toolbar-filters-toggles\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.region.enabled', 'Regi\u00E3o de filtros na toolbar') }}</mat-label>\n <mat-select\n [ngModel]=\"toolbarFiltersEnabled\"\n (ngModelChange)=\"onToolbarFiltersEnabledChange($event)\"\n data-testid=\"table-toolbar-filters-enabled-select\"\n >\n <mat-option [value]=\"true\">{{ tx('common.enabled', 'Ativada') }}</mat-option>\n <mat-option [value]=\"false\">{{ tx('common.disabled', 'Desativada') }}</mat-option>\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.region.showQuickFilters', 'Exibir escopos r\u00E1pidos') }}</mat-label>\n <mat-select\n [ngModel]=\"toolbarShowQuickFilters\"\n (ngModelChange)=\"onToolbarShowQuickFiltersChange($event)\"\n data-testid=\"table-toolbar-show-quick-filters-select\"\n >\n <mat-option [value]=\"true\">{{ tx('common.yes', 'Sim') }}</mat-option>\n <mat-option [value]=\"false\">{{ tx('common.no', 'N\u00E3o') }}</mat-option>\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.region.showAdvancedButton', 'Bot\u00E3o de gaveta avan\u00E7ada') }}</mat-label>\n <mat-select\n [ngModel]=\"toolbarShowAdvancedButton\"\n (ngModelChange)=\"onToolbarShowAdvancedButtonChange($event)\"\n data-testid=\"table-toolbar-show-advanced-button-select\"\n >\n <mat-option [value]=\"true\">{{ tx('common.yes', 'Sim') }}</mat-option>\n <mat-option [value]=\"false\">{{ tx('common.no', 'N\u00E3o') }}</mat-option>\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.region.showAddButton', 'Bot\u00E3o \"Gerenciar campos\"') }}</mat-label>\n <mat-select\n [ngModel]=\"advancedFilterShowAddButton\"\n (ngModelChange)=\"onAdvancedFilterShowAddButtonChange($event)\"\n data-testid=\"table-toolbar-show-add-button-select\"\n >\n <mat-option [value]=\"true\">{{ tx('common.yes', 'Sim') }}</mat-option>\n <mat-option [value]=\"false\">{{ tx('common.no', 'N\u00E3o') }}</mat-option>\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.region.showFilterSettings', 'Bot\u00E3o \"Configura\u00E7\u00F5es do filtro\"') }}</mat-label>\n <mat-select\n [ngModel]=\"advancedFilterShowFilterSettings\"\n (ngModelChange)=\"onAdvancedFilterShowFilterSettingsChange($event)\"\n data-testid=\"table-toolbar-show-filter-settings-select\"\n >\n <mat-option [value]=\"true\">{{ tx('common.yes', 'Sim') }}</mat-option>\n <mat-option [value]=\"false\">{{ tx('common.no', 'N\u00E3o') }}</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n @if (duplicatedQuickFilterInlineFields.length > 0) {\n <div\n class=\"alert alert--warning connect-alert\"\n data-testid=\"table-filter-duplicate-scope-warning\"\n >\n <mat-icon class=\"alert__icon\">warning</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n {{\n tx(\n 'filters.region.duplicateWarning',\n 'Os campos abaixo j\u00E1 possuem escopos r\u00E1pidos na toolbar e tamb\u00E9m est\u00E3o nos filtros inline:'\n )\n }}\n <strong>{{ duplicatedQuickFilterInlineFields.join(', ') }}</strong>\n </div>\n <button\n mat-stroked-button\n color=\"primary\"\n type=\"button\"\n data-testid=\"table-filter-consolidate-duplicates-btn\"\n (click)=\"consolidateDuplicateInlineFilterFields()\"\n >\n {{ tx('filters.region.consolidateBtn', 'Consolidar nos escopos r\u00E1pidos') }}\n </button>\n </div>\n }\n\n <div class=\"quick-filters-header\">\n <strong>{{ tx('filters.quickFilters.heading', 'Escopos r\u00E1pidos (quickFilters)') }}</strong>\n <div class=\"quick-filters-actions\">\n <button\n mat-stroked-button\n type=\"button\"\n data-testid=\"table-quick-filters-preset-boolean-btn\"\n (click)=\"applyBooleanQuickFilterPreset()\"\n >\n {{ tx('filters.quickFilters.presetBoolean', 'Preset Todos / Ativos / Inativos') }}\n </button>\n <button\n mat-stroked-button\n color=\"primary\"\n type=\"button\"\n data-testid=\"table-quick-filters-add-btn\"\n (click)=\"addQuickFilterScope()\"\n >\n {{ tx('filters.quickFilters.add', 'Adicionar escopo') }}\n </button>\n </div>\n </div>\n\n @if (toolbarQuickFilters.length > 0) {\n <div class=\"quick-filters-list\" data-testid=\"table-quick-filters-list\">\n @for (qf of toolbarQuickFilters; track $index; let idx = $index) {\n <div class=\"quick-filter-row\" [attr.data-testid]=\"'table-quick-filter-row-' + idx\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.quickFilters.id', 'ID') }}</mat-label>\n <input\n matInput\n [ngModel]=\"qf.id\"\n (ngModelChange)=\"updateQuickFilterMeta(idx, { id: $event })\"\n [attr.data-testid]=\"'table-quick-filter-id-' + idx\"\n />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.quickFilters.label', 'R\u00F3tulo') }}</mat-label>\n <input\n matInput\n [ngModel]=\"qf.label\"\n (ngModelChange)=\"updateQuickFilterMeta(idx, { label: $event })\"\n [attr.data-testid]=\"'table-quick-filter-label-' + idx\"\n />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.quickFilters.mode', 'Tipo de escopo') }}</mat-label>\n <mat-select\n [ngModel]=\"getQuickFilterMode(qf)\"\n (ngModelChange)=\"onQuickFilterModeChange(idx, $event)\"\n [attr.data-testid]=\"'table-quick-filter-mode-' + idx\"\n >\n <mat-option value=\"all\">{{ tx('filters.quickFilters.mode.all', 'Todos (limpar filtro)') }}</mat-option>\n <mat-option value=\"boolean\">{{ tx('filters.quickFilters.mode.boolean', 'Status booleano (Sim/N\u00E3o)') }}</mat-option>\n <mat-option value=\"equals\">{{ tx('filters.quickFilters.mode.equals', 'Campo igual a valor') }}</mat-option>\n </mat-select>\n </mat-form-field>\n\n @if (getQuickFilterMode(qf) !== 'all') {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.quickFilters.field', 'Campo') }}</mat-label>\n <input\n matInput\n [ngModel]=\"getQuickFilterField(qf)\"\n (ngModelChange)=\"onQuickFilterFieldChange(idx, $event)\"\n [attr.data-testid]=\"'table-quick-filter-field-' + idx\"\n />\n </mat-form-field>\n\n @if (getQuickFilterMode(qf) === 'boolean') {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.quickFilters.value', 'Valor') }}</mat-label>\n <mat-select\n [ngModel]=\"getQuickFilterValue(qf)\"\n (ngModelChange)=\"onQuickFilterValueChange(idx, $event)\"\n [attr.data-testid]=\"'table-quick-filter-bool-value-' + idx\"\n >\n <mat-option [value]=\"true\">{{ tx('common.boolean.true', 'Verdadeiro (true)') }}</mat-option>\n <mat-option [value]=\"false\">{{ tx('common.boolean.false', 'Falso (false)') }}</mat-option>\n </mat-select>\n </mat-form-field>\n } @else {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.quickFilters.value', 'Valor') }}</mat-label>\n <input\n matInput\n [ngModel]=\"getQuickFilterValue(qf)\"\n (ngModelChange)=\"onQuickFilterValueChange(idx, $event)\"\n [attr.data-testid]=\"'table-quick-filter-value-' + idx\"\n />\n </mat-form-field>\n }\n }\n\n <button\n mat-stroked-button\n color=\"warn\"\n type=\"button\"\n [attr.data-testid]=\"'table-quick-filter-remove-' + idx\"\n (click)=\"removeQuickFilterScope(idx)\"\n >\n {{ tx('common.remove', 'Remover') }}\n </button>\n </div>\n }\n </div>\n }\n </section>\n\n <filter-settings\n #tableFilterSettingsRef\n [metadata]=\"getFilterSettingsMetas()\"\n [metadataSource]=\"isUsingFilterDtoMetas() ? 'filter-dto' : 'columns'\"\n [metadataLoading]=\"filterDtoMetasLoading\"\n [metadataErrorMsg]=\"filterDtoMetasErrorMsg\"\n [settings]=\"\n editedConfig.behavior?.filtering?.advancedFilters?.settings\n \"\n (settingsChange)=\"onFilterSettingsChange($event)\"\n ></filter-settings>\n </div>\n }\n @case ('messages') {\n <messages-localization-editor\n [config]=\"editedConfig\"\n (configChange)=\"onMessagesLocalizationConfigChange($event)\"\n (messagesLocalizationChange)=\"\n onMessagesLocalizationChange($event)\n \"\n ></messages-localization-editor>\n }\n @case ('dialogs') {\n <confirm-dialog-appearance-editor\n [config]=\"editedConfig\"\n (configChange)=\"onJsonConfigChange($event)\"\n ></confirm-dialog-appearance-editor>\n }\n @case ('json') {\n <json-config-editor\n [document]=\"buildJsonAuthoringDocument()\"\n (documentChange)=\"onJsonConfigChange($event)\"\n (validationChange)=\"onJsonValidationChange($event)\"\n (editorEvent)=\"onJsonEditorEvent($event)\"\n ></json-config-editor>\n }\n }\n </div>\n </mat-tab>\n }\n</mat-tab-group>\n@if (statusMessage) { <div class=\"config-editor-status\">\n <span\n class=\"status-text\"\n [class.error]=\"hasErrors\"\n [class.success]=\"hasSuccess\"\n >{{ statusMessage }}</span\n >\n</div> }\n", styles: ["@charset \"UTF-8\";:host{display:block;min-width:0;container:praxis-authoring-editor/inline-size}.editor-section-selector{display:block;padding:var(--pfx-spacing-2, 8px)}.editor-section-selector mat-form-field{width:100%}@container praxis-authoring-editor (max-width: 768px){.editor-section-selector{display:block}::ng-deep .config-tabs>.mat-mdc-tab-header{display:none}}.config-tabs{flex:1 1 auto;display:flex;flex-direction:column}.config-tabs .mat-mdc-tab{min-width:120px}.config-tabs .mat-mdc-tab-body-wrapper,.config-tabs .mat-mdc-tab-group-container{flex:1 1 auto;min-height:0}.config-tabs .mat-mdc-tab-body-content{height:100%;min-height:0;overflow:visible}.tab-content{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;padding:8px 8px 56px;box-sizing:border-box;overflow:visible}.overview-grid{display:grid;min-width:0;grid-template-columns:minmax(0,1fr);gap:12px}.overview-grid .overview-row{display:grid;grid-template-columns:280px 1fr;align-items:end;gap:12px}.overview-grid .overview-row .help.small{opacity:.75;font-size:12px}.overview-mode-alert{align-items:center}.connect-grid{display:grid;gap:12px;padding:8px;grid-template-columns:minmax(0,1fr);align-items:start}.connect-note{grid-column:1/-1;margin:0;font-size:12px;color:var(--md-sys-color-on-surface-variant)}.connect-alert{grid-column:1/-1;align-items:center}.connect-alert__text{flex:1}.connect-alert__meta{opacity:.85}.educational-card{margin-bottom:24px;background:var(--md-sys-color-surface-container-low);border-left:4px solid var(--md-sys-color-primary);flex-shrink:0}.config-editor-status{padding:12px 16px;margin-top:auto;border-top:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container);flex-shrink:0;position:relative}.status-text{font-size:.875rem;line-height:1.2}.status-text.error{color:var(--md-sys-color-error)}.status-text.success{color:var(--md-sys-color-primary)}@media(max-width:768px){.tab-content{padding:8px 8px 72px}.config-editor-status{padding:12px 16px}.connect-grid{grid-template-columns:1fr}.overview-grid .overview-row{grid-template-columns:1fr;align-items:start}:host{display:flex;flex-direction:column;flex:1 1 auto;min-height:0}}.responsive-settings{display:grid;grid-template-columns:minmax(0,1fr);gap:12px;min-width:0;padding:16px;border:1px solid var(--md-sys-color-outline-variant);border-radius:var(--md-sys-shape-corner-medium, 12px);background:var(--md-sys-color-surface-container-low)}.responsive-settings__heading{display:flex;align-items:center;gap:8px}.responsive-settings h3,.responsive-settings p{margin:0}.responsive-settings h3{font-size:1rem;font-weight:600}.responsive-settings p{font-size:.875rem;line-height:1.5}.responsive-settings__hint{color:var(--md-sys-color-on-surface-variant)}.responsive-settings__error{color:var(--md-sys-color-error)}.responsive-settings mat-form-field{width:100%;min-width:0}.filters-authoring-stack{display:grid;grid-template-columns:minmax(0,1fr);gap:16px}.toolbar-filters-toggles{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px}.quick-filters-header{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:8px}.quick-filters-actions{display:flex;flex-wrap:wrap;gap:8px}.quick-filters-list{display:grid;gap:10px}.quick-filter-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));align-items:center;gap:8px;padding:10px;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;background:var(--md-sys-color-surface)}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i7.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { 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: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i4$1.MatTabLabel, selector: "[mat-tab-label], [matTabLabel]" }, { kind: "component", type: i4$1.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i4$1.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.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.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { 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.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i17$1.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i17$1.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4.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: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: JsonConfigEditorComponent, selector: "json-config-editor", inputs: ["document"], outputs: ["documentChange", "validationChange", "editorEvent"] }, { kind: "component", type: ColumnsConfigEditorComponent, selector: "columns-config-editor", inputs: ["config", "readonly", "fieldMetadata"], outputs: ["configChange", "columnChange"] }, { kind: "component", type: BehaviorConfigEditorComponent, selector: "behavior-config-editor", inputs: ["config", "resourcePath", "forceClientStrategies"], outputs: ["configChange", "behaviorChange"] }, { kind: "component", type: HeaderAppearanceEditorComponent, selector: "header-appearance-editor", inputs: ["config"], outputs: ["configChange"] }, { kind: "component", type: ToolbarActionsEditorComponent, selector: "toolbar-actions-editor", inputs: ["config"], outputs: ["configChange", "validationChange", "toolbarActionsChange"] }, { kind: "component", type: MessagesLocalizationEditorComponent, selector: "messages-localization-editor", inputs: ["config"], outputs: ["configChange", "messagesLocalizationChange"] }, { kind: "component", type: FilterSettingsComponent, selector: "filter-settings", inputs: ["metadata", "metadataSource", "metadataLoading", "metadataErrorMsg", "settings", "configKey"], outputs: ["settingsChange"] }, { kind: "component", type: CrudIntegrationEditorComponent, selector: "crud-integration-editor", inputs: ["tableId", "crudContext", "componentKeyId"] }, { kind: "component", type: ConfirmDialogAppearanceEditorComponent, selector: "confirm-dialog-appearance-editor", inputs: ["config"], outputs: ["configChange"] }, { kind: "component", type: TableRulesEditorComponent, selector: "table-rules-editor", inputs: ["config", "resourcePath", "fields", "i18nRules", "debugLogs", "debugLevel"], outputs: ["configChange"] }, { kind: "component", type: TableDetailEditorComponent, selector: "praxis-table-detail-editor", inputs: ["config"], outputs: ["configChange", "draftStateChange"] }] });
73683
73895
  }
73684
73896
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisTableConfigEditor, decorators: [{
73685
73897
  type: Component,
@@ -73707,7 +73919,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
73707
73919
  ], providers: [
73708
73920
  TableConfigService,
73709
73921
  providePraxisI18nConfig(PRAXIS_TABLE_EDITOR_I18N_CONFIG),
73710
- ], template: "<div class=\"editor-section-selector\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('sections.selector', 'Editor section') }}</mat-label>\n <mat-select [(ngModel)]=\"activeSectionIndex\" data-testid=\"table-config-editor-sections\">\n @for (section of sections; track section.id; let index = $index) {\n <mat-option [value]=\"index\">{{ section.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n</div>\n<mat-tab-group [preserveContent]=\"true\" class=\"config-tabs\" [(selectedIndex)]=\"activeSectionIndex\">\n @for (section of sections; track section.id) {\n <mat-tab>\n <ng-template mat-tab-label>\n @if (section.icon) { <mat-icon [praxisIcon]=\"section.icon\"></mat-icon> }\n <span [attr.data-testid]=\"'config-tab-' + section.id\">{{ section.label }}</span>\n </ng-template>\n <div class=\"tab-content\">\n @switch (section.id) {\n @case ('connect') {\n <div class=\"connect-grid\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('connect.resource', 'Resource') }}</mat-label>\n <input matInput [(ngModel)]=\"resourcePath\" (ngModelChange)=\"onResourcePathChange($event)\" [placeholder]=\"tx('connect.resource.placeholder', 'e.g. employees')\" />\n <mat-icon matSuffix>link</mat-icon>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('connect.primaryKey', 'Primary key') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"idField\"\n (ngModelChange)=\"onIdFieldChange($event)\"\n [matAutocomplete]=\"idFieldPicker\"\n [placeholder]=\"tx('connect.primaryKey.placeholder', 'e.g. id, uuid, code')\"\n />\n <mat-icon matSuffix>fingerprint</mat-icon>\n <mat-autocomplete\n #idFieldPicker=\"matAutocomplete\"\n (optionSelected)=\"onIdFieldOptionSelected($event.option.value)\"\n >\n @for (option of filteredIdFieldOptions; track option.name) {\n <mat-option [value]=\"option.name\">\n <span>{{ option.name }}</span>\n @if (option.label && option.label !== option.name) {\n <small> - {{ option.label }}</small>\n }\n </mat-option>\n }\n @if (filteredIdFieldOptions.length === 0) {\n <mat-option disabled>\n {{ tx('connect.primaryKey.empty', 'No matching field found.') }}\n </mat-option>\n }\n </mat-autocomplete>\n </mat-form-field>\n <p class=\"connect-note\">{{ tx('connect.note', 'Define the API resource and, when needed, the primary key.') }}</p>\n <!-- Diverg\u00EAncia: idField -->\n @if (idFieldDiverges) { <div class=\"alert alert--warning connect-alert\">\n <mat-icon class=\"alert__icon\">warning</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n {{ tx('connect.serverPrimaryKeyDiverges', 'The primary key on the server differs from the current configuration.') }}\n <span class=\"connect-alert__meta\">(config: {{ idField || 'id' }} | servidor: {{ serverIdField || 'id' }})</span>\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onReconcileIdField()\">{{ tx('connect.reconcile', 'Reconcile') }}</button>\n </div> }\n <!-- Diverg\u00EAncia: serverHash -->\n @if (schemaHashDiverges) { <div class=\"alert alert--info connect-alert\">\n <mat-icon class=\"alert__icon\">info</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n {{ tx('connect.schemaSnapshotDiverges', 'The saved schema snapshot differs from the current server schema.') }}\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onAcceptServerHash()\">{{ tx('connect.updateMetadata', 'Update metadata') }}</button>\n </div> }\n @if (!schemaHashDiverges && schemaSnapshotMissing) { <div class=\"alert alert--info connect-alert\">\n <mat-icon class=\"alert__icon\">info</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n {{ tx('connect.schemaSnapshotMissing', 'A current schema exists on the server, but there is still no persisted snapshot for reconciliation.') }}\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onAcceptServerHash()\">{{ tx('connect.saveSnapshot', 'Save snapshot') }}</button>\n </div> }\n </div>\n }\n @case ('overview') {\n <div class=\"overview-grid\">\n <section class=\"responsive-settings\" aria-labelledby=\"table-responsive-title\">\n <div class=\"responsive-settings__heading\">\n <mat-icon aria-hidden=\"true\">devices</mat-icon>\n <h3 id=\"table-responsive-title\">{{ tx('overview.responsive.title', 'Narrow screens') }}</h3>\n </div>\n <p>{{ tx('overview.responsive.description', 'Choose how records appear when the table has less space.') }}</p>\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('overview.responsive.mode', 'Record layout') }}</mat-label>\n <mat-select [ngModel]=\"mobileCardMode\" (ngModelChange)=\"onMobileCardModeChange($event)\">\n <mat-option [value]=\"false\">{{ tx('overview.responsive.table', 'Table') }}</mat-option>\n <mat-option [value]=\"true\">{{ tx('overview.responsive.cards', 'Cards') }}</mat-option>\n </mat-select>\n </mat-form-field>\n @if (mobileCardMode) {\n <p class=\"responsive-settings__hint\">{{ tx('overview.responsive.cardsHint', 'Each record becomes a card with its visible columns. Selection and row actions remain available. Wider areas keep the table layout.') }}</p>\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('overview.responsive.breakpoint', 'Show cards at widths up to') }}</mat-label>\n <input matInput type=\"number\" min=\"1\" step=\"1\" required\n [ngModel]=\"mobileBreakpoint\" (ngModelChange)=\"onMobileBreakpointChange($event)\"\n [attr.aria-invalid]=\"mobileBreakpointInvalid\"\n aria-describedby=\"table-responsive-width-help\" />\n <span matTextSuffix>px</span>\n </mat-form-field>\n @if (mobileBreakpointInvalid) {\n <p class=\"responsive-settings__error\" role=\"alert\">{{ tx('overview.responsive.invalidWidth', 'Enter a positive whole number.') }}</p>\n }\n <p id=\"table-responsive-width-help\" class=\"responsive-settings__hint\">{{ tx('overview.responsive.widthHint', 'Uses the smaller width of the screen and the table area. Default: 768 px.') }}</p>\n <p class=\"responsive-settings__hint\">{{ tx('overview.responsive.virtualizationHint', 'Cards require standard rows. With virtual scrolling enabled, records remain in a table.') }}</p>\n }\n </section>\n <div class=\"overview-row\">\n <mat-form-field appearance=\"outline\" class=\"hs-field\">\n <mat-label>{{ tx('overview.horizontalScroll', 'Horizontal scroll') }}</mat-label>\n <mat-select [(ngModel)]=\"horizontalScroll\" (ngModelChange)=\"onHorizontalScrollChange($event)\">\n <mat-option value=\"auto\">{{ tx('overview.horizontalScroll.auto', 'Auto (default)') }}</mat-option>\n <mat-option value=\"wrap\">{{ tx('overview.horizontalScroll.wrap', 'Wrap') }}</mat-option>\n <mat-option value=\"none\">{{ tx('overview.horizontalScroll.none', 'Host controls') }}</mat-option>\n </mat-select>\n </mat-form-field>\n <div class=\"help small\">{{ tx('overview.horizontalScroll.help', 'Controls how the table handles horizontal width and scrollbar behavior.') }}</div>\n </div>\n @if (isEffectiveLocalMode()) { <div class=\"alert alert--info overview-mode-alert\">\n <mat-icon class=\"alert__icon\">info</mat-icon>\n <div class=\"alert__text\">\n {{ tx('overview.localModeActive', 'Effective local mode is active: server pagination and sorting strategies are blocked and persisted as client mode.') }}\n </div>\n </div> }\n <behavior-config-editor\n [config]=\"editedConfig\"\n [resourcePath]=\"resourcePath\"\n [forceClientStrategies]=\"isEffectiveLocalMode()\"\n (configChange)=\"onBehaviorConfigChange($event)\"\n (behaviorChange)=\"onBehaviorChange($event)\"\n ></behavior-config-editor>\n </div>\n }\n @case ('columns') {\n <columns-config-editor\n [config]=\"editedConfig\"\n [fieldMetadata]=\"getColumnAuthoringMetas()\"\n (configChange)=\"onColumnsConfigChange($event)\"\n (columnChange)=\"onColumnChange($event)\"\n ></columns-config-editor>\n }\n @case ('detail') {\n <praxis-table-detail-editor [config]=\"editedConfig\"\n (configChange)=\"onDetailConfigChange($event)\"\n (draftStateChange)=\"onDetailDraftStateChange($event)\"\n ></praxis-table-detail-editor>\n }\n @case ('rules') {\n <table-rules-editor\n [config]=\"editedConfig\"\n [resourcePath]=\"resourcePath\"\n (configChange)=\"onRulesConfigChange($event)\"\n ></table-rules-editor>\n }\n @case ('header') {\n <header-appearance-editor\n [config]=\"editedConfig\"\n (configChange)=\"onColumnsConfigChange($event)\"\n ></header-appearance-editor>\n }\n @case ('toolbar') {\n <toolbar-actions-editor\n [config]=\"editedConfig\"\n (configChange)=\"onToolbarActionsConfigChange($event)\"\n (toolbarActionsChange)=\"onToolbarActionsChange($event)\"\n (validationChange)=\"onSurfaceDraftValidationChange($event)\"\n ></toolbar-actions-editor>\n }\n <!-- Aba extra para integra\u00E7\u00F5es CRUD (vis\u00EDvel quando em contexto CRUD) -->\n @case ('crud') {\n <crud-integration-editor\n #crudEditorRef\n [tableId]=\"crudContext?.tableId || 'default'\"\n [crudContext]=\"crudContext\"\n [componentKeyId]=\"crudContext?.componentKeyId\"\n ></crud-integration-editor>\n }\n @case ('filters') {\n <div class=\"filters-authoring-stack\">\n <section\n class=\"responsive-settings toolbar-filters-region\"\n data-testid=\"table-unified-filter-region-section\"\n aria-labelledby=\"table-unified-filter-region-title\"\n >\n <div class=\"responsive-settings__heading\">\n <mat-icon aria-hidden=\"true\">filter_list</mat-icon>\n <h3 id=\"table-unified-filter-region-title\">\n {{ tx('filters.region.title', 'Regi\u00E3o \u00FAnica de filtros e escopos da toolbar') }}\n </h3>\n </div>\n <p class=\"responsive-settings__hint\">\n {{\n tx(\n 'filters.region.description',\n 'Centralize escopos r\u00E1pidos (Todos / Ativos / Inativos), busca e gaveta avan\u00E7ada na toolbar da tabela sem controles duplicados.'\n )\n }}\n </p>\n\n <div class=\"toolbar-filters-toggles\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.region.enabled', 'Regi\u00E3o de filtros na toolbar') }}</mat-label>\n <mat-select\n [ngModel]=\"toolbarFiltersEnabled\"\n (ngModelChange)=\"onToolbarFiltersEnabledChange($event)\"\n data-testid=\"table-toolbar-filters-enabled-select\"\n >\n <mat-option [value]=\"true\">{{ tx('common.enabled', 'Ativada') }}</mat-option>\n <mat-option [value]=\"false\">{{ tx('common.disabled', 'Desativada') }}</mat-option>\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.region.showQuickFilters', 'Exibir escopos r\u00E1pidos') }}</mat-label>\n <mat-select\n [ngModel]=\"toolbarShowQuickFilters\"\n (ngModelChange)=\"onToolbarShowQuickFiltersChange($event)\"\n data-testid=\"table-toolbar-show-quick-filters-select\"\n >\n <mat-option [value]=\"true\">{{ tx('common.yes', 'Sim') }}</mat-option>\n <mat-option [value]=\"false\">{{ tx('common.no', 'N\u00E3o') }}</mat-option>\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.region.showAdvancedButton', 'Bot\u00E3o de gaveta avan\u00E7ada') }}</mat-label>\n <mat-select\n [ngModel]=\"toolbarShowAdvancedButton\"\n (ngModelChange)=\"onToolbarShowAdvancedButtonChange($event)\"\n data-testid=\"table-toolbar-show-advanced-button-select\"\n >\n <mat-option [value]=\"true\">{{ tx('common.yes', 'Sim') }}</mat-option>\n <mat-option [value]=\"false\">{{ tx('common.no', 'N\u00E3o') }}</mat-option>\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.region.showAddButton', 'Bot\u00E3o \"Gerenciar campos\"') }}</mat-label>\n <mat-select\n [ngModel]=\"advancedFilterShowAddButton\"\n (ngModelChange)=\"onAdvancedFilterShowAddButtonChange($event)\"\n data-testid=\"table-toolbar-show-add-button-select\"\n >\n <mat-option [value]=\"true\">{{ tx('common.yes', 'Sim') }}</mat-option>\n <mat-option [value]=\"false\">{{ tx('common.no', 'N\u00E3o') }}</mat-option>\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.region.showFilterSettings', 'Bot\u00E3o \"Configura\u00E7\u00F5es do filtro\"') }}</mat-label>\n <mat-select\n [ngModel]=\"advancedFilterShowFilterSettings\"\n (ngModelChange)=\"onAdvancedFilterShowFilterSettingsChange($event)\"\n data-testid=\"table-toolbar-show-filter-settings-select\"\n >\n <mat-option [value]=\"true\">{{ tx('common.yes', 'Sim') }}</mat-option>\n <mat-option [value]=\"false\">{{ tx('common.no', 'N\u00E3o') }}</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n @if (duplicatedQuickFilterInlineFields.length > 0) {\n <div\n class=\"alert alert--warning connect-alert\"\n data-testid=\"table-filter-duplicate-scope-warning\"\n >\n <mat-icon class=\"alert__icon\">warning</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n {{\n tx(\n 'filters.region.duplicateWarning',\n 'Os campos abaixo j\u00E1 possuem escopos r\u00E1pidos na toolbar e tamb\u00E9m est\u00E3o nos filtros inline:'\n )\n }}\n <strong>{{ duplicatedQuickFilterInlineFields.join(', ') }}</strong>\n </div>\n <button\n mat-stroked-button\n color=\"primary\"\n type=\"button\"\n data-testid=\"table-filter-consolidate-duplicates-btn\"\n (click)=\"consolidateDuplicateInlineFilterFields()\"\n >\n {{ tx('filters.region.consolidateBtn', 'Consolidar nos escopos r\u00E1pidos') }}\n </button>\n </div>\n }\n\n <div class=\"quick-filters-header\">\n <strong>{{ tx('filters.quickFilters.heading', 'Escopos r\u00E1pidos (quickFilters)') }}</strong>\n <div class=\"quick-filters-actions\">\n <button\n mat-stroked-button\n type=\"button\"\n data-testid=\"table-quick-filters-preset-boolean-btn\"\n (click)=\"applyBooleanQuickFilterPreset()\"\n >\n {{ tx('filters.quickFilters.presetBoolean', 'Preset Todos / Ativos / Inativos') }}\n </button>\n <button\n mat-stroked-button\n color=\"primary\"\n type=\"button\"\n data-testid=\"table-quick-filters-add-btn\"\n (click)=\"addQuickFilterScope()\"\n >\n {{ tx('filters.quickFilters.add', 'Adicionar escopo') }}\n </button>\n </div>\n </div>\n\n @if (toolbarQuickFilters.length > 0) {\n <div class=\"quick-filters-list\" data-testid=\"table-quick-filters-list\">\n @for (qf of toolbarQuickFilters; track $index; let idx = $index) {\n <div class=\"quick-filter-row\" [attr.data-testid]=\"'table-quick-filter-row-' + idx\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.quickFilters.id', 'ID') }}</mat-label>\n <input\n matInput\n [ngModel]=\"qf.id\"\n (ngModelChange)=\"updateQuickFilterMeta(idx, { id: $event })\"\n [attr.data-testid]=\"'table-quick-filter-id-' + idx\"\n />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.quickFilters.label', 'R\u00F3tulo') }}</mat-label>\n <input\n matInput\n [ngModel]=\"qf.label\"\n (ngModelChange)=\"updateQuickFilterMeta(idx, { label: $event })\"\n [attr.data-testid]=\"'table-quick-filter-label-' + idx\"\n />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.quickFilters.mode', 'Tipo de escopo') }}</mat-label>\n <mat-select\n [ngModel]=\"getQuickFilterMode(qf)\"\n (ngModelChange)=\"onQuickFilterModeChange(idx, $event)\"\n [attr.data-testid]=\"'table-quick-filter-mode-' + idx\"\n >\n <mat-option value=\"all\">{{ tx('filters.quickFilters.mode.all', 'Todos (limpar filtro)') }}</mat-option>\n <mat-option value=\"boolean\">{{ tx('filters.quickFilters.mode.boolean', 'Status booleano (Sim/N\u00E3o)') }}</mat-option>\n <mat-option value=\"equals\">{{ tx('filters.quickFilters.mode.equals', 'Campo igual a valor') }}</mat-option>\n </mat-select>\n </mat-form-field>\n\n @if (getQuickFilterMode(qf) !== 'all') {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.quickFilters.field', 'Campo') }}</mat-label>\n <input\n matInput\n [ngModel]=\"getQuickFilterField(qf)\"\n (ngModelChange)=\"onQuickFilterFieldChange(idx, $event)\"\n [attr.data-testid]=\"'table-quick-filter-field-' + idx\"\n />\n </mat-form-field>\n\n @if (getQuickFilterMode(qf) === 'boolean') {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.quickFilters.value', 'Valor') }}</mat-label>\n <mat-select\n [ngModel]=\"getQuickFilterValue(qf)\"\n (ngModelChange)=\"onQuickFilterValueChange(idx, $event)\"\n [attr.data-testid]=\"'table-quick-filter-bool-value-' + idx\"\n >\n <mat-option [value]=\"true\">{{ tx('common.boolean.true', 'Verdadeiro (true)') }}</mat-option>\n <mat-option [value]=\"false\">{{ tx('common.boolean.false', 'Falso (false)') }}</mat-option>\n </mat-select>\n </mat-form-field>\n } @else {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.quickFilters.value', 'Valor') }}</mat-label>\n <input\n matInput\n [ngModel]=\"getQuickFilterValue(qf)\"\n (ngModelChange)=\"onQuickFilterValueChange(idx, $event)\"\n [attr.data-testid]=\"'table-quick-filter-value-' + idx\"\n />\n </mat-form-field>\n }\n }\n\n <button\n mat-stroked-button\n color=\"warn\"\n type=\"button\"\n [attr.data-testid]=\"'table-quick-filter-remove-' + idx\"\n (click)=\"removeQuickFilterScope(idx)\"\n >\n {{ tx('common.remove', 'Remover') }}\n </button>\n </div>\n }\n </div>\n }\n </section>\n\n <filter-settings\n #tableFilterSettingsRef\n [metadata]=\"getFilterSettingsMetas()\"\n [metadataSource]=\"isUsingFilterDtoMetas() ? 'filter-dto' : 'columns'\"\n [metadataLoading]=\"filterDtoMetasLoading\"\n [metadataErrorMsg]=\"filterDtoMetasErrorMsg\"\n [settings]=\"\n editedConfig.behavior?.filtering?.advancedFilters?.settings\n \"\n (settingsChange)=\"onFilterSettingsChange($event)\"\n ></filter-settings>\n </div>\n }\n @case ('messages') {\n <messages-localization-editor\n [config]=\"editedConfig\"\n (configChange)=\"onMessagesLocalizationConfigChange($event)\"\n (messagesLocalizationChange)=\"\n onMessagesLocalizationChange($event)\n \"\n ></messages-localization-editor>\n }\n @case ('dialogs') {\n <confirm-dialog-appearance-editor\n [config]=\"editedConfig\"\n (configChange)=\"onJsonConfigChange($event)\"\n ></confirm-dialog-appearance-editor>\n }\n @case ('json') {\n <json-config-editor\n [document]=\"buildJsonAuthoringDocument()\"\n (documentChange)=\"onJsonConfigChange($event)\"\n (validationChange)=\"onJsonValidationChange($event)\"\n (editorEvent)=\"onJsonEditorEvent($event)\"\n ></json-config-editor>\n }\n }\n </div>\n </mat-tab>\n }\n</mat-tab-group>\n@if (statusMessage) { <div class=\"config-editor-status\">\n <span\n class=\"status-text\"\n [class.error]=\"hasErrors\"\n [class.success]=\"hasSuccess\"\n >{{ statusMessage }}</span\n >\n</div> }\n", styles: ["@charset \"UTF-8\";:host{display:block;min-width:0;container:praxis-authoring-editor/inline-size}.editor-section-selector{display:none;padding:var(--pfx-spacing-2, 8px)}.editor-section-selector mat-form-field{width:100%}@container praxis-authoring-editor (max-width: 768px){.editor-section-selector{display:block}::ng-deep .config-tabs>.mat-mdc-tab-header{display:none}}.config-tabs{flex:1 1 auto;display:flex;flex-direction:column}.config-tabs .mat-mdc-tab{min-width:120px}.config-tabs .mat-mdc-tab-body-wrapper,.config-tabs .mat-mdc-tab-group-container{flex:1 1 auto;min-height:0}.config-tabs .mat-mdc-tab-body-content{height:100%;min-height:0;overflow:visible}.tab-content{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;padding:8px 8px 56px;box-sizing:border-box;overflow:visible}.overview-grid{display:grid;min-width:0;grid-template-columns:minmax(0,1fr);gap:12px}.overview-grid .overview-row{display:grid;grid-template-columns:280px 1fr;align-items:end;gap:12px}.overview-grid .overview-row .help.small{opacity:.75;font-size:12px}.overview-mode-alert{align-items:center}.connect-grid{display:grid;gap:12px;padding:8px;grid-template-columns:minmax(0,1fr);align-items:start}.connect-note{grid-column:1/-1;margin:0;font-size:12px;color:var(--md-sys-color-on-surface-variant)}.connect-alert{grid-column:1/-1;align-items:center}.connect-alert__text{flex:1}.connect-alert__meta{opacity:.85}.educational-card{margin-bottom:24px;background:var(--md-sys-color-surface-container-low);border-left:4px solid var(--md-sys-color-primary);flex-shrink:0}.config-editor-status{padding:12px 16px;margin-top:auto;border-top:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container);flex-shrink:0;position:relative}.status-text{font-size:.875rem;line-height:1.2}.status-text.error{color:var(--md-sys-color-error)}.status-text.success{color:var(--md-sys-color-primary)}@media(max-width:768px){.tab-content{padding:8px 8px 72px}.config-editor-status{padding:12px 16px}.connect-grid{grid-template-columns:1fr}.overview-grid .overview-row{grid-template-columns:1fr;align-items:start}:host{display:flex;flex-direction:column;flex:1 1 auto;min-height:0}}.responsive-settings{display:grid;grid-template-columns:minmax(0,1fr);gap:12px;min-width:0;padding:16px;border:1px solid var(--md-sys-color-outline-variant);border-radius:var(--md-sys-shape-corner-medium, 12px);background:var(--md-sys-color-surface-container-low)}.responsive-settings__heading{display:flex;align-items:center;gap:8px}.responsive-settings h3,.responsive-settings p{margin:0}.responsive-settings h3{font-size:1rem;font-weight:600}.responsive-settings p{font-size:.875rem;line-height:1.5}.responsive-settings__hint{color:var(--md-sys-color-on-surface-variant)}.responsive-settings__error{color:var(--md-sys-color-error)}.responsive-settings mat-form-field{width:100%;min-width:0}.filters-authoring-stack{display:grid;grid-template-columns:minmax(0,1fr);gap:16px}.toolbar-filters-toggles{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px}.quick-filters-header{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:8px}.quick-filters-actions{display:flex;flex-wrap:wrap;gap:8px}.quick-filters-list{display:grid;gap:10px}.quick-filter-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));align-items:center;gap:8px;padding:10px;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;background:var(--md-sys-color-surface)}\n"] }]
73922
+ ], template: "<div class=\"editor-section-selector\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('sections.selector', 'Editor section') }}</mat-label>\n <mat-select [(ngModel)]=\"activeSectionIndex\" data-testid=\"table-config-editor-sections\">\n @for (section of sections; track section.id; let index = $index) {\n <mat-option [value]=\"index\">{{ section.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n</div>\n<mat-tab-group [preserveContent]=\"true\" class=\"config-tabs\" [(selectedIndex)]=\"activeSectionIndex\">\n @for (section of sections; track section.id) {\n <mat-tab>\n <ng-template mat-tab-label>\n @if (section.icon) { <mat-icon [praxisIcon]=\"section.icon\"></mat-icon> }\n <span [attr.data-testid]=\"'config-tab-' + section.id\">{{ section.label }}</span>\n </ng-template>\n <div class=\"tab-content\">\n @switch (section.id) {\n @case ('connect') {\n <div class=\"connect-grid\">\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('connect.resource', 'Resource') }}</mat-label>\n <input matInput [(ngModel)]=\"resourcePath\" (ngModelChange)=\"onResourcePathChange($event)\" [placeholder]=\"tx('connect.resource.placeholder', 'e.g. employees')\" />\n <mat-icon matSuffix>link</mat-icon>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>{{ tx('connect.primaryKey', 'Primary key') }}</mat-label>\n <input\n matInput\n [(ngModel)]=\"idField\"\n (ngModelChange)=\"onIdFieldChange($event)\"\n [matAutocomplete]=\"idFieldPicker\"\n [placeholder]=\"tx('connect.primaryKey.placeholder', 'e.g. id, uuid, code')\"\n />\n <mat-icon matSuffix>fingerprint</mat-icon>\n <mat-autocomplete\n #idFieldPicker=\"matAutocomplete\"\n (optionSelected)=\"onIdFieldOptionSelected($event.option.value)\"\n >\n @for (option of filteredIdFieldOptions; track option.name) {\n <mat-option [value]=\"option.name\">\n <span>{{ option.name }}</span>\n @if (option.label && option.label !== option.name) {\n <small> - {{ option.label }}</small>\n }\n </mat-option>\n }\n @if (filteredIdFieldOptions.length === 0) {\n <mat-option disabled>\n {{ tx('connect.primaryKey.empty', 'No matching field found.') }}\n </mat-option>\n }\n </mat-autocomplete>\n </mat-form-field>\n <p class=\"connect-note\">{{ tx('connect.note', 'Define the API resource and, when needed, the primary key.') }}</p>\n <!-- Diverg\u00EAncia: idField -->\n @if (idFieldDiverges) { <div class=\"alert alert--warning connect-alert\">\n <mat-icon class=\"alert__icon\">warning</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n {{ tx('connect.serverPrimaryKeyDiverges', 'The primary key on the server differs from the current configuration.') }}\n <span class=\"connect-alert__meta\">(config: {{ idField || 'id' }} | servidor: {{ serverIdField || 'id' }})</span>\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onReconcileIdField()\">{{ tx('connect.reconcile', 'Reconcile') }}</button>\n </div> }\n <!-- Diverg\u00EAncia: serverHash -->\n @if (schemaHashDiverges) { <div class=\"alert alert--info connect-alert\">\n <mat-icon class=\"alert__icon\">info</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n {{ tx('connect.schemaSnapshotDiverges', 'The saved schema snapshot differs from the current server schema.') }}\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onAcceptServerHash()\">{{ tx('connect.updateMetadata', 'Update metadata') }}</button>\n </div> }\n @if (!schemaHashDiverges && schemaSnapshotMissing) { <div class=\"alert alert--info connect-alert\">\n <mat-icon class=\"alert__icon\">info</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n {{ tx('connect.schemaSnapshotMissing', 'A current schema exists on the server, but there is still no persisted snapshot for reconciliation.') }}\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onAcceptServerHash()\">{{ tx('connect.saveSnapshot', 'Save snapshot') }}</button>\n </div> }\n </div>\n }\n @case ('overview') {\n <div class=\"overview-grid\">\n <section class=\"responsive-settings\" aria-labelledby=\"table-responsive-title\">\n <div class=\"responsive-settings__heading\">\n <mat-icon aria-hidden=\"true\">devices</mat-icon>\n <h3 id=\"table-responsive-title\">{{ tx('overview.responsive.title', 'Narrow screens') }}</h3>\n </div>\n <p>{{ tx('overview.responsive.description', 'Choose how records appear when the table has less space.') }}</p>\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('overview.responsive.mode', 'Record layout') }}</mat-label>\n <mat-select [ngModel]=\"mobileCardMode\" (ngModelChange)=\"onMobileCardModeChange($event)\">\n <mat-option [value]=\"false\">{{ tx('overview.responsive.table', 'Table') }}</mat-option>\n <mat-option [value]=\"true\">{{ tx('overview.responsive.cards', 'Cards') }}</mat-option>\n </mat-select>\n </mat-form-field>\n @if (mobileCardMode) {\n <p class=\"responsive-settings__hint\">{{ tx('overview.responsive.cardsHint', 'Each record becomes a card with its visible columns. Selection and row actions remain available. Wider areas keep the table layout.') }}</p>\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('overview.responsive.breakpoint', 'Show cards at widths up to') }}</mat-label>\n <input matInput type=\"number\" min=\"1\" step=\"1\" required\n [ngModel]=\"mobileBreakpoint\" (ngModelChange)=\"onMobileBreakpointChange($event)\"\n [attr.aria-invalid]=\"mobileBreakpointInvalid\"\n aria-describedby=\"table-responsive-width-help\" />\n <span matTextSuffix>px</span>\n </mat-form-field>\n @if (mobileBreakpointInvalid) {\n <p class=\"responsive-settings__error\" role=\"alert\">{{ tx('overview.responsive.invalidWidth', 'Enter a positive whole number.') }}</p>\n }\n <p id=\"table-responsive-width-help\" class=\"responsive-settings__hint\">{{ tx('overview.responsive.widthHint', 'Uses the smaller width of the screen and the table area. Default: 768 px.') }}</p>\n <p class=\"responsive-settings__hint\">{{ tx('overview.responsive.virtualizationHint', 'Cards require standard rows. With virtual scrolling enabled, records remain in a table.') }}</p>\n }\n </section>\n <div class=\"overview-row\">\n <mat-form-field appearance=\"outline\" class=\"hs-field\">\n <mat-label>{{ tx('overview.horizontalScroll', 'Horizontal scroll') }}</mat-label>\n <mat-select [(ngModel)]=\"horizontalScroll\" (ngModelChange)=\"onHorizontalScrollChange($event)\">\n <mat-option value=\"auto\">{{ tx('overview.horizontalScroll.auto', 'Auto (default)') }}</mat-option>\n <mat-option value=\"wrap\">{{ tx('overview.horizontalScroll.wrap', 'Wrap') }}</mat-option>\n <mat-option value=\"none\">{{ tx('overview.horizontalScroll.none', 'Host controls') }}</mat-option>\n </mat-select>\n </mat-form-field>\n <div class=\"help small\">{{ tx('overview.horizontalScroll.help', 'Controls how the table handles horizontal width and scrollbar behavior.') }}</div>\n </div>\n @if (isEffectiveLocalMode()) { <div class=\"alert alert--info overview-mode-alert\">\n <mat-icon class=\"alert__icon\">info</mat-icon>\n <div class=\"alert__text\">\n {{ tx('overview.localModeActive', 'Effective local mode is active: server pagination and sorting strategies are blocked and persisted as client mode.') }}\n </div>\n </div> }\n <behavior-config-editor\n [config]=\"editedConfig\"\n [resourcePath]=\"resourcePath\"\n [forceClientStrategies]=\"isEffectiveLocalMode()\"\n (configChange)=\"onBehaviorConfigChange($event)\"\n (behaviorChange)=\"onBehaviorChange($event)\"\n ></behavior-config-editor>\n </div>\n }\n @case ('columns') {\n <columns-config-editor\n [config]=\"editedConfig\"\n [fieldMetadata]=\"getColumnAuthoringMetas()\"\n (configChange)=\"onColumnsConfigChange($event)\"\n (columnChange)=\"onColumnChange($event)\"\n ></columns-config-editor>\n }\n @case ('detail') {\n <praxis-table-detail-editor [config]=\"editedConfig\"\n (configChange)=\"onDetailConfigChange($event)\"\n (draftStateChange)=\"onDetailDraftStateChange($event)\"\n ></praxis-table-detail-editor>\n }\n @case ('rules') {\n <table-rules-editor\n [config]=\"editedConfig\"\n [resourcePath]=\"resourcePath\"\n (configChange)=\"onRulesConfigChange($event)\"\n ></table-rules-editor>\n }\n @case ('header') {\n <header-appearance-editor\n [config]=\"editedConfig\"\n (configChange)=\"onColumnsConfigChange($event)\"\n ></header-appearance-editor>\n }\n @case ('toolbar') {\n <toolbar-actions-editor\n [config]=\"editedConfig\"\n (configChange)=\"onToolbarActionsConfigChange($event)\"\n (toolbarActionsChange)=\"onToolbarActionsChange($event)\"\n (validationChange)=\"onSurfaceDraftValidationChange($event)\"\n ></toolbar-actions-editor>\n }\n <!-- Aba extra para integra\u00E7\u00F5es CRUD (vis\u00EDvel quando em contexto CRUD) -->\n @case ('crud') {\n <crud-integration-editor\n #crudEditorRef\n [tableId]=\"crudContext?.tableId || 'default'\"\n [crudContext]=\"crudContext\"\n [componentKeyId]=\"crudContext?.componentKeyId\"\n ></crud-integration-editor>\n }\n @case ('filters') {\n <div class=\"filters-authoring-stack\">\n <section\n class=\"responsive-settings toolbar-filters-region\"\n data-testid=\"table-unified-filter-region-section\"\n aria-labelledby=\"table-unified-filter-region-title\"\n >\n <div class=\"responsive-settings__heading\">\n <mat-icon aria-hidden=\"true\">filter_list</mat-icon>\n <h3 id=\"table-unified-filter-region-title\">\n {{ tx('filters.region.title', 'Regi\u00E3o \u00FAnica de filtros e escopos da toolbar') }}\n </h3>\n </div>\n <p class=\"responsive-settings__hint\">\n {{\n tx(\n 'filters.region.description',\n 'Centralize escopos r\u00E1pidos (Todos / Ativos / Inativos), busca e gaveta avan\u00E7ada na toolbar da tabela sem controles duplicados.'\n )\n }}\n </p>\n\n <div class=\"toolbar-filters-toggles\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.region.enabled', 'Regi\u00E3o de filtros na toolbar') }}</mat-label>\n <mat-select\n [ngModel]=\"toolbarFiltersEnabled\"\n (ngModelChange)=\"onToolbarFiltersEnabledChange($event)\"\n data-testid=\"table-toolbar-filters-enabled-select\"\n >\n <mat-option [value]=\"true\">{{ tx('common.enabled', 'Ativada') }}</mat-option>\n <mat-option [value]=\"false\">{{ tx('common.disabled', 'Desativada') }}</mat-option>\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.region.showQuickFilters', 'Exibir escopos r\u00E1pidos') }}</mat-label>\n <mat-select\n [ngModel]=\"toolbarShowQuickFilters\"\n (ngModelChange)=\"onToolbarShowQuickFiltersChange($event)\"\n data-testid=\"table-toolbar-show-quick-filters-select\"\n >\n <mat-option [value]=\"true\">{{ tx('common.yes', 'Sim') }}</mat-option>\n <mat-option [value]=\"false\">{{ tx('common.no', 'N\u00E3o') }}</mat-option>\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.region.showAdvancedButton', 'Bot\u00E3o de gaveta avan\u00E7ada') }}</mat-label>\n <mat-select\n [ngModel]=\"toolbarShowAdvancedButton\"\n (ngModelChange)=\"onToolbarShowAdvancedButtonChange($event)\"\n data-testid=\"table-toolbar-show-advanced-button-select\"\n >\n <mat-option [value]=\"true\">{{ tx('common.yes', 'Sim') }}</mat-option>\n <mat-option [value]=\"false\">{{ tx('common.no', 'N\u00E3o') }}</mat-option>\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.region.showAddButton', 'Bot\u00E3o \"Gerenciar campos\"') }}</mat-label>\n <mat-select\n [ngModel]=\"advancedFilterShowAddButton\"\n (ngModelChange)=\"onAdvancedFilterShowAddButtonChange($event)\"\n data-testid=\"table-toolbar-show-add-button-select\"\n >\n <mat-option [value]=\"true\">{{ tx('common.yes', 'Sim') }}</mat-option>\n <mat-option [value]=\"false\">{{ tx('common.no', 'N\u00E3o') }}</mat-option>\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.region.showFilterSettings', 'Bot\u00E3o \"Configura\u00E7\u00F5es do filtro\"') }}</mat-label>\n <mat-select\n [ngModel]=\"advancedFilterShowFilterSettings\"\n (ngModelChange)=\"onAdvancedFilterShowFilterSettingsChange($event)\"\n data-testid=\"table-toolbar-show-filter-settings-select\"\n >\n <mat-option [value]=\"true\">{{ tx('common.yes', 'Sim') }}</mat-option>\n <mat-option [value]=\"false\">{{ tx('common.no', 'N\u00E3o') }}</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n @if (duplicatedQuickFilterInlineFields.length > 0) {\n <div\n class=\"alert alert--warning connect-alert\"\n data-testid=\"table-filter-duplicate-scope-warning\"\n >\n <mat-icon class=\"alert__icon\">warning</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n {{\n tx(\n 'filters.region.duplicateWarning',\n 'Os campos abaixo j\u00E1 possuem escopos r\u00E1pidos na toolbar e tamb\u00E9m est\u00E3o nos filtros inline:'\n )\n }}\n <strong>{{ duplicatedQuickFilterInlineFields.join(', ') }}</strong>\n </div>\n <button\n mat-stroked-button\n color=\"primary\"\n type=\"button\"\n data-testid=\"table-filter-consolidate-duplicates-btn\"\n (click)=\"consolidateDuplicateInlineFilterFields()\"\n >\n {{ tx('filters.region.consolidateBtn', 'Consolidar nos escopos r\u00E1pidos') }}\n </button>\n </div>\n }\n\n <div class=\"quick-filters-header\">\n <strong>{{ tx('filters.quickFilters.heading', 'Escopos r\u00E1pidos (quickFilters)') }}</strong>\n <div class=\"quick-filters-actions\">\n <button\n mat-stroked-button\n type=\"button\"\n data-testid=\"table-quick-filters-preset-boolean-btn\"\n (click)=\"applyBooleanQuickFilterPreset()\"\n >\n {{ tx('filters.quickFilters.presetBoolean', 'Preset Todos / Ativos / Inativos') }}\n </button>\n <button\n mat-stroked-button\n color=\"primary\"\n type=\"button\"\n data-testid=\"table-quick-filters-add-btn\"\n (click)=\"addQuickFilterScope()\"\n >\n {{ tx('filters.quickFilters.add', 'Adicionar escopo') }}\n </button>\n </div>\n </div>\n\n @if (toolbarQuickFilters.length > 0) {\n <div class=\"quick-filters-list\" data-testid=\"table-quick-filters-list\">\n @for (qf of toolbarQuickFilters; track $index; let idx = $index) {\n <div class=\"quick-filter-row\" [attr.data-testid]=\"'table-quick-filter-row-' + idx\">\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.quickFilters.id', 'ID') }}</mat-label>\n <input\n matInput\n [ngModel]=\"qf.id\"\n (ngModelChange)=\"updateQuickFilterMeta(idx, { id: $event })\"\n [attr.data-testid]=\"'table-quick-filter-id-' + idx\"\n />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.quickFilters.label', 'R\u00F3tulo') }}</mat-label>\n <input\n matInput\n [ngModel]=\"qf.label\"\n (ngModelChange)=\"updateQuickFilterMeta(idx, { label: $event })\"\n [attr.data-testid]=\"'table-quick-filter-label-' + idx\"\n />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.quickFilters.mode', 'Tipo de escopo') }}</mat-label>\n <mat-select\n [ngModel]=\"getQuickFilterMode(qf)\"\n (ngModelChange)=\"onQuickFilterModeChange(idx, $event)\"\n [attr.data-testid]=\"'table-quick-filter-mode-' + idx\"\n >\n <mat-option value=\"all\">{{ tx('filters.quickFilters.mode.all', 'Todos (limpar filtro)') }}</mat-option>\n <mat-option value=\"boolean\">{{ tx('filters.quickFilters.mode.boolean', 'Status booleano (Sim/N\u00E3o)') }}</mat-option>\n <mat-option value=\"equals\">{{ tx('filters.quickFilters.mode.equals', 'Campo igual a valor') }}</mat-option>\n </mat-select>\n </mat-form-field>\n\n @if (getQuickFilterMode(qf) !== 'all') {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.quickFilters.field', 'Campo') }}</mat-label>\n <input\n matInput\n [ngModel]=\"getQuickFilterField(qf)\"\n (ngModelChange)=\"onQuickFilterFieldChange(idx, $event)\"\n [attr.data-testid]=\"'table-quick-filter-field-' + idx\"\n />\n </mat-form-field>\n\n @if (getQuickFilterMode(qf) === 'boolean') {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.quickFilters.value', 'Valor') }}</mat-label>\n <mat-select\n [ngModel]=\"getQuickFilterValue(qf)\"\n (ngModelChange)=\"onQuickFilterValueChange(idx, $event)\"\n [attr.data-testid]=\"'table-quick-filter-bool-value-' + idx\"\n >\n <mat-option [value]=\"true\">{{ tx('common.boolean.true', 'Verdadeiro (true)') }}</mat-option>\n <mat-option [value]=\"false\">{{ tx('common.boolean.false', 'Falso (false)') }}</mat-option>\n </mat-select>\n </mat-form-field>\n } @else {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <mat-label>{{ tx('filters.quickFilters.value', 'Valor') }}</mat-label>\n <input\n matInput\n [ngModel]=\"getQuickFilterValue(qf)\"\n (ngModelChange)=\"onQuickFilterValueChange(idx, $event)\"\n [attr.data-testid]=\"'table-quick-filter-value-' + idx\"\n />\n </mat-form-field>\n }\n }\n\n <button\n mat-stroked-button\n color=\"warn\"\n type=\"button\"\n [attr.data-testid]=\"'table-quick-filter-remove-' + idx\"\n (click)=\"removeQuickFilterScope(idx)\"\n >\n {{ tx('common.remove', 'Remover') }}\n </button>\n </div>\n }\n </div>\n }\n </section>\n\n <filter-settings\n #tableFilterSettingsRef\n [metadata]=\"getFilterSettingsMetas()\"\n [metadataSource]=\"isUsingFilterDtoMetas() ? 'filter-dto' : 'columns'\"\n [metadataLoading]=\"filterDtoMetasLoading\"\n [metadataErrorMsg]=\"filterDtoMetasErrorMsg\"\n [settings]=\"\n editedConfig.behavior?.filtering?.advancedFilters?.settings\n \"\n (settingsChange)=\"onFilterSettingsChange($event)\"\n ></filter-settings>\n </div>\n }\n @case ('messages') {\n <messages-localization-editor\n [config]=\"editedConfig\"\n (configChange)=\"onMessagesLocalizationConfigChange($event)\"\n (messagesLocalizationChange)=\"\n onMessagesLocalizationChange($event)\n \"\n ></messages-localization-editor>\n }\n @case ('dialogs') {\n <confirm-dialog-appearance-editor\n [config]=\"editedConfig\"\n (configChange)=\"onJsonConfigChange($event)\"\n ></confirm-dialog-appearance-editor>\n }\n @case ('json') {\n <json-config-editor\n [document]=\"buildJsonAuthoringDocument()\"\n (documentChange)=\"onJsonConfigChange($event)\"\n (validationChange)=\"onJsonValidationChange($event)\"\n (editorEvent)=\"onJsonEditorEvent($event)\"\n ></json-config-editor>\n }\n }\n </div>\n </mat-tab>\n }\n</mat-tab-group>\n@if (statusMessage) { <div class=\"config-editor-status\">\n <span\n class=\"status-text\"\n [class.error]=\"hasErrors\"\n [class.success]=\"hasSuccess\"\n >{{ statusMessage }}</span\n >\n</div> }\n", styles: ["@charset \"UTF-8\";:host{display:block;min-width:0;container:praxis-authoring-editor/inline-size}.editor-section-selector{display:block;padding:var(--pfx-spacing-2, 8px)}.editor-section-selector mat-form-field{width:100%}@container praxis-authoring-editor (max-width: 768px){.editor-section-selector{display:block}::ng-deep .config-tabs>.mat-mdc-tab-header{display:none}}.config-tabs{flex:1 1 auto;display:flex;flex-direction:column}.config-tabs .mat-mdc-tab{min-width:120px}.config-tabs .mat-mdc-tab-body-wrapper,.config-tabs .mat-mdc-tab-group-container{flex:1 1 auto;min-height:0}.config-tabs .mat-mdc-tab-body-content{height:100%;min-height:0;overflow:visible}.tab-content{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;padding:8px 8px 56px;box-sizing:border-box;overflow:visible}.overview-grid{display:grid;min-width:0;grid-template-columns:minmax(0,1fr);gap:12px}.overview-grid .overview-row{display:grid;grid-template-columns:280px 1fr;align-items:end;gap:12px}.overview-grid .overview-row .help.small{opacity:.75;font-size:12px}.overview-mode-alert{align-items:center}.connect-grid{display:grid;gap:12px;padding:8px;grid-template-columns:minmax(0,1fr);align-items:start}.connect-note{grid-column:1/-1;margin:0;font-size:12px;color:var(--md-sys-color-on-surface-variant)}.connect-alert{grid-column:1/-1;align-items:center}.connect-alert__text{flex:1}.connect-alert__meta{opacity:.85}.educational-card{margin-bottom:24px;background:var(--md-sys-color-surface-container-low);border-left:4px solid var(--md-sys-color-primary);flex-shrink:0}.config-editor-status{padding:12px 16px;margin-top:auto;border-top:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container);flex-shrink:0;position:relative}.status-text{font-size:.875rem;line-height:1.2}.status-text.error{color:var(--md-sys-color-error)}.status-text.success{color:var(--md-sys-color-primary)}@media(max-width:768px){.tab-content{padding:8px 8px 72px}.config-editor-status{padding:12px 16px}.connect-grid{grid-template-columns:1fr}.overview-grid .overview-row{grid-template-columns:1fr;align-items:start}:host{display:flex;flex-direction:column;flex:1 1 auto;min-height:0}}.responsive-settings{display:grid;grid-template-columns:minmax(0,1fr);gap:12px;min-width:0;padding:16px;border:1px solid var(--md-sys-color-outline-variant);border-radius:var(--md-sys-shape-corner-medium, 12px);background:var(--md-sys-color-surface-container-low)}.responsive-settings__heading{display:flex;align-items:center;gap:8px}.responsive-settings h3,.responsive-settings p{margin:0}.responsive-settings h3{font-size:1rem;font-weight:600}.responsive-settings p{font-size:.875rem;line-height:1.5}.responsive-settings__hint{color:var(--md-sys-color-on-surface-variant)}.responsive-settings__error{color:var(--md-sys-color-error)}.responsive-settings mat-form-field{width:100%;min-width:0}.filters-authoring-stack{display:grid;grid-template-columns:minmax(0,1fr);gap:16px}.toolbar-filters-toggles{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px}.quick-filters-header{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:8px}.quick-filters-actions{display:flex;flex-wrap:wrap;gap:8px}.quick-filters-list{display:grid;gap:10px}.quick-filter-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));align-items:center;gap:8px;padding:10px;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;background:var(--md-sys-color-surface)}\n"] }]
73711
73923
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1.TableConfigService }], propDecorators: { filterSettingsEditor: [{
73712
73924
  type: ViewChild,
73713
73925
  args: ['tableFilterSettingsRef']
@@ -1,6 +1,6 @@
1
1
  import { Observable, firstValueFrom } from 'rxjs';
2
2
  import { withAuthoringScopePolicy } from '@praxisui/ai';
3
- import { ar as setTableActionGlobalActionRef } from './praxisui-table-praxisui-table-Dg1mPimH.mjs';
3
+ import { ar as setTableActionGlobalActionRef } from './praxisui-table-praxisui-table-CWOg6JDN.mjs';
4
4
 
5
5
  class TableAgenticAuthoringTurnFlow {
6
6
  adapter;
@@ -1,7 +1,7 @@
1
1
  import { firstValueFrom } from 'rxjs';
2
2
  import { BaseAiAdapter, sanitizePraxisAssistantText, createComponentAuthoringContext } from '@praxisui/ai';
3
3
  import { PRAXIS_GLOBAL_ACTION_CATALOG, deepMerge } from '@praxisui/core';
4
- import { W as TABLE_COMPONENT_EDIT_PLAN_OPERATION_IDS, p as PRAXIS_TABLE_AUTHORING_MANIFEST, T as TABLE_AI_CAPABILITIES, a6 as coerceTableComponentEditPlans, a8 as compileTableComponentEditPlans, Y as TASK_PRESETS, ae as getTableComponentEditPlanCapabilities, R as TABLE_COMPONENT_EDIT_PLAN_EXPECTED_PATHS, O as TABLE_COMPONENT_EDIT_PLAN_ALLOWED_CHANGE_KINDS, U as TABLE_COMPONENT_EDIT_PLAN_JSON_SCHEMA, X as TABLE_COMPONENT_EDIT_PLAN_VERSION, Q as TABLE_COMPONENT_EDIT_PLAN_BATCH_KIND, V as TABLE_COMPONENT_EDIT_PLAN_KIND } from './praxisui-table-praxisui-table-Dg1mPimH.mjs';
4
+ import { W as TABLE_COMPONENT_EDIT_PLAN_OPERATION_IDS, p as PRAXIS_TABLE_AUTHORING_MANIFEST, T as TABLE_AI_CAPABILITIES, a6 as coerceTableComponentEditPlans, a8 as compileTableComponentEditPlans, Y as TASK_PRESETS, ae as getTableComponentEditPlanCapabilities, R as TABLE_COMPONENT_EDIT_PLAN_EXPECTED_PATHS, O as TABLE_COMPONENT_EDIT_PLAN_ALLOWED_CHANGE_KINDS, U as TABLE_COMPONENT_EDIT_PLAN_JSON_SCHEMA, X as TABLE_COMPONENT_EDIT_PLAN_VERSION, Q as TABLE_COMPONENT_EDIT_PLAN_BATCH_KIND, V as TABLE_COMPONENT_EDIT_PLAN_KIND } from './praxisui-table-praxisui-table-CWOg6JDN.mjs';
5
5
 
6
6
  const TABLE_ROW_EXPRESSION_CONTEXT_OPTION = {
7
7
  mode: 'expression',
@@ -1,2 +1,2 @@
1
- export { A as ANALYTICS_TABLE_ROW_KEY_FIELD, a as AnalyticalDrawerRegistryService, b as AnalyticsTableConfigAdapterService, c as AnalyticsTableContractService, d as AnalyticsTableStatsApiService, B as BOOLEAN_PRESETS, e as BehaviorConfigEditorComponent, C as CURRENCY_PRESETS, f as ColumnsConfigEditorComponent, D as DATE_PRESETS, g as DataFormatterComponent, h as DataFormattingService, F as FORMULA_TEMPLATES, i as FilterConfigService, j as FilterSettingsComponent, k as FormulaGeneratorService, J as JsonConfigEditorComponent, M as MessagesLocalizationEditorComponent, N as NUMBER_PRESETS, P as PERCENTAGE_PRESETS, l as PRAXIS_ANALYTICAL_DRAWER_SCHEMAS, m as PRAXIS_FILTER_AUTHORING_MANIFEST, n as PRAXIS_FILTER_COMPONENT_METADATA, o as PRAXIS_FILTER_VIEW_CONTEXT_SCHEMA_VERSION, p as PRAXIS_TABLE_AUTHORING_MANIFEST, q as PRAXIS_TABLE_COMPONENT_METADATA, r as PRAXIS_TABLE_TOOLBAR_APPEARANCE_PRESETS, s as PRAXIS_TABLE_TOOLBAR_DEFAULT_APPEARANCE, t as PRAXIS_TABLE_TOOLBAR_TOKEN_PRESETS, u as PraxisAnalyticalDrawerComponent, v as PraxisFilter, w as PraxisFilterOutletComponent, x as PraxisFilterOutletDirective, y as PraxisFilterWidgetConfigEditor, z as PraxisScopeBarComponent, E as PraxisTable, G as PraxisTableConfigEditor, H as PraxisTableInlineAuthoringEditorComponent, I as PraxisTableToolbar, K as PraxisTableWidgetConfigEditor, S as STRING_PRESETS, T as TABLE_AI_CAPABILITIES, L as TABLE_COMPONENT_AI_CAPABILITIES, O as TABLE_COMPONENT_EDIT_PLAN_ALLOWED_CHANGE_KINDS, Q as TABLE_COMPONENT_EDIT_PLAN_BATCH_KIND, R as TABLE_COMPONENT_EDIT_PLAN_EXPECTED_PATHS, U as TABLE_COMPONENT_EDIT_PLAN_JSON_SCHEMA, V as TABLE_COMPONENT_EDIT_PLAN_KIND, X as TABLE_COMPONENT_EDIT_PLAN_VERSION, Y as TASK_PRESETS, Z as TableDefaultsProvider, _ as TableRulesEditorComponent, $ as ToolbarActionsEditorComponent, a0 as ValueMappingEditorComponent, a1 as VisualFormulaBuilderComponent, a2 as analyticsComparisonMetricField, a3 as applyComputedFieldsToRows, a4 as buildTableApplyPlan, a5 as coerceTableComponentEditPlan, a6 as coerceTableComponentEditPlans, a7 as compileTableComponentEditPlan, a8 as compileTableComponentEditPlans, a9 as createTableAuthoringDocument, aa as formatComputedFieldValue, ab as getActionId, ac as getEnum, ad as getTableCapabilities, ae as getTableComponentEditPlanCapabilities, af as interpolateRelationEndpoint, ag as isAnalyticalDrawerSchema, ah as isTableRendererSupportedByRichContentP0, ai as mapTableRendererToRichContentP0, aj as normalizeComputedFieldDefinition, ak as normalizeTableAuthoringDocument, al as parseLegacyOrTableDocument, am as providePraxisAnalyticalDrawerSchemas, an as providePraxisFilterMetadata, ao as providePraxisTableMetadata, ap as providePraxisTableToolbarAppearance, aq as serializeTableAuthoringDocument, as as toCanonicalTableConfig, at as validateTableAuthoringDocument } from './praxisui-table-praxisui-table-Dg1mPimH.mjs';
1
+ export { A as ANALYTICS_TABLE_ROW_KEY_FIELD, a as AnalyticalDrawerRegistryService, b as AnalyticsTableConfigAdapterService, c as AnalyticsTableContractService, d as AnalyticsTableStatsApiService, B as BOOLEAN_PRESETS, e as BehaviorConfigEditorComponent, C as CURRENCY_PRESETS, f as ColumnsConfigEditorComponent, D as DATE_PRESETS, g as DataFormatterComponent, h as DataFormattingService, F as FORMULA_TEMPLATES, i as FilterConfigService, j as FilterSettingsComponent, k as FormulaGeneratorService, J as JsonConfigEditorComponent, M as MessagesLocalizationEditorComponent, N as NUMBER_PRESETS, P as PERCENTAGE_PRESETS, l as PRAXIS_ANALYTICAL_DRAWER_SCHEMAS, m as PRAXIS_FILTER_AUTHORING_MANIFEST, n as PRAXIS_FILTER_COMPONENT_METADATA, o as PRAXIS_FILTER_VIEW_CONTEXT_SCHEMA_VERSION, p as PRAXIS_TABLE_AUTHORING_MANIFEST, q as PRAXIS_TABLE_COMPONENT_METADATA, r as PRAXIS_TABLE_TOOLBAR_APPEARANCE_PRESETS, s as PRAXIS_TABLE_TOOLBAR_DEFAULT_APPEARANCE, t as PRAXIS_TABLE_TOOLBAR_TOKEN_PRESETS, u as PraxisAnalyticalDrawerComponent, v as PraxisFilter, w as PraxisFilterOutletComponent, x as PraxisFilterOutletDirective, y as PraxisFilterWidgetConfigEditor, z as PraxisScopeBarComponent, E as PraxisTable, G as PraxisTableConfigEditor, H as PraxisTableInlineAuthoringEditorComponent, I as PraxisTableToolbar, K as PraxisTableWidgetConfigEditor, S as STRING_PRESETS, T as TABLE_AI_CAPABILITIES, L as TABLE_COMPONENT_AI_CAPABILITIES, O as TABLE_COMPONENT_EDIT_PLAN_ALLOWED_CHANGE_KINDS, Q as TABLE_COMPONENT_EDIT_PLAN_BATCH_KIND, R as TABLE_COMPONENT_EDIT_PLAN_EXPECTED_PATHS, U as TABLE_COMPONENT_EDIT_PLAN_JSON_SCHEMA, V as TABLE_COMPONENT_EDIT_PLAN_KIND, X as TABLE_COMPONENT_EDIT_PLAN_VERSION, Y as TASK_PRESETS, Z as TableDefaultsProvider, _ as TableRulesEditorComponent, $ as ToolbarActionsEditorComponent, a0 as ValueMappingEditorComponent, a1 as VisualFormulaBuilderComponent, a2 as analyticsComparisonMetricField, a3 as applyComputedFieldsToRows, a4 as buildTableApplyPlan, a5 as coerceTableComponentEditPlan, a6 as coerceTableComponentEditPlans, a7 as compileTableComponentEditPlan, a8 as compileTableComponentEditPlans, a9 as createTableAuthoringDocument, aa as formatComputedFieldValue, ab as getActionId, ac as getEnum, ad as getTableCapabilities, ae as getTableComponentEditPlanCapabilities, af as interpolateRelationEndpoint, ag as isAnalyticalDrawerSchema, ah as isTableRendererSupportedByRichContentP0, ai as mapTableRendererToRichContentP0, aj as normalizeComputedFieldDefinition, ak as normalizeTableAuthoringDocument, al as parseLegacyOrTableDocument, am as providePraxisAnalyticalDrawerSchemas, an as providePraxisFilterMetadata, ao as providePraxisTableMetadata, ap as providePraxisTableToolbarAppearance, aq as serializeTableAuthoringDocument, as as toCanonicalTableConfig, at as validateTableAuthoringDocument } from './praxisui-table-praxisui-table-CWOg6JDN.mjs';
2
2
  export { createDynamicRecord, extractRecordId, isDynamicDataRecord, resolveDataField } from '@praxisui/core';
package/package.json CHANGED
@@ -1,24 +1,24 @@
1
1
  {
2
2
  "name": "@praxisui/table",
3
- "version": "10.0.0-rc.30",
3
+ "version": "10.0.0-rc.32",
4
4
  "description": "Advanced data table for Angular (Praxis UI) with editing, filtering, sorting, virtualization, and settings panel integration.",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "^21.0.0",
7
7
  "@angular/core": "^21.0.0",
8
8
  "@angular/platform-browser": "^21.0.0",
9
- "@praxisui/ai": "^10.0.0-rc.30",
10
- "@praxisui/core": "^10.0.0-rc.30",
11
- "@praxisui/dynamic-fields": "^10.0.0-rc.30",
12
- "@praxisui/dynamic-form": "^10.0.0-rc.30",
13
- "@praxisui/metadata-editor": "^10.0.0-rc.30",
14
- "@praxisui/rich-content": "^10.0.0-rc.30",
15
- "@praxisui/settings-panel": "^10.0.0-rc.30",
16
- "@praxisui/table-rule-builder": "^10.0.0-rc.30",
9
+ "@praxisui/ai": "^10.0.0-rc.32",
10
+ "@praxisui/core": "^10.0.0-rc.32",
11
+ "@praxisui/dynamic-fields": "^10.0.0-rc.32",
12
+ "@praxisui/dynamic-form": "^10.0.0-rc.32",
13
+ "@praxisui/metadata-editor": "^10.0.0-rc.32",
14
+ "@praxisui/rich-content": "^10.0.0-rc.32",
15
+ "@praxisui/settings-panel": "^10.0.0-rc.32",
16
+ "@praxisui/table-rule-builder": "^10.0.0-rc.32",
17
17
  "@angular/cdk": "^21.0.0",
18
18
  "@angular/forms": "^21.0.0",
19
19
  "@angular/material": "^21.0.0",
20
20
  "@angular/router": "^21.0.0",
21
- "@praxisui/dialog": "^10.0.0-rc.30",
21
+ "@praxisui/dialog": "^10.0.0-rc.32",
22
22
  "rxjs": "~7.8.0"
23
23
  },
24
24
  "dependencies": {
@@ -1,8 +1,8 @@
1
- import * as i0 from '@angular/core';
2
- import { InjectionToken, AfterViewInit, OnDestroy, EventEmitter, ElementRef, Provider, OnInit, OnChanges, DoCheck, DestroyRef, ChangeDetectorRef, NgZone, SimpleChanges, ComponentRef, AfterViewChecked, AfterContentInit, TemplateRef, Injector, Type } from '@angular/core';
3
1
  import * as _praxisui_core from '@praxisui/core';
4
- import { BulkExecutionContract, ResourceDiscoveryRequestOptions, BulkWireId, RestApiLinks, BulkSyncSession, BulkFieldChange, TableToolbarAppearanceConfig, TableConfig, TableToolbarAppearanceDensity, ToolbarActionEvent, PraxisExportScope, LoggerService, PraxisI18nService, TableToolbarAppearanceVariant, TableToolbarAppearanceShape, TableToolbarAppearanceDivider, TableToolbarTokenName, ToolbarAction as ToolbarAction$1, GlobalConfigService, FilterPresentationConfig, AsyncConfigStorage, LocalStorageConfigService, ApiUrlEntry, FieldMetadata, SchemaIdParams, FormConfig, GenericCrudService, DynamicFormService, SchemaNormalizerService, ComponentKeyService, ComponentDocMeta, PraxisTextValue, PraxisAnalyticsProjection, PraxisDataQueryContext, ApiUrlConfig, DynamicDataRecord, MaterializedResourceIdentity, PraxisQueryFilterExpression, RecordRelatedSurfaceContextPack, PraxisResourceEvent, LoadingState, ColumnDefinition, RichBlockNode, ResourceCapabilityDigest, ConnectionStorage, LoadingOrchestrator, GlobalActionService, EnterpriseRuntimeContextService, PraxisLoadingRenderer, PraxisRuntimeComponentObservationEnvelope, TableDetailRichListNode, TableDetailListNode, TableDetailCardGridNode, TableDetailCardGridCardNode, TableDetailTimelineNode, JsonLogicDataRecord, TableDetailListItemAction, TableDetailActionBarNode, TableDetailActionBarAction, TableDetailRefNode, TableDetailTemplateRefNode, TableDetailDiagramEmbedNode, TableDetailEmbedAction, RichContentDocument, RichBlockHostCapabilities, GlobalActionRef, PraxisRuntimeGlobalActionEffect, ResourceActionCatalogItem, ResourceSurfaceCatalogItem, ResourceCapabilityOperation, JsonLogicExpression, PraxisPresentationVisualizationKind, PraxisTableCellVisualizationConstraint, PraxisPresentationVisualizationThreshold, PraxisPresentationVisualizationTone, GlobalActionCatalogEntry, IconPickerService, SurfaceOpenPayload, GlobalActionField, FieldDefinition, PraxisRuntimeConditionalEffectRule, PraxisJsonLogicService, TableConfigService, SettingsValueProvider as SettingsValueProvider$1, ResourceQuickConnectComponent, AnalyticsSchemaContractService, AnalyticsPresentationResolver, RichPresenterNode, RichComposeNode, ComponentAuthoringManifest, AiCapability, AiCapabilityCategory, AiValueKind, AiCapabilityCatalog, TableDrawerConfig, PraxisAnalyticalDrawerTab, PraxisPresentationVisualizationConfig, PraxisAnalyticalDrawerSchema, SafeExpressionEvaluator, PraxisComputedFieldsConfig, PraxisComputedFieldDefinition } from '@praxisui/core';
2
+ import { BulkExecutionContract, ResourceDiscoveryRequestOptions, BulkWireId, RestApiLinks, BulkSyncSession, BulkFieldChange, TableToolbarAppearanceConfig, TableConfig, TableToolbarAppearanceDensity, ToolbarActionEvent, PraxisExportScope, LoggerService, PraxisI18nService, TableToolbarAppearanceVariant, TableToolbarAppearanceShape, TableToolbarAppearanceDivider, TableToolbarTokenName, ToolbarAction as ToolbarAction$1, GlobalConfigService, FilterPresentationConfig, AsyncConfigStorage, LocalStorageConfigService, ApiUrlEntry, FieldMetadata, SchemaIdParams, FormConfig, GenericCrudService, DynamicFormService, SchemaNormalizerService, ComponentKeyService, ComponentDocMeta, PraxisTextValue, PraxisAnalyticsProjection, PraxisDataQueryContext, ApiUrlConfig, DynamicDataRecord, MaterializedResourceIdentity, TableConfigInput, PraxisQueryFilterExpression, RecordRelatedSurfaceContextPack, PraxisResourceEvent, LoadingState, ColumnDefinition, RichBlockNode, ResourceCapabilityDigest, ConnectionStorage, LoadingOrchestrator, GlobalActionService, EnterpriseRuntimeContextService, PraxisLoadingRenderer, PraxisRuntimeComponentObservationEnvelope, TableDetailRichListNode, TableDetailListNode, TableDetailCardGridNode, TableDetailCardGridCardNode, TableDetailTimelineNode, JsonLogicDataRecord, TableDetailListItemAction, TableDetailActionBarNode, TableDetailActionBarAction, TableDetailRefNode, TableDetailTemplateRefNode, TableDetailDiagramEmbedNode, TableDetailEmbedAction, RichContentDocument, RichBlockHostCapabilities, GlobalActionRef, PraxisRuntimeGlobalActionEffect, ResourceActionCatalogItem, ResourceSurfaceCatalogItem, ResourceCapabilityOperation, JsonLogicExpression, PraxisPresentationVisualizationKind, PraxisTableCellVisualizationConstraint, PraxisPresentationVisualizationThreshold, PraxisPresentationVisualizationTone, GlobalActionCatalogEntry, IconPickerService, SurfaceOpenPayload, GlobalActionField, FieldDefinition, PraxisRuntimeConditionalEffectRule, PraxisJsonLogicService, TableConfigService, SettingsValueProvider as SettingsValueProvider$1, ResourceQuickConnectComponent, AnalyticsSchemaContractService, AnalyticsPresentationResolver, RichPresenterNode, RichComposeNode, ComponentAuthoringManifest, AiCapability, AiCapabilityCategory, AiValueKind, AiCapabilityCatalog, TableDrawerConfig, PraxisAnalyticalDrawerTab, PraxisPresentationVisualizationConfig, PraxisAnalyticalDrawerSchema, SafeExpressionEvaluator, PraxisComputedFieldsConfig, PraxisComputedFieldDefinition } from '@praxisui/core';
5
3
  export { DynamicDataCollection, DynamicDataRecord, DynamicRecordMeta, ResourceLinkItem, ResourceLinks, createDynamicRecord, extractRecordId, isDynamicDataRecord, resolveDataField } from '@praxisui/core';
4
+ import * as i0 from '@angular/core';
5
+ import { InjectionToken, AfterViewInit, OnDestroy, EventEmitter, ElementRef, Provider, OnInit, OnChanges, DoCheck, DestroyRef, ChangeDetectorRef, NgZone, SimpleChanges, ComponentRef, AfterViewChecked, AfterContentInit, TemplateRef, Injector, Type } from '@angular/core';
6
6
  import { ActivatedRoute } from '@angular/router';
7
7
  import { SafeHtml } from '@angular/platform-browser';
8
8
  import { MatTable, MatTableDataSource } from '@angular/material/table';
@@ -454,6 +454,7 @@ declare class TableDefaultsProvider {
454
454
  constructor(global: GlobalConfigService);
455
455
  /**
456
456
  * Returns default configuration for a given table identifier.
457
+ * Precedence inside defaults: GlobalConfig.table > createDefaultTableConfig().
457
458
  * @param tableId Table identifier
458
459
  */
459
460
  getDefaults(_tableId: string): TableConfig;
@@ -1428,7 +1429,10 @@ declare class PraxisTable implements OnInit, OnChanges, AfterViewInit, AfterView
1428
1429
  readonly paginatorSelectConfig: MatPaginatorSelectConfig;
1429
1430
  private static readonly ROW_ANIMATION_PRESETS;
1430
1431
  private static readonly ROW_ANIMATION_PRESET_ALIASES;
1432
+ static ngAcceptInputType_config: TableConfigInput;
1431
1433
  config: TableConfig;
1434
+ private hostInputConfig?;
1435
+ private persistedUserConfig?;
1432
1436
  resourcePath: string;
1433
1437
  data: any[] | null;
1434
1438
  tableId: string;
@@ -1443,9 +1447,15 @@ declare class PraxisTable implements OnInit, OnChanges, AfterViewInit, AfterView
1443
1447
  autoOpenSettingsOnOutdated: boolean;
1444
1448
  crudContext: any;
1445
1449
  filterCriteria: Record<string, any>;
1446
- filterTeleport: boolean;
1450
+ readonly filterCriteriaChange: EventEmitter<Record<string, any>>;
1451
+ private _filterTeleport;
1452
+ set filterTeleport(value: boolean);
1453
+ get filterTeleport(): boolean;
1447
1454
  widgetId?: string;
1448
- filterBarTemplate?: TemplateRef<unknown>;
1455
+ readonly filterTemplateSignal: i0.WritableSignal<TemplateRef<unknown> | undefined>;
1456
+ private _filterBarTemplate?;
1457
+ set filterBarTemplate(tpl: TemplateRef<unknown> | undefined);
1458
+ get filterBarTemplate(): TemplateRef<unknown> | undefined;
1449
1459
  get filterTemplate(): TemplateRef<unknown> | undefined;
1450
1460
  private readonly widgetEventBus;
1451
1461
  queryContext: PraxisDataQueryContextBridge | null;
@@ -1854,6 +1864,7 @@ declare class PraxisTable implements OnInit, OnChanges, AfterViewInit, AfterView
1854
1864
  private tableConfigKey;
1855
1865
  private filterConfigKey;
1856
1866
  private ensureConfigDefaults;
1867
+ private recomposeEffectiveTableConfig;
1857
1868
  private hasDeclaredAppearanceDensity;
1858
1869
  private hasDeclaredAppearanceSection;
1859
1870
  private shouldApplyAppearanceDefaultValue;
@@ -2372,6 +2383,7 @@ declare class PraxisTable implements OnInit, OnChanges, AfterViewInit, AfterView
2372
2383
  private syncQuickFilterStateFromEffectiveCriteria;
2373
2384
  private clearQuickFilterRuntimeState;
2374
2385
  private hasEquivalentFilterCriteria;
2386
+ private lastAppliedFilterCriteria;
2375
2387
  applyAdvancedFilterCriteria(criteria: Record<string, any> | null | undefined, reason?: string): void;
2376
2388
  private setAdvancedFilterCriteria;
2377
2389
  private syncAdvancedFilterValueFromCriteria;
@@ -2755,6 +2767,7 @@ declare class PraxisTable implements OnInit, OnChanges, AfterViewInit, AfterView
2755
2767
  private isColumnFiltersSupported;
2756
2768
  private isRowDraggingSupported;
2757
2769
  private isRowExpansionSupported;
2770
+ private hasComplementarySchemaExpansionFields;
2758
2771
  private enforceAppearanceSchemaGuards;
2759
2772
  private enforceUnsupportedFeatureGuards;
2760
2773
  getToolbarEvaluationContext(): Record<string, any>;
@@ -3065,7 +3078,7 @@ declare class PraxisTable implements OnInit, OnChanges, AfterViewInit, AfterView
3065
3078
  private scheduleRowDiscoveryMarkForCheck;
3066
3079
  ngOnDestroy(): void;
3067
3080
  static ɵfac: i0.ɵɵFactoryDeclaration<PraxisTable, [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, { optional: true; }, { optional: true; }, { optional: true; }, { optional: true; }, { optional: true; }]>;
3068
- static ɵcmp: i0.ɵɵComponentDeclaration<PraxisTable, "praxis-table", never, { "config": { "alias": "config"; "required": false; }; "resourcePath": { "alias": "resourcePath"; "required": false; }; "data": { "alias": "data"; "required": false; }; "tableId": { "alias": "tableId"; "required": true; }; "componentInstanceId": { "alias": "componentInstanceId"; "required": false; }; "configPersistenceStrategy": { "alias": "configPersistenceStrategy"; "required": false; }; "title": { "alias": "title"; "required": false; }; "subtitle": { "alias": "subtitle"; "required": false; }; "icon": { "alias": "icon"; "required": false; }; "autoDelete": { "alias": "autoDelete"; "required": false; }; "notifyIfOutdated": { "alias": "notifyIfOutdated"; "required": false; }; "snoozeMs": { "alias": "snoozeMs"; "required": false; }; "autoOpenSettingsOnOutdated": { "alias": "autoOpenSettingsOnOutdated"; "required": false; }; "crudContext": { "alias": "crudContext"; "required": false; }; "filterCriteria": { "alias": "filterCriteria"; "required": false; }; "filterTeleport": { "alias": "filterTeleport"; "required": false; }; "widgetId": { "alias": "widgetId"; "required": false; }; "queryContext": { "alias": "queryContext"; "required": false; }; "aiContext": { "alias": "aiContext"; "required": false; }; "aiAssistantVoiceInputMode": { "alias": "aiAssistantVoiceInputMode"; "required": false; }; "aiAssistantVoiceLanguage": { "alias": "aiAssistantVoiceLanguage"; "required": false; }; "horizontalScroll": { "alias": "horizontalScroll"; "required": false; }; "enableCustomization": { "alias": "enableCustomization"; "required": false; }; "authoringCapability": { "alias": "authoringCapability"; "required": false; }; "dense": { "alias": "dense"; "required": false; }; }, { "queryContextClear": "queryContextClear"; "queryContextChange": "queryContextChange"; "rowClick": "rowClick"; "widgetEvent": "widgetEvent"; "resourceEvent": "resourceEvent"; "rowDoubleClick": "rowDoubleClick"; "rowExpansionChange": "rowExpansionChange"; "rowAction": "rowAction"; "toolbarAction": "toolbarAction"; "bulkAction": "bulkAction"; "exportAction": "exportAction"; "columnReorder": "columnReorder"; "columnReorderAttempt": "columnReorderAttempt"; "columnResize": "columnResize"; "beforeDelete": "beforeDelete"; "afterDelete": "afterDelete"; "deleteError": "deleteError"; "beforeBulkDelete": "beforeBulkDelete"; "afterBulkDelete": "afterBulkDelete"; "bulkDeleteError": "bulkDeleteError"; "schemaStatusChange": "schemaStatusChange"; "configChange": "configChange"; "metadataChange": "metadataChange"; "loadingStateChange": "loadingStateChange"; "collectionLinksChange": "collectionLinksChange"; "selectionChange": "selectionChange"; "drawerOpen": "drawerOpen"; "drawerClose": "drawerClose"; "drawerTabChange": "drawerTabChange"; }, ["toolbar", "projectedFilter"], ["[advancedFilter]", "[toolbar]", "[advancedFilter]", "[toolbar]", "[advancedFilter]", "[toolbar]"], true, never>;
3081
+ static ɵcmp: i0.ɵɵComponentDeclaration<PraxisTable, "praxis-table", never, { "config": { "alias": "config"; "required": false; }; "resourcePath": { "alias": "resourcePath"; "required": false; }; "data": { "alias": "data"; "required": false; }; "tableId": { "alias": "tableId"; "required": true; }; "componentInstanceId": { "alias": "componentInstanceId"; "required": false; }; "configPersistenceStrategy": { "alias": "configPersistenceStrategy"; "required": false; }; "title": { "alias": "title"; "required": false; }; "subtitle": { "alias": "subtitle"; "required": false; }; "icon": { "alias": "icon"; "required": false; }; "autoDelete": { "alias": "autoDelete"; "required": false; }; "notifyIfOutdated": { "alias": "notifyIfOutdated"; "required": false; }; "snoozeMs": { "alias": "snoozeMs"; "required": false; }; "autoOpenSettingsOnOutdated": { "alias": "autoOpenSettingsOnOutdated"; "required": false; }; "crudContext": { "alias": "crudContext"; "required": false; }; "filterCriteria": { "alias": "filterCriteria"; "required": false; }; "filterTeleport": { "alias": "filterTeleport"; "required": false; }; "widgetId": { "alias": "widgetId"; "required": false; }; "queryContext": { "alias": "queryContext"; "required": false; }; "aiContext": { "alias": "aiContext"; "required": false; }; "aiAssistantVoiceInputMode": { "alias": "aiAssistantVoiceInputMode"; "required": false; }; "aiAssistantVoiceLanguage": { "alias": "aiAssistantVoiceLanguage"; "required": false; }; "horizontalScroll": { "alias": "horizontalScroll"; "required": false; }; "enableCustomization": { "alias": "enableCustomization"; "required": false; }; "authoringCapability": { "alias": "authoringCapability"; "required": false; }; "dense": { "alias": "dense"; "required": false; }; }, { "filterCriteriaChange": "filterCriteriaChange"; "queryContextClear": "queryContextClear"; "queryContextChange": "queryContextChange"; "rowClick": "rowClick"; "widgetEvent": "widgetEvent"; "resourceEvent": "resourceEvent"; "rowDoubleClick": "rowDoubleClick"; "rowExpansionChange": "rowExpansionChange"; "rowAction": "rowAction"; "toolbarAction": "toolbarAction"; "bulkAction": "bulkAction"; "exportAction": "exportAction"; "columnReorder": "columnReorder"; "columnReorderAttempt": "columnReorderAttempt"; "columnResize": "columnResize"; "beforeDelete": "beforeDelete"; "afterDelete": "afterDelete"; "deleteError": "deleteError"; "beforeBulkDelete": "beforeBulkDelete"; "afterBulkDelete": "afterBulkDelete"; "bulkDeleteError": "bulkDeleteError"; "schemaStatusChange": "schemaStatusChange"; "configChange": "configChange"; "metadataChange": "metadataChange"; "loadingStateChange": "loadingStateChange"; "collectionLinksChange": "collectionLinksChange"; "selectionChange": "selectionChange"; "drawerOpen": "drawerOpen"; "drawerClose": "drawerClose"; "drawerTabChange": "drawerTabChange"; }, ["toolbar", "projectedFilter"], ["[advancedFilter]", "[toolbar]", "[advancedFilter]", "[toolbar]", "[advancedFilter]", "[toolbar]"], true, never>;
3069
3082
  static ngAcceptInputType_filterTeleport: unknown;
3070
3083
  static ngAcceptInputType_enableCustomization: unknown;
3071
3084
  }