@acorex/platform 20.9.6 → 20.9.8
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.
- package/fesm2022/acorex-platform-core.mjs +244 -4
- package/fesm2022/acorex-platform-core.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-components.mjs +96 -19
- package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-entity-contracts.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-entity.mjs +3 -3
- package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-widget-core.mjs +82 -43
- package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-widgets.mjs +168 -80
- package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
- package/package.json +1 -1
- package/types/acorex-platform-core.d.ts +68 -2
- package/types/acorex-platform-layout-components.d.ts +12 -0
- package/types/acorex-platform-layout-entity-contracts.d.ts +1 -1
- package/types/acorex-platform-layout-entity.d.ts +1 -1
- package/types/acorex-platform-layout-widget-core.d.ts +11 -4
- package/types/acorex-platform-layout-widgets.d.ts +20 -0
|
@@ -55,7 +55,7 @@ import { AXDataTableModule } 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';
|
|
@@ -2887,6 +2888,7 @@ class AXPQueryFiltersComponent {
|
|
|
2887
2888
|
this.shortcutRegistry = inject(AXPKeyboardShortcutRegistry);
|
|
2888
2889
|
this.destroyRef = inject(DestroyRef);
|
|
2889
2890
|
this.elementRef = inject((ElementRef));
|
|
2891
|
+
this.dataSourceQueryService = inject(AXPDataSourceQueryService);
|
|
2890
2892
|
this.filterFocusShortcutKeys = chordToKbdItemKeys('ctrl+f');
|
|
2891
2893
|
this.filtersDefinitions = input([], ...(ngDevMode ? [{ debugName: "filtersDefinitions" }] : /* istanbul ignore next */ []));
|
|
2892
2894
|
this.initialFilters = input([], ...(ngDevMode ? [{ debugName: "initialFilters" }] : /* istanbul ignore next */ []));
|
|
@@ -2906,6 +2908,10 @@ class AXPQueryFiltersComponent {
|
|
|
2906
2908
|
this.filterContextPath$ = new Subject();
|
|
2907
2909
|
this.filterContextPathSnapshotReady = false;
|
|
2908
2910
|
this.previousFilterContextSnapshot = {};
|
|
2911
|
+
/** Committed context entry for the filter being edited; restored when popover closes without Apply. */
|
|
2912
|
+
this.editingContextSnapshot = null;
|
|
2913
|
+
/** Bumped on locale change so filter chip labels re-resolve i18n keys. */
|
|
2914
|
+
this.translationEpoch = signal(0, ...(ngDevMode ? [{ debugName: "translationEpoch" }] : /* istanbul ignore next */ []));
|
|
2909
2915
|
this.inlineFilters = () => {
|
|
2910
2916
|
return this.filtersDefinitions().filter((f) => f.filterType.inline &&
|
|
2911
2917
|
!this.filterOperatorMiddleware
|
|
@@ -2931,9 +2937,22 @@ class AXPQueryFiltersComponent {
|
|
|
2931
2937
|
}
|
|
2932
2938
|
}
|
|
2933
2939
|
if (filter.widget?.type === 'select-editor' || filter.widget?.type === 'select-filter') {
|
|
2934
|
-
const
|
|
2935
|
-
const textField = filter.widget.options?.['textField'];
|
|
2936
|
-
const valueField = filter.widget.options?.['valueField'];
|
|
2940
|
+
const rawDataSource = filter.widget.options?.['dataSource'];
|
|
2941
|
+
const textField = filter.widget.options?.['textField'] ?? 'title';
|
|
2942
|
+
const valueField = filter.widget.options?.['valueField'] ?? 'id';
|
|
2943
|
+
if (typeof rawDataSource === 'string') {
|
|
2944
|
+
const found = await this.dataSourceQueryService.queryByKey(rawDataSource, val, {
|
|
2945
|
+
valueField,
|
|
2946
|
+
});
|
|
2947
|
+
if (Array.isArray(found)) {
|
|
2948
|
+
return found.map((data) => String(get(data, textField) ?? '')).join(', ');
|
|
2949
|
+
}
|
|
2950
|
+
if (found) {
|
|
2951
|
+
return String(get(found, textField) ?? val);
|
|
2952
|
+
}
|
|
2953
|
+
return String(val ?? '');
|
|
2954
|
+
}
|
|
2955
|
+
const dataSource = rawDataSource;
|
|
2937
2956
|
if (dataSource?.config?.byKey) {
|
|
2938
2957
|
if (Array.isArray(val)) {
|
|
2939
2958
|
const results = await Promise.all(val.map((v) => dataSource.config.byKey(v)));
|
|
@@ -3076,17 +3095,24 @@ class AXPQueryFiltersComponent {
|
|
|
3076
3095
|
this.#updateTagsEffect = effect(() => {
|
|
3077
3096
|
const filters = this.selectedFilters();
|
|
3078
3097
|
const context = this.context();
|
|
3098
|
+
const editingField = this.activeFilter()?.field;
|
|
3099
|
+
void this.translationEpoch();
|
|
3079
3100
|
Promise.all(filters.map(async (filter) => {
|
|
3080
|
-
const
|
|
3081
|
-
const
|
|
3082
|
-
const
|
|
3101
|
+
const isEditingThisFilter = editingField === filter.field;
|
|
3102
|
+
const committedEntry = this.getCommittedFilterEntry(filter, context, editingField);
|
|
3103
|
+
const val = committedEntry?.value ?? filter.value;
|
|
3104
|
+
const displayText = isEditingThisFilter ? undefined : committedEntry?.displayText;
|
|
3083
3105
|
const rawDisplay = displayText != null && displayText !== '' ? displayText : await this.getDisplayValue(filter, val);
|
|
3084
|
-
const displayValue = this.formatFilterDisplayValue(rawDisplay);
|
|
3106
|
+
const displayValue = await this.formatFilterDisplayValue(rawDisplay);
|
|
3107
|
+
const operatorType = committedEntry?.operation?.type ?? filter.operator?.type;
|
|
3108
|
+
const operatorKey = ALL_DEFAULT_OPERATORS.find((o) => o.name === operatorType)?.title;
|
|
3109
|
+
const title = await this.translateLabel(filter.title);
|
|
3110
|
+
const operator = operatorKey ? await this.translateLabel(operatorKey) : '';
|
|
3085
3111
|
return {
|
|
3086
3112
|
...filter,
|
|
3087
3113
|
readOnly: filter.readOnly === true ||
|
|
3088
3114
|
filter.widget?.options?.['readonly'] === true,
|
|
3089
|
-
query: `${
|
|
3115
|
+
query: `${title} ${operator} '${displayValue}'`.trim(),
|
|
3090
3116
|
};
|
|
3091
3117
|
})).then((results) => {
|
|
3092
3118
|
this.asyncTags.set(results.filter((r) => !r.hidden));
|
|
@@ -3217,6 +3243,9 @@ class AXPQueryFiltersComponent {
|
|
|
3217
3243
|
},
|
|
3218
3244
|
],
|
|
3219
3245
|
});
|
|
3246
|
+
this.translate.langChanges$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
|
|
3247
|
+
this.translationEpoch.update((epoch) => epoch + 1);
|
|
3248
|
+
});
|
|
3220
3249
|
}
|
|
3221
3250
|
convertQueriesToDefinitions(queries) {
|
|
3222
3251
|
const definitions = this.filtersDefinitions();
|
|
@@ -3262,6 +3291,7 @@ class AXPQueryFiltersComponent {
|
|
|
3262
3291
|
if (this.isFilterDisabled(field)) {
|
|
3263
3292
|
return;
|
|
3264
3293
|
}
|
|
3294
|
+
this.captureEditingContextSnapshot(field);
|
|
3265
3295
|
this.activeFilter.set(field);
|
|
3266
3296
|
this.tagBox()?.inputValue.set('');
|
|
3267
3297
|
}
|
|
@@ -3298,13 +3328,19 @@ class AXPQueryFiltersComponent {
|
|
|
3298
3328
|
const contextField = this.resolveFilterContextField(reservedFitler);
|
|
3299
3329
|
const selectedFiltersFields = this.selectedFilters().map((f) => f.field);
|
|
3300
3330
|
const context = this.context();
|
|
3301
|
-
const
|
|
3331
|
+
const appliedEntry = context[contextField];
|
|
3332
|
+
const appliedValue = appliedEntry?.value ?? appliedEntry;
|
|
3333
|
+
const appliedOperator = appliedEntry?.operation ?? reservedFitler.operator;
|
|
3302
3334
|
if (selectedFiltersFields.includes(field)) {
|
|
3303
|
-
this.selectedFilters.update((prev) => prev.map((f) =>
|
|
3335
|
+
this.selectedFilters.update((prev) => prev.map((f) => f.field === field ? { ...f, value: appliedValue, operator: appliedOperator } : f));
|
|
3304
3336
|
}
|
|
3305
3337
|
else {
|
|
3306
|
-
this.selectedFilters.update((prev) => [
|
|
3338
|
+
this.selectedFilters.update((prev) => [
|
|
3339
|
+
...prev,
|
|
3340
|
+
{ ...reservedFitler, value: appliedValue, operator: appliedOperator },
|
|
3341
|
+
]);
|
|
3307
3342
|
}
|
|
3343
|
+
this.editingContextSnapshot = null;
|
|
3308
3344
|
this.activeFilter.set(null);
|
|
3309
3345
|
this.popover()?.close();
|
|
3310
3346
|
}
|
|
@@ -3349,6 +3385,7 @@ class AXPQueryFiltersComponent {
|
|
|
3349
3385
|
if (item.field === 'all') {
|
|
3350
3386
|
return;
|
|
3351
3387
|
}
|
|
3388
|
+
this.captureEditingContextSnapshot(item);
|
|
3352
3389
|
this.activeFilter.set(item);
|
|
3353
3390
|
this.popover()?.open();
|
|
3354
3391
|
}
|
|
@@ -3437,6 +3474,7 @@ class AXPQueryFiltersComponent {
|
|
|
3437
3474
|
}
|
|
3438
3475
|
}
|
|
3439
3476
|
handlePopoverClosed(e) {
|
|
3477
|
+
this.restoreEditingContextSnapshot();
|
|
3440
3478
|
this.activeFilter.set(null);
|
|
3441
3479
|
}
|
|
3442
3480
|
onPopoverOpened(e) {
|
|
@@ -3468,21 +3506,60 @@ class AXPQueryFiltersComponent {
|
|
|
3468
3506
|
/**
|
|
3469
3507
|
* Resolves filter chip labels; translates i18n keys stored in select `displayText` or datasource titles.
|
|
3470
3508
|
*/
|
|
3471
|
-
formatFilterDisplayValue(value) {
|
|
3509
|
+
async formatFilterDisplayValue(value) {
|
|
3472
3510
|
if (value == null || value === '') {
|
|
3473
3511
|
return '';
|
|
3474
3512
|
}
|
|
3475
3513
|
const parts = Array.isArray(value) ? value : [value];
|
|
3476
|
-
|
|
3477
|
-
.map((part) => {
|
|
3514
|
+
const labels = await Promise.all(parts.map(async (part) => {
|
|
3478
3515
|
const text = String(part ?? '').trim();
|
|
3479
3516
|
if (!text) {
|
|
3480
3517
|
return '';
|
|
3481
3518
|
}
|
|
3482
|
-
return
|
|
3483
|
-
})
|
|
3484
|
-
|
|
3485
|
-
|
|
3519
|
+
return this.translateLabel(text);
|
|
3520
|
+
}));
|
|
3521
|
+
return labels.filter(Boolean).join(', ');
|
|
3522
|
+
}
|
|
3523
|
+
async translateLabel(text) {
|
|
3524
|
+
const trimmed = text.trim();
|
|
3525
|
+
if (!trimmed) {
|
|
3526
|
+
return '';
|
|
3527
|
+
}
|
|
3528
|
+
if (trimmed.startsWith('@')) {
|
|
3529
|
+
return (await this.translate.translateAsync(trimmed)) || trimmed;
|
|
3530
|
+
}
|
|
3531
|
+
return trimmed;
|
|
3532
|
+
}
|
|
3533
|
+
captureEditingContextSnapshot(filter) {
|
|
3534
|
+
const contextField = this.resolveFilterContextField(filter);
|
|
3535
|
+
const context = this.context();
|
|
3536
|
+
this.editingContextSnapshot = {
|
|
3537
|
+
contextField,
|
|
3538
|
+
entry: cloneDeep(context[contextField]),
|
|
3539
|
+
};
|
|
3540
|
+
}
|
|
3541
|
+
restoreEditingContextSnapshot() {
|
|
3542
|
+
const snapshot = this.editingContextSnapshot;
|
|
3543
|
+
if (!snapshot) {
|
|
3544
|
+
return;
|
|
3545
|
+
}
|
|
3546
|
+
const { contextField, entry } = snapshot;
|
|
3547
|
+
this.context.update((prev) => ({
|
|
3548
|
+
...prev,
|
|
3549
|
+
[contextField]: cloneDeep(entry),
|
|
3550
|
+
}));
|
|
3551
|
+
this.editingContextSnapshot = null;
|
|
3552
|
+
}
|
|
3553
|
+
/**
|
|
3554
|
+
* Returns the committed filter context entry (snapshot while editing, live context otherwise).
|
|
3555
|
+
*/
|
|
3556
|
+
getCommittedFilterEntry(filter, context, editingField) {
|
|
3557
|
+
const contextField = this.resolveFilterContextField(filter);
|
|
3558
|
+
const snapshot = this.editingContextSnapshot;
|
|
3559
|
+
if (editingField === filter.field && snapshot?.contextField === contextField) {
|
|
3560
|
+
return snapshot.entry;
|
|
3561
|
+
}
|
|
3562
|
+
return context[contextField];
|
|
3486
3563
|
}
|
|
3487
3564
|
createExpressionScope(filtersContext) {
|
|
3488
3565
|
const base = (filtersContext ?? {});
|