@acorex/platform 21.0.0-next.8 → 21.0.0-next.83
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/contracts/README.md +3 -0
- package/fesm2022/acorex-platform-auth.mjs +201 -81
- package/fesm2022/acorex-platform-auth.mjs.map +1 -1
- package/fesm2022/acorex-platform-common-common-settings.provider-Ytey9uhY.mjs +177 -0
- package/fesm2022/acorex-platform-common-common-settings.provider-Ytey9uhY.mjs.map +1 -0
- package/fesm2022/acorex-platform-common.mjs +4490 -880
- package/fesm2022/acorex-platform-common.mjs.map +1 -1
- package/fesm2022/acorex-platform-contracts.mjs +38 -0
- package/fesm2022/acorex-platform-contracts.mjs.map +1 -0
- package/fesm2022/acorex-platform-core.mjs +2612 -667
- package/fesm2022/acorex-platform-core.mjs.map +1 -1
- package/fesm2022/acorex-platform-domain.mjs +557 -826
- package/fesm2022/acorex-platform-domain.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-builder.mjs +1255 -209
- package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-components-binding-expression-editor-popup.component-CXEdvDTf.mjs +121 -0
- package/fesm2022/acorex-platform-layout-components-binding-expression-editor-popup.component-CXEdvDTf.mjs.map +1 -0
- package/fesm2022/acorex-platform-layout-components.mjs +6486 -1915
- package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-designer.mjs +612 -287
- package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-entity-attachments-page.component-CwG6n_Yb.mjs +376 -0
- package/fesm2022/acorex-platform-layout-entity-attachments-page.component-CwG6n_Yb.mjs.map +1 -0
- package/fesm2022/acorex-platform-layout-entity-file-list-popup.component--IeMZLIr.mjs +100 -0
- package/fesm2022/acorex-platform-layout-entity-file-list-popup.component--IeMZLIr.mjs.map +1 -0
- package/fesm2022/acorex-platform-layout-entity.mjs +22502 -9964
- package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-views.mjs +1360 -338
- package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-widget-core.mjs +2226 -524
- package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -1
- package/fesm2022/{acorex-platform-layout-widgets-button-widget-designer.component-C3VoBb_b.mjs → acorex-platform-layout-widgets-button-widget-designer.component-Dy7jF-oD.mjs} +10 -10
- package/fesm2022/acorex-platform-layout-widgets-button-widget-designer.component-Dy7jF-oD.mjs.map +1 -0
- package/fesm2022/{acorex-platform-layout-widgets-image-preview.popup-V31OpYah.mjs → acorex-platform-layout-widgets-image-preview.popup-C_EPAvCU.mjs} +6 -7
- package/fesm2022/acorex-platform-layout-widgets-image-preview.popup-C_EPAvCU.mjs.map +1 -0
- package/fesm2022/{acorex-platform-layout-widgets-page-widget-designer.component-BtZMBxYp.mjs → acorex-platform-layout-widgets-page-widget-designer.component-D10yO28c.mjs} +12 -12
- package/fesm2022/acorex-platform-layout-widgets-page-widget-designer.component-D10yO28c.mjs.map +1 -0
- package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-J0zcGKBX.mjs +116 -0
- package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-J0zcGKBX.mjs.map +1 -0
- package/fesm2022/{acorex-platform-layout-widgets-tabular-data-edit-popup.component-Ck7-wpT2.mjs → acorex-platform-layout-widgets-tabular-data-edit-popup.component-DjpZU6gz.mjs} +6 -6
- package/fesm2022/acorex-platform-layout-widgets-tabular-data-edit-popup.component-DjpZU6gz.mjs.map +1 -0
- package/fesm2022/{acorex-platform-layout-widgets-tabular-data-view-popup.component-y8vjUiVs.mjs → acorex-platform-layout-widgets-tabular-data-view-popup.component-gX-3Kx9I.mjs} +5 -5
- package/fesm2022/acorex-platform-layout-widgets-tabular-data-view-popup.component-gX-3Kx9I.mjs.map +1 -0
- package/fesm2022/{acorex-platform-layout-widgets-text-block-widget-designer.component-Df1BFkSa.mjs → acorex-platform-layout-widgets-text-block-widget-designer.component-Vo4fWHtX.mjs} +6 -6
- package/fesm2022/acorex-platform-layout-widgets-text-block-widget-designer.component-Vo4fWHtX.mjs.map +1 -0
- package/fesm2022/acorex-platform-layout-widgets.mjs +18742 -16715
- package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
- package/fesm2022/acorex-platform-native.mjs +8 -7
- package/fesm2022/acorex-platform-native.mjs.map +1 -1
- package/fesm2022/acorex-platform-runtime.mjs +391 -166
- package/fesm2022/acorex-platform-runtime.mjs.map +1 -1
- package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-CWLfNqV0.mjs +160 -0
- package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-CWLfNqV0.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-C7cT82K2.mjs +120 -0
- package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-C7cT82K2.mjs.map +1 -0
- package/fesm2022/{acorex-platform-themes-default-entity-master-single-view.component-eMBby9k4.mjs → acorex-platform-themes-default-entity-master-single-view.component-Br9p5aXT.mjs} +21 -28
- package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-Br9p5aXT.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-default-error-401.component-B1nsdpTY.mjs +48 -0
- package/fesm2022/acorex-platform-themes-default-error-401.component-B1nsdpTY.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-default-error-404.component-D4UvRe8u.mjs +42 -0
- package/fesm2022/acorex-platform-themes-default-error-404.component-D4UvRe8u.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-default-error-offline.component-DR6G8gPC.mjs +19 -0
- package/fesm2022/acorex-platform-themes-default-error-offline.component-DR6G8gPC.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-default.mjs +2337 -94
- package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
- package/fesm2022/{acorex-platform-themes-shared-icon-chooser-column.component-C0EpfU2k.mjs → acorex-platform-themes-shared-icon-chooser-column.component-CqkWJYdv.mjs} +6 -6
- package/fesm2022/acorex-platform-themes-shared-icon-chooser-column.component-CqkWJYdv.mjs.map +1 -0
- package/fesm2022/{acorex-platform-themes-shared-icon-chooser-view.component-9W52W6Nu.mjs → acorex-platform-themes-shared-icon-chooser-view.component-BOTuLdWN.mjs} +6 -6
- package/fesm2022/acorex-platform-themes-shared-icon-chooser-view.component-BOTuLdWN.mjs.map +1 -0
- package/fesm2022/{acorex-platform-themes-shared-settings.provider-DSs1o1M6.mjs → acorex-platform-themes-shared-settings.provider-BjuzSe0T.mjs} +52 -33
- package/fesm2022/acorex-platform-themes-shared-settings.provider-BjuzSe0T.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-D566Kdvy.mjs +94 -0
- package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-D566Kdvy.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-D7-rCGl7.mjs +86 -0
- package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-D7-rCGl7.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-shared.mjs +793 -612
- package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
- package/fesm2022/acorex-platform-workflow.mjs +997 -274
- package/fesm2022/acorex-platform-workflow.mjs.map +1 -1
- package/fesm2022/acorex-platform.mjs.map +1 -1
- package/package.json +41 -35
- package/{auth/index.d.ts → types/acorex-platform-auth.d.ts} +40 -19
- package/types/acorex-platform-common.d.ts +3673 -0
- package/types/acorex-platform-contracts.d.ts +940 -0
- package/{core/index.d.ts → types/acorex-platform-core.d.ts} +1029 -170
- package/{domain/index.d.ts → types/acorex-platform-domain.d.ts} +744 -412
- package/{layout/builder/index.d.ts → types/acorex-platform-layout-builder.d.ts} +262 -55
- package/types/acorex-platform-layout-components.d.ts +3329 -0
- package/{layout/designer/index.d.ts → types/acorex-platform-layout-designer.d.ts} +96 -18
- package/types/acorex-platform-layout-entity.d.ts +4512 -0
- package/{layout/views/index.d.ts → types/acorex-platform-layout-views.d.ts} +334 -91
- package/{layout/widget-core/index.d.ts → types/acorex-platform-layout-widget-core.d.ts} +438 -162
- package/{layout/widgets/index.d.ts → types/acorex-platform-layout-widgets.d.ts} +1152 -507
- package/{native/index.d.ts → types/acorex-platform-native.d.ts} +0 -7
- package/types/acorex-platform-runtime.d.ts +571 -0
- package/{themes/default/index.d.ts → types/acorex-platform-themes-default.d.ts} +268 -11
- package/{themes/shared/index.d.ts → types/acorex-platform-themes-shared.d.ts} +30 -2
- package/{workflow/index.d.ts → types/acorex-platform-workflow.d.ts} +436 -748
- package/common/index.d.ts +0 -2470
- package/fesm2022/acorex-platform-common-common-settings.provider-zhqNP3xb.mjs +0 -71
- package/fesm2022/acorex-platform-common-common-settings.provider-zhqNP3xb.mjs.map +0 -1
- package/fesm2022/acorex-platform-layout-widgets-button-widget-designer.component-C3VoBb_b.mjs.map +0 -1
- package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-CxrsI6Hn.mjs +0 -135
- package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-CxrsI6Hn.mjs.map +0 -1
- package/fesm2022/acorex-platform-layout-widgets-image-preview.popup-V31OpYah.mjs.map +0 -1
- package/fesm2022/acorex-platform-layout-widgets-page-widget-designer.component-BtZMBxYp.mjs.map +0 -1
- package/fesm2022/acorex-platform-layout-widgets-tabular-data-edit-popup.component-Ck7-wpT2.mjs.map +0 -1
- package/fesm2022/acorex-platform-layout-widgets-tabular-data-view-popup.component-y8vjUiVs.mjs.map +0 -1
- package/fesm2022/acorex-platform-layout-widgets-text-block-widget-designer.component-Df1BFkSa.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-VIGuU5M4.mjs +0 -157
- package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-VIGuU5M4.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-D3qZa5fM.mjs +0 -1542
- package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-D3qZa5fM.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-Ua3ZA5hk.mjs +0 -101
- package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-Ua3ZA5hk.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-eMBby9k4.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-error-401.component-cfREo88K.mjs +0 -31
- package/fesm2022/acorex-platform-themes-default-error-401.component-cfREo88K.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-error-404.component-CdCV5ZoA.mjs +0 -25
- package/fesm2022/acorex-platform-themes-default-error-404.component-CdCV5ZoA.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-error-offline.component-E7SzBcAt.mjs +0 -19
- package/fesm2022/acorex-platform-themes-default-error-offline.component-E7SzBcAt.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-shared-icon-chooser-column.component-C0EpfU2k.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-shared-icon-chooser-view.component-9W52W6Nu.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-shared-settings.provider-DSs1o1M6.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-Dz0cylyQ.mjs +0 -65
- package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-Dz0cylyQ.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-DY0JtT1v.mjs +0 -64
- package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-DY0JtT1v.mjs.map +0 -1
- package/layout/components/index.d.ts +0 -1670
- package/layout/entity/index.d.ts +0 -2294
- package/runtime/index.d.ts +0 -307
- /package/{index.d.ts → types/acorex-platform.d.ts} +0 -0
|
@@ -1,11 +1,29 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, inject, Injectable,
|
|
2
|
+
import { InjectionToken, inject, Injectable, Directive, computed, Injector, ChangeDetectionStrategy, Component, input, ElementRef, ViewContainerRef, signal, effect, runInInjectionContext, Optional, Inject, NgModule, EventEmitter, HostListener, Output, provideAppInitializer, Pipe } from '@angular/core';
|
|
3
|
+
import { AXTranslationService, resolveMultiLanguageString } from '@acorex/core/translation';
|
|
4
|
+
import { AXDataSource } from '@acorex/cdk/common';
|
|
5
|
+
import { isPlainObject, get, set, isNil, isEmpty, isArray, merge, isObjectLike, transform, isEqual, differenceWith, union, cloneDeep, has, sortBy, isUndefined, endsWith, startsWith, includes, lte, gte, lt, gt, orderBy } from 'lodash-es';
|
|
3
6
|
import { signalStore, withState, withComputed, withMethods, patchState } from '@ngrx/signals';
|
|
4
|
-
import
|
|
7
|
+
import * as i1 from '@acorex/components/skeleton';
|
|
8
|
+
import { AXSkeletonModule } from '@acorex/components/skeleton';
|
|
5
9
|
import { Subject, interval, fromEvent } from 'rxjs';
|
|
10
|
+
import { toSignal } from '@angular/core/rxjs-interop';
|
|
11
|
+
import { DOCUMENT } from '@angular/common';
|
|
12
|
+
import { AXLocaleService } from '@acorex/core/locale';
|
|
6
13
|
import { AXCalendarService } from '@acorex/core/date-time';
|
|
7
14
|
import { startWith, map, debounceTime } from 'rxjs/operators';
|
|
8
15
|
|
|
16
|
+
//#region ---- Datasource keys ----
|
|
17
|
+
/** Platform core datasource registry names. */
|
|
18
|
+
const AXPPlatformCoreDataSourceKeys = {
|
|
19
|
+
ModuleManifests: 'platform-module-manifests',
|
|
20
|
+
CatalogScopes: 'platform-catalog-scopes',
|
|
21
|
+
};
|
|
22
|
+
//#endregion
|
|
23
|
+
|
|
24
|
+
//#region ---- Contracts barrel ----
|
|
25
|
+
//#endregion
|
|
26
|
+
|
|
9
27
|
const AXP_ACTIVITY_LOG_PROVIDER = new InjectionToken('AXP_ACTIVITY_LOGS_PROVIDER');
|
|
10
28
|
class AXPActivityLogService {
|
|
11
29
|
constructor() {
|
|
@@ -17,10 +35,10 @@ class AXPActivityLogService {
|
|
|
17
35
|
async getHistoryByIds(refId, refType, ids) {
|
|
18
36
|
return (await Promise.all(this.providers?.map((p) => p.getHistoryByIds(refId, refType, ids)) ?? [])).flat();
|
|
19
37
|
}
|
|
20
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
21
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
38
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPActivityLogService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
39
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPActivityLogService, providedIn: 'root' }); }
|
|
22
40
|
}
|
|
23
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
41
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPActivityLogService, decorators: [{
|
|
24
42
|
type: Injectable,
|
|
25
43
|
args: [{ providedIn: 'root' }]
|
|
26
44
|
}] });
|
|
@@ -28,6 +46,180 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
|
|
|
28
46
|
class AXPActivityLogProvider {
|
|
29
47
|
}
|
|
30
48
|
|
|
49
|
+
//#region ---- Catalog scope definition ----
|
|
50
|
+
//#endregion
|
|
51
|
+
|
|
52
|
+
//#region ---- Provider contract ----
|
|
53
|
+
const AXP_CATALOG_SCOPE_DEFINITION_PROVIDER = new InjectionToken('AXP_CATALOG_SCOPE_DEFINITION_PROVIDER');
|
|
54
|
+
//#endregion
|
|
55
|
+
|
|
56
|
+
//#region ---- Imports ----
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region ---- Aggregator service ----
|
|
59
|
+
/**
|
|
60
|
+
* Collects {@link AXPCatalogScopeDefinition} entries from all registered providers.
|
|
61
|
+
*/
|
|
62
|
+
class AXPCatalogScopeDefinitionProviderService {
|
|
63
|
+
constructor() {
|
|
64
|
+
//#region ---- Services & Dependencies ----
|
|
65
|
+
this.providers = inject(AXP_CATALOG_SCOPE_DEFINITION_PROVIDER, { optional: true });
|
|
66
|
+
this.translation = inject(AXTranslationService);
|
|
67
|
+
}
|
|
68
|
+
//#endregion
|
|
69
|
+
//#region ---- Public API ----
|
|
70
|
+
async items() {
|
|
71
|
+
const merged = [];
|
|
72
|
+
const seen = new Set();
|
|
73
|
+
if (!Array.isArray(this.providers)) {
|
|
74
|
+
return merged;
|
|
75
|
+
}
|
|
76
|
+
for (const raw of this.providers) {
|
|
77
|
+
const provider = await Promise.resolve(raw);
|
|
78
|
+
if (!provider || typeof provider.items !== 'function') {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
for (const item of await provider.items()) {
|
|
82
|
+
const name = item.name?.trim();
|
|
83
|
+
if (!name) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (seen.has(name)) {
|
|
87
|
+
throw new Error(`Duplicate catalog scope definition: "${name}"`);
|
|
88
|
+
}
|
|
89
|
+
seen.add(name);
|
|
90
|
+
merged.push(item);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return merged.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
94
|
+
}
|
|
95
|
+
async get(name) {
|
|
96
|
+
const key = name?.trim();
|
|
97
|
+
if (!key) {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
return (await this.items()).find((d) => d.name === key);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Scopes applicable to the given entity ref (`Module.Entity`).
|
|
104
|
+
*/
|
|
105
|
+
async byTarget(entityRef) {
|
|
106
|
+
const target = entityRef?.trim();
|
|
107
|
+
if (!target) {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
return (await this.items()).filter((d) => d.targets?.some((t) => t === target));
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Resolves display title for a scope key (active locale).
|
|
114
|
+
*/
|
|
115
|
+
resolveTitle(definition) {
|
|
116
|
+
const locale = this.translation.getActiveLang() ?? 'en-US';
|
|
117
|
+
if (typeof definition.title === 'string') {
|
|
118
|
+
const title = definition.title.trim();
|
|
119
|
+
if (title.startsWith('@')) {
|
|
120
|
+
return this.translation.translateSync(title) || title;
|
|
121
|
+
}
|
|
122
|
+
return title;
|
|
123
|
+
}
|
|
124
|
+
return resolveMultiLanguageString(definition.title, locale) || definition.name;
|
|
125
|
+
}
|
|
126
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPCatalogScopeDefinitionProviderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
127
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPCatalogScopeDefinitionProviderService, providedIn: 'root' }); }
|
|
128
|
+
}
|
|
129
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPCatalogScopeDefinitionProviderService, decorators: [{
|
|
130
|
+
type: Injectable,
|
|
131
|
+
args: [{ providedIn: 'root' }]
|
|
132
|
+
}] });
|
|
133
|
+
|
|
134
|
+
//#region ---- Imports ----
|
|
135
|
+
//#endregion
|
|
136
|
+
//#region ---- Data source definition ----
|
|
137
|
+
/**
|
|
138
|
+
* Registered catalog scopes for select / selection-list widgets via {@link AXPPlatformCoreDataSourceKeys.CatalogScopes}.
|
|
139
|
+
*/
|
|
140
|
+
class AXPCatalogScopeDefinitionsDataSourceDefinition {
|
|
141
|
+
constructor() {
|
|
142
|
+
//#region ---- Services & Dependencies ----
|
|
143
|
+
this.catalogScopeRegistry = inject(AXPCatalogScopeDefinitionProviderService);
|
|
144
|
+
//#endregion
|
|
145
|
+
}
|
|
146
|
+
//#endregion
|
|
147
|
+
//#region ---- Public API ----
|
|
148
|
+
async items() {
|
|
149
|
+
return [
|
|
150
|
+
{
|
|
151
|
+
name: AXPPlatformCoreDataSourceKeys.CatalogScopes,
|
|
152
|
+
title: 'Catalog scopes',
|
|
153
|
+
source: () => new AXDataSource({
|
|
154
|
+
key: 'name',
|
|
155
|
+
pageSize: 500,
|
|
156
|
+
load: async (e) => {
|
|
157
|
+
const all = await this.catalogScopeRegistry.items();
|
|
158
|
+
const localeFilter = this.readTargetFilter(e?.filter);
|
|
159
|
+
const items = all
|
|
160
|
+
.filter((scope) => !localeFilter || scope.targets?.includes(localeFilter))
|
|
161
|
+
.map((scope) => ({
|
|
162
|
+
name: scope.name,
|
|
163
|
+
title: this.catalogScopeRegistry.resolveTitle(scope),
|
|
164
|
+
description: scope.description,
|
|
165
|
+
targets: scope.targets,
|
|
166
|
+
icon: scope.icon,
|
|
167
|
+
cssClass: scope.cssClass,
|
|
168
|
+
}));
|
|
169
|
+
return { items, total: items.length };
|
|
170
|
+
},
|
|
171
|
+
byKey: async (key) => {
|
|
172
|
+
const name = String(key ?? '').trim();
|
|
173
|
+
if (!name) {
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
const scope = await this.catalogScopeRegistry.get(name);
|
|
177
|
+
if (!scope) {
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
name: scope.name,
|
|
182
|
+
title: this.catalogScopeRegistry.resolveTitle(scope),
|
|
183
|
+
description: scope.description,
|
|
184
|
+
targets: scope.targets,
|
|
185
|
+
icon: scope.icon,
|
|
186
|
+
cssClass: scope.cssClass,
|
|
187
|
+
};
|
|
188
|
+
},
|
|
189
|
+
}),
|
|
190
|
+
columns: [
|
|
191
|
+
{ name: 'name', title: 'Name', datatype: 'string', type: 'text-editor' },
|
|
192
|
+
{ name: 'title', title: 'Title', datatype: 'string', type: 'text-editor' },
|
|
193
|
+
],
|
|
194
|
+
filters: [
|
|
195
|
+
{
|
|
196
|
+
field: 'targets',
|
|
197
|
+
title: 'Target entity',
|
|
198
|
+
operator: { type: 'contains' },
|
|
199
|
+
widget: { type: 'text-editor' },
|
|
200
|
+
filterType: { advance: false, inline: false },
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
textField: { name: 'title', title: 'Title' },
|
|
204
|
+
valueField: { name: 'name', title: 'Name' },
|
|
205
|
+
},
|
|
206
|
+
];
|
|
207
|
+
}
|
|
208
|
+
//#endregion
|
|
209
|
+
//#region ---- Helpers ----
|
|
210
|
+
readTargetFilter(filter) {
|
|
211
|
+
if (!filter || typeof filter !== 'object') {
|
|
212
|
+
return undefined;
|
|
213
|
+
}
|
|
214
|
+
const f = filter;
|
|
215
|
+
if (f.field === 'targets' && typeof f.value === 'string' && f.value.trim()) {
|
|
216
|
+
return f.value.trim();
|
|
217
|
+
}
|
|
218
|
+
return undefined;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
//#endregion
|
|
222
|
+
|
|
31
223
|
//#region ---- Color Palette Provider ----
|
|
32
224
|
/**
|
|
33
225
|
* Abstract class for color palette providers
|
|
@@ -170,10 +362,10 @@ class AXPColorPaletteService {
|
|
|
170
362
|
const uniquePalettes = allPalettes.filter((palette, index, self) => index === self.findIndex((p) => p.name === palette.name));
|
|
171
363
|
this.cache = uniquePalettes;
|
|
172
364
|
}
|
|
173
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
174
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
365
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPColorPaletteService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
366
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPColorPaletteService, providedIn: 'root' }); }
|
|
175
367
|
}
|
|
176
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
368
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPColorPaletteService, decorators: [{
|
|
177
369
|
type: Injectable,
|
|
178
370
|
args: [{
|
|
179
371
|
providedIn: 'root',
|
|
@@ -288,13 +480,277 @@ class AXPDefaultColorPalettesProvider extends AXPColorPaletteProvider {
|
|
|
288
480
|
},
|
|
289
481
|
];
|
|
290
482
|
}
|
|
291
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
292
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
483
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDefaultColorPalettesProvider, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
484
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDefaultColorPalettesProvider }); }
|
|
293
485
|
}
|
|
294
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
486
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDefaultColorPalettesProvider, decorators: [{
|
|
295
487
|
type: Injectable
|
|
296
488
|
}] });
|
|
297
489
|
|
|
490
|
+
//#region ---- Column Width Provider ----
|
|
491
|
+
/**
|
|
492
|
+
* Injection token for column width providers
|
|
493
|
+
* Use this to register multiple column width providers with multi: true
|
|
494
|
+
*
|
|
495
|
+
* @example
|
|
496
|
+
* ```typescript
|
|
497
|
+
* providers: [
|
|
498
|
+
* {
|
|
499
|
+
* provide: AXP_COLUMN_WIDTH_PROVIDER,
|
|
500
|
+
* useValue: {
|
|
501
|
+
* matchers: [
|
|
502
|
+
* { name: 'title', width: '300px', priority: 10 },
|
|
503
|
+
* { widgetType: /text-box/i, width: '200px' }
|
|
504
|
+
* ],
|
|
505
|
+
* priority: 150
|
|
506
|
+
* },
|
|
507
|
+
* multi: true
|
|
508
|
+
* }
|
|
509
|
+
* ]
|
|
510
|
+
* ```
|
|
511
|
+
*/
|
|
512
|
+
const AXP_COLUMN_WIDTH_PROVIDER = new InjectionToken('AXP_COLUMN_WIDTH_PROVIDER');
|
|
513
|
+
//#endregion
|
|
514
|
+
|
|
515
|
+
//#region ---- Column Width Service ----
|
|
516
|
+
/**
|
|
517
|
+
* Service for resolving column widths based on registered providers
|
|
518
|
+
* Supports matching by column name (priority) and widget type (fallback)
|
|
519
|
+
* Uses multi-provider pattern for extensibility
|
|
520
|
+
*/
|
|
521
|
+
class AXPColumnWidthService {
|
|
522
|
+
constructor() {
|
|
523
|
+
//#region ---- Services & Dependencies ----
|
|
524
|
+
this.providers = inject(AXP_COLUMN_WIDTH_PROVIDER, {
|
|
525
|
+
optional: true,
|
|
526
|
+
}) ?? [];
|
|
527
|
+
}
|
|
528
|
+
//#region ---- Public Methods ----
|
|
529
|
+
/**
|
|
530
|
+
* Resolve width for a column based on registered providers
|
|
531
|
+
* Only returns a width if column doesn't already have one set
|
|
532
|
+
*
|
|
533
|
+
* @param column Column to resolve width for
|
|
534
|
+
* @returns Resolved width string (normalized) or undefined if no match or column already has width
|
|
535
|
+
*/
|
|
536
|
+
resolveWidth(column) {
|
|
537
|
+
// If column already has a width set, don't override
|
|
538
|
+
const existingWidth = column.width || column.options?.width;
|
|
539
|
+
if (existingWidth) {
|
|
540
|
+
return undefined;
|
|
541
|
+
}
|
|
542
|
+
// Get widget type from column (support both widget.type and showAs.type)
|
|
543
|
+
const widgetType = column.widget?.type || column.showAs?.type;
|
|
544
|
+
// Collect all matchers from all providers
|
|
545
|
+
const allMatchers = this.collectMatchers();
|
|
546
|
+
// Try name-based matches first (higher priority)
|
|
547
|
+
const nameMatch = this.findNameMatch(column.name, allMatchers);
|
|
548
|
+
if (nameMatch) {
|
|
549
|
+
return this.normalizeWidth(nameMatch.width);
|
|
550
|
+
}
|
|
551
|
+
// Fallback to widget-type matches (case-insensitive)
|
|
552
|
+
if (widgetType) {
|
|
553
|
+
const widgetMatch = this.findWidgetTypeMatch(widgetType, allMatchers);
|
|
554
|
+
if (widgetMatch) {
|
|
555
|
+
return this.normalizeWidth(widgetMatch.width);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
return undefined;
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Apply resolved width to a column (mutates the column)
|
|
562
|
+
* Only applies if column doesn't already have a width
|
|
563
|
+
*
|
|
564
|
+
* @param column Column to apply width to
|
|
565
|
+
* @returns true if width was applied, false if column already had width or no match found
|
|
566
|
+
*/
|
|
567
|
+
applyWidth(column) {
|
|
568
|
+
const width = this.resolveWidth(column);
|
|
569
|
+
if (!width) {
|
|
570
|
+
return false;
|
|
571
|
+
}
|
|
572
|
+
// Apply to the appropriate property
|
|
573
|
+
if (column.options) {
|
|
574
|
+
column.options.width = width;
|
|
575
|
+
}
|
|
576
|
+
else {
|
|
577
|
+
column.width = width;
|
|
578
|
+
}
|
|
579
|
+
return true;
|
|
580
|
+
}
|
|
581
|
+
//#endregion
|
|
582
|
+
//#region ---- Private Methods ----
|
|
583
|
+
/**
|
|
584
|
+
* Collect all matchers from all providers, sorted by provider priority and matcher priority
|
|
585
|
+
*/
|
|
586
|
+
collectMatchers() {
|
|
587
|
+
const allMatchers = [];
|
|
588
|
+
for (const provider of this.providers) {
|
|
589
|
+
const providerPriority = provider.priority ?? 100;
|
|
590
|
+
for (const matcher of provider.matchers) {
|
|
591
|
+
allMatchers.push({
|
|
592
|
+
matcher,
|
|
593
|
+
providerPriority,
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
// Sort by provider priority (descending), then by matcher priority (descending)
|
|
598
|
+
allMatchers.sort((a, b) => {
|
|
599
|
+
if (a.providerPriority !== b.providerPriority) {
|
|
600
|
+
return b.providerPriority - a.providerPriority; // Higher priority first
|
|
601
|
+
}
|
|
602
|
+
const priorityA = a.matcher.priority ?? 0;
|
|
603
|
+
const priorityB = b.matcher.priority ?? 0;
|
|
604
|
+
return priorityB - priorityA; // Higher priority first
|
|
605
|
+
});
|
|
606
|
+
return allMatchers;
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Find first matching matcher by column name
|
|
610
|
+
* Name matches take precedence over widget type matches
|
|
611
|
+
*/
|
|
612
|
+
findNameMatch(columnName, allMatchers) {
|
|
613
|
+
for (const { matcher } of allMatchers) {
|
|
614
|
+
if (!matcher.name) {
|
|
615
|
+
continue; // Skip if no name matcher
|
|
616
|
+
}
|
|
617
|
+
if (typeof matcher.name === 'string') {
|
|
618
|
+
// Exact string match (case-sensitive for names)
|
|
619
|
+
if (matcher.name === columnName) {
|
|
620
|
+
return matcher;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
else if (matcher.name instanceof RegExp) {
|
|
624
|
+
// RegExp match
|
|
625
|
+
if (matcher.name.test(columnName)) {
|
|
626
|
+
return matcher;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
return undefined;
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Find first matching matcher by widget type (case-insensitive)
|
|
634
|
+
*/
|
|
635
|
+
findWidgetTypeMatch(widgetType, allMatchers) {
|
|
636
|
+
const widgetTypeLower = widgetType.toLowerCase();
|
|
637
|
+
for (const { matcher } of allMatchers) {
|
|
638
|
+
if (!matcher.widgetType) {
|
|
639
|
+
continue; // Skip if no widget type matcher
|
|
640
|
+
}
|
|
641
|
+
if (typeof matcher.widgetType === 'string') {
|
|
642
|
+
// Case-insensitive string match
|
|
643
|
+
if (matcher.widgetType.toLowerCase() === widgetTypeLower) {
|
|
644
|
+
return matcher;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
else if (matcher.widgetType instanceof RegExp) {
|
|
648
|
+
// RegExp match (should already be case-insensitive if needed, but we'll test case-insensitively)
|
|
649
|
+
// Create a new regex with case-insensitive flag if not already present
|
|
650
|
+
let regex = matcher.widgetType;
|
|
651
|
+
if (!regex.flags.includes('i')) {
|
|
652
|
+
regex = new RegExp(regex.source, regex.flags + 'i');
|
|
653
|
+
}
|
|
654
|
+
if (regex.test(widgetType)) {
|
|
655
|
+
return matcher;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
return undefined;
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Normalize width value to string format
|
|
663
|
+
* Numbers are converted to pixels (e.g., 150 -> '150px')
|
|
664
|
+
* Strings are preserved as-is (supports any CSS unit)
|
|
665
|
+
*/
|
|
666
|
+
normalizeWidth(width) {
|
|
667
|
+
if (typeof width === 'number') {
|
|
668
|
+
return `${width}px`;
|
|
669
|
+
}
|
|
670
|
+
return width;
|
|
671
|
+
}
|
|
672
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPColumnWidthService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
673
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPColumnWidthService, providedIn: 'root' }); }
|
|
674
|
+
}
|
|
675
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPColumnWidthService, decorators: [{
|
|
676
|
+
type: Injectable,
|
|
677
|
+
args: [{
|
|
678
|
+
providedIn: 'root',
|
|
679
|
+
}]
|
|
680
|
+
}] });
|
|
681
|
+
|
|
682
|
+
//#region ---- Column Width Types ----
|
|
683
|
+
//#endregion
|
|
684
|
+
|
|
685
|
+
//#region ---- Default Column Width Provider ----
|
|
686
|
+
/**
|
|
687
|
+
* Default column width configuration
|
|
688
|
+
* Migrated from DEFAULT_COLUMN_WIDTHS in column-width.middleware.ts
|
|
689
|
+
* Provides sensible defaults for common column names
|
|
690
|
+
*/
|
|
691
|
+
const defaultColumnWidthProvider = {
|
|
692
|
+
priority: 100,
|
|
693
|
+
matchers: [
|
|
694
|
+
// Priority: name-based matches (higher priority = more specific)
|
|
695
|
+
{ name: 'code', width: '150px', priority: 10 },
|
|
696
|
+
{ name: 'name', width: '250px', priority: 10 },
|
|
697
|
+
{ name: 'title', width: '250px', priority: 10 },
|
|
698
|
+
{ name: 'description', width: '350px', priority: 10 },
|
|
699
|
+
{ name: 'note', width: '350px', priority: 10 },
|
|
700
|
+
{ name: 'notes', width: '350px', priority: 10 },
|
|
701
|
+
{ name: 'content', width: '500px', priority: 10 },
|
|
702
|
+
{ name: 'level', width: '100px', priority: 10 },
|
|
703
|
+
{ name: 'order', width: '100px', priority: 10 },
|
|
704
|
+
{ name: 'phones', width: '300px', priority: 10 },
|
|
705
|
+
{ name: 'emails', width: '300px', priority: 10 },
|
|
706
|
+
],
|
|
707
|
+
};
|
|
708
|
+
//#endregion
|
|
709
|
+
|
|
710
|
+
class AXPComponentSlot {
|
|
711
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPComponentSlot, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
712
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: AXPComponentSlot, isStandalone: true, ngImport: i0 }); }
|
|
713
|
+
}
|
|
714
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPComponentSlot, decorators: [{
|
|
715
|
+
type: Directive
|
|
716
|
+
}] });
|
|
717
|
+
|
|
718
|
+
//#region ---- Locale map shape ----
|
|
719
|
+
/** Per-locale string map (`{ 'en-US': '...', 'fa-IR': '...' }`). */
|
|
720
|
+
function isLocaleStringMap(value) {
|
|
721
|
+
if (!isPlainObject(value)) {
|
|
722
|
+
return false;
|
|
723
|
+
}
|
|
724
|
+
const entries = Object.values(value);
|
|
725
|
+
if (entries.length === 0) {
|
|
726
|
+
return false;
|
|
727
|
+
}
|
|
728
|
+
return entries.every((entry) => entry === null || entry === undefined || typeof entry === 'string');
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Use locale-map read/write when the option is on or live/saved data is already a map.
|
|
732
|
+
* Auto-enables when API data is a map but the editor option was left off.
|
|
733
|
+
*/
|
|
734
|
+
function shouldUseLocaleMapShape(localeMapOptionEnabled, current, saved) {
|
|
735
|
+
return localeMapOptionEnabled || isLocaleStringMap(current) || isLocaleStringMap(saved);
|
|
736
|
+
}
|
|
737
|
+
/** Updates one locale entry; preserves sibling locales from current or saved map. */
|
|
738
|
+
function buildLocaleTextMapValue(current, saved, lang, input) {
|
|
739
|
+
const seed = isLocaleStringMap(current)
|
|
740
|
+
? { ...current }
|
|
741
|
+
: isLocaleStringMap(saved)
|
|
742
|
+
? { ...saved }
|
|
743
|
+
: {};
|
|
744
|
+
seed[lang] = input ?? '';
|
|
745
|
+
for (const key of Object.keys(seed)) {
|
|
746
|
+
if ((seed[key] ?? '').trim() === '') {
|
|
747
|
+
delete seed[key];
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
return Object.keys(seed).length === 0 ? null : seed;
|
|
751
|
+
}
|
|
752
|
+
//#endregion
|
|
753
|
+
|
|
298
754
|
function extractNestedFieldsWildcard(obj, basePath, fields) {
|
|
299
755
|
const result = {};
|
|
300
756
|
if (fields.length === 1 && fields[0] === '*') {
|
|
@@ -313,7 +769,41 @@ function setSmart(obj, smartPath, value) {
|
|
|
313
769
|
const lastPart = pathParts[pathParts.length - 1];
|
|
314
770
|
const fieldMatch = lastPart.match(/^\{(.+)\}$/);
|
|
315
771
|
if (!fieldMatch) {
|
|
772
|
+
// Fix: If value is undefined and we're setting a nested property,
|
|
773
|
+
// check if the parent object exists and if setting this would result in
|
|
774
|
+
// all properties being undefined. If so, set the parent to null instead.
|
|
775
|
+
if (isNil(value) && pathParts.length > 1) {
|
|
776
|
+
const parentPath = pathParts.slice(0, -1).join('.');
|
|
777
|
+
const parentObj = get(obj, parentPath);
|
|
778
|
+
// If parent is already null/undefined, don't recreate the object structure
|
|
779
|
+
if (isNil(parentObj)) {
|
|
780
|
+
return obj;
|
|
781
|
+
}
|
|
782
|
+
// Check if parent is a plain object (not array, not null)
|
|
783
|
+
if (isPlainObject(parentObj)) {
|
|
784
|
+
const existingKeys = Object.keys(parentObj);
|
|
785
|
+
// Check if all existing properties are null/undefined using lodash
|
|
786
|
+
const allNil = isEmpty(existingKeys) || existingKeys.every((key) => isNil(parentObj[key]));
|
|
787
|
+
if (allNil) {
|
|
788
|
+
// Set parent to null instead of creating/keeping an object with all undefined properties
|
|
789
|
+
set(obj, parentPath, null);
|
|
790
|
+
return obj;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
316
794
|
set(obj, smartPath, value);
|
|
795
|
+
// After setting, check again if parent should be null using lodash
|
|
796
|
+
if (isNil(value) && pathParts.length > 1) {
|
|
797
|
+
const parentPath = pathParts.slice(0, -1).join('.');
|
|
798
|
+
const parentObj = get(obj, parentPath);
|
|
799
|
+
if (isPlainObject(parentObj)) {
|
|
800
|
+
// Use lodash to check if all properties are null/undefined
|
|
801
|
+
const allNil = Object.keys(parentObj).every((key) => isNil(parentObj[key]));
|
|
802
|
+
if (allNil) {
|
|
803
|
+
set(obj, parentPath, null);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
}
|
|
317
807
|
return obj;
|
|
318
808
|
}
|
|
319
809
|
const fields = fieldMatch[1].split(',').map((f) => f.trim());
|
|
@@ -522,152 +1012,972 @@ function getDetailedChanges(obj1, obj2) {
|
|
|
522
1012
|
function getEnumValues(enumType) {
|
|
523
1013
|
return Object.entries(enumType).map(([key, value]) => ({ id: value, title: key }));
|
|
524
1014
|
}
|
|
525
|
-
|
|
526
|
-
|
|
1015
|
+
//#region ---- Form value equality (dirty / baseline) ----
|
|
1016
|
+
/** Whether a value uses the multi-language text widget shape (string or locale map). */
|
|
1017
|
+
function isMultiLanguageFormShape(value) {
|
|
1018
|
+
if (value === null || value === undefined) {
|
|
1019
|
+
return true;
|
|
1020
|
+
}
|
|
1021
|
+
if (typeof value === 'string') {
|
|
1022
|
+
return true;
|
|
1023
|
+
}
|
|
1024
|
+
return isLocaleStringMap(value);
|
|
527
1025
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
state: 'initiated', // Current state
|
|
535
|
-
initialSnapshot: {}, // Snapshot of the first initialized state
|
|
536
|
-
previousSnapshot: {}, // Snapshot of the previous state
|
|
537
|
-
lastChange: {
|
|
538
|
-
state: 'initiated',
|
|
539
|
-
}, // Last change event
|
|
540
|
-
})),
|
|
541
|
-
// Computed Signals
|
|
542
|
-
withComputed(({ data, state, lastChange, initialSnapshot, previousSnapshot }) => ({
|
|
543
|
-
isChanged: computed(() => state() === 'changed'),
|
|
544
|
-
isReset: computed(() => state() === 'restored'),
|
|
545
|
-
isInitiated: computed(() => state() === 'initiated'),
|
|
546
|
-
isEmpty: computed(() => Object.keys(data()).length === 0),
|
|
547
|
-
isDirty: computed(() => !isEqual(data(), previousSnapshot())),
|
|
548
|
-
snapshot: computed(() => cloneDeep(data())), // Current data snapshot
|
|
549
|
-
initial: computed(() => cloneDeep(initialSnapshot())), // Initial snapshot
|
|
550
|
-
previous: computed(() => cloneDeep(previousSnapshot())), // Previous snapshot
|
|
551
|
-
changeEvent: computed(() => lastChange()), // Reactive last change event
|
|
552
|
-
})),
|
|
553
|
-
// Methods for State Management
|
|
554
|
-
withMethods((store) => ({
|
|
555
|
-
// Update a specific value
|
|
556
|
-
update(path, value) {
|
|
557
|
-
const currentData = cloneDeep(store.data());
|
|
558
|
-
const oldValue = getSmart(currentData, path);
|
|
559
|
-
// Skip if the value hasn't changed
|
|
560
|
-
if (isEqual(oldValue, value)) {
|
|
561
|
-
return;
|
|
1026
|
+
/** Drops empty/whitespace locale entries from a multi-language map. */
|
|
1027
|
+
function normalizeLocaleStringMap(value) {
|
|
1028
|
+
const normalized = {};
|
|
1029
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
1030
|
+
if (typeof entry !== 'string') {
|
|
1031
|
+
continue;
|
|
562
1032
|
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
oldValue,
|
|
567
|
-
newValue: value,
|
|
568
|
-
path,
|
|
569
|
-
state: 'changed',
|
|
570
|
-
data: updatedData,
|
|
571
|
-
};
|
|
572
|
-
// Patch the state
|
|
573
|
-
patchState(store, {
|
|
574
|
-
previousSnapshot: store.snapshot(), // Save the previous state
|
|
575
|
-
data: updatedData,
|
|
576
|
-
state: 'changed',
|
|
577
|
-
lastChange: changeEvent,
|
|
578
|
-
});
|
|
579
|
-
},
|
|
580
|
-
patch(context) {
|
|
581
|
-
const currentData = cloneDeep(store.data());
|
|
582
|
-
// Update the value and prepare the change event
|
|
583
|
-
const updatedData = { ...currentData, ...context };
|
|
584
|
-
const changeEvent = {
|
|
585
|
-
state: 'patch',
|
|
586
|
-
data: updatedData,
|
|
587
|
-
};
|
|
588
|
-
// Patch the state
|
|
589
|
-
patchState(store, {
|
|
590
|
-
previousSnapshot: store.snapshot(), // Save the previous state
|
|
591
|
-
data: updatedData,
|
|
592
|
-
state: 'changed',
|
|
593
|
-
lastChange: changeEvent,
|
|
594
|
-
});
|
|
595
|
-
},
|
|
596
|
-
// Reset to the initial state
|
|
597
|
-
reset() {
|
|
598
|
-
const initialData = store.initial();
|
|
599
|
-
const changeEvent = {
|
|
600
|
-
oldValue: cloneDeep(store.data()), // Current data becomes old value
|
|
601
|
-
newValue: cloneDeep(initialData), // Reset to the initial state
|
|
602
|
-
path: '',
|
|
603
|
-
state: 'restored',
|
|
604
|
-
data: initialData,
|
|
605
|
-
};
|
|
606
|
-
patchState(store, {
|
|
607
|
-
previousSnapshot: store.snapshot(), // Save the previous state
|
|
608
|
-
data: initialData,
|
|
609
|
-
state: 'restored',
|
|
610
|
-
lastChange: changeEvent,
|
|
611
|
-
});
|
|
612
|
-
},
|
|
613
|
-
// Initialize the state
|
|
614
|
-
set(initialData) {
|
|
615
|
-
const currentData = store.data();
|
|
616
|
-
if (isEqual(currentData, initialData)) {
|
|
617
|
-
return; // Skip if the current state matches the initial state
|
|
1033
|
+
const trimmed = entry.trim();
|
|
1034
|
+
if (trimmed !== '') {
|
|
1035
|
+
normalized[key] = trimmed;
|
|
618
1036
|
}
|
|
619
|
-
const changeEvent = {
|
|
620
|
-
oldValue: null,
|
|
621
|
-
newValue: cloneDeep(initialData),
|
|
622
|
-
path: '',
|
|
623
|
-
state: 'initiated',
|
|
624
|
-
data: initialData,
|
|
625
|
-
};
|
|
626
|
-
patchState(store, {
|
|
627
|
-
initialSnapshot: cloneDeep(initialData), // Save the initial state
|
|
628
|
-
previousSnapshot: store.snapshot(), // Save the current state as the previous
|
|
629
|
-
data: initialData,
|
|
630
|
-
state: 'initiated',
|
|
631
|
-
lastChange: changeEvent,
|
|
632
|
-
});
|
|
633
|
-
},
|
|
634
|
-
// Get a specific value
|
|
635
|
-
getValue(path) {
|
|
636
|
-
return getSmart(store.data(), path);
|
|
637
|
-
},
|
|
638
|
-
})));
|
|
639
|
-
|
|
640
|
-
class AXPDataGenerator {
|
|
641
|
-
static uuid() {
|
|
642
|
-
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (char) => {
|
|
643
|
-
const random = (Math.random() * 16) | 0;
|
|
644
|
-
const value = char === 'x' ? random : (random & 0x3) | 0x8;
|
|
645
|
-
return value.toString(16);
|
|
646
|
-
});
|
|
647
1037
|
}
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
1038
|
+
return Object.keys(normalized).length === 0 ? null : normalized;
|
|
1039
|
+
}
|
|
1040
|
+
/**
|
|
1041
|
+
* Compares multi-language field values (plain string vs locale map) for dirty/baseline checks.
|
|
1042
|
+
* Returns `null` when either side is not a multi-language shape (caller uses generic normalization).
|
|
1043
|
+
*/
|
|
1044
|
+
function compareMultiLanguageFormValues(a, b) {
|
|
1045
|
+
if (!isMultiLanguageFormShape(a) || !isMultiLanguageFormShape(b)) {
|
|
1046
|
+
return null;
|
|
658
1047
|
}
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
if (args.length === 1) {
|
|
663
|
-
start = args[0];
|
|
1048
|
+
const normalizeScalar = (value) => {
|
|
1049
|
+
if (value === null || value === undefined) {
|
|
1050
|
+
return null;
|
|
664
1051
|
}
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
1052
|
+
if (typeof value === 'string') {
|
|
1053
|
+
const trimmed = value.trim();
|
|
1054
|
+
return trimmed === '' ? null : trimmed;
|
|
668
1055
|
}
|
|
669
|
-
|
|
670
|
-
|
|
1056
|
+
if (isLocaleStringMap(value)) {
|
|
1057
|
+
return normalizeLocaleStringMap(value);
|
|
1058
|
+
}
|
|
1059
|
+
return null;
|
|
1060
|
+
};
|
|
1061
|
+
const left = normalizeScalar(a);
|
|
1062
|
+
const right = normalizeScalar(b);
|
|
1063
|
+
if (left === null && right === null) {
|
|
1064
|
+
return true;
|
|
1065
|
+
}
|
|
1066
|
+
if (left === null || right === null) {
|
|
1067
|
+
return false;
|
|
1068
|
+
}
|
|
1069
|
+
if (typeof left === 'string' && typeof right === 'string') {
|
|
1070
|
+
return left === right;
|
|
1071
|
+
}
|
|
1072
|
+
if (typeof left === 'string' && typeof right === 'object') {
|
|
1073
|
+
const values = Object.values(right);
|
|
1074
|
+
return values.length > 0 && values.every((text) => text === left);
|
|
1075
|
+
}
|
|
1076
|
+
if (typeof right === 'string' && typeof left === 'object') {
|
|
1077
|
+
const values = Object.values(left);
|
|
1078
|
+
return values.length > 0 && values.every((text) => text === right);
|
|
1079
|
+
}
|
|
1080
|
+
if (typeof left !== 'object' || typeof right !== 'object') {
|
|
1081
|
+
return false;
|
|
1082
|
+
}
|
|
1083
|
+
const leftMap = left;
|
|
1084
|
+
const rightMap = right;
|
|
1085
|
+
const leftKeys = Object.keys(leftMap).sort();
|
|
1086
|
+
const rightKeys = Object.keys(rightMap).sort();
|
|
1087
|
+
if (leftKeys.length !== rightKeys.length) {
|
|
1088
|
+
return false;
|
|
1089
|
+
}
|
|
1090
|
+
return leftKeys.every((key) => leftMap[key] === rightMap[key]);
|
|
1091
|
+
}
|
|
1092
|
+
/** Lookup/select filter-mode payload: `{ value, displayText?, operation }`. */
|
|
1093
|
+
function isFilterFormShape(value) {
|
|
1094
|
+
if (!isPlainObject(value)) {
|
|
1095
|
+
return false;
|
|
1096
|
+
}
|
|
1097
|
+
const record = value;
|
|
1098
|
+
return 'value' in record && 'operation' in record;
|
|
1099
|
+
}
|
|
1100
|
+
/**
|
|
1101
|
+
* Compares filter-mode widget values by selection identity; ignores derived `displayText`.
|
|
1102
|
+
*/
|
|
1103
|
+
function compareFilterFormValues(a, b) {
|
|
1104
|
+
if (!isFilterFormShape(a) || !isFilterFormShape(b)) {
|
|
1105
|
+
return null;
|
|
1106
|
+
}
|
|
1107
|
+
const left = a;
|
|
1108
|
+
const right = b;
|
|
1109
|
+
if (!isFormValueEqual(left['value'], right['value'])) {
|
|
1110
|
+
return false;
|
|
1111
|
+
}
|
|
1112
|
+
const opLeft = get(left, 'operation.type');
|
|
1113
|
+
const opRight = get(right, 'operation.type');
|
|
1114
|
+
if (opLeft !== undefined || opRight !== undefined) {
|
|
1115
|
+
return opLeft === opRight;
|
|
1116
|
+
}
|
|
1117
|
+
return true;
|
|
1118
|
+
}
|
|
1119
|
+
function isReferenceScalar(value) {
|
|
1120
|
+
return typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean';
|
|
1121
|
+
}
|
|
1122
|
+
/** Scalar id or `{ id }` identity used by lookup/select widgets. */
|
|
1123
|
+
function getReferenceId(value) {
|
|
1124
|
+
if (isNil(value)) {
|
|
1125
|
+
return undefined;
|
|
1126
|
+
}
|
|
1127
|
+
if (isReferenceScalar(value)) {
|
|
1128
|
+
return value;
|
|
1129
|
+
}
|
|
1130
|
+
if (isPlainObject(value)) {
|
|
1131
|
+
const id = get(value, 'id');
|
|
1132
|
+
if (!isNil(id)) {
|
|
1133
|
+
return id;
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
return undefined;
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
* Compares scalar selection ids to full lookup/select items (`5` vs `{ id: 5, ... }`).
|
|
1140
|
+
* Does not apply when both sides are objects — those use structural compare so field edits stay dirty.
|
|
1141
|
+
*/
|
|
1142
|
+
function compareReferenceFormValues(a, b) {
|
|
1143
|
+
const refA = getReferenceId(a);
|
|
1144
|
+
const refB = getReferenceId(b);
|
|
1145
|
+
if (refA === undefined || refB === undefined) {
|
|
1146
|
+
return null;
|
|
1147
|
+
}
|
|
1148
|
+
const aIsScalar = isReferenceScalar(a);
|
|
1149
|
+
const bIsScalar = isReferenceScalar(b);
|
|
1150
|
+
if (!aIsScalar && !bIsScalar) {
|
|
1151
|
+
return null;
|
|
1152
|
+
}
|
|
1153
|
+
return isEqual(normalizeFormValue(refA), normalizeFormValue(refB));
|
|
1154
|
+
}
|
|
1155
|
+
/**
|
|
1156
|
+
* Semantic equality for lookup/select values when restoring saved baseline shape.
|
|
1157
|
+
* Compares by selection identity (id), including two full items with the same id.
|
|
1158
|
+
*/
|
|
1159
|
+
function isSelectionValueEqual(a, b) {
|
|
1160
|
+
const filterEqual = compareFilterFormValues(a, b);
|
|
1161
|
+
if (filterEqual !== null) {
|
|
1162
|
+
return filterEqual;
|
|
1163
|
+
}
|
|
1164
|
+
const refA = getReferenceId(a);
|
|
1165
|
+
const refB = getReferenceId(b);
|
|
1166
|
+
if (refA !== undefined && refB !== undefined) {
|
|
1167
|
+
return isEqual(normalizeFormValue(refA), normalizeFormValue(refB));
|
|
1168
|
+
}
|
|
1169
|
+
return isFormValueEqual(a, b);
|
|
1170
|
+
}
|
|
1171
|
+
/**
|
|
1172
|
+
* Stable sort key for order-insensitive array comparison (after normalization).
|
|
1173
|
+
*/
|
|
1174
|
+
function formValueSortKey(value) {
|
|
1175
|
+
if (value === null) {
|
|
1176
|
+
return '\0';
|
|
1177
|
+
}
|
|
1178
|
+
if (typeof value === 'number') {
|
|
1179
|
+
return `n:${value}`;
|
|
1180
|
+
}
|
|
1181
|
+
if (typeof value === 'boolean') {
|
|
1182
|
+
return `b:${value}`;
|
|
1183
|
+
}
|
|
1184
|
+
if (typeof value === 'string') {
|
|
1185
|
+
return `s:${value}`;
|
|
1186
|
+
}
|
|
1187
|
+
try {
|
|
1188
|
+
return `j:${JSON.stringify(value)}`;
|
|
1189
|
+
}
|
|
1190
|
+
catch {
|
|
1191
|
+
return `u:${String(value)}`;
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
/**
|
|
1195
|
+
* Sorts normalized array items so order does not affect dirty checks.
|
|
1196
|
+
*/
|
|
1197
|
+
function sortNormalizedArrayItems(items) {
|
|
1198
|
+
return [...items].sort((a, b) => formValueSortKey(a).localeCompare(formValueSortKey(b)));
|
|
1199
|
+
}
|
|
1200
|
+
/**
|
|
1201
|
+
* Normalizes a form/context value for semantic baseline comparison.
|
|
1202
|
+
*
|
|
1203
|
+
* Rules:
|
|
1204
|
+
* - `null`, `undefined`, whitespace-only strings, and empty arrays → `null` (empty)
|
|
1205
|
+
* - Numeric strings → numbers (`"5"` and `5` match)
|
|
1206
|
+
* - Arrays → elements normalized; empty/all-empty → `null`; otherwise sorted (order ignored)
|
|
1207
|
+
* - Plain objects → keys sorted; `null`/empty nested values omitted
|
|
1208
|
+
* - `Date` → epoch milliseconds
|
|
1209
|
+
*/
|
|
1210
|
+
function normalizeFormValue(value) {
|
|
1211
|
+
if (value === undefined || value === null) {
|
|
1212
|
+
return null;
|
|
1213
|
+
}
|
|
1214
|
+
if (typeof value === 'string') {
|
|
1215
|
+
const trimmed = value.trim();
|
|
1216
|
+
if (trimmed === '') {
|
|
1217
|
+
return null;
|
|
1218
|
+
}
|
|
1219
|
+
if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
|
|
1220
|
+
const num = Number(trimmed);
|
|
1221
|
+
if (!Number.isNaN(num)) {
|
|
1222
|
+
return num;
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
return trimmed;
|
|
1226
|
+
}
|
|
1227
|
+
if (typeof value === 'number') {
|
|
1228
|
+
return Number.isNaN(value) ? null : value;
|
|
1229
|
+
}
|
|
1230
|
+
if (typeof value === 'boolean') {
|
|
1231
|
+
return value;
|
|
1232
|
+
}
|
|
1233
|
+
if (value instanceof Date) {
|
|
1234
|
+
return value.getTime();
|
|
1235
|
+
}
|
|
1236
|
+
if (isArray(value)) {
|
|
1237
|
+
const normalizedItems = value
|
|
1238
|
+
.map((item) => normalizeFormValue(item))
|
|
1239
|
+
.filter((item) => item !== null);
|
|
1240
|
+
if (normalizedItems.length === 0) {
|
|
1241
|
+
return null;
|
|
1242
|
+
}
|
|
1243
|
+
return sortNormalizedArrayItems(normalizedItems);
|
|
1244
|
+
}
|
|
1245
|
+
if (isPlainObject(value)) {
|
|
1246
|
+
const record = value;
|
|
1247
|
+
if (isLocaleStringMap(record)) {
|
|
1248
|
+
return normalizeLocaleStringMap(record);
|
|
1249
|
+
}
|
|
1250
|
+
const normalized = {};
|
|
1251
|
+
for (const key of Object.keys(record).sort()) {
|
|
1252
|
+
const next = normalizeFormValue(record[key]);
|
|
1253
|
+
if (next !== null) {
|
|
1254
|
+
normalized[key] = next;
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
return normalized;
|
|
1258
|
+
}
|
|
1259
|
+
return value;
|
|
1260
|
+
}
|
|
1261
|
+
/**
|
|
1262
|
+
* Semantic equality for dirty-state and baseline checks.
|
|
1263
|
+
* Prefer over lodash `isEqual` when comparing user-edited form/context data to a saved baseline.
|
|
1264
|
+
*
|
|
1265
|
+
* Arrays: `[]`, `null`, and all-empty lists are equivalent; non-empty arrays compare as multisets (order ignored).
|
|
1266
|
+
*/
|
|
1267
|
+
function isFormValueEqual(a, b) {
|
|
1268
|
+
const multiLanguageEqual = compareMultiLanguageFormValues(a, b);
|
|
1269
|
+
if (multiLanguageEqual !== null) {
|
|
1270
|
+
return multiLanguageEqual;
|
|
1271
|
+
}
|
|
1272
|
+
const filterEqual = compareFilterFormValues(a, b);
|
|
1273
|
+
if (filterEqual !== null) {
|
|
1274
|
+
return filterEqual;
|
|
1275
|
+
}
|
|
1276
|
+
const referenceEqual = compareReferenceFormValues(a, b);
|
|
1277
|
+
if (referenceEqual !== null) {
|
|
1278
|
+
return referenceEqual;
|
|
1279
|
+
}
|
|
1280
|
+
if (isArray(a) && isArray(b)) {
|
|
1281
|
+
// Empty repeater rows normalize away; raw length must differ before multiset compare.
|
|
1282
|
+
if (a.length !== b.length) {
|
|
1283
|
+
return false;
|
|
1284
|
+
}
|
|
1285
|
+
return isEqual(normalizeFormValue(a), normalizeFormValue(b));
|
|
1286
|
+
}
|
|
1287
|
+
if (isPlainObject(a) && isPlainObject(b)) {
|
|
1288
|
+
const left = a;
|
|
1289
|
+
const right = b;
|
|
1290
|
+
const keys = new Set([...Object.keys(left), ...Object.keys(right)]);
|
|
1291
|
+
for (const key of keys) {
|
|
1292
|
+
if (!isFormValueEqual(left[key], right[key])) {
|
|
1293
|
+
return false;
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
return true;
|
|
1297
|
+
}
|
|
1298
|
+
return isEqual(normalizeFormValue(a), normalizeFormValue(b));
|
|
1299
|
+
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Returns whether `current` differs from the saved/clean `baseline` snapshot.
|
|
1302
|
+
*/
|
|
1303
|
+
function isFormContextDirty(current, baseline) {
|
|
1304
|
+
return !isFormValueEqual(current, baseline);
|
|
1305
|
+
}
|
|
1306
|
+
/**
|
|
1307
|
+
* Clones a context object for use as a dirty-tracking baseline.
|
|
1308
|
+
*/
|
|
1309
|
+
function captureFormContextBaseline(context) {
|
|
1310
|
+
return cloneDeep(context);
|
|
1311
|
+
}
|
|
1312
|
+
//#endregion
|
|
1313
|
+
|
|
1314
|
+
class AXPContextChangeEvent {
|
|
1315
|
+
}
|
|
1316
|
+
//#endregion
|
|
1317
|
+
//#region ---- Context signal store ----
|
|
1318
|
+
const AXPContextStore = signalStore(withState(() => ({
|
|
1319
|
+
data: {},
|
|
1320
|
+
state: 'initiated',
|
|
1321
|
+
/** Last committed / saved baseline — discard reverts to this snapshot. */
|
|
1322
|
+
savedSnapshot: {},
|
|
1323
|
+
previousSnapshot: {},
|
|
1324
|
+
lastChange: {
|
|
1325
|
+
state: 'initiated',
|
|
1326
|
+
},
|
|
1327
|
+
})), withComputed(({ data, state, lastChange, savedSnapshot, previousSnapshot }) => ({
|
|
1328
|
+
isChanged: computed(() => state() === 'changed'),
|
|
1329
|
+
isReset: computed(() => state() === 'restored'),
|
|
1330
|
+
isInitiated: computed(() => state() === 'initiated'),
|
|
1331
|
+
isEmpty: computed(() => Object.keys(data()).length === 0),
|
|
1332
|
+
isSavedCommitted: computed(() => !isEmpty(savedSnapshot())),
|
|
1333
|
+
isDirty: computed(() => {
|
|
1334
|
+
const saved = savedSnapshot();
|
|
1335
|
+
if (isEmpty(saved)) {
|
|
1336
|
+
return false;
|
|
1337
|
+
}
|
|
1338
|
+
return isFormContextDirty(data(), saved);
|
|
1339
|
+
}),
|
|
1340
|
+
snapshot: computed(() => cloneDeep(data())),
|
|
1341
|
+
saved: computed(() => cloneDeep(savedSnapshot())),
|
|
1342
|
+
/** @deprecated Use {@link saved} — only saved baseline is tracked. */
|
|
1343
|
+
initial: computed(() => cloneDeep(savedSnapshot())),
|
|
1344
|
+
previous: computed(() => cloneDeep(previousSnapshot())),
|
|
1345
|
+
changeEvent: computed(() => lastChange()),
|
|
1346
|
+
})), withMethods((store) => {
|
|
1347
|
+
const updateValue = (path, value, options) => {
|
|
1348
|
+
const currentData = cloneDeep(store.data());
|
|
1349
|
+
const oldValue = getSmart(currentData, path);
|
|
1350
|
+
const hasSaved = !isEmpty(store.savedSnapshot());
|
|
1351
|
+
const savedAtPath = hasSaved ? getSmart(store.savedSnapshot(), path) : undefined;
|
|
1352
|
+
if (hasSaved && isSelectionValueEqual(value, savedAtPath) && !isEqual(value, savedAtPath)) {
|
|
1353
|
+
value = cloneDeep(savedAtPath);
|
|
1354
|
+
}
|
|
1355
|
+
const isArrayLengthChange = Array.isArray(value) && (!Array.isArray(oldValue) || value.length !== oldValue.length);
|
|
1356
|
+
if (!isArrayLengthChange && isEqual(oldValue, value)) {
|
|
1357
|
+
return;
|
|
1358
|
+
}
|
|
1359
|
+
const shouldNormalizeSavedShape = hasSaved && isSelectionValueEqual(value, savedAtPath) && !isEqual(oldValue, value);
|
|
1360
|
+
if (!isArrayLengthChange && isFormValueEqual(oldValue, value) && !shouldNormalizeSavedShape) {
|
|
1361
|
+
return;
|
|
1362
|
+
}
|
|
1363
|
+
const origin = options?.origin ?? 'system';
|
|
1364
|
+
const updatedData = setSmart(currentData, path, value);
|
|
1365
|
+
const changeEvent = {
|
|
1366
|
+
oldValue,
|
|
1367
|
+
newValue: value,
|
|
1368
|
+
path,
|
|
1369
|
+
state: 'changed',
|
|
1370
|
+
data: updatedData,
|
|
1371
|
+
origin,
|
|
1372
|
+
};
|
|
1373
|
+
patchState(store, {
|
|
1374
|
+
previousSnapshot: store.snapshot(),
|
|
1375
|
+
data: updatedData,
|
|
1376
|
+
state: 'changed',
|
|
1377
|
+
lastChange: changeEvent,
|
|
1378
|
+
});
|
|
1379
|
+
};
|
|
1380
|
+
const applyObjectPaths = (obj, options, prefix = '') => {
|
|
1381
|
+
for (const [key, value] of Object.entries(obj ?? {})) {
|
|
1382
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
1383
|
+
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
1384
|
+
applyObjectPaths(value, options, path);
|
|
1385
|
+
}
|
|
1386
|
+
else {
|
|
1387
|
+
updateValue(path, value, options);
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
};
|
|
1391
|
+
const revertToSaved = () => {
|
|
1392
|
+
const savedData = store.saved();
|
|
1393
|
+
const changeEvent = {
|
|
1394
|
+
oldValue: cloneDeep(store.data()),
|
|
1395
|
+
newValue: cloneDeep(savedData),
|
|
1396
|
+
path: '',
|
|
1397
|
+
state: 'restored',
|
|
1398
|
+
data: savedData,
|
|
1399
|
+
};
|
|
1400
|
+
patchState(store, {
|
|
1401
|
+
previousSnapshot: store.snapshot(),
|
|
1402
|
+
data: savedData,
|
|
1403
|
+
state: 'restored',
|
|
1404
|
+
lastChange: changeEvent,
|
|
1405
|
+
});
|
|
1406
|
+
};
|
|
1407
|
+
return {
|
|
1408
|
+
update(path, value, options) {
|
|
1409
|
+
updateValue(path, value, options);
|
|
1410
|
+
},
|
|
1411
|
+
applyObjectPaths,
|
|
1412
|
+
patch(context, options) {
|
|
1413
|
+
const currentData = cloneDeep(store.data());
|
|
1414
|
+
const updatedData = { ...currentData, ...context };
|
|
1415
|
+
const changeEvent = {
|
|
1416
|
+
state: 'patch',
|
|
1417
|
+
data: updatedData,
|
|
1418
|
+
};
|
|
1419
|
+
const syncedSnapshot = cloneDeep(updatedData);
|
|
1420
|
+
patchState(store, {
|
|
1421
|
+
...(options?.updateSaved
|
|
1422
|
+
? { savedSnapshot: syncedSnapshot, previousSnapshot: syncedSnapshot }
|
|
1423
|
+
: { previousSnapshot: store.snapshot() }),
|
|
1424
|
+
data: updatedData,
|
|
1425
|
+
state: 'changed',
|
|
1426
|
+
lastChange: changeEvent,
|
|
1427
|
+
});
|
|
1428
|
+
},
|
|
1429
|
+
/** Reverts live data to the last saved snapshot. */
|
|
1430
|
+
revertToSaved,
|
|
1431
|
+
reset() {
|
|
1432
|
+
revertToSaved();
|
|
1433
|
+
},
|
|
1434
|
+
/** Loads live data; saved baseline is committed separately via {@link commitSaved}. */
|
|
1435
|
+
set(initialData) {
|
|
1436
|
+
const currentData = store.data();
|
|
1437
|
+
if (isFormValueEqual(currentData, initialData)) {
|
|
1438
|
+
return;
|
|
1439
|
+
}
|
|
1440
|
+
const changeEvent = {
|
|
1441
|
+
oldValue: null,
|
|
1442
|
+
newValue: cloneDeep(initialData),
|
|
1443
|
+
path: '',
|
|
1444
|
+
state: 'initiated',
|
|
1445
|
+
data: initialData,
|
|
1446
|
+
};
|
|
1447
|
+
patchState(store, {
|
|
1448
|
+
previousSnapshot: store.snapshot(),
|
|
1449
|
+
data: initialData,
|
|
1450
|
+
state: 'initiated',
|
|
1451
|
+
lastChange: changeEvent,
|
|
1452
|
+
});
|
|
1453
|
+
},
|
|
1454
|
+
getValue(path) {
|
|
1455
|
+
return getSmart(store.data(), path);
|
|
1456
|
+
},
|
|
1457
|
+
getSavedValue(path) {
|
|
1458
|
+
return getSmart(store.savedSnapshot(), path);
|
|
1459
|
+
},
|
|
1460
|
+
hasValue(path) {
|
|
1461
|
+
return has(store.data(), path);
|
|
1462
|
+
},
|
|
1463
|
+
/** Marks the current data as the saved baseline. */
|
|
1464
|
+
commitSaved() {
|
|
1465
|
+
const snapshot = cloneDeep(store.data());
|
|
1466
|
+
patchState(store, {
|
|
1467
|
+
savedSnapshot: snapshot,
|
|
1468
|
+
previousSnapshot: snapshot,
|
|
1469
|
+
state: 'initiated',
|
|
1470
|
+
lastChange: {
|
|
1471
|
+
state: 'initiated',
|
|
1472
|
+
data: snapshot,
|
|
1473
|
+
},
|
|
1474
|
+
});
|
|
1475
|
+
},
|
|
1476
|
+
/** Merges parent-bound entity fields without resetting the saved baseline. */
|
|
1477
|
+
applyParentBind(merged) {
|
|
1478
|
+
if (isFormValueEqual(store.data(), merged)) {
|
|
1479
|
+
return;
|
|
1480
|
+
}
|
|
1481
|
+
patchState(store, {
|
|
1482
|
+
data: merged,
|
|
1483
|
+
});
|
|
1484
|
+
},
|
|
1485
|
+
};
|
|
1486
|
+
}));
|
|
1487
|
+
//#endregion
|
|
1488
|
+
|
|
1489
|
+
class AXPExpressionEvaluatorScopeProviderContext {
|
|
1490
|
+
constructor() {
|
|
1491
|
+
this.scopes = {};
|
|
1492
|
+
}
|
|
1493
|
+
addScope(namespace, functions) {
|
|
1494
|
+
this.scopes[namespace] = { ...this.scopes[namespace], ...functions };
|
|
1495
|
+
}
|
|
1496
|
+
getScopes() {
|
|
1497
|
+
return this.scopes;
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
const AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER = new InjectionToken('AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER');
|
|
1501
|
+
class AXPExpressionEvaluatorScopeProviderService {
|
|
1502
|
+
constructor() {
|
|
1503
|
+
this.injector = inject(Injector);
|
|
1504
|
+
this.cache = null;
|
|
1505
|
+
}
|
|
1506
|
+
async load() {
|
|
1507
|
+
if (this.cache)
|
|
1508
|
+
return;
|
|
1509
|
+
const raw = this.injector.get(AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER, [], { optional: true });
|
|
1510
|
+
const providers = Array.isArray(raw)
|
|
1511
|
+
? raw
|
|
1512
|
+
: typeof raw === 'function'
|
|
1513
|
+
? raw()
|
|
1514
|
+
: [];
|
|
1515
|
+
const context = new AXPExpressionEvaluatorScopeProviderContext();
|
|
1516
|
+
for (const provider of providers) {
|
|
1517
|
+
// Resolve lazy-loaded providers (provideLazyProvider useFactory returns Promise<Provider>)
|
|
1518
|
+
const resolved = await Promise.resolve(provider);
|
|
1519
|
+
if (!resolved || typeof resolved.provide !== 'function') {
|
|
1520
|
+
continue;
|
|
1521
|
+
}
|
|
1522
|
+
await resolved.provide(context);
|
|
1523
|
+
}
|
|
1524
|
+
this.cache = context.getScopes();
|
|
1525
|
+
}
|
|
1526
|
+
async getScopesAsync() {
|
|
1527
|
+
await this.load();
|
|
1528
|
+
return this.cache || {};
|
|
1529
|
+
}
|
|
1530
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPExpressionEvaluatorScopeProviderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1531
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPExpressionEvaluatorScopeProviderService, providedIn: 'root' }); }
|
|
1532
|
+
}
|
|
1533
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPExpressionEvaluatorScopeProviderService, decorators: [{
|
|
1534
|
+
type: Injectable,
|
|
1535
|
+
args: [{ providedIn: 'root' }]
|
|
1536
|
+
}] });
|
|
1537
|
+
|
|
1538
|
+
class AXPExpressionEvaluatorService {
|
|
1539
|
+
constructor() {
|
|
1540
|
+
// Memoization cache for compiled expressions
|
|
1541
|
+
this.expressionCache = new Map();
|
|
1542
|
+
this.providerService = inject(AXPExpressionEvaluatorScopeProviderService);
|
|
1543
|
+
}
|
|
1544
|
+
getOrCompileFunction(expression) {
|
|
1545
|
+
if (!this.expressionCache.has(expression)) {
|
|
1546
|
+
// Check if expression contains multiple statements (has semicolons or newlines)
|
|
1547
|
+
const hasMultipleStatements = expression.includes(';') || expression.includes('\n');
|
|
1548
|
+
let fn;
|
|
1549
|
+
if (hasMultipleStatements) {
|
|
1550
|
+
// For multiple statements, execute them in sequence and return the last expression
|
|
1551
|
+
fn = new Function('scope', `with (scope) { return (async function() { ${expression} })(); }`);
|
|
1552
|
+
}
|
|
1553
|
+
else {
|
|
1554
|
+
// For single expressions, use return
|
|
1555
|
+
fn = new Function('scope', `with (scope) { return (async function() { return ${expression}; })(); }`);
|
|
1556
|
+
}
|
|
1557
|
+
this.expressionCache.set(expression, fn);
|
|
1558
|
+
}
|
|
1559
|
+
return this.expressionCache.get(expression);
|
|
1560
|
+
}
|
|
1561
|
+
async getMergedScope(userScope) {
|
|
1562
|
+
const pluginScopes = await this.providerService.getScopesAsync();
|
|
1563
|
+
// Merge pluginScopes and userScope (userScope takes precedence)
|
|
1564
|
+
return { ...pluginScopes, ...userScope };
|
|
1565
|
+
}
|
|
1566
|
+
async evaluate(source, scope = {}) {
|
|
1567
|
+
try {
|
|
1568
|
+
const mergedScope = await this.getMergedScope(scope);
|
|
1569
|
+
if (typeof source === 'string' && source.includes('{{')) {
|
|
1570
|
+
return await this.evaluateStringExpression(source, mergedScope);
|
|
1571
|
+
}
|
|
1572
|
+
else if (Array.isArray(source)) {
|
|
1573
|
+
const evaluatedArray = [];
|
|
1574
|
+
for (const item of source) {
|
|
1575
|
+
evaluatedArray.push(await this.evaluate(item, mergedScope));
|
|
1576
|
+
}
|
|
1577
|
+
return evaluatedArray;
|
|
1578
|
+
}
|
|
1579
|
+
else if (typeof source === 'object' && source !== null) {
|
|
1580
|
+
const evaluatedObject = {};
|
|
1581
|
+
for (const key in source) {
|
|
1582
|
+
if (source.hasOwnProperty(key)) {
|
|
1583
|
+
evaluatedObject[key] = await this.evaluate(source[key], mergedScope);
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
return evaluatedObject;
|
|
1587
|
+
}
|
|
1588
|
+
else {
|
|
1589
|
+
return source;
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
catch (error) {
|
|
1593
|
+
console.error('Expression evaluator - Error evaluating expression:', source, error);
|
|
1594
|
+
return false;
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
async evaluateStringExpression(templateExpression, scope) {
|
|
1598
|
+
// Check if the input is exactly a single {{ ... }} expression (handle multiline)
|
|
1599
|
+
const exactMatch = templateExpression.match(/^\s*\{\{\s*([\s\S]*?)\s*\}\}\s*$/);
|
|
1600
|
+
if (exactMatch) {
|
|
1601
|
+
const expression = exactMatch[1];
|
|
1602
|
+
const sandbox = this.getOrCompileFunction(expression);
|
|
1603
|
+
const result = await sandbox(scope);
|
|
1604
|
+
return result;
|
|
1605
|
+
}
|
|
1606
|
+
// Otherwise, interpolate all {{ ... }} expressions in the string
|
|
1607
|
+
const regex = /\{\{\s*([\s\S]*?)\s*\}\}/g;
|
|
1608
|
+
// Collect all matches and their positions
|
|
1609
|
+
const matches = [];
|
|
1610
|
+
let match;
|
|
1611
|
+
while ((match = regex.exec(templateExpression)) !== null) {
|
|
1612
|
+
matches.push({
|
|
1613
|
+
expression: match[1],
|
|
1614
|
+
start: match.index,
|
|
1615
|
+
end: regex.lastIndex,
|
|
1616
|
+
raw: match[0],
|
|
1617
|
+
});
|
|
1618
|
+
}
|
|
1619
|
+
// Evaluate all expressions in parallel
|
|
1620
|
+
const values = await Promise.all(matches.map((m) => {
|
|
1621
|
+
const sandbox = this.getOrCompileFunction(m.expression);
|
|
1622
|
+
return sandbox(scope);
|
|
1623
|
+
}));
|
|
1624
|
+
// Reconstruct the string with evaluated values
|
|
1625
|
+
let result = '';
|
|
1626
|
+
let lastIndex = 0;
|
|
1627
|
+
matches.forEach((m, i) => {
|
|
1628
|
+
result += templateExpression.slice(lastIndex, m.start);
|
|
1629
|
+
const value = values[i];
|
|
1630
|
+
result += value !== undefined && value !== null ? value : '';
|
|
1631
|
+
lastIndex = m.end;
|
|
1632
|
+
});
|
|
1633
|
+
result += templateExpression.slice(lastIndex);
|
|
1634
|
+
return result;
|
|
1635
|
+
}
|
|
1636
|
+
isExpression(expression) {
|
|
1637
|
+
if (typeof expression === 'string') {
|
|
1638
|
+
return expression.trim().startsWith('{{') && expression.trim().endsWith('}}');
|
|
1639
|
+
}
|
|
1640
|
+
return false;
|
|
1641
|
+
}
|
|
1642
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPExpressionEvaluatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1643
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPExpressionEvaluatorService, providedIn: 'root' }); }
|
|
1644
|
+
}
|
|
1645
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPExpressionEvaluatorService, decorators: [{
|
|
1646
|
+
type: Injectable,
|
|
1647
|
+
args: [{ providedIn: 'root' }]
|
|
1648
|
+
}] });
|
|
1649
|
+
|
|
1650
|
+
class AXPComponentSlotPlaceholderComponent {
|
|
1651
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPComponentSlotPlaceholderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1652
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.9", type: AXPComponentSlotPlaceholderComponent, isStandalone: true, selector: "axp-component-slot-placeholder", ngImport: i0, template: `<div>
|
|
1653
|
+
<ax-skeleton class="ax-w-full ax-h-10 ax-rounded-md"></ax-skeleton>
|
|
1654
|
+
</div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i1.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1655
|
+
}
|
|
1656
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPComponentSlotPlaceholderComponent, decorators: [{
|
|
1657
|
+
type: Component,
|
|
1658
|
+
args: [{
|
|
1659
|
+
selector: 'axp-component-slot-placeholder',
|
|
1660
|
+
template: `<div>
|
|
1661
|
+
<ax-skeleton class="ax-w-full ax-h-10 ax-rounded-md"></ax-skeleton>
|
|
1662
|
+
</div>`,
|
|
1663
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1664
|
+
imports: [AXSkeletonModule],
|
|
1665
|
+
standalone: true,
|
|
1666
|
+
}]
|
|
1667
|
+
}] });
|
|
1668
|
+
|
|
1669
|
+
class AXPComponentSlotRegistryService {
|
|
1670
|
+
constructor() {
|
|
1671
|
+
this.registry = new Map();
|
|
1672
|
+
}
|
|
1673
|
+
register(slotName, config) {
|
|
1674
|
+
let configs = this.registry.get(slotName) || [];
|
|
1675
|
+
// Check if the component is already registered in this slot
|
|
1676
|
+
const isDuplicate = configs.some(existingConfig => existingConfig.name === config.name);
|
|
1677
|
+
if (!isDuplicate) {
|
|
1678
|
+
configs = [...configs, config]; // Add the new configuration
|
|
1679
|
+
this.registry.set(slotName, configs);
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
get(slotName) {
|
|
1683
|
+
return this.registry.get(slotName) || [];
|
|
1684
|
+
}
|
|
1685
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPComponentSlotRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1686
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPComponentSlotRegistryService, providedIn: 'root' }); }
|
|
1687
|
+
}
|
|
1688
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPComponentSlotRegistryService, decorators: [{
|
|
1689
|
+
type: Injectable,
|
|
1690
|
+
args: [{
|
|
1691
|
+
providedIn: 'root'
|
|
1692
|
+
}]
|
|
1693
|
+
}] });
|
|
1694
|
+
|
|
1695
|
+
class AXPComponentSlotDirective {
|
|
1696
|
+
constructor() {
|
|
1697
|
+
this.name = input.required(...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
|
|
1698
|
+
this.host = input(...(ngDevMode ? [undefined, { debugName: "host" }] : /* istanbul ignore next */ []));
|
|
1699
|
+
this.context = input(...(ngDevMode ? [undefined, { debugName: "context" }] : /* istanbul ignore next */ []));
|
|
1700
|
+
this.elementRef = inject(ElementRef);
|
|
1701
|
+
this.registryService = inject(AXPComponentSlotRegistryService);
|
|
1702
|
+
this.injector = inject(Injector);
|
|
1703
|
+
this.evaluator = inject(AXPExpressionEvaluatorService);
|
|
1704
|
+
this.viewContainerRef = inject(ViewContainerRef);
|
|
1705
|
+
this.contextStore = inject(AXPContextStore);
|
|
1706
|
+
this.isEmpty = computed(() => this._viewCount() === 0, ...(ngDevMode ? [{ debugName: "isEmpty" }] : /* istanbul ignore next */ []));
|
|
1707
|
+
// Create a signal to store the count of children
|
|
1708
|
+
this._viewCount = signal(0, ...(ngDevMode ? [{ debugName: "_viewCount" }] : /* istanbul ignore next */ []));
|
|
1709
|
+
// Track component references for updates
|
|
1710
|
+
this.componentRefs = [];
|
|
1711
|
+
// Track placeholder references for replacement
|
|
1712
|
+
this.placeholderRefs = new Map();
|
|
1713
|
+
// Watch for context changes and update component instances
|
|
1714
|
+
effect(() => {
|
|
1715
|
+
const currentContext = this.context();
|
|
1716
|
+
const currentHost = this.host();
|
|
1717
|
+
// Update all component references with new context and host
|
|
1718
|
+
this.componentRefs.forEach((componentRef) => {
|
|
1719
|
+
if (componentRef.instance) {
|
|
1720
|
+
Object.assign(componentRef.instance, {
|
|
1721
|
+
host: currentHost,
|
|
1722
|
+
context: currentContext,
|
|
1723
|
+
});
|
|
1724
|
+
}
|
|
1725
|
+
});
|
|
1726
|
+
});
|
|
1727
|
+
}
|
|
1728
|
+
async ngOnInit() {
|
|
1729
|
+
await this.loadComponents();
|
|
1730
|
+
// Update the signal after loading
|
|
1731
|
+
this._viewCount.set(this.viewContainerRef.length);
|
|
1732
|
+
}
|
|
1733
|
+
async loadComponents() {
|
|
1734
|
+
this.viewContainerRef.clear();
|
|
1735
|
+
// Clear previous component references
|
|
1736
|
+
this.componentRefs = [];
|
|
1737
|
+
this.placeholderRefs.clear();
|
|
1738
|
+
const configs = sortBy(this.registryService.get(this.name()), (c) => c.priority ?? 0);
|
|
1739
|
+
if (!configs) {
|
|
1740
|
+
console.error(`No component found for slot ${this.name()}`);
|
|
1741
|
+
return;
|
|
1742
|
+
}
|
|
1743
|
+
// Evaluate conditions and check features in parallel for performance
|
|
1744
|
+
const results = await Promise.all(configs.map(async (c) => {
|
|
1745
|
+
// Check condition if provided
|
|
1746
|
+
const conditionPassed = c.condition ? await this.evaluateCondition(c.condition) : true;
|
|
1747
|
+
// Check features if provided
|
|
1748
|
+
// const featuresPassed =
|
|
1749
|
+
// c.features && c.features.length > 0 ? this.sessionService.isFeatureEnabled(...c.features) : true;
|
|
1750
|
+
return {
|
|
1751
|
+
config: c,
|
|
1752
|
+
// visible: conditionPassed && featuresPassed,
|
|
1753
|
+
visible: conditionPassed,
|
|
1754
|
+
};
|
|
1755
|
+
}));
|
|
1756
|
+
// Filter visible components while preserving priority order
|
|
1757
|
+
const slots = results.filter((r) => r.visible).map((r) => r.config);
|
|
1758
|
+
// Create skeleton placeholders immediately in priority order
|
|
1759
|
+
slots.forEach((config, index) => {
|
|
1760
|
+
const placeholderRef = this.viewContainerRef.createComponent(AXPComponentSlotPlaceholderComponent);
|
|
1761
|
+
this.placeholderRefs.set(index, placeholderRef);
|
|
1762
|
+
});
|
|
1763
|
+
// Load all components in parallel and replace placeholders as they become ready
|
|
1764
|
+
const loadPromises = slots.map(async (config, index) => {
|
|
1765
|
+
let component;
|
|
1766
|
+
let options = {};
|
|
1767
|
+
// load component
|
|
1768
|
+
if (typeof config.loadComponent === 'function') {
|
|
1769
|
+
await runInInjectionContext(this.injector, async () => {
|
|
1770
|
+
component = await config.loadComponent?.();
|
|
1771
|
+
});
|
|
1772
|
+
}
|
|
1773
|
+
else if (config.component) {
|
|
1774
|
+
component = config.component;
|
|
1775
|
+
}
|
|
1776
|
+
// load options
|
|
1777
|
+
if (typeof config.options === 'function') {
|
|
1778
|
+
await runInInjectionContext(this.injector, async () => {
|
|
1779
|
+
const fun = config.options;
|
|
1780
|
+
options = await fun();
|
|
1781
|
+
});
|
|
1782
|
+
}
|
|
1783
|
+
else if (config.options) {
|
|
1784
|
+
options = await this.evaluator.evaluate(config.options, {});
|
|
1785
|
+
}
|
|
1786
|
+
// Replace placeholder with actual component as soon as it's ready
|
|
1787
|
+
if (!component) {
|
|
1788
|
+
console.warn(`Component failed to load for slot ${this.name()} at index ${index}`);
|
|
1789
|
+
// Remove placeholder if component failed to load
|
|
1790
|
+
const placeholderRef = this.placeholderRefs.get(index);
|
|
1791
|
+
if (placeholderRef) {
|
|
1792
|
+
placeholderRef.destroy();
|
|
1793
|
+
this.placeholderRefs.delete(index);
|
|
1794
|
+
}
|
|
1795
|
+
return;
|
|
1796
|
+
}
|
|
1797
|
+
// Get the placeholder reference at this index
|
|
1798
|
+
const placeholderRef = this.placeholderRefs.get(index);
|
|
1799
|
+
if (!placeholderRef) {
|
|
1800
|
+
console.warn(`Placeholder not found for index ${index}`);
|
|
1801
|
+
return;
|
|
1802
|
+
}
|
|
1803
|
+
// Get the index of the placeholder in the view container
|
|
1804
|
+
const placeholderIndex = this.viewContainerRef.indexOf(placeholderRef.hostView);
|
|
1805
|
+
// Remove the placeholder
|
|
1806
|
+
placeholderRef.destroy();
|
|
1807
|
+
this.placeholderRefs.delete(index);
|
|
1808
|
+
// Create the actual component at the same position
|
|
1809
|
+
const componentRef = this.viewContainerRef.createComponent(component, { index: placeholderIndex });
|
|
1810
|
+
// Store the component reference for future updates
|
|
1811
|
+
this.componentRefs.push(componentRef);
|
|
1812
|
+
Object.assign(componentRef.instance, {
|
|
1813
|
+
host: this.host(),
|
|
1814
|
+
context: this.context(),
|
|
1815
|
+
...options,
|
|
1816
|
+
});
|
|
1817
|
+
});
|
|
1818
|
+
// Wait for all components to finish loading (they render as they become ready)
|
|
1819
|
+
await Promise.all(loadPromises);
|
|
1820
|
+
}
|
|
1821
|
+
async evaluateCondition(condition) {
|
|
1822
|
+
if (typeof condition === 'string') {
|
|
1823
|
+
const result = await this.evaluator.evaluate(condition, {});
|
|
1824
|
+
return result;
|
|
1825
|
+
}
|
|
1826
|
+
return condition(this.contextStore.data());
|
|
1827
|
+
//return condition(this.context());
|
|
1828
|
+
}
|
|
1829
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPComponentSlotDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1830
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: AXPComponentSlotDirective, isStandalone: false, selector: "axp-component-slot", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, host: { classPropertyName: "host", publicName: "host", isSignal: true, isRequired: false, transformFunction: null }, context: { classPropertyName: "context", publicName: "context", isSignal: true, isRequired: false, transformFunction: null } }, exportAs: ["slot"], ngImport: i0 }); }
|
|
1831
|
+
}
|
|
1832
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPComponentSlotDirective, decorators: [{
|
|
1833
|
+
type: Directive,
|
|
1834
|
+
args: [{
|
|
1835
|
+
selector: 'axp-component-slot',
|
|
1836
|
+
standalone: false,
|
|
1837
|
+
exportAs: 'slot',
|
|
1838
|
+
}]
|
|
1839
|
+
}], ctorParameters: () => [], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], host: [{ type: i0.Input, args: [{ isSignal: true, alias: "host", required: false }] }], context: [{ type: i0.Input, args: [{ isSignal: true, alias: "context", required: false }] }] } });
|
|
1840
|
+
|
|
1841
|
+
class AXPComponentSlotModule {
|
|
1842
|
+
static forRoot(configs) {
|
|
1843
|
+
return {
|
|
1844
|
+
ngModule: AXPComponentSlotModule,
|
|
1845
|
+
providers: [
|
|
1846
|
+
{
|
|
1847
|
+
provide: 'AXPComponentSlotModuleFactory',
|
|
1848
|
+
useFactory: (registry) => () => {
|
|
1849
|
+
if (configs) {
|
|
1850
|
+
for (const [key, value] of Object.entries(configs)) {
|
|
1851
|
+
value.forEach(v => {
|
|
1852
|
+
registry.register(key, v);
|
|
1853
|
+
});
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
},
|
|
1857
|
+
deps: [AXPComponentSlotRegistryService],
|
|
1858
|
+
multi: true
|
|
1859
|
+
}
|
|
1860
|
+
]
|
|
1861
|
+
};
|
|
1862
|
+
}
|
|
1863
|
+
static forChild(configs) {
|
|
1864
|
+
return {
|
|
1865
|
+
ngModule: AXPComponentSlotModule,
|
|
1866
|
+
providers: [
|
|
1867
|
+
{
|
|
1868
|
+
provide: 'AXPComponentSlotModuleFactory',
|
|
1869
|
+
useFactory: (registry) => () => {
|
|
1870
|
+
if (configs) {
|
|
1871
|
+
for (const [key, value] of Object.entries(configs)) {
|
|
1872
|
+
value.forEach(v => {
|
|
1873
|
+
registry.register(key, v);
|
|
1874
|
+
});
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
},
|
|
1878
|
+
deps: [AXPComponentSlotRegistryService],
|
|
1879
|
+
multi: true
|
|
1880
|
+
}
|
|
1881
|
+
]
|
|
1882
|
+
};
|
|
1883
|
+
}
|
|
1884
|
+
/**
|
|
1885
|
+
* @ignore
|
|
1886
|
+
*/
|
|
1887
|
+
constructor(instances) {
|
|
1888
|
+
instances?.forEach(f => {
|
|
1889
|
+
f();
|
|
1890
|
+
});
|
|
1891
|
+
}
|
|
1892
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPComponentSlotModule, deps: [{ token: 'AXPComponentSlotModuleFactory', optional: true }], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
1893
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: AXPComponentSlotModule, declarations: [AXPComponentSlotDirective], exports: [AXPComponentSlotDirective] }); }
|
|
1894
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPComponentSlotModule }); }
|
|
1895
|
+
}
|
|
1896
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPComponentSlotModule, decorators: [{
|
|
1897
|
+
type: NgModule,
|
|
1898
|
+
args: [{
|
|
1899
|
+
declarations: [AXPComponentSlotDirective],
|
|
1900
|
+
exports: [AXPComponentSlotDirective]
|
|
1901
|
+
}]
|
|
1902
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
1903
|
+
type: Optional
|
|
1904
|
+
}, {
|
|
1905
|
+
type: Inject,
|
|
1906
|
+
args: ['AXPComponentSlotModuleFactory']
|
|
1907
|
+
}] }] });
|
|
1908
|
+
|
|
1909
|
+
//#region ---- AI metadata ----
|
|
1910
|
+
//#endregion
|
|
1911
|
+
|
|
1912
|
+
const AXP_CONTEXT_DEFINITION_PROVIDER = new InjectionToken('AXP_CONTEXT_DEFINITION_PROVIDER');
|
|
1913
|
+
//#endregion
|
|
1914
|
+
|
|
1915
|
+
//#region ---- Aggregator service ----
|
|
1916
|
+
/**
|
|
1917
|
+
* Collects {@link AXPContextDefinition} entries from all registered {@link AXPContextDefinitionProvider} instances.
|
|
1918
|
+
*/
|
|
1919
|
+
class AXPContextDefinitionProviderService {
|
|
1920
|
+
constructor() {
|
|
1921
|
+
//#region ---- Services & Dependencies ----
|
|
1922
|
+
this.providers = inject(AXP_CONTEXT_DEFINITION_PROVIDER, { optional: true });
|
|
1923
|
+
}
|
|
1924
|
+
//#endregion
|
|
1925
|
+
//#region ---- Public API ----
|
|
1926
|
+
async items() {
|
|
1927
|
+
const items = [];
|
|
1928
|
+
if (!Array.isArray(this.providers)) {
|
|
1929
|
+
return items;
|
|
1930
|
+
}
|
|
1931
|
+
for (const raw of this.providers) {
|
|
1932
|
+
const provider = await Promise.resolve(raw);
|
|
1933
|
+
if (provider && typeof provider.items === 'function') {
|
|
1934
|
+
items.push(...(await provider.items()));
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
return items;
|
|
1938
|
+
}
|
|
1939
|
+
async get(name) {
|
|
1940
|
+
return (await this.items()).find((d) => d.name === name);
|
|
1941
|
+
}
|
|
1942
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPContextDefinitionProviderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1943
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPContextDefinitionProviderService, providedIn: 'root' }); }
|
|
1944
|
+
}
|
|
1945
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPContextDefinitionProviderService, decorators: [{
|
|
1946
|
+
type: Injectable,
|
|
1947
|
+
args: [{ providedIn: 'root' }]
|
|
1948
|
+
}] });
|
|
1949
|
+
|
|
1950
|
+
class AXPDataGenerator {
|
|
1951
|
+
static uuid() {
|
|
1952
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (char) => {
|
|
1953
|
+
const random = (Math.random() * 16) | 0;
|
|
1954
|
+
const value = char === 'x' ? random : (random & 0x3) | 0x8;
|
|
1955
|
+
return value.toString(16);
|
|
1956
|
+
});
|
|
1957
|
+
}
|
|
1958
|
+
static number(...args) {
|
|
1959
|
+
let min = 0;
|
|
1960
|
+
let max = 100;
|
|
1961
|
+
if (args.length == 1)
|
|
1962
|
+
max = args[0];
|
|
1963
|
+
if (args.length == 2) {
|
|
1964
|
+
min = args[0];
|
|
1965
|
+
max = args[1];
|
|
1966
|
+
}
|
|
1967
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
1968
|
+
}
|
|
1969
|
+
static date(...args) {
|
|
1970
|
+
let start = new Date(2000, 0, 1);
|
|
1971
|
+
let end = new Date();
|
|
1972
|
+
if (args.length === 1) {
|
|
1973
|
+
start = args[0];
|
|
1974
|
+
}
|
|
1975
|
+
else if (args.length === 2) {
|
|
1976
|
+
start = args[0];
|
|
1977
|
+
end = args[1];
|
|
1978
|
+
}
|
|
1979
|
+
const startTime = start.getTime();
|
|
1980
|
+
const endTime = end.getTime();
|
|
671
1981
|
return new Date(startTime + Math.random() * (endTime - startTime));
|
|
672
1982
|
}
|
|
673
1983
|
static array(length = 5, generator) {
|
|
@@ -829,11 +2139,14 @@ class AXPDataSourceDefinitionProviderService {
|
|
|
829
2139
|
}
|
|
830
2140
|
async items() {
|
|
831
2141
|
const items = [];
|
|
832
|
-
// Load from DI tokens
|
|
2142
|
+
// Load from DI tokens; resolve lazy providers (provideLazyProvider uses useFactory that returns Promise<T>)
|
|
833
2143
|
if (Array.isArray(this.providers)) {
|
|
834
|
-
for (const
|
|
835
|
-
|
|
836
|
-
|
|
2144
|
+
for (const raw of this.providers) {
|
|
2145
|
+
const provider = await Promise.resolve(raw);
|
|
2146
|
+
if (provider && typeof provider.items === 'function') {
|
|
2147
|
+
set(provider, '__parent__', this);
|
|
2148
|
+
items.push(...(await provider.items()));
|
|
2149
|
+
}
|
|
837
2150
|
}
|
|
838
2151
|
}
|
|
839
2152
|
return items;
|
|
@@ -841,52 +2154,32 @@ class AXPDataSourceDefinitionProviderService {
|
|
|
841
2154
|
async get(name) {
|
|
842
2155
|
return (await this.items()).find((c) => c.name == name);
|
|
843
2156
|
}
|
|
844
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
845
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
2157
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDataSourceDefinitionProviderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2158
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDataSourceDefinitionProviderService, providedIn: 'root' }); }
|
|
846
2159
|
}
|
|
847
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
2160
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDataSourceDefinitionProviderService, decorators: [{
|
|
848
2161
|
type: Injectable,
|
|
849
2162
|
args: [{ providedIn: 'root' }]
|
|
850
2163
|
}] });
|
|
851
2164
|
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
this.
|
|
856
|
-
this.
|
|
857
|
-
|
|
858
|
-
this.updateEffect = effect(() => {
|
|
859
|
-
const currentData = this.data();
|
|
860
|
-
const currentPath = this.path();
|
|
861
|
-
set(this.elementRef.nativeElement, currentPath, currentData);
|
|
862
|
-
}, ...(ngDevMode ? [{ debugName: "updateEffect" }] : []));
|
|
863
|
-
}
|
|
864
|
-
ngOnInit() {
|
|
865
|
-
// Initial data setup
|
|
866
|
-
set(this.elementRef.nativeElement, this.path(), this.data());
|
|
867
|
-
}
|
|
868
|
-
/**
|
|
869
|
-
* Get data from the element
|
|
870
|
-
*/
|
|
871
|
-
getData() {
|
|
872
|
-
return get(this.elementRef.nativeElement, this.path());
|
|
873
|
-
}
|
|
874
|
-
/**
|
|
875
|
-
* Update data at runtime
|
|
876
|
-
*/
|
|
877
|
-
setData(data) {
|
|
878
|
-
set(this.elementRef.nativeElement, this.path(), data);
|
|
2165
|
+
// src/app/directives/grid-layout.directive.ts
|
|
2166
|
+
class AXPContentCheckerDirective {
|
|
2167
|
+
constructor() {
|
|
2168
|
+
this.viewContainerRef = inject(ViewContainerRef);
|
|
2169
|
+
this.elementRef = inject((ElementRef));
|
|
2170
|
+
this.isEmpty = computed(() => this.viewContainerRef.length === 0, ...(ngDevMode ? [{ debugName: "isEmpty" }] : /* istanbul ignore next */ []));
|
|
879
2171
|
}
|
|
880
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
881
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "
|
|
2172
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPContentCheckerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2173
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: AXPContentCheckerDirective, isStandalone: true, selector: "[axp-content-checker]", exportAs: ["checker"], ngImport: i0 }); }
|
|
882
2174
|
}
|
|
883
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
2175
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPContentCheckerDirective, decorators: [{
|
|
884
2176
|
type: Directive,
|
|
885
2177
|
args: [{
|
|
886
|
-
selector: '[axp-
|
|
887
|
-
standalone: true
|
|
2178
|
+
selector: '[axp-content-checker]',
|
|
2179
|
+
standalone: true,
|
|
2180
|
+
exportAs: 'checker'
|
|
888
2181
|
}]
|
|
889
|
-
}]
|
|
2182
|
+
}] });
|
|
890
2183
|
|
|
891
2184
|
class AXPDblClickDirective {
|
|
892
2185
|
constructor() {
|
|
@@ -909,10 +2202,10 @@ class AXPDblClickDirective {
|
|
|
909
2202
|
}
|
|
910
2203
|
this.lastTap = currentTime;
|
|
911
2204
|
}
|
|
912
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
913
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "
|
|
2205
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDblClickDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2206
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: AXPDblClickDirective, isStandalone: true, selector: "[onDblClick]", outputs: { onDblClick: "onDblClick" }, host: { listeners: { "dblclick": "handleOnDblClick($event)", "touchend": "onTouchEnd($event)" } }, ngImport: i0 }); }
|
|
914
2207
|
}
|
|
915
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
2208
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDblClickDirective, decorators: [{
|
|
916
2209
|
type: Directive,
|
|
917
2210
|
args: [{
|
|
918
2211
|
selector: '[onDblClick]'
|
|
@@ -927,12 +2220,51 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
|
|
|
927
2220
|
args: ['touchend', ['$event']]
|
|
928
2221
|
}] } });
|
|
929
2222
|
|
|
2223
|
+
class AXPElementDataDirective {
|
|
2224
|
+
constructor(elementRef) {
|
|
2225
|
+
this.elementRef = elementRef;
|
|
2226
|
+
this.data = input(null, { ...(ngDevMode ? { debugName: "data" } : /* istanbul ignore next */ {}), alias: 'axp-data' });
|
|
2227
|
+
this.path = input('__data__', { ...(ngDevMode ? { debugName: "path" } : /* istanbul ignore next */ {}), alias: 'axp-data-path' });
|
|
2228
|
+
// Effect to update element data when inputs change
|
|
2229
|
+
this.updateEffect = effect(() => {
|
|
2230
|
+
const currentData = this.data();
|
|
2231
|
+
const currentPath = this.path();
|
|
2232
|
+
set(this.elementRef.nativeElement, currentPath, currentData);
|
|
2233
|
+
}, ...(ngDevMode ? [{ debugName: "updateEffect" }] : /* istanbul ignore next */ []));
|
|
2234
|
+
}
|
|
2235
|
+
ngOnInit() {
|
|
2236
|
+
// Initial data setup
|
|
2237
|
+
set(this.elementRef.nativeElement, this.path(), this.data());
|
|
2238
|
+
}
|
|
2239
|
+
/**
|
|
2240
|
+
* Get data from the element
|
|
2241
|
+
*/
|
|
2242
|
+
getData() {
|
|
2243
|
+
return get(this.elementRef.nativeElement, this.path());
|
|
2244
|
+
}
|
|
2245
|
+
/**
|
|
2246
|
+
* Update data at runtime
|
|
2247
|
+
*/
|
|
2248
|
+
setData(data) {
|
|
2249
|
+
set(this.elementRef.nativeElement, this.path(), data);
|
|
2250
|
+
}
|
|
2251
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPElementDataDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2252
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: AXPElementDataDirective, isStandalone: true, selector: "[axp-data]", inputs: { data: { classPropertyName: "data", publicName: "axp-data", isSignal: true, isRequired: false, transformFunction: null }, path: { classPropertyName: "path", publicName: "axp-data-path", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
|
|
2253
|
+
}
|
|
2254
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPElementDataDirective, decorators: [{
|
|
2255
|
+
type: Directive,
|
|
2256
|
+
args: [{
|
|
2257
|
+
selector: '[axp-data]',
|
|
2258
|
+
standalone: true
|
|
2259
|
+
}]
|
|
2260
|
+
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "axp-data", required: false }] }], path: [{ type: i0.Input, args: [{ isSignal: true, alias: "axp-data-path", required: false }] }] } });
|
|
2261
|
+
|
|
930
2262
|
// src/app/directives/grid-layout.directive.ts
|
|
931
2263
|
class AXPGridLayoutDirective {
|
|
932
2264
|
constructor(el, renderer) {
|
|
933
2265
|
this.el = el;
|
|
934
2266
|
this.renderer = renderer;
|
|
935
|
-
this.options = input.required(...(ngDevMode ?
|
|
2267
|
+
this.options = input.required({ ...(ngDevMode ? { debugName: "options" } : /* istanbul ignore next */ {}), alias: 'axp-grid-layout' });
|
|
936
2268
|
}
|
|
937
2269
|
ngOnChanges(changes) {
|
|
938
2270
|
if (changes['options']) {
|
|
@@ -968,240 +2300,24 @@ class AXPGridLayoutDirective {
|
|
|
968
2300
|
}
|
|
969
2301
|
});
|
|
970
2302
|
}
|
|
971
|
-
clearClasses() {
|
|
972
|
-
const currentClasses = this.el.nativeElement.className.split(' ');
|
|
973
|
-
currentClasses.forEach((className) => {
|
|
974
|
-
if (className.startsWith('ax-col-') || className.startsWith('ax-row-') || className.startsWith('ax-order-')) {
|
|
975
|
-
this.renderer.removeClass(this.el.nativeElement, className);
|
|
976
|
-
}
|
|
977
|
-
});
|
|
978
|
-
}
|
|
979
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPGridLayoutDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
980
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.12", type: AXPGridLayoutDirective, isStandalone: true, selector: "[axp-grid-layout]", inputs: { options: { classPropertyName: "options", publicName: "axp-grid-layout", isSignal: true, isRequired: true, transformFunction: null } }, usesOnChanges: true, ngImport: i0 }); }
|
|
981
|
-
}
|
|
982
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPGridLayoutDirective, decorators: [{
|
|
983
|
-
type: Directive,
|
|
984
|
-
args: [{
|
|
985
|
-
selector: '[axp-grid-layout]',
|
|
986
|
-
standalone: true,
|
|
987
|
-
}]
|
|
988
|
-
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "axp-grid-layout", required: true }] }] } });
|
|
989
|
-
|
|
990
|
-
// src/app/directives/grid-layout.directive.ts
|
|
991
|
-
class AXPContentCheckerDirective {
|
|
992
|
-
constructor() {
|
|
993
|
-
this.viewContainerRef = inject(ViewContainerRef);
|
|
994
|
-
this.elementRef = inject((ElementRef));
|
|
995
|
-
this.isEmpty = computed(() => this.viewContainerRef.length === 0, ...(ngDevMode ? [{ debugName: "isEmpty" }] : []));
|
|
996
|
-
}
|
|
997
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPContentCheckerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
998
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.12", type: AXPContentCheckerDirective, isStandalone: true, selector: "[axp-content-checker]", exportAs: ["checker"], ngImport: i0 }); }
|
|
999
|
-
}
|
|
1000
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPContentCheckerDirective, decorators: [{
|
|
1001
|
-
type: Directive,
|
|
1002
|
-
args: [{
|
|
1003
|
-
selector: '[axp-content-checker]',
|
|
1004
|
-
standalone: true,
|
|
1005
|
-
exportAs: 'checker'
|
|
1006
|
-
}]
|
|
1007
|
-
}] });
|
|
1008
|
-
|
|
1009
|
-
//#region ---- Imports ----
|
|
1010
|
-
//#endregion
|
|
1011
|
-
//#region ---- Directive ----
|
|
1012
|
-
/**
|
|
1013
|
-
* Fullscreen directive that provides CSS-based fullscreen functionality
|
|
1014
|
-
* Usage: <element axFullscreen #fullscreen="axFullscreen"></element>
|
|
1015
|
-
* Then: fullscreen.toggle() or fullscreen.enter() or fullscreen.exit()
|
|
1016
|
-
*/
|
|
1017
|
-
class AXFullscreenDirective {
|
|
1018
|
-
constructor(elementRef) {
|
|
1019
|
-
this.elementRef = elementRef;
|
|
1020
|
-
//#region ---- Properties ----
|
|
1021
|
-
/**
|
|
1022
|
-
* Current fullscreen state
|
|
1023
|
-
*/
|
|
1024
|
-
this.isFullscreenState = signal(false, ...(ngDevMode ? [{ debugName: "isFullscreenState" }] : []));
|
|
1025
|
-
/**
|
|
1026
|
-
* Original element styles to restore
|
|
1027
|
-
*/
|
|
1028
|
-
this.originalStyles = {};
|
|
1029
|
-
/**
|
|
1030
|
-
* Original parent element reference
|
|
1031
|
-
*/
|
|
1032
|
-
this.originalParent = null;
|
|
1033
|
-
/**
|
|
1034
|
-
* Fullscreen container element
|
|
1035
|
-
*/
|
|
1036
|
-
this.fullscreenContainer = null;
|
|
1037
|
-
/**
|
|
1038
|
-
* Fullscreen change event
|
|
1039
|
-
*/
|
|
1040
|
-
this.fullscreenChange = output();
|
|
1041
|
-
//#endregion
|
|
1042
|
-
//#region ---- Constructor ----
|
|
1043
|
-
this.destroyRef = inject(DestroyRef);
|
|
1044
|
-
this.renderer = inject(Renderer2);
|
|
1045
|
-
// Sync state changes to output
|
|
1046
|
-
effect(() => {
|
|
1047
|
-
const isFullscreen = this.isFullscreenState();
|
|
1048
|
-
untracked(() => {
|
|
1049
|
-
this.fullscreenChange.emit(isFullscreen);
|
|
1050
|
-
});
|
|
1051
|
-
});
|
|
1052
|
-
}
|
|
1053
|
-
//#endregion
|
|
1054
|
-
//#region ---- Public API ----
|
|
1055
|
-
/**
|
|
1056
|
-
* Toggle fullscreen state
|
|
1057
|
-
*/
|
|
1058
|
-
async toggle() {
|
|
1059
|
-
if (this.isFullscreenState()) {
|
|
1060
|
-
await this.exit();
|
|
1061
|
-
}
|
|
1062
|
-
else {
|
|
1063
|
-
await this.enter();
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
/**
|
|
1067
|
-
* Enter fullscreen mode using CSS
|
|
1068
|
-
*/
|
|
1069
|
-
async enter() {
|
|
1070
|
-
if (this.isFullscreenState()) {
|
|
1071
|
-
return;
|
|
1072
|
-
}
|
|
1073
|
-
const element = this.elementRef.nativeElement;
|
|
1074
|
-
try {
|
|
1075
|
-
// Store original styles and parent
|
|
1076
|
-
this.storeOriginalStyles(element);
|
|
1077
|
-
this.originalParent = element.parentElement;
|
|
1078
|
-
// Create fullscreen container
|
|
1079
|
-
this.fullscreenContainer = this.renderer.createElement('div');
|
|
1080
|
-
this.renderer.setStyle(this.fullscreenContainer, 'position', 'fixed');
|
|
1081
|
-
this.renderer.setStyle(this.fullscreenContainer, 'top', '0');
|
|
1082
|
-
this.renderer.setStyle(this.fullscreenContainer, 'left', '0');
|
|
1083
|
-
this.renderer.setStyle(this.fullscreenContainer, 'width', '100vw');
|
|
1084
|
-
this.renderer.setStyle(this.fullscreenContainer, 'height', '100vh');
|
|
1085
|
-
this.renderer.setStyle(this.fullscreenContainer, 'z-index', '9999');
|
|
1086
|
-
this.renderer.setStyle(this.fullscreenContainer, 'background-color', '#ffffff');
|
|
1087
|
-
this.renderer.setStyle(this.fullscreenContainer, 'overflow', 'auto');
|
|
1088
|
-
// Move element to container
|
|
1089
|
-
this.renderer.appendChild(this.fullscreenContainer, element);
|
|
1090
|
-
// Apply fullscreen styles to element
|
|
1091
|
-
this.renderer.setStyle(element, 'position', 'relative');
|
|
1092
|
-
this.renderer.setStyle(element, 'width', '100%');
|
|
1093
|
-
this.renderer.setStyle(element, 'height', '100%');
|
|
1094
|
-
this.renderer.setStyle(element, 'margin', '0');
|
|
1095
|
-
this.renderer.setStyle(element, 'padding', '0');
|
|
1096
|
-
// Append container to body
|
|
1097
|
-
this.renderer.appendChild(document.body, this.fullscreenContainer);
|
|
1098
|
-
// Prevent body scroll
|
|
1099
|
-
this.renderer.setStyle(document.body, 'overflow', 'hidden');
|
|
1100
|
-
this.isFullscreenState.set(true);
|
|
1101
|
-
}
|
|
1102
|
-
catch (error) {
|
|
1103
|
-
console.error('Error entering fullscreen:', error);
|
|
1104
|
-
this.restoreOriginalStyles(element);
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1107
|
-
/**
|
|
1108
|
-
* Exit fullscreen mode
|
|
1109
|
-
*/
|
|
1110
|
-
async exit() {
|
|
1111
|
-
if (!this.isFullscreenState()) {
|
|
1112
|
-
return;
|
|
1113
|
-
}
|
|
1114
|
-
const element = this.elementRef.nativeElement;
|
|
1115
|
-
try {
|
|
1116
|
-
// Restore body scroll
|
|
1117
|
-
this.renderer.removeStyle(document.body, 'overflow');
|
|
1118
|
-
// Move element back to original parent
|
|
1119
|
-
if (this.fullscreenContainer && this.originalParent) {
|
|
1120
|
-
this.renderer.removeChild(this.fullscreenContainer, element);
|
|
1121
|
-
this.renderer.appendChild(this.originalParent, element);
|
|
1122
|
-
// Remove container
|
|
1123
|
-
this.renderer.removeChild(document.body, this.fullscreenContainer);
|
|
1124
|
-
this.fullscreenContainer = null;
|
|
1125
|
-
}
|
|
1126
|
-
// Restore original styles
|
|
1127
|
-
this.restoreOriginalStyles(element);
|
|
1128
|
-
this.isFullscreenState.set(false);
|
|
1129
|
-
}
|
|
1130
|
-
catch (error) {
|
|
1131
|
-
console.error('Error exiting fullscreen:', error);
|
|
1132
|
-
}
|
|
1133
|
-
}
|
|
1134
|
-
/**
|
|
1135
|
-
* Check if currently in fullscreen mode
|
|
1136
|
-
*/
|
|
1137
|
-
isFullscreen() {
|
|
1138
|
-
return this.isFullscreenState();
|
|
1139
|
-
}
|
|
1140
|
-
//#endregion
|
|
1141
|
-
//#region ---- Private Methods ----
|
|
1142
|
-
/**
|
|
1143
|
-
* Store original element styles
|
|
1144
|
-
*/
|
|
1145
|
-
storeOriginalStyles(element) {
|
|
1146
|
-
const computedStyle = window.getComputedStyle(element);
|
|
1147
|
-
this.originalStyles = {
|
|
1148
|
-
position: computedStyle.position,
|
|
1149
|
-
top: computedStyle.top,
|
|
1150
|
-
left: computedStyle.left,
|
|
1151
|
-
width: computedStyle.width,
|
|
1152
|
-
height: computedStyle.height,
|
|
1153
|
-
zIndex: computedStyle.zIndex,
|
|
1154
|
-
backgroundColor: computedStyle.backgroundColor,
|
|
1155
|
-
margin: computedStyle.margin,
|
|
1156
|
-
padding: computedStyle.padding,
|
|
1157
|
-
};
|
|
1158
|
-
}
|
|
1159
|
-
/**
|
|
1160
|
-
* Restore original element styles
|
|
1161
|
-
*/
|
|
1162
|
-
restoreOriginalStyles(element) {
|
|
1163
|
-
if (!this.originalStyles) {
|
|
1164
|
-
return;
|
|
1165
|
-
}
|
|
1166
|
-
// Restore each style property
|
|
1167
|
-
Object.entries(this.originalStyles).forEach(([key, value]) => {
|
|
1168
|
-
const styleKey = this.camelToKebabCase(key);
|
|
1169
|
-
if (value) {
|
|
1170
|
-
this.renderer.setStyle(element, styleKey, value);
|
|
1171
|
-
}
|
|
1172
|
-
else {
|
|
1173
|
-
this.renderer.removeStyle(element, styleKey);
|
|
1174
|
-
}
|
|
1175
|
-
});
|
|
1176
|
-
this.originalStyles = {};
|
|
1177
|
-
}
|
|
1178
|
-
/**
|
|
1179
|
-
* Convert camelCase to kebab-case for CSS properties
|
|
1180
|
-
*/
|
|
1181
|
-
camelToKebabCase(str) {
|
|
1182
|
-
return str.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase();
|
|
1183
|
-
}
|
|
1184
|
-
//#endregion
|
|
1185
|
-
//#region ---- Lifecycle ----
|
|
1186
|
-
ngOnDestroy() {
|
|
1187
|
-
// Exit fullscreen if still active
|
|
1188
|
-
if (this.isFullscreenState()) {
|
|
1189
|
-
this.exit().catch(() => {
|
|
1190
|
-
// Ignore errors during cleanup
|
|
1191
|
-
});
|
|
1192
|
-
}
|
|
2303
|
+
clearClasses() {
|
|
2304
|
+
const currentClasses = this.el.nativeElement.className.split(' ');
|
|
2305
|
+
currentClasses.forEach((className) => {
|
|
2306
|
+
if (className.startsWith('ax-col-') || className.startsWith('ax-row-') || className.startsWith('ax-order-')) {
|
|
2307
|
+
this.renderer.removeClass(this.el.nativeElement, className);
|
|
2308
|
+
}
|
|
2309
|
+
});
|
|
1193
2310
|
}
|
|
1194
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1195
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "
|
|
2311
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPGridLayoutDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2312
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: AXPGridLayoutDirective, isStandalone: true, selector: "[axp-grid-layout]", inputs: { options: { classPropertyName: "options", publicName: "axp-grid-layout", isSignal: true, isRequired: true, transformFunction: null } }, usesOnChanges: true, ngImport: i0 }); }
|
|
1196
2313
|
}
|
|
1197
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
2314
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPGridLayoutDirective, decorators: [{
|
|
1198
2315
|
type: Directive,
|
|
1199
2316
|
args: [{
|
|
1200
|
-
selector: '[
|
|
2317
|
+
selector: '[axp-grid-layout]',
|
|
1201
2318
|
standalone: true,
|
|
1202
|
-
exportAs: 'axFullscreen',
|
|
1203
2319
|
}]
|
|
1204
|
-
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: {
|
|
2320
|
+
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "axp-grid-layout", required: true }] }] } });
|
|
1205
2321
|
|
|
1206
2322
|
const AXP_DISTRIBUTED_EVENT_LISTENER_PROVIDER = new InjectionToken('AXP_DISTRIBUTED_EVENT_LISTENER_PROVIDER');
|
|
1207
2323
|
|
|
@@ -1339,10 +2455,10 @@ class AXPDistributedEventListenerService {
|
|
|
1339
2455
|
this.listenersByKey.clear();
|
|
1340
2456
|
this.providersLoaded = false;
|
|
1341
2457
|
}
|
|
1342
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1343
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
2458
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDistributedEventListenerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2459
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDistributedEventListenerService, providedIn: 'root' }); }
|
|
1344
2460
|
}
|
|
1345
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
2461
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDistributedEventListenerService, decorators: [{
|
|
1346
2462
|
type: Injectable,
|
|
1347
2463
|
args: [{
|
|
1348
2464
|
providedIn: 'root',
|
|
@@ -1466,220 +2582,777 @@ class AXPBroadcastEventService {
|
|
|
1466
2582
|
// Cleanup multi-tab event history
|
|
1467
2583
|
this.multiTabEventHistory.clear();
|
|
1468
2584
|
}
|
|
1469
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1470
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
2585
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPBroadcastEventService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2586
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPBroadcastEventService, providedIn: 'root' }); }
|
|
1471
2587
|
}
|
|
1472
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
2588
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPBroadcastEventService, decorators: [{
|
|
1473
2589
|
type: Injectable,
|
|
1474
2590
|
args: [{
|
|
1475
2591
|
providedIn: 'root',
|
|
1476
2592
|
}]
|
|
1477
2593
|
}], ctorParameters: () => [] });
|
|
1478
2594
|
|
|
1479
|
-
class
|
|
2595
|
+
class AXPHookService {
|
|
1480
2596
|
constructor() {
|
|
1481
|
-
this.
|
|
1482
|
-
|
|
1483
|
-
addScope(namespace, functions) {
|
|
1484
|
-
this.scopes[namespace] = { ...this.scopes[namespace], ...functions };
|
|
2597
|
+
this.listenerProviders = inject(AXP_DISTRIBUTED_EVENT_LISTENER_PROVIDER, { optional: true }) || [];
|
|
2598
|
+
this.injector = inject(Injector);
|
|
1485
2599
|
}
|
|
1486
|
-
|
|
1487
|
-
|
|
2600
|
+
/**
|
|
2601
|
+
* Resolve all providers (handle both direct providers and Promise<provider>)
|
|
2602
|
+
*/
|
|
2603
|
+
async resolveProviders() {
|
|
2604
|
+
return Promise.all(this.listenerProviders.map(p => p instanceof Promise ? p : Promise.resolve(p)));
|
|
1488
2605
|
}
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
2606
|
+
/**
|
|
2607
|
+
* Fire sync hooks (fire-and-forget).
|
|
2608
|
+
* All providers with the given key will be executed.
|
|
2609
|
+
* Execution is not awaited.
|
|
2610
|
+
*/
|
|
2611
|
+
fire(key, data) {
|
|
2612
|
+
this.resolveProviders().then(providers => {
|
|
2613
|
+
providers
|
|
2614
|
+
.filter(p => p.key === key)
|
|
2615
|
+
.sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0))
|
|
2616
|
+
.forEach(p => {
|
|
2617
|
+
try {
|
|
2618
|
+
runInInjectionContext(this.injector, () => p.execute(data));
|
|
2619
|
+
}
|
|
2620
|
+
catch (err) {
|
|
2621
|
+
console.error(`[AXPHookService] Hook '${key}' failed`, err);
|
|
2622
|
+
}
|
|
2623
|
+
});
|
|
2624
|
+
});
|
|
1495
2625
|
}
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
2626
|
+
/**
|
|
2627
|
+
* Run async hooks sequentially (waterfall).
|
|
2628
|
+
* The output of each hook is passed as input to the next hook.
|
|
2629
|
+
* Returns the final merged data after all hooks are executed.
|
|
2630
|
+
*/
|
|
2631
|
+
async runAsync(key, initialData) {
|
|
2632
|
+
const providers = (await this.resolveProviders())
|
|
2633
|
+
.filter(p => p.key === key)
|
|
2634
|
+
.sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
|
|
2635
|
+
let data = initialData;
|
|
2636
|
+
for (const p of providers) {
|
|
2637
|
+
data = await Promise.resolve(runInInjectionContext(this.injector, () => p.execute(data)));
|
|
1504
2638
|
}
|
|
1505
|
-
|
|
1506
|
-
}
|
|
1507
|
-
async getScopesAsync() {
|
|
1508
|
-
await this.load();
|
|
1509
|
-
return this.cache || {};
|
|
2639
|
+
return data;
|
|
1510
2640
|
}
|
|
1511
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1512
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
2641
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPHookService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2642
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPHookService, providedIn: 'root' }); }
|
|
1513
2643
|
}
|
|
1514
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
2644
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPHookService, decorators: [{
|
|
1515
2645
|
type: Injectable,
|
|
1516
2646
|
args: [{ providedIn: 'root' }]
|
|
1517
2647
|
}] });
|
|
1518
2648
|
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
2649
|
+
//#region ---- Types ----
|
|
2650
|
+
/** Priority tier — higher values win when multiple bindings match. */
|
|
2651
|
+
var AXPKeyboardShortcutPriority;
|
|
2652
|
+
(function (AXPKeyboardShortcutPriority) {
|
|
2653
|
+
AXPKeyboardShortcutPriority[AXPKeyboardShortcutPriority["Global"] = 0] = "Global";
|
|
2654
|
+
AXPKeyboardShortcutPriority[AXPKeyboardShortcutPriority["Page"] = 10] = "Page";
|
|
2655
|
+
AXPKeyboardShortcutPriority[AXPKeyboardShortcutPriority["Panel"] = 20] = "Panel";
|
|
2656
|
+
AXPKeyboardShortcutPriority[AXPKeyboardShortcutPriority["Modal"] = 100] = "Modal";
|
|
2657
|
+
})(AXPKeyboardShortcutPriority || (AXPKeyboardShortcutPriority = {}));
|
|
2658
|
+
//#endregion
|
|
2659
|
+
|
|
2660
|
+
//#region ---- Overlay Layer DOM Utilities ----
|
|
2661
|
+
/** Acorex overlay root (modal shell, popup, dialog). */
|
|
2662
|
+
const AX_OVERLAY_CONTAINER_SELECTOR = '.ax-overlay-container';
|
|
2663
|
+
/** Anchored widget panel inside an overlay (select, date picker, dropdown). */
|
|
2664
|
+
const AX_OVERLAY_PANE_SELECTOR = '.ax-overlay-pane';
|
|
2665
|
+
/**
|
|
2666
|
+
* True when a visible overlay layer is foreground (modal, popup, widget popover/picker).
|
|
2667
|
+
*/
|
|
2668
|
+
function hasForegroundOverlayLayer(document) {
|
|
2669
|
+
if (getVisibleAnchoredOverlayPanes(document).length > 0) {
|
|
2670
|
+
return true;
|
|
1524
2671
|
}
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
2672
|
+
return getVisibleOverlayContainers(document).length > 0;
|
|
2673
|
+
}
|
|
2674
|
+
/**
|
|
2675
|
+
* Returns visible anchored overlay panes (widget popovers/pickers).
|
|
2676
|
+
*/
|
|
2677
|
+
function getVisibleAnchoredOverlayPanes(document) {
|
|
2678
|
+
return Array.from(document.querySelectorAll(AX_OVERLAY_PANE_SELECTOR)).filter(isVisibleOverlayElement);
|
|
2679
|
+
}
|
|
2680
|
+
/**
|
|
2681
|
+
* Visible anchored panes that are not contained by `excludeContainer` (e.g. dialog shell).
|
|
2682
|
+
*/
|
|
2683
|
+
function getNestedVisibleOverlayPanes(document, excludeContainer) {
|
|
2684
|
+
return getVisibleAnchoredOverlayPanes(document).filter((pane) => !excludeContainer?.contains(pane));
|
|
2685
|
+
}
|
|
2686
|
+
/**
|
|
2687
|
+
* Collects visible overlay containers in stacking order (later / higher z-index wins).
|
|
2688
|
+
*/
|
|
2689
|
+
function getVisibleOverlayContainers(document) {
|
|
2690
|
+
return Array.from(document.querySelectorAll(AX_OVERLAY_CONTAINER_SELECTOR))
|
|
2691
|
+
.filter(isVisibleOverlayElement)
|
|
2692
|
+
.sort(compareOverlayStackOrder);
|
|
2693
|
+
}
|
|
2694
|
+
/** Topmost visible overlay container, if any. */
|
|
2695
|
+
function getTopVisibleOverlayContainer(document) {
|
|
2696
|
+
const containers = getVisibleOverlayContainers(document);
|
|
2697
|
+
return containers.length ? containers[containers.length - 1] : null;
|
|
2698
|
+
}
|
|
2699
|
+
/** Nearest overlay container ancestor of `element`. */
|
|
2700
|
+
function findOverlayContainerAncestor(element) {
|
|
2701
|
+
return element.closest(AX_OVERLAY_CONTAINER_SELECTOR);
|
|
2702
|
+
}
|
|
2703
|
+
/**
|
|
2704
|
+
* Returns true when the overlay element is rendered and visible in the viewport.
|
|
2705
|
+
*/
|
|
2706
|
+
function isVisibleOverlayElement(element) {
|
|
2707
|
+
if (!(element instanceof HTMLElement)) {
|
|
2708
|
+
return false;
|
|
2709
|
+
}
|
|
2710
|
+
const style = getComputedStyle(element);
|
|
2711
|
+
if (style.display === 'none' || style.visibility === 'hidden' || Number(style.opacity) === 0) {
|
|
2712
|
+
return false;
|
|
2713
|
+
}
|
|
2714
|
+
const rect = element.getBoundingClientRect();
|
|
2715
|
+
return rect.width > 0 && rect.height > 0;
|
|
2716
|
+
}
|
|
2717
|
+
function compareOverlayStackOrder(a, b) {
|
|
2718
|
+
const za = Number(getComputedStyle(a).zIndex) || 0;
|
|
2719
|
+
const zb = Number(getComputedStyle(b).zIndex) || 0;
|
|
2720
|
+
if (za !== zb) {
|
|
2721
|
+
return za - zb;
|
|
2722
|
+
}
|
|
2723
|
+
const position = a.compareDocumentPosition(b);
|
|
2724
|
+
if (position & Node.DOCUMENT_POSITION_FOLLOWING) {
|
|
2725
|
+
return -1;
|
|
2726
|
+
}
|
|
2727
|
+
if (position & Node.DOCUMENT_POSITION_PRECEDING) {
|
|
2728
|
+
return 1;
|
|
2729
|
+
}
|
|
2730
|
+
return 0;
|
|
2731
|
+
}
|
|
2732
|
+
//#endregion
|
|
2733
|
+
|
|
2734
|
+
//#region ---- Imports ----
|
|
2735
|
+
const HORIZONTAL_DIRECTIONAL_KEY_MIRRORS = {
|
|
2736
|
+
arrowleft: 'arrowright',
|
|
2737
|
+
arrowright: 'arrowleft',
|
|
2738
|
+
left: 'right',
|
|
2739
|
+
right: 'left',
|
|
2740
|
+
'[': ']',
|
|
2741
|
+
']': '[',
|
|
2742
|
+
};
|
|
2743
|
+
/** Maps a horizontal direction key to its RTL semantic counterpart. */
|
|
2744
|
+
function mirrorHorizontalDirectionalKey(key) {
|
|
2745
|
+
const normalized = normalizeShortcutToken(key);
|
|
2746
|
+
return HORIZONTAL_DIRECTIONAL_KEY_MIRRORS[normalized] ?? normalized;
|
|
2747
|
+
}
|
|
2748
|
+
/** True when the chord's primary key is a horizontal direction key (`←`/`→`, `[`/`]`). */
|
|
2749
|
+
function isHorizontalDirectionalShortcutKey(key) {
|
|
2750
|
+
return normalizeShortcutToken(key) in HORIZONTAL_DIRECTIONAL_KEY_MIRRORS;
|
|
2751
|
+
}
|
|
2752
|
+
/**
|
|
2753
|
+
* Resolves direction behavior for a chord.
|
|
2754
|
+
* Horizontal direction keys default to `semantic`; others default to `physical`.
|
|
2755
|
+
* An explicit binding value always wins.
|
|
2756
|
+
*/
|
|
2757
|
+
function resolveEffectiveDirectionBehavior(chord, explicit) {
|
|
2758
|
+
if (explicit === 'physical' || explicit === 'semantic') {
|
|
2759
|
+
return explicit;
|
|
2760
|
+
}
|
|
2761
|
+
const { key } = parseKeyboardShortcutChord(chord);
|
|
2762
|
+
return isHorizontalDirectionalShortcutKey(key) ? 'semantic' : 'physical';
|
|
2763
|
+
}
|
|
2764
|
+
/** Resolves the key the user presses for a semantically registered chord in RTL. */
|
|
2765
|
+
function resolveSemanticDirectionalKey(storedKey, rtl) {
|
|
2766
|
+
const normalized = normalizeShortcutToken(storedKey);
|
|
2767
|
+
if (!rtl) {
|
|
2768
|
+
return normalized;
|
|
2769
|
+
}
|
|
2770
|
+
return mirrorHorizontalDirectionalKey(normalized);
|
|
2771
|
+
}
|
|
2772
|
+
/** Chord string with directional key mirrored for RTL display/matching when semantic. */
|
|
2773
|
+
function resolveDisplayShortcutChord(chord, isRtl, explicitDirectionBehavior) {
|
|
2774
|
+
const directionBehavior = resolveEffectiveDirectionBehavior(chord, explicitDirectionBehavior);
|
|
2775
|
+
const parts = chord
|
|
2776
|
+
.trim()
|
|
2777
|
+
.split('+')
|
|
2778
|
+
.map((part) => part.trim())
|
|
2779
|
+
.filter(Boolean);
|
|
2780
|
+
if (parts.length === 0) {
|
|
2781
|
+
return chord;
|
|
2782
|
+
}
|
|
2783
|
+
const keyPart = parts.pop() ?? '';
|
|
2784
|
+
const rtl = directionBehavior === 'semantic' && isRtl;
|
|
2785
|
+
const resolvedKey = resolveSemanticDirectionalKey(keyPart, rtl);
|
|
2786
|
+
return [...parts, resolvedKey].join('+');
|
|
2787
|
+
}
|
|
2788
|
+
const MODIFIER_ALIASES = {
|
|
2789
|
+
ctrl: 'ctrl',
|
|
2790
|
+
control: 'ctrl',
|
|
2791
|
+
shift: 'shift',
|
|
2792
|
+
alt: 'alt',
|
|
2793
|
+
option: 'alt',
|
|
2794
|
+
meta: 'meta',
|
|
2795
|
+
cmd: 'meta',
|
|
2796
|
+
command: 'meta',
|
|
2797
|
+
win: 'meta',
|
|
2798
|
+
};
|
|
2799
|
+
const KEY_DISPLAY_MAC = {
|
|
2800
|
+
ctrl: '⌃',
|
|
2801
|
+
control: '⌃',
|
|
2802
|
+
shift: '⇧',
|
|
2803
|
+
alt: '⌥',
|
|
2804
|
+
option: '⌥',
|
|
2805
|
+
meta: '⌘',
|
|
2806
|
+
cmd: '⌘',
|
|
2807
|
+
command: '⌘',
|
|
2808
|
+
win: '⌘',
|
|
2809
|
+
enter: '↵',
|
|
2810
|
+
escape: 'Esc',
|
|
2811
|
+
esc: 'Esc',
|
|
2812
|
+
home: 'Home',
|
|
2813
|
+
arrowleft: '←',
|
|
2814
|
+
arrowright: '→',
|
|
2815
|
+
arrowup: '↑',
|
|
2816
|
+
arrowdown: '↓',
|
|
2817
|
+
pageup: 'PgUp',
|
|
2818
|
+
pagedown: 'PgDn',
|
|
2819
|
+
backspace: '⌫',
|
|
2820
|
+
delete: '⌦',
|
|
2821
|
+
space: 'Space',
|
|
2822
|
+
};
|
|
2823
|
+
/**
|
|
2824
|
+
* Parses a shortcut chord such as `Enter`, `Escape`, or `ctrl+shift+s`.
|
|
2825
|
+
*/
|
|
2826
|
+
function parseKeyboardShortcutChord(chord) {
|
|
2827
|
+
const parts = chord
|
|
2828
|
+
.trim()
|
|
2829
|
+
.toLowerCase()
|
|
2830
|
+
.split('+')
|
|
2831
|
+
.map((part) => part.trim())
|
|
2832
|
+
.filter(Boolean);
|
|
2833
|
+
const key = normalizeShortcutToken(parts.pop() ?? '');
|
|
2834
|
+
const parsed = {
|
|
2835
|
+
ctrl: false,
|
|
2836
|
+
shift: false,
|
|
2837
|
+
alt: false,
|
|
2838
|
+
meta: false,
|
|
2839
|
+
key,
|
|
2840
|
+
};
|
|
2841
|
+
for (const part of parts) {
|
|
2842
|
+
const alias = MODIFIER_ALIASES[part];
|
|
2843
|
+
if (alias) {
|
|
2844
|
+
parsed[alias] = true;
|
|
1539
2845
|
}
|
|
1540
|
-
return this.expressionCache.get(expression);
|
|
1541
2846
|
}
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
2847
|
+
return parsed;
|
|
2848
|
+
}
|
|
2849
|
+
/**
|
|
2850
|
+
* Returns true when the keyboard event matches the given chord.
|
|
2851
|
+
* On macOS, `ctrl+*` chords also match `meta+*` for common platform shortcuts.
|
|
2852
|
+
*/
|
|
2853
|
+
function matchesKeyboardShortcutChord(event, chord, options) {
|
|
2854
|
+
const parsed = parseKeyboardShortcutChord(chord);
|
|
2855
|
+
const mac = isMacPlatform();
|
|
2856
|
+
const directionBehavior = resolveEffectiveDirectionBehavior(chord, options?.directionBehavior);
|
|
2857
|
+
if (!matchesShortcutModifiers(parsed, event, mac)) {
|
|
2858
|
+
return false;
|
|
1546
2859
|
}
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
else {
|
|
1570
|
-
return source;
|
|
1571
|
-
}
|
|
2860
|
+
const actualKey = normalizeShortcutEventKey(event);
|
|
2861
|
+
const expectedKey = normalizeShortcutToken(parsed.key);
|
|
2862
|
+
const rtlSemantic = directionBehavior === 'semantic' &&
|
|
2863
|
+
options?.isRtl === true &&
|
|
2864
|
+
isHorizontalDirectionalShortcutKey(expectedKey);
|
|
2865
|
+
if (rtlSemantic) {
|
|
2866
|
+
return actualKey === mirrorHorizontalDirectionalKey(expectedKey);
|
|
2867
|
+
}
|
|
2868
|
+
return actualKey === expectedKey;
|
|
2869
|
+
}
|
|
2870
|
+
/**
|
|
2871
|
+
* Matches modifier keys for a parsed chord.
|
|
2872
|
+
* `ctrl+alt` chords also accept AltGraph (Windows) so layout-produced characters still match physical keys.
|
|
2873
|
+
*/
|
|
2874
|
+
function matchesShortcutModifiers(parsed, event, mac) {
|
|
2875
|
+
if (parsed.shift !== event.shiftKey) {
|
|
2876
|
+
return false;
|
|
2877
|
+
}
|
|
2878
|
+
const altGraph = hasAltGraphModifier(event);
|
|
2879
|
+
if (parsed.ctrl && parsed.alt) {
|
|
2880
|
+
if (altGraph) {
|
|
2881
|
+
return true;
|
|
1572
2882
|
}
|
|
1573
|
-
|
|
1574
|
-
|
|
2883
|
+
return event.ctrlKey && event.altKey;
|
|
2884
|
+
}
|
|
2885
|
+
if (parsed.alt !== event.altKey) {
|
|
2886
|
+
return false;
|
|
2887
|
+
}
|
|
2888
|
+
if (parsed.meta) {
|
|
2889
|
+
if (!event.metaKey) {
|
|
2890
|
+
return false;
|
|
2891
|
+
}
|
|
2892
|
+
return true;
|
|
2893
|
+
}
|
|
2894
|
+
if (parsed.ctrl) {
|
|
2895
|
+
if (mac) {
|
|
2896
|
+
return event.metaKey || event.ctrlKey;
|
|
2897
|
+
}
|
|
2898
|
+
return event.ctrlKey;
|
|
2899
|
+
}
|
|
2900
|
+
if (event.ctrlKey || event.metaKey) {
|
|
2901
|
+
return false;
|
|
2902
|
+
}
|
|
2903
|
+
return true;
|
|
2904
|
+
}
|
|
2905
|
+
function hasAltGraphModifier(event) {
|
|
2906
|
+
return typeof event.getModifierState === 'function' && event.getModifierState('AltGraph');
|
|
2907
|
+
}
|
|
2908
|
+
/**
|
|
2909
|
+
* Formats one chord for UI display (OS-aware modifier symbols).
|
|
2910
|
+
*/
|
|
2911
|
+
function formatKeyboardShortcutChord(chord, options) {
|
|
2912
|
+
const directionBehavior = resolveEffectiveDirectionBehavior(chord, options?.directionBehavior);
|
|
2913
|
+
const resolvedChord = options?.skipDirectionResolve
|
|
2914
|
+
? chord
|
|
2915
|
+
: resolveDisplayShortcutChord(chord, options?.isRtl === true, directionBehavior);
|
|
2916
|
+
const mac = isMacPlatform();
|
|
2917
|
+
const parts = resolvedChord
|
|
2918
|
+
.trim()
|
|
2919
|
+
.split('+')
|
|
2920
|
+
.map((part) => part.trim())
|
|
2921
|
+
.filter(Boolean);
|
|
2922
|
+
if (parts.length === 0) {
|
|
2923
|
+
return '';
|
|
2924
|
+
}
|
|
2925
|
+
const keyPart = normalizeShortcutToken(parts.pop() ?? '');
|
|
2926
|
+
const modifierParts = parts.map((part) => formatShortcutPart(part.toLowerCase(), mac));
|
|
2927
|
+
const keyDisplay = formatShortcutPart(keyPart, mac);
|
|
2928
|
+
if (mac) {
|
|
2929
|
+
return [...modifierParts, keyDisplay].join('');
|
|
2930
|
+
}
|
|
2931
|
+
return [...modifierParts, keyDisplay].join('+');
|
|
2932
|
+
}
|
|
2933
|
+
/**
|
|
2934
|
+
* Formats multiple chords as a single display string (e.g. `← / PgUp`).
|
|
2935
|
+
*/
|
|
2936
|
+
function formatKeyboardShortcutChords(chords, options) {
|
|
2937
|
+
return chords.map((chord) => formatKeyboardShortcutChord(chord, options)).join(' / ');
|
|
2938
|
+
}
|
|
2939
|
+
/** Maps a chord to `ax-kbd-item` key parts (modifiers + display symbols). */
|
|
2940
|
+
function chordToKbdItemKeys(chord, options) {
|
|
2941
|
+
const directionBehavior = resolveEffectiveDirectionBehavior(chord, options?.directionBehavior);
|
|
2942
|
+
const resolvedChord = options?.skipDirectionResolve
|
|
2943
|
+
? chord
|
|
2944
|
+
: resolveDisplayShortcutChord(chord, options?.isRtl === true, directionBehavior);
|
|
2945
|
+
const mac = isMacPlatform();
|
|
2946
|
+
const symbolKeys = {
|
|
2947
|
+
arrowleft: '←',
|
|
2948
|
+
arrowright: '→',
|
|
2949
|
+
arrowup: '↑',
|
|
2950
|
+
arrowdown: '↓',
|
|
2951
|
+
pageup: 'PgUp',
|
|
2952
|
+
pagedown: 'PgDn',
|
|
2953
|
+
enter: '↵',
|
|
2954
|
+
escape: 'Esc',
|
|
2955
|
+
esc: 'Esc',
|
|
2956
|
+
space: 'Space',
|
|
2957
|
+
'[': '[',
|
|
2958
|
+
']': ']',
|
|
2959
|
+
};
|
|
2960
|
+
return resolvedChord
|
|
2961
|
+
.trim()
|
|
2962
|
+
.split('+')
|
|
2963
|
+
.map((part) => part.trim())
|
|
2964
|
+
.filter(Boolean)
|
|
2965
|
+
.map((part) => {
|
|
2966
|
+
const normalized = part.toLowerCase();
|
|
2967
|
+
if (mac && (normalized === 'ctrl' || normalized === 'control')) {
|
|
2968
|
+
return '⌘';
|
|
2969
|
+
}
|
|
2970
|
+
return symbolKeys[normalized] ?? part;
|
|
2971
|
+
});
|
|
2972
|
+
}
|
|
2973
|
+
/**
|
|
2974
|
+
* When focus is in a native edit control, avoid stealing plain key shortcuts.
|
|
2975
|
+
*/
|
|
2976
|
+
function isKeyboardTargetInsideEditableField(target) {
|
|
2977
|
+
if (!(target instanceof HTMLElement)) {
|
|
2978
|
+
return false;
|
|
2979
|
+
}
|
|
2980
|
+
if (target.isContentEditable) {
|
|
2981
|
+
return true;
|
|
2982
|
+
}
|
|
2983
|
+
const field = target.closest('input, textarea, select');
|
|
2984
|
+
if (!field || field.disabled) {
|
|
2985
|
+
return false;
|
|
2986
|
+
}
|
|
2987
|
+
if (field instanceof HTMLInputElement) {
|
|
2988
|
+
const type = field.type;
|
|
2989
|
+
if (type === 'hidden' ||
|
|
2990
|
+
type === 'checkbox' ||
|
|
2991
|
+
type === 'radio' ||
|
|
2992
|
+
type === 'button' ||
|
|
2993
|
+
type === 'submit' ||
|
|
2994
|
+
type === 'reset' ||
|
|
2995
|
+
type === 'file' ||
|
|
2996
|
+
field.readOnly) {
|
|
1575
2997
|
return false;
|
|
1576
2998
|
}
|
|
2999
|
+
return true;
|
|
3000
|
+
}
|
|
3001
|
+
if (field instanceof HTMLTextAreaElement) {
|
|
3002
|
+
return !field.readOnly;
|
|
3003
|
+
}
|
|
3004
|
+
return true;
|
|
3005
|
+
}
|
|
3006
|
+
/**
|
|
3007
|
+
* Returns true when Esc should close an open widget overlay first (select, date picker, popover, modal).
|
|
3008
|
+
* Used by the global shortcut registry so page-level Esc bindings do not swallow overlay dismiss.
|
|
3009
|
+
*/
|
|
3010
|
+
function shouldDeferEscapeToOpenOverlay(document) {
|
|
3011
|
+
return hasForegroundOverlayLayer(document);
|
|
3012
|
+
}
|
|
3013
|
+
function isMacPlatform() {
|
|
3014
|
+
if (typeof navigator === 'undefined') {
|
|
3015
|
+
return false;
|
|
3016
|
+
}
|
|
3017
|
+
return /Mac|iPhone|iPad|iPod/i.test(navigator.userAgent);
|
|
3018
|
+
}
|
|
3019
|
+
function normalizeShortcutToken(key) {
|
|
3020
|
+
const normalized = key.trim().toLowerCase();
|
|
3021
|
+
if (normalized === 'esc') {
|
|
3022
|
+
return 'escape';
|
|
3023
|
+
}
|
|
3024
|
+
if (normalized === 'left') {
|
|
3025
|
+
return 'arrowleft';
|
|
3026
|
+
}
|
|
3027
|
+
if (normalized === 'right') {
|
|
3028
|
+
return 'arrowright';
|
|
3029
|
+
}
|
|
3030
|
+
if (normalized === 'up') {
|
|
3031
|
+
return 'arrowup';
|
|
3032
|
+
}
|
|
3033
|
+
if (normalized === 'down') {
|
|
3034
|
+
return 'arrowdown';
|
|
3035
|
+
}
|
|
3036
|
+
if (normalized === '[' || normalized === 'bracketleft') {
|
|
3037
|
+
return '[';
|
|
3038
|
+
}
|
|
3039
|
+
if (normalized === ']' || normalized === 'bracketright') {
|
|
3040
|
+
return ']';
|
|
3041
|
+
}
|
|
3042
|
+
return normalized;
|
|
3043
|
+
}
|
|
3044
|
+
function normalizeShortcutEventKey(event) {
|
|
3045
|
+
if (event.key === 'Enter') {
|
|
3046
|
+
return 'enter';
|
|
3047
|
+
}
|
|
3048
|
+
if (event.key === 'Escape') {
|
|
3049
|
+
return 'escape';
|
|
3050
|
+
}
|
|
3051
|
+
if (event.key === 'Home') {
|
|
3052
|
+
return 'home';
|
|
3053
|
+
}
|
|
3054
|
+
if (event.key === ' ') {
|
|
3055
|
+
return 'space';
|
|
3056
|
+
}
|
|
3057
|
+
if (event.key === '[' || event.code === 'BracketLeft') {
|
|
3058
|
+
return '[';
|
|
3059
|
+
}
|
|
3060
|
+
if (event.key === ']' || event.code === 'BracketRight') {
|
|
3061
|
+
return ']';
|
|
3062
|
+
}
|
|
3063
|
+
const codeMatch = event.code.match(/^Key([A-Z])$/);
|
|
3064
|
+
if (codeMatch) {
|
|
3065
|
+
return codeMatch[1].toLowerCase();
|
|
3066
|
+
}
|
|
3067
|
+
const digitMatch = event.code.match(/^Digit([0-9])$/);
|
|
3068
|
+
if (digitMatch) {
|
|
3069
|
+
return digitMatch[1];
|
|
3070
|
+
}
|
|
3071
|
+
if (event.key.length === 1) {
|
|
3072
|
+
return event.key.toLowerCase();
|
|
3073
|
+
}
|
|
3074
|
+
const fnMatch = event.code.match(/^F(\d+)$/);
|
|
3075
|
+
if (fnMatch) {
|
|
3076
|
+
return `f${fnMatch[1]}`;
|
|
3077
|
+
}
|
|
3078
|
+
return event.key.toLowerCase();
|
|
3079
|
+
}
|
|
3080
|
+
function formatShortcutPart(part, mac) {
|
|
3081
|
+
const normalized = normalizeShortcutToken(part);
|
|
3082
|
+
if (mac && KEY_DISPLAY_MAC[normalized]) {
|
|
3083
|
+
return KEY_DISPLAY_MAC[normalized];
|
|
3084
|
+
}
|
|
3085
|
+
if (!mac && MODIFIER_ALIASES[normalized]) {
|
|
3086
|
+
return normalized.charAt(0).toUpperCase() + normalized.slice(1);
|
|
3087
|
+
}
|
|
3088
|
+
if (KEY_DISPLAY_MAC[normalized]) {
|
|
3089
|
+
return KEY_DISPLAY_MAC[normalized];
|
|
3090
|
+
}
|
|
3091
|
+
if (normalized.length === 1) {
|
|
3092
|
+
return normalized.toUpperCase();
|
|
3093
|
+
}
|
|
3094
|
+
return normalized.charAt(0).toUpperCase() + normalized.slice(1);
|
|
3095
|
+
}
|
|
3096
|
+
//#endregion
|
|
3097
|
+
|
|
3098
|
+
//#region ---- Keyboard Shortcut Declaration Utilities ----
|
|
3099
|
+
/**
|
|
3100
|
+
* Normalizes a declarative shortcut (`'ctrl+s'` or `{ keys: 'home', allowInEditableFields: false }`).
|
|
3101
|
+
*/
|
|
3102
|
+
function normalizeKeyboardShortcut(shortcut) {
|
|
3103
|
+
if (typeof shortcut === 'string') {
|
|
3104
|
+
const chord = shortcut.trim();
|
|
3105
|
+
if (!chord) {
|
|
3106
|
+
return undefined;
|
|
3107
|
+
}
|
|
3108
|
+
return { keys: [chord] };
|
|
1577
3109
|
}
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
}
|
|
1587
|
-
// Otherwise, interpolate all {{ ... }} expressions in the string
|
|
1588
|
-
const regex = /\{\{\s*([\s\S]*?)\s*\}\}/g;
|
|
1589
|
-
// Collect all matches and their positions
|
|
1590
|
-
const matches = [];
|
|
1591
|
-
let match;
|
|
1592
|
-
while ((match = regex.exec(templateExpression)) !== null) {
|
|
1593
|
-
matches.push({
|
|
1594
|
-
expression: match[1],
|
|
1595
|
-
start: match.index,
|
|
1596
|
-
end: regex.lastIndex,
|
|
1597
|
-
raw: match[0],
|
|
1598
|
-
});
|
|
1599
|
-
}
|
|
1600
|
-
// Evaluate all expressions in parallel
|
|
1601
|
-
const values = await Promise.all(matches.map((m) => {
|
|
1602
|
-
const sandbox = this.getOrCompileFunction(m.expression);
|
|
1603
|
-
return sandbox(scope);
|
|
1604
|
-
}));
|
|
1605
|
-
// Reconstruct the string with evaluated values
|
|
1606
|
-
let result = '';
|
|
1607
|
-
let lastIndex = 0;
|
|
1608
|
-
matches.forEach((m, i) => {
|
|
1609
|
-
result += templateExpression.slice(lastIndex, m.start);
|
|
1610
|
-
const value = values[i];
|
|
1611
|
-
result += value !== undefined && value !== null ? value : '';
|
|
1612
|
-
lastIndex = m.end;
|
|
1613
|
-
});
|
|
1614
|
-
result += templateExpression.slice(lastIndex);
|
|
1615
|
-
return result;
|
|
3110
|
+
return normalizeKeyboardShortcutConfig(shortcut);
|
|
3111
|
+
}
|
|
3112
|
+
/**
|
|
3113
|
+
* Normalizes an array of declarative shortcuts. Empty or invalid entries are skipped.
|
|
3114
|
+
*/
|
|
3115
|
+
function normalizeKeyboardShortcuts(shortcuts) {
|
|
3116
|
+
if (!shortcuts?.length) {
|
|
3117
|
+
return [];
|
|
1616
3118
|
}
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
3119
|
+
const result = [];
|
|
3120
|
+
for (const shortcut of shortcuts) {
|
|
3121
|
+
const normalized = normalizeKeyboardShortcut(shortcut);
|
|
3122
|
+
if (normalized) {
|
|
3123
|
+
result.push(normalized);
|
|
1620
3124
|
}
|
|
1621
|
-
return false;
|
|
1622
3125
|
}
|
|
1623
|
-
|
|
1624
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPExpressionEvaluatorService, providedIn: 'root' }); }
|
|
3126
|
+
return result;
|
|
1625
3127
|
}
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
3128
|
+
/** Returns the first chord from declarative shortcuts (for UI hints such as `ax-kbd`). */
|
|
3129
|
+
function getPrimaryKeyboardShortcutChord(shortcuts) {
|
|
3130
|
+
return normalizeKeyboardShortcuts(shortcuts)[0]?.keys[0];
|
|
3131
|
+
}
|
|
3132
|
+
function normalizeKeyboardShortcutConfig(shortcut) {
|
|
3133
|
+
const keys = Array.isArray(shortcut.keys) ? shortcut.keys : [shortcut.keys];
|
|
3134
|
+
const normalizedKeys = keys.map((chord) => chord.trim()).filter(Boolean);
|
|
3135
|
+
if (normalizedKeys.length === 0) {
|
|
3136
|
+
return undefined;
|
|
3137
|
+
}
|
|
3138
|
+
return {
|
|
3139
|
+
keys: normalizedKeys,
|
|
3140
|
+
allowInEditableFields: shortcut.allowInEditableFields,
|
|
3141
|
+
when: shortcut.when,
|
|
3142
|
+
directionBehavior: shortcut.directionBehavior,
|
|
3143
|
+
};
|
|
3144
|
+
}
|
|
3145
|
+
//#endregion
|
|
1630
3146
|
|
|
1631
|
-
|
|
3147
|
+
//#region ---- Imports ----
|
|
3148
|
+
//#endregion
|
|
3149
|
+
class AXPKeyboardShortcutRegistry {
|
|
1632
3150
|
constructor() {
|
|
1633
|
-
|
|
1634
|
-
this.
|
|
3151
|
+
//#region ---- Services & Dependencies ----
|
|
3152
|
+
this.document = inject(DOCUMENT);
|
|
3153
|
+
this.localeService = inject(AXLocaleService);
|
|
3154
|
+
this.activeProfile = toSignal(this.localeService.profileChanged$, {
|
|
3155
|
+
initialValue: this.localeService.activeProfile(),
|
|
3156
|
+
});
|
|
3157
|
+
this.registrations = signal([], ...(ngDevMode ? [{ debugName: "registrations" }] : /* istanbul ignore next */ []));
|
|
3158
|
+
this.helpDialogHandler = null;
|
|
3159
|
+
this.listenerAttached = false;
|
|
3160
|
+
//#endregion
|
|
3161
|
+
//#region ---- Computed Properties ----
|
|
3162
|
+
/** Active shortcut entries for the help dialog. */
|
|
3163
|
+
this.helpEntries = computed(() => {
|
|
3164
|
+
this.activeProfile();
|
|
3165
|
+
return this.buildHelpEntries();
|
|
3166
|
+
}, ...(ngDevMode ? [{ debugName: "helpEntries" }] : /* istanbul ignore next */ []));
|
|
3167
|
+
this.onDocumentKeyDown = (event) => {
|
|
3168
|
+
if (event.key === 'Escape' && shouldDeferEscapeToOpenOverlay(this.document)) {
|
|
3169
|
+
return;
|
|
3170
|
+
}
|
|
3171
|
+
if (event.key === 'F1') {
|
|
3172
|
+
event.preventDefault();
|
|
3173
|
+
event.stopImmediatePropagation();
|
|
3174
|
+
if (hasForegroundOverlayLayer(this.document)) {
|
|
3175
|
+
return;
|
|
3176
|
+
}
|
|
3177
|
+
}
|
|
3178
|
+
const candidates = this.resolveActiveCandidates(event);
|
|
3179
|
+
if (candidates.length === 0) {
|
|
3180
|
+
return;
|
|
3181
|
+
}
|
|
3182
|
+
const match = candidates[0];
|
|
3183
|
+
event.preventDefault();
|
|
3184
|
+
event.stopImmediatePropagation();
|
|
3185
|
+
void match.binding.handler(event);
|
|
3186
|
+
};
|
|
1635
3187
|
}
|
|
3188
|
+
//#endregion
|
|
3189
|
+
//#region ---- Public Methods ----
|
|
1636
3190
|
/**
|
|
1637
|
-
*
|
|
3191
|
+
* Registers a scope of shortcuts. Unregisters automatically when `owner` is destroyed.
|
|
1638
3192
|
*/
|
|
1639
|
-
|
|
1640
|
-
|
|
3193
|
+
register(options) {
|
|
3194
|
+
const { owner, ...registrationOptions } = options;
|
|
3195
|
+
const registrationId = `${options.id}-${this.createRegistrationId()}`;
|
|
3196
|
+
const entry = {
|
|
3197
|
+
...registrationOptions,
|
|
3198
|
+
registrationId,
|
|
3199
|
+
priority: options.priority ?? 0,
|
|
3200
|
+
};
|
|
3201
|
+
this.registrations.update((items) => [
|
|
3202
|
+
...items.filter((item) => item.id !== options.id),
|
|
3203
|
+
entry,
|
|
3204
|
+
]);
|
|
3205
|
+
this.ensureListener();
|
|
3206
|
+
try {
|
|
3207
|
+
owner.onDestroy(() => {
|
|
3208
|
+
this.unregister(registrationId);
|
|
3209
|
+
});
|
|
3210
|
+
}
|
|
3211
|
+
catch {
|
|
3212
|
+
// Owner view was already destroyed (e.g. async registration after navigation).
|
|
3213
|
+
this.unregister(registrationId);
|
|
3214
|
+
}
|
|
1641
3215
|
}
|
|
1642
3216
|
/**
|
|
1643
|
-
*
|
|
1644
|
-
* All providers with the given key will be executed.
|
|
1645
|
-
* Execution is not awaited.
|
|
3217
|
+
* Wires the F1 help dialog opener (provided by platform/common).
|
|
1646
3218
|
*/
|
|
1647
|
-
|
|
1648
|
-
this.
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
3219
|
+
setHelpDialogHandler(handler) {
|
|
3220
|
+
this.helpDialogHandler = handler;
|
|
3221
|
+
this.ensureHelpShortcutRegistered();
|
|
3222
|
+
}
|
|
3223
|
+
/** OS-aware display string for UI hints. */
|
|
3224
|
+
formatDisplayKeys(chords) {
|
|
3225
|
+
return formatKeyboardShortcutChords(chords, { isRtl: this.isRtlProfile() });
|
|
3226
|
+
}
|
|
3227
|
+
//#endregion
|
|
3228
|
+
//#region ---- Private Methods ----
|
|
3229
|
+
ensureHelpShortcutRegistered() {
|
|
3230
|
+
const alreadyRegistered = this.registrations().some((entry) => entry.id === 'system:keyboard-shortcuts-help');
|
|
3231
|
+
if (alreadyRegistered || !this.helpDialogHandler) {
|
|
3232
|
+
return;
|
|
3233
|
+
}
|
|
3234
|
+
const handler = this.helpDialogHandler;
|
|
3235
|
+
const entry = {
|
|
3236
|
+
registrationId: 'system:keyboard-shortcuts-help',
|
|
3237
|
+
id: 'system:keyboard-shortcuts-help',
|
|
3238
|
+
priority: -1,
|
|
3239
|
+
scope: '@general:keyboard-shortcuts.groups.system',
|
|
3240
|
+
shortcuts: [
|
|
3241
|
+
{
|
|
3242
|
+
keys: ['f1'],
|
|
3243
|
+
title: '@general:keyboard-shortcuts.help-action',
|
|
3244
|
+
allowInEditableFields: true,
|
|
3245
|
+
handler: () => {
|
|
3246
|
+
void handler();
|
|
3247
|
+
},
|
|
3248
|
+
},
|
|
3249
|
+
],
|
|
3250
|
+
};
|
|
3251
|
+
this.registrations.update((items) => [...items, entry]);
|
|
3252
|
+
this.ensureListener();
|
|
3253
|
+
}
|
|
3254
|
+
createRegistrationId() {
|
|
3255
|
+
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
|
|
3256
|
+
return crypto.randomUUID();
|
|
3257
|
+
}
|
|
3258
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
3259
|
+
}
|
|
3260
|
+
unregister(registrationId) {
|
|
3261
|
+
this.registrations.update((items) => items.filter((item) => item.registrationId !== registrationId));
|
|
3262
|
+
}
|
|
3263
|
+
ensureListener() {
|
|
3264
|
+
if (this.listenerAttached || typeof this.document === 'undefined') {
|
|
3265
|
+
return;
|
|
3266
|
+
}
|
|
3267
|
+
this.document.addEventListener('keydown', this.onDocumentKeyDown, true);
|
|
3268
|
+
this.listenerAttached = true;
|
|
3269
|
+
}
|
|
3270
|
+
resolveActiveCandidates(event) {
|
|
3271
|
+
const editable = isKeyboardTargetInsideEditableField(event.target);
|
|
3272
|
+
const candidates = [];
|
|
3273
|
+
for (const registration of this.registrations()) {
|
|
3274
|
+
if (!this.isRegistrationActive(registration)) {
|
|
3275
|
+
continue;
|
|
3276
|
+
}
|
|
3277
|
+
const priority = registration.priority ?? 0;
|
|
3278
|
+
for (const binding of registration.shortcuts) {
|
|
3279
|
+
if (binding.when && !binding.when()) {
|
|
3280
|
+
continue;
|
|
1655
3281
|
}
|
|
1656
|
-
|
|
1657
|
-
|
|
3282
|
+
if (editable && !binding.allowInEditableFields) {
|
|
3283
|
+
continue;
|
|
1658
3284
|
}
|
|
1659
|
-
|
|
1660
|
-
|
|
3285
|
+
for (const chord of binding.keys) {
|
|
3286
|
+
const directionBehavior = resolveEffectiveDirectionBehavior(chord, binding.directionBehavior);
|
|
3287
|
+
if (!matchesKeyboardShortcutChord(event, chord, {
|
|
3288
|
+
directionBehavior,
|
|
3289
|
+
isRtl: this.isRtlProfile(),
|
|
3290
|
+
})) {
|
|
3291
|
+
continue;
|
|
3292
|
+
}
|
|
3293
|
+
candidates.push({
|
|
3294
|
+
priority,
|
|
3295
|
+
registration,
|
|
3296
|
+
binding,
|
|
3297
|
+
chord,
|
|
3298
|
+
});
|
|
3299
|
+
break;
|
|
3300
|
+
}
|
|
3301
|
+
}
|
|
3302
|
+
}
|
|
3303
|
+
return candidates.sort((a, b) => b.priority - a.priority);
|
|
1661
3304
|
}
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
* Returns the final merged data after all hooks are executed.
|
|
1666
|
-
*/
|
|
1667
|
-
async runAsync(key, initialData) {
|
|
1668
|
-
const providers = (await this.resolveProviders())
|
|
1669
|
-
.filter(p => p.key === key)
|
|
1670
|
-
.sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
|
|
1671
|
-
let data = initialData;
|
|
1672
|
-
for (const p of providers) {
|
|
1673
|
-
data = await Promise.resolve(runInInjectionContext(this.injector, () => p.execute(data)));
|
|
3305
|
+
isRegistrationActive(registration) {
|
|
3306
|
+
if (registration.when && !registration.when()) {
|
|
3307
|
+
return false;
|
|
1674
3308
|
}
|
|
1675
|
-
|
|
3309
|
+
const element = registration.elementRef?.nativeElement;
|
|
3310
|
+
if (element && !this.document.contains(element)) {
|
|
3311
|
+
return false;
|
|
3312
|
+
}
|
|
3313
|
+
return true;
|
|
3314
|
+
}
|
|
3315
|
+
isRtlProfile() {
|
|
3316
|
+
return this.activeProfile()?.i18nMeta?.rtl === true;
|
|
3317
|
+
}
|
|
3318
|
+
buildHelpEntries() {
|
|
3319
|
+
const entries = [];
|
|
3320
|
+
const isRtl = this.isRtlProfile();
|
|
3321
|
+
for (const registration of this.registrations()) {
|
|
3322
|
+
if (!this.isRegistrationActive(registration)) {
|
|
3323
|
+
continue;
|
|
3324
|
+
}
|
|
3325
|
+
if (registration.id === 'system:keyboard-shortcuts-help') {
|
|
3326
|
+
continue;
|
|
3327
|
+
}
|
|
3328
|
+
const priority = registration.priority ?? 0;
|
|
3329
|
+
for (const binding of registration.shortcuts) {
|
|
3330
|
+
const displayChords = binding.keys.map((chord) => {
|
|
3331
|
+
const directionBehavior = resolveEffectiveDirectionBehavior(chord, binding.directionBehavior);
|
|
3332
|
+
return resolveDisplayShortcutChord(chord, isRtl, directionBehavior);
|
|
3333
|
+
});
|
|
3334
|
+
entries.push({
|
|
3335
|
+
registrationId: registration.registrationId,
|
|
3336
|
+
bindingTitle: binding.title,
|
|
3337
|
+
chords: [...binding.keys],
|
|
3338
|
+
displayChords,
|
|
3339
|
+
displayKeys: formatKeyboardShortcutChords(displayChords, { skipDirectionResolve: true }),
|
|
3340
|
+
scope: registration.scope,
|
|
3341
|
+
group: binding.group ?? registration.group,
|
|
3342
|
+
priority,
|
|
3343
|
+
});
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
return entries.sort((a, b) => b.priority - a.priority || a.bindingTitle.localeCompare(b.bindingTitle));
|
|
1676
3347
|
}
|
|
1677
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1678
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
3348
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPKeyboardShortcutRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3349
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPKeyboardShortcutRegistry, providedIn: 'root' }); }
|
|
1679
3350
|
}
|
|
1680
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
3351
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPKeyboardShortcutRegistry, decorators: [{
|
|
1681
3352
|
type: Injectable,
|
|
1682
|
-
args: [{
|
|
3353
|
+
args: [{
|
|
3354
|
+
providedIn: 'root',
|
|
3355
|
+
}]
|
|
1683
3356
|
}] });
|
|
1684
3357
|
|
|
1685
3358
|
//#region ---- Imports ----
|
|
@@ -1957,37 +3630,108 @@ class AXPModuleManifestRegistry {
|
|
|
1957
3630
|
}
|
|
1958
3631
|
return { missingModules, missingFeatures };
|
|
1959
3632
|
}
|
|
1960
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1961
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
3633
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPModuleManifestRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3634
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPModuleManifestRegistry, providedIn: 'root' }); }
|
|
1962
3635
|
}
|
|
1963
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
3636
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPModuleManifestRegistry, decorators: [{
|
|
1964
3637
|
type: Injectable,
|
|
1965
3638
|
args: [{ providedIn: 'root' }]
|
|
1966
3639
|
}] });
|
|
1967
3640
|
|
|
3641
|
+
//#region ---- Imports ----
|
|
3642
|
+
//#endregion
|
|
3643
|
+
//#region ---- Module manifests data source ----
|
|
3644
|
+
/**
|
|
3645
|
+
* Data source definition for module names/titles from {@link AXPModuleManifestRegistry}.
|
|
3646
|
+
*/
|
|
3647
|
+
class AXPModuleManifestsDataSourceDefinition {
|
|
3648
|
+
constructor() {
|
|
3649
|
+
//#region ---- Services & Dependencies ----
|
|
3650
|
+
this.manifestRegistry = inject(AXPModuleManifestRegistry);
|
|
3651
|
+
//#endregion
|
|
3652
|
+
}
|
|
3653
|
+
//#endregion
|
|
3654
|
+
//#region ---- Public API ----
|
|
3655
|
+
async items() {
|
|
3656
|
+
return [
|
|
3657
|
+
{
|
|
3658
|
+
name: AXPPlatformCoreDataSourceKeys.ModuleManifests,
|
|
3659
|
+
title: 'Module manifests',
|
|
3660
|
+
source: () => new AXDataSource({
|
|
3661
|
+
key: 'id',
|
|
3662
|
+
load: async () => {
|
|
3663
|
+
await this.manifestRegistry.initialize();
|
|
3664
|
+
const list = this.manifestRegistry.getAll().map((m) => ({
|
|
3665
|
+
id: m.name,
|
|
3666
|
+
title: m.title || m.name,
|
|
3667
|
+
}));
|
|
3668
|
+
return { items: list, total: list.length };
|
|
3669
|
+
},
|
|
3670
|
+
byKey: async (key) => {
|
|
3671
|
+
await this.manifestRegistry.initialize();
|
|
3672
|
+
const m = this.manifestRegistry.getAll().find((x) => x.name === key);
|
|
3673
|
+
return m ? { id: m.name, title: m.title || m.name } : undefined;
|
|
3674
|
+
},
|
|
3675
|
+
pageSize: 1000,
|
|
3676
|
+
}),
|
|
3677
|
+
columns: [
|
|
3678
|
+
{
|
|
3679
|
+
name: 'id',
|
|
3680
|
+
title: 'ID',
|
|
3681
|
+
datatype: 'string',
|
|
3682
|
+
type: 'text-editor',
|
|
3683
|
+
},
|
|
3684
|
+
{
|
|
3685
|
+
name: 'title',
|
|
3686
|
+
title: 'Title',
|
|
3687
|
+
datatype: 'string',
|
|
3688
|
+
type: 'text-editor',
|
|
3689
|
+
},
|
|
3690
|
+
],
|
|
3691
|
+
filters: [
|
|
3692
|
+
{
|
|
3693
|
+
field: 'title',
|
|
3694
|
+
title: 'Title',
|
|
3695
|
+
operator: { type: 'equal' },
|
|
3696
|
+
widget: { type: 'text-editor' },
|
|
3697
|
+
filterType: { advance: true, inline: true },
|
|
3698
|
+
},
|
|
3699
|
+
],
|
|
3700
|
+
textField: { name: 'title', title: 'Title' },
|
|
3701
|
+
valueField: { name: 'id', title: 'ID' },
|
|
3702
|
+
},
|
|
3703
|
+
];
|
|
3704
|
+
}
|
|
3705
|
+
}
|
|
3706
|
+
//#endregion
|
|
3707
|
+
|
|
1968
3708
|
class AXPAppStartUpService {
|
|
1969
3709
|
constructor() {
|
|
3710
|
+
this.translationService = inject(AXTranslationService);
|
|
1970
3711
|
this.tasks = [];
|
|
1971
3712
|
}
|
|
3713
|
+
//#endregion
|
|
3714
|
+
//#region ---- Public Methods ----
|
|
1972
3715
|
registerTask(task) {
|
|
1973
3716
|
this.tasks.push(task);
|
|
1974
3717
|
}
|
|
1975
3718
|
async runAllTasks() {
|
|
1976
3719
|
for (const task of this.tasks.sort((a, b) => a.priority - b.priority)) {
|
|
1977
|
-
this.updateStatus(task.statusText);
|
|
3720
|
+
await this.updateStatus(task.statusText);
|
|
1978
3721
|
await task.run();
|
|
1979
3722
|
}
|
|
1980
3723
|
}
|
|
1981
|
-
updateStatus(status) {
|
|
3724
|
+
async updateStatus(status) {
|
|
1982
3725
|
const loadingText = document.querySelector('#loadingText');
|
|
1983
3726
|
if (loadingText) {
|
|
1984
|
-
|
|
3727
|
+
const translatedStatus = this.translationService.resolve(status);
|
|
3728
|
+
loadingText.innerHTML = await this.translationService.translateAsync(translatedStatus);
|
|
1985
3729
|
}
|
|
1986
3730
|
}
|
|
1987
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1988
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
3731
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPAppStartUpService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3732
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPAppStartUpService, providedIn: 'root' }); }
|
|
1989
3733
|
}
|
|
1990
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
3734
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPAppStartUpService, decorators: [{
|
|
1991
3735
|
type: Injectable,
|
|
1992
3736
|
args: [{
|
|
1993
3737
|
providedIn: 'root',
|
|
@@ -2018,24 +3762,57 @@ class AXPModuleManifestModule {
|
|
|
2018
3762
|
// Register manifest initialization task
|
|
2019
3763
|
appInitService.registerTask({
|
|
2020
3764
|
name: 'ModuleManifests',
|
|
2021
|
-
statusText: '
|
|
3765
|
+
statusText: '@general:app-startup.loading-module-manifests',
|
|
2022
3766
|
priority: 1, // Load early, before features/permissions
|
|
2023
3767
|
run: async () => {
|
|
2024
3768
|
await manifestRegistry.initialize();
|
|
2025
3769
|
},
|
|
2026
3770
|
});
|
|
2027
3771
|
}
|
|
2028
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
2029
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "
|
|
2030
|
-
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "
|
|
3772
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPModuleManifestModule, deps: [{ token: AXPAppStartUpService }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
3773
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: AXPModuleManifestModule }); }
|
|
3774
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPModuleManifestModule }); }
|
|
2031
3775
|
}
|
|
2032
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
3776
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPModuleManifestModule, decorators: [{
|
|
2033
3777
|
type: NgModule,
|
|
2034
3778
|
args: [{
|
|
2035
3779
|
providers: [],
|
|
2036
3780
|
}]
|
|
2037
3781
|
}], ctorParameters: () => [{ type: AXPAppStartUpService }, { type: i0.Injector }] });
|
|
2038
3782
|
|
|
3783
|
+
//#region ---- Imports ----
|
|
3784
|
+
//#endregion
|
|
3785
|
+
//#region ---- Compare & sort ----
|
|
3786
|
+
const DEFAULT_COLLATOR_OPTIONS = { sensitivity: 'base' };
|
|
3787
|
+
/**
|
|
3788
|
+
* Locale-aware comparison of {@link AXPMultiLanguageString} values (plain string, `@scope:key`, or locale map).
|
|
3789
|
+
* Resolves each side with {@link resolveMultiLanguageString} for `locale`, then compares with `String#localeCompare`.
|
|
3790
|
+
*/
|
|
3791
|
+
function compareMultiLanguageStrings(a, b, locale, collatorOptions = DEFAULT_COLLATOR_OPTIONS) {
|
|
3792
|
+
const sa = resolveMultiLanguageString(a ?? '', locale);
|
|
3793
|
+
const sb = resolveMultiLanguageString(b ?? '', locale);
|
|
3794
|
+
return sa.localeCompare(sb, locale, collatorOptions);
|
|
3795
|
+
}
|
|
3796
|
+
/**
|
|
3797
|
+
* Stable sort copy: orders items by resolved string for `getValue(item)` using the active `locale`.
|
|
3798
|
+
*/
|
|
3799
|
+
function sortByMultiLanguageString(items, getValue, locale, collatorOptions) {
|
|
3800
|
+
return [...items].sort((x, y) => compareMultiLanguageStrings(getValue(x), getValue(y), locale, collatorOptions));
|
|
3801
|
+
}
|
|
3802
|
+
/**
|
|
3803
|
+
* True when the resolved value contains `query` (case-insensitive) for `locale`.
|
|
3804
|
+
* Useful for search/filter flows over {@link AXPMultiLanguageString}.
|
|
3805
|
+
*/
|
|
3806
|
+
function searchInMultiLanguageString(value, query, locale) {
|
|
3807
|
+
const q = query.trim().toLocaleLowerCase(locale);
|
|
3808
|
+
if (!q) {
|
|
3809
|
+
return true;
|
|
3810
|
+
}
|
|
3811
|
+
const text = resolveMultiLanguageString(value ?? '', locale).toLocaleLowerCase(locale);
|
|
3812
|
+
return text.includes(q);
|
|
3813
|
+
}
|
|
3814
|
+
//#endregion
|
|
3815
|
+
|
|
2039
3816
|
//#region ---- Tag Types ----
|
|
2040
3817
|
//#endregion
|
|
2041
3818
|
|
|
@@ -2187,10 +3964,10 @@ class AXPTagService {
|
|
|
2187
3964
|
const uniqueTags = allTags.filter((tag, index, self) => index === self.findIndex((t) => t.title.toLowerCase() === tag.title.toLowerCase()));
|
|
2188
3965
|
this.cache = uniqueTags;
|
|
2189
3966
|
}
|
|
2190
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
2191
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
3967
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTagService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3968
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTagService, providedIn: 'root' }); }
|
|
2192
3969
|
}
|
|
2193
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
3970
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTagService, decorators: [{
|
|
2194
3971
|
type: Injectable,
|
|
2195
3972
|
args: [{
|
|
2196
3973
|
providedIn: 'root',
|
|
@@ -3121,14 +4898,25 @@ function getNestedKeys(obj, prefix = '') {
|
|
|
3121
4898
|
}
|
|
3122
4899
|
return keys;
|
|
3123
4900
|
}
|
|
4901
|
+
/** Normalizes definition categories to an array for iteration and serialization. */
|
|
4902
|
+
function normalizeDefinitionCategories(categories) {
|
|
4903
|
+
if (categories == null) {
|
|
4904
|
+
return [];
|
|
4905
|
+
}
|
|
4906
|
+
return Array.isArray(categories) ? categories : [categories];
|
|
4907
|
+
}
|
|
4908
|
+
|
|
4909
|
+
//#region ---- Imports ----
|
|
4910
|
+
//#endregion
|
|
3124
4911
|
|
|
3125
4912
|
function applySystemActionDefault(action, type) {
|
|
3126
4913
|
const systemAction = getSystemActions(type);
|
|
3127
4914
|
return {
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
4915
|
+
...action,
|
|
4916
|
+
name: action.name ?? systemAction.key,
|
|
4917
|
+
title: action.title ?? systemAction.title,
|
|
4918
|
+
icon: action.icon ?? systemAction.icon,
|
|
4919
|
+
color: action.color ?? systemAction.color,
|
|
3132
4920
|
};
|
|
3133
4921
|
}
|
|
3134
4922
|
|
|
@@ -3153,8 +4941,6 @@ class AXPIconLogoConfig {
|
|
|
3153
4941
|
|
|
3154
4942
|
var AXPPlatformScope;
|
|
3155
4943
|
(function (AXPPlatformScope) {
|
|
3156
|
-
AXPPlatformScope["Environment"] = "C";
|
|
3157
|
-
AXPPlatformScope["Global"] = "G";
|
|
3158
4944
|
AXPPlatformScope["Platform"] = "P";
|
|
3159
4945
|
AXPPlatformScope["Tenant"] = "T";
|
|
3160
4946
|
AXPPlatformScope["User"] = "U";
|
|
@@ -3162,8 +4948,6 @@ var AXPPlatformScope;
|
|
|
3162
4948
|
;
|
|
3163
4949
|
function resolvePlatformScopeKey(name) {
|
|
3164
4950
|
const scopeMap = {
|
|
3165
|
-
environment: AXPPlatformScope.Environment,
|
|
3166
|
-
global: AXPPlatformScope.Global,
|
|
3167
4951
|
platform: AXPPlatformScope.Platform,
|
|
3168
4952
|
tenant: AXPPlatformScope.Tenant,
|
|
3169
4953
|
user: AXPPlatformScope.User,
|
|
@@ -3172,8 +4956,6 @@ function resolvePlatformScopeKey(name) {
|
|
|
3172
4956
|
}
|
|
3173
4957
|
function resolvePlatformScopeName(scope) {
|
|
3174
4958
|
const scopeMap = {
|
|
3175
|
-
C: 'environment',
|
|
3176
|
-
G: 'global',
|
|
3177
4959
|
P: 'platform',
|
|
3178
4960
|
T: 'tenant',
|
|
3179
4961
|
U: 'user',
|
|
@@ -3193,7 +4975,7 @@ var AXPExportTemplateToken;
|
|
|
3193
4975
|
class AXPCountdownPipe {
|
|
3194
4976
|
constructor() {
|
|
3195
4977
|
this.calendarService = inject(AXCalendarService);
|
|
3196
|
-
this.countdownSignal = signal(this.setupTimer(), ...(ngDevMode ? [{ debugName: "countdownSignal" }] : []));
|
|
4978
|
+
this.countdownSignal = signal(this.setupTimer(), ...(ngDevMode ? [{ debugName: "countdownSignal" }] : /* istanbul ignore next */ []));
|
|
3197
4979
|
this.targetDate = 0;
|
|
3198
4980
|
this.prevValue = 0;
|
|
3199
4981
|
}
|
|
@@ -3222,10 +5004,10 @@ class AXPCountdownPipe {
|
|
|
3222
5004
|
updateTargetDate(value) {
|
|
3223
5005
|
this.targetDate = new Date(value).getTime();
|
|
3224
5006
|
}
|
|
3225
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
3226
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "
|
|
5007
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPCountdownPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
5008
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: AXPCountdownPipe, isStandalone: true, name: "countdown", pure: false }); }
|
|
3227
5009
|
}
|
|
3228
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
5010
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPCountdownPipe, decorators: [{
|
|
3229
5011
|
type: Pipe,
|
|
3230
5012
|
args: [{
|
|
3231
5013
|
name: 'countdown',
|
|
@@ -3235,6 +5017,31 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
|
|
|
3235
5017
|
}] });
|
|
3236
5018
|
|
|
3237
5019
|
const loggingEnabled = false; // Set to true to enable logging, false to disable
|
|
5020
|
+
//#region ---- Multilingual string helpers ----
|
|
5021
|
+
/**
|
|
5022
|
+
* Lowercased text for client-side filtering: plain string or all locale values joined.
|
|
5023
|
+
*/
|
|
5024
|
+
function toFilterSearchText(value) {
|
|
5025
|
+
if (typeof value === 'string') {
|
|
5026
|
+
return value.toLowerCase();
|
|
5027
|
+
}
|
|
5028
|
+
if (isLocaleStringMap(value)) {
|
|
5029
|
+
return Object.values(value).join(' ').toLowerCase();
|
|
5030
|
+
}
|
|
5031
|
+
return undefined;
|
|
5032
|
+
}
|
|
5033
|
+
function filterValuesAreEqual(itemRaw, condRaw) {
|
|
5034
|
+
if (typeof condRaw === 'string' && isLocaleStringMap(itemRaw)) {
|
|
5035
|
+
return Object.values(itemRaw).some((v) => v.toLowerCase() === condRaw.toLowerCase());
|
|
5036
|
+
}
|
|
5037
|
+
if (typeof itemRaw === 'string' && isLocaleStringMap(condRaw)) {
|
|
5038
|
+
return Object.values(condRaw).some((v) => v.toLowerCase() === itemRaw.toLowerCase());
|
|
5039
|
+
}
|
|
5040
|
+
const itemScalar = typeof itemRaw === 'string' ? itemRaw.toLowerCase() : itemRaw;
|
|
5041
|
+
const condScalar = typeof condRaw === 'string' ? condRaw.toLowerCase() : condRaw;
|
|
5042
|
+
return isEqual(itemScalar, condScalar);
|
|
5043
|
+
}
|
|
5044
|
+
//#endregion
|
|
3238
5045
|
function applyCondition(item, condition) {
|
|
3239
5046
|
const rawValue = condition.field ? get(item, condition.field) : null;
|
|
3240
5047
|
const itemValue = typeof rawValue === 'string' ? rawValue.toLowerCase() : rawValue;
|
|
@@ -3255,7 +5062,12 @@ function applyCondition(item, condition) {
|
|
|
3255
5062
|
switch (op) {
|
|
3256
5063
|
case 'equal':
|
|
3257
5064
|
case 'eq':
|
|
3258
|
-
|
|
5065
|
+
if (!isNil(condition.field) && condition.field !== '') {
|
|
5066
|
+
result = filterValuesAreEqual(rawValue, condition.value);
|
|
5067
|
+
}
|
|
5068
|
+
else {
|
|
5069
|
+
result = isEqual(valueToCompare, conditionValue);
|
|
5070
|
+
}
|
|
3259
5071
|
if (loggingEnabled) {
|
|
3260
5072
|
console.log('Equal check result:', result);
|
|
3261
5073
|
}
|
|
@@ -3263,7 +5075,12 @@ function applyCondition(item, condition) {
|
|
|
3263
5075
|
case 'notEqual':
|
|
3264
5076
|
case 'ne':
|
|
3265
5077
|
case 'neq':
|
|
3266
|
-
|
|
5078
|
+
if (!isNil(condition.field) && condition.field !== '') {
|
|
5079
|
+
result = !filterValuesAreEqual(rawValue, condition.value);
|
|
5080
|
+
}
|
|
5081
|
+
else {
|
|
5082
|
+
result = !isEqual(valueToCompare, conditionValue);
|
|
5083
|
+
}
|
|
3267
5084
|
if (loggingEnabled) {
|
|
3268
5085
|
console.log('Not equal check result:', result);
|
|
3269
5086
|
}
|
|
@@ -3296,12 +5113,15 @@ function applyCondition(item, condition) {
|
|
|
3296
5113
|
console.log('Less than or equal check result:', result);
|
|
3297
5114
|
}
|
|
3298
5115
|
break;
|
|
3299
|
-
case 'contains':
|
|
3300
|
-
|
|
3301
|
-
|
|
5116
|
+
case 'contains': {
|
|
5117
|
+
const needle = toFilterSearchText(condition.value) ??
|
|
5118
|
+
(condition.value == null ? '' : String(condition.value).toLowerCase());
|
|
5119
|
+
const haystack = toFilterSearchText(rawValue);
|
|
5120
|
+
if (haystack !== undefined) {
|
|
5121
|
+
result = includes(haystack, needle);
|
|
3302
5122
|
}
|
|
3303
|
-
else if (Array.isArray(
|
|
3304
|
-
result = includes(
|
|
5123
|
+
else if (Array.isArray(rawValue)) {
|
|
5124
|
+
result = includes(rawValue.map((val) => toFilterSearchText(val) ?? val.toString().toLowerCase()), needle);
|
|
3305
5125
|
}
|
|
3306
5126
|
else {
|
|
3307
5127
|
result = false;
|
|
@@ -3310,13 +5130,18 @@ function applyCondition(item, condition) {
|
|
|
3310
5130
|
console.log('Contains check result:', result);
|
|
3311
5131
|
}
|
|
3312
5132
|
break;
|
|
5133
|
+
}
|
|
3313
5134
|
case 'in':
|
|
3314
5135
|
if (Array.isArray(conditionValue)) {
|
|
3315
5136
|
if (typeof valueToCompare === 'string') {
|
|
3316
|
-
result = conditionValue.some(val => typeof val === 'string' ? val.toLowerCase() === valueToCompare : val === valueToCompare);
|
|
5137
|
+
result = conditionValue.some((val) => typeof val === 'string' ? val.toLowerCase() === valueToCompare : val === valueToCompare);
|
|
5138
|
+
}
|
|
5139
|
+
else if (isLocaleStringMap(rawValue)) {
|
|
5140
|
+
const localeValues = Object.values(rawValue).map((v) => v.toLowerCase());
|
|
5141
|
+
result = localeValues.some((localeVal) => conditionValue.some((val) => typeof val === 'string' ? val.toLowerCase() === localeVal : val === localeVal));
|
|
3317
5142
|
}
|
|
3318
5143
|
else if (Array.isArray(valueToCompare)) {
|
|
3319
|
-
result = valueToCompare.some(val => conditionValue.some(condVal => typeof val === 'string' && typeof condVal === 'string'
|
|
5144
|
+
result = valueToCompare.some((val) => conditionValue.some((condVal) => typeof val === 'string' && typeof condVal === 'string'
|
|
3320
5145
|
? val.toLowerCase() === condVal.toLowerCase()
|
|
3321
5146
|
: val === condVal));
|
|
3322
5147
|
}
|
|
@@ -3331,12 +5156,15 @@ function applyCondition(item, condition) {
|
|
|
3331
5156
|
console.log('In check result:', result);
|
|
3332
5157
|
}
|
|
3333
5158
|
break;
|
|
3334
|
-
case 'notContains':
|
|
3335
|
-
|
|
3336
|
-
|
|
5159
|
+
case 'notContains': {
|
|
5160
|
+
const needleNc = toFilterSearchText(condition.value) ??
|
|
5161
|
+
(condition.value == null ? '' : String(condition.value).toLowerCase());
|
|
5162
|
+
const haystackNc = toFilterSearchText(rawValue);
|
|
5163
|
+
if (haystackNc !== undefined) {
|
|
5164
|
+
result = !includes(haystackNc, needleNc);
|
|
3337
5165
|
}
|
|
3338
|
-
else if (Array.isArray(
|
|
3339
|
-
result = !includes(
|
|
5166
|
+
else if (Array.isArray(rawValue)) {
|
|
5167
|
+
result = !includes(rawValue.map((val) => toFilterSearchText(val) ?? val.toString().toLowerCase()), needleNc);
|
|
3340
5168
|
}
|
|
3341
5169
|
else {
|
|
3342
5170
|
result = false;
|
|
@@ -3345,18 +5173,27 @@ function applyCondition(item, condition) {
|
|
|
3345
5173
|
console.log('Not contains check result:', result);
|
|
3346
5174
|
}
|
|
3347
5175
|
break;
|
|
3348
|
-
|
|
3349
|
-
|
|
5176
|
+
}
|
|
5177
|
+
case 'startsWith': {
|
|
5178
|
+
const prefix = toFilterSearchText(condition.value) ??
|
|
5179
|
+
(condition.value == null ? '' : String(condition.value).toLowerCase());
|
|
5180
|
+
const haystackSw = toFilterSearchText(rawValue);
|
|
5181
|
+
result = haystackSw !== undefined && startsWith(haystackSw, prefix);
|
|
3350
5182
|
if (loggingEnabled) {
|
|
3351
5183
|
console.log('Starts with check result:', result);
|
|
3352
5184
|
}
|
|
3353
5185
|
break;
|
|
3354
|
-
|
|
3355
|
-
|
|
5186
|
+
}
|
|
5187
|
+
case 'endsWith': {
|
|
5188
|
+
const suffix = toFilterSearchText(condition.value) ??
|
|
5189
|
+
(condition.value == null ? '' : String(condition.value).toLowerCase());
|
|
5190
|
+
const haystackEw = toFilterSearchText(rawValue);
|
|
5191
|
+
result = haystackEw !== undefined && endsWith(haystackEw, suffix);
|
|
3356
5192
|
if (loggingEnabled) {
|
|
3357
5193
|
console.log('Ends with check result:', result);
|
|
3358
5194
|
}
|
|
3359
5195
|
break;
|
|
5196
|
+
}
|
|
3360
5197
|
case 'isEmpty':
|
|
3361
5198
|
result = isEmpty(valueToCompare);
|
|
3362
5199
|
if (loggingEnabled) {
|
|
@@ -3508,45 +5345,45 @@ class AXPDeviceService {
|
|
|
3508
5345
|
/**
|
|
3509
5346
|
* Current screen size (Small, Medium, Large)
|
|
3510
5347
|
*/
|
|
3511
|
-
this.screenSize = signal(getScreenSize(), ...(ngDevMode ? [{ debugName: "screenSize" }] : []));
|
|
5348
|
+
this.screenSize = signal(getScreenSize(), ...(ngDevMode ? [{ debugName: "screenSize" }] : /* istanbul ignore next */ []));
|
|
3512
5349
|
/**
|
|
3513
5350
|
* Current device type (Mobile, Tablet, Desktop)
|
|
3514
5351
|
*/
|
|
3515
|
-
this.deviceType = signal(getDeviceType(), ...(ngDevMode ? [{ debugName: "deviceType" }] : []));
|
|
5352
|
+
this.deviceType = signal(getDeviceType(), ...(ngDevMode ? [{ debugName: "deviceType" }] : /* istanbul ignore next */ []));
|
|
3516
5353
|
/**
|
|
3517
5354
|
* Whether the device supports touch input
|
|
3518
5355
|
*/
|
|
3519
|
-
this.isTouchDevice = signal(isTouchDevice(), ...(ngDevMode ? [{ debugName: "isTouchDevice" }] : []));
|
|
5356
|
+
this.isTouchDevice = signal(isTouchDevice(), ...(ngDevMode ? [{ debugName: "isTouchDevice" }] : /* istanbul ignore next */ []));
|
|
3520
5357
|
//#endregion
|
|
3521
5358
|
//#region ---- Computed Signals ----
|
|
3522
5359
|
/**
|
|
3523
5360
|
* Whether the screen size is Small
|
|
3524
5361
|
*/
|
|
3525
|
-
this.isSmall = computed(() => this.screenSize() === AXPScreenSize.Small, ...(ngDevMode ? [{ debugName: "isSmall" }] : []));
|
|
5362
|
+
this.isSmall = computed(() => this.screenSize() === AXPScreenSize.Small, ...(ngDevMode ? [{ debugName: "isSmall" }] : /* istanbul ignore next */ []));
|
|
3526
5363
|
/**
|
|
3527
5364
|
* Whether the screen size is Medium
|
|
3528
5365
|
*/
|
|
3529
|
-
this.isMedium = computed(() => this.screenSize() === AXPScreenSize.Medium, ...(ngDevMode ? [{ debugName: "isMedium" }] : []));
|
|
5366
|
+
this.isMedium = computed(() => this.screenSize() === AXPScreenSize.Medium, ...(ngDevMode ? [{ debugName: "isMedium" }] : /* istanbul ignore next */ []));
|
|
3530
5367
|
/**
|
|
3531
5368
|
* Whether the screen size is Large
|
|
3532
5369
|
*/
|
|
3533
|
-
this.isLarge = computed(() => this.screenSize() === AXPScreenSize.Large, ...(ngDevMode ? [{ debugName: "isLarge" }] : []));
|
|
5370
|
+
this.isLarge = computed(() => this.screenSize() === AXPScreenSize.Large, ...(ngDevMode ? [{ debugName: "isLarge" }] : /* istanbul ignore next */ []));
|
|
3534
5371
|
/**
|
|
3535
5372
|
* Whether the device is Mobile
|
|
3536
5373
|
*/
|
|
3537
|
-
this.isMobileDevice = computed(() => this.deviceType() === AXPDeviceType.Mobile, ...(ngDevMode ? [{ debugName: "isMobileDevice" }] : []));
|
|
5374
|
+
this.isMobileDevice = computed(() => this.deviceType() === AXPDeviceType.Mobile, ...(ngDevMode ? [{ debugName: "isMobileDevice" }] : /* istanbul ignore next */ []));
|
|
3538
5375
|
/**
|
|
3539
5376
|
* Whether the device is Tablet
|
|
3540
5377
|
*/
|
|
3541
|
-
this.isTabletDevice = computed(() => this.deviceType() === AXPDeviceType.Tablet, ...(ngDevMode ? [{ debugName: "isTabletDevice" }] : []));
|
|
5378
|
+
this.isTabletDevice = computed(() => this.deviceType() === AXPDeviceType.Tablet, ...(ngDevMode ? [{ debugName: "isTabletDevice" }] : /* istanbul ignore next */ []));
|
|
3542
5379
|
/**
|
|
3543
5380
|
* Whether the device is Desktop
|
|
3544
5381
|
*/
|
|
3545
|
-
this.isDesktopDevice = computed(() => this.deviceType() === AXPDeviceType.Desktop, ...(ngDevMode ? [{ debugName: "isDesktopDevice" }] : []));
|
|
5382
|
+
this.isDesktopDevice = computed(() => this.deviceType() === AXPDeviceType.Desktop, ...(ngDevMode ? [{ debugName: "isDesktopDevice" }] : /* istanbul ignore next */ []));
|
|
3546
5383
|
/**
|
|
3547
5384
|
* Whether the device supports touch (alias for isTouchDevice)
|
|
3548
5385
|
*/
|
|
3549
|
-
this.isTouchScreen = computed(() => this.isTouchDevice(), ...(ngDevMode ? [{ debugName: "isTouchScreen" }] : []));
|
|
5386
|
+
this.isTouchScreen = computed(() => this.isTouchDevice(), ...(ngDevMode ? [{ debugName: "isTouchScreen" }] : /* istanbul ignore next */ []));
|
|
3550
5387
|
//#endregion
|
|
3551
5388
|
//#region ---- Private Properties ----
|
|
3552
5389
|
this._resizeListener = null;
|
|
@@ -3612,10 +5449,10 @@ class AXPDeviceService {
|
|
|
3612
5449
|
destroy() {
|
|
3613
5450
|
this._removeResizeListener();
|
|
3614
5451
|
}
|
|
3615
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
3616
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
5452
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDeviceService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
5453
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDeviceService, providedIn: 'root' }); }
|
|
3617
5454
|
}
|
|
3618
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
5455
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDeviceService, decorators: [{
|
|
3619
5456
|
type: Injectable,
|
|
3620
5457
|
args: [{
|
|
3621
5458
|
providedIn: 'root',
|
|
@@ -3719,10 +5556,10 @@ class AXHighlightService {
|
|
|
3719
5556
|
resultHTML += '</span>';
|
|
3720
5557
|
return resultHTML;
|
|
3721
5558
|
}
|
|
3722
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
3723
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
5559
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXHighlightService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
5560
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXHighlightService, providedIn: 'root' }); }
|
|
3724
5561
|
}
|
|
3725
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
5562
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXHighlightService, decorators: [{
|
|
3726
5563
|
type: Injectable,
|
|
3727
5564
|
args: [{
|
|
3728
5565
|
providedIn: 'root',
|
|
@@ -3754,17 +5591,23 @@ async function createProviderWithInjectionContext(loader) {
|
|
|
3754
5591
|
*
|
|
3755
5592
|
* @param token - The injection token to provide
|
|
3756
5593
|
* @param loader - Function that returns a promise resolving to the provider class
|
|
3757
|
-
* @param multi - Whether the provider is multi-provider (
|
|
5594
|
+
* @param multi - Optional. Whether the provider is a multi-provider (array of values). Defaults to `true`. Pass `false` for a single provider.
|
|
3758
5595
|
* @returns Provider configuration object
|
|
3759
5596
|
*
|
|
3760
5597
|
* @example
|
|
3761
5598
|
* ```typescript
|
|
3762
|
-
*
|
|
3763
|
-
*
|
|
3764
|
-
*
|
|
3765
|
-
*
|
|
3766
|
-
*
|
|
3767
|
-
*
|
|
5599
|
+
* // Multi-provider (default)
|
|
5600
|
+
* provideLazyProvider(
|
|
5601
|
+
* AXP_DATASOURCE_DEFINITION_PROVIDER,
|
|
5602
|
+
* () => import('./datasource.provider').then(m => m.AXMDataSourceProvider)
|
|
5603
|
+
* )
|
|
5604
|
+
*
|
|
5605
|
+
* // Single provider
|
|
5606
|
+
* provideLazyProvider(
|
|
5607
|
+
* SOME_TOKEN,
|
|
5608
|
+
* () => import('./my.provider').then(m => m.MyProvider),
|
|
5609
|
+
* false
|
|
5610
|
+
* )
|
|
3768
5611
|
* ```
|
|
3769
5612
|
*/
|
|
3770
5613
|
function provideLazyProvider(token, loader, multi = true) {
|
|
@@ -3775,11 +5618,113 @@ function provideLazyProvider(token, loader, multi = true) {
|
|
|
3775
5618
|
};
|
|
3776
5619
|
}
|
|
3777
5620
|
|
|
5621
|
+
//#region ---- Unwrapping ----
|
|
5622
|
+
/**
|
|
5623
|
+
* If `raw` is a plain non-array object with a `value` property, returns `value`; otherwise returns `raw`.
|
|
5624
|
+
*/
|
|
5625
|
+
function unwrapValueProperty(raw) {
|
|
5626
|
+
if (raw && typeof raw === 'object' && !Array.isArray(raw) && !(raw instanceof Date) && 'value' in raw) {
|
|
5627
|
+
return raw.value;
|
|
5628
|
+
}
|
|
5629
|
+
return raw;
|
|
5630
|
+
}
|
|
5631
|
+
//#endregion
|
|
5632
|
+
//#region ---- Scalar coercion ----
|
|
5633
|
+
/**
|
|
5634
|
+
* Coerces to a trimmed string. Plain objects may carry `{ value }` from setting widgets or `{ id }` from select payloads using `valueField: 'id'`.
|
|
5635
|
+
*/
|
|
5636
|
+
function coerceUnknownToTrimmedString(raw) {
|
|
5637
|
+
if (typeof raw === 'string') {
|
|
5638
|
+
return raw.trim();
|
|
5639
|
+
}
|
|
5640
|
+
if (raw && typeof raw === 'object' && !Array.isArray(raw) && !(raw instanceof Date)) {
|
|
5641
|
+
const r = raw;
|
|
5642
|
+
if ('value' in r && r['value'] != null) {
|
|
5643
|
+
return String(r['value']).trim();
|
|
5644
|
+
}
|
|
5645
|
+
if ('id' in r && r['id'] != null) {
|
|
5646
|
+
return String(r['id']).trim();
|
|
5647
|
+
}
|
|
5648
|
+
}
|
|
5649
|
+
return '';
|
|
5650
|
+
}
|
|
5651
|
+
/**
|
|
5652
|
+
* Coerces to boolean: direct booleans pass through; wrapped `{ value: true | 'true' }` is respected; otherwise false.
|
|
5653
|
+
*/
|
|
5654
|
+
function coerceUnknownToBoolean(raw) {
|
|
5655
|
+
if (typeof raw === 'boolean') {
|
|
5656
|
+
return raw;
|
|
5657
|
+
}
|
|
5658
|
+
if (raw && typeof raw === 'object' && 'value' in raw) {
|
|
5659
|
+
const v = raw.value;
|
|
5660
|
+
return v === true || v === 'true';
|
|
5661
|
+
}
|
|
5662
|
+
return false;
|
|
5663
|
+
}
|
|
5664
|
+
/** Parses a finite number after optional `{ value }` unwrap; non-finite or empty string yields NaN. */
|
|
5665
|
+
function coerceUnknownToFiniteNumber(raw) {
|
|
5666
|
+
const v = unwrapValueProperty(raw);
|
|
5667
|
+
if (v === null || v === undefined) {
|
|
5668
|
+
return NaN;
|
|
5669
|
+
}
|
|
5670
|
+
if (typeof v === 'number') {
|
|
5671
|
+
return Number.isFinite(v) ? v : NaN;
|
|
5672
|
+
}
|
|
5673
|
+
if (typeof v === 'boolean') {
|
|
5674
|
+
return v ? 1 : 0;
|
|
5675
|
+
}
|
|
5676
|
+
if (typeof v === 'string') {
|
|
5677
|
+
const t = v.trim();
|
|
5678
|
+
if (t === '') {
|
|
5679
|
+
return NaN;
|
|
5680
|
+
}
|
|
5681
|
+
const n = Number(t);
|
|
5682
|
+
return Number.isFinite(n) ? n : NaN;
|
|
5683
|
+
}
|
|
5684
|
+
return NaN;
|
|
5685
|
+
}
|
|
5686
|
+
/** Parses a Date after optional `{ value }` unwrap; invalid input yields Invalid Date. */
|
|
5687
|
+
function coerceUnknownToDate(raw) {
|
|
5688
|
+
const v = unwrapValueProperty(raw);
|
|
5689
|
+
if (v === null || v === undefined) {
|
|
5690
|
+
return new Date(NaN);
|
|
5691
|
+
}
|
|
5692
|
+
if (v instanceof Date) {
|
|
5693
|
+
return new Date(v.getTime());
|
|
5694
|
+
}
|
|
5695
|
+
if (typeof v === 'number' && Number.isFinite(v)) {
|
|
5696
|
+
return new Date(v);
|
|
5697
|
+
}
|
|
5698
|
+
if (typeof v === 'string') {
|
|
5699
|
+
const t = v.trim();
|
|
5700
|
+
if (t === '') {
|
|
5701
|
+
return new Date(NaN);
|
|
5702
|
+
}
|
|
5703
|
+
return new Date(t);
|
|
5704
|
+
}
|
|
5705
|
+
return new Date(NaN);
|
|
5706
|
+
}
|
|
5707
|
+
//#endregion
|
|
5708
|
+
|
|
3778
5709
|
function extractTextFromHtml(value) {
|
|
3779
5710
|
const div = document.createElement('div');
|
|
3780
5711
|
div.innerHTML = value;
|
|
3781
5712
|
return div.textContent || div.innerText || '';
|
|
3782
5713
|
}
|
|
5714
|
+
/**
|
|
5715
|
+
* True when the string likely contains HTML markup (e.g. `<p>`, `<ul>`), as opposed to plain text.
|
|
5716
|
+
* Used to choose innerHTML vs text interpolation for multilingual fields.
|
|
5717
|
+
*/
|
|
5718
|
+
function containsHtmlMarkup(value) {
|
|
5719
|
+
if (value == null) {
|
|
5720
|
+
return false;
|
|
5721
|
+
}
|
|
5722
|
+
const t = value.trim();
|
|
5723
|
+
if (!t) {
|
|
5724
|
+
return false;
|
|
5725
|
+
}
|
|
5726
|
+
return /<\/?[a-z][\s\S]*?>/i.test(t);
|
|
5727
|
+
}
|
|
3783
5728
|
/**
|
|
3784
5729
|
* Generate kebab-case group name from title
|
|
3785
5730
|
*/
|
|
@@ -3797,5 +5742,5 @@ function generateKebabCase(title) {
|
|
|
3797
5742
|
* Generated bundle index. Do not edit.
|
|
3798
5743
|
*/
|
|
3799
5744
|
|
|
3800
|
-
export {
|
|
5745
|
+
export { AXHighlightService, AXPActivityLogProvider, AXPActivityLogService, AXPAppStartUpProvider, AXPAppStartUpService, AXPBroadcastEventService, AXPCatalogScopeDefinitionProviderService, AXPCatalogScopeDefinitionsDataSourceDefinition, AXPColorPaletteProvider, AXPColorPaletteService, AXPColumnWidthService, AXPComponentLogoConfig, AXPComponentSlot, AXPComponentSlotDirective, AXPComponentSlotModule, AXPComponentSlotRegistryService, AXPContentCheckerDirective, AXPContextChangeEvent, AXPContextDefinitionProviderService, AXPContextStore, AXPCountdownPipe, AXPDataGenerator, AXPDataSourceDefinitionProviderService, AXPDblClickDirective, AXPDefaultColorPalettesProvider, AXPDeviceService, AXPDeviceType, AXPDistributedEventListenerService, AXPElementDataDirective, AXPExportTemplateToken, AXPExpressionEvaluatorScopeProviderContext, AXPExpressionEvaluatorScopeProviderService, AXPExpressionEvaluatorService, AXPFeatureDefinitionProviderContext, AXPGridLayoutDirective, AXPHookService, AXPIconLogoConfig, AXPImageUrlLogoConfig, AXPKeyboardShortcutPriority, AXPKeyboardShortcutRegistry, AXPModuleManifestModule, AXPModuleManifestRegistry, AXPModuleManifestsDataSourceDefinition, AXPPlatformCoreDataSourceKeys, AXPPlatformScope, AXPScreenSize, AXPSystemActionType, AXPSystemActions, AXPTagProvider, AXPTagService, AXP_ACTIVITY_LOG_PROVIDER, AXP_CATALOG_SCOPE_DEFINITION_PROVIDER, AXP_COLOR_PALETTE_PROVIDER, AXP_COLUMN_WIDTH_PROVIDER, AXP_CONTEXT_DEFINITION_PROVIDER, AXP_DATASOURCE_DEFINITION_PROVIDER, AXP_DISTRIBUTED_EVENT_LISTENER_PROVIDER, AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER, AXP_FEATURE_DEFINITION_PROVIDER, AXP_MODULE_MANIFEST_PROVIDER, AXP_SESSION_SERVICE, AXP_TAG_PROVIDER, AX_OVERLAY_CONTAINER_SELECTOR, AX_OVERLAY_PANE_SELECTOR, applyFilterArray, applyPagination, applyQueryArray, applySortArray, applySystemActionDefault, buildLocaleTextMapValue, captureFormContextBaseline, chordToKbdItemKeys, cleanDeep, coerceUnknownToBoolean, coerceUnknownToDate, coerceUnknownToFiniteNumber, coerceUnknownToTrimmedString, compareMultiLanguageStrings, containsHtmlMarkup, createProviderWithInjectionContext, defaultColumnWidthProvider, extractNestedFieldsWildcard, extractTextFromHtml, extractValue, findOverlayContainerAncestor, formatKeyboardShortcutChord, formatKeyboardShortcutChords, generateKebabCase, getActionButton, getChangedPaths, getDetailedChanges, getEnumValues, getNestedKeys, getNestedVisibleOverlayPanes, getPrimaryKeyboardShortcutChord, getSmart, getSystemActions, getTopVisibleOverlayContainer, getVisibleAnchoredOverlayPanes, getVisibleOverlayContainers, hasForegroundOverlayLayer, isFormContextDirty, isFormValueEqual, isHorizontalDirectionalShortcutKey, isKeyboardTargetInsideEditableField, isLocaleStringMap, isMacPlatform, isSelectionValueEqual, isVisibleOverlayElement, matchesKeyboardShortcutChord, mirrorHorizontalDirectionalKey, normalizeDefinitionCategories, normalizeKeyboardShortcut, normalizeKeyboardShortcuts, objectKeyValueTransforms, parseKeyboardShortcutChord, provideLazyProvider, resolveActionLook, resolveDisplayShortcutChord, resolveEffectiveDirectionBehavior, resolvePlatformScopeKey, resolvePlatformScopeName, resolveSemanticDirectionalKey, searchInMultiLanguageString, setSmart, shouldDeferEscapeToOpenOverlay, shouldUseLocaleMapShape, sortByMultiLanguageString, unwrapValueProperty };
|
|
3801
5746
|
//# sourceMappingURL=acorex-platform-core.mjs.map
|