@acorex/platform 20.9.6 → 20.9.9

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.
@@ -14,9 +14,9 @@ import { AXFormatModule, AXFormatService } from '@acorex/core/format';
14
14
  import { AXPWorkflowService } from '@acorex/platform/workflow';
15
15
  import { AXPLayoutRendererComponent, AXPLayoutBuilderService } from '@acorex/platform/layout/builder';
16
16
  import * as i1 from '@angular/common';
17
- import { CommonModule, NgTemplateOutlet, isPlatformBrowser, AsyncPipe } from '@angular/common';
17
+ import { CommonModule, NgTemplateOutlet, AsyncPipe, isPlatformBrowser } from '@angular/common';
18
18
  import * as i0 from '@angular/core';
19
- import { input, ChangeDetectionStrategy, ViewEncapsulation, Component, inject, signal, effect, computed, InjectionToken, Injectable, Directive, viewChild, contentChild, ElementRef, output, afterNextRender, model, untracked, DestroyRef, PLATFORM_ID, linkedSignal, ViewChildren, EventEmitter, Output, TemplateRef, contentChildren, Input } from '@angular/core';
19
+ import { input, ChangeDetectionStrategy, ViewEncapsulation, Component, inject, signal, effect, computed, InjectionToken, Injectable, Directive, viewChild, contentChild, ElementRef, output, afterNextRender, model, EventEmitter, forwardRef, ViewChild, Output, Input, untracked, DestroyRef, PLATFORM_ID, linkedSignal, ViewChildren, TemplateRef, contentChildren } from '@angular/core';
20
20
  import { AXAccordionCdkModule } from '@acorex/cdk/accordion';
21
21
  import { AXTagModule } from '@acorex/components/tag';
22
22
  import * as i1$1 from '@acorex/components/avatar';
@@ -51,11 +51,11 @@ import { AXLabelModule } from '@acorex/components/label';
51
51
  import * as i3$3 from '@acorex/platform/layout/widget-core';
52
52
  import { AXPWidgetCoreModule, AXPWidgetRegistryService } from '@acorex/platform/layout/widget-core';
53
53
  import * as i4$1 from '@acorex/components/data-table';
54
- import { AXDataTableModule } from '@acorex/components/data-table';
54
+ import { AXDataTableModule, AXDataTableColumnComponent } from '@acorex/components/data-table';
55
55
  import * as i3$4 from '@acorex/components/dropdown-button';
56
56
  import { AXDropdownButtonModule } from '@acorex/components/dropdown-button';
57
57
  import { AXBasePageComponent } from '@acorex/components/page';
58
- import { setupPopupFooterKeyboardShortcuts, AXPExpressionEvaluatorService, AXPColumnWidthService, AXPKeyboardShortcutRegistry, chordToKbdItemKeys, AXPDeviceService, AXPImageUrlLogoConfig, AXPComponentLogoConfig, AXPIconLogoConfig } from '@acorex/platform/core';
58
+ import { setupPopupFooterKeyboardShortcuts, AXPExpressionEvaluatorService, AXPColumnWidthService, AXPKeyboardShortcutRegistry, AXPDataSourceQueryService, chordToKbdItemKeys, AXPDeviceService, AXPImageUrlLogoConfig, AXPComponentLogoConfig, AXPIconLogoConfig } from '@acorex/platform/core';
59
59
  import { AXPopupService } from '@acorex/components/popup';
60
60
  import { moveItemInArray as moveItemInArray$1, AXDragDirective, AXDragHandleDirective, AXDropListDirective } from '@acorex/cdk/drag-drop';
61
61
  import * as i2$5 from '@acorex/components/select-box';
@@ -69,6 +69,7 @@ import { AXTagBoxModule } from '@acorex/components/tag-box';
69
69
  import { AXCalendarService } from '@acorex/core/date-time';
70
70
  import { AXPFilterOperatorMiddlewareService, AXPSettingsService, ALL_DEFAULT_OPERATORS, AXPFileStorageService, withDefaultMultiLanguageOnWidgetProperty, AXPDefaultMultiLanguageConfigService, resolveSearchableText, AXP_PLATFORM_CONFIG_TOKEN } from '@acorex/platform/common';
71
71
  import { resolveEntityListFilterQueryField, resolveEntityListFilterContextField } from '@acorex/platform/layout/entity-contracts';
72
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
72
73
  import { Subject, filter, BehaviorSubject, timer, takeUntil } from 'rxjs';
73
74
  import * as i1$7 from '@acorex/components/image-editor';
74
75
  import { AXImageEditorContainerComponent, AXImageEditorModule } from '@acorex/components/image-editor';
@@ -2234,6 +2235,215 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
2234
2235
  }]
2235
2236
  }] });
2236
2237
 
2238
+ //#endregion
2239
+ //#region ---- Text Command Column ----
2240
+ /**
2241
+ * Data-table column that renders a field value as a clickable text link and emits a command event on click.
2242
+ */
2243
+ class AXPTextCommandColumnComponent extends AXDataTableColumnComponent {
2244
+ constructor() {
2245
+ super(...arguments);
2246
+ this.caption = '';
2247
+ /** Row field path used to resolve the link label. */
2248
+ this.textField = '';
2249
+ this.sortEnabled = false;
2250
+ this.disabled = false;
2251
+ this.emptyDisplay = '---';
2252
+ this.commandClick = new EventEmitter();
2253
+ this.sortToggle = new EventEmitter();
2254
+ }
2255
+ get loadingEnabled() {
2256
+ return false;
2257
+ }
2258
+ get name() {
2259
+ return `col-${this.textField}`;
2260
+ }
2261
+ get renderFooterTemplate() {
2262
+ return this.footerTemplate ?? this.contentFooterTemplate;
2263
+ }
2264
+ get renderHeaderTemplate() {
2265
+ return this.headerTemplate;
2266
+ }
2267
+ get renderCellTemplate() {
2268
+ return this.cellTemplate;
2269
+ }
2270
+ ngOnInit() {
2271
+ this.syncColumnState();
2272
+ }
2273
+ ngOnChanges() {
2274
+ this.syncColumnState();
2275
+ }
2276
+ resolveLabel(rowData) {
2277
+ const value = get(rowData, this.textField);
2278
+ if (value == null || value === '') {
2279
+ return null;
2280
+ }
2281
+ return String(value);
2282
+ }
2283
+ handleClick(rowData) {
2284
+ if (this.disabled) {
2285
+ return;
2286
+ }
2287
+ this.commandClick.emit({
2288
+ data: rowData,
2289
+ textField: this.textField,
2290
+ });
2291
+ }
2292
+ onHeaderClick(event) {
2293
+ if (!this.sortEnabled) {
2294
+ return;
2295
+ }
2296
+ event.stopPropagation();
2297
+ this.sortToggle.emit(event);
2298
+ }
2299
+ syncColumnState() {
2300
+ this.allowSorting = this.sortEnabled;
2301
+ this.sortOrder = this.headerSortDirection;
2302
+ this.sortIndex = this.headerSortPriority;
2303
+ this.fixed = this.columnFixed;
2304
+ if (this.customWidth) {
2305
+ this.width = this.customWidth;
2306
+ }
2307
+ }
2308
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTextCommandColumnComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
2309
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPTextCommandColumnComponent, isStandalone: true, selector: "axp-text-command-column", inputs: { caption: "caption", textField: "textField", customWidth: "customWidth", columnFixed: "columnFixed", footerTemplate: "footerTemplate", sortEnabled: "sortEnabled", headerSortDirection: "headerSortDirection", headerSortPriority: "headerSortPriority", disabled: "disabled", emptyDisplay: "emptyDisplay" }, outputs: { commandClick: "commandClick", sortToggle: "sortToggle" }, providers: [
2310
+ {
2311
+ provide: AXDataTableColumnComponent,
2312
+ useExisting: forwardRef(() => AXPTextCommandColumnComponent),
2313
+ },
2314
+ ], viewQueries: [{ propertyName: "headerTemplate", first: true, predicate: ["header"], descendants: true, static: true }, { propertyName: "cellTemplate", first: true, predicate: ["cell"], descendants: true, static: true }, { propertyName: "contentFooterTemplate", first: true, predicate: ["footer"], descendants: true, static: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: `
2315
+ <ng-template #header>
2316
+ <div
2317
+ class="axp-widget-column-header ax-w-full ax-flex ax-items-center ax-select-none"
2318
+ [class.axp-widget-column-header--sortable]="sortEnabled"
2319
+ [class.axp-widget-column-header--sort-active]="headerSortDirection === 'asc' || headerSortDirection === 'desc'"
2320
+ [class.ax-gap-1]="!sortEnabled"
2321
+ (click)="onHeaderClick($event)"
2322
+ >
2323
+ <span
2324
+ class="axp-widget-column-header__caption ax-truncate"
2325
+ [class.axp-widget-column-header__caption--with-sort]="sortEnabled"
2326
+ >{{ caption | translate | async }}</span>
2327
+ @if (sortEnabled) {
2328
+ <span class="axp-widget-column-header__sort ax-flex ax-items-center ax-gap-0.5 ax-shrink-0">
2329
+ @if (headerSortPriority !== undefined && headerSortPriority > 1) {
2330
+ <span class="axp-widget-column-header__sort-priority ax-text-xs ax-opacity-70">{{ headerSortPriority }}</span>
2331
+ }
2332
+ <i
2333
+ class="fa-solid fa-arrow-up-long ax-text-neutral-400"
2334
+ [class.ax-text-primary]="headerSortDirection === 'asc'"
2335
+ ></i>
2336
+ <i
2337
+ class="fa-solid fa-arrow-down-long ax-text-neutral-400"
2338
+ [class.ax-text-primary]="headerSortDirection === 'desc'"
2339
+ ></i>
2340
+ </span>
2341
+ }
2342
+ </div>
2343
+ </ng-template>
2344
+ <ng-template #cell let-row>
2345
+ @if (resolveLabel(row.data); as label) {
2346
+ <span
2347
+ class="ax-text-primary ax-cursor-pointer hover:ax-underline"
2348
+ [class.ax-opacity-50]="disabled"
2349
+ [class.ax-pointer-events-none]="disabled"
2350
+ (click)="handleClick(row.data)"
2351
+ >{{ label | translate | async }}</span>
2352
+ } @else {
2353
+ <span class="ax-text-muted">{{ emptyDisplay }}</span>
2354
+ }
2355
+ </ng-template>
2356
+ <ng-template #footer></ng-template>
2357
+ `, isInline: true, styles: [".axp-widget-column-header--sortable{cursor:pointer}.axp-widget-column-header__caption{min-width:0}.axp-widget-column-header__caption--with-sort{flex:1 1 0}.axp-widget-column-header__sort{opacity:0;transition:opacity .15s ease}.axp-widget-column-header--sortable:hover .axp-widget-column-header__sort,.axp-widget-column-header--sortable:focus-within .axp-widget-column-header__sort,.axp-widget-column-header--sort-active .axp-widget-column-header__sort{opacity:1}.axp-widget-column-header__sort-priority{line-height:1}:host-context(.ax-data-table-head-cell.ax-interactive:hover) .axp-widget-column-header__sort,:host-context(.ax-data-table-head-cell.ax-interactive:focus-within) .axp-widget-column-header__sort,:host-context(.ax-data-table-head-cell) .axp-widget-column-header--sort-active .axp-widget-column-header__sort{opacity:1}\n"], dependencies: [{ kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i3.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2358
+ }
2359
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTextCommandColumnComponent, decorators: [{
2360
+ type: Component,
2361
+ args: [{ selector: 'axp-text-command-column', template: `
2362
+ <ng-template #header>
2363
+ <div
2364
+ class="axp-widget-column-header ax-w-full ax-flex ax-items-center ax-select-none"
2365
+ [class.axp-widget-column-header--sortable]="sortEnabled"
2366
+ [class.axp-widget-column-header--sort-active]="headerSortDirection === 'asc' || headerSortDirection === 'desc'"
2367
+ [class.ax-gap-1]="!sortEnabled"
2368
+ (click)="onHeaderClick($event)"
2369
+ >
2370
+ <span
2371
+ class="axp-widget-column-header__caption ax-truncate"
2372
+ [class.axp-widget-column-header__caption--with-sort]="sortEnabled"
2373
+ >{{ caption | translate | async }}</span>
2374
+ @if (sortEnabled) {
2375
+ <span class="axp-widget-column-header__sort ax-flex ax-items-center ax-gap-0.5 ax-shrink-0">
2376
+ @if (headerSortPriority !== undefined && headerSortPriority > 1) {
2377
+ <span class="axp-widget-column-header__sort-priority ax-text-xs ax-opacity-70">{{ headerSortPriority }}</span>
2378
+ }
2379
+ <i
2380
+ class="fa-solid fa-arrow-up-long ax-text-neutral-400"
2381
+ [class.ax-text-primary]="headerSortDirection === 'asc'"
2382
+ ></i>
2383
+ <i
2384
+ class="fa-solid fa-arrow-down-long ax-text-neutral-400"
2385
+ [class.ax-text-primary]="headerSortDirection === 'desc'"
2386
+ ></i>
2387
+ </span>
2388
+ }
2389
+ </div>
2390
+ </ng-template>
2391
+ <ng-template #cell let-row>
2392
+ @if (resolveLabel(row.data); as label) {
2393
+ <span
2394
+ class="ax-text-primary ax-cursor-pointer hover:ax-underline"
2395
+ [class.ax-opacity-50]="disabled"
2396
+ [class.ax-pointer-events-none]="disabled"
2397
+ (click)="handleClick(row.data)"
2398
+ >{{ label | translate | async }}</span>
2399
+ } @else {
2400
+ <span class="ax-text-muted">{{ emptyDisplay }}</span>
2401
+ }
2402
+ </ng-template>
2403
+ <ng-template #footer></ng-template>
2404
+ `, changeDetection: ChangeDetectionStrategy.OnPush, imports: [AXTranslationModule, AsyncPipe], providers: [
2405
+ {
2406
+ provide: AXDataTableColumnComponent,
2407
+ useExisting: forwardRef(() => AXPTextCommandColumnComponent),
2408
+ },
2409
+ ], styles: [".axp-widget-column-header--sortable{cursor:pointer}.axp-widget-column-header__caption{min-width:0}.axp-widget-column-header__caption--with-sort{flex:1 1 0}.axp-widget-column-header__sort{opacity:0;transition:opacity .15s ease}.axp-widget-column-header--sortable:hover .axp-widget-column-header__sort,.axp-widget-column-header--sortable:focus-within .axp-widget-column-header__sort,.axp-widget-column-header--sort-active .axp-widget-column-header__sort{opacity:1}.axp-widget-column-header__sort-priority{line-height:1}:host-context(.ax-data-table-head-cell.ax-interactive:hover) .axp-widget-column-header__sort,:host-context(.ax-data-table-head-cell.ax-interactive:focus-within) .axp-widget-column-header__sort,:host-context(.ax-data-table-head-cell) .axp-widget-column-header--sort-active .axp-widget-column-header__sort{opacity:1}\n"] }]
2410
+ }], propDecorators: { caption: [{
2411
+ type: Input,
2412
+ args: [{ required: true }]
2413
+ }], textField: [{
2414
+ type: Input,
2415
+ args: [{ required: true }]
2416
+ }], customWidth: [{
2417
+ type: Input
2418
+ }], columnFixed: [{
2419
+ type: Input
2420
+ }], footerTemplate: [{
2421
+ type: Input
2422
+ }], sortEnabled: [{
2423
+ type: Input
2424
+ }], headerSortDirection: [{
2425
+ type: Input
2426
+ }], headerSortPriority: [{
2427
+ type: Input
2428
+ }], disabled: [{
2429
+ type: Input
2430
+ }], emptyDisplay: [{
2431
+ type: Input
2432
+ }], commandClick: [{
2433
+ type: Output
2434
+ }], sortToggle: [{
2435
+ type: Output
2436
+ }], headerTemplate: [{
2437
+ type: ViewChild,
2438
+ args: ['header', { static: true }]
2439
+ }], cellTemplate: [{
2440
+ type: ViewChild,
2441
+ args: ['cell', { static: true }]
2442
+ }], contentFooterTemplate: [{
2443
+ type: ViewChild,
2444
+ args: ['footer', { static: true }]
2445
+ }] } });
2446
+
2237
2447
  class AXPDragDropListComponent {
2238
2448
  constructor() {
2239
2449
  // Inputs
@@ -2887,6 +3097,7 @@ class AXPQueryFiltersComponent {
2887
3097
  this.shortcutRegistry = inject(AXPKeyboardShortcutRegistry);
2888
3098
  this.destroyRef = inject(DestroyRef);
2889
3099
  this.elementRef = inject((ElementRef));
3100
+ this.dataSourceQueryService = inject(AXPDataSourceQueryService);
2890
3101
  this.filterFocusShortcutKeys = chordToKbdItemKeys('ctrl+f');
2891
3102
  this.filtersDefinitions = input([], ...(ngDevMode ? [{ debugName: "filtersDefinitions" }] : /* istanbul ignore next */ []));
2892
3103
  this.initialFilters = input([], ...(ngDevMode ? [{ debugName: "initialFilters" }] : /* istanbul ignore next */ []));
@@ -2906,6 +3117,10 @@ class AXPQueryFiltersComponent {
2906
3117
  this.filterContextPath$ = new Subject();
2907
3118
  this.filterContextPathSnapshotReady = false;
2908
3119
  this.previousFilterContextSnapshot = {};
3120
+ /** Committed context entry for the filter being edited; restored when popover closes without Apply. */
3121
+ this.editingContextSnapshot = null;
3122
+ /** Bumped on locale change so filter chip labels re-resolve i18n keys. */
3123
+ this.translationEpoch = signal(0, ...(ngDevMode ? [{ debugName: "translationEpoch" }] : /* istanbul ignore next */ []));
2909
3124
  this.inlineFilters = () => {
2910
3125
  return this.filtersDefinitions().filter((f) => f.filterType.inline &&
2911
3126
  !this.filterOperatorMiddleware
@@ -2931,9 +3146,22 @@ class AXPQueryFiltersComponent {
2931
3146
  }
2932
3147
  }
2933
3148
  if (filter.widget?.type === 'select-editor' || filter.widget?.type === 'select-filter') {
2934
- const dataSource = filter.widget.options?.['dataSource'];
2935
- const textField = filter.widget.options?.['textField'];
2936
- const valueField = filter.widget.options?.['valueField'];
3149
+ const rawDataSource = filter.widget.options?.['dataSource'];
3150
+ const textField = filter.widget.options?.['textField'] ?? 'title';
3151
+ const valueField = filter.widget.options?.['valueField'] ?? 'id';
3152
+ if (typeof rawDataSource === 'string') {
3153
+ const found = await this.dataSourceQueryService.queryByKey(rawDataSource, val, {
3154
+ valueField,
3155
+ });
3156
+ if (Array.isArray(found)) {
3157
+ return found.map((data) => String(get(data, textField) ?? '')).join(', ');
3158
+ }
3159
+ if (found) {
3160
+ return String(get(found, textField) ?? val);
3161
+ }
3162
+ return String(val ?? '');
3163
+ }
3164
+ const dataSource = rawDataSource;
2937
3165
  if (dataSource?.config?.byKey) {
2938
3166
  if (Array.isArray(val)) {
2939
3167
  const results = await Promise.all(val.map((v) => dataSource.config.byKey(v)));
@@ -3076,17 +3304,23 @@ class AXPQueryFiltersComponent {
3076
3304
  this.#updateTagsEffect = effect(() => {
3077
3305
  const filters = this.selectedFilters();
3078
3306
  const context = this.context();
3307
+ const editingField = this.activeFilter()?.field;
3308
+ void this.translationEpoch();
3079
3309
  Promise.all(filters.map(async (filter) => {
3080
- const contextField = this.resolveFilterContextField(filter);
3081
- const val = context[contextField]?.value;
3082
- const displayText = context[contextField]?.displayText;
3310
+ const committedEntry = this.getCommittedFilterEntry(filter, context, editingField);
3311
+ const val = committedEntry?.value ?? filter.value;
3312
+ const displayText = committedEntry?.displayText;
3083
3313
  const rawDisplay = displayText != null && displayText !== '' ? displayText : await this.getDisplayValue(filter, val);
3084
- const displayValue = this.formatFilterDisplayValue(rawDisplay);
3314
+ const displayValue = await this.formatFilterDisplayValue(rawDisplay);
3315
+ const operatorType = committedEntry?.operation?.type ?? filter.operator?.type;
3316
+ const operatorKey = ALL_DEFAULT_OPERATORS.find((o) => o.name === operatorType)?.title;
3317
+ const title = await this.translateLabel(filter.title);
3318
+ const operator = operatorKey ? await this.translateLabel(operatorKey) : '';
3085
3319
  return {
3086
3320
  ...filter,
3087
3321
  readOnly: filter.readOnly === true ||
3088
3322
  filter.widget?.options?.['readonly'] === true,
3089
- query: `${this.translate.translateSync(filter.title)} ${this.translate.translateSync(this.getActiveOperator(filter)) || ''} '${displayValue}'`,
3323
+ query: `${title} ${operator} '${displayValue}'`.trim(),
3090
3324
  };
3091
3325
  })).then((results) => {
3092
3326
  this.asyncTags.set(results.filter((r) => !r.hidden));
@@ -3217,6 +3451,9 @@ class AXPQueryFiltersComponent {
3217
3451
  },
3218
3452
  ],
3219
3453
  });
3454
+ this.translate.langChanges$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
3455
+ this.translationEpoch.update((epoch) => epoch + 1);
3456
+ });
3220
3457
  }
3221
3458
  convertQueriesToDefinitions(queries) {
3222
3459
  const definitions = this.filtersDefinitions();
@@ -3262,6 +3499,7 @@ class AXPQueryFiltersComponent {
3262
3499
  if (this.isFilterDisabled(field)) {
3263
3500
  return;
3264
3501
  }
3502
+ this.captureEditingContextSnapshot(field);
3265
3503
  this.activeFilter.set(field);
3266
3504
  this.tagBox()?.inputValue.set('');
3267
3505
  }
@@ -3298,13 +3536,19 @@ class AXPQueryFiltersComponent {
3298
3536
  const contextField = this.resolveFilterContextField(reservedFitler);
3299
3537
  const selectedFiltersFields = this.selectedFilters().map((f) => f.field);
3300
3538
  const context = this.context();
3301
- const appliedValue = context[contextField]?.value ?? context[contextField];
3539
+ const appliedEntry = context[contextField];
3540
+ const appliedValue = appliedEntry?.value ?? appliedEntry;
3541
+ const appliedOperator = appliedEntry?.operation ?? reservedFitler.operator;
3302
3542
  if (selectedFiltersFields.includes(field)) {
3303
- this.selectedFilters.update((prev) => prev.map((f) => (f.field === field ? { ...f, value: appliedValue } : f)));
3543
+ this.selectedFilters.update((prev) => prev.map((f) => f.field === field ? { ...f, value: appliedValue, operator: appliedOperator } : f));
3304
3544
  }
3305
3545
  else {
3306
- this.selectedFilters.update((prev) => [...prev, { ...reservedFitler, value: appliedValue }]);
3546
+ this.selectedFilters.update((prev) => [
3547
+ ...prev,
3548
+ { ...reservedFitler, value: appliedValue, operator: appliedOperator },
3549
+ ]);
3307
3550
  }
3551
+ this.editingContextSnapshot = null;
3308
3552
  this.activeFilter.set(null);
3309
3553
  this.popover()?.close();
3310
3554
  }
@@ -3349,6 +3593,7 @@ class AXPQueryFiltersComponent {
3349
3593
  if (item.field === 'all') {
3350
3594
  return;
3351
3595
  }
3596
+ this.captureEditingContextSnapshot(item);
3352
3597
  this.activeFilter.set(item);
3353
3598
  this.popover()?.open();
3354
3599
  }
@@ -3437,6 +3682,7 @@ class AXPQueryFiltersComponent {
3437
3682
  }
3438
3683
  }
3439
3684
  handlePopoverClosed(e) {
3685
+ this.restoreEditingContextSnapshot();
3440
3686
  this.activeFilter.set(null);
3441
3687
  }
3442
3688
  onPopoverOpened(e) {
@@ -3468,21 +3714,60 @@ class AXPQueryFiltersComponent {
3468
3714
  /**
3469
3715
  * Resolves filter chip labels; translates i18n keys stored in select `displayText` or datasource titles.
3470
3716
  */
3471
- formatFilterDisplayValue(value) {
3717
+ async formatFilterDisplayValue(value) {
3472
3718
  if (value == null || value === '') {
3473
3719
  return '';
3474
3720
  }
3475
3721
  const parts = Array.isArray(value) ? value : [value];
3476
- return parts
3477
- .map((part) => {
3722
+ const labels = await Promise.all(parts.map(async (part) => {
3478
3723
  const text = String(part ?? '').trim();
3479
3724
  if (!text) {
3480
3725
  return '';
3481
3726
  }
3482
- return text.startsWith('@') ? this.translate.translateSync(text) || text : text;
3483
- })
3484
- .filter(Boolean)
3485
- .join(', ');
3727
+ return this.translateLabel(text);
3728
+ }));
3729
+ return labels.filter(Boolean).join(', ');
3730
+ }
3731
+ async translateLabel(text) {
3732
+ const trimmed = text.trim();
3733
+ if (!trimmed) {
3734
+ return '';
3735
+ }
3736
+ if (trimmed.startsWith('@')) {
3737
+ return (await this.translate.translateAsync(trimmed)) || trimmed;
3738
+ }
3739
+ return trimmed;
3740
+ }
3741
+ captureEditingContextSnapshot(filter) {
3742
+ const contextField = this.resolveFilterContextField(filter);
3743
+ const context = this.context();
3744
+ this.editingContextSnapshot = {
3745
+ contextField,
3746
+ entry: cloneDeep(context[contextField]),
3747
+ };
3748
+ }
3749
+ restoreEditingContextSnapshot() {
3750
+ const snapshot = this.editingContextSnapshot;
3751
+ if (!snapshot) {
3752
+ return;
3753
+ }
3754
+ const { contextField, entry } = snapshot;
3755
+ this.context.update((prev) => ({
3756
+ ...prev,
3757
+ [contextField]: cloneDeep(entry),
3758
+ }));
3759
+ this.editingContextSnapshot = null;
3760
+ }
3761
+ /**
3762
+ * Returns the committed filter context entry (snapshot while editing, live context otherwise).
3763
+ */
3764
+ getCommittedFilterEntry(filter, context, editingField) {
3765
+ const contextField = this.resolveFilterContextField(filter);
3766
+ const snapshot = this.editingContextSnapshot;
3767
+ if (editingField === filter.field && snapshot?.contextField === contextField) {
3768
+ return snapshot.entry;
3769
+ }
3770
+ return context[contextField];
3486
3771
  }
3487
3772
  createExpressionScope(filtersContext) {
3488
3773
  const base = (filtersContext ?? {});
@@ -9721,5 +10006,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
9721
10006
  * Generated bundle index. Do not edit.
9722
10007
  */
9723
10008
 
9724
- export { AXPActivityLogComponent, AXPAvatarComponent, AXPCategoryTreeComponent, AXPColorPalettePickerComponent, AXPColumnItemListComponent, AXPCompareViewComponent, AXPConditionBuilderComponent, AXPConditionBuilderConditionComponent, AXPDataSelectorComponent, AXPDataSelectorService, AXPDragDropListComponent, AXPExpressionFieldDefinitions, AXPImageEditorPopupComponent, AXPImageEditorService, AXPItemConfiguratorComponent, AXPLayoutFloatingZoomBarComponent, AXPLayoutSideDetailPanelComponent, AXPLayoutSideDetailPanelSectionDirective, AXPLogoComponent, AXPMarkdownTemplateDirective, AXPMarkdownViewerComponent, AXPMenuBadgeHelper, AXPMenuCustomizerComponent, AXPMenuCustomizerService, AXPOutcomeResultsViewerComponent, AXPPageComponentRegistryService, AXPPreloadFiltersComponent, AXPPropertyViewerComponent, AXPPropertyViewerPopupComponent, AXPPropertyViewerService, AXPQueryColumnsComponent, AXPQueryFiltersComponent, AXPQuerySortsComponent, AXPQueryViewsComponent, AXPRepeaterRowsLayoutComponent, AXPResourceAppointmentBoardProvider, AXPResourceAppointmentComponent, AXPResourceAppointmentService, AXPSectionItemsBuilderComponent, AXPSignatureEditorService, AXPSpreadsheetComponent, AXPStandardSectionItemsBuilderComponent, AXPStateMessageComponent, AXPStopwatchComponent, AXPTableColumnsEditorComponent, AXPTableColumnsEditorPopupComponent, AXPTableColumnsEditorService, AXPTableDataEditorComponent, AXPTableDataEditorPopupComponent, AXPTableDataEditorService, AXPTaskBadgeDirective, AXPTaskBadgeProvider, AXPTaskBadgeService, AXPTemplateViewerComponent, AXPTemplateViewerService, AXPThemeLayoutActionsComponent, AXPThemeLayoutBlockComponent, AXPThemeLayoutContainerComponent, AXPThemeLayoutEndSideComponent, AXPThemeLayoutFooterComponent, AXPThemeLayoutHeaderComponent, AXPThemeLayoutListComponent, AXPThemeLayoutListItemComponent, AXPThemeLayoutListItemsGroupComponent, AXPThemeLayoutPageHeaderComponent, AXPThemeLayoutPagePrimaryActionsComponent, AXPThemeLayoutPageSecondaryActionsComponent, AXPThemeLayoutSectionComponent, AXPThemeLayoutStartSideComponent, AXPThemeLayoutToolbarComponent, AXPUserAvatarComponent, AXPUserAvatarService, AXPVirtualKeypadComponent, AXPWidgetFieldConfiguratorComponent, AXPWidgetItemComponent, AXPWidgetPropertyViewerComponent, AXPWidgetPropertyViewerPopupComponent, AXPWidgetPropertyViewerService, AXP_EXPRESSION_LOGIC_DEFINITIONS, AXP_EXPRESSION_OPERATION_DEFINITIONS, AXP_MENU_CUSTOMIZER_SERVICE, AXP_PAGE_COMPONENT_PROVIDER, AXP_RESOURCE_APPOINTMENT_PROVIDER, AXP_TASK_BADGE_PROVIDERS, AXP_USER_AVATAR_PROVIDER, STANDARD_SECTION_ITEMS_SECTION_TABS, buildPropertyViewerInitialContextFromProperties, buildPropertyViewerTabsFromProperties, buildTableColumnsEditorLayout, getFieldDefinitions, getLogicDefinition, getOperationDefinition, isPropertyBindingExpressionFormValue, preparePropertyForViewer, preparePropertyViewerTabs, registerFieldDefinitions, withValidationsOnEditorNode };
10009
+ export { AXPActivityLogComponent, AXPAvatarComponent, AXPCategoryTreeComponent, AXPColorPalettePickerComponent, AXPColumnItemListComponent, AXPCompareViewComponent, AXPConditionBuilderComponent, AXPConditionBuilderConditionComponent, AXPDataSelectorComponent, AXPDataSelectorService, AXPDragDropListComponent, AXPExpressionFieldDefinitions, AXPImageEditorPopupComponent, AXPImageEditorService, AXPItemConfiguratorComponent, AXPLayoutFloatingZoomBarComponent, AXPLayoutSideDetailPanelComponent, AXPLayoutSideDetailPanelSectionDirective, AXPLogoComponent, AXPMarkdownTemplateDirective, AXPMarkdownViewerComponent, AXPMenuBadgeHelper, AXPMenuCustomizerComponent, AXPMenuCustomizerService, AXPOutcomeResultsViewerComponent, AXPPageComponentRegistryService, AXPPreloadFiltersComponent, AXPPropertyViewerComponent, AXPPropertyViewerPopupComponent, AXPPropertyViewerService, AXPQueryColumnsComponent, AXPQueryFiltersComponent, AXPQuerySortsComponent, AXPQueryViewsComponent, AXPRepeaterRowsLayoutComponent, AXPResourceAppointmentBoardProvider, AXPResourceAppointmentComponent, AXPResourceAppointmentService, AXPSectionItemsBuilderComponent, AXPSignatureEditorService, AXPSpreadsheetComponent, AXPStandardSectionItemsBuilderComponent, AXPStateMessageComponent, AXPStopwatchComponent, AXPTableColumnsEditorComponent, AXPTableColumnsEditorPopupComponent, AXPTableColumnsEditorService, AXPTableDataEditorComponent, AXPTableDataEditorPopupComponent, AXPTableDataEditorService, AXPTaskBadgeDirective, AXPTaskBadgeProvider, AXPTaskBadgeService, AXPTemplateViewerComponent, AXPTemplateViewerService, AXPTextCommandColumnComponent, AXPThemeLayoutActionsComponent, AXPThemeLayoutBlockComponent, AXPThemeLayoutContainerComponent, AXPThemeLayoutEndSideComponent, AXPThemeLayoutFooterComponent, AXPThemeLayoutHeaderComponent, AXPThemeLayoutListComponent, AXPThemeLayoutListItemComponent, AXPThemeLayoutListItemsGroupComponent, AXPThemeLayoutPageHeaderComponent, AXPThemeLayoutPagePrimaryActionsComponent, AXPThemeLayoutPageSecondaryActionsComponent, AXPThemeLayoutSectionComponent, AXPThemeLayoutStartSideComponent, AXPThemeLayoutToolbarComponent, AXPUserAvatarComponent, AXPUserAvatarService, AXPVirtualKeypadComponent, AXPWidgetFieldConfiguratorComponent, AXPWidgetItemComponent, AXPWidgetPropertyViewerComponent, AXPWidgetPropertyViewerPopupComponent, AXPWidgetPropertyViewerService, AXP_EXPRESSION_LOGIC_DEFINITIONS, AXP_EXPRESSION_OPERATION_DEFINITIONS, AXP_MENU_CUSTOMIZER_SERVICE, AXP_PAGE_COMPONENT_PROVIDER, AXP_RESOURCE_APPOINTMENT_PROVIDER, AXP_TASK_BADGE_PROVIDERS, AXP_USER_AVATAR_PROVIDER, STANDARD_SECTION_ITEMS_SECTION_TABS, buildPropertyViewerInitialContextFromProperties, buildPropertyViewerTabsFromProperties, buildTableColumnsEditorLayout, getFieldDefinitions, getLogicDefinition, getOperationDefinition, isPropertyBindingExpressionFormValue, preparePropertyForViewer, preparePropertyViewerTabs, registerFieldDefinitions, withValidationsOnEditorNode };
9725
10010
  //# sourceMappingURL=acorex-platform-layout-components.mjs.map