@praxisui/table 10.0.0-rc.31 → 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-16T17:45:27.774Z",
3
+ "generatedAt": "2026-10-17T14:44:09.509Z",
4
4
  "packageName": "@praxisui/table",
5
- "packageVersion": "10.0.0-rc.31",
5
+ "packageVersion": "10.0.0-rc.32",
6
6
  "sourceRegistry": "praxis-component-registry-ingestion",
7
7
  "sourceRegistryVersion": "1.0.0",
8
8
  "componentCount": 3,
@@ -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
  }
@@ -21041,7 +21048,10 @@ class PraxisTable {
21041
21048
  'audit-warning': 'audit-review',
21042
21049
  'sync-warning': 'sync-pending',
21043
21050
  };
21044
- config = createDefaultTableConfig();
21051
+ static ngAcceptInputType_config;
21052
+ config = { columns: [] };
21053
+ hostInputConfig;
21054
+ persistedUserConfig;
21045
21055
  resourcePath;
21046
21056
  data = null;
21047
21057
  tableId;
@@ -21056,11 +21066,29 @@ class PraxisTable {
21056
21066
  autoOpenSettingsOnOutdated = false;
21057
21067
  crudContext;
21058
21068
  filterCriteria = {};
21059
- 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
+ }
21060
21080
  widgetId;
21061
- 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
+ }
21062
21090
  get filterTemplate() {
21063
- return this.filterBarTemplate;
21091
+ return this.filterTemplateSignal();
21064
21092
  }
21065
21093
  widgetEventBus = inject(PraxisWidgetEventBus, { optional: true });
21066
21094
  queryContext = null;
@@ -23062,6 +23090,40 @@ class PraxisTable {
23062
23090
  }
23063
23091
  catch { }
23064
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
+ }
23065
23127
  hasDeclaredAppearanceDensity(config) {
23066
23128
  const appearance = config?.appearance;
23067
23129
  return !!appearance && Object.prototype.hasOwnProperty.call(appearance, 'density');
@@ -23307,11 +23369,19 @@ class PraxisTable {
23307
23369
  return false;
23308
23370
  if (config.toolbar?.visible)
23309
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;
23310
23376
  if (config.export?.enabled)
23311
23377
  return true;
23312
23378
  if (config.actions?.bulk?.enabled)
23313
23379
  return true;
23314
- 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)
23315
23385
  return true;
23316
23386
  return (config.toolbar?.actions?.length ?? 0) > 0;
23317
23387
  }
@@ -23965,7 +24035,7 @@ class PraxisTable {
23965
24035
  if (this.aiAdapter || this.aiAdapterLoadStarted)
23966
24036
  return;
23967
24037
  this.aiAdapterLoadStarted = true;
23968
- import('./praxisui-table-table-ai.adapter-CBRNHKvA.mjs')
24038
+ import('./praxisui-table-table-ai.adapter-DqlbvUHe.mjs')
23969
24039
  .then(({ TableAiAdapter }) => {
23970
24040
  if (!this.isAiAssistantEnabled()) {
23971
24041
  this.aiAssistantOpenAfterAdapterLoad = false;
@@ -24737,7 +24807,7 @@ class PraxisTable {
24737
24807
  initializeAiAssistantController() {
24738
24808
  if (!this.aiAdapter || this.aiAssistantController)
24739
24809
  return;
24740
- import('./praxisui-table-table-agentic-authoring-turn-flow-CITekGF7.mjs')
24810
+ import('./praxisui-table-table-agentic-authoring-turn-flow-EvaZNCNd.mjs')
24741
24811
  .then(({ TableAgenticAuthoringTurnFlow }) => {
24742
24812
  if (this.aiAssistantController || !this.aiAdapter)
24743
24813
  return;
@@ -25607,6 +25677,8 @@ class PraxisTable {
25607
25677
  };
25608
25678
  this.queryContextClear.emit(clearEvent);
25609
25679
  this.queryContextChange.emit(this.queryContext);
25680
+ this.filterCriteriaChange.emit({ ...this.getEffectiveEditableFilterCriteria() });
25681
+ this.setShowToolbar(this.shouldExposeToolbar(), true);
25610
25682
  // Host-mediated widgetEvent for composition runtime
25611
25683
  this.widgetEvent.emit({
25612
25684
  ownerWidgetKey: this.componentInstanceId || this.tableId || undefined,
@@ -25665,6 +25737,8 @@ class PraxisTable {
25665
25737
  };
25666
25738
  this.queryContextClear.emit(clearEvent);
25667
25739
  this.queryContextChange.emit(this.queryContext);
25740
+ this.filterCriteriaChange.emit({ ...this.getEffectiveEditableFilterCriteria() });
25741
+ this.setShowToolbar(this.shouldExposeToolbar(), true);
25668
25742
  this.widgetEvent.emit({
25669
25743
  ownerWidgetKey: this.componentInstanceId || this.tableId || undefined,
25670
25744
  sourceComponentId: 'praxis-table',
@@ -25810,8 +25884,11 @@ class PraxisTable {
25810
25884
  ref.saved$.subscribe(apply);
25811
25885
  }
25812
25886
  async ngOnInit() {
25887
+ if (!this.hostInputConfig && this.config) {
25888
+ this.hostInputConfig = this.clonePlainObject(this.config);
25889
+ }
25813
25890
  // Ensure config exists before any awaited operations to avoid template errors
25814
- this.ensureConfigDefaults();
25891
+ this.recomposeEffectiveTableConfig();
25815
25892
  this.enforceUnsupportedFeatureGuards();
25816
25893
  this.syncPaginatorIntl();
25817
25894
  this.setShowToolbar(this.shouldExposeToolbar(), true);
@@ -25827,6 +25904,21 @@ class PraxisTable {
25827
25904
  }
25828
25905
  }));
25829
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 { }
25830
25922
  // Resolve global fallback for schema prefs
25831
25923
  try {
25832
25924
  await this.resolveSchemaPrefs();
@@ -25857,14 +25949,8 @@ class PraxisTable {
25857
25949
  try {
25858
25950
  const storedConfig = await firstValueFrom(this.asyncConfigStorage.loadConfig(cfgKey));
25859
25951
  if (storedConfig) {
25860
- this.hostDeclaredAppearanceDensity =
25861
- this.hostDeclaredAppearanceDensity || this.hasDeclaredAppearanceDensity(storedConfig);
25862
- this.hostDeclaredAppearanceSpacing =
25863
- this.hostDeclaredAppearanceSpacing || this.hasDeclaredAppearanceSection(storedConfig, 'spacing');
25864
- this.hostDeclaredAppearanceTypography =
25865
- this.hostDeclaredAppearanceTypography || this.hasDeclaredAppearanceSection(storedConfig, 'typography');
25866
- this.config = this.mergePersistedTableConfigWithInput(storedConfig, this.config);
25867
- this.ensureConfigDefaults();
25952
+ this.persistedUserConfig = this.clonePlainObject(storedConfig);
25953
+ this.recomposeEffectiveTableConfig();
25868
25954
  this.enforceUnsupportedFeatureGuards();
25869
25955
  this.setupColumns();
25870
25956
  this.cdr.markForCheck();
@@ -25889,8 +25975,8 @@ class PraxisTable {
25889
25975
  }
25890
25976
  }
25891
25977
  catch { }
25892
- // Apply global defaults as fallback only for missing properties
25893
- this.ensureConfigDefaults();
25978
+ // Recompose effective config after GlobalConfig.ready() and persisted user config hydration
25979
+ this.recomposeEffectiveTableConfig();
25894
25980
  this.resetDetailRuntimeForConfig();
25895
25981
  this.syncPaginatorIntl();
25896
25982
  // ngOnInit is async and Angular does not await it before ngAfterContentInit.
@@ -25914,6 +26000,7 @@ class PraxisTable {
25914
26000
  resetPageSizeOverride: true,
25915
26001
  });
25916
26002
  this.applyDataSourceSettings();
26003
+ this.applyAppearanceVariables();
25917
26004
  if (this.isAiAssistantEnabled()) {
25918
26005
  this.ensureAiAdapterLoaded();
25919
26006
  }
@@ -25944,10 +26031,24 @@ class PraxisTable {
25944
26031
  this.localPageSizeRuntimeOverride = false;
25945
26032
  }
25946
26033
  if (changes['config']?.currentValue && this.config) {
25947
- this.hostDeclaredAppearanceDensity = this.hasDeclaredAppearanceDensity(changes['config'].currentValue);
25948
- this.hostDeclaredAppearanceSpacing = this.hasDeclaredAppearanceSection(changes['config'].currentValue, 'spacing');
25949
- 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');
25950
26048
  this.config = this.reconcileTableConfigInputChange(changes['config'].previousValue, changes['config'].currentValue);
26049
+ if (this.persistedUserConfig) {
26050
+ this.persistedUserConfig = this.clonePlainObject(this.config);
26051
+ }
25951
26052
  }
25952
26053
  this.ensureConfigDefaults();
25953
26054
  this.enforceUnsupportedFeatureGuards();
@@ -26087,6 +26188,16 @@ class PraxisTable {
26087
26188
  shouldFetchRemoteDataForQueryChanges(changes) {
26088
26189
  if (!this.isRemoteMode())
26089
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
+ }
26090
26201
  if (this.hasInitialized)
26091
26202
  return true;
26092
26203
  const queryChanges = [changes['filterCriteria'], changes['queryContext']];
@@ -28368,8 +28479,13 @@ class PraxisTable {
28368
28479
  return false;
28369
28480
  if (visibleFieldNames.has(name))
28370
28481
  return false;
28371
- if (name.startsWith('_') || name === 'id' || name === '__v')
28482
+ if (name.startsWith('_')
28483
+ || name === 'id'
28484
+ || name === '__v'
28485
+ || name === 'version'
28486
+ || name === 'resourceVersion') {
28372
28487
  return false;
28488
+ }
28373
28489
  if (this.isSensitiveAiAssistantField(name))
28374
28490
  return false;
28375
28491
  return true;
@@ -32295,6 +32411,7 @@ class PraxisTable {
32295
32411
  this.clearQuickFilterRuntimeState();
32296
32412
  this.applyAdvancedFilterCriteria(editableCriteria, 'advanced-filter-submit');
32297
32413
  this.syncQuickFilterStateFromEffectiveCriteria();
32414
+ this.filterCriteriaChange.emit({ ...this.filterCriteria });
32298
32415
  if (this.widgetEventBus) {
32299
32416
  this.widgetEventBus.publish({
32300
32417
  sourceWidgetId: this.widgetId || this.tableId,
@@ -32311,6 +32428,7 @@ class PraxisTable {
32311
32428
  this.clearQuickFilterRuntimeState();
32312
32429
  this.applyAdvancedFilterCriteria({}, 'advanced-filter-clear');
32313
32430
  this.syncQuickFilterStateFromEffectiveCriteria();
32431
+ this.filterCriteriaChange.emit({});
32314
32432
  if (this.widgetEventBus) {
32315
32433
  this.widgetEventBus.publish({
32316
32434
  sourceWidgetId: this.widgetId || this.tableId,
@@ -32342,6 +32460,7 @@ class PraxisTable {
32342
32460
  this.activeQuickFilterId = id;
32343
32461
  this.activeQuickFilterCriteria = {};
32344
32462
  this.applyAdvancedFilterCriteria({}, 'quick-filter-apply');
32463
+ this.filterCriteriaChange.emit({});
32345
32464
  return;
32346
32465
  }
32347
32466
  const current = normalizePraxisFilterCriteriaRecord(this.filterCriteria);
@@ -32359,12 +32478,14 @@ class PraxisTable {
32359
32478
  this.clearQuickFilterRuntimeState();
32360
32479
  this.applyAdvancedFilterCriteria(cleanedBase, 'quick-filter-clear');
32361
32480
  this.syncQuickFilterStateFromEffectiveCriteria();
32481
+ this.filterCriteriaChange.emit({ ...this.filterCriteria });
32362
32482
  return;
32363
32483
  }
32364
32484
  this.quickFilterBaseCriteria = cleanedBase;
32365
32485
  this.activeQuickFilterId = id;
32366
32486
  this.activeQuickFilterCriteria = { ...cleanedBase, ...patch };
32367
32487
  this.applyAdvancedFilterCriteria(this.activeQuickFilterCriteria, 'quick-filter-apply');
32488
+ this.filterCriteriaChange.emit({ ...this.filterCriteria });
32368
32489
  }
32369
32490
  getConfiguredQuickFilters() {
32370
32491
  const filters = this.config?.toolbar?.filters;
@@ -32507,6 +32628,7 @@ class PraxisTable {
32507
32628
  };
32508
32629
  this.queryContextClear?.emit(clearEvent);
32509
32630
  this.queryContextChange?.emit(this.queryContext);
32631
+ this.filterCriteriaChange.emit({ ...this.getEffectiveEditableFilterCriteria() });
32510
32632
  this.widgetEvent?.emit({
32511
32633
  ownerWidgetKey: this.componentInstanceId || this.tableId || undefined,
32512
32634
  sourceComponentId: 'praxis-table',
@@ -32562,8 +32684,15 @@ class PraxisTable {
32562
32684
  }
32563
32685
  hasEquivalentFilterCriteria(left, right) {
32564
32686
  const canonicalize = (value) => {
32565
- if (Array.isArray(value))
32566
- 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
+ }
32567
32696
  if (!value || typeof value !== 'object')
32568
32697
  return value;
32569
32698
  return Object.keys(value)
@@ -32575,8 +32704,10 @@ class PraxisTable {
32575
32704
  };
32576
32705
  return JSON.stringify(canonicalize(left)) === JSON.stringify(canonicalize(right));
32577
32706
  }
32707
+ lastAppliedFilterCriteria = null;
32578
32708
  applyAdvancedFilterCriteria(criteria, reason = 'external-api') {
32579
32709
  this.setAdvancedFilterCriteria(criteria);
32710
+ this.lastAppliedFilterCriteria = { ...this.filterCriteria };
32580
32711
  this.applyExpansionCollapsePolicy('filterChange');
32581
32712
  this.clearCellAnimationChangeState();
32582
32713
  this.pageIndex = 0;
@@ -33062,6 +33193,7 @@ class PraxisTable {
33062
33193
  const compactConfig = this.toPortableTableConfig(plan.canonicalConfig);
33063
33194
  const cfg = this.attachRuntimeMetadataToEditorConfig(compactConfig, plan.metadata?.attachServerMeta === true && !shouldReloadSchema, plan.persistence?.saveConfig === true);
33064
33195
  if (plan.persistence?.saveConfig) {
33196
+ this.persistedUserConfig = this.clonePlainObject(cfg);
33065
33197
  const cfgKey = this.tableConfigKey();
33066
33198
  if (cfgKey) {
33067
33199
  this.saveConfigWithAck(cfgKey, cfg, 'table-settings-save', {
@@ -33230,8 +33362,27 @@ class PraxisTable {
33230
33362
  onResetPersistenceSettled(false, 'reset-filter-config');
33231
33363
  });
33232
33364
  }
33365
+ this.persistedUserConfig = undefined;
33233
33366
  const defaults = this.tableDefaultsProvider.getDefaults(this.tableId);
33234
- 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);
33235
33386
  if (operationCount === 0) {
33236
33387
  this.showResetSuccessFeedback(successTrigger);
33237
33388
  }
@@ -34805,6 +34956,7 @@ class PraxisTable {
34805
34956
  .subscribe({
34806
34957
  next: () => {
34807
34958
  acknowledged = true;
34959
+ this.persistedUserConfig = this.clonePlainObject(payload);
34808
34960
  if (applyReconciledConfigOnSuccess) {
34809
34961
  this.config = payload;
34810
34962
  this.runColumnResizeLayoutMutation(() => this.setupColumns());
@@ -35104,7 +35256,11 @@ class PraxisTable {
35104
35256
  this.config?.toolbar?.filters?.enabled !== false &&
35105
35257
  (!this.config?.toolbar?.filters?.quickFilters || this.config.toolbar.filters.quickFilters.length === 0)) {
35106
35258
  const toolbar = this.ensureToolbarConfigForRuntime();
35107
- 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
+ };
35108
35264
  toolbar.filters.quickFilters = [...discoveredQuickFilters];
35109
35265
  if (toolbar.filters.enabled === undefined) {
35110
35266
  toolbar.filters.enabled = true;
@@ -38306,8 +38462,56 @@ class PraxisTable {
38306
38462
  }, { dedupeKey: 'praxis-table:expansion:virtualization-fail-closed' });
38307
38463
  return false;
38308
38464
  }
38465
+ if (!this.hasComplementarySchemaExpansionFields()) {
38466
+ return false;
38467
+ }
38309
38468
  return true;
38310
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
+ }
38311
38515
  enforceAppearanceSchemaGuards() {
38312
38516
  const appearance = (this.config?.appearance || {});
38313
38517
  if (!appearance || typeof appearance !== 'object')
@@ -42272,7 +42476,7 @@ class PraxisTable {
42272
42476
  this.removeViewportChangeListeners = null;
42273
42477
  }
42274
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 });
42275
- 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: [
42276
42480
  providePraxisI18nConfig(RESOURCE_DISCOVERY_I18N_CONFIG),
42277
42481
  providePraxisI18nConfig(PRAXIS_TABLE_RUNTIME_I18N_CONFIG),
42278
42482
  MatPaginatorIntl,
@@ -42371,6 +42575,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
42371
42575
  type: Input
42372
42576
  }], filterCriteria: [{
42373
42577
  type: Input
42578
+ }], filterCriteriaChange: [{
42579
+ type: Output
42374
42580
  }], filterTeleport: [{
42375
42581
  type: Input,
42376
42582
  args: [{ transform: booleanAttribute }]
@@ -73685,7 +73891,7 @@ class PraxisTableConfigEditor {
73685
73891
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisTableConfigEditor, isStandalone: true, selector: "praxis-table-config-editor", providers: [
73686
73892
  TableConfigService,
73687
73893
  providePraxisI18nConfig(PRAXIS_TABLE_EDITOR_I18N_CONFIG),
73688
- ], 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"] }] });
73689
73895
  }
73690
73896
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisTableConfigEditor, decorators: [{
73691
73897
  type: Component,
@@ -73713,7 +73919,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
73713
73919
  ], providers: [
73714
73920
  TableConfigService,
73715
73921
  providePraxisI18nConfig(PRAXIS_TABLE_EDITOR_I18N_CONFIG),
73716
- ], 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"] }]
73717
73923
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1.TableConfigService }], propDecorators: { filterSettingsEditor: [{
73718
73924
  type: ViewChild,
73719
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-CPAjJGGd.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-CPAjJGGd.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-CPAjJGGd.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.31",
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.31",
10
- "@praxisui/core": "^10.0.0-rc.31",
11
- "@praxisui/dynamic-fields": "^10.0.0-rc.31",
12
- "@praxisui/dynamic-form": "^10.0.0-rc.31",
13
- "@praxisui/metadata-editor": "^10.0.0-rc.31",
14
- "@praxisui/rich-content": "^10.0.0-rc.31",
15
- "@praxisui/settings-panel": "^10.0.0-rc.31",
16
- "@praxisui/table-rule-builder": "^10.0.0-rc.31",
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.31",
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
  }