@acorex-platform/framework-client 0.0.10

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.
Files changed (112) hide show
  1. package/README.md +7 -0
  2. package/auth/README.md +3 -0
  3. package/common/README.md +3 -0
  4. package/core/README.md +4 -0
  5. package/fesm2022/acorex-platform-framework-client-auth.mjs +1383 -0
  6. package/fesm2022/acorex-platform-framework-client-auth.mjs.map +1 -0
  7. package/fesm2022/acorex-platform-framework-client-common-common-settings.provider-CbzOYCnI.mjs +190 -0
  8. package/fesm2022/acorex-platform-framework-client-common-common-settings.provider-CbzOYCnI.mjs.map +1 -0
  9. package/fesm2022/acorex-platform-framework-client-common.mjs +9467 -0
  10. package/fesm2022/acorex-platform-framework-client-common.mjs.map +1 -0
  11. package/fesm2022/acorex-platform-framework-client-core.mjs +4655 -0
  12. package/fesm2022/acorex-platform-framework-client-core.mjs.map +1 -0
  13. package/fesm2022/acorex-platform-framework-client-domain.mjs +3189 -0
  14. package/fesm2022/acorex-platform-framework-client-domain.mjs.map +1 -0
  15. package/fesm2022/acorex-platform-framework-client-layout-builder.mjs +3439 -0
  16. package/fesm2022/acorex-platform-framework-client-layout-builder.mjs.map +1 -0
  17. package/fesm2022/acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-CWVU3wXL.mjs +134 -0
  18. package/fesm2022/acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-CWVU3wXL.mjs.map +1 -0
  19. package/fesm2022/acorex-platform-framework-client-layout-components.mjs +9777 -0
  20. package/fesm2022/acorex-platform-framework-client-layout-components.mjs.map +1 -0
  21. package/fesm2022/acorex-platform-framework-client-layout-designer.mjs +2799 -0
  22. package/fesm2022/acorex-platform-framework-client-layout-designer.mjs.map +1 -0
  23. package/fesm2022/acorex-platform-framework-client-layout-entity-attachments-page.component-CxBxxpmA.mjs +376 -0
  24. package/fesm2022/acorex-platform-framework-client-layout-entity-attachments-page.component-CxBxxpmA.mjs.map +1 -0
  25. package/fesm2022/acorex-platform-framework-client-layout-entity-file-list-popup.component-l0JAHKIH.mjs +100 -0
  26. package/fesm2022/acorex-platform-framework-client-layout-entity-file-list-popup.component-l0JAHKIH.mjs.map +1 -0
  27. package/fesm2022/acorex-platform-framework-client-layout-entity.mjs +26299 -0
  28. package/fesm2022/acorex-platform-framework-client-layout-entity.mjs.map +1 -0
  29. package/fesm2022/acorex-platform-framework-client-layout-views.mjs +2960 -0
  30. package/fesm2022/acorex-platform-framework-client-layout-views.mjs.map +1 -0
  31. package/fesm2022/acorex-platform-framework-client-layout-widget-core.mjs +4711 -0
  32. package/fesm2022/acorex-platform-framework-client-layout-widget-core.mjs.map +1 -0
  33. package/fesm2022/acorex-platform-framework-client-layout-widgets-button-widget-designer.component-CpkWwygb.mjs +72 -0
  34. package/fesm2022/acorex-platform-framework-client-layout-widgets-button-widget-designer.component-CpkWwygb.mjs.map +1 -0
  35. package/fesm2022/acorex-platform-framework-client-layout-widgets-image-preview.popup-C_EPAvCU.mjs +29 -0
  36. package/fesm2022/acorex-platform-framework-client-layout-widgets-image-preview.popup-C_EPAvCU.mjs.map +1 -0
  37. package/fesm2022/acorex-platform-framework-client-layout-widgets-page-widget-designer.component-CmtTq2Mt.mjs +172 -0
  38. package/fesm2022/acorex-platform-framework-client-layout-widgets-page-widget-designer.component-CmtTq2Mt.mjs.map +1 -0
  39. package/fesm2022/acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-DSx0E3NV.mjs +513 -0
  40. package/fesm2022/acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-DSx0E3NV.mjs.map +1 -0
  41. package/fesm2022/acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-DdfcGV1C.mjs +274 -0
  42. package/fesm2022/acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-DdfcGV1C.mjs.map +1 -0
  43. package/fesm2022/acorex-platform-framework-client-layout-widgets-tabular-data-view-popup.component-CntopmDS.mjs +64 -0
  44. package/fesm2022/acorex-platform-framework-client-layout-widgets-tabular-data-view-popup.component-CntopmDS.mjs.map +1 -0
  45. package/fesm2022/acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-B5kSWxGk.mjs +34 -0
  46. package/fesm2022/acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-B5kSWxGk.mjs.map +1 -0
  47. package/fesm2022/acorex-platform-framework-client-layout-widgets.mjs +28553 -0
  48. package/fesm2022/acorex-platform-framework-client-layout-widgets.mjs.map +1 -0
  49. package/fesm2022/acorex-platform-framework-client-native.mjs +155 -0
  50. package/fesm2022/acorex-platform-framework-client-native.mjs.map +1 -0
  51. package/fesm2022/acorex-platform-framework-client-runtime.mjs +911 -0
  52. package/fesm2022/acorex-platform-framework-client-runtime.mjs.map +1 -0
  53. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-create-view.component-DnTHwsDJ.mjs +160 -0
  54. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-create-view.component-DnTHwsDJ.mjs.map +1 -0
  55. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-modify-view.component-BpR9g6QS.mjs +121 -0
  56. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-modify-view.component-BpR9g6QS.mjs.map +1 -0
  57. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-single-view.component-D1a7UI71.mjs +236 -0
  58. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-single-view.component-D1a7UI71.mjs.map +1 -0
  59. package/fesm2022/acorex-platform-framework-client-themes-default-error-401.component-D--_MIar.mjs +48 -0
  60. package/fesm2022/acorex-platform-framework-client-themes-default-error-401.component-D--_MIar.mjs.map +1 -0
  61. package/fesm2022/acorex-platform-framework-client-themes-default-error-404.component-Byyqk8pH.mjs +42 -0
  62. package/fesm2022/acorex-platform-framework-client-themes-default-error-404.component-Byyqk8pH.mjs.map +1 -0
  63. package/fesm2022/acorex-platform-framework-client-themes-default-error-offline.component-DR6G8gPC.mjs +19 -0
  64. package/fesm2022/acorex-platform-framework-client-themes-default-error-offline.component-DR6G8gPC.mjs.map +1 -0
  65. package/fesm2022/acorex-platform-framework-client-themes-default.mjs +4160 -0
  66. package/fesm2022/acorex-platform-framework-client-themes-default.mjs.map +1 -0
  67. package/fesm2022/acorex-platform-framework-client-themes-shared-icon-chooser-column.component-CXSGiTCK.mjs +55 -0
  68. package/fesm2022/acorex-platform-framework-client-themes-shared-icon-chooser-column.component-CXSGiTCK.mjs.map +1 -0
  69. package/fesm2022/acorex-platform-framework-client-themes-shared-icon-chooser-view.component-BY5clQ1J.mjs +57 -0
  70. package/fesm2022/acorex-platform-framework-client-themes-shared-icon-chooser-view.component-BY5clQ1J.mjs.map +1 -0
  71. package/fesm2022/acorex-platform-framework-client-themes-shared-settings.provider-DEL2YOTP.mjs +234 -0
  72. package/fesm2022/acorex-platform-framework-client-themes-shared-settings.provider-DEL2YOTP.mjs.map +1 -0
  73. package/fesm2022/acorex-platform-framework-client-themes-shared-theme-color-chooser-column.component-BXl8u4Yp.mjs +94 -0
  74. package/fesm2022/acorex-platform-framework-client-themes-shared-theme-color-chooser-column.component-BXl8u4Yp.mjs.map +1 -0
  75. package/fesm2022/acorex-platform-framework-client-themes-shared-theme-color-chooser-view.component-i2sOOkVj.mjs +86 -0
  76. package/fesm2022/acorex-platform-framework-client-themes-shared-theme-color-chooser-view.component-i2sOOkVj.mjs.map +1 -0
  77. package/fesm2022/acorex-platform-framework-client-themes-shared.mjs +2375 -0
  78. package/fesm2022/acorex-platform-framework-client-themes-shared.mjs.map +1 -0
  79. package/fesm2022/acorex-platform-framework-client-workflow.mjs +2836 -0
  80. package/fesm2022/acorex-platform-framework-client-workflow.mjs.map +1 -0
  81. package/fesm2022/acorex-platform-framework-client.mjs +6 -0
  82. package/fesm2022/acorex-platform-framework-client.mjs.map +1 -0
  83. package/layout/builder/README.md +1578 -0
  84. package/layout/components/README.md +3 -0
  85. package/layout/designer/README.md +4 -0
  86. package/layout/entity/README.md +4 -0
  87. package/layout/views/README.md +3 -0
  88. package/layout/widget-core/README.md +4 -0
  89. package/layout/widgets/README.md +3 -0
  90. package/native/README.md +4 -0
  91. package/package.json +98 -0
  92. package/runtime/README.md +3 -0
  93. package/themes/default/README.md +3 -0
  94. package/themes/shared/README.md +3 -0
  95. package/types/acorex-platform-framework-client-auth.d.ts +690 -0
  96. package/types/acorex-platform-framework-client-common.d.ts +3287 -0
  97. package/types/acorex-platform-framework-client-core.d.ts +1849 -0
  98. package/types/acorex-platform-framework-client-domain.d.ts +1968 -0
  99. package/types/acorex-platform-framework-client-layout-builder.d.ts +1058 -0
  100. package/types/acorex-platform-framework-client-layout-components.d.ts +3362 -0
  101. package/types/acorex-platform-framework-client-layout-designer.d.ts +501 -0
  102. package/types/acorex-platform-framework-client-layout-entity.d.ts +4374 -0
  103. package/types/acorex-platform-framework-client-layout-views.d.ts +975 -0
  104. package/types/acorex-platform-framework-client-layout-widget-core.d.ts +1080 -0
  105. package/types/acorex-platform-framework-client-layout-widgets.d.ts +5263 -0
  106. package/types/acorex-platform-framework-client-native.d.ts +28 -0
  107. package/types/acorex-platform-framework-client-runtime.d.ts +301 -0
  108. package/types/acorex-platform-framework-client-themes-default.d.ts +889 -0
  109. package/types/acorex-platform-framework-client-themes-shared.d.ts +202 -0
  110. package/types/acorex-platform-framework-client-workflow.d.ts +1345 -0
  111. package/types/acorex-platform-framework-client.d.ts +2 -0
  112. package/workflow/README.md +4 -0
@@ -0,0 +1,4655 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, Injectable, Directive, computed, Injector, ChangeDetectionStrategy, Component, input, ElementRef, ViewContainerRef, signal, effect, runInInjectionContext, Optional, Inject, NgModule, EventEmitter, HostListener, Output, DestroyRef, untracked, provideAppInitializer, Pipe } from '@angular/core';
3
+ import { AXTranslationService, resolveMultiLanguageString } from '@acorex/core/translation';
4
+ import { AXDataSource } from '@acorex/cdk/common';
5
+ import { AXPPlatformCoreDataSourceKeys, isFormContextDirty, getSmart, isSelectionValueEqual, isFormValueEqual, setSmart, extractValue, AXPKeyboardShortcutPriority, getSystemActions, isLocaleStringMap } from '@acorex-platform/framework-shared/core';
6
+ import { cloneDeep, isEmpty, isEqual, has, unset, isPlainObject, get, sortBy, set, isUndefined, isNil, endsWith, startsWith, includes, lte, gte, lt, gt, orderBy } from 'lodash-es';
7
+ import { signalStore, withState, withComputed, withMethods, patchState } from '@ngrx/signals';
8
+ import { AXFormatService } from '@acorex/core/format';
9
+ import * as i1 from '@acorex/components/skeleton';
10
+ import { AXSkeletonModule } from '@acorex/components/skeleton';
11
+ import { Subject, interval, fromEvent } from 'rxjs';
12
+ import { toSignal } from '@angular/core/rxjs-interop';
13
+ import { DOCUMENT } from '@angular/common';
14
+ import { AXLocaleService } from '@acorex/core/locale';
15
+ import { delay } from '@acorex/core/utils';
16
+ import { AXCalendarService } from '@acorex/core/date-time';
17
+ import { startWith, map, debounceTime } from 'rxjs/operators';
18
+
19
+ const AXP_ACTIVITY_LOG_PROVIDER = new InjectionToken('AXP_ACTIVITY_LOGS_PROVIDER');
20
+ class AXPActivityLogService {
21
+ constructor() {
22
+ this.providers = inject(AXP_ACTIVITY_LOG_PROVIDER, { optional: true });
23
+ }
24
+ async getHistory(refId, refType, options) {
25
+ return (await Promise.all(this.providers?.map((p) => p.getHistory(refId, refType, options)) ?? [])).flat();
26
+ }
27
+ async getHistoryByIds(refId, refType, ids) {
28
+ return (await Promise.all(this.providers?.map((p) => p.getHistoryByIds(refId, refType, ids)) ?? [])).flat();
29
+ }
30
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPActivityLogService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
31
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPActivityLogService, providedIn: 'root' }); }
32
+ }
33
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPActivityLogService, decorators: [{
34
+ type: Injectable,
35
+ args: [{ providedIn: 'root' }]
36
+ }] });
37
+
38
+ class AXPActivityLogProvider {
39
+ }
40
+
41
+ //#region ---- Catalog scope definition ----
42
+ //#endregion
43
+
44
+ //#region ---- Provider contract ----
45
+ const AXP_CATALOG_SCOPE_DEFINITION_PROVIDER = new InjectionToken('AXP_CATALOG_SCOPE_DEFINITION_PROVIDER');
46
+ //#endregion
47
+
48
+ //#region ---- Imports ----
49
+ //#endregion
50
+ //#region ---- Aggregator service ----
51
+ /**
52
+ * Collects {@link AXPCatalogScopeDefinition} entries from all registered providers.
53
+ */
54
+ class AXPCatalogScopeDefinitionProviderService {
55
+ constructor() {
56
+ //#region ---- Services & Dependencies ----
57
+ this.providers = inject(AXP_CATALOG_SCOPE_DEFINITION_PROVIDER, { optional: true });
58
+ this.translation = inject(AXTranslationService);
59
+ }
60
+ //#endregion
61
+ //#region ---- Public API ----
62
+ async items() {
63
+ const merged = [];
64
+ const seen = new Set();
65
+ if (!Array.isArray(this.providers)) {
66
+ return merged;
67
+ }
68
+ for (const raw of this.providers) {
69
+ const provider = await Promise.resolve(raw);
70
+ if (!provider || typeof provider.items !== 'function') {
71
+ continue;
72
+ }
73
+ for (const item of await provider.items()) {
74
+ const name = item.name?.trim();
75
+ if (!name) {
76
+ continue;
77
+ }
78
+ if (seen.has(name)) {
79
+ throw new Error(`Duplicate catalog scope definition: "${name}"`);
80
+ }
81
+ seen.add(name);
82
+ merged.push(item);
83
+ }
84
+ }
85
+ return merged.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
86
+ }
87
+ async get(name) {
88
+ const key = name?.trim();
89
+ if (!key) {
90
+ return undefined;
91
+ }
92
+ return (await this.items()).find((d) => d.name === key);
93
+ }
94
+ /**
95
+ * Scopes applicable to the given entity ref (`Module.Entity`).
96
+ */
97
+ async byTarget(entityRef) {
98
+ const target = entityRef?.trim();
99
+ if (!target) {
100
+ return [];
101
+ }
102
+ return (await this.items()).filter((d) => d.targets?.some((t) => t === target));
103
+ }
104
+ /**
105
+ * Resolves display title for a scope key (active locale).
106
+ */
107
+ resolveTitle(definition) {
108
+ const locale = this.translation.getActiveLang() ?? 'en-US';
109
+ if (typeof definition.title === 'string') {
110
+ const title = definition.title.trim();
111
+ if (title.startsWith('@')) {
112
+ return this.translation.translateSync(title) || title;
113
+ }
114
+ return title;
115
+ }
116
+ return resolveMultiLanguageString(definition.title, locale) || definition.name;
117
+ }
118
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPCatalogScopeDefinitionProviderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
119
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPCatalogScopeDefinitionProviderService, providedIn: 'root' }); }
120
+ }
121
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPCatalogScopeDefinitionProviderService, decorators: [{
122
+ type: Injectable,
123
+ args: [{ providedIn: 'root' }]
124
+ }] });
125
+
126
+ //#region ---- Imports ----
127
+ //#endregion
128
+ //#region ---- Data source definition ----
129
+ /**
130
+ * Registered catalog scopes for select / selection-list widgets via {@link AXPPlatformCoreDataSourceKeys.CatalogScopes}.
131
+ */
132
+ class AXPCatalogScopeDefinitionsDataSourceDefinition {
133
+ constructor() {
134
+ //#region ---- Services & Dependencies ----
135
+ this.catalogScopeRegistry = inject(AXPCatalogScopeDefinitionProviderService);
136
+ //#endregion
137
+ }
138
+ //#endregion
139
+ //#region ---- Public API ----
140
+ async items() {
141
+ return [
142
+ {
143
+ name: AXPPlatformCoreDataSourceKeys.CatalogScopes,
144
+ title: 'Catalog scopes',
145
+ source: () => new AXDataSource({
146
+ key: 'name',
147
+ pageSize: 500,
148
+ load: async (e) => {
149
+ const all = await this.catalogScopeRegistry.items();
150
+ const localeFilter = this.readTargetFilter(e?.filter);
151
+ const items = all
152
+ .filter((scope) => !localeFilter || scope.targets?.includes(localeFilter))
153
+ .map((scope) => ({
154
+ name: scope.name,
155
+ title: this.catalogScopeRegistry.resolveTitle(scope),
156
+ description: scope.description,
157
+ targets: scope.targets,
158
+ icon: scope.icon,
159
+ cssClass: scope.cssClass,
160
+ }));
161
+ return { items, total: items.length };
162
+ },
163
+ byKey: async (key) => {
164
+ const name = String(key ?? '').trim();
165
+ if (!name) {
166
+ return undefined;
167
+ }
168
+ const scope = await this.catalogScopeRegistry.get(name);
169
+ if (!scope) {
170
+ return undefined;
171
+ }
172
+ return {
173
+ name: scope.name,
174
+ title: this.catalogScopeRegistry.resolveTitle(scope),
175
+ description: scope.description,
176
+ targets: scope.targets,
177
+ icon: scope.icon,
178
+ cssClass: scope.cssClass,
179
+ };
180
+ },
181
+ }),
182
+ columns: [
183
+ { name: 'name', title: 'Name', datatype: 'string', type: 'text-editor' },
184
+ { name: 'title', title: 'Title', datatype: 'string', type: 'text-editor' },
185
+ ],
186
+ filters: [
187
+ {
188
+ field: 'targets',
189
+ title: 'Target entity',
190
+ operator: { type: 'contains' },
191
+ widget: { type: 'text-editor' },
192
+ filterType: { advance: false, inline: false },
193
+ },
194
+ ],
195
+ textField: { name: 'title', title: 'Title' },
196
+ valueField: { name: 'name', title: 'Name' },
197
+ },
198
+ ];
199
+ }
200
+ //#endregion
201
+ //#region ---- Helpers ----
202
+ readTargetFilter(filter) {
203
+ if (!filter || typeof filter !== 'object') {
204
+ return undefined;
205
+ }
206
+ const f = filter;
207
+ if (f.field === 'targets' && typeof f.value === 'string' && f.value.trim()) {
208
+ return f.value.trim();
209
+ }
210
+ return undefined;
211
+ }
212
+ }
213
+ //#endregion
214
+
215
+ //#region ---- Color Palette Provider ----
216
+ /**
217
+ * Abstract class for color palette providers
218
+ * Implement this to provide palettes from different sources (system, app, module, etc.)
219
+ */
220
+ class AXPColorPaletteProvider {
221
+ }
222
+ /**
223
+ * Injection token for color palette providers
224
+ * Use this to register multiple palette providers
225
+ */
226
+ const AXP_COLOR_PALETTE_PROVIDER = new InjectionToken('AXP_COLOR_PALETTE_PROVIDER');
227
+ //#endregion
228
+
229
+ //#region ---- Color Palette Service ----
230
+ /**
231
+ * Service for managing color palettes from multiple providers
232
+ * Aggregates palettes from all registered providers
233
+ */
234
+ class AXPColorPaletteService {
235
+ constructor() {
236
+ //#region ---- Dependencies ----
237
+ this.providers = inject(AXP_COLOR_PALETTE_PROVIDER, { optional: true }) ?? [];
238
+ //#endregion
239
+ //#region ---- State ----
240
+ this.cache = null;
241
+ }
242
+ //#endregion
243
+ //#region ---- Public API ----
244
+ /**
245
+ * Get all palettes from all providers
246
+ * @param options Filter options
247
+ * @returns Promise of palettes
248
+ */
249
+ async getPalettes(options) {
250
+ if (!this.cache) {
251
+ await this.loadPalettes();
252
+ }
253
+ let palettes = this.cache;
254
+ // Apply category filter
255
+ if (options?.category) {
256
+ palettes = palettes.filter((palette) => palette.category === options.category);
257
+ }
258
+ // Apply search filter
259
+ if (options?.search) {
260
+ const searchLower = options.search.toLowerCase();
261
+ palettes = palettes.filter((palette) => palette.name.toLowerCase().includes(searchLower) ||
262
+ palette.title.toLowerCase().includes(searchLower) ||
263
+ palette.description?.toLowerCase().includes(searchLower));
264
+ }
265
+ return palettes;
266
+ }
267
+ /**
268
+ * Get a palette by name
269
+ * @param name The unique name of the palette
270
+ * @returns Promise of palette or undefined if not found
271
+ */
272
+ async getPalette(name) {
273
+ if (!this.cache) {
274
+ await this.loadPalettes();
275
+ }
276
+ return this.cache.find((p) => p.name === name);
277
+ }
278
+ /**
279
+ * Get palettes by category
280
+ * @param category The category to filter by
281
+ * @returns Promise of palettes in the specified category
282
+ */
283
+ async getByCategory(category) {
284
+ return this.getPalettes({ category });
285
+ }
286
+ /**
287
+ * Check if a palette exists
288
+ * @param name The unique name of the palette
289
+ * @returns Promise of true if the palette exists
290
+ */
291
+ async has(name) {
292
+ const palette = await this.getPalette(name);
293
+ return palette !== undefined;
294
+ }
295
+ /**
296
+ * Reload palettes from all providers
297
+ */
298
+ async reload() {
299
+ this.cache = null;
300
+ await this.loadPalettes();
301
+ }
302
+ /**
303
+ * Get the default palette (material-design) or fallback colors
304
+ * @returns Promise of default palette, never returns undefined
305
+ */
306
+ async getDefaultPalette() {
307
+ const defaultPalette = await this.getPalette('material-design');
308
+ if (defaultPalette) {
309
+ return defaultPalette;
310
+ }
311
+ // Fallback to hardcoded material design colors if provider fails
312
+ return {
313
+ name: 'material-design',
314
+ title: 'Material Design',
315
+ category: 'material',
316
+ description: 'Google Material Design color palette',
317
+ colors: [
318
+ '#F44336', // Red
319
+ '#E91E63', // Pink
320
+ '#9C27B0', // Purple
321
+ '#673AB7', // Deep Purple
322
+ '#3F51B5', // Indigo
323
+ '#2196F3', // Blue
324
+ '#03A9F4', // Light Blue
325
+ '#00BCD4', // Cyan
326
+ '#009688', // Teal
327
+ '#4CAF50', // Green
328
+ '#8BC34A', // Light Green
329
+ '#CDDC39', // Lime
330
+ '#FFEB3B', // Yellow
331
+ '#FFC107', // Amber
332
+ '#FF9800', // Orange
333
+ '#FF5722', // Deep Orange
334
+ ],
335
+ };
336
+ }
337
+ //#endregion
338
+ //#region ---- Private Methods ----
339
+ /**
340
+ * Load palettes from all providers
341
+ */
342
+ async loadPalettes() {
343
+ const allPalettes = [];
344
+ for (const provider of this.providers) {
345
+ try {
346
+ const palettes = await provider.provide();
347
+ allPalettes.push(...palettes);
348
+ }
349
+ catch (error) {
350
+ console.error(`Error loading palettes from provider ${provider.name}:`, error);
351
+ }
352
+ }
353
+ // Remove duplicates based on name
354
+ const uniquePalettes = allPalettes.filter((palette, index, self) => index === self.findIndex((p) => p.name === palette.name));
355
+ this.cache = uniquePalettes;
356
+ }
357
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPColorPaletteService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
358
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPColorPaletteService, providedIn: 'root' }); }
359
+ }
360
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPColorPaletteService, decorators: [{
361
+ type: Injectable,
362
+ args: [{
363
+ providedIn: 'root',
364
+ }]
365
+ }] });
366
+
367
+ //#region ---- Color Palette Types ----
368
+ //#endregion
369
+
370
+ //#region ---- Default System Palettes Provider ----
371
+ /**
372
+ * Default system color palette provider
373
+ * Provides built-in color palettes
374
+ */
375
+ class AXPDefaultColorPalettesProvider extends AXPColorPaletteProvider {
376
+ constructor() {
377
+ super(...arguments);
378
+ this.name = 'system';
379
+ }
380
+ async provide() {
381
+ return [
382
+ {
383
+ name: 'material-design',
384
+ title: 'Material Design',
385
+ category: 'material',
386
+ description: 'Google Material Design color palette',
387
+ colors: [
388
+ '#F44336', // Red
389
+ '#E91E63', // Pink
390
+ '#9C27B0', // Purple
391
+ '#673AB7', // Deep Purple
392
+ '#3F51B5', // Indigo
393
+ '#2196F3', // Blue
394
+ '#03A9F4', // Light Blue
395
+ '#00BCD4', // Cyan
396
+ '#009688', // Teal
397
+ '#4CAF50', // Green
398
+ '#8BC34A', // Light Green
399
+ '#CDDC39', // Lime
400
+ '#FFEB3B', // Yellow
401
+ '#FFC107', // Amber
402
+ '#FF9800', // Orange
403
+ '#FF5722', // Deep Orange
404
+ ],
405
+ },
406
+ {
407
+ name: 'pastel',
408
+ title: 'Pastel Colors',
409
+ category: 'theme',
410
+ description: 'Soft pastel color palette',
411
+ colors: [
412
+ '#FFB3BA', // Light Pink
413
+ '#FFDFBA', // Light Peach
414
+ '#FFFFBA', // Light Yellow
415
+ '#BAFFC9', // Light Green
416
+ '#BAE1FF', // Light Blue
417
+ '#E0BBE4', // Light Purple
418
+ '#FFDFD3', // Light Coral
419
+ '#D4F1F4', // Light Cyan
420
+ ],
421
+ },
422
+ {
423
+ name: 'vibrant',
424
+ title: 'Vibrant Colors',
425
+ category: 'theme',
426
+ description: 'Bold and vibrant color palette',
427
+ colors: [
428
+ '#FF6B6B', // Vibrant Red
429
+ '#4ECDC4', // Vibrant Teal
430
+ '#45B7D1', // Vibrant Blue
431
+ '#FFA07A', // Vibrant Salmon
432
+ '#98D8C8', // Vibrant Mint
433
+ '#F7DC6F', // Vibrant Yellow
434
+ '#BB8FCE', // Vibrant Purple
435
+ '#85C1E2', // Vibrant Sky Blue
436
+ ],
437
+ },
438
+ {
439
+ name: 'earth-tones',
440
+ title: 'Earth Tones',
441
+ category: 'theme',
442
+ description: 'Natural earth tone color palette',
443
+ colors: [
444
+ '#8B4513', // Saddle Brown
445
+ '#A0522D', // Sienna
446
+ '#D2691E', // Chocolate
447
+ '#CD853F', // Peru
448
+ '#DEB887', // Burlywood
449
+ '#F5DEB3', // Wheat
450
+ '#556B2F', // Dark Olive Green
451
+ '#6B8E23', // Olive Drab
452
+ ],
453
+ },
454
+ {
455
+ name: 'monochrome',
456
+ title: 'Monochrome',
457
+ category: 'theme',
458
+ description: 'Grayscale color palette',
459
+ colors: [
460
+ '#000000', // Black
461
+ '#262626', // Very Dark Gray
462
+ '#404040', // Dark Gray
463
+ '#595959', // Medium Dark Gray
464
+ '#737373', // Medium Gray
465
+ '#8C8C8C', // Light Medium Gray
466
+ '#A6A6A6', // Light Gray
467
+ '#BFBFBF', // Very Light Gray
468
+ '#D9D9D9', // Lighter Gray
469
+ '#F2F2F2', // Almost White
470
+ '#FFFFFF', // White
471
+ ],
472
+ },
473
+ ];
474
+ }
475
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDefaultColorPalettesProvider, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
476
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDefaultColorPalettesProvider }); }
477
+ }
478
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDefaultColorPalettesProvider, decorators: [{
479
+ type: Injectable
480
+ }] });
481
+
482
+ //#region ---- Column Width Provider ----
483
+ /**
484
+ * Injection token for column width providers
485
+ * Use this to register multiple column width providers with multi: true
486
+ *
487
+ * @example
488
+ * ```typescript
489
+ * providers: [
490
+ * {
491
+ * provide: AXP_COLUMN_WIDTH_PROVIDER,
492
+ * useValue: {
493
+ * matchers: [
494
+ * { name: 'title', width: '300px', priority: 10 },
495
+ * { widgetType: /text-box/i, width: '200px' }
496
+ * ],
497
+ * priority: 150
498
+ * },
499
+ * multi: true
500
+ * }
501
+ * ]
502
+ * ```
503
+ */
504
+ const AXP_COLUMN_WIDTH_PROVIDER = new InjectionToken('AXP_COLUMN_WIDTH_PROVIDER');
505
+ //#endregion
506
+
507
+ //#region ---- Column Width Service ----
508
+ /**
509
+ * Service for resolving column widths based on registered providers
510
+ * Supports matching by column name (priority) and widget type (fallback)
511
+ * Uses multi-provider pattern for extensibility
512
+ */
513
+ class AXPColumnWidthService {
514
+ constructor() {
515
+ //#region ---- Services & Dependencies ----
516
+ this.providers = inject(AXP_COLUMN_WIDTH_PROVIDER, {
517
+ optional: true,
518
+ }) ?? [];
519
+ }
520
+ //#region ---- Public Methods ----
521
+ /**
522
+ * Resolve width for a column based on registered providers
523
+ * Only returns a width if column doesn't already have one set
524
+ *
525
+ * @param column Column to resolve width for
526
+ * @returns Resolved width string (normalized) or undefined if no match or column already has width
527
+ */
528
+ resolveWidth(column) {
529
+ // If column already has a width set, don't override
530
+ const existingWidth = column.width || column.options?.width;
531
+ if (existingWidth) {
532
+ return undefined;
533
+ }
534
+ // Get widget type from column (support both widget.type and showAs.type)
535
+ const widgetType = column.widget?.type || column.showAs?.type;
536
+ // Collect all matchers from all providers
537
+ const allMatchers = this.collectMatchers();
538
+ // Try name-based matches first (higher priority)
539
+ const nameMatch = this.findNameMatch(column.name, allMatchers);
540
+ if (nameMatch) {
541
+ return this.normalizeWidth(nameMatch.width);
542
+ }
543
+ // Fallback to widget-type matches (case-insensitive)
544
+ if (widgetType) {
545
+ const widgetMatch = this.findWidgetTypeMatch(widgetType, allMatchers);
546
+ if (widgetMatch) {
547
+ return this.normalizeWidth(widgetMatch.width);
548
+ }
549
+ }
550
+ return undefined;
551
+ }
552
+ /**
553
+ * Apply resolved width to a column (mutates the column)
554
+ * Only applies if column doesn't already have a width
555
+ *
556
+ * @param column Column to apply width to
557
+ * @returns true if width was applied, false if column already had width or no match found
558
+ */
559
+ applyWidth(column) {
560
+ const width = this.resolveWidth(column);
561
+ if (!width) {
562
+ return false;
563
+ }
564
+ // Apply to the appropriate property
565
+ if (column.options) {
566
+ column.options.width = width;
567
+ }
568
+ else {
569
+ column.width = width;
570
+ }
571
+ return true;
572
+ }
573
+ //#endregion
574
+ //#region ---- Private Methods ----
575
+ /**
576
+ * Collect all matchers from all providers, sorted by provider priority and matcher priority
577
+ */
578
+ collectMatchers() {
579
+ const allMatchers = [];
580
+ for (const provider of this.providers) {
581
+ const providerPriority = provider.priority ?? 100;
582
+ for (const matcher of provider.matchers) {
583
+ allMatchers.push({
584
+ matcher,
585
+ providerPriority,
586
+ });
587
+ }
588
+ }
589
+ // Sort by provider priority (descending), then by matcher priority (descending)
590
+ allMatchers.sort((a, b) => {
591
+ if (a.providerPriority !== b.providerPriority) {
592
+ return b.providerPriority - a.providerPriority; // Higher priority first
593
+ }
594
+ const priorityA = a.matcher.priority ?? 0;
595
+ const priorityB = b.matcher.priority ?? 0;
596
+ return priorityB - priorityA; // Higher priority first
597
+ });
598
+ return allMatchers;
599
+ }
600
+ /**
601
+ * Find first matching matcher by column name
602
+ * Name matches take precedence over widget type matches
603
+ */
604
+ findNameMatch(columnName, allMatchers) {
605
+ for (const { matcher } of allMatchers) {
606
+ if (!matcher.name) {
607
+ continue; // Skip if no name matcher
608
+ }
609
+ if (typeof matcher.name === 'string') {
610
+ // Exact string match (case-sensitive for names)
611
+ if (matcher.name === columnName) {
612
+ return matcher;
613
+ }
614
+ }
615
+ else if (matcher.name instanceof RegExp) {
616
+ // RegExp match
617
+ if (matcher.name.test(columnName)) {
618
+ return matcher;
619
+ }
620
+ }
621
+ }
622
+ return undefined;
623
+ }
624
+ /**
625
+ * Find first matching matcher by widget type (case-insensitive)
626
+ */
627
+ findWidgetTypeMatch(widgetType, allMatchers) {
628
+ const widgetTypeLower = widgetType.toLowerCase();
629
+ for (const { matcher } of allMatchers) {
630
+ if (!matcher.widgetType) {
631
+ continue; // Skip if no widget type matcher
632
+ }
633
+ if (typeof matcher.widgetType === 'string') {
634
+ // Case-insensitive string match
635
+ if (matcher.widgetType.toLowerCase() === widgetTypeLower) {
636
+ return matcher;
637
+ }
638
+ }
639
+ else if (matcher.widgetType instanceof RegExp) {
640
+ // RegExp match (should already be case-insensitive if needed, but we'll test case-insensitively)
641
+ // Create a new regex with case-insensitive flag if not already present
642
+ let regex = matcher.widgetType;
643
+ if (!regex.flags.includes('i')) {
644
+ regex = new RegExp(regex.source, regex.flags + 'i');
645
+ }
646
+ if (regex.test(widgetType)) {
647
+ return matcher;
648
+ }
649
+ }
650
+ }
651
+ return undefined;
652
+ }
653
+ /**
654
+ * Normalize width value to string format
655
+ * Numbers are converted to pixels (e.g., 150 -> '150px')
656
+ * Strings are preserved as-is (supports any CSS unit)
657
+ */
658
+ normalizeWidth(width) {
659
+ if (typeof width === 'number') {
660
+ return `${width}px`;
661
+ }
662
+ return width;
663
+ }
664
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPColumnWidthService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
665
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPColumnWidthService, providedIn: 'root' }); }
666
+ }
667
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPColumnWidthService, decorators: [{
668
+ type: Injectable,
669
+ args: [{
670
+ providedIn: 'root',
671
+ }]
672
+ }] });
673
+
674
+ //#region ---- Column Width Types ----
675
+ //#endregion
676
+
677
+ //#region ---- Default Column Width Provider ----
678
+ /**
679
+ * Default column width configuration
680
+ * Migrated from DEFAULT_COLUMN_WIDTHS in column-width.middleware.ts
681
+ * Provides sensible defaults for common column names
682
+ */
683
+ const defaultColumnWidthProvider = {
684
+ priority: 100,
685
+ matchers: [
686
+ // Priority: name-based matches (higher priority = more specific)
687
+ { name: 'code', width: '150px', priority: 10 },
688
+ { name: 'name', width: '250px', priority: 10 },
689
+ { name: 'title', width: '250px', priority: 10 },
690
+ { name: 'description', width: '350px', priority: 10 },
691
+ { name: 'note', width: '350px', priority: 10 },
692
+ { name: 'notes', width: '350px', priority: 10 },
693
+ { name: 'content', width: '500px', priority: 10 },
694
+ { name: 'level', width: '100px', priority: 10 },
695
+ { name: 'order', width: '100px', priority: 10 },
696
+ { name: 'phones', width: '300px', priority: 10 },
697
+ { name: 'emails', width: '300px', priority: 10 },
698
+ ],
699
+ };
700
+ //#endregion
701
+
702
+ class AXPComponentSlot {
703
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPComponentSlot, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
704
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: AXPComponentSlot, isStandalone: true, ngImport: i0 }); }
705
+ }
706
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPComponentSlot, decorators: [{
707
+ type: Directive
708
+ }] });
709
+
710
+ //#region ---- Context signal store ----
711
+ const AXPContextStore = signalStore(withState(() => ({
712
+ data: {},
713
+ state: 'initiated',
714
+ /** Last committed / saved baseline — discard reverts to this snapshot. */
715
+ savedSnapshot: {},
716
+ previousSnapshot: {},
717
+ /** True after a user-origin update since the last {@link commitSaved}. */
718
+ userEditedSinceCommit: false,
719
+ lastChange: {
720
+ state: 'initiated',
721
+ },
722
+ })), withComputed(({ data, state, lastChange, savedSnapshot, previousSnapshot, userEditedSinceCommit }) => ({
723
+ isChanged: computed(() => state() === 'changed'),
724
+ isReset: computed(() => state() === 'restored'),
725
+ isInitiated: computed(() => state() === 'initiated'),
726
+ isEmpty: computed(() => Object.keys(data()).length === 0),
727
+ isSavedCommitted: computed(() => !isEmpty(savedSnapshot())),
728
+ hasUserEditedSinceCommit: computed(() => userEditedSinceCommit()),
729
+ isDirty: computed(() => {
730
+ const saved = savedSnapshot();
731
+ if (isEmpty(saved)) {
732
+ return false;
733
+ }
734
+ if (!userEditedSinceCommit()) {
735
+ return false;
736
+ }
737
+ return isFormContextDirty(data(), saved);
738
+ }),
739
+ snapshot: computed(() => cloneDeep(data())),
740
+ saved: computed(() => cloneDeep(savedSnapshot())),
741
+ /** @deprecated Use {@link saved} — only saved baseline is tracked. */
742
+ initial: computed(() => cloneDeep(savedSnapshot())),
743
+ previous: computed(() => cloneDeep(previousSnapshot())),
744
+ changeEvent: computed(() => lastChange()),
745
+ })), withMethods((store) => {
746
+ const updateValue = (path, value, options) => {
747
+ const currentData = cloneDeep(store.data());
748
+ const oldValue = getSmart(currentData, path);
749
+ const hasSaved = !isEmpty(store.savedSnapshot());
750
+ const saved = store.savedSnapshot();
751
+ const savedAtPath = hasSaved ? getSmart(saved, path) : undefined;
752
+ const origin = options?.origin ?? 'system';
753
+ if (hasSaved && isSelectionValueEqual(value, savedAtPath) && !isEqual(value, savedAtPath)) {
754
+ value = cloneDeep(savedAtPath);
755
+ }
756
+ if (hasSaved && !isEqual(value, savedAtPath) && isFormValueEqual(value, savedAtPath)) {
757
+ value = cloneDeep(savedAtPath);
758
+ }
759
+ const isArrayLengthChange = Array.isArray(value) && (!Array.isArray(oldValue) || value.length !== oldValue.length);
760
+ /** Same selection id as baseline but different object shape (lookup/select item vs scalar). */
761
+ const needsSavedShapeRestore = hasSaved && isSelectionValueEqual(value, savedAtPath) && !isFormValueEqual(value, savedAtPath);
762
+ if (!isArrayLengthChange && isEqual(oldValue, value) && !needsSavedShapeRestore) {
763
+ return;
764
+ }
765
+ const shouldNormalizeSavedShape = hasSaved && isSelectionValueEqual(value, savedAtPath) && !isEqual(oldValue, value);
766
+ if (!isArrayLengthChange &&
767
+ isFormValueEqual(oldValue, value) &&
768
+ !shouldNormalizeSavedShape &&
769
+ !needsSavedShapeRestore) {
770
+ return;
771
+ }
772
+ const updatedData = setSmart(currentData, path, value);
773
+ const isStillDirty = hasSaved && isFormContextDirty(updatedData, saved);
774
+ const nextUserEdited = isStillDirty
775
+ ? origin === 'user'
776
+ ? true
777
+ : store.userEditedSinceCommit()
778
+ : false;
779
+ const changeEvent = {
780
+ oldValue,
781
+ newValue: value,
782
+ path,
783
+ state: 'changed',
784
+ data: updatedData,
785
+ origin,
786
+ };
787
+ patchState(store, {
788
+ previousSnapshot: store.snapshot(),
789
+ data: updatedData,
790
+ state: 'changed',
791
+ lastChange: changeEvent,
792
+ userEditedSinceCommit: nextUserEdited,
793
+ });
794
+ };
795
+ const applyObjectPaths = (obj, options, prefix = '') => {
796
+ for (const [key, value] of Object.entries(obj ?? {})) {
797
+ const path = prefix ? `${prefix}.${key}` : key;
798
+ if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
799
+ applyObjectPaths(value, options, path);
800
+ }
801
+ else {
802
+ updateValue(path, value, options);
803
+ }
804
+ }
805
+ };
806
+ const removePathFromContext = (path, origin = 'system') => {
807
+ const currentData = cloneDeep(store.data());
808
+ if (!has(currentData, path)) {
809
+ return;
810
+ }
811
+ unset(currentData, path);
812
+ const parts = path.split('.');
813
+ for (let i = parts.length - 1; i > 0; i--) {
814
+ const parentPath = parts.slice(0, i).join('.');
815
+ const parentObj = getSmart(currentData, parentPath);
816
+ if (isPlainObject(parentObj) && isEmpty(Object.keys(parentObj))) {
817
+ unset(currentData, parentPath);
818
+ }
819
+ }
820
+ const saved = store.savedSnapshot();
821
+ const isStillDirty = !isEmpty(saved) && isFormContextDirty(currentData, saved);
822
+ patchState(store, {
823
+ previousSnapshot: store.snapshot(),
824
+ data: currentData,
825
+ state: 'changed',
826
+ lastChange: {
827
+ path,
828
+ state: 'changed',
829
+ data: currentData,
830
+ origin,
831
+ },
832
+ userEditedSinceCommit: isStillDirty
833
+ ? origin === 'user'
834
+ ? true
835
+ : store.userEditedSinceCommit()
836
+ : false,
837
+ });
838
+ };
839
+ const syncPathToSavedBaseline = (path, options) => {
840
+ const saved = store.savedSnapshot();
841
+ if (isEmpty(saved)) {
842
+ return;
843
+ }
844
+ const origin = options?.origin ?? 'system';
845
+ if (has(saved, path)) {
846
+ updateValue(path, cloneDeep(getSmart(saved, path)), { origin });
847
+ return;
848
+ }
849
+ removePathFromContext(path, origin);
850
+ };
851
+ const revertToSaved = () => {
852
+ const savedData = store.saved();
853
+ const changeEvent = {
854
+ oldValue: cloneDeep(store.data()),
855
+ newValue: cloneDeep(savedData),
856
+ path: '',
857
+ state: 'restored',
858
+ data: savedData,
859
+ };
860
+ patchState(store, {
861
+ previousSnapshot: store.snapshot(),
862
+ data: savedData,
863
+ state: 'restored',
864
+ lastChange: changeEvent,
865
+ userEditedSinceCommit: false,
866
+ });
867
+ };
868
+ return {
869
+ update(path, value, options) {
870
+ updateValue(path, value, options);
871
+ },
872
+ applyObjectPaths,
873
+ /** Aligns a context path with the saved baseline (restore value or remove if absent). */
874
+ syncPathToSavedBaseline,
875
+ patch(context, options) {
876
+ const currentData = cloneDeep(store.data());
877
+ const updatedData = { ...currentData, ...context };
878
+ const changeEvent = {
879
+ state: 'patch',
880
+ data: updatedData,
881
+ };
882
+ const syncedSnapshot = cloneDeep(updatedData);
883
+ patchState(store, {
884
+ ...(options?.updateSaved
885
+ ? { savedSnapshot: syncedSnapshot, previousSnapshot: syncedSnapshot }
886
+ : { previousSnapshot: store.snapshot() }),
887
+ data: updatedData,
888
+ state: 'changed',
889
+ lastChange: changeEvent,
890
+ });
891
+ },
892
+ /** Reverts live data to the last saved snapshot. */
893
+ revertToSaved,
894
+ reset() {
895
+ revertToSaved();
896
+ },
897
+ /** Loads live data; saved baseline is committed separately via {@link commitSaved}. */
898
+ set(initialData) {
899
+ const currentData = store.data();
900
+ if (isFormValueEqual(currentData, initialData)) {
901
+ return;
902
+ }
903
+ const changeEvent = {
904
+ oldValue: null,
905
+ newValue: cloneDeep(initialData),
906
+ path: '',
907
+ state: 'initiated',
908
+ data: initialData,
909
+ };
910
+ patchState(store, {
911
+ previousSnapshot: store.snapshot(),
912
+ data: initialData,
913
+ state: 'initiated',
914
+ lastChange: changeEvent,
915
+ userEditedSinceCommit: false,
916
+ });
917
+ },
918
+ getValue(path) {
919
+ return getSmart(store.data(), path);
920
+ },
921
+ getSavedValue(path) {
922
+ return getSmart(store.savedSnapshot(), path);
923
+ },
924
+ hasValue(path) {
925
+ return has(store.data(), path);
926
+ },
927
+ /** Marks the current data as the saved baseline. */
928
+ commitSaved() {
929
+ const snapshot = cloneDeep(store.data());
930
+ patchState(store, {
931
+ savedSnapshot: snapshot,
932
+ previousSnapshot: snapshot,
933
+ state: 'initiated',
934
+ userEditedSinceCommit: false,
935
+ lastChange: {
936
+ state: 'initiated',
937
+ data: snapshot,
938
+ },
939
+ });
940
+ },
941
+ /** Merges parent-bound entity fields without resetting the saved baseline. */
942
+ applyParentBind(merged) {
943
+ if (isFormValueEqual(store.data(), merged)) {
944
+ return;
945
+ }
946
+ patchState(store, {
947
+ data: merged,
948
+ });
949
+ },
950
+ };
951
+ }));
952
+ //#endregion
953
+
954
+ //#region ---- Imports ----
955
+ //#endregion
956
+ //#region ---- Pipe normalization ----
957
+ /** Normalizes shorthand pipe names to `{ type, options? }` specs. */
958
+ function normalizeContextEvalPipes(pipes) {
959
+ return pipes.map((pipe) => (typeof pipe === 'string' ? { type: pipe } : pipe));
960
+ }
961
+ //#endregion
962
+ //#region ---- Pipe application ----
963
+ /**
964
+ * Applies a left-to-right formatter pipeline via {@link AXFormatService}.
965
+ * Unknown formatters fall back to a trimmed string representation.
966
+ */
967
+ function applyContextEvalPipes(value, pipes, formatService) {
968
+ if (!pipes?.length) {
969
+ return value;
970
+ }
971
+ let current = value;
972
+ for (const pipe of normalizeContextEvalPipes(pipes)) {
973
+ try {
974
+ current = formatService.format(current, pipe.type, pipe.options);
975
+ }
976
+ catch {
977
+ current = current == null ? '' : String(current);
978
+ }
979
+ }
980
+ return current;
981
+ }
982
+ /** Reads a nested path from data and optionally applies display pipes. */
983
+ function contextEvalWithPipes(data, path, formatService, pipes) {
984
+ return applyContextEvalPipes(get(data, path), pipes, formatService);
985
+ }
986
+ //#endregion
987
+
988
+ //#region ---- Imports ----
989
+ //#endregion
990
+ /**
991
+ * Builds sync `context.eval(path, { pipes })` functions backed by {@link AXFormatService}.
992
+ * Supports built-in and custom formatters registered on {@link AXFormatModule}.
993
+ */
994
+ class AXPContextEvalFactory {
995
+ constructor() {
996
+ //#region ---- Services & Dependencies ----
997
+ this.formatService = inject(AXFormatService);
998
+ }
999
+ //#endregion
1000
+ //#region ---- Factory ----
1001
+ /** Creates a sync eval function for the given record/context data object. */
1002
+ create(data) {
1003
+ return (path, options) => {
1004
+ const raw = get(data, path);
1005
+ return applyContextEvalPipes(raw, options?.pipes, this.formatService);
1006
+ };
1007
+ }
1008
+ /** Creates `{ eval }` scope object for expression evaluator scopes. */
1009
+ createScope(data) {
1010
+ return { eval: this.create(data) };
1011
+ }
1012
+ /**
1013
+ * Display-tier scope that re-reads data on each eval (for live stores / changing row context).
1014
+ */
1015
+ createLiveScope(getData) {
1016
+ return {
1017
+ eval: (path, options) => this.create(getData())(path, options),
1018
+ };
1019
+ }
1020
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPContextEvalFactory, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1021
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPContextEvalFactory, providedIn: 'root' }); }
1022
+ }
1023
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPContextEvalFactory, decorators: [{
1024
+ type: Injectable,
1025
+ args: [{ providedIn: 'root' }]
1026
+ }] });
1027
+
1028
+ //#region ---- Types ----
1029
+ //#endregion
1030
+
1031
+ //#region ---- Template normalization ----
1032
+ /**
1033
+ * Normalizes display templates before {@link AXPExpressionEvaluatorService} evaluation.
1034
+ * Converts simple dot paths (e.g. `{{ position.title }}`) to `context.eval("position.title")`
1035
+ * so row data is read from scope.context instead of bare JavaScript identifiers.
1036
+ */
1037
+ function normalizeExpressionTemplate(template) {
1038
+ let normalized = template.replace(/\{\{\s*context\.eval\(['"]([^'"]+)['"]\)\s*\}\}/g, '{{ context.eval("$1") }}');
1039
+ if (!normalized.includes('{{')) {
1040
+ normalized = normalized.replace(/\{/g, '{{').replace(/\}/g, '}}');
1041
+ }
1042
+ return normalized.replace(/\{\{\s*(?!context\.)(?!host\.)([\w]+(?:\.[\w]+)*)\s*\}\}/g, '{{ context.eval("$1") }}');
1043
+ }
1044
+ //#endregion
1045
+
1046
+ class AXPExpressionEvaluatorScopeProviderContext {
1047
+ constructor() {
1048
+ this.scopes = {};
1049
+ }
1050
+ addScope(namespace, functions) {
1051
+ this.scopes[namespace] = { ...this.scopes[namespace], ...functions };
1052
+ }
1053
+ getScopes() {
1054
+ return this.scopes;
1055
+ }
1056
+ }
1057
+ const AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER = new InjectionToken('AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER');
1058
+ class AXPExpressionEvaluatorScopeProviderService {
1059
+ constructor() {
1060
+ this.injector = inject(Injector);
1061
+ this.cache = null;
1062
+ }
1063
+ async load() {
1064
+ if (this.cache)
1065
+ return;
1066
+ const raw = this.injector.get(AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER, [], { optional: true });
1067
+ const providers = Array.isArray(raw)
1068
+ ? raw
1069
+ : typeof raw === 'function'
1070
+ ? raw()
1071
+ : [];
1072
+ const context = new AXPExpressionEvaluatorScopeProviderContext();
1073
+ for (const provider of providers) {
1074
+ // Resolve lazy-loaded providers (provideLazyProvider useFactory returns Promise<Provider>)
1075
+ const resolved = await Promise.resolve(provider);
1076
+ if (!resolved || typeof resolved.provide !== 'function') {
1077
+ continue;
1078
+ }
1079
+ await resolved.provide(context);
1080
+ }
1081
+ this.cache = context.getScopes();
1082
+ }
1083
+ async getScopesAsync() {
1084
+ await this.load();
1085
+ return this.cache || {};
1086
+ }
1087
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPExpressionEvaluatorScopeProviderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1088
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPExpressionEvaluatorScopeProviderService, providedIn: 'root' }); }
1089
+ }
1090
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPExpressionEvaluatorScopeProviderService, decorators: [{
1091
+ type: Injectable,
1092
+ args: [{ providedIn: 'root' }]
1093
+ }] });
1094
+
1095
+ class AXPExpressionEvaluatorService {
1096
+ constructor() {
1097
+ // Memoization cache for compiled expressions
1098
+ this.expressionCache = new Map();
1099
+ this.providerService = inject(AXPExpressionEvaluatorScopeProviderService);
1100
+ }
1101
+ getOrCompileFunction(expression) {
1102
+ if (!this.expressionCache.has(expression)) {
1103
+ // Check if expression contains multiple statements (has semicolons or newlines)
1104
+ const hasMultipleStatements = expression.includes(';') || expression.includes('\n');
1105
+ let fn;
1106
+ if (hasMultipleStatements) {
1107
+ // For multiple statements, execute them in sequence and return the last expression
1108
+ fn = new Function('scope', `with (scope) { return (async function() { ${expression} })(); }`);
1109
+ }
1110
+ else {
1111
+ // For single expressions, use return
1112
+ fn = new Function('scope', `with (scope) { return (async function() { return ${expression}; })(); }`);
1113
+ }
1114
+ this.expressionCache.set(expression, fn);
1115
+ }
1116
+ return this.expressionCache.get(expression);
1117
+ }
1118
+ async getMergedScope(userScope) {
1119
+ const pluginScopes = await this.providerService.getScopesAsync();
1120
+ // Merge pluginScopes and userScope (userScope takes precedence)
1121
+ return { ...pluginScopes, ...userScope };
1122
+ }
1123
+ async evaluate(source, scope = {}) {
1124
+ try {
1125
+ const mergedScope = await this.getMergedScope(scope);
1126
+ if (typeof source === 'string' && source.includes('{{')) {
1127
+ return await this.evaluateStringExpression(source, mergedScope);
1128
+ }
1129
+ else if (Array.isArray(source)) {
1130
+ const evaluatedArray = [];
1131
+ for (const item of source) {
1132
+ evaluatedArray.push(await this.evaluate(item, mergedScope));
1133
+ }
1134
+ return evaluatedArray;
1135
+ }
1136
+ else if (typeof source === 'object' && source !== null) {
1137
+ const evaluatedObject = {};
1138
+ for (const key in source) {
1139
+ if (source.hasOwnProperty(key)) {
1140
+ evaluatedObject[key] = await this.evaluate(source[key], mergedScope);
1141
+ }
1142
+ }
1143
+ return evaluatedObject;
1144
+ }
1145
+ else {
1146
+ return source;
1147
+ }
1148
+ }
1149
+ catch (error) {
1150
+ console.error('Expression evaluator - Error evaluating expression:', source, error);
1151
+ return false;
1152
+ }
1153
+ }
1154
+ async evaluateStringExpression(templateExpression, scope) {
1155
+ const normalizedTemplate = normalizeExpressionTemplate(templateExpression);
1156
+ // Check if the input is exactly a single {{ ... }} expression (handle multiline)
1157
+ const exactMatch = normalizedTemplate.match(/^\s*\{\{\s*([\s\S]*?)\s*\}\}\s*$/);
1158
+ if (exactMatch) {
1159
+ const expression = exactMatch[1];
1160
+ const sandbox = this.getOrCompileFunction(expression);
1161
+ const result = await sandbox(scope);
1162
+ return result;
1163
+ }
1164
+ // Otherwise, interpolate all {{ ... }} expressions in the string
1165
+ const regex = /\{\{\s*([\s\S]*?)\s*\}\}/g;
1166
+ // Collect all matches and their positions
1167
+ const matches = [];
1168
+ let match;
1169
+ while ((match = regex.exec(normalizedTemplate)) !== null) {
1170
+ matches.push({
1171
+ expression: match[1],
1172
+ start: match.index,
1173
+ end: regex.lastIndex,
1174
+ raw: match[0],
1175
+ });
1176
+ }
1177
+ // Evaluate all expressions in parallel
1178
+ const values = await Promise.all(matches.map((m) => {
1179
+ const sandbox = this.getOrCompileFunction(m.expression);
1180
+ return sandbox(scope);
1181
+ }));
1182
+ // Reconstruct the string with evaluated values
1183
+ let result = '';
1184
+ let lastIndex = 0;
1185
+ matches.forEach((m, i) => {
1186
+ result += normalizedTemplate.slice(lastIndex, m.start);
1187
+ const value = values[i];
1188
+ result += value !== undefined && value !== null ? value : '';
1189
+ lastIndex = m.end;
1190
+ });
1191
+ result += normalizedTemplate.slice(lastIndex);
1192
+ return result;
1193
+ }
1194
+ isExpression(expression) {
1195
+ if (typeof expression === 'string') {
1196
+ return expression.trim().startsWith('{{') && expression.trim().endsWith('}}');
1197
+ }
1198
+ return false;
1199
+ }
1200
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPExpressionEvaluatorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1201
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPExpressionEvaluatorService, providedIn: 'root' }); }
1202
+ }
1203
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPExpressionEvaluatorService, decorators: [{
1204
+ type: Injectable,
1205
+ args: [{ providedIn: 'root' }]
1206
+ }] });
1207
+
1208
+ class AXPComponentSlotPlaceholderComponent {
1209
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPComponentSlotPlaceholderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1210
+ 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>
1211
+ <ax-skeleton class="ax-w-full ax-h-10 ax-rounded-md"></ax-skeleton>
1212
+ </div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i1.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1213
+ }
1214
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPComponentSlotPlaceholderComponent, decorators: [{
1215
+ type: Component,
1216
+ args: [{
1217
+ selector: 'axp-component-slot-placeholder',
1218
+ template: `<div>
1219
+ <ax-skeleton class="ax-w-full ax-h-10 ax-rounded-md"></ax-skeleton>
1220
+ </div>`,
1221
+ changeDetection: ChangeDetectionStrategy.OnPush,
1222
+ imports: [AXSkeletonModule],
1223
+ standalone: true,
1224
+ }]
1225
+ }] });
1226
+
1227
+ class AXPComponentSlotRegistryService {
1228
+ constructor() {
1229
+ this.registry = new Map();
1230
+ }
1231
+ register(slotName, config) {
1232
+ let configs = this.registry.get(slotName) || [];
1233
+ // Check if the component is already registered in this slot
1234
+ const isDuplicate = configs.some(existingConfig => existingConfig.name === config.name);
1235
+ if (!isDuplicate) {
1236
+ configs = [...configs, config]; // Add the new configuration
1237
+ this.registry.set(slotName, configs);
1238
+ }
1239
+ }
1240
+ get(slotName) {
1241
+ return this.registry.get(slotName) || [];
1242
+ }
1243
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPComponentSlotRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1244
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPComponentSlotRegistryService, providedIn: 'root' }); }
1245
+ }
1246
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPComponentSlotRegistryService, decorators: [{
1247
+ type: Injectable,
1248
+ args: [{
1249
+ providedIn: 'root'
1250
+ }]
1251
+ }] });
1252
+
1253
+ class AXPComponentSlotDirective {
1254
+ constructor() {
1255
+ this.name = input.required(...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
1256
+ this.host = input(...(ngDevMode ? [undefined, { debugName: "host" }] : /* istanbul ignore next */ []));
1257
+ this.context = input(...(ngDevMode ? [undefined, { debugName: "context" }] : /* istanbul ignore next */ []));
1258
+ this.elementRef = inject(ElementRef);
1259
+ this.registryService = inject(AXPComponentSlotRegistryService);
1260
+ this.injector = inject(Injector);
1261
+ this.evaluator = inject(AXPExpressionEvaluatorService);
1262
+ this.viewContainerRef = inject(ViewContainerRef);
1263
+ this.contextStore = inject(AXPContextStore);
1264
+ this.isEmpty = computed(() => this._viewCount() === 0, ...(ngDevMode ? [{ debugName: "isEmpty" }] : /* istanbul ignore next */ []));
1265
+ // Create a signal to store the count of children
1266
+ this._viewCount = signal(0, ...(ngDevMode ? [{ debugName: "_viewCount" }] : /* istanbul ignore next */ []));
1267
+ // Track component references for updates
1268
+ this.componentRefs = [];
1269
+ // Track placeholder references for replacement
1270
+ this.placeholderRefs = new Map();
1271
+ // Watch for context changes and update component instances
1272
+ effect(() => {
1273
+ const currentContext = this.context();
1274
+ const currentHost = this.host();
1275
+ // Update all component references with new context and host
1276
+ this.componentRefs.forEach((componentRef) => {
1277
+ if (componentRef.instance) {
1278
+ Object.assign(componentRef.instance, {
1279
+ host: currentHost,
1280
+ context: currentContext,
1281
+ });
1282
+ }
1283
+ });
1284
+ });
1285
+ }
1286
+ async ngOnInit() {
1287
+ await this.loadComponents();
1288
+ // Update the signal after loading
1289
+ this._viewCount.set(this.viewContainerRef.length);
1290
+ }
1291
+ async loadComponents() {
1292
+ this.viewContainerRef.clear();
1293
+ // Clear previous component references
1294
+ this.componentRefs = [];
1295
+ this.placeholderRefs.clear();
1296
+ const configs = sortBy(this.registryService.get(this.name()), (c) => c.priority ?? 0);
1297
+ if (!configs) {
1298
+ console.error(`No component found for slot ${this.name()}`);
1299
+ return;
1300
+ }
1301
+ // Evaluate conditions and check features in parallel for performance
1302
+ const results = await Promise.all(configs.map(async (c) => {
1303
+ // Check condition if provided
1304
+ const conditionPassed = c.condition ? await this.evaluateCondition(c.condition) : true;
1305
+ // Check features if provided
1306
+ // const featuresPassed =
1307
+ // c.features && c.features.length > 0 ? this.sessionService.isFeatureEnabled(...c.features) : true;
1308
+ return {
1309
+ config: c,
1310
+ // visible: conditionPassed && featuresPassed,
1311
+ visible: conditionPassed,
1312
+ };
1313
+ }));
1314
+ // Filter visible components while preserving priority order
1315
+ const slots = results.filter((r) => r.visible).map((r) => r.config);
1316
+ // Create skeleton placeholders immediately in priority order
1317
+ slots.forEach((config, index) => {
1318
+ const placeholderRef = this.viewContainerRef.createComponent(AXPComponentSlotPlaceholderComponent);
1319
+ this.placeholderRefs.set(index, placeholderRef);
1320
+ });
1321
+ // Load all components in parallel and replace placeholders as they become ready
1322
+ const loadPromises = slots.map(async (config, index) => {
1323
+ let component;
1324
+ let options = {};
1325
+ // load component
1326
+ if (typeof config.loadComponent === 'function') {
1327
+ await runInInjectionContext(this.injector, async () => {
1328
+ component = await config.loadComponent?.();
1329
+ });
1330
+ }
1331
+ else if (config.component) {
1332
+ component = config.component;
1333
+ }
1334
+ // load options
1335
+ if (typeof config.options === 'function') {
1336
+ await runInInjectionContext(this.injector, async () => {
1337
+ const fun = config.options;
1338
+ options = await fun();
1339
+ });
1340
+ }
1341
+ else if (config.options) {
1342
+ options = await this.evaluator.evaluate(config.options, {});
1343
+ }
1344
+ // Replace placeholder with actual component as soon as it's ready
1345
+ if (!component) {
1346
+ console.warn(`Component failed to load for slot ${this.name()} at index ${index}`);
1347
+ // Remove placeholder if component failed to load
1348
+ const placeholderRef = this.placeholderRefs.get(index);
1349
+ if (placeholderRef) {
1350
+ placeholderRef.destroy();
1351
+ this.placeholderRefs.delete(index);
1352
+ }
1353
+ return;
1354
+ }
1355
+ // Get the placeholder reference at this index
1356
+ const placeholderRef = this.placeholderRefs.get(index);
1357
+ if (!placeholderRef) {
1358
+ console.warn(`Placeholder not found for index ${index}`);
1359
+ return;
1360
+ }
1361
+ // Get the index of the placeholder in the view container
1362
+ const placeholderIndex = this.viewContainerRef.indexOf(placeholderRef.hostView);
1363
+ // Remove the placeholder
1364
+ placeholderRef.destroy();
1365
+ this.placeholderRefs.delete(index);
1366
+ // Create the actual component at the same position
1367
+ const componentRef = this.viewContainerRef.createComponent(component, { index: placeholderIndex });
1368
+ // Store the component reference for future updates
1369
+ this.componentRefs.push(componentRef);
1370
+ Object.assign(componentRef.instance, {
1371
+ host: this.host(),
1372
+ context: this.context(),
1373
+ ...options,
1374
+ });
1375
+ });
1376
+ // Wait for all components to finish loading (they render as they become ready)
1377
+ await Promise.all(loadPromises);
1378
+ }
1379
+ async evaluateCondition(condition) {
1380
+ if (typeof condition === 'string') {
1381
+ const result = await this.evaluator.evaluate(condition, {});
1382
+ return result;
1383
+ }
1384
+ return condition(this.contextStore.data());
1385
+ //return condition(this.context());
1386
+ }
1387
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPComponentSlotDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1388
+ 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 }); }
1389
+ }
1390
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPComponentSlotDirective, decorators: [{
1391
+ type: Directive,
1392
+ args: [{
1393
+ selector: 'axp-component-slot',
1394
+ standalone: false,
1395
+ exportAs: 'slot',
1396
+ }]
1397
+ }], 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 }] }] } });
1398
+
1399
+ class AXPComponentSlotModule {
1400
+ static forRoot(configs) {
1401
+ return {
1402
+ ngModule: AXPComponentSlotModule,
1403
+ providers: [
1404
+ {
1405
+ provide: 'AXPComponentSlotModuleFactory',
1406
+ useFactory: (registry) => () => {
1407
+ if (configs) {
1408
+ for (const [key, value] of Object.entries(configs)) {
1409
+ value.forEach(v => {
1410
+ registry.register(key, v);
1411
+ });
1412
+ }
1413
+ }
1414
+ },
1415
+ deps: [AXPComponentSlotRegistryService],
1416
+ multi: true
1417
+ }
1418
+ ]
1419
+ };
1420
+ }
1421
+ static forChild(configs) {
1422
+ return {
1423
+ ngModule: AXPComponentSlotModule,
1424
+ providers: [
1425
+ {
1426
+ provide: 'AXPComponentSlotModuleFactory',
1427
+ useFactory: (registry) => () => {
1428
+ if (configs) {
1429
+ for (const [key, value] of Object.entries(configs)) {
1430
+ value.forEach(v => {
1431
+ registry.register(key, v);
1432
+ });
1433
+ }
1434
+ }
1435
+ },
1436
+ deps: [AXPComponentSlotRegistryService],
1437
+ multi: true
1438
+ }
1439
+ ]
1440
+ };
1441
+ }
1442
+ /**
1443
+ * @ignore
1444
+ */
1445
+ constructor(instances) {
1446
+ instances?.forEach(f => {
1447
+ f();
1448
+ });
1449
+ }
1450
+ 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 }); }
1451
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: AXPComponentSlotModule, declarations: [AXPComponentSlotDirective], exports: [AXPComponentSlotDirective] }); }
1452
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPComponentSlotModule }); }
1453
+ }
1454
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPComponentSlotModule, decorators: [{
1455
+ type: NgModule,
1456
+ args: [{
1457
+ declarations: [AXPComponentSlotDirective],
1458
+ exports: [AXPComponentSlotDirective]
1459
+ }]
1460
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
1461
+ type: Optional
1462
+ }, {
1463
+ type: Inject,
1464
+ args: ['AXPComponentSlotModuleFactory']
1465
+ }] }] });
1466
+
1467
+ /** Trace namespace for form dirty-state diagnostics (lookup/select revert, discard, baseline). */
1468
+ const FORM_DIRTY_TRACE_NS = 'FORM STATE';
1469
+
1470
+ //#region ---- AI metadata ----
1471
+ //#endregion
1472
+
1473
+ const AXP_CONTEXT_DEFINITION_PROVIDER = new InjectionToken('AXP_CONTEXT_DEFINITION_PROVIDER');
1474
+ //#endregion
1475
+
1476
+ //#region ---- Aggregator service ----
1477
+ /**
1478
+ * Collects {@link AXPContextDefinition} entries from all registered {@link AXPContextDefinitionProvider} instances.
1479
+ */
1480
+ class AXPContextDefinitionProviderService {
1481
+ constructor() {
1482
+ //#region ---- Services & Dependencies ----
1483
+ this.providers = inject(AXP_CONTEXT_DEFINITION_PROVIDER, { optional: true });
1484
+ }
1485
+ //#endregion
1486
+ //#region ---- Public API ----
1487
+ async items() {
1488
+ const items = [];
1489
+ if (!Array.isArray(this.providers)) {
1490
+ return items;
1491
+ }
1492
+ for (const raw of this.providers) {
1493
+ const provider = await Promise.resolve(raw);
1494
+ if (provider && typeof provider.items === 'function') {
1495
+ items.push(...(await provider.items()));
1496
+ }
1497
+ }
1498
+ return items;
1499
+ }
1500
+ async get(name) {
1501
+ return (await this.items()).find((d) => d.name === name);
1502
+ }
1503
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPContextDefinitionProviderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1504
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPContextDefinitionProviderService, providedIn: 'root' }); }
1505
+ }
1506
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPContextDefinitionProviderService, decorators: [{
1507
+ type: Injectable,
1508
+ args: [{ providedIn: 'root' }]
1509
+ }] });
1510
+
1511
+ //#region ---- Imports ----
1512
+ //#endregion
1513
+ //#region ---- Debug Service ----
1514
+ /**
1515
+ * Platform debug and trace logging.
1516
+ * Call `trace` / `traceGroup` for diagnostics (gated by tenant Debug Mode).
1517
+ * Call `warn` / `error` for always-on console output.
1518
+ *
1519
+ * Debug Mode is applied via `setDebugMode()` (wired from settings in platform common).
1520
+ */
1521
+ class AXPDebugService {
1522
+ //#endregion
1523
+ //#region ---- Lifecycle ----
1524
+ constructor() {
1525
+ //#endregion
1526
+ //#region ---- State ----
1527
+ this.debugModeEnabled = false;
1528
+ AXPDebugService.instance = this;
1529
+ }
1530
+ //#endregion
1531
+ //#region ---- Debug Mode ----
1532
+ isDebugMode() {
1533
+ return this.debugModeEnabled;
1534
+ }
1535
+ setDebugMode(enabled) {
1536
+ this.debugModeEnabled = enabled;
1537
+ }
1538
+ //#endregion
1539
+ //#region ---- Trace API ----
1540
+ /**
1541
+ * @param namespace Module/feature namespace (kebab-case). Filter console with `[AXP:<namespace>]`.
1542
+ * @param title Short area name (component, service, middleware).
1543
+ * @param message What happened.
1544
+ * @param data Optional structured payload, or a factory evaluated only when Debug Mode is on.
1545
+ */
1546
+ trace(namespace, title, message, data) {
1547
+ if (!this.debugModeEnabled) {
1548
+ return;
1549
+ }
1550
+ this.log('log', namespace, title, message, data);
1551
+ }
1552
+ /**
1553
+ * Same as {@link trace} but emits a collapsible console group (useful for multi-field payloads).
1554
+ */
1555
+ traceGroup(namespace, title, message, data) {
1556
+ if (!this.debugModeEnabled) {
1557
+ return;
1558
+ }
1559
+ const resolved = this.resolveData(data);
1560
+ console.groupCollapsed(this.formatPrefix(namespace, title, message));
1561
+ if (resolved != null) {
1562
+ for (const [key, value] of Object.entries(resolved)) {
1563
+ if (value !== undefined) {
1564
+ console.log(key, value);
1565
+ }
1566
+ }
1567
+ }
1568
+ console.groupEnd();
1569
+ }
1570
+ warn(namespace, title, message, data) {
1571
+ this.log('warn', namespace, title, message, data);
1572
+ }
1573
+ error(namespace, title, message, data) {
1574
+ this.log('error', namespace, title, message, data);
1575
+ }
1576
+ //#endregion
1577
+ //#region ---- Utility Methods ----
1578
+ formatPrefix(namespace, title, message) {
1579
+ return `[AXP:${namespace}] ${title} — ${message}`;
1580
+ }
1581
+ resolveData(data) {
1582
+ if (data == null) {
1583
+ return undefined;
1584
+ }
1585
+ return typeof data === 'function' ? data() : data;
1586
+ }
1587
+ log(level, namespace, title, message, data) {
1588
+ const prefix = this.formatPrefix(namespace, title, message);
1589
+ const resolved = this.resolveData(data);
1590
+ if (resolved != null) {
1591
+ console[level](prefix, resolved);
1592
+ return;
1593
+ }
1594
+ console[level](prefix);
1595
+ }
1596
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDebugService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1597
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDebugService, providedIn: 'root' }); }
1598
+ }
1599
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDebugService, decorators: [{
1600
+ type: Injectable,
1601
+ args: [{
1602
+ providedIn: 'root',
1603
+ }]
1604
+ }], ctorParameters: () => [] });
1605
+
1606
+ class AXPDataGenerator {
1607
+ static uuid() {
1608
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (char) => {
1609
+ const random = (Math.random() * 16) | 0;
1610
+ const value = char === 'x' ? random : (random & 0x3) | 0x8;
1611
+ return value.toString(16);
1612
+ });
1613
+ }
1614
+ static number(...args) {
1615
+ let min = 0;
1616
+ let max = 100;
1617
+ if (args.length == 1)
1618
+ max = args[0];
1619
+ if (args.length == 2) {
1620
+ min = args[0];
1621
+ max = args[1];
1622
+ }
1623
+ return Math.floor(Math.random() * (max - min + 1)) + min;
1624
+ }
1625
+ static date(...args) {
1626
+ let start = new Date(2000, 0, 1);
1627
+ let end = new Date();
1628
+ if (args.length === 1) {
1629
+ start = args[0];
1630
+ }
1631
+ else if (args.length === 2) {
1632
+ start = args[0];
1633
+ end = args[1];
1634
+ }
1635
+ const startTime = start.getTime();
1636
+ const endTime = end.getTime();
1637
+ return new Date(startTime + Math.random() * (endTime - startTime));
1638
+ }
1639
+ static array(length = 5, generator) {
1640
+ return Array.from({ length }, generator);
1641
+ }
1642
+ static pick(...args) {
1643
+ if (args.length < 1) {
1644
+ throw new Error('Invalid parameters');
1645
+ }
1646
+ const items = args[0];
1647
+ const count = args[1] ?? 1;
1648
+ if (count < 1) {
1649
+ throw new Error('Count must be at least 1');
1650
+ }
1651
+ // If the count is greater than the number of items, just return a shuffled copy of the array
1652
+ if (count >= items.length) {
1653
+ return [...items].sort(() => Math.random() - 0.5);
1654
+ }
1655
+ // Shuffle the array and slice the first 'count' elements
1656
+ const shuffled = items.slice();
1657
+ for (let i = shuffled.length - 1; i > 0; i--) {
1658
+ const j = Math.floor(Math.random() * (i + 1));
1659
+ [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; // Swap elements
1660
+ }
1661
+ return count == 1 ? shuffled.slice(0, count)[0] : shuffled.slice(0, count);
1662
+ }
1663
+ static string(length = 10) {
1664
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
1665
+ return Array.from({ length }, () => chars.charAt(Math.floor(Math.random() * chars.length))).join('');
1666
+ }
1667
+ static boolean() {
1668
+ return Math.random() >= 0.5;
1669
+ }
1670
+ static item(array = []) {
1671
+ return array.length > 0 ? array[Math.floor(Math.random() * array.length)] : undefined;
1672
+ }
1673
+ static color(format = 'hex') {
1674
+ const h = Math.floor(Math.random() * 360);
1675
+ const s = Math.floor(Math.random() * 14) + 72;
1676
+ const l = Math.floor(Math.random() * 8) + 50;
1677
+ if (format === 'hsl') {
1678
+ return `hsl(${h}, ${s}%, ${l}%)`;
1679
+ }
1680
+ const { r, g, b } = this.hslToRgb(h, s / 100, l / 100);
1681
+ if (format === 'rgb') {
1682
+ return `rgb(${r}, ${g}, ${b})`;
1683
+ }
1684
+ const toHex = (val) => val.toString(16).padStart(2, '0');
1685
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
1686
+ }
1687
+ static hslToRgb(h, s, l) {
1688
+ const c = (1 - Math.abs(2 * l - 1)) * s;
1689
+ const x = c * (1 - Math.abs((h / 60) % 2 - 1));
1690
+ const m = l - c / 2;
1691
+ let r = 0, g = 0, b = 0;
1692
+ if (h < 60)
1693
+ [r, g, b] = [c, x, 0];
1694
+ else if (h < 120)
1695
+ [r, g, b] = [x, c, 0];
1696
+ else if (h < 180)
1697
+ [r, g, b] = [0, c, x];
1698
+ else if (h < 240)
1699
+ [r, g, b] = [0, x, c];
1700
+ else if (h < 300)
1701
+ [r, g, b] = [x, 0, c];
1702
+ else
1703
+ [r, g, b] = [c, 0, x];
1704
+ return {
1705
+ r: Math.round((r + m) * 255),
1706
+ g: Math.round((g + m) * 255),
1707
+ b: Math.round((b + m) * 255)
1708
+ };
1709
+ }
1710
+ static alphanumeric(length = 10) {
1711
+ const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
1712
+ return Array.from({ length }, () => chars.charAt(Math.floor(Math.random() * chars.length))).join('');
1713
+ }
1714
+ static alphabet(length = 10) {
1715
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
1716
+ return Array.from({ length }, () => chars.charAt(Math.floor(Math.random() * chars.length))).join('');
1717
+ }
1718
+ static phone() {
1719
+ const areaCode = this.number(100, 999);
1720
+ const exchangeCode = this.number(100, 999);
1721
+ const lineNumber = this.number(1000, 9999);
1722
+ return `${areaCode}-${exchangeCode}-${lineNumber}`;
1723
+ }
1724
+ static firstName() {
1725
+ const firstNames = ['John', 'Allen', 'Jak', 'Rose', 'Kate', 'Liam', 'Olivia', 'Noah', 'Emma', 'Ava'];
1726
+ return this.pick(firstNames);
1727
+ }
1728
+ static lastName() {
1729
+ const lastNames = ['Gates', 'Jonsen', 'Smith', 'Ford', 'Jakson', 'Brown', 'Johnson', 'Williams', 'Jones', 'Garcia'];
1730
+ return this.pick(lastNames);
1731
+ }
1732
+ static email(...args) {
1733
+ const domains = ['gmail.com', 'yahoo.com', 'outlook.com', 'example.com'];
1734
+ const domain = this.pick(domains);
1735
+ const separator = this.pick(['.', '_', '']);
1736
+ const randomSuffix = this.boolean() ? this.number(1, 99).toString() : '';
1737
+ if (args.length === 2) {
1738
+ const firstName = args[0].toLowerCase();
1739
+ const lastName = args[1].toLowerCase();
1740
+ return `${firstName}${separator}${lastName}${randomSuffix}@${domain}`;
1741
+ }
1742
+ else {
1743
+ const firstName = this.firstName().toLowerCase();
1744
+ const lastName = this.lastName().toLowerCase();
1745
+ return `${firstName}${separator}${lastName}${randomSuffix}@${domain}`;
1746
+ }
1747
+ }
1748
+ static country() {
1749
+ const countries = ['USA', 'Canada', 'Mexico', 'Germany', 'France', 'Japan', 'Australia'];
1750
+ return this.pick(countries);
1751
+ }
1752
+ static city() {
1753
+ const cities = ['Los Angeles', 'Toronto', 'Vancouver', 'Berlin', 'Paris', 'Tokyo', 'Sydney'];
1754
+ return this.pick(cities);
1755
+ }
1756
+ static state() {
1757
+ const states = ['NY', 'CA', 'TX', 'ON', 'BC', 'NSW', 'BER', 'IDF', 'TYO'];
1758
+ return this.pick(states);
1759
+ }
1760
+ static address() {
1761
+ const streets = ['Main St', 'High St', 'Maple Ave', 'Oak St', 'Pine St', 'Cedar St'];
1762
+ const streetNumber = this.number(100, 9999);
1763
+ const street = this.pick(streets);
1764
+ const city = this.city();
1765
+ const state = this.state();
1766
+ const zip = this.number(10000, 99999);
1767
+ const country = this.country();
1768
+ return `${streetNumber} ${street}, ${city}, ${state} ${zip}, ${country}`;
1769
+ }
1770
+ static avatar() {
1771
+ return `https://avatar.iran.liara.run/public/${this.pick([35, 22, 16, 6, 31])}`;
1772
+ //return `https://i.pravatar.cc/300?u=${this.uuid()}`;
1773
+ }
1774
+ }
1775
+
1776
+ function objectKeyValueTransforms(keyName) {
1777
+ return {
1778
+ getter: (value) => {
1779
+ return extractValue(value, keyName);
1780
+ },
1781
+ setter: (value) => {
1782
+ return extractValue(value, keyName);
1783
+ },
1784
+ };
1785
+ }
1786
+
1787
+ const AXP_DATASOURCE_DEFINITION_PROVIDER = new InjectionToken('AXP_DATASOURCE_TYPE_PROVIDER');
1788
+ class AXPDataSourceDefinitionProviderService {
1789
+ constructor() {
1790
+ this.providers = inject(AXP_DATASOURCE_DEFINITION_PROVIDER, { optional: true });
1791
+ }
1792
+ async items() {
1793
+ const items = [];
1794
+ // Load from DI tokens; resolve lazy providers (provideLazyProvider uses useFactory that returns Promise<T>)
1795
+ if (Array.isArray(this.providers)) {
1796
+ for (const raw of this.providers) {
1797
+ const provider = await Promise.resolve(raw);
1798
+ if (provider && typeof provider.items === 'function') {
1799
+ set(provider, '__parent__', this);
1800
+ items.push(...(await provider.items()));
1801
+ }
1802
+ }
1803
+ }
1804
+ return items;
1805
+ }
1806
+ async get(name) {
1807
+ return (await this.items()).find((c) => c.name == name);
1808
+ }
1809
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDataSourceDefinitionProviderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1810
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDataSourceDefinitionProviderService, providedIn: 'root' }); }
1811
+ }
1812
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDataSourceDefinitionProviderService, decorators: [{
1813
+ type: Injectable,
1814
+ args: [{ providedIn: 'root' }]
1815
+ }] });
1816
+
1817
+ // src/app/directives/grid-layout.directive.ts
1818
+ class AXPContentCheckerDirective {
1819
+ constructor() {
1820
+ this.viewContainerRef = inject(ViewContainerRef);
1821
+ this.elementRef = inject((ElementRef));
1822
+ this.isEmpty = computed(() => this.viewContainerRef.length === 0, ...(ngDevMode ? [{ debugName: "isEmpty" }] : /* istanbul ignore next */ []));
1823
+ }
1824
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPContentCheckerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1825
+ 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 }); }
1826
+ }
1827
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPContentCheckerDirective, decorators: [{
1828
+ type: Directive,
1829
+ args: [{
1830
+ selector: '[axp-content-checker]',
1831
+ standalone: true,
1832
+ exportAs: 'checker'
1833
+ }]
1834
+ }] });
1835
+
1836
+ class AXPDblClickDirective {
1837
+ constructor() {
1838
+ this.onDblClick = new EventEmitter();
1839
+ this.lastTap = 0;
1840
+ this.doubleTapThreshold = 300; // milliseconds
1841
+ }
1842
+ // Listen for the native dblclick event (desktop)
1843
+ handleOnDblClick(event) {
1844
+ this.onDblClick.emit(event);
1845
+ }
1846
+ // Listen for touchend events (mobile)
1847
+ onTouchEnd(event) {
1848
+ const currentTime = new Date().getTime();
1849
+ const tapGap = currentTime - this.lastTap;
1850
+ if (tapGap > 0 && tapGap < this.doubleTapThreshold) {
1851
+ // Detected a double-tap
1852
+ this.onDblClick.emit(event);
1853
+ event.preventDefault(); // Optionally prevent further default actions
1854
+ }
1855
+ this.lastTap = currentTime;
1856
+ }
1857
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDblClickDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1858
+ 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 }); }
1859
+ }
1860
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDblClickDirective, decorators: [{
1861
+ type: Directive,
1862
+ args: [{
1863
+ selector: '[onDblClick]'
1864
+ }]
1865
+ }], propDecorators: { onDblClick: [{
1866
+ type: Output
1867
+ }], handleOnDblClick: [{
1868
+ type: HostListener,
1869
+ args: ['dblclick', ['$event']]
1870
+ }], onTouchEnd: [{
1871
+ type: HostListener,
1872
+ args: ['touchend', ['$event']]
1873
+ }] } });
1874
+
1875
+ class AXPElementDataDirective {
1876
+ constructor(elementRef) {
1877
+ this.elementRef = elementRef;
1878
+ this.data = input(null, { ...(ngDevMode ? { debugName: "data" } : /* istanbul ignore next */ {}), alias: 'axp-data' });
1879
+ this.path = input('__data__', { ...(ngDevMode ? { debugName: "path" } : /* istanbul ignore next */ {}), alias: 'axp-data-path' });
1880
+ // Effect to update element data when inputs change
1881
+ this.updateEffect = effect(() => {
1882
+ const currentData = this.data();
1883
+ const currentPath = this.path();
1884
+ set(this.elementRef.nativeElement, currentPath, currentData);
1885
+ }, ...(ngDevMode ? [{ debugName: "updateEffect" }] : /* istanbul ignore next */ []));
1886
+ }
1887
+ ngOnInit() {
1888
+ // Initial data setup
1889
+ set(this.elementRef.nativeElement, this.path(), this.data());
1890
+ }
1891
+ /**
1892
+ * Get data from the element
1893
+ */
1894
+ getData() {
1895
+ return get(this.elementRef.nativeElement, this.path());
1896
+ }
1897
+ /**
1898
+ * Update data at runtime
1899
+ */
1900
+ setData(data) {
1901
+ set(this.elementRef.nativeElement, this.path(), data);
1902
+ }
1903
+ 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 }); }
1904
+ 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 }); }
1905
+ }
1906
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPElementDataDirective, decorators: [{
1907
+ type: Directive,
1908
+ args: [{
1909
+ selector: '[axp-data]',
1910
+ standalone: true
1911
+ }]
1912
+ }], 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 }] }] } });
1913
+
1914
+ // src/app/directives/grid-layout.directive.ts
1915
+ class AXPGridLayoutDirective {
1916
+ constructor(el, renderer) {
1917
+ this.el = el;
1918
+ this.renderer = renderer;
1919
+ this.options = input.required({ ...(ngDevMode ? { debugName: "options" } : /* istanbul ignore next */ {}), alias: 'axp-grid-layout' });
1920
+ }
1921
+ ngOnChanges(changes) {
1922
+ if (changes['options']) {
1923
+ this.applyTailwindClasses();
1924
+ }
1925
+ }
1926
+ applyTailwindClasses() {
1927
+ const placement = this.options()?.positions;
1928
+ if (!placement)
1929
+ return;
1930
+ // Clear existing grid classes
1931
+ this.clearClasses();
1932
+ // Apply new grid classes based on the input options
1933
+ this.setClasses(placement.sm, '');
1934
+ this.setClasses(placement.md, 'md:');
1935
+ this.setClasses(placement.lg, 'lg:');
1936
+ this.setClasses(placement.xl, 'xl:');
1937
+ this.setClasses(placement.xxl, '2xl:');
1938
+ }
1939
+ setClasses(placement, prefix) {
1940
+ if (placement == null)
1941
+ return;
1942
+ const colStart = placement.colStart ? `${prefix}ax-col-start-${placement.colStart}` : '';
1943
+ const colEnd = placement.colEnd ? `${prefix}ax-col-end-${placement.colEnd}` : '';
1944
+ const colSpan = placement.colSpan ? `${prefix}ax-col-span-${placement.colSpan}` : '';
1945
+ const rowStart = placement.rowStart ? `${prefix}ax-row-start-${placement.rowStart}` : '';
1946
+ const rowEnd = placement.rowEnd ? `${prefix}ax-row-end-${placement.rowEnd}` : '';
1947
+ const rowSpan = placement.rowSpan ? `${prefix}ax-row-span-${placement.rowSpan}` : '';
1948
+ const order = placement.order ? `${prefix}ax-order-${placement.order}` : ''; // Handling order
1949
+ [colStart, colEnd, colSpan, rowStart, rowEnd, rowSpan, order].forEach(cls => {
1950
+ if (cls) {
1951
+ this.renderer.addClass(this.el.nativeElement, cls);
1952
+ }
1953
+ });
1954
+ }
1955
+ clearClasses() {
1956
+ const currentClasses = this.el.nativeElement.className.split(' ');
1957
+ currentClasses.forEach((className) => {
1958
+ if (className.startsWith('ax-col-') || className.startsWith('ax-row-') || className.startsWith('ax-order-')) {
1959
+ this.renderer.removeClass(this.el.nativeElement, className);
1960
+ }
1961
+ });
1962
+ }
1963
+ 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 }); }
1964
+ 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 }); }
1965
+ }
1966
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPGridLayoutDirective, decorators: [{
1967
+ type: Directive,
1968
+ args: [{
1969
+ selector: '[axp-grid-layout]',
1970
+ standalone: true,
1971
+ }]
1972
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "axp-grid-layout", required: true }] }] } });
1973
+
1974
+ const AXP_DISTRIBUTED_EVENT_LISTENER_PROVIDER = new InjectionToken('AXP_DISTRIBUTED_EVENT_LISTENER_PROVIDER');
1975
+
1976
+ //#region ---- Imports ----
1977
+ //#endregion
1978
+ class AXPDistributedEventListenerService {
1979
+ constructor() {
1980
+ //#region ---- Providers & Caches ----
1981
+ this.listenerProviders = inject(AXP_DISTRIBUTED_EVENT_LISTENER_PROVIDER, { optional: true }) || [];
1982
+ this.injector = inject(Injector);
1983
+ /** Cache for listeners by key. */
1984
+ this.listenersByKey = new Map();
1985
+ /** Flag to track if providers have been loaded */
1986
+ this.providersLoaded = false;
1987
+ }
1988
+ //#endregion
1989
+ //#region ---- Public API ----
1990
+ /**
1991
+ * Dispatches an event to all registered listeners for the given key.
1992
+ * @param key The event key.
1993
+ * @param data The event data to pass to listeners.
1994
+ */
1995
+ async dispatch(key, data) {
1996
+ const providers = await this.resolveProviders();
1997
+ const matched = providers.filter(p => p.key === key);
1998
+ if (!matched.length) {
1999
+ return;
2000
+ }
2001
+ for (const provider of matched) {
2002
+ try {
2003
+ await Promise.resolve(runInInjectionContext(this.injector, () => provider.execute(data)));
2004
+ }
2005
+ catch (err) {
2006
+ console.error(`[AXPDistributedEventListenerService] Provider for key='${key}' failed`, err);
2007
+ }
2008
+ }
2009
+ }
2010
+ /**
2011
+ * Dispatch with async response (first matching provider, with retry/timeout)
2012
+ */
2013
+ async dispatchAsync(key, data, options = {
2014
+ timeout: 60_000,
2015
+ retries: 3,
2016
+ retryDelay: 1000,
2017
+ }) {
2018
+ const providers = await this.resolveProviders();
2019
+ const matched = providers.filter(p => p.key === key);
2020
+ if (!matched.length) {
2021
+ throw new Error(`No provider found for key='${key}'`);
2022
+ }
2023
+ const attempt = (n) => {
2024
+ return new Promise((resolve, reject) => {
2025
+ let finished = false;
2026
+ const timer = setTimeout(() => {
2027
+ if (!finished) {
2028
+ if (n < options.retries) {
2029
+ resolve(attempt(n + 1));
2030
+ }
2031
+ else {
2032
+ reject(new Error(`Timeout: no response for key='${key}' after ${options.retries} attempts.`));
2033
+ }
2034
+ }
2035
+ }, options.timeout);
2036
+ try {
2037
+ const provider = matched[0];
2038
+ const result = runInInjectionContext(this.injector, () => provider.execute(data));
2039
+ Promise.resolve(result)
2040
+ .then((r) => {
2041
+ finished = true;
2042
+ clearTimeout(timer);
2043
+ resolve(r);
2044
+ })
2045
+ .catch(err => {
2046
+ finished = true;
2047
+ clearTimeout(timer);
2048
+ reject(err);
2049
+ });
2050
+ }
2051
+ catch (err) {
2052
+ clearTimeout(timer);
2053
+ reject(err);
2054
+ }
2055
+ });
2056
+ };
2057
+ return attempt(0);
2058
+ }
2059
+ /**
2060
+ * Resolve all providers (handle both direct provider and Promise<provider>)
2061
+ */
2062
+ async resolveProviders() {
2063
+ return Promise.all(this.listenerProviders.map(p => p instanceof Promise ? p : Promise.resolve(p)));
2064
+ }
2065
+ /**
2066
+ * Returns all listeners for a specific event key.
2067
+ * @param key The event key.
2068
+ * @returns Array of listeners for the key.
2069
+ */
2070
+ async getListeners(key) {
2071
+ await this.ensureProvidersLoaded();
2072
+ return this.listenersByKey.get(key) || [];
2073
+ }
2074
+ /**
2075
+ * Returns all registered event keys.
2076
+ * @returns Array of all event keys that have listeners.
2077
+ */
2078
+ async getRegisteredKeys() {
2079
+ await this.ensureProvidersLoaded();
2080
+ return Array.from(this.listenersByKey.keys());
2081
+ }
2082
+ //#endregion
2083
+ //#region ---- Private Methods ----
2084
+ /**
2085
+ * Ensures that all providers have been loaded and cached.
2086
+ */
2087
+ async ensureProvidersLoaded() {
2088
+ if (this.providersLoaded) {
2089
+ return;
2090
+ }
2091
+ // Resolve all providers
2092
+ const resolvedProviders = await Promise.all(this.listenerProviders);
2093
+ // Group listeners by key
2094
+ for (const provider of resolvedProviders) {
2095
+ if (provider) {
2096
+ const existingListeners = this.listenersByKey.get(provider.key) || [];
2097
+ existingListeners.push(provider);
2098
+ this.listenersByKey.set(provider.key, existingListeners);
2099
+ }
2100
+ }
2101
+ this.providersLoaded = true;
2102
+ }
2103
+ //#endregion
2104
+ //#region ---- Cache Management ----
2105
+ /** Clears the listeners cache and forces reload on next access. */
2106
+ clearListenersCache() {
2107
+ this.listenersByKey.clear();
2108
+ this.providersLoaded = false;
2109
+ }
2110
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDistributedEventListenerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2111
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDistributedEventListenerService, providedIn: 'root' }); }
2112
+ }
2113
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDistributedEventListenerService, decorators: [{
2114
+ type: Injectable,
2115
+ args: [{
2116
+ providedIn: 'root',
2117
+ }]
2118
+ }] });
2119
+
2120
+ class AXPBroadcastEventService {
2121
+ constructor() {
2122
+ this.eventSubjects = new Map();
2123
+ this.pendingRequests = new Map();
2124
+ this.multiTabEventHistory = new Set(); // Prevent duplicate processing of the same event
2125
+ this.instanceId = AXPDataGenerator.uuid();
2126
+ this.channel = new BroadcastChannel('platform_global_events');
2127
+ // Handle incoming messages
2128
+ this.channel.onmessage = (event) => {
2129
+ const { type, payload, requestId, response, sourceId } = event.data;
2130
+ // Prevent processing the same event multiple times (multi-tab synchronization)
2131
+ if (requestId && this.multiTabEventHistory.has(requestId)) {
2132
+ return;
2133
+ }
2134
+ if (requestId) {
2135
+ this.multiTabEventHistory.add(requestId); // Store processed requestId
2136
+ setTimeout(() => this.multiTabEventHistory.delete(requestId), 10000); // Cleanup after 10 sec
2137
+ }
2138
+ // Handle responses to pending requests
2139
+ if (requestId && !isUndefined(response) && this.pendingRequests.has(requestId)) {
2140
+ this.pendingRequests.get(requestId)(response);
2141
+ this.pendingRequests.delete(requestId);
2142
+ return;
2143
+ }
2144
+ // Handle normal broadcast events
2145
+ // Ignore events originating from this same instance to avoid double-delivery
2146
+ if (sourceId && sourceId === this.instanceId) {
2147
+ return;
2148
+ }
2149
+ if (type && this.eventSubjects.has(type)) {
2150
+ this.eventSubjects.get(type).next({ data: payload, requestId });
2151
+ }
2152
+ };
2153
+ }
2154
+ /**
2155
+ * Publish an event without expecting a response
2156
+ */
2157
+ publish(type, payload) {
2158
+ this.channel.postMessage({ type, payload, sourceId: this.instanceId });
2159
+ // Local echo so same-tab listeners receive the event as well
2160
+ if (this.eventSubjects.has(type)) {
2161
+ this.eventSubjects.get(type).next({ data: payload });
2162
+ }
2163
+ }
2164
+ /**
2165
+ * Subscribe to an event
2166
+ */
2167
+ listen(type) {
2168
+ if (!this.eventSubjects.has(type)) {
2169
+ this.eventSubjects.set(type, new Subject());
2170
+ }
2171
+ return this.eventSubjects.get(type).asObservable();
2172
+ }
2173
+ /**
2174
+ * Unsubscribe from an event
2175
+ */
2176
+ unsubscribe(type) {
2177
+ if (this.eventSubjects.has(type)) {
2178
+ this.eventSubjects.get(type).complete();
2179
+ this.eventSubjects.delete(type);
2180
+ }
2181
+ }
2182
+ /**
2183
+ * Send a message and wait for a response with retry logic
2184
+ */
2185
+ async sendAndWaitForResponse(type, payload, options = {
2186
+ timeout: 60 * 60 * 1000,
2187
+ retries: 3,
2188
+ retryDelay: 1000,
2189
+ }) {
2190
+ const requestId = AXPDataGenerator.uuid();
2191
+ const attemptRequest = (attempt) => {
2192
+ return new Promise((resolve, reject) => {
2193
+ this.pendingRequests.set(requestId, resolve);
2194
+ this.channel.postMessage({ type, payload, requestId });
2195
+ setTimeout(() => {
2196
+ if (this.pendingRequests.has(requestId)) {
2197
+ this.pendingRequests.delete(requestId);
2198
+ if (attempt < options.retries) {
2199
+ console.warn(`Retrying request '${type}' (${attempt + 1}/${options.retries})...`);
2200
+ resolve(attemptRequest(attempt + 1)); // Retry request
2201
+ }
2202
+ else {
2203
+ reject(new Error(`Timeout: No response received for event '${type}' after ${options.retries} attempts.`));
2204
+ }
2205
+ }
2206
+ }, options.timeout);
2207
+ });
2208
+ };
2209
+ return attemptRequest(0);
2210
+ }
2211
+ /**
2212
+ * Respond to a request
2213
+ */
2214
+ respondToRequest(requestId, response) {
2215
+ if (!requestId) {
2216
+ console.warn('Invalid requestId. Cannot send response.');
2217
+ return;
2218
+ }
2219
+ this.channel.postMessage({ requestId, response });
2220
+ }
2221
+ /**
2222
+ * Cleanup when the service is destroyed
2223
+ */
2224
+ ngOnDestroy() {
2225
+ this.channel.close();
2226
+ // Complete all subjects
2227
+ this.eventSubjects.forEach((subject) => subject.complete());
2228
+ this.eventSubjects.clear();
2229
+ // Reject all pending requests to avoid dangling promises
2230
+ this.pendingRequests.forEach((resolve, requestId) => {
2231
+ resolve(Promise.reject(new Error(`Service destroyed. Request ${requestId} was not completed.`)));
2232
+ });
2233
+ this.pendingRequests.clear();
2234
+ // Cleanup multi-tab event history
2235
+ this.multiTabEventHistory.clear();
2236
+ }
2237
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPBroadcastEventService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2238
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPBroadcastEventService, providedIn: 'root' }); }
2239
+ }
2240
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPBroadcastEventService, decorators: [{
2241
+ type: Injectable,
2242
+ args: [{
2243
+ providedIn: 'root',
2244
+ }]
2245
+ }], ctorParameters: () => [] });
2246
+
2247
+ class AXPHookService {
2248
+ constructor() {
2249
+ this.listenerProviders = inject(AXP_DISTRIBUTED_EVENT_LISTENER_PROVIDER, { optional: true }) || [];
2250
+ this.injector = inject(Injector);
2251
+ }
2252
+ /**
2253
+ * Resolve all providers (handle both direct providers and Promise<provider>)
2254
+ */
2255
+ async resolveProviders() {
2256
+ return Promise.all(this.listenerProviders.map(p => p instanceof Promise ? p : Promise.resolve(p)));
2257
+ }
2258
+ /**
2259
+ * Fire sync hooks (fire-and-forget).
2260
+ * All providers with the given key will be executed.
2261
+ * Execution is not awaited.
2262
+ */
2263
+ fire(key, data) {
2264
+ this.resolveProviders().then(providers => {
2265
+ providers
2266
+ .filter(p => p.key === key)
2267
+ .sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0))
2268
+ .forEach(p => {
2269
+ try {
2270
+ runInInjectionContext(this.injector, () => p.execute(data));
2271
+ }
2272
+ catch (err) {
2273
+ console.error(`[AXPHookService] Hook '${key}' failed`, err);
2274
+ }
2275
+ });
2276
+ });
2277
+ }
2278
+ /**
2279
+ * Run async hooks sequentially (waterfall).
2280
+ * The output of each hook is passed as input to the next hook.
2281
+ * Returns the final merged data after all hooks are executed.
2282
+ */
2283
+ async runAsync(key, initialData) {
2284
+ const providers = (await this.resolveProviders())
2285
+ .filter(p => p.key === key)
2286
+ .sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
2287
+ let data = initialData;
2288
+ for (const p of providers) {
2289
+ data = await Promise.resolve(runInInjectionContext(this.injector, () => p.execute(data)));
2290
+ }
2291
+ return data;
2292
+ }
2293
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPHookService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2294
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPHookService, providedIn: 'root' }); }
2295
+ }
2296
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPHookService, decorators: [{
2297
+ type: Injectable,
2298
+ args: [{ providedIn: 'root' }]
2299
+ }] });
2300
+
2301
+ //#region ---- Overlay Layer DOM Utilities ----
2302
+ /** Acorex overlay root (modal shell, popup, dialog). */
2303
+ const AX_OVERLAY_CONTAINER_SELECTOR = '.ax-overlay-container';
2304
+ /** Anchored widget panel inside an overlay (select, date picker, dropdown). */
2305
+ const AX_OVERLAY_PANE_SELECTOR = '.ax-overlay-pane';
2306
+ /**
2307
+ * True when a visible overlay layer is foreground (modal, popup, widget popover/picker).
2308
+ */
2309
+ function hasForegroundOverlayLayer(document) {
2310
+ if (getVisibleAnchoredOverlayPanes(document).length > 0) {
2311
+ return true;
2312
+ }
2313
+ return getVisibleOverlayContainers(document).length > 0;
2314
+ }
2315
+ /**
2316
+ * Returns visible anchored overlay panes (widget popovers/pickers).
2317
+ */
2318
+ function getVisibleAnchoredOverlayPanes(document) {
2319
+ return Array.from(document.querySelectorAll(AX_OVERLAY_PANE_SELECTOR)).filter(isVisibleOverlayElement);
2320
+ }
2321
+ /**
2322
+ * Visible anchored panes that are not contained by `excludeContainer` (e.g. dialog shell).
2323
+ */
2324
+ function getNestedVisibleOverlayPanes(document, excludeContainer) {
2325
+ return getVisibleAnchoredOverlayPanes(document).filter((pane) => !excludeContainer?.contains(pane));
2326
+ }
2327
+ /**
2328
+ * Collects visible overlay containers in stacking order (later / higher z-index wins).
2329
+ */
2330
+ function getVisibleOverlayContainers(document) {
2331
+ return Array.from(document.querySelectorAll(AX_OVERLAY_CONTAINER_SELECTOR))
2332
+ .filter(isVisibleOverlayElement)
2333
+ .sort(compareOverlayStackOrder);
2334
+ }
2335
+ /** Topmost visible overlay container, if any. */
2336
+ function getTopVisibleOverlayContainer(document) {
2337
+ const containers = getVisibleOverlayContainers(document);
2338
+ return containers.length ? containers[containers.length - 1] : null;
2339
+ }
2340
+ /** Nearest overlay container ancestor of `element`. */
2341
+ function findOverlayContainerAncestor(element) {
2342
+ return element.closest(AX_OVERLAY_CONTAINER_SELECTOR);
2343
+ }
2344
+ /**
2345
+ * Returns true when the overlay element is rendered and visible in the viewport.
2346
+ */
2347
+ function isVisibleOverlayElement(element) {
2348
+ if (!(element instanceof HTMLElement)) {
2349
+ return false;
2350
+ }
2351
+ const style = getComputedStyle(element);
2352
+ if (style.display === 'none' || style.visibility === 'hidden' || Number(style.opacity) === 0) {
2353
+ return false;
2354
+ }
2355
+ const rect = element.getBoundingClientRect();
2356
+ return rect.width > 0 && rect.height > 0;
2357
+ }
2358
+ function compareOverlayStackOrder(a, b) {
2359
+ const za = Number(getComputedStyle(a).zIndex) || 0;
2360
+ const zb = Number(getComputedStyle(b).zIndex) || 0;
2361
+ if (za !== zb) {
2362
+ return za - zb;
2363
+ }
2364
+ const position = a.compareDocumentPosition(b);
2365
+ if (position & Node.DOCUMENT_POSITION_FOLLOWING) {
2366
+ return -1;
2367
+ }
2368
+ if (position & Node.DOCUMENT_POSITION_PRECEDING) {
2369
+ return 1;
2370
+ }
2371
+ return 0;
2372
+ }
2373
+ //#endregion
2374
+
2375
+ //#region ---- Imports ----
2376
+ const HORIZONTAL_DIRECTIONAL_KEY_MIRRORS = {
2377
+ arrowleft: 'arrowright',
2378
+ arrowright: 'arrowleft',
2379
+ left: 'right',
2380
+ right: 'left',
2381
+ '[': ']',
2382
+ ']': '[',
2383
+ };
2384
+ /** Maps a horizontal direction key to its RTL semantic counterpart. */
2385
+ function mirrorHorizontalDirectionalKey(key) {
2386
+ const normalized = normalizeShortcutToken$1(key);
2387
+ return HORIZONTAL_DIRECTIONAL_KEY_MIRRORS[normalized] ?? normalized;
2388
+ }
2389
+ /** True when the chord's primary key is a horizontal direction key (`←`/`→`, `[`/`]`). */
2390
+ function isHorizontalDirectionalShortcutKey(key) {
2391
+ return normalizeShortcutToken$1(key) in HORIZONTAL_DIRECTIONAL_KEY_MIRRORS;
2392
+ }
2393
+ /**
2394
+ * Resolves direction behavior for a chord.
2395
+ * Horizontal direction keys default to `semantic`; others default to `physical`.
2396
+ * An explicit binding value always wins.
2397
+ */
2398
+ function resolveEffectiveDirectionBehavior(chord, explicit) {
2399
+ if (explicit === 'physical' || explicit === 'semantic') {
2400
+ return explicit;
2401
+ }
2402
+ const { key } = parseKeyboardShortcutChord(chord);
2403
+ return isHorizontalDirectionalShortcutKey(key) ? 'semantic' : 'physical';
2404
+ }
2405
+ /** Resolves the key the user presses for a semantically registered chord in RTL. */
2406
+ function resolveSemanticDirectionalKey(storedKey, rtl) {
2407
+ const normalized = normalizeShortcutToken$1(storedKey);
2408
+ if (!rtl) {
2409
+ return normalized;
2410
+ }
2411
+ return mirrorHorizontalDirectionalKey(normalized);
2412
+ }
2413
+ /** Chord string with directional key mirrored for RTL display/matching when semantic. */
2414
+ function resolveDisplayShortcutChord(chord, isRtl, explicitDirectionBehavior) {
2415
+ const directionBehavior = resolveEffectiveDirectionBehavior(chord, explicitDirectionBehavior);
2416
+ const parts = chord
2417
+ .trim()
2418
+ .split('+')
2419
+ .map((part) => part.trim())
2420
+ .filter(Boolean);
2421
+ if (parts.length === 0) {
2422
+ return chord;
2423
+ }
2424
+ const keyPart = parts.pop() ?? '';
2425
+ const rtl = directionBehavior === 'semantic' && isRtl;
2426
+ const resolvedKey = resolveSemanticDirectionalKey(keyPart, rtl);
2427
+ return [...parts, resolvedKey].join('+');
2428
+ }
2429
+ const MODIFIER_ALIASES = {
2430
+ ctrl: 'ctrl',
2431
+ control: 'ctrl',
2432
+ shift: 'shift',
2433
+ alt: 'alt',
2434
+ option: 'alt',
2435
+ meta: 'meta',
2436
+ cmd: 'meta',
2437
+ command: 'meta',
2438
+ win: 'meta',
2439
+ };
2440
+ const KEY_DISPLAY_MAC = {
2441
+ ctrl: '⌃',
2442
+ control: '⌃',
2443
+ shift: '⇧',
2444
+ alt: '⌥',
2445
+ option: '⌥',
2446
+ meta: '⌘',
2447
+ cmd: '⌘',
2448
+ command: '⌘',
2449
+ win: '⌘',
2450
+ enter: '↵',
2451
+ escape: 'Esc',
2452
+ esc: 'Esc',
2453
+ home: 'Home',
2454
+ arrowleft: '←',
2455
+ arrowright: '→',
2456
+ arrowup: '↑',
2457
+ arrowdown: '↓',
2458
+ pageup: 'PgUp',
2459
+ pagedown: 'PgDn',
2460
+ backspace: '⌫',
2461
+ delete: '⌦',
2462
+ space: 'Space',
2463
+ };
2464
+ /**
2465
+ * Parses a shortcut chord such as `Enter`, `Escape`, or `ctrl+shift+s`.
2466
+ */
2467
+ function parseKeyboardShortcutChord(chord) {
2468
+ const parts = chord
2469
+ .trim()
2470
+ .toLowerCase()
2471
+ .split('+')
2472
+ .map((part) => part.trim())
2473
+ .filter(Boolean);
2474
+ const key = normalizeShortcutToken$1(parts.pop() ?? '');
2475
+ const parsed = {
2476
+ ctrl: false,
2477
+ shift: false,
2478
+ alt: false,
2479
+ meta: false,
2480
+ key,
2481
+ };
2482
+ for (const part of parts) {
2483
+ const alias = MODIFIER_ALIASES[part];
2484
+ if (alias) {
2485
+ parsed[alias] = true;
2486
+ }
2487
+ }
2488
+ return parsed;
2489
+ }
2490
+ /**
2491
+ * Returns true when the keyboard event matches the given chord.
2492
+ * On macOS, `ctrl+*` chords also match `meta+*` for common platform shortcuts.
2493
+ */
2494
+ function matchesKeyboardShortcutChord(event, chord, options) {
2495
+ const parsed = parseKeyboardShortcutChord(chord);
2496
+ const mac = isMacPlatform();
2497
+ const directionBehavior = resolveEffectiveDirectionBehavior(chord, options?.directionBehavior);
2498
+ if (!matchesShortcutModifiers(parsed, event, mac)) {
2499
+ return false;
2500
+ }
2501
+ const actualKey = normalizeShortcutEventKey(event);
2502
+ const expectedKey = normalizeShortcutToken$1(parsed.key);
2503
+ const rtlSemantic = directionBehavior === 'semantic' &&
2504
+ options?.isRtl === true &&
2505
+ isHorizontalDirectionalShortcutKey(expectedKey);
2506
+ if (rtlSemantic) {
2507
+ return actualKey === mirrorHorizontalDirectionalKey(expectedKey);
2508
+ }
2509
+ return actualKey === expectedKey;
2510
+ }
2511
+ /**
2512
+ * Matches modifier keys for a parsed chord.
2513
+ * `ctrl+alt` chords also accept AltGraph (Windows) so layout-produced characters still match physical keys.
2514
+ */
2515
+ function matchesShortcutModifiers(parsed, event, mac) {
2516
+ if (parsed.shift !== event.shiftKey) {
2517
+ return false;
2518
+ }
2519
+ const altGraph = hasAltGraphModifier(event);
2520
+ if (parsed.ctrl && parsed.alt) {
2521
+ if (altGraph) {
2522
+ return true;
2523
+ }
2524
+ return event.ctrlKey && event.altKey;
2525
+ }
2526
+ if (parsed.alt !== event.altKey) {
2527
+ return false;
2528
+ }
2529
+ if (parsed.meta) {
2530
+ if (!event.metaKey) {
2531
+ return false;
2532
+ }
2533
+ return true;
2534
+ }
2535
+ if (parsed.ctrl) {
2536
+ if (mac) {
2537
+ return event.metaKey || event.ctrlKey;
2538
+ }
2539
+ return event.ctrlKey;
2540
+ }
2541
+ if (event.ctrlKey || event.metaKey) {
2542
+ return false;
2543
+ }
2544
+ return true;
2545
+ }
2546
+ function hasAltGraphModifier(event) {
2547
+ return typeof event.getModifierState === 'function' && event.getModifierState('AltGraph');
2548
+ }
2549
+ /**
2550
+ * Formats one chord for UI display (OS-aware modifier symbols).
2551
+ */
2552
+ function formatKeyboardShortcutChord(chord, options) {
2553
+ const directionBehavior = resolveEffectiveDirectionBehavior(chord, options?.directionBehavior);
2554
+ const resolvedChord = options?.skipDirectionResolve
2555
+ ? chord
2556
+ : resolveDisplayShortcutChord(chord, options?.isRtl === true, directionBehavior);
2557
+ const mac = isMacPlatform();
2558
+ const parts = resolvedChord
2559
+ .trim()
2560
+ .split('+')
2561
+ .map((part) => part.trim())
2562
+ .filter(Boolean);
2563
+ if (parts.length === 0) {
2564
+ return '';
2565
+ }
2566
+ const keyPart = normalizeShortcutToken$1(parts.pop() ?? '');
2567
+ const modifierParts = parts.map((part) => formatShortcutPart(part.toLowerCase(), mac));
2568
+ const keyDisplay = formatShortcutPart(keyPart, mac);
2569
+ if (mac) {
2570
+ return [...modifierParts, keyDisplay].join('');
2571
+ }
2572
+ return [...modifierParts, keyDisplay].join('+');
2573
+ }
2574
+ /**
2575
+ * Formats multiple chords as a single display string (e.g. `← / PgUp`).
2576
+ */
2577
+ function formatKeyboardShortcutChords(chords, options) {
2578
+ return chords.map((chord) => formatKeyboardShortcutChord(chord, options)).join(' / ');
2579
+ }
2580
+ /** Maps a chord to `ax-kbd-item` key parts (modifiers + display symbols). */
2581
+ function chordToKbdItemKeys(chord, options) {
2582
+ const directionBehavior = resolveEffectiveDirectionBehavior(chord, options?.directionBehavior);
2583
+ const resolvedChord = options?.skipDirectionResolve
2584
+ ? chord
2585
+ : resolveDisplayShortcutChord(chord, options?.isRtl === true, directionBehavior);
2586
+ const mac = isMacPlatform();
2587
+ const symbolKeys = {
2588
+ arrowleft: '←',
2589
+ arrowright: '→',
2590
+ arrowup: '↑',
2591
+ arrowdown: '↓',
2592
+ pageup: 'PgUp',
2593
+ pagedown: 'PgDn',
2594
+ enter: '↵',
2595
+ escape: 'Esc',
2596
+ esc: 'Esc',
2597
+ space: 'Space',
2598
+ '[': '[',
2599
+ ']': ']',
2600
+ };
2601
+ return resolvedChord
2602
+ .trim()
2603
+ .split('+')
2604
+ .map((part) => part.trim())
2605
+ .filter(Boolean)
2606
+ .map((part) => {
2607
+ const normalized = part.toLowerCase();
2608
+ if (mac && (normalized === 'ctrl' || normalized === 'control')) {
2609
+ return '⌘';
2610
+ }
2611
+ return symbolKeys[normalized] ?? part;
2612
+ });
2613
+ }
2614
+ /**
2615
+ * When focus is in a native edit control, avoid stealing plain key shortcuts.
2616
+ */
2617
+ function isKeyboardTargetInsideEditableField(target) {
2618
+ if (!(target instanceof HTMLElement)) {
2619
+ return false;
2620
+ }
2621
+ if (target.isContentEditable) {
2622
+ return true;
2623
+ }
2624
+ const field = target.closest('input, textarea, select');
2625
+ if (!field || field.disabled) {
2626
+ return false;
2627
+ }
2628
+ if (field instanceof HTMLInputElement) {
2629
+ const type = field.type;
2630
+ if (type === 'hidden' ||
2631
+ type === 'checkbox' ||
2632
+ type === 'radio' ||
2633
+ type === 'button' ||
2634
+ type === 'submit' ||
2635
+ type === 'reset' ||
2636
+ type === 'file' ||
2637
+ field.readOnly) {
2638
+ return false;
2639
+ }
2640
+ return true;
2641
+ }
2642
+ if (field instanceof HTMLTextAreaElement) {
2643
+ return !field.readOnly;
2644
+ }
2645
+ return true;
2646
+ }
2647
+ /**
2648
+ * Returns true when Esc should close an open widget overlay first (select, date picker, popover, modal).
2649
+ * Used by the global shortcut registry so page-level Esc bindings do not swallow overlay dismiss.
2650
+ */
2651
+ function shouldDeferEscapeToOpenOverlay(document) {
2652
+ return hasForegroundOverlayLayer(document);
2653
+ }
2654
+ function isMacPlatform() {
2655
+ if (typeof navigator === 'undefined') {
2656
+ return false;
2657
+ }
2658
+ return /Mac|iPhone|iPad|iPod/i.test(navigator.userAgent);
2659
+ }
2660
+ function normalizeShortcutToken$1(key) {
2661
+ const normalized = key.trim().toLowerCase();
2662
+ if (normalized === 'esc') {
2663
+ return 'escape';
2664
+ }
2665
+ if (normalized === 'left') {
2666
+ return 'arrowleft';
2667
+ }
2668
+ if (normalized === 'right') {
2669
+ return 'arrowright';
2670
+ }
2671
+ if (normalized === 'up') {
2672
+ return 'arrowup';
2673
+ }
2674
+ if (normalized === 'down') {
2675
+ return 'arrowdown';
2676
+ }
2677
+ if (normalized === '[' || normalized === 'bracketleft') {
2678
+ return '[';
2679
+ }
2680
+ if (normalized === ']' || normalized === 'bracketright') {
2681
+ return ']';
2682
+ }
2683
+ return normalized;
2684
+ }
2685
+ function normalizeShortcutEventKey(event) {
2686
+ if (event.key === 'Enter') {
2687
+ return 'enter';
2688
+ }
2689
+ if (event.key === 'Escape') {
2690
+ return 'escape';
2691
+ }
2692
+ if (event.key === 'Home') {
2693
+ return 'home';
2694
+ }
2695
+ if (event.key === ' ') {
2696
+ return 'space';
2697
+ }
2698
+ if (event.key === '[' || event.code === 'BracketLeft') {
2699
+ return '[';
2700
+ }
2701
+ if (event.key === ']' || event.code === 'BracketRight') {
2702
+ return ']';
2703
+ }
2704
+ const codeMatch = event.code.match(/^Key([A-Z])$/);
2705
+ if (codeMatch) {
2706
+ return codeMatch[1].toLowerCase();
2707
+ }
2708
+ const digitMatch = event.code.match(/^Digit([0-9])$/);
2709
+ if (digitMatch) {
2710
+ return digitMatch[1];
2711
+ }
2712
+ if (event.key.length === 1) {
2713
+ return event.key.toLowerCase();
2714
+ }
2715
+ const fnMatch = event.code.match(/^F(\d+)$/);
2716
+ if (fnMatch) {
2717
+ return `f${fnMatch[1]}`;
2718
+ }
2719
+ return event.key.toLowerCase();
2720
+ }
2721
+ function formatShortcutPart(part, mac) {
2722
+ const normalized = normalizeShortcutToken$1(part);
2723
+ if (mac && KEY_DISPLAY_MAC[normalized]) {
2724
+ return KEY_DISPLAY_MAC[normalized];
2725
+ }
2726
+ if (!mac && MODIFIER_ALIASES[normalized]) {
2727
+ return normalized.charAt(0).toUpperCase() + normalized.slice(1);
2728
+ }
2729
+ if (KEY_DISPLAY_MAC[normalized]) {
2730
+ return KEY_DISPLAY_MAC[normalized];
2731
+ }
2732
+ if (normalized.length === 1) {
2733
+ return normalized.toUpperCase();
2734
+ }
2735
+ return normalized.charAt(0).toUpperCase() + normalized.slice(1);
2736
+ }
2737
+ //#endregion
2738
+
2739
+ //#region ---- Keyboard Shortcut Declaration Utilities ----
2740
+ /**
2741
+ * Normalizes a declarative shortcut (`'ctrl+s'` or `{ keys: 'home', allowInEditableFields: false }`).
2742
+ */
2743
+ function normalizeKeyboardShortcut(shortcut) {
2744
+ if (typeof shortcut === 'string') {
2745
+ const chord = shortcut.trim();
2746
+ if (!chord) {
2747
+ return undefined;
2748
+ }
2749
+ return { keys: [chord] };
2750
+ }
2751
+ return normalizeKeyboardShortcutConfig(shortcut);
2752
+ }
2753
+ /**
2754
+ * Normalizes an array of declarative shortcuts. Empty or invalid entries are skipped.
2755
+ */
2756
+ function normalizeKeyboardShortcuts(shortcuts) {
2757
+ if (!shortcuts?.length) {
2758
+ return [];
2759
+ }
2760
+ const result = [];
2761
+ for (const shortcut of shortcuts) {
2762
+ const normalized = normalizeKeyboardShortcut(shortcut);
2763
+ if (normalized) {
2764
+ result.push(normalized);
2765
+ }
2766
+ }
2767
+ return result;
2768
+ }
2769
+ /** Returns the first chord from declarative shortcuts (for UI hints such as `ax-kbd`). */
2770
+ function getPrimaryKeyboardShortcutChord(shortcuts) {
2771
+ return normalizeKeyboardShortcuts(shortcuts)[0]?.keys[0];
2772
+ }
2773
+ function normalizeKeyboardShortcutConfig(shortcut) {
2774
+ const keys = Array.isArray(shortcut.keys) ? shortcut.keys : [shortcut.keys];
2775
+ const normalizedKeys = keys.map((chord) => chord.trim()).filter(Boolean);
2776
+ if (normalizedKeys.length === 0) {
2777
+ return undefined;
2778
+ }
2779
+ return {
2780
+ keys: normalizedKeys,
2781
+ allowInEditableFields: shortcut.allowInEditableFields,
2782
+ when: shortcut.when,
2783
+ directionBehavior: shortcut.directionBehavior,
2784
+ };
2785
+ }
2786
+ //#endregion
2787
+
2788
+ //#region ---- Imports ----
2789
+ //#endregion
2790
+ class AXPKeyboardShortcutRegistry {
2791
+ constructor() {
2792
+ //#region ---- Services & Dependencies ----
2793
+ this.document = inject(DOCUMENT);
2794
+ this.localeService = inject(AXLocaleService);
2795
+ this.activeProfile = toSignal(this.localeService.profileChanged$, {
2796
+ initialValue: this.localeService.activeProfile(),
2797
+ });
2798
+ this.registrations = signal([], ...(ngDevMode ? [{ debugName: "registrations" }] : /* istanbul ignore next */ []));
2799
+ this.helpDialogHandler = null;
2800
+ this.listenerAttached = false;
2801
+ //#endregion
2802
+ //#region ---- Computed Properties ----
2803
+ /** Active shortcut entries for the help dialog. */
2804
+ this.helpEntries = computed(() => {
2805
+ this.activeProfile();
2806
+ return this.buildHelpEntries();
2807
+ }, ...(ngDevMode ? [{ debugName: "helpEntries" }] : /* istanbul ignore next */ []));
2808
+ this.onDocumentKeyDown = (event) => {
2809
+ if (event.key === 'Escape' && shouldDeferEscapeToOpenOverlay(this.document)) {
2810
+ return;
2811
+ }
2812
+ if (event.key === 'F1') {
2813
+ event.preventDefault();
2814
+ event.stopImmediatePropagation();
2815
+ if (hasForegroundOverlayLayer(this.document)) {
2816
+ return;
2817
+ }
2818
+ }
2819
+ const candidates = this.resolveActiveCandidates(event);
2820
+ if (candidates.length === 0) {
2821
+ return;
2822
+ }
2823
+ const match = candidates[0];
2824
+ event.preventDefault();
2825
+ event.stopImmediatePropagation();
2826
+ void match.binding.handler(event);
2827
+ };
2828
+ }
2829
+ //#endregion
2830
+ //#region ---- Public Methods ----
2831
+ /**
2832
+ * Registers a scope of shortcuts. Unregisters automatically when `owner` is destroyed.
2833
+ */
2834
+ register(options) {
2835
+ const { owner, ...registrationOptions } = options;
2836
+ const registrationId = `${options.id}-${this.createRegistrationId()}`;
2837
+ const entry = {
2838
+ ...registrationOptions,
2839
+ registrationId,
2840
+ priority: options.priority ?? 0,
2841
+ };
2842
+ this.registrations.update((items) => [
2843
+ ...items.filter((item) => item.id !== options.id),
2844
+ entry,
2845
+ ]);
2846
+ this.ensureListener();
2847
+ try {
2848
+ owner.onDestroy(() => {
2849
+ this.unregister(registrationId);
2850
+ });
2851
+ }
2852
+ catch {
2853
+ // Owner view was already destroyed (e.g. async registration after navigation).
2854
+ this.unregister(registrationId);
2855
+ }
2856
+ }
2857
+ /**
2858
+ * Wires the F1 help dialog opener (provided by platform/common).
2859
+ */
2860
+ setHelpDialogHandler(handler) {
2861
+ this.helpDialogHandler = handler;
2862
+ this.ensureHelpShortcutRegistered();
2863
+ }
2864
+ /** OS-aware display string for UI hints. */
2865
+ formatDisplayKeys(chords) {
2866
+ return formatKeyboardShortcutChords(chords, { isRtl: this.isRtlProfile() });
2867
+ }
2868
+ //#endregion
2869
+ //#region ---- Private Methods ----
2870
+ ensureHelpShortcutRegistered() {
2871
+ const alreadyRegistered = this.registrations().some((entry) => entry.id === 'system:keyboard-shortcuts-help');
2872
+ if (alreadyRegistered || !this.helpDialogHandler) {
2873
+ return;
2874
+ }
2875
+ const handler = this.helpDialogHandler;
2876
+ const entry = {
2877
+ registrationId: 'system:keyboard-shortcuts-help',
2878
+ id: 'system:keyboard-shortcuts-help',
2879
+ priority: -1,
2880
+ scope: '@general:keyboard-shortcuts.groups.system',
2881
+ shortcuts: [
2882
+ {
2883
+ keys: ['f1'],
2884
+ title: '@general:keyboard-shortcuts.help-action',
2885
+ allowInEditableFields: true,
2886
+ handler: () => {
2887
+ void handler();
2888
+ },
2889
+ },
2890
+ ],
2891
+ };
2892
+ this.registrations.update((items) => [...items, entry]);
2893
+ this.ensureListener();
2894
+ }
2895
+ createRegistrationId() {
2896
+ if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
2897
+ return crypto.randomUUID();
2898
+ }
2899
+ return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
2900
+ }
2901
+ unregister(registrationId) {
2902
+ this.registrations.update((items) => items.filter((item) => item.registrationId !== registrationId));
2903
+ }
2904
+ ensureListener() {
2905
+ if (this.listenerAttached || typeof this.document === 'undefined') {
2906
+ return;
2907
+ }
2908
+ this.document.addEventListener('keydown', this.onDocumentKeyDown, true);
2909
+ this.listenerAttached = true;
2910
+ }
2911
+ resolveActiveCandidates(event) {
2912
+ const editable = isKeyboardTargetInsideEditableField(event.target);
2913
+ const candidates = [];
2914
+ for (const registration of this.registrations()) {
2915
+ if (!this.isRegistrationActive(registration)) {
2916
+ continue;
2917
+ }
2918
+ const priority = registration.priority ?? 0;
2919
+ for (const binding of registration.shortcuts) {
2920
+ if (binding.when && !binding.when()) {
2921
+ continue;
2922
+ }
2923
+ if (editable && !binding.allowInEditableFields) {
2924
+ continue;
2925
+ }
2926
+ for (const chord of binding.keys) {
2927
+ const directionBehavior = resolveEffectiveDirectionBehavior(chord, binding.directionBehavior);
2928
+ if (!matchesKeyboardShortcutChord(event, chord, {
2929
+ directionBehavior,
2930
+ isRtl: this.isRtlProfile(),
2931
+ })) {
2932
+ continue;
2933
+ }
2934
+ candidates.push({
2935
+ priority,
2936
+ registration,
2937
+ binding,
2938
+ chord,
2939
+ });
2940
+ break;
2941
+ }
2942
+ }
2943
+ }
2944
+ return candidates.sort((a, b) => b.priority - a.priority);
2945
+ }
2946
+ isRegistrationActive(registration) {
2947
+ if (registration.when && !registration.when()) {
2948
+ return false;
2949
+ }
2950
+ const element = registration.elementRef?.nativeElement;
2951
+ if (element && !this.document.contains(element)) {
2952
+ return false;
2953
+ }
2954
+ return true;
2955
+ }
2956
+ isRtlProfile() {
2957
+ return this.activeProfile()?.i18nMeta?.rtl === true;
2958
+ }
2959
+ buildHelpEntries() {
2960
+ const entries = [];
2961
+ const isRtl = this.isRtlProfile();
2962
+ for (const registration of this.registrations()) {
2963
+ if (!this.isRegistrationActive(registration)) {
2964
+ continue;
2965
+ }
2966
+ if (registration.id === 'system:keyboard-shortcuts-help') {
2967
+ continue;
2968
+ }
2969
+ const priority = registration.priority ?? 0;
2970
+ for (const binding of registration.shortcuts) {
2971
+ const displayChords = binding.keys.map((chord) => {
2972
+ const directionBehavior = resolveEffectiveDirectionBehavior(chord, binding.directionBehavior);
2973
+ return resolveDisplayShortcutChord(chord, isRtl, directionBehavior);
2974
+ });
2975
+ entries.push({
2976
+ registrationId: registration.registrationId,
2977
+ bindingTitle: binding.title,
2978
+ chords: [...binding.keys],
2979
+ displayChords,
2980
+ displayKeys: formatKeyboardShortcutChords(displayChords, { skipDirectionResolve: true }),
2981
+ scope: registration.scope,
2982
+ group: binding.group ?? registration.group,
2983
+ priority,
2984
+ });
2985
+ }
2986
+ }
2987
+ return entries.sort((a, b) => b.priority - a.priority || a.bindingTitle.localeCompare(b.bindingTitle));
2988
+ }
2989
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPKeyboardShortcutRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2990
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPKeyboardShortcutRegistry, providedIn: 'root' }); }
2991
+ }
2992
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPKeyboardShortcutRegistry, decorators: [{
2993
+ type: Injectable,
2994
+ args: [{
2995
+ providedIn: 'root',
2996
+ }]
2997
+ }] });
2998
+
2999
+ //#region ---- Dialog Action Shortcut Utilities ----
3000
+ const DEFAULT_CANCEL_DIALOG_ACTION_SHORTCUTS = ['Esc'];
3001
+ const DEFAULT_SUBMIT_DIALOG_ACTION_SHORTCUTS = ['Enter', 'ctrl+s'];
3002
+ const PRIMARY_DIALOG_ACTION_COMMANDS = new Set([
3003
+ 'submit',
3004
+ 'create',
3005
+ 'apply',
3006
+ 'save',
3007
+ 'select',
3008
+ 'entity-form-done',
3009
+ 'upload-image',
3010
+ ]);
3011
+ /**
3012
+ * Parses a shortcut string such as `Enter`, `Escape`, or `ctrl+shift+s`.
3013
+ */
3014
+ function parseDialogActionShortcut(shortcut) {
3015
+ const parts = shortcut
3016
+ .trim()
3017
+ .toLowerCase()
3018
+ .split('+')
3019
+ .map((part) => part.trim())
3020
+ .filter(Boolean);
3021
+ const key = normalizeShortcutToken(parts.pop() ?? '');
3022
+ return {
3023
+ ctrl: parts.includes('ctrl') || parts.includes('control'),
3024
+ shift: parts.includes('shift'),
3025
+ alt: parts.includes('alt') || parts.includes('option'),
3026
+ meta: parts.includes('meta') || parts.includes('cmd') || parts.includes('command'),
3027
+ key,
3028
+ };
3029
+ }
3030
+ /**
3031
+ * Returns true when the keyboard event matches the given shortcut definition.
3032
+ */
3033
+ function matchesDialogActionShortcut(event, shortcut) {
3034
+ const parsed = parseDialogActionShortcut(shortcut);
3035
+ if (parsed.ctrl !== event.ctrlKey) {
3036
+ return false;
3037
+ }
3038
+ if (parsed.shift !== event.shiftKey) {
3039
+ return false;
3040
+ }
3041
+ if (parsed.alt !== event.altKey) {
3042
+ return false;
3043
+ }
3044
+ if (parsed.meta !== event.metaKey) {
3045
+ return false;
3046
+ }
3047
+ return normalizeShortcutKey(event) === normalizeShortcutToken(parsed.key);
3048
+ }
3049
+ /**
3050
+ * Resolves footer action shortcuts: defaults when omitted, merge when extras are provided, none when `[]`.
3051
+ */
3052
+ function resolveDialogActionShortcuts(defaults, overrides) {
3053
+ if (overrides !== undefined) {
3054
+ if (overrides.length === 0) {
3055
+ return undefined;
3056
+ }
3057
+ return flattenDialogActionShortcutChords([...defaults, ...overrides]);
3058
+ }
3059
+ return flattenDialogActionShortcutChords(defaults);
3060
+ }
3061
+ /**
3062
+ * Applies built-in footer shortcuts when actions are declared without `shortcuts`
3063
+ * (e.g. workflow `show-layout-popup` raw action config).
3064
+ */
3065
+ function resolveConfiguredFooterActionShortcuts(command, explicit) {
3066
+ if (explicit !== undefined) {
3067
+ return resolveDialogActionShortcuts([], explicit);
3068
+ }
3069
+ if (command === 'cancel') {
3070
+ return resolveDialogActionShortcuts(DEFAULT_CANCEL_DIALOG_ACTION_SHORTCUTS, undefined);
3071
+ }
3072
+ if (command && PRIMARY_DIALOG_ACTION_COMMANDS.has(command)) {
3073
+ return resolveDialogActionShortcuts(DEFAULT_SUBMIT_DIALOG_ACTION_SHORTCUTS, undefined);
3074
+ }
3075
+ return undefined;
3076
+ }
3077
+ function flattenDialogActionShortcutChords(shortcuts) {
3078
+ const chords = normalizeKeyboardShortcuts(shortcuts).flatMap((shortcut) => shortcut.keys);
3079
+ const deduped = dedupeDialogActionShortcuts(chords);
3080
+ return deduped.length ? deduped : undefined;
3081
+ }
3082
+ function dedupeDialogActionShortcuts(shortcuts) {
3083
+ const seen = new Set();
3084
+ const result = [];
3085
+ for (const shortcut of shortcuts) {
3086
+ const token = normalizeShortcutToken(shortcut);
3087
+ if (!token || seen.has(token)) {
3088
+ continue;
3089
+ }
3090
+ seen.add(token);
3091
+ result.push(shortcut.trim());
3092
+ }
3093
+ return result;
3094
+ }
3095
+ function normalizeShortcutToken(key) {
3096
+ const normalized = key.trim().toLowerCase();
3097
+ if (normalized === 'esc') {
3098
+ return 'escape';
3099
+ }
3100
+ return normalized;
3101
+ }
3102
+ /**
3103
+ * Whether a dialog footer shortcut may fire while focus is inside an editable field.
3104
+ */
3105
+ function isDialogShortcutAllowedInEditableField(shortcut) {
3106
+ const parsed = parseDialogActionShortcut(shortcut);
3107
+ const isPlainEnter = parsed.key === 'enter' && !parsed.ctrl && !parsed.shift && !parsed.alt && !parsed.meta;
3108
+ return !isPlainEnter;
3109
+ }
3110
+ /**
3111
+ * Normalizes dialog footer chord strings for {@link AXPKeyboardShortcutRegistry} (`Esc` → `escape`).
3112
+ */
3113
+ function normalizeDialogActionShortcutChord(shortcut) {
3114
+ const parts = shortcut
3115
+ .trim()
3116
+ .split('+')
3117
+ .map((part) => part.trim())
3118
+ .filter(Boolean);
3119
+ if (parts.length === 0) {
3120
+ return shortcut.trim();
3121
+ }
3122
+ const key = normalizeShortcutToken(parts.pop() ?? '');
3123
+ const modifiers = parts.map((part) => part.toLowerCase());
3124
+ return modifiers.length ? `${modifiers.join('+')}+${key}` : key;
3125
+ }
3126
+ /**
3127
+ * Whether a shortcut should fire given the current focus target.
3128
+ * Enter without modifiers is suppressed in multiline / rich-text fields.
3129
+ */
3130
+ function shouldTriggerDialogActionShortcut(event, shortcut) {
3131
+ const parsed = parseDialogActionShortcut(shortcut);
3132
+ const isPlainEnter = parsed.key === 'enter' && !parsed.ctrl && !parsed.shift && !parsed.alt && !parsed.meta;
3133
+ if (!isPlainEnter) {
3134
+ return true;
3135
+ }
3136
+ return !isKeyboardTargetMultilineEditable(event.target);
3137
+ }
3138
+ function normalizeShortcutKey(event) {
3139
+ if (event.key === 'Enter') {
3140
+ return 'enter';
3141
+ }
3142
+ if (event.key === 'Escape') {
3143
+ return 'escape';
3144
+ }
3145
+ if (event.key === ' ') {
3146
+ return 'space';
3147
+ }
3148
+ if (event.key.length === 1) {
3149
+ return event.key.toLowerCase();
3150
+ }
3151
+ const codeMatch = event.code.match(/^Key([A-Z])$/);
3152
+ if (codeMatch) {
3153
+ return codeMatch[1].toLowerCase();
3154
+ }
3155
+ const digitMatch = event.code.match(/^Digit([0-9])$/);
3156
+ if (digitMatch) {
3157
+ return digitMatch[1];
3158
+ }
3159
+ return event.key.toLowerCase();
3160
+ }
3161
+ function isKeyboardTargetMultilineEditable(target) {
3162
+ if (!(target instanceof HTMLElement)) {
3163
+ return false;
3164
+ }
3165
+ if (target.isContentEditable) {
3166
+ return true;
3167
+ }
3168
+ const textarea = target.closest('textarea');
3169
+ if (textarea && !textarea.readOnly && !textarea.disabled) {
3170
+ return true;
3171
+ }
3172
+ const richText = target.closest('[contenteditable="true"]');
3173
+ return richText instanceof HTMLElement;
3174
+ }
3175
+ //#endregion
3176
+
3177
+ //#region ---- Popup Footer Keyboard Shortcuts ----
3178
+ /**
3179
+ * Registers popup footer shortcuts at modal priority so they win over page bindings and stop bubbling.
3180
+ */
3181
+ function registerPopupFooterKeyboardShortcuts(options) {
3182
+ const group = '@general:keyboard-shortcuts.groups.dialog';
3183
+ const scope = options.scope ?? group;
3184
+ const shortcuts = [];
3185
+ for (const action of options.actions) {
3186
+ if (action.hidden?.()) {
3187
+ continue;
3188
+ }
3189
+ const chords = action.shortcuts !== undefined
3190
+ ? resolveConfiguredFooterActionShortcuts(action.command, action.shortcuts)
3191
+ : resolveConfiguredFooterActionShortcuts(action.command, undefined);
3192
+ if (!chords?.length) {
3193
+ continue;
3194
+ }
3195
+ for (const shortcut of chords) {
3196
+ shortcuts.push({
3197
+ keys: [normalizeDialogActionShortcutChord(shortcut)],
3198
+ title: action.title,
3199
+ group,
3200
+ allowInEditableFields: isDialogShortcutAllowedInEditableField(shortcut),
3201
+ when: () => {
3202
+ if (options.when && !options.when()) {
3203
+ return false;
3204
+ }
3205
+ if (action.hidden?.()) {
3206
+ return false;
3207
+ }
3208
+ if (action.disabled?.()) {
3209
+ return false;
3210
+ }
3211
+ return true;
3212
+ },
3213
+ handler: () => {
3214
+ void action.handler();
3215
+ },
3216
+ });
3217
+ }
3218
+ }
3219
+ options.registry.register({
3220
+ owner: options.owner,
3221
+ elementRef: options.elementRef,
3222
+ id: options.id,
3223
+ priority: AXPKeyboardShortcutPriority.Modal,
3224
+ scope,
3225
+ when: options.when,
3226
+ shortcuts,
3227
+ });
3228
+ }
3229
+ /**
3230
+ * Binds footer shortcuts for AXPopupService-hosted components. Call from the popup constructor.
3231
+ */
3232
+ function setupPopupFooterKeyboardShortcuts(getActions, config) {
3233
+ const destroyRef = inject(DestroyRef);
3234
+ const elementRef = inject(ElementRef);
3235
+ const registry = inject(AXPKeyboardShortcutRegistry);
3236
+ const id = config?.id ?? 'popup-footer';
3237
+ effect(() => {
3238
+ getActions();
3239
+ config?.when?.();
3240
+ untracked(() => {
3241
+ registerPopupFooterKeyboardShortcuts({
3242
+ registry,
3243
+ owner: destroyRef,
3244
+ elementRef,
3245
+ id,
3246
+ scope: config?.scope,
3247
+ when: config?.when,
3248
+ actions: getActions(),
3249
+ });
3250
+ });
3251
+ });
3252
+ }
3253
+ //#endregion
3254
+
3255
+ /** Global overlay flag + task count ({@link AXPLayoutThemeService}). */
3256
+ const OVERLAY_LAYOUT_TRACE_NS = 'OVERLAY-LAYOUT';
3257
+ /** Root shell loading dialog sync ({@link AXPRootLayoutComponent}). */
3258
+ const OVERLAY_ROOT_LAYOUT_TRACE_NS = 'OVERLAY-ROOT-LAYOUT';
3259
+ /** Details view page load and overlay release orchestration. */
3260
+ const OVERLAY_DETAILS_VIEW_TRACE_NS = 'OVERLAY-DETAILS-VIEW';
3261
+ /** Widget tree mount / settle polling ({@link AXPWidgetContainerComponent}). */
3262
+ const OVERLAY_WIDGET_SETTLE_TRACE_NS = 'OVERLAY-WIDGET-SETTLE';
3263
+
3264
+ //#region ---- Imports ----
3265
+ //#endregion
3266
+
3267
+ //#region ---- Provider Interfaces ----
3268
+ /**
3269
+ * Injection token for session service.
3270
+ * Implementation: @acorex-platform/framework-client/auth
3271
+ */
3272
+ const AXP_SESSION_SERVICE = new InjectionToken('AXP_SESSION_SERVICE');
3273
+ /**
3274
+ * Injection token for module manifest providers.
3275
+ * Modules register their manifest providers using this token.
3276
+ */
3277
+ const AXP_MODULE_MANIFEST_PROVIDER = new InjectionToken('AXP_MODULE_MANIFEST_PROVIDER');
3278
+ //#endregion
3279
+
3280
+ //#region ---- Imports ----
3281
+ //#endregion
3282
+ //#region ---- Feature Definition Provider Interface ----
3283
+ /**
3284
+ * Context for feature definition providers.
3285
+ * Allows providers to add additional feature definitions dynamically.
3286
+ */
3287
+ class AXPFeatureDefinitionProviderContext {
3288
+ constructor() {
3289
+ this.features = new Map();
3290
+ }
3291
+ /**
3292
+ * Add a feature definition.
3293
+ * @param key Full feature key (ModuleName:FeatureKey format with colon separator)
3294
+ * @param definition Feature definition
3295
+ */
3296
+ addFeature(key, definition) {
3297
+ this.features.set(key, definition);
3298
+ }
3299
+ /**
3300
+ * Get all feature definitions.
3301
+ */
3302
+ getFeatures() {
3303
+ return this.features;
3304
+ }
3305
+ }
3306
+ /**
3307
+ * Injection token for feature definition providers.
3308
+ */
3309
+ const AXP_FEATURE_DEFINITION_PROVIDER = new InjectionToken('AXP_FEATURE_DEFINITION_PROVIDER', {
3310
+ providedIn: 'root',
3311
+ factory: () => [],
3312
+ });
3313
+ //#endregion
3314
+
3315
+ //#region ---- Imports ----
3316
+ //#endregion
3317
+ //#region ---- Registry Service ----
3318
+ /**
3319
+ * Registry service for module manifests.
3320
+ * Collects and manages all registered module manifests.
3321
+ */
3322
+ class AXPModuleManifestRegistry {
3323
+ constructor() {
3324
+ //#region ---- Fields ----
3325
+ this.manifests = new Map();
3326
+ this.featureDefinitions = new Map();
3327
+ this.injector = inject(Injector);
3328
+ this.initializationPromise = null;
3329
+ this.isInitialized = false;
3330
+ }
3331
+ //#endregion
3332
+ //#region ---- Initialization ----
3333
+ /**
3334
+ * Initialize registry by loading all manifest providers.
3335
+ * Should be called during app startup.
3336
+ * Safe to call multiple times - will only initialize once.
3337
+ */
3338
+ async initialize() {
3339
+ // If already initialized, return immediately
3340
+ if (this.isInitialized) {
3341
+ return;
3342
+ }
3343
+ // If initialization is in progress, wait for it
3344
+ if (this.initializationPromise) {
3345
+ return this.initializationPromise;
3346
+ }
3347
+ // Start initialization
3348
+ this.initializationPromise = this._doInitialize();
3349
+ await this.initializationPromise;
3350
+ }
3351
+ /**
3352
+ * Internal initialization logic.
3353
+ */
3354
+ async _doInitialize() {
3355
+ // Get manifests from injector (using useValue, they are provided directly)
3356
+ let manifests;
3357
+ try {
3358
+ const injectedManifests = this.injector.get(AXP_MODULE_MANIFEST_PROVIDER, []);
3359
+ manifests = Array.isArray(injectedManifests) ? injectedManifests : injectedManifests ? [injectedManifests] : [];
3360
+ }
3361
+ catch (error) {
3362
+ console.error('Failed to get manifest providers from injector:', error);
3363
+ manifests = [];
3364
+ }
3365
+ // Register all manifests directly
3366
+ const registerPromises = manifests.map(async (manifest) => {
3367
+ try {
3368
+ // Validate manifest
3369
+ if (!manifest) {
3370
+ console.error('Invalid manifest:', manifest);
3371
+ return;
3372
+ }
3373
+ await this.register(manifest);
3374
+ }
3375
+ catch (error) {
3376
+ console.error(`Failed to register manifest:`, error);
3377
+ }
3378
+ });
3379
+ await Promise.all(registerPromises);
3380
+ // Load and register features from all feature definition providers
3381
+ await this.loadFeatureDefinitions();
3382
+ this.isInitialized = true;
3383
+ }
3384
+ //#endregion
3385
+ //#region ---- Manifest Management ----
3386
+ /**
3387
+ * Register a module manifest.
3388
+ */
3389
+ async register(manifest) {
3390
+ // Validate manifest
3391
+ if (!manifest.name) {
3392
+ throw new Error('Manifest must have a name');
3393
+ }
3394
+ if (!manifest.version) {
3395
+ throw new Error(`Manifest for module '${manifest.name}' must have a version`);
3396
+ }
3397
+ // Register manifest
3398
+ this.manifests.set(manifest.name, manifest);
3399
+ }
3400
+ /**
3401
+ * Get manifest for a module.
3402
+ */
3403
+ get(moduleName) {
3404
+ return this.manifests.get(moduleName);
3405
+ }
3406
+ /**
3407
+ * Get all registered manifests.
3408
+ */
3409
+ getAll() {
3410
+ return Array.from(this.manifests.values());
3411
+ }
3412
+ /**
3413
+ * Check if a module manifest is registered.
3414
+ */
3415
+ has(moduleName) {
3416
+ return this.manifests.has(moduleName);
3417
+ }
3418
+ /**
3419
+ * Load feature definitions from all registered feature definition providers.
3420
+ * Features are automatically associated with modules based on the module name in the feature key.
3421
+ */
3422
+ async loadFeatureDefinitions() {
3423
+ try {
3424
+ const featureProviders = this.injector.get(AXP_FEATURE_DEFINITION_PROVIDER, []);
3425
+ if (!featureProviders || featureProviders.length === 0) {
3426
+ return;
3427
+ }
3428
+ const context = new AXPFeatureDefinitionProviderContext();
3429
+ // Call all providers to collect features
3430
+ for (const provider of featureProviders) {
3431
+ if (provider instanceof Promise) {
3432
+ // If provider is a promise, resolve it
3433
+ const resolvedProvider = await provider;
3434
+ await resolvedProvider.provide(context);
3435
+ }
3436
+ else {
3437
+ // If provider is a direct instance, use it directly
3438
+ await provider.provide(context);
3439
+ }
3440
+ }
3441
+ // Register all features and associate them with their modules
3442
+ const features = context.getFeatures();
3443
+ for (const [key, definition] of features.entries()) {
3444
+ // Extract module name from key (format: ModuleName:FeatureKey)
3445
+ const [moduleName, ...keyParts] = key.split(':');
3446
+ const shortKey = keyParts.join(':');
3447
+ // Verify that the module exists
3448
+ if (!this.has(moduleName)) {
3449
+ console.warn(`Feature '${key}' references unknown module '${moduleName}'. Skipping.`);
3450
+ continue;
3451
+ }
3452
+ // Register feature definition
3453
+ this.registerFeatureDefinition({
3454
+ ...definition,
3455
+ name: key,
3456
+ module: moduleName,
3457
+ key: shortKey,
3458
+ });
3459
+ }
3460
+ }
3461
+ catch (error) {
3462
+ console.error('Failed to load feature definitions:', error);
3463
+ }
3464
+ }
3465
+ //#endregion
3466
+ //#region ---- Feature Definition Management ----
3467
+ /**
3468
+ * Get all feature definitions from all manifests.
3469
+ */
3470
+ getAllFeatureDefinitions() {
3471
+ return Array.from(this.featureDefinitions.values());
3472
+ }
3473
+ /**
3474
+ * Get feature definition by full name.
3475
+ */
3476
+ getFeatureDefinition(featureName) {
3477
+ return this.featureDefinitions.get(featureName);
3478
+ }
3479
+ /**
3480
+ * Check if a feature is defined in any manifest.
3481
+ */
3482
+ isFeatureDefined(featureName) {
3483
+ return this.featureDefinitions.has(featureName);
3484
+ }
3485
+ /**
3486
+ * Get default value for a feature.
3487
+ * Returns undefined if feature is not defined.
3488
+ */
3489
+ getFeatureDefault(featureName) {
3490
+ const definition = this.getFeatureDefinition(featureName);
3491
+ return definition?.defaultValue;
3492
+ }
3493
+ /**
3494
+ * Get all feature definitions for a specific module.
3495
+ */
3496
+ getModuleFeatures(moduleName) {
3497
+ return Array.from(this.featureDefinitions.values()).filter((def) => def.module === moduleName);
3498
+ }
3499
+ /**
3500
+ * Register a feature definition dynamically (e.g., from feature definition provider).
3501
+ * Used to add features that are defined at runtime via AXP_FEATURE_DEFINITION_PROVIDER.
3502
+ */
3503
+ registerFeatureDefinition(definition) {
3504
+ this.featureDefinitions.set(definition.name, definition);
3505
+ }
3506
+ //#endregion
3507
+ //#region ---- Dependency Checking ----
3508
+ /**
3509
+ * Check if module dependencies are satisfied.
3510
+ * Returns array of missing dependencies.
3511
+ * Dependencies can be module names or feature keys (ModuleName.FeatureKey format).
3512
+ */
3513
+ checkDependencies(moduleName) {
3514
+ const manifest = this.get(moduleName);
3515
+ if (!manifest?.dependencies || manifest.dependencies.length === 0) {
3516
+ return { missingModules: [], missingFeatures: [] };
3517
+ }
3518
+ const missingModules = [];
3519
+ const missingFeatures = [];
3520
+ for (const dependency of manifest.dependencies) {
3521
+ // If dependency contains a dot, it's a feature (ModuleName.FeatureKey)
3522
+ if (dependency.includes('.')) {
3523
+ // Convert dependency format (ModuleName.FeatureKey) to feature name format (ModuleName:Feature:FeatureKey)
3524
+ const [moduleName, featureKey] = dependency.split('.');
3525
+ const featureName = `${moduleName}:Feature:${featureKey}`;
3526
+ if (!this.isFeatureDefined(featureName)) {
3527
+ missingFeatures.push(dependency);
3528
+ }
3529
+ }
3530
+ else {
3531
+ // Otherwise, it's a module name
3532
+ if (!this.has(dependency)) {
3533
+ missingModules.push(dependency);
3534
+ }
3535
+ }
3536
+ }
3537
+ return { missingModules, missingFeatures };
3538
+ }
3539
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPModuleManifestRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3540
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPModuleManifestRegistry, providedIn: 'root' }); }
3541
+ }
3542
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPModuleManifestRegistry, decorators: [{
3543
+ type: Injectable,
3544
+ args: [{ providedIn: 'root' }]
3545
+ }] });
3546
+
3547
+ //#region ---- Imports ----
3548
+ //#endregion
3549
+ //#region ---- Module manifests data source ----
3550
+ /**
3551
+ * Data source definition for module names/titles from {@link AXPModuleManifestRegistry}.
3552
+ */
3553
+ class AXPModuleManifestsDataSourceDefinition {
3554
+ constructor() {
3555
+ //#region ---- Services & Dependencies ----
3556
+ this.manifestRegistry = inject(AXPModuleManifestRegistry);
3557
+ //#endregion
3558
+ }
3559
+ //#endregion
3560
+ //#region ---- Public API ----
3561
+ async items() {
3562
+ return [
3563
+ {
3564
+ name: AXPPlatformCoreDataSourceKeys.ModuleManifests,
3565
+ title: 'Module manifests',
3566
+ source: () => new AXDataSource({
3567
+ key: 'id',
3568
+ load: async () => {
3569
+ await this.manifestRegistry.initialize();
3570
+ const list = this.manifestRegistry.getAll().map((m) => ({
3571
+ id: m.name,
3572
+ title: m.title || m.name,
3573
+ }));
3574
+ return { items: list, total: list.length };
3575
+ },
3576
+ byKey: async (key) => {
3577
+ await this.manifestRegistry.initialize();
3578
+ const m = this.manifestRegistry.getAll().find((x) => x.name === key);
3579
+ return m ? { id: m.name, title: m.title || m.name } : undefined;
3580
+ },
3581
+ pageSize: 1000,
3582
+ }),
3583
+ columns: [
3584
+ {
3585
+ name: 'id',
3586
+ title: 'ID',
3587
+ datatype: 'string',
3588
+ type: 'text-editor',
3589
+ },
3590
+ {
3591
+ name: 'title',
3592
+ title: 'Title',
3593
+ datatype: 'string',
3594
+ type: 'text-editor',
3595
+ },
3596
+ ],
3597
+ filters: [
3598
+ {
3599
+ field: 'title',
3600
+ title: 'Title',
3601
+ operator: { type: 'equal' },
3602
+ widget: { type: 'text-editor' },
3603
+ filterType: { advance: true, inline: true },
3604
+ },
3605
+ ],
3606
+ textField: { name: 'title', title: 'Title' },
3607
+ valueField: { name: 'id', title: 'ID' },
3608
+ },
3609
+ ];
3610
+ }
3611
+ }
3612
+ //#endregion
3613
+
3614
+ class AXPAppStartUpService {
3615
+ constructor() {
3616
+ this.translationService = inject(AXTranslationService);
3617
+ this.tasks = [];
3618
+ }
3619
+ //#endregion
3620
+ //#region ---- Public Methods ----
3621
+ registerTask(task) {
3622
+ this.tasks.push(task);
3623
+ }
3624
+ async runAllTasks() {
3625
+ for (const task of this.tasks.sort((a, b) => a.priority - b.priority)) {
3626
+ await this.updateStatus(task.statusText);
3627
+ await task.run();
3628
+ }
3629
+ }
3630
+ async updateStatus(status) {
3631
+ const loadingText = document.querySelector('#loadingText');
3632
+ if (loadingText) {
3633
+ const translatedStatus = this.translationService.resolve(status);
3634
+ loadingText.innerHTML = await this.translationService.translateAsync(translatedStatus);
3635
+ }
3636
+ }
3637
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPAppStartUpService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3638
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPAppStartUpService, providedIn: 'root' }); }
3639
+ }
3640
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPAppStartUpService, decorators: [{
3641
+ type: Injectable,
3642
+ args: [{
3643
+ providedIn: 'root',
3644
+ }]
3645
+ }] });
3646
+ function initAppFactory(appInitService) {
3647
+ return () => appInitService.runAllTasks();
3648
+ }
3649
+ const AXPAppStartUpProvider = provideAppInitializer(() => {
3650
+ const initializerFn = initAppFactory(inject(AXPAppStartUpService));
3651
+ return initializerFn();
3652
+ });
3653
+
3654
+ //#region ---- Imports ----
3655
+ //#endregion
3656
+ //#region ---- Module ----
3657
+ /**
3658
+ * Module for managing module manifests initialization.
3659
+ * Handles the registration of manifest startup tasks.
3660
+ */
3661
+ class AXPModuleManifestModule {
3662
+ /**
3663
+ * @ignore
3664
+ * Initializes module manifest tasks on module construction.
3665
+ */
3666
+ constructor(appInitService, injector) {
3667
+ const manifestRegistry = injector.get(AXPModuleManifestRegistry);
3668
+ // Register manifest initialization task
3669
+ appInitService.registerTask({
3670
+ name: 'ModuleManifests',
3671
+ statusText: '@general:app-startup.loading-module-manifests',
3672
+ priority: 1, // Load early, before features/permissions
3673
+ run: async () => {
3674
+ await manifestRegistry.initialize();
3675
+ },
3676
+ });
3677
+ }
3678
+ 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 }); }
3679
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: AXPModuleManifestModule }); }
3680
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPModuleManifestModule }); }
3681
+ }
3682
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPModuleManifestModule, decorators: [{
3683
+ type: NgModule,
3684
+ args: [{
3685
+ providers: [],
3686
+ }]
3687
+ }], ctorParameters: () => [{ type: AXPAppStartUpService }, { type: i0.Injector }] });
3688
+
3689
+ //#region ---- Imports ----
3690
+ //#endregion
3691
+ //#region ---- Constants ----
3692
+ const DEFAULT_IDLE_TIMEOUT_MS = 2_000;
3693
+ const DEFAULT_MIN_IDLE_MS = 16;
3694
+ //#endregion
3695
+ //#region ---- Service ----
3696
+ /**
3697
+ * Schedules low-priority work during browser idle periods.
3698
+ * Pauses when the tab is hidden or when explicitly paused (e.g. user-initiated seeding).
3699
+ */
3700
+ class AXPIdleSchedulerService {
3701
+ constructor() {
3702
+ //#region ---- State ----
3703
+ this.pausedState = signal(false, ...(ngDevMode ? [{ debugName: "pausedState" }] : /* istanbul ignore next */ []));
3704
+ this.paused = this.pausedState.asReadonly();
3705
+ this.queue = [];
3706
+ this.running = false;
3707
+ }
3708
+ //#endregion
3709
+ //#region ---- Public API ----
3710
+ pause() {
3711
+ this.pausedState.set(true);
3712
+ }
3713
+ resume() {
3714
+ this.pausedState.set(false);
3715
+ void this.drainQueue();
3716
+ }
3717
+ /**
3718
+ * Resolves after the browser reports idle time, or after timeoutMs (whichever comes first).
3719
+ */
3720
+ waitForIdle(options) {
3721
+ const timeoutMs = options?.timeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS;
3722
+ const minIdleMs = options?.minIdleMs ?? DEFAULT_MIN_IDLE_MS;
3723
+ if (this.pausedState() || typeof document !== 'undefined' && document.visibilityState === 'hidden') {
3724
+ return delay(timeoutMs);
3725
+ }
3726
+ return new Promise((resolve) => {
3727
+ let settled = false;
3728
+ const finish = () => {
3729
+ if (settled)
3730
+ return;
3731
+ settled = true;
3732
+ resolve();
3733
+ };
3734
+ const timeoutId = setTimeout(finish, timeoutMs);
3735
+ if (typeof requestIdleCallback === 'function') {
3736
+ requestIdleCallback(() => {
3737
+ clearTimeout(timeoutId);
3738
+ finish();
3739
+ }, { timeout: timeoutMs });
3740
+ }
3741
+ else {
3742
+ clearTimeout(timeoutId);
3743
+ setTimeout(finish, minIdleMs);
3744
+ }
3745
+ });
3746
+ }
3747
+ /** Enqueues async work processed sequentially during idle slices. */
3748
+ enqueue(task, options) {
3749
+ if (options?.key) {
3750
+ this.queue = this.queue.filter((item) => item.key !== options.key);
3751
+ }
3752
+ this.queue.push({ key: options?.key, task });
3753
+ void this.drainQueue();
3754
+ }
3755
+ //#endregion
3756
+ //#region ---- Private ----
3757
+ async drainQueue() {
3758
+ if (this.running || this.pausedState()) {
3759
+ return;
3760
+ }
3761
+ this.running = true;
3762
+ try {
3763
+ while (this.queue.length > 0 && !this.pausedState()) {
3764
+ const next = this.queue.shift();
3765
+ if (!next)
3766
+ break;
3767
+ await this.waitForIdle();
3768
+ try {
3769
+ await next.task();
3770
+ }
3771
+ catch (err) {
3772
+ const message = err instanceof Error ? err.message : String(err);
3773
+ console.error('[AXPIdleScheduler] Task failed:', message);
3774
+ }
3775
+ }
3776
+ }
3777
+ finally {
3778
+ this.running = false;
3779
+ if (this.queue.length > 0 && !this.pausedState()) {
3780
+ void this.drainQueue();
3781
+ }
3782
+ }
3783
+ }
3784
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPIdleSchedulerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3785
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPIdleSchedulerService, providedIn: 'root' }); }
3786
+ }
3787
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPIdleSchedulerService, decorators: [{
3788
+ type: Injectable,
3789
+ args: [{ providedIn: 'root' }]
3790
+ }] });
3791
+
3792
+ //#region ---- Idle scheduler types ----
3793
+ //#endregion
3794
+
3795
+ //#region ---- Tag Types ----
3796
+ //#endregion
3797
+
3798
+ //#region ---- Tag Provider ----
3799
+ /**
3800
+ * Abstract class for tag providers
3801
+ * Implement this to provide tags from different sources (system, tenant, user, etc.)
3802
+ */
3803
+ class AXPTagProvider {
3804
+ /**
3805
+ * Create a new tag (optional - not all providers support creation)
3806
+ * @param tag Tag to create
3807
+ * @returns Created tag with id
3808
+ */
3809
+ async create(tag) {
3810
+ throw new Error(`Provider ${this.name} does not support tag creation`);
3811
+ }
3812
+ /**
3813
+ * Update an existing tag (optional)
3814
+ * @param tag Tag to update
3815
+ * @returns Updated tag
3816
+ */
3817
+ async update(tag) {
3818
+ throw new Error(`Provider ${this.name} does not support tag updates`);
3819
+ }
3820
+ /**
3821
+ * Delete a tag (optional)
3822
+ * @param id Tag id to delete
3823
+ */
3824
+ async delete(id) {
3825
+ throw new Error(`Provider ${this.name} does not support tag deletion`);
3826
+ }
3827
+ }
3828
+ /**
3829
+ * Injection token for tag providers
3830
+ * Use this to register multiple tag providers
3831
+ */
3832
+ const AXP_TAG_PROVIDER = new InjectionToken('AXP_TAG_PROVIDER');
3833
+ //#endregion
3834
+
3835
+ //#region ---- Tag Service ----
3836
+ /**
3837
+ * Service for managing tags from multiple providers
3838
+ * Aggregates tags from all registered providers
3839
+ */
3840
+ class AXPTagService {
3841
+ constructor() {
3842
+ //#region ---- Dependencies ----
3843
+ this.providers = inject(AXP_TAG_PROVIDER, { optional: true }) ?? [];
3844
+ //#endregion
3845
+ //#region ---- State ----
3846
+ this.cache = null;
3847
+ }
3848
+ //#endregion
3849
+ //#region ---- Public API ----
3850
+ /**
3851
+ * Get all tags from all providers
3852
+ * @param options Filter options
3853
+ * @returns Promise of tags
3854
+ */
3855
+ async getTags(options) {
3856
+ if (!this.cache) {
3857
+ await this.loadTags();
3858
+ }
3859
+ let tags = this.cache;
3860
+ // Apply scope filter
3861
+ if (options?.scope && options.scope.length > 0) {
3862
+ tags = tags.filter((tag) => tag.scope && options.scope.includes(tag.scope));
3863
+ }
3864
+ // Apply search filter
3865
+ if (options?.search) {
3866
+ const searchLower = options.search.toLowerCase();
3867
+ tags = tags.filter((tag) => tag.title.toLowerCase().includes(searchLower) ||
3868
+ tag.description?.toLowerCase().includes(searchLower));
3869
+ }
3870
+ return tags;
3871
+ }
3872
+ /**
3873
+ * Create a new tag using the appropriate provider
3874
+ * @param tag Tag to create
3875
+ * @param providerName Provider to use (defaults to first writable provider)
3876
+ * @returns Created tag
3877
+ */
3878
+ async createTag(tag, providerName) {
3879
+ const provider = providerName
3880
+ ? this.providers.find((p) => p.name === providerName)
3881
+ : this.providers.find((p) => p.create);
3882
+ if (!provider || !provider.create) {
3883
+ throw new Error('No writable tag provider available');
3884
+ }
3885
+ const createdTag = await provider.create(tag);
3886
+ // Invalidate cache
3887
+ this.cache = null;
3888
+ return createdTag;
3889
+ }
3890
+ /**
3891
+ * Update an existing tag
3892
+ * @param tag Tag to update
3893
+ * @returns Updated tag
3894
+ */
3895
+ async updateTag(tag) {
3896
+ const provider = this.providers.find((p) => p.name === tag.scope && p.update);
3897
+ if (!provider || !provider.update) {
3898
+ throw new Error(`No provider found for updating tag with scope: ${tag.scope}`);
3899
+ }
3900
+ const updatedTag = await provider.update(tag);
3901
+ // Invalidate cache
3902
+ this.cache = null;
3903
+ return updatedTag;
3904
+ }
3905
+ /**
3906
+ * Delete a tag
3907
+ * @param tagId Tag id to delete
3908
+ * @param scope Tag scope
3909
+ */
3910
+ async deleteTag(tagId, scope) {
3911
+ const provider = this.providers.find((p) => p.name === scope && p.delete);
3912
+ if (!provider || !provider.delete) {
3913
+ throw new Error(`No provider found for deleting tag with scope: ${scope}`);
3914
+ }
3915
+ await provider.delete(tagId);
3916
+ // Invalidate cache
3917
+ this.cache = null;
3918
+ }
3919
+ /**
3920
+ * Reload tags from all providers
3921
+ */
3922
+ async reload() {
3923
+ this.cache = null;
3924
+ await this.loadTags();
3925
+ }
3926
+ //#endregion
3927
+ //#region ---- Private Methods ----
3928
+ /**
3929
+ * Load tags from all providers
3930
+ */
3931
+ async loadTags() {
3932
+ const allTags = [];
3933
+ for (const provider of this.providers) {
3934
+ try {
3935
+ const tags = await provider.provide();
3936
+ allTags.push(...tags);
3937
+ }
3938
+ catch (error) {
3939
+ console.error(`Error loading tags from provider ${provider.name}:`, error);
3940
+ }
3941
+ }
3942
+ // Remove duplicates based on title (case-insensitive)
3943
+ const uniqueTags = allTags.filter((tag, index, self) => index === self.findIndex((t) => t.title.toLowerCase() === tag.title.toLowerCase()));
3944
+ this.cache = uniqueTags;
3945
+ }
3946
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTagService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3947
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTagService, providedIn: 'root' }); }
3948
+ }
3949
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTagService, decorators: [{
3950
+ type: Injectable,
3951
+ args: [{
3952
+ providedIn: 'root',
3953
+ }]
3954
+ }] });
3955
+
3956
+ function applySystemActionDefault(action, type) {
3957
+ const systemAction = getSystemActions(type);
3958
+ return {
3959
+ ...action,
3960
+ name: action.name ?? systemAction.key,
3961
+ title: action.title ?? systemAction.title,
3962
+ icon: action.icon ?? systemAction.icon,
3963
+ color: action.color ?? systemAction.color,
3964
+ };
3965
+ }
3966
+
3967
+ class AXPImageUrlLogoConfig {
3968
+ constructor(url, width, height) {
3969
+ this.url = url;
3970
+ this.width = width;
3971
+ this.height = height;
3972
+ }
3973
+ }
3974
+ class AXPComponentLogoConfig {
3975
+ constructor(component) {
3976
+ this.component = component;
3977
+ }
3978
+ }
3979
+ class AXPIconLogoConfig {
3980
+ constructor(icon, color) {
3981
+ this.icon = icon;
3982
+ this.color = color;
3983
+ }
3984
+ }
3985
+
3986
+ class AXPCountdownPipe {
3987
+ constructor() {
3988
+ this.calendarService = inject(AXCalendarService);
3989
+ this.countdownSignal = signal(this.setupTimer(), ...(ngDevMode ? [{ debugName: "countdownSignal" }] : /* istanbul ignore next */ []));
3990
+ this.targetDate = 0;
3991
+ this.prevValue = 0;
3992
+ }
3993
+ transform(value) {
3994
+ if (this.prevValue != value) {
3995
+ this.prevValue = value;
3996
+ const expireTime = this.calendarService.calendar.add(new Date(), 'second', value).date;
3997
+ this.updateTargetDate(expireTime.toISOString());
3998
+ }
3999
+ return this.countdownSignal();
4000
+ }
4001
+ setupTimer() {
4002
+ return interval(1000).pipe(startWith(0), map(() => {
4003
+ const diff = this.targetDate - new Date().getTime();
4004
+ if (diff < 0) {
4005
+ return "Time's up!";
4006
+ }
4007
+ const times = [86400, 3600, 60, 1].map((seconds) => Math.floor((diff / 1000 / seconds) % (seconds === 1 ? 60 : 24)));
4008
+ const labels = ['d', 'h', 'm', 's'];
4009
+ return times
4010
+ .map((t, i) => (t > 0 || i === 3 ? `${t}${labels[i]}` : ''))
4011
+ .join(' ')
4012
+ .trim();
4013
+ }));
4014
+ }
4015
+ updateTargetDate(value) {
4016
+ this.targetDate = new Date(value).getTime();
4017
+ }
4018
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPCountdownPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
4019
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: AXPCountdownPipe, isStandalone: true, name: "countdown", pure: false }); }
4020
+ }
4021
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPCountdownPipe, decorators: [{
4022
+ type: Pipe,
4023
+ args: [{
4024
+ name: 'countdown',
4025
+ pure: false,
4026
+ standalone: true,
4027
+ }]
4028
+ }] });
4029
+
4030
+ const loggingEnabled = false; // Set to true to enable logging, false to disable
4031
+ //#region ---- Multilingual string helpers ----
4032
+ /**
4033
+ * Lowercased text for client-side filtering: plain string or all locale values joined.
4034
+ */
4035
+ function toFilterSearchText(value) {
4036
+ if (typeof value === 'string') {
4037
+ return value.toLowerCase();
4038
+ }
4039
+ if (isLocaleStringMap(value)) {
4040
+ return Object.values(value).join(' ').toLowerCase();
4041
+ }
4042
+ return undefined;
4043
+ }
4044
+ function toSortComparableValue(value, locale) {
4045
+ if (value == null) {
4046
+ return '';
4047
+ }
4048
+ if (typeof value === 'number' || typeof value === 'boolean') {
4049
+ return value;
4050
+ }
4051
+ if (typeof value === 'string') {
4052
+ return value.toLocaleLowerCase(locale);
4053
+ }
4054
+ if (value instanceof Date) {
4055
+ return value.getTime();
4056
+ }
4057
+ if (isLocaleStringMap(value)) {
4058
+ return resolveMultiLanguageString(value, locale).toLocaleLowerCase(locale);
4059
+ }
4060
+ return String(value).toLocaleLowerCase(locale);
4061
+ }
4062
+ function filterValuesAreEqual(itemRaw, condRaw) {
4063
+ if (typeof condRaw === 'string' && isLocaleStringMap(itemRaw)) {
4064
+ return Object.values(itemRaw).some((v) => v.toLowerCase() === condRaw.toLowerCase());
4065
+ }
4066
+ if (typeof itemRaw === 'string' && isLocaleStringMap(condRaw)) {
4067
+ return Object.values(condRaw).some((v) => v.toLowerCase() === itemRaw.toLowerCase());
4068
+ }
4069
+ const itemScalar = typeof itemRaw === 'string' ? itemRaw.toLowerCase() : itemRaw;
4070
+ const condScalar = typeof condRaw === 'string' ? condRaw.toLowerCase() : condRaw;
4071
+ return isEqual(itemScalar, condScalar);
4072
+ }
4073
+ //#endregion
4074
+ function applyCondition(item, condition) {
4075
+ const rawValue = condition.field ? get(item, condition.field) : null;
4076
+ const itemValue = typeof rawValue === 'string' ? rawValue.toLowerCase() : rawValue;
4077
+ const conditionValue = typeof condition.value === 'string' ? condition.value.toLowerCase() : condition.value;
4078
+ // Conditional Logging for debugging
4079
+ if (loggingEnabled) {
4080
+ console.log('Condition:', condition);
4081
+ console.log('Item Value:', itemValue);
4082
+ console.log('Condition Value:', conditionValue);
4083
+ }
4084
+ let result;
4085
+ const valueToCompare = isNil(condition.field) || condition.field === '' ? conditionValue : itemValue;
4086
+ const conditionType = condition?.operator?.type;
4087
+ if (!conditionType) {
4088
+ return true;
4089
+ }
4090
+ const op = String(conditionType);
4091
+ switch (op) {
4092
+ case 'equal':
4093
+ case 'eq':
4094
+ if (!isNil(condition.field) && condition.field !== '') {
4095
+ result = filterValuesAreEqual(rawValue, condition.value);
4096
+ }
4097
+ else {
4098
+ result = isEqual(valueToCompare, conditionValue);
4099
+ }
4100
+ if (loggingEnabled) {
4101
+ console.log('Equal check result:', result);
4102
+ }
4103
+ break;
4104
+ case 'notEqual':
4105
+ case 'ne':
4106
+ case 'neq':
4107
+ if (!isNil(condition.field) && condition.field !== '') {
4108
+ result = !filterValuesAreEqual(rawValue, condition.value);
4109
+ }
4110
+ else {
4111
+ result = !isEqual(valueToCompare, conditionValue);
4112
+ }
4113
+ if (loggingEnabled) {
4114
+ console.log('Not equal check result:', result);
4115
+ }
4116
+ break;
4117
+ case 'greaterThan':
4118
+ case 'gt':
4119
+ result = gt(valueToCompare, conditionValue);
4120
+ if (loggingEnabled) {
4121
+ console.log('Greater than check result:', result);
4122
+ }
4123
+ break;
4124
+ case 'lessThan':
4125
+ case 'lt':
4126
+ result = lt(valueToCompare, conditionValue);
4127
+ if (loggingEnabled) {
4128
+ console.log('Less than check result:', result);
4129
+ }
4130
+ break;
4131
+ case 'greaterThanOrEqual':
4132
+ case 'gte':
4133
+ result = gte(valueToCompare, conditionValue);
4134
+ if (loggingEnabled) {
4135
+ console.log('Greater than or equal check result:', result);
4136
+ }
4137
+ break;
4138
+ case 'lessThanOrEqual':
4139
+ case 'lte':
4140
+ result = lte(valueToCompare, conditionValue);
4141
+ if (loggingEnabled) {
4142
+ console.log('Less than or equal check result:', result);
4143
+ }
4144
+ break;
4145
+ case 'contains': {
4146
+ const needle = toFilterSearchText(condition.value) ??
4147
+ (condition.value == null ? '' : String(condition.value).toLowerCase());
4148
+ const haystack = toFilterSearchText(rawValue);
4149
+ if (haystack !== undefined) {
4150
+ result = includes(haystack, needle);
4151
+ }
4152
+ else if (Array.isArray(rawValue)) {
4153
+ result = includes(rawValue.map((val) => toFilterSearchText(val) ?? val.toString().toLowerCase()), needle);
4154
+ }
4155
+ else {
4156
+ result = false;
4157
+ }
4158
+ if (loggingEnabled) {
4159
+ console.log('Contains check result:', result);
4160
+ }
4161
+ break;
4162
+ }
4163
+ case 'in':
4164
+ if (Array.isArray(conditionValue)) {
4165
+ if (typeof valueToCompare === 'string') {
4166
+ result = conditionValue.some((val) => typeof val === 'string' ? val.toLowerCase() === valueToCompare : val === valueToCompare);
4167
+ }
4168
+ else if (isLocaleStringMap(rawValue)) {
4169
+ const localeValues = Object.values(rawValue).map((v) => v.toLowerCase());
4170
+ result = localeValues.some((localeVal) => conditionValue.some((val) => typeof val === 'string' ? val.toLowerCase() === localeVal : val === localeVal));
4171
+ }
4172
+ else if (Array.isArray(valueToCompare)) {
4173
+ result = valueToCompare.some((val) => conditionValue.some((condVal) => typeof val === 'string' && typeof condVal === 'string'
4174
+ ? val.toLowerCase() === condVal.toLowerCase()
4175
+ : val === condVal));
4176
+ }
4177
+ else {
4178
+ result = conditionValue.includes(valueToCompare);
4179
+ }
4180
+ }
4181
+ else {
4182
+ result = false;
4183
+ }
4184
+ if (loggingEnabled) {
4185
+ console.log('In check result:', result);
4186
+ }
4187
+ break;
4188
+ case 'notContains': {
4189
+ const needleNc = toFilterSearchText(condition.value) ??
4190
+ (condition.value == null ? '' : String(condition.value).toLowerCase());
4191
+ const haystackNc = toFilterSearchText(rawValue);
4192
+ if (haystackNc !== undefined) {
4193
+ result = !includes(haystackNc, needleNc);
4194
+ }
4195
+ else if (Array.isArray(rawValue)) {
4196
+ result = !includes(rawValue.map((val) => toFilterSearchText(val) ?? val.toString().toLowerCase()), needleNc);
4197
+ }
4198
+ else {
4199
+ result = false;
4200
+ }
4201
+ if (loggingEnabled) {
4202
+ console.log('Not contains check result:', result);
4203
+ }
4204
+ break;
4205
+ }
4206
+ case 'startsWith': {
4207
+ const prefix = toFilterSearchText(condition.value) ??
4208
+ (condition.value == null ? '' : String(condition.value).toLowerCase());
4209
+ const haystackSw = toFilterSearchText(rawValue);
4210
+ result = haystackSw !== undefined && startsWith(haystackSw, prefix);
4211
+ if (loggingEnabled) {
4212
+ console.log('Starts with check result:', result);
4213
+ }
4214
+ break;
4215
+ }
4216
+ case 'endsWith': {
4217
+ const suffix = toFilterSearchText(condition.value) ??
4218
+ (condition.value == null ? '' : String(condition.value).toLowerCase());
4219
+ const haystackEw = toFilterSearchText(rawValue);
4220
+ result = haystackEw !== undefined && endsWith(haystackEw, suffix);
4221
+ if (loggingEnabled) {
4222
+ console.log('Ends with check result:', result);
4223
+ }
4224
+ break;
4225
+ }
4226
+ case 'isEmpty':
4227
+ result = isEmpty(valueToCompare);
4228
+ if (loggingEnabled) {
4229
+ console.log('Is empty check result:', result);
4230
+ }
4231
+ break;
4232
+ case 'isNull':
4233
+ result = isNil(valueToCompare);
4234
+ if (loggingEnabled) {
4235
+ console.log('Is null check result:', result);
4236
+ }
4237
+ break;
4238
+ case 'isNotEmpty':
4239
+ result = !isEmpty(valueToCompare);
4240
+ if (loggingEnabled) {
4241
+ console.log('Is not empty check result:', result);
4242
+ }
4243
+ break;
4244
+ case 'between':
4245
+ result = !isNil(valueToCompare) && valueToCompare >= condition.value.from && valueToCompare <= condition.value.to;
4246
+ if (loggingEnabled) {
4247
+ console.log('Between check result:', result);
4248
+ }
4249
+ break;
4250
+ default:
4251
+ result = true;
4252
+ if (loggingEnabled) {
4253
+ console.log('Default case, returning true');
4254
+ }
4255
+ }
4256
+ // Apply negative flag if present on operator (invert the result)
4257
+ const isNegative = !!condition?.operator?.negative;
4258
+ return isNegative ? !result : result;
4259
+ }
4260
+ function applyFilterArray(dataArray, filters, logic = 'and') {
4261
+ if (filters && filters.length) {
4262
+ return dataArray.filter((item) => {
4263
+ if (logic === 'and') {
4264
+ return filters.every((f) => {
4265
+ return f.filters ? applyFilterArray([item], f.filters, f.logic).length > 0 : applyCondition(item, f);
4266
+ });
4267
+ }
4268
+ else {
4269
+ // logic === 'or'
4270
+ return filters.some((f) => {
4271
+ return f.filters ? applyFilterArray([item], f.filters, f.logic).length > 0 : applyCondition(item, f);
4272
+ });
4273
+ }
4274
+ });
4275
+ }
4276
+ else {
4277
+ return dataArray;
4278
+ }
4279
+ }
4280
+ function applySortArray(dataArray, sorts, locale = 'en-US') {
4281
+ if (sorts && sorts.length > 0) {
4282
+ const iteratees = sorts.map((s) => (item) => toSortComparableValue(get(item, s.field), locale));
4283
+ const sortOrders = sorts.map((s) => (s.dir === 'desc' ? 'desc' : 'asc'));
4284
+ return orderBy(dataArray, iteratees, sortOrders);
4285
+ }
4286
+ else
4287
+ return dataArray;
4288
+ }
4289
+ function applyPagination(dataArray, skip, take) {
4290
+ return dataArray.slice(skip, skip + take);
4291
+ }
4292
+ async function applyQueryArray(dataArray, query) {
4293
+ let result = [...dataArray];
4294
+ // Apply filtering
4295
+ if (query.filter) {
4296
+ result = applyFilterArray(result, [query.filter], query.filter.logic ?? 'and');
4297
+ }
4298
+ // Store total count before pagination
4299
+ const total = result.length;
4300
+ // Apply sorting
4301
+ if (query.sort && query.sort.length > 0) {
4302
+ result = applySortArray(result, query.sort);
4303
+ }
4304
+ // Apply pagination
4305
+ result = applyPagination(result, query.skip, query.take);
4306
+ return {
4307
+ items: result,
4308
+ total,
4309
+ };
4310
+ }
4311
+
4312
+ //#region ---- Type Definitions ----
4313
+ /**
4314
+ * Screen Size Enum
4315
+ */
4316
+ var AXPScreenSize;
4317
+ (function (AXPScreenSize) {
4318
+ AXPScreenSize["Small"] = "small";
4319
+ AXPScreenSize["Medium"] = "medium";
4320
+ AXPScreenSize["Large"] = "large";
4321
+ })(AXPScreenSize || (AXPScreenSize = {}));
4322
+ /**
4323
+ * Device Type Enum
4324
+ */
4325
+ var AXPDeviceType;
4326
+ (function (AXPDeviceType) {
4327
+ AXPDeviceType["Mobile"] = "mobile";
4328
+ AXPDeviceType["Tablet"] = "tablet";
4329
+ AXPDeviceType["Desktop"] = "desktop";
4330
+ })(AXPDeviceType || (AXPDeviceType = {}));
4331
+ //#endregion
4332
+ //#region ---- Helper Functions ----
4333
+ /**
4334
+ * Get the initial screen size based on window width
4335
+ */
4336
+ const getScreenSize = () => {
4337
+ const width = window.innerWidth;
4338
+ if (width <= 600)
4339
+ return AXPScreenSize.Small;
4340
+ if (width <= 1024)
4341
+ return AXPScreenSize.Medium;
4342
+ return AXPScreenSize.Large;
4343
+ };
4344
+ /**
4345
+ * Determine device type based on viewport width
4346
+ */
4347
+ const getDeviceType = () => {
4348
+ const width = window.innerWidth;
4349
+ if (width <= 600) {
4350
+ return AXPDeviceType.Mobile;
4351
+ }
4352
+ else if (width <= 1024) {
4353
+ return AXPDeviceType.Tablet;
4354
+ }
4355
+ return AXPDeviceType.Desktop;
4356
+ };
4357
+ /**
4358
+ * Determine if the device supports touch
4359
+ */
4360
+ const isTouchDevice = () => {
4361
+ return 'ontouchstart' in window || navigator.maxTouchPoints > 0;
4362
+ };
4363
+ //#endregion
4364
+ //#region ---- Device Service ----
4365
+ /**
4366
+ * Injectable service that provides reactive device and screen size information.
4367
+ * Monitors window resize events and updates signals accordingly.
4368
+ */
4369
+ class AXPDeviceService {
4370
+ //#endregion
4371
+ //#region ---- Constructor & Lifecycle ----
4372
+ constructor() {
4373
+ //#region ---- State Signals ----
4374
+ /**
4375
+ * Current screen size (Small, Medium, Large)
4376
+ */
4377
+ this.screenSize = signal(getScreenSize(), ...(ngDevMode ? [{ debugName: "screenSize" }] : /* istanbul ignore next */ []));
4378
+ /**
4379
+ * Current device type (Mobile, Tablet, Desktop)
4380
+ */
4381
+ this.deviceType = signal(getDeviceType(), ...(ngDevMode ? [{ debugName: "deviceType" }] : /* istanbul ignore next */ []));
4382
+ /**
4383
+ * Whether the device supports touch input
4384
+ */
4385
+ this.isTouchDevice = signal(isTouchDevice(), ...(ngDevMode ? [{ debugName: "isTouchDevice" }] : /* istanbul ignore next */ []));
4386
+ //#endregion
4387
+ //#region ---- Computed Signals ----
4388
+ /**
4389
+ * Whether the screen size is Small
4390
+ */
4391
+ this.isSmall = computed(() => this.screenSize() === AXPScreenSize.Small, ...(ngDevMode ? [{ debugName: "isSmall" }] : /* istanbul ignore next */ []));
4392
+ /**
4393
+ * Whether the screen size is Medium
4394
+ */
4395
+ this.isMedium = computed(() => this.screenSize() === AXPScreenSize.Medium, ...(ngDevMode ? [{ debugName: "isMedium" }] : /* istanbul ignore next */ []));
4396
+ /**
4397
+ * Whether the screen size is Large
4398
+ */
4399
+ this.isLarge = computed(() => this.screenSize() === AXPScreenSize.Large, ...(ngDevMode ? [{ debugName: "isLarge" }] : /* istanbul ignore next */ []));
4400
+ /**
4401
+ * Whether the device is Mobile
4402
+ */
4403
+ this.isMobileDevice = computed(() => this.deviceType() === AXPDeviceType.Mobile, ...(ngDevMode ? [{ debugName: "isMobileDevice" }] : /* istanbul ignore next */ []));
4404
+ /**
4405
+ * Whether the device is Tablet
4406
+ */
4407
+ this.isTabletDevice = computed(() => this.deviceType() === AXPDeviceType.Tablet, ...(ngDevMode ? [{ debugName: "isTabletDevice" }] : /* istanbul ignore next */ []));
4408
+ /**
4409
+ * Whether the device is Desktop
4410
+ */
4411
+ this.isDesktopDevice = computed(() => this.deviceType() === AXPDeviceType.Desktop, ...(ngDevMode ? [{ debugName: "isDesktopDevice" }] : /* istanbul ignore next */ []));
4412
+ /**
4413
+ * Whether the device supports touch (alias for isTouchDevice)
4414
+ */
4415
+ this.isTouchScreen = computed(() => this.isTouchDevice(), ...(ngDevMode ? [{ debugName: "isTouchScreen" }] : /* istanbul ignore next */ []));
4416
+ //#endregion
4417
+ //#region ---- Private Properties ----
4418
+ this._resizeListener = null;
4419
+ this._setupResizeListener();
4420
+ }
4421
+ //#endregion
4422
+ //#region ---- Private Methods ----
4423
+ /**
4424
+ * Update screen size and device type based on window width
4425
+ */
4426
+ _updateScreenSize() {
4427
+ const width = window.innerWidth;
4428
+ let newScreenSize;
4429
+ if (width <= 600) {
4430
+ newScreenSize = AXPScreenSize.Small;
4431
+ }
4432
+ else if (width <= 1024) {
4433
+ newScreenSize = AXPScreenSize.Medium;
4434
+ }
4435
+ else {
4436
+ newScreenSize = AXPScreenSize.Large;
4437
+ }
4438
+ // Determine device type based on width to keep it reactive to devtools emulation
4439
+ let newDeviceType;
4440
+ if (width <= 600) {
4441
+ newDeviceType = AXPDeviceType.Mobile;
4442
+ }
4443
+ else if (width <= 1024) {
4444
+ newDeviceType = AXPDeviceType.Tablet;
4445
+ }
4446
+ else {
4447
+ newDeviceType = AXPDeviceType.Desktop;
4448
+ }
4449
+ if (newScreenSize !== this.screenSize() || newDeviceType !== this.deviceType()) {
4450
+ this.screenSize.set(newScreenSize);
4451
+ this.deviceType.set(newDeviceType);
4452
+ }
4453
+ }
4454
+ /**
4455
+ * Set up event listener for window resize
4456
+ */
4457
+ _setupResizeListener() {
4458
+ this._resizeListener = fromEvent(window, 'resize')
4459
+ .pipe(debounceTime(250)) // 250ms debounce delay
4460
+ .subscribe(() => {
4461
+ this._updateScreenSize();
4462
+ });
4463
+ }
4464
+ /**
4465
+ * Remove event listener for window resize
4466
+ */
4467
+ _removeResizeListener() {
4468
+ if (this._resizeListener) {
4469
+ this._resizeListener.unsubscribe();
4470
+ this._resizeListener = null;
4471
+ }
4472
+ }
4473
+ //#endregion
4474
+ //#region ---- Public Methods ----
4475
+ /**
4476
+ * Clean up resources when service is destroyed
4477
+ */
4478
+ destroy() {
4479
+ this._removeResizeListener();
4480
+ }
4481
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDeviceService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4482
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDeviceService, providedIn: 'root' }); }
4483
+ }
4484
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDeviceService, decorators: [{
4485
+ type: Injectable,
4486
+ args: [{
4487
+ providedIn: 'root',
4488
+ }]
4489
+ }], ctorParameters: () => [] });
4490
+
4491
+ /**
4492
+ * Service for applying and resetting text highlighting within specified HTML elements.
4493
+ */
4494
+ class AXHighlightService {
4495
+ constructor() { }
4496
+ /**
4497
+ * Highlights all occurrences of a search value within the elements matching the provided query selector.
4498
+ *
4499
+ * @param querySelector - CSS selector to identify target elements.
4500
+ * @param searchValue - Text to search and highlight.
4501
+ */
4502
+ highlight(querySelector, searchValue) {
4503
+ this.clear();
4504
+ if (!querySelector || !searchValue) {
4505
+ return;
4506
+ }
4507
+ this.querySelector = querySelector;
4508
+ this.searchValue = searchValue;
4509
+ const elements = document.querySelectorAll(querySelector);
4510
+ elements.forEach((element) => {
4511
+ this.applyHighlight(element);
4512
+ });
4513
+ }
4514
+ /**
4515
+ * Resets all highlighted text within the elements matching the previously used query selector.
4516
+ */
4517
+ clear() {
4518
+ const elements = document.querySelectorAll(`${this.querySelector} .ax-highlight-text`);
4519
+ elements.forEach((element) => {
4520
+ let combinedText = '';
4521
+ element.querySelectorAll('span').forEach((span) => {
4522
+ combinedText += span.textContent;
4523
+ });
4524
+ const mergedSpan = document.createElement('span');
4525
+ mergedSpan.textContent = combinedText;
4526
+ element?.parentNode?.replaceChild(mergedSpan, element);
4527
+ });
4528
+ }
4529
+ /**
4530
+ * Applies highlighting to a specific element by matching text against the search value.
4531
+ *
4532
+ * @param element - The HTML element to apply highlights to.
4533
+ */
4534
+ applyHighlight(element) {
4535
+ const searchRegex = new RegExp(`(${this.searchValue})`, 'gi');
4536
+ this.recursivelyHighlight(element, searchRegex);
4537
+ }
4538
+ /**
4539
+ * Recursively applies highlighting to text nodes within an element.
4540
+ *
4541
+ * @param element - The current node being processed.
4542
+ * @param searchRegex - Regular expression for matching text to highlight.
4543
+ */
4544
+ recursivelyHighlight(element, searchRegex) {
4545
+ const childNodes = Array.from(element.childNodes);
4546
+ childNodes.forEach((node) => {
4547
+ if (node.nodeType === Node.TEXT_NODE) {
4548
+ const textContent = node.textContent || '';
4549
+ if (textContent.match(searchRegex)) {
4550
+ const highlightedHTML = this.createHighlightedHTML(textContent, searchRegex);
4551
+ const tempContainer = document.createElement('div');
4552
+ tempContainer.innerHTML = highlightedHTML;
4553
+ const fragment = document.createDocumentFragment();
4554
+ Array.from(tempContainer.childNodes).forEach((child) => fragment.appendChild(child));
4555
+ node.replaceWith(fragment);
4556
+ }
4557
+ }
4558
+ else if (node.nodeType === Node.ELEMENT_NODE) {
4559
+ this.recursivelyHighlight(node, searchRegex);
4560
+ }
4561
+ });
4562
+ }
4563
+ /**
4564
+ * Generates the HTML structure for highlighted text.
4565
+ *
4566
+ * @param textContent - The original text content to process.
4567
+ * @param searchRegex - Regular expression for matching text to highlight.
4568
+ * @returns A string of HTML with highlighted portions wrapped in span elements.
4569
+ */
4570
+ createHighlightedHTML(textContent, searchRegex) {
4571
+ let parts = [];
4572
+ let match;
4573
+ let lastIndex = 0;
4574
+ let resultHTML = '<span class="ax-highlight-text">';
4575
+ while ((match = searchRegex.exec(textContent)) !== null) {
4576
+ if (match.index > lastIndex) {
4577
+ resultHTML += `<span>${textContent.slice(lastIndex, match.index)}</span>`;
4578
+ }
4579
+ resultHTML += `<span class="ax-highlight">${match[0]}</span>`;
4580
+ lastIndex = searchRegex.lastIndex;
4581
+ }
4582
+ if (lastIndex < textContent.length) {
4583
+ resultHTML += `<span>${textContent.slice(lastIndex)}</span>`;
4584
+ }
4585
+ resultHTML += '</span>';
4586
+ return resultHTML;
4587
+ }
4588
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXHighlightService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4589
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXHighlightService, providedIn: 'root' }); }
4590
+ }
4591
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXHighlightService, decorators: [{
4592
+ type: Injectable,
4593
+ args: [{
4594
+ providedIn: 'root',
4595
+ }]
4596
+ }], ctorParameters: () => [] });
4597
+
4598
+ /**
4599
+ * Creates a provider instance within an injection context.
4600
+ * Handles dynamic import, injector resolution, and provider instantiation.
4601
+ *
4602
+ * @param loader - Function that returns a promise resolving to the provider class
4603
+ * @returns Promise that resolves to the provider instance
4604
+ *
4605
+ * @example
4606
+ * ```typescript
4607
+ * useFactory: () => createProviderWithInjectionContext(() =>
4608
+ * import('./settings.provider').then(m => m.AXMSettingProvider)
4609
+ * )
4610
+ * ```
4611
+ */
4612
+ async function createProviderWithInjectionContext(loader) {
4613
+ const injector = inject(Injector);
4614
+ const ProviderClass = await loader();
4615
+ return runInInjectionContext(injector, () => new ProviderClass());
4616
+ }
4617
+ /**
4618
+ * Creates a provider configuration for lazy-loaded providers with injection context.
4619
+ * Simplifies provider registration in NgModule providers array.
4620
+ *
4621
+ * @param token - The injection token to provide
4622
+ * @param loader - Function that returns a promise resolving to the provider class
4623
+ * @param multi - Optional. Whether the provider is a multi-provider (array of values). Defaults to `true`. Pass `false` for a single provider.
4624
+ * @returns Provider configuration object
4625
+ *
4626
+ * @example
4627
+ * ```typescript
4628
+ * // Multi-provider (default)
4629
+ * provideLazyProvider(
4630
+ * AXP_DATASOURCE_DEFINITION_PROVIDER,
4631
+ * () => import('./datasource.provider').then(m => m.AXMDataSourceProvider)
4632
+ * )
4633
+ *
4634
+ * // Single provider
4635
+ * provideLazyProvider(
4636
+ * SOME_TOKEN,
4637
+ * () => import('./my.provider').then(m => m.MyProvider),
4638
+ * false
4639
+ * )
4640
+ * ```
4641
+ */
4642
+ function provideLazyProvider(token, loader, multi = true) {
4643
+ return {
4644
+ provide: token,
4645
+ useFactory: () => createProviderWithInjectionContext(loader),
4646
+ multi,
4647
+ };
4648
+ }
4649
+
4650
+ /**
4651
+ * Generated bundle index. Do not edit.
4652
+ */
4653
+
4654
+ export { AXHighlightService, AXPActivityLogProvider, AXPActivityLogService, AXPAppStartUpProvider, AXPAppStartUpService, AXPBroadcastEventService, AXPCatalogScopeDefinitionProviderService, AXPCatalogScopeDefinitionsDataSourceDefinition, AXPColorPaletteProvider, AXPColorPaletteService, AXPColumnWidthService, AXPComponentLogoConfig, AXPComponentSlot, AXPComponentSlotDirective, AXPComponentSlotModule, AXPComponentSlotRegistryService, AXPContentCheckerDirective, AXPContextDefinitionProviderService, AXPContextEvalFactory, AXPContextStore, AXPCountdownPipe, AXPDataGenerator, AXPDataSourceDefinitionProviderService, AXPDblClickDirective, AXPDebugService, AXPDefaultColorPalettesProvider, AXPDeviceService, AXPDeviceType, AXPDistributedEventListenerService, AXPElementDataDirective, AXPExpressionEvaluatorScopeProviderContext, AXPExpressionEvaluatorScopeProviderService, AXPExpressionEvaluatorService, AXPFeatureDefinitionProviderContext, AXPGridLayoutDirective, AXPHookService, AXPIconLogoConfig, AXPIdleSchedulerService, AXPImageUrlLogoConfig, AXPKeyboardShortcutRegistry, AXPModuleManifestModule, AXPModuleManifestRegistry, AXPModuleManifestsDataSourceDefinition, AXPScreenSize, 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, DEFAULT_CANCEL_DIALOG_ACTION_SHORTCUTS, DEFAULT_SUBMIT_DIALOG_ACTION_SHORTCUTS, FORM_DIRTY_TRACE_NS, OVERLAY_DETAILS_VIEW_TRACE_NS, OVERLAY_LAYOUT_TRACE_NS, OVERLAY_ROOT_LAYOUT_TRACE_NS, OVERLAY_WIDGET_SETTLE_TRACE_NS, applyContextEvalPipes, applyFilterArray, applyPagination, applyQueryArray, applySortArray, applySystemActionDefault, chordToKbdItemKeys, contextEvalWithPipes, createProviderWithInjectionContext, defaultColumnWidthProvider, findOverlayContainerAncestor, formatKeyboardShortcutChord, formatKeyboardShortcutChords, getNestedVisibleOverlayPanes, getPrimaryKeyboardShortcutChord, getTopVisibleOverlayContainer, getVisibleAnchoredOverlayPanes, getVisibleOverlayContainers, hasForegroundOverlayLayer, isDialogShortcutAllowedInEditableField, isHorizontalDirectionalShortcutKey, isKeyboardTargetInsideEditableField, isMacPlatform, isVisibleOverlayElement, matchesDialogActionShortcut, matchesKeyboardShortcutChord, mirrorHorizontalDirectionalKey, normalizeContextEvalPipes, normalizeDialogActionShortcutChord, normalizeExpressionTemplate, normalizeKeyboardShortcut, normalizeKeyboardShortcuts, objectKeyValueTransforms, parseDialogActionShortcut, parseKeyboardShortcutChord, provideLazyProvider, registerPopupFooterKeyboardShortcuts, resolveConfiguredFooterActionShortcuts, resolveDialogActionShortcuts, resolveDisplayShortcutChord, resolveEffectiveDirectionBehavior, resolveSemanticDirectionalKey, setupPopupFooterKeyboardShortcuts, shouldDeferEscapeToOpenOverlay, shouldTriggerDialogActionShortcut };
4655
+ //# sourceMappingURL=acorex-platform-framework-client-core.mjs.map