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