@acorex/platform 21.0.0-next.76 → 21.0.0-next.79
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/{acorex-platform-common-common-settings.provider-ChoT4m4k.mjs → acorex-platform-common-common-settings.provider-Ytey9uhY.mjs} +1 -42
- package/fesm2022/acorex-platform-common-common-settings.provider-Ytey9uhY.mjs.map +1 -0
- package/fesm2022/acorex-platform-common.mjs +580 -1445
- package/fesm2022/acorex-platform-common.mjs.map +1 -1
- package/fesm2022/acorex-platform-contracts.mjs +1 -1330
- package/fesm2022/acorex-platform-contracts.mjs.map +1 -1
- package/fesm2022/acorex-platform-core.mjs +5 -208
- package/fesm2022/acorex-platform-core.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
- package/fesm2022/{acorex-platform-layout-entity-attachments-page.component-B0EkdqvH.mjs → acorex-platform-layout-entity-attachments-page.component-CwG6n_Yb.mjs} +3 -3
- package/fesm2022/{acorex-platform-layout-entity-attachments-page.component-B0EkdqvH.mjs.map → acorex-platform-layout-entity-attachments-page.component-CwG6n_Yb.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-layout-entity-file-list-popup.component-_yrP5SQe.mjs → acorex-platform-layout-entity-file-list-popup.component--IeMZLIr.mjs} +4 -4
- package/fesm2022/{acorex-platform-layout-entity-file-list-popup.component-_yrP5SQe.mjs.map → acorex-platform-layout-entity-file-list-popup.component--IeMZLIr.mjs.map} +1 -1
- package/fesm2022/acorex-platform-layout-entity.mjs +694 -733
- package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-views.mjs +171 -372
- package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-widget-core.mjs +103 -3
- package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-J0zcGKBX.mjs +116 -0
- package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-J0zcGKBX.mjs.map +1 -0
- package/fesm2022/acorex-platform-layout-widgets.mjs +6 -11
- package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
- package/fesm2022/{acorex-platform-themes-default-entity-master-single-view.component-DrUwqKKA.mjs → acorex-platform-themes-default-entity-master-single-view.component-Br9p5aXT.mjs} +3 -2
- package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-Br9p5aXT.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-default.mjs +52 -643
- package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
- package/fesm2022/acorex-platform-workflow.mjs +8 -5
- package/fesm2022/acorex-platform-workflow.mjs.map +1 -1
- package/package.json +1 -1
- package/types/acorex-platform-common.d.ts +1056 -337
- package/types/acorex-platform-contracts.d.ts +4 -1679
- package/types/acorex-platform-core.d.ts +181 -92
- package/types/acorex-platform-layout-components.d.ts +1 -2
- package/types/acorex-platform-layout-entity.d.ts +221 -99
- package/types/acorex-platform-layout-views.d.ts +5 -39
- package/types/acorex-platform-layout-widget-core.d.ts +3 -2
- package/types/acorex-platform-layout-widgets.d.ts +4 -11
- package/types/acorex-platform-themes-default.d.ts +57 -120
- package/types/acorex-platform-workflow.d.ts +18 -5
- package/fesm2022/acorex-platform-common-common-settings.provider-ChoT4m4k.mjs.map +0 -1
- package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-CFyfix31.mjs +0 -513
- package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-CFyfix31.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-DrUwqKKA.mjs.map +0 -1
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, inject, Injectable, Injector, signal, DestroyRef, makeEnvironmentProviders, NgModule, computed, input, output, ViewEncapsulation, ChangeDetectionStrategy, Component, viewChild, ErrorHandler, EventEmitter, Input, Output, Directive,
|
|
3
|
-
import {
|
|
4
|
-
export * from '@acorex/platform/contracts';
|
|
2
|
+
import { InjectionToken, inject, Injectable, Injector, signal, DestroyRef, makeEnvironmentProviders, NgModule, computed, input, output, ViewEncapsulation, ChangeDetectionStrategy, Component, viewChild, ErrorHandler, EventEmitter, Input, Output, Directive, Inject, runInInjectionContext, model, linkedSignal, afterNextRender } from '@angular/core';
|
|
3
|
+
import { kebabCase, merge, sortBy, cloneDeep, get, isNumber, isPlainObject, omit } from 'lodash-es';
|
|
5
4
|
import { Router, NavigationEnd, NavigationStart, ROUTES, RedirectCommand, RouterModule } from '@angular/router';
|
|
6
5
|
import { AXPSessionService, AXPSessionStatus, AXPAuthGuard } from '@acorex/platform/auth';
|
|
7
6
|
import { Subject, distinctUntilChanged, merge as merge$1, first, switchMap, from, map } from 'rxjs';
|
|
8
|
-
import * as
|
|
9
|
-
import { unwrapValueProperty, coerceUnknownToDate, coerceUnknownToFiniteNumber, coerceUnknownToBoolean, coerceUnknownToTrimmedString, AXPPlatformScope, AXPBroadcastEventService, objectKeyValueTransforms, AXPDataGenerator, AXPKeyboardShortcutRegistry, chordToKbdItemKeys, hasForegroundOverlayLayer, AXPKeyboardShortcutPriority, normalizeKeyboardShortcuts, AXPSystemActionType,
|
|
10
|
-
import { merge, sortBy, cloneDeep, get, isNumber, isNil, isObject, isString, isPlainObject, omit, defaultTo } from 'lodash-es';
|
|
7
|
+
import * as i2$1 from '@acorex/platform/core';
|
|
8
|
+
import { unwrapValueProperty, coerceUnknownToDate, coerceUnknownToFiniteNumber, coerceUnknownToBoolean, coerceUnknownToTrimmedString, AXPPlatformScope, AXPBroadcastEventService, objectKeyValueTransforms, AXPDataGenerator, AXPKeyboardShortcutRegistry, chordToKbdItemKeys, hasForegroundOverlayLayer, AXPKeyboardShortcutPriority, normalizeKeyboardShortcuts, AXPSystemActionType, isLocaleStringMap, AXPExpressionEvaluatorService, AXPModuleManifestModule, AXPComponentSlotModule, AXPAppStartUpProvider, AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER, AXPHookService, AXPModuleManifestRegistry } from '@acorex/platform/core';
|
|
11
9
|
import { AXCalendarService, AXDateTimeModule } from '@acorex/core/date-time';
|
|
12
10
|
import { AXDialogService } from '@acorex/components/dialog';
|
|
13
11
|
import * as i5 from '@acorex/core/translation';
|
|
14
|
-
import { AXTranslationService, AXTranslationModule,
|
|
12
|
+
import { AXTranslationService, AXTranslationModule, resolveMultiLanguageString } from '@acorex/core/translation';
|
|
15
13
|
import * as i4$1 from '@angular/common';
|
|
16
14
|
import { Location, CommonModule, AsyncPipe, DOCUMENT } from '@angular/common';
|
|
17
15
|
import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
|
|
@@ -26,15 +24,13 @@ import * as i3 from '@acorex/components/progress-bar';
|
|
|
26
24
|
import { AXProgressBarModule } from '@acorex/components/progress-bar';
|
|
27
25
|
import { AXPopupService, AXPopupModule } from '@acorex/components/popup';
|
|
28
26
|
import { AXToastService, AXToastModule } from '@acorex/components/toast';
|
|
29
|
-
import * as
|
|
30
|
-
import { AXFormatService } from '@acorex/core/format';
|
|
31
|
-
import * as i4$2 from '@acorex/platform/workflow';
|
|
27
|
+
import * as i3$1 from '@acorex/platform/workflow';
|
|
32
28
|
import { AXPWorkflowService, createWorkFlowEvent, AXPWorkflowAction, AXPWorkflowError, AXPWorkflowModule } from '@acorex/platform/workflow';
|
|
33
29
|
import { AXKBDComponent, AXKBDItemComponent } from '@acorex/components/kbd';
|
|
34
30
|
import { AXDataSource } from '@acorex/cdk/common';
|
|
35
31
|
import { AXPCommandExecutor, AXPCommandService } from '@acorex/platform/runtime';
|
|
36
32
|
import { signalStore, withState, withMethods, patchState, withHooks } from '@ngrx/signals';
|
|
37
|
-
import {
|
|
33
|
+
import { AXFormatService } from '@acorex/core/format';
|
|
38
34
|
import { AXFileService } from '@acorex/core/file';
|
|
39
35
|
import { AXCheckBoxModule } from '@acorex/components/check-box';
|
|
40
36
|
import * as i6 from '@acorex/components/color-box';
|
|
@@ -168,10 +164,110 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
168
164
|
}]
|
|
169
165
|
}] });
|
|
170
166
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
167
|
+
var AXPRelationshipKind;
|
|
168
|
+
(function (AXPRelationshipKind) {
|
|
169
|
+
AXPRelationshipKind[AXPRelationshipKind["Association"] = 0] = "Association";
|
|
170
|
+
AXPRelationshipKind[AXPRelationshipKind["Composition"] = 1] = "Composition";
|
|
171
|
+
AXPRelationshipKind[AXPRelationshipKind["Aggregation"] = 2] = "Aggregation";
|
|
172
|
+
})(AXPRelationshipKind || (AXPRelationshipKind = {}));
|
|
173
|
+
var AXPRelationshipCardinality;
|
|
174
|
+
(function (AXPRelationshipCardinality) {
|
|
175
|
+
AXPRelationshipCardinality[AXPRelationshipCardinality["OneToOne"] = 0] = "OneToOne";
|
|
176
|
+
AXPRelationshipCardinality[AXPRelationshipCardinality["OneToMany"] = 1] = "OneToMany";
|
|
177
|
+
AXPRelationshipCardinality[AXPRelationshipCardinality["ManyToMany"] = 2] = "ManyToMany";
|
|
178
|
+
})(AXPRelationshipCardinality || (AXPRelationshipCardinality = {}));
|
|
179
|
+
var AXPEntityCommandScope;
|
|
180
|
+
(function (AXPEntityCommandScope) {
|
|
181
|
+
AXPEntityCommandScope["TypeLevel"] = "typeLevel";
|
|
182
|
+
AXPEntityCommandScope["Selected"] = "selected";
|
|
183
|
+
AXPEntityCommandScope["Individual"] = "individual";
|
|
184
|
+
AXPEntityCommandScope["Section"] = "section";
|
|
185
|
+
})(AXPEntityCommandScope || (AXPEntityCommandScope = {}));
|
|
186
|
+
var AXPEntityQueryType;
|
|
187
|
+
(function (AXPEntityQueryType) {
|
|
188
|
+
AXPEntityQueryType["Single"] = "single";
|
|
189
|
+
AXPEntityQueryType["List"] = "list";
|
|
190
|
+
})(AXPEntityQueryType || (AXPEntityQueryType = {}));
|
|
191
|
+
/** Whether the card field is shown as a header badge (not in the card body). */
|
|
192
|
+
function isCardFieldBadgeDisplay(display) {
|
|
193
|
+
if (display === 'badge') {
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
return typeof display === 'object' && display !== null && display.type === 'badge';
|
|
197
|
+
}
|
|
198
|
+
/** Resolves static badge color config from {@link AXPCardField.display} and legacy {@link AXPCardField.badgeColor}. */
|
|
199
|
+
function resolveCardFieldBadgeColor(field) {
|
|
200
|
+
if (typeof field.display === 'object' && field.display.type === 'badge') {
|
|
201
|
+
return field.display.options?.color ?? field.badgeColor;
|
|
202
|
+
}
|
|
203
|
+
if (field.display === 'badge') {
|
|
204
|
+
return field.badgeColor;
|
|
205
|
+
}
|
|
206
|
+
return undefined;
|
|
207
|
+
}
|
|
208
|
+
function getEntityInfo(entity) {
|
|
209
|
+
return {
|
|
210
|
+
source: `${entity.module}.${entity.name}`,
|
|
211
|
+
module: {
|
|
212
|
+
route: kebabCase(entity.module),
|
|
213
|
+
},
|
|
214
|
+
entity: {
|
|
215
|
+
route: kebabCase(entity.name),
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
function createQueryView(name, title, fixed, params = {
|
|
220
|
+
columns: [],
|
|
221
|
+
conditions: [],
|
|
222
|
+
sorts: [],
|
|
223
|
+
}) {
|
|
224
|
+
params = Object.assign({ columns: [], sorts: [], conditions: [] }, params);
|
|
225
|
+
return {
|
|
226
|
+
name,
|
|
227
|
+
title,
|
|
228
|
+
fixed,
|
|
229
|
+
columns: params.columns ?? [],
|
|
230
|
+
conditions: params.conditions ?? [],
|
|
231
|
+
sorts: params.sorts ?? [],
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
function createAllQueryView(params = {
|
|
235
|
+
columns: [],
|
|
236
|
+
sorts: [],
|
|
237
|
+
conditions: [],
|
|
238
|
+
}) {
|
|
239
|
+
params = Object.assign({ columns: [], sorts: [], conditions: [] }, params);
|
|
240
|
+
return createQueryView('all', '@general:terms.interface.selection.all-items', true, {
|
|
241
|
+
columns: params.columns ?? [],
|
|
242
|
+
conditions: params.conditions ?? [],
|
|
243
|
+
sorts: params.sorts ?? [],
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
/** Resolves master list layouts that are currently enabled. */
|
|
247
|
+
function resolveEnabledMasterListLayouts(layouts) {
|
|
248
|
+
if (!layouts) {
|
|
249
|
+
return ['table'];
|
|
250
|
+
}
|
|
251
|
+
const enabled = [];
|
|
252
|
+
if (layouts.table?.enabled !== false) {
|
|
253
|
+
enabled.push('table');
|
|
254
|
+
}
|
|
255
|
+
if (layouts.card && layouts.card.enabled !== false) {
|
|
256
|
+
enabled.push('card');
|
|
257
|
+
}
|
|
258
|
+
return enabled.length > 0 ? enabled : ['table'];
|
|
259
|
+
}
|
|
260
|
+
/** First enabled layout; used as the default when no user preference is stored. */
|
|
261
|
+
function resolveDefaultMasterListLayout(layouts) {
|
|
262
|
+
return resolveEnabledMasterListLayouts(layouts)[0];
|
|
263
|
+
}
|
|
264
|
+
// new version
|
|
265
|
+
var AXPEntityType;
|
|
266
|
+
(function (AXPEntityType) {
|
|
267
|
+
AXPEntityType[AXPEntityType["Entity"] = 0] = "Entity";
|
|
268
|
+
AXPEntityType[AXPEntityType["AggregateRoot"] = 1] = "AggregateRoot";
|
|
269
|
+
AXPEntityType[AXPEntityType["ValueObject"] = 2] = "ValueObject";
|
|
270
|
+
})(AXPEntityType || (AXPEntityType = {}));
|
|
175
271
|
|
|
176
272
|
const EQ_OPER = { name: 'equal', title: '@general:terms.operators.equal', hasValue: true };
|
|
177
273
|
const NOT_EQ_OPER = { name: 'notEqual', title: '@general:terms.operators.not-equal', hasValue: true };
|
|
@@ -376,6 +472,38 @@ const AXP_HOME_PAGE_DEFAULT_KEY = new InjectionToken('AXP_HOME_PAGE_DEFAULT_KEY'
|
|
|
376
472
|
factory: () => 'home'
|
|
377
473
|
});
|
|
378
474
|
|
|
475
|
+
/**
|
|
476
|
+
* Regional and locale-related setting keys used by platform widgets and components.
|
|
477
|
+
* Most values use the `LocaleManagement:Setting:Regional.*` prefix; definitions live in the locale-management module where applicable.
|
|
478
|
+
*/
|
|
479
|
+
var AXPRegionalSetting;
|
|
480
|
+
(function (AXPRegionalSetting) {
|
|
481
|
+
AXPRegionalSetting["LocaleProfile"] = "LocaleManagement:Setting:Regional.LocaleProfile";
|
|
482
|
+
AXPRegionalSetting["TimeZone"] = "LocaleManagement:Setting:Regional.TimeZone";
|
|
483
|
+
AXPRegionalSetting["Language"] = "LocaleManagement:Setting:Regional.Language";
|
|
484
|
+
AXPRegionalSetting["Country"] = "LocaleManagement:Setting:Regional.Country";
|
|
485
|
+
AXPRegionalSetting["Calendar"] = "LocaleManagement:Setting:Regional.Calendar";
|
|
486
|
+
/** User-selected holiday calendars shown on the task board and other calendar views. */
|
|
487
|
+
AXPRegionalSetting["CalendarEvents"] = "LocaleManagement:Setting:Regional.CalendarEvents";
|
|
488
|
+
AXPRegionalSetting["FirstDayOfWeek"] = "LocaleManagement:Setting:Regional.FirstDayOfWeek";
|
|
489
|
+
AXPRegionalSetting["WeekendDays"] = "LocaleManagement:Setting:Regional.WeekendDays";
|
|
490
|
+
AXPRegionalSetting["ShortDate"] = "LocaleManagement:Setting:Regional.ShortDate";
|
|
491
|
+
AXPRegionalSetting["MediumDate"] = "LocaleManagement:Setting:Regional.MediumDate";
|
|
492
|
+
AXPRegionalSetting["LongDate"] = "LocaleManagement:Setting:Regional.LongDate";
|
|
493
|
+
AXPRegionalSetting["ShortTime"] = "LocaleManagement:Setting:Regional.ShortTime";
|
|
494
|
+
AXPRegionalSetting["MediumTime"] = "LocaleManagement:Setting:Regional.MediumTime";
|
|
495
|
+
AXPRegionalSetting["LongTime"] = "LocaleManagement:Setting:Regional.LongTime";
|
|
496
|
+
AXPRegionalSetting["MeasurementUnits"] = "LocaleManagement:Setting:Regional.MeasurementUnits";
|
|
497
|
+
AXPRegionalSetting["TemperatureUnits"] = "LocaleManagement:Setting:Regional.TemperatureUnits";
|
|
498
|
+
AXPRegionalSetting["DistanceUnits"] = "LocaleManagement:Setting:Regional.DistanceUnits";
|
|
499
|
+
AXPRegionalSetting["WeightUnits"] = "LocaleManagement:Setting:Regional.WeightUnits";
|
|
500
|
+
AXPRegionalSetting["VolumeUnits"] = "LocaleManagement:Setting:Regional.VolumeUnits";
|
|
501
|
+
AXPRegionalSetting["SpeedUnits"] = "LocaleManagement:Setting:Regional.SpeedUnits";
|
|
502
|
+
AXPRegionalSetting["AreaUnits"] = "LocaleManagement:Setting:Regional.AreaUnits";
|
|
503
|
+
/** Default multi-language behavior for standard text field names; defined in locale-management `AXMRegionalSettingProvider`. */
|
|
504
|
+
AXPRegionalSetting["MultiLanguageSupport"] = "LocaleManagement:Setting:Regional.MultiLanguageSupport";
|
|
505
|
+
})(AXPRegionalSetting || (AXPRegionalSetting = {}));
|
|
506
|
+
|
|
379
507
|
class AXPSettingDefinitionProviderContext {
|
|
380
508
|
constructor() {
|
|
381
509
|
this.rootGroups = [];
|
|
@@ -473,7 +601,6 @@ class AXPSettingDefinitionSectionBuilder {
|
|
|
473
601
|
isRequired: setting.isRequired ?? false,
|
|
474
602
|
isInherited: setting.isInherited ?? false,
|
|
475
603
|
isEncrypted: setting.isEncrypted ?? false,
|
|
476
|
-
requiresPageRefresh: setting.requiresPageRefresh ?? false,
|
|
477
604
|
defaultValue: setting.defaultValue,
|
|
478
605
|
scope: setting.scope ?? 'P',
|
|
479
606
|
layout: setting.widget.layout,
|
|
@@ -608,26 +735,6 @@ class AXPSettingDefinitionProviderService {
|
|
|
608
735
|
collect(this.cache);
|
|
609
736
|
return scopes;
|
|
610
737
|
}
|
|
611
|
-
async requiresPageRefreshByKey() {
|
|
612
|
-
const refreshByKey = {};
|
|
613
|
-
const collect = (groups) => {
|
|
614
|
-
groups.forEach((group) => {
|
|
615
|
-
group.sections.forEach((section) => {
|
|
616
|
-
section.settings.forEach((setting) => {
|
|
617
|
-
if (setting.requiresPageRefresh) {
|
|
618
|
-
refreshByKey[setting.name] = true;
|
|
619
|
-
}
|
|
620
|
-
});
|
|
621
|
-
});
|
|
622
|
-
collect(group.groups);
|
|
623
|
-
});
|
|
624
|
-
};
|
|
625
|
-
if (!this.cache) {
|
|
626
|
-
await this.load();
|
|
627
|
-
}
|
|
628
|
-
collect(this.cache);
|
|
629
|
-
return refreshByKey;
|
|
630
|
-
}
|
|
631
738
|
findGroup(scope, groupName) {
|
|
632
739
|
return this.searchRecursive(this.getList(scope), groupName, []); // Initialize with an empty breadcrumb
|
|
633
740
|
}
|
|
@@ -1318,8 +1425,6 @@ var AXPCommonSettings;
|
|
|
1318
1425
|
AXPCommonSettings["DebugMode"] = "PlatformDevTools:Setting:Developer.DebugMode";
|
|
1319
1426
|
AXPCommonSettings["ShowRowIndexColumn"] = "Common:Setting:Entity.ShowRowIndexColumn";
|
|
1320
1427
|
AXPCommonSettings["EntityListPersistenceMode"] = "Common:Setting:Entity.ListPersistenceMode";
|
|
1321
|
-
AXPCommonSettings["NavigationMode"] = "Common:Setting:Navigation.Mode";
|
|
1322
|
-
AXPCommonSettings["MaxOpenTabs"] = "Common:Setting:Navigation.MaxOpenTabs";
|
|
1323
1428
|
})(AXPCommonSettings || (AXPCommonSettings = {}));
|
|
1324
1429
|
|
|
1325
1430
|
//#endregion
|
|
@@ -1506,176 +1611,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
1506
1611
|
}]
|
|
1507
1612
|
}], ctorParameters: () => [] });
|
|
1508
1613
|
|
|
1509
|
-
//#endregion
|
|
1510
|
-
/**
|
|
1511
|
-
* Holds detached Angular route component instances keyed by tab key so tabular navigation can
|
|
1512
|
-
* preserve a page's full state (data-table selection, scroll, unsaved form values, etc.) while it
|
|
1513
|
-
* is in the background, then reattach it without re-rendering.
|
|
1514
|
-
*
|
|
1515
|
-
* The registry has no `Router` dependency on purpose: it is consumed by the {@link RouteReuseStrategy}
|
|
1516
|
-
* implementation, so depending on `Router` here would create a construction-time cycle.
|
|
1517
|
-
*/
|
|
1518
|
-
class AXPTabRouteCacheRegistry {
|
|
1519
|
-
constructor() {
|
|
1520
|
-
//#region ---- State ----
|
|
1521
|
-
this.handles = new Map();
|
|
1522
|
-
this.openKeys = new Set();
|
|
1523
|
-
this.enabled = false;
|
|
1524
|
-
this.deactivateGuardSuspended = false;
|
|
1525
|
-
}
|
|
1526
|
-
//#endregion
|
|
1527
|
-
//#region ---- Deactivate Guard Suspension ----
|
|
1528
|
-
/**
|
|
1529
|
-
* Runs `action` while unsaved-changes `canDeactivate` guards are suspended, so switching between
|
|
1530
|
-
* tabs preserves a dirty page without prompting. Closing a tab does not use this path, so the
|
|
1531
|
-
* prompt still appears there.
|
|
1532
|
-
*/
|
|
1533
|
-
async runWithSuspendedDeactivateGuards(action) {
|
|
1534
|
-
const previous = this.deactivateGuardSuspended;
|
|
1535
|
-
this.deactivateGuardSuspended = true;
|
|
1536
|
-
try {
|
|
1537
|
-
return await action();
|
|
1538
|
-
}
|
|
1539
|
-
finally {
|
|
1540
|
-
this.deactivateGuardSuspended = previous;
|
|
1541
|
-
}
|
|
1542
|
-
}
|
|
1543
|
-
isDeactivateGuardSuspended() {
|
|
1544
|
-
return this.deactivateGuardSuspended;
|
|
1545
|
-
}
|
|
1546
|
-
//#endregion
|
|
1547
|
-
//#region ---- Mode ----
|
|
1548
|
-
/** Enables caching (tabular mode). Disabling clears and destroys every cached component. */
|
|
1549
|
-
setEnabled(enabled) {
|
|
1550
|
-
if (this.enabled === enabled) {
|
|
1551
|
-
return;
|
|
1552
|
-
}
|
|
1553
|
-
this.enabled = enabled;
|
|
1554
|
-
if (!enabled) {
|
|
1555
|
-
this.clear();
|
|
1556
|
-
}
|
|
1557
|
-
}
|
|
1558
|
-
isEnabled() {
|
|
1559
|
-
return this.enabled;
|
|
1560
|
-
}
|
|
1561
|
-
//#endregion
|
|
1562
|
-
//#region ---- Open Keys Sync ----
|
|
1563
|
-
/**
|
|
1564
|
-
* Reconciles open scoped tab keys and active tab URLs. Cached handles whose key is not an open
|
|
1565
|
-
* scoped tab key, an active tab URL, or a child path under an open scoped tab are destroyed.
|
|
1566
|
-
*/
|
|
1567
|
-
syncOpenKeys(scopedKeys, activeTabUrls = []) {
|
|
1568
|
-
const scopedSet = new Set(scopedKeys);
|
|
1569
|
-
const activePathSet = new Set(activeTabUrls.map((url) => url.split('?')[0].split('#')[0].replace(/\/+$/, '') || '/'));
|
|
1570
|
-
for (const cacheKey of [...this.handles.keys()]) {
|
|
1571
|
-
const normalizedCacheKey = cacheKey.replace(/\/+$/, '') || '/';
|
|
1572
|
-
const keepsScopedKey = scopedSet.has(normalizedCacheKey);
|
|
1573
|
-
const keepsActiveUrl = activePathSet.has(normalizedCacheKey);
|
|
1574
|
-
const keepsUnderScopedTab = [...scopedSet].some((scopedKey) => normalizedCacheKey === scopedKey || normalizedCacheKey.startsWith(`${scopedKey}/`));
|
|
1575
|
-
if (!keepsScopedKey && !keepsActiveUrl && !keepsUnderScopedTab) {
|
|
1576
|
-
this.destroyHandle(this.handles.get(cacheKey));
|
|
1577
|
-
this.handles.delete(cacheKey);
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
|
-
this.openKeys = scopedSet;
|
|
1581
|
-
}
|
|
1582
|
-
isOpen(key) {
|
|
1583
|
-
return this.openKeys.has(key);
|
|
1584
|
-
}
|
|
1585
|
-
/**
|
|
1586
|
-
* Returns the live component instance for a cached background tab, when available.
|
|
1587
|
-
* Matches the tab key directly or any cached handle under a scoped tab prefix.
|
|
1588
|
-
*/
|
|
1589
|
-
getComponentInstance(key) {
|
|
1590
|
-
const normalizedKey = key.replace(/\/+$/, '') || '/';
|
|
1591
|
-
const direct = this.handles.get(normalizedKey) ?? this.handles.get(key);
|
|
1592
|
-
if (direct) {
|
|
1593
|
-
return this.readComponentInstance(direct);
|
|
1594
|
-
}
|
|
1595
|
-
for (const [cacheKey, handle] of this.handles) {
|
|
1596
|
-
const normalizedCacheKey = cacheKey.replace(/\/+$/, '') || '/';
|
|
1597
|
-
if (normalizedCacheKey === normalizedKey || normalizedCacheKey.startsWith(`${normalizedKey}/`)) {
|
|
1598
|
-
return this.readComponentInstance(handle);
|
|
1599
|
-
}
|
|
1600
|
-
}
|
|
1601
|
-
return null;
|
|
1602
|
-
}
|
|
1603
|
-
//#endregion
|
|
1604
|
-
//#region ---- Handle Store ----
|
|
1605
|
-
/**
|
|
1606
|
-
* Caches a detached background handle. Overwrites without destroying the previous handle: on a
|
|
1607
|
-
* repeat visit Angular hands back a new handle object wrapping the same live `componentRef`, so
|
|
1608
|
-
* destroying the old one would destroy the live component and corrupt the view (NG0922).
|
|
1609
|
-
*/
|
|
1610
|
-
store(key, handle) {
|
|
1611
|
-
this.handles.set(key, handle);
|
|
1612
|
-
}
|
|
1613
|
-
/**
|
|
1614
|
-
* Returns the cached handle without mutating the map. Angular reads a handle twice per
|
|
1615
|
-
* navigation (once while building the future state, once while attaching), so `retrieve` must be
|
|
1616
|
-
* idempotent; the handle is released afterwards via {@link release} when Angular calls
|
|
1617
|
-
* `store(snapshot, null)`.
|
|
1618
|
-
*/
|
|
1619
|
-
retrieve(key) {
|
|
1620
|
-
return this.handles.get(key) ?? null;
|
|
1621
|
-
}
|
|
1622
|
-
/**
|
|
1623
|
-
* Drops a handle that the router has just reattached (it is now a live foreground view, so it
|
|
1624
|
-
* must not be destroyed). The map keeps only truly backgrounded handles.
|
|
1625
|
-
*/
|
|
1626
|
-
release(key) {
|
|
1627
|
-
this.handles.delete(key);
|
|
1628
|
-
}
|
|
1629
|
-
has(key) {
|
|
1630
|
-
return this.handles.has(key);
|
|
1631
|
-
}
|
|
1632
|
-
/** Destroys and removes a single cached handle (used when a tab closes). */
|
|
1633
|
-
evict(key) {
|
|
1634
|
-
this.destroyHandle(this.handles.get(key));
|
|
1635
|
-
this.handles.delete(key);
|
|
1636
|
-
}
|
|
1637
|
-
/** Destroys and removes every cached handle. */
|
|
1638
|
-
clear() {
|
|
1639
|
-
for (const handle of this.handles.values()) {
|
|
1640
|
-
this.destroyHandle(handle);
|
|
1641
|
-
}
|
|
1642
|
-
this.handles.clear();
|
|
1643
|
-
this.openKeys.clear();
|
|
1644
|
-
}
|
|
1645
|
-
//#endregion
|
|
1646
|
-
//#region ---- Utility Methods ----
|
|
1647
|
-
readComponentInstance(handle) {
|
|
1648
|
-
const componentRef = handle.componentRef;
|
|
1649
|
-
return componentRef?.instance ?? null;
|
|
1650
|
-
}
|
|
1651
|
-
destroyHandle(handle) {
|
|
1652
|
-
if (!handle) {
|
|
1653
|
-
return;
|
|
1654
|
-
}
|
|
1655
|
-
const componentRef = handle.componentRef;
|
|
1656
|
-
componentRef?.destroy?.();
|
|
1657
|
-
}
|
|
1658
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTabRouteCacheRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1659
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTabRouteCacheRegistry, providedIn: 'root' }); }
|
|
1660
|
-
}
|
|
1661
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTabRouteCacheRegistry, decorators: [{
|
|
1662
|
-
type: Injectable,
|
|
1663
|
-
args: [{
|
|
1664
|
-
providedIn: 'root',
|
|
1665
|
-
}]
|
|
1666
|
-
}] });
|
|
1667
|
-
|
|
1668
1614
|
//#region ---- Unsaved Changes Can Deactivate Guard Factory ----
|
|
1669
1615
|
function createUnsavedChangesCanDeactivateGuard() {
|
|
1670
|
-
return (component) =>
|
|
1671
|
-
// Tab switches keep the page alive in the background, so skip the prompt; closing a tab does not
|
|
1672
|
-
// suspend guards and still prompts.
|
|
1673
|
-
const tabCache = inject(AXPTabRouteCacheRegistry, { optional: true });
|
|
1674
|
-
if (tabCache?.isDeactivateGuardSuspended()) {
|
|
1675
|
-
return true;
|
|
1676
|
-
}
|
|
1677
|
-
return component.canDeactivate();
|
|
1678
|
-
};
|
|
1616
|
+
return (component) => component.canDeactivate();
|
|
1679
1617
|
}
|
|
1680
1618
|
//#endregion
|
|
1681
1619
|
|
|
@@ -3053,56 +2991,6 @@ function axpRedirectToUnauthorized(router = inject(Router)) {
|
|
|
3053
2991
|
return new RedirectCommand(router.parseUrl(AXP_UNAUTHORIZED_ROUTE));
|
|
3054
2992
|
}
|
|
3055
2993
|
|
|
3056
|
-
//#endregion
|
|
3057
|
-
/**
|
|
3058
|
-
* Platform formatter for display text: resolves ML objects, i18n keys (`@scope:key`), and primitives.
|
|
3059
|
-
* Register via {@link AXPCommonModule} (auto) or `AXFormatModule.forRoot({ formatters: [AXPTranslateFormatter] })`.
|
|
3060
|
-
*/
|
|
3061
|
-
class AXPTranslateFormatter {
|
|
3062
|
-
constructor() {
|
|
3063
|
-
//#region ---- Services & Dependencies ----
|
|
3064
|
-
this.translationService = inject(AXTranslationService);
|
|
3065
|
-
}
|
|
3066
|
-
//#endregion
|
|
3067
|
-
//#region ---- AXFormatter ----
|
|
3068
|
-
get name() {
|
|
3069
|
-
return 'translate';
|
|
3070
|
-
}
|
|
3071
|
-
format(value, options) {
|
|
3072
|
-
if (isNil(value) || value === '') {
|
|
3073
|
-
return '';
|
|
3074
|
-
}
|
|
3075
|
-
if (isObject(value) && !Array.isArray(value) && !(value instanceof Date)) {
|
|
3076
|
-
const resolved = this.translationService.resolve(value);
|
|
3077
|
-
return resolved?.trim() ?? '';
|
|
3078
|
-
}
|
|
3079
|
-
if (isString(value)) {
|
|
3080
|
-
const text = value.trim();
|
|
3081
|
-
if (!text) {
|
|
3082
|
-
return '';
|
|
3083
|
-
}
|
|
3084
|
-
if (text.startsWith('@')) {
|
|
3085
|
-
const translateOptions = typeof options === 'object' ? options : undefined;
|
|
3086
|
-
const translated = translateSync(text, translateOptions);
|
|
3087
|
-
return translated?.trim() ?? text;
|
|
3088
|
-
}
|
|
3089
|
-
return text;
|
|
3090
|
-
}
|
|
3091
|
-
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
3092
|
-
return String(value);
|
|
3093
|
-
}
|
|
3094
|
-
if (value instanceof Date) {
|
|
3095
|
-
return value.toISOString();
|
|
3096
|
-
}
|
|
3097
|
-
return String(value).trim();
|
|
3098
|
-
}
|
|
3099
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTranslateFormatter, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3100
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTranslateFormatter }); }
|
|
3101
|
-
}
|
|
3102
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTranslateFormatter, decorators: [{
|
|
3103
|
-
type: Injectable
|
|
3104
|
-
}] });
|
|
3105
|
-
|
|
3106
2994
|
class AXPFileStorageService {
|
|
3107
2995
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileStorageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3108
2996
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileStorageService }); }
|
|
@@ -4868,8 +4756,6 @@ class AXPMenuSearchDefinitionProvider {
|
|
|
4868
4756
|
}
|
|
4869
4757
|
}
|
|
4870
4758
|
|
|
4871
|
-
const AXP_SEARCH_DEFINITION_LAZY_RESOLVER = new InjectionToken('AXP_SEARCH_DEFINITION_LAZY_RESOLVER');
|
|
4872
|
-
|
|
4873
4759
|
class AXPSearchDefinitionProviderContext {
|
|
4874
4760
|
constructor() {
|
|
4875
4761
|
this.definitions = [];
|
|
@@ -4944,7 +4830,6 @@ const AXP_SEARCH_DEFINITION_PROVIDER = new InjectionToken('AXP_SEARCH_DEFINITION
|
|
|
4944
4830
|
class AXPSearchDefinitionProviderService {
|
|
4945
4831
|
constructor() {
|
|
4946
4832
|
this.providers = inject(AXP_SEARCH_DEFINITION_PROVIDER, { optional: true });
|
|
4947
|
-
this.lazyResolver = inject(AXP_SEARCH_DEFINITION_LAZY_RESOLVER, { optional: true });
|
|
4948
4833
|
this.cache = null;
|
|
4949
4834
|
}
|
|
4950
4835
|
async load() {
|
|
@@ -4975,29 +4860,12 @@ class AXPSearchDefinitionProviderService {
|
|
|
4975
4860
|
}
|
|
4976
4861
|
}
|
|
4977
4862
|
findDefinition(definitionName) {
|
|
4978
|
-
const definition = this.getList().find((
|
|
4863
|
+
const definition = this.getList().find((definition) => definition.name === definitionName);
|
|
4979
4864
|
if (!definition) {
|
|
4980
4865
|
throw new Error(`Definition with name ${definitionName} not found`);
|
|
4981
4866
|
}
|
|
4982
4867
|
return definition;
|
|
4983
4868
|
}
|
|
4984
|
-
async findDefinitionAsync(definitionName) {
|
|
4985
|
-
await this.load();
|
|
4986
|
-
const existing = this.getList().find((item) => item.name === definitionName);
|
|
4987
|
-
if (existing) {
|
|
4988
|
-
return existing;
|
|
4989
|
-
}
|
|
4990
|
-
const lazy = this.lazyResolver;
|
|
4991
|
-
if (!lazy) {
|
|
4992
|
-
return null;
|
|
4993
|
-
}
|
|
4994
|
-
const resolved = await lazy.resolve(definitionName);
|
|
4995
|
-
if (!resolved) {
|
|
4996
|
-
return null;
|
|
4997
|
-
}
|
|
4998
|
-
this.cache = [...(this.cache ?? []), resolved];
|
|
4999
|
-
return resolved;
|
|
5000
|
-
}
|
|
5001
4869
|
findDefinitionByGroup(definitionGroup) {
|
|
5002
4870
|
const definition = this.getList().find((definition) => definition.group === definitionGroup);
|
|
5003
4871
|
if (!definition) {
|
|
@@ -5016,8 +4884,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
5016
4884
|
//#region ---- Imports ----
|
|
5017
4885
|
//#endregion
|
|
5018
4886
|
//#region ---- Template normalization ----
|
|
5019
|
-
/**
|
|
5020
|
-
|
|
4887
|
+
/**
|
|
4888
|
+
* Normalizes entity display templates for {@link AXPExpressionEvaluatorService}.
|
|
4889
|
+
* Converts `context.eval('path')`, single braces, and simple dot paths to `context.eval` segments.
|
|
4890
|
+
*/
|
|
4891
|
+
function normalizeEntityDisplayTemplate(template) {
|
|
4892
|
+
let normalized = template.replace(/\{\{\s*context\.eval\(['"]([^'"]+)['"]\)\s*\}\}/g, '{{ context.eval("$1") }}');
|
|
4893
|
+
if (!normalized.includes('{{')) {
|
|
4894
|
+
normalized = normalized.replace(/\{/g, '{{').replace(/\}/g, '}}');
|
|
4895
|
+
}
|
|
4896
|
+
return normalized.replace(/\{\{\s*(?!context\.)([\w]+(?:\.[\w]+)*)\s*\}\}/g, '{{ context.eval("$1") }}');
|
|
4897
|
+
}
|
|
5021
4898
|
/** True when formatted text still contains unresolved template markers. */
|
|
5022
4899
|
function isUnresolvedEntityDisplayTemplate(value) {
|
|
5023
4900
|
return /\{\{/.test(value) || /context\.eval\s*\(/.test(value) || value === '[object Object]';
|
|
@@ -5148,55 +5025,17 @@ function resolveEntityRowTitleTemplate(entity) {
|
|
|
5148
5025
|
}
|
|
5149
5026
|
return undefined;
|
|
5150
5027
|
}
|
|
5151
|
-
/**
|
|
5152
|
-
* Record title template for modify popups: `interfaces.master.update.title` → `single.title`.
|
|
5153
|
-
*/
|
|
5154
|
-
function resolveEntityModifyRecordTitleTemplate(entity) {
|
|
5155
|
-
if (!entity) {
|
|
5156
|
-
return undefined;
|
|
5157
|
-
}
|
|
5158
|
-
const updateTitle = entity.interfaces?.master?.update?.title?.trim();
|
|
5159
|
-
if (updateTitle && isRowBoundTemplate(updateTitle)) {
|
|
5160
|
-
return normalizeEntityDisplayTemplate(updateTitle);
|
|
5161
|
-
}
|
|
5162
|
-
const singleTitle = entity.interfaces?.master?.single?.title?.trim();
|
|
5163
|
-
if (singleTitle && isRowBoundTemplate(singleTitle)) {
|
|
5164
|
-
return normalizeEntityDisplayTemplate(singleTitle);
|
|
5165
|
-
}
|
|
5166
|
-
return undefined;
|
|
5167
|
-
}
|
|
5168
|
-
/**
|
|
5169
|
-
* Modify/edit popup chrome: `{action} {individual} ({update.title ?? single.title})` when a record template exists.
|
|
5170
|
-
*/
|
|
5171
|
-
async function resolveEntityModifyPopupTitle(entity, recordData, translationService, formatService, expressionEvaluator, contextEvalFactory, actionTitleKey = '@general:actions.modify.title') {
|
|
5172
|
-
const actionPrefix = translationService.translateSync(actionTitleKey);
|
|
5173
|
-
const entityLabel = translationService.translateSync(entity.formats.individual);
|
|
5174
|
-
const baseTitle = `${actionPrefix} ${entityLabel}`.trim();
|
|
5175
|
-
const recordTemplate = resolveEntityModifyRecordTitleTemplate(entity);
|
|
5176
|
-
if (!recordTemplate) {
|
|
5177
|
-
return baseTitle;
|
|
5178
|
-
}
|
|
5179
|
-
const locale = translationService.getActiveLang();
|
|
5180
|
-
const resolved = await resolveSearchDisplayTemplate(recordTemplate, recordData, locale, translationService, formatService, expressionEvaluator, contextEvalFactory);
|
|
5181
|
-
const recordTitle = sanitizeResolvedEntityDisplayTitle(typeof resolved === 'string'
|
|
5182
|
-
? resolved
|
|
5183
|
-
: resolveSearchableText(resolved, locale, translationService));
|
|
5184
|
-
if (!isMeaningfulEntityDisplayTitle(recordTitle)) {
|
|
5185
|
-
return baseTitle;
|
|
5186
|
-
}
|
|
5187
|
-
return `${baseTitle} (${recordTitle})`;
|
|
5188
|
-
}
|
|
5189
5028
|
//#endregion
|
|
5190
5029
|
//#region ---- Runtime resolution ----
|
|
5191
5030
|
/**
|
|
5192
5031
|
* Resolves an entity row title using entity-driven fallback templates and property paths.
|
|
5193
5032
|
*/
|
|
5194
|
-
async function resolveEntitySearchTitleFallback(format, data, locale, translationService, formatService, expressionEvaluator
|
|
5033
|
+
async function resolveEntitySearchTitleFallback(format, data, locale, translationService, formatService, expressionEvaluator) {
|
|
5195
5034
|
if (data == null) {
|
|
5196
5035
|
return '';
|
|
5197
5036
|
}
|
|
5198
5037
|
for (const template of format.titleFallbackTemplates ?? []) {
|
|
5199
|
-
const resolved = await resolveSearchDisplayTemplate(template, data, locale, translationService, formatService, expressionEvaluator
|
|
5038
|
+
const resolved = await resolveSearchDisplayTemplate(template, data, locale, translationService, formatService, expressionEvaluator);
|
|
5200
5039
|
const text = sanitizeResolvedEntityDisplayTitle(typeof resolved === 'string'
|
|
5201
5040
|
? resolved
|
|
5202
5041
|
: resolveSearchableText(resolved, locale, translationService));
|
|
@@ -5237,8 +5076,13 @@ function resolveSearchableText(value, locale, translationService) {
|
|
|
5237
5076
|
}
|
|
5238
5077
|
return trimmed;
|
|
5239
5078
|
}
|
|
5240
|
-
if (isLocaleStringMap(value)
|
|
5241
|
-
return
|
|
5079
|
+
if (isLocaleStringMap(value)) {
|
|
5080
|
+
return Object.values(value)
|
|
5081
|
+
.filter((entry) => (entry ?? '').trim() !== '')
|
|
5082
|
+
.join(' ');
|
|
5083
|
+
}
|
|
5084
|
+
if (translationService.isValidMultiLanguageObject(value)) {
|
|
5085
|
+
return resolveMultiLanguageString(value, locale);
|
|
5242
5086
|
}
|
|
5243
5087
|
return '';
|
|
5244
5088
|
}
|
|
@@ -5250,11 +5094,6 @@ function searchInSearchableValue(value, query, locale, translationService) {
|
|
|
5250
5094
|
if (!q) {
|
|
5251
5095
|
return true;
|
|
5252
5096
|
}
|
|
5253
|
-
if (isLocaleStringMap(value)) {
|
|
5254
|
-
return Object.values(value)
|
|
5255
|
-
.filter((entry) => typeof entry === 'string' && entry.trim() !== '')
|
|
5256
|
-
.some((entry) => entry.toLocaleLowerCase(locale).includes(q));
|
|
5257
|
-
}
|
|
5258
5097
|
const text = resolveSearchableText(value, locale, translationService).toLocaleLowerCase(locale);
|
|
5259
5098
|
return text.includes(q);
|
|
5260
5099
|
}
|
|
@@ -5287,31 +5126,26 @@ function searchInRecordValues(value, query, locale, translationService, depth =
|
|
|
5287
5126
|
function countMustacheExpressions(template) {
|
|
5288
5127
|
return (template.match(/\{\{/g) ?? []).length;
|
|
5289
5128
|
}
|
|
5290
|
-
/** Standard
|
|
5291
|
-
function createEntityRowExpressionScope(data
|
|
5129
|
+
/** Standard row scope for entity title/search templates. */
|
|
5130
|
+
function createEntityRowExpressionScope(data) {
|
|
5292
5131
|
return {
|
|
5293
|
-
context:
|
|
5132
|
+
context: {
|
|
5133
|
+
eval: (path) => get(data, path),
|
|
5134
|
+
},
|
|
5294
5135
|
};
|
|
5295
5136
|
}
|
|
5296
|
-
/**
|
|
5297
|
-
* Coerces an evaluated display segment to plain text.
|
|
5298
|
-
* Piped eval returns strings; unpiped MLS still resolves via compat fallback.
|
|
5299
|
-
*/
|
|
5300
|
-
function formatDisplaySegment(segment, locale, translationService) {
|
|
5301
|
-
if (segment === false || segment == null) {
|
|
5302
|
-
return '';
|
|
5303
|
-
}
|
|
5304
|
-
if (isString(segment)) {
|
|
5305
|
-
return segment.trim();
|
|
5306
|
-
}
|
|
5307
|
-
return resolveSearchableText(segment, locale, translationService);
|
|
5308
|
-
}
|
|
5309
5137
|
/**
|
|
5310
5138
|
* Evaluates a normalized entity display template via {@link AXPExpressionEvaluatorService}.
|
|
5311
5139
|
*/
|
|
5312
|
-
async function evaluateEntityDisplayTemplate(template, data, expressionEvaluator, locale, translationService
|
|
5140
|
+
async function evaluateEntityDisplayTemplate(template, data, expressionEvaluator, locale, translationService) {
|
|
5313
5141
|
const normalized = normalizeEntityDisplayTemplate(template);
|
|
5314
|
-
const scope = createEntityRowExpressionScope(data
|
|
5142
|
+
const scope = createEntityRowExpressionScope(data);
|
|
5143
|
+
const formatSegment = (segment) => {
|
|
5144
|
+
if (segment === false || segment == null) {
|
|
5145
|
+
return '';
|
|
5146
|
+
}
|
|
5147
|
+
return resolveSearchableText(segment, locale, translationService);
|
|
5148
|
+
};
|
|
5315
5149
|
if (!normalized.includes('{{')) {
|
|
5316
5150
|
return normalized;
|
|
5317
5151
|
}
|
|
@@ -5325,7 +5159,7 @@ async function evaluateEntityDisplayTemplate(template, data, expressionEvaluator
|
|
|
5325
5159
|
for (const match of normalized.matchAll(regex)) {
|
|
5326
5160
|
result += normalized.slice(lastIndex, match.index);
|
|
5327
5161
|
const segment = await expressionEvaluator.evaluate(`{{ ${match[1].trim()} }}`, scope);
|
|
5328
|
-
result +=
|
|
5162
|
+
result += formatSegment(segment);
|
|
5329
5163
|
lastIndex = (match.index ?? 0) + match[0].length;
|
|
5330
5164
|
}
|
|
5331
5165
|
result += normalized.slice(lastIndex);
|
|
@@ -5372,7 +5206,7 @@ function resolveQueryMatchingDisplayText(value, query, locale, translationServic
|
|
|
5372
5206
|
/**
|
|
5373
5207
|
* Resolves entity search display templates; coerces multi-language field values to readable text.
|
|
5374
5208
|
*/
|
|
5375
|
-
async function resolveSearchDisplayTemplate(template, data, locale, translationService, formatService, expressionEvaluator
|
|
5209
|
+
async function resolveSearchDisplayTemplate(template, data, locale, translationService, formatService, expressionEvaluator) {
|
|
5376
5210
|
if (!template) {
|
|
5377
5211
|
return '';
|
|
5378
5212
|
}
|
|
@@ -5383,7 +5217,7 @@ async function resolveSearchDisplayTemplate(template, data, locale, translationS
|
|
|
5383
5217
|
const formatted = formatService.format(template, 'string', data);
|
|
5384
5218
|
return sanitizeResolvedEntityDisplayTitle(typeof formatted === 'string' ? formatted : String(formatted ?? ''));
|
|
5385
5219
|
}
|
|
5386
|
-
const evaluated = await evaluateEntityDisplayTemplate(template, data, expressionEvaluator, locale, translationService
|
|
5220
|
+
const evaluated = await evaluateEntityDisplayTemplate(template, data, expressionEvaluator, locale, translationService);
|
|
5387
5221
|
return sanitizeResolvedEntityDisplayTitle(resolveSearchableText(evaluated, locale, translationService));
|
|
5388
5222
|
}
|
|
5389
5223
|
//#endregion
|
|
@@ -5408,7 +5242,6 @@ class AXPSearchService {
|
|
|
5408
5242
|
this.formatService = inject(AXFormatService);
|
|
5409
5243
|
this.translationService = inject(AXTranslationService);
|
|
5410
5244
|
this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
|
|
5411
|
-
this.contextEvalFactory = inject(AXPContextEvalFactory);
|
|
5412
5245
|
}
|
|
5413
5246
|
async search(text) {
|
|
5414
5247
|
//TODO better handle this
|
|
@@ -5438,23 +5271,20 @@ class AXPSearchService {
|
|
|
5438
5271
|
for (const resultArray of results) {
|
|
5439
5272
|
for (const result of resultArray) {
|
|
5440
5273
|
try {
|
|
5441
|
-
const definition =
|
|
5442
|
-
if (!definition) {
|
|
5443
|
-
continue;
|
|
5444
|
-
}
|
|
5274
|
+
const definition = this.definitionService.findDefinition(result.group);
|
|
5445
5275
|
const resolvedTitle = result.title ??
|
|
5446
5276
|
(definition.format.title
|
|
5447
|
-
? await resolveSearchDisplayTemplate(definition.format.title, result.data, locale, this.translationService, this.formatService, this.expressionEvaluator
|
|
5277
|
+
? await resolveSearchDisplayTemplate(definition.format.title, result.data, locale, this.translationService, this.formatService, this.expressionEvaluator)
|
|
5448
5278
|
: '');
|
|
5449
5279
|
const resolvedDescription = result.description ??
|
|
5450
5280
|
(definition.format.description
|
|
5451
|
-
? await resolveSearchDisplayTemplate(definition.format.description, result.data, locale, this.translationService, this.formatService, this.expressionEvaluator
|
|
5281
|
+
? await resolveSearchDisplayTemplate(definition.format.description, result.data, locale, this.translationService, this.formatService, this.expressionEvaluator)
|
|
5452
5282
|
: undefined);
|
|
5453
5283
|
let titleText = sanitizeResolvedEntityDisplayTitle(typeof resolvedTitle === 'string'
|
|
5454
5284
|
? resolvedTitle
|
|
5455
5285
|
: resolveSearchableText(resolvedTitle, locale, this.translationService));
|
|
5456
5286
|
if (!isMeaningfulEntityDisplayTitle(titleText)) {
|
|
5457
|
-
titleText = await resolveEntitySearchTitleFallback(definition.format, result.data, locale, this.translationService, this.formatService, this.expressionEvaluator
|
|
5287
|
+
titleText = await resolveEntitySearchTitleFallback(definition.format, result.data, locale, this.translationService, this.formatService, this.expressionEvaluator);
|
|
5458
5288
|
}
|
|
5459
5289
|
if (!isMeaningfulEntityDisplayTitle(titleText)) {
|
|
5460
5290
|
continue;
|
|
@@ -5949,1090 +5779,36 @@ function axpReplaceEntityListRouteQueryParams(router, queryParams) {
|
|
|
5949
5779
|
}
|
|
5950
5780
|
//#endregion
|
|
5951
5781
|
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
const
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
* Path segments define identity; query params are excluded so the same entity record
|
|
5958
|
-
* or list reuses one tab even when filters or refresh tokens change.
|
|
5959
|
-
*/
|
|
5960
|
-
function axpBuildTabKey(url) {
|
|
5961
|
-
const normalized = normalizeRouterUrl(url);
|
|
5962
|
-
const pathOnly = normalized.split('?')[0].split('#')[0];
|
|
5963
|
-
return pathOnly;
|
|
5964
|
-
}
|
|
5965
|
-
/** Normalizes a router URL for storage and navigation. */
|
|
5966
|
-
function normalizeRouterUrl(url) {
|
|
5967
|
-
const trimmed = url.trim();
|
|
5968
|
-
if (!trimmed.length) {
|
|
5969
|
-
return '/';
|
|
5970
|
-
}
|
|
5971
|
-
const withLeadingSlash = trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
|
|
5972
|
-
const [pathAndFragment, queryString = ''] = withLeadingSlash.split('?');
|
|
5973
|
-
const [pathPart, fragment = ''] = pathAndFragment.split('#');
|
|
5974
|
-
const normalizedPath = pathPart.replace(/\/+$/, '') || '/';
|
|
5975
|
-
const filteredQuery = filterQueryString(queryString);
|
|
5976
|
-
let result = normalizedPath;
|
|
5977
|
-
if (filteredQuery) {
|
|
5978
|
-
result += `?${filteredQuery}`;
|
|
5979
|
-
}
|
|
5980
|
-
if (fragment) {
|
|
5981
|
-
result += `#${fragment}`;
|
|
5982
|
-
}
|
|
5983
|
-
return result;
|
|
5984
|
-
}
|
|
5985
|
-
function filterQueryString(queryString) {
|
|
5986
|
-
if (!queryString) {
|
|
5987
|
-
return '';
|
|
5988
|
-
}
|
|
5989
|
-
const params = new URLSearchParams(queryString);
|
|
5990
|
-
const kept = [];
|
|
5991
|
-
params.forEach((value, key) => {
|
|
5992
|
-
if (!IGNORED_QUERY_KEYS.has(key)) {
|
|
5993
|
-
kept.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
|
|
5994
|
-
}
|
|
5995
|
-
});
|
|
5996
|
-
kept.sort();
|
|
5997
|
-
return kept.join('&');
|
|
5998
|
-
}
|
|
5999
|
-
/** Human-readable fallback title from the URL path. */
|
|
6000
|
-
function axpFallbackTabTitle(url) {
|
|
6001
|
-
const pathOnly = url.split('?')[0].split('#')[0];
|
|
6002
|
-
const segments = pathOnly.split('/').filter(Boolean);
|
|
6003
|
-
const last = defaultTo(segments[segments.length - 1], 'Page');
|
|
6004
|
-
return decodeURIComponent(last);
|
|
6005
|
-
}
|
|
6006
|
-
//#endregion
|
|
6007
|
-
|
|
6008
|
-
//#region ---- Types ----
|
|
6009
|
-
const AXP_HOME_TAB_KEY = 'axp:home';
|
|
6010
|
-
/** Route `data` key for tab-navigation behavior (`data.tab`). */
|
|
6011
|
-
const AXP_TAB_ROUTE_DATA_KEY = 'tab';
|
|
6012
|
-
//#endregion
|
|
6013
|
-
|
|
6014
|
-
//#region ---- Tab Preview Service ----
|
|
6015
|
-
/**
|
|
6016
|
-
* In-memory structured previews for tab hover cards and dirty indicators.
|
|
6017
|
-
* Populated by active/cached pages via {@link AXPPageLayoutBaseComponent}.
|
|
6018
|
-
*/
|
|
6019
|
-
class AXPTabPreviewService {
|
|
5782
|
+
//TODO Loading, Redirect, Drawer, Show toast
|
|
5783
|
+
const AXPRedirectEvent = createWorkFlowEvent('Redirect Event Fired');
|
|
5784
|
+
const AXPRefreshEvent = createWorkFlowEvent('Refresh Event Fired');
|
|
5785
|
+
const AXPReloadEvent = createWorkFlowEvent('Reload Event Fired');
|
|
5786
|
+
class AXPWorkflowNavigateAction extends AXPWorkflowAction {
|
|
6020
5787
|
constructor() {
|
|
6021
|
-
|
|
6022
|
-
this.
|
|
6023
|
-
this.previews = this.previewMap.asReadonly();
|
|
6024
|
-
}
|
|
6025
|
-
//#endregion
|
|
6026
|
-
//#region ---- Public API ----
|
|
6027
|
-
setPreview(key, preview) {
|
|
6028
|
-
this.previewMap.update((current) => ({
|
|
6029
|
-
...current,
|
|
6030
|
-
[key]: preview,
|
|
6031
|
-
}));
|
|
6032
|
-
}
|
|
6033
|
-
patchPreview(key, patch) {
|
|
6034
|
-
this.previewMap.update((current) => {
|
|
6035
|
-
const existing = current[key];
|
|
6036
|
-
if (!existing) {
|
|
6037
|
-
if (!patch.url) {
|
|
6038
|
-
return current;
|
|
6039
|
-
}
|
|
6040
|
-
return {
|
|
6041
|
-
...current,
|
|
6042
|
-
[key]: {
|
|
6043
|
-
title: patch.title ?? '',
|
|
6044
|
-
url: patch.url,
|
|
6045
|
-
icon: patch.icon,
|
|
6046
|
-
subtitle: patch.subtitle,
|
|
6047
|
-
isDirty: patch.isDirty ?? false,
|
|
6048
|
-
breadcrumbTrail: patch.breadcrumbTrail ?? [],
|
|
6049
|
-
},
|
|
6050
|
-
};
|
|
6051
|
-
}
|
|
6052
|
-
return {
|
|
6053
|
-
...current,
|
|
6054
|
-
[key]: {
|
|
6055
|
-
...existing,
|
|
6056
|
-
...patch,
|
|
6057
|
-
},
|
|
6058
|
-
};
|
|
6059
|
-
});
|
|
6060
|
-
}
|
|
6061
|
-
getPreview(key) {
|
|
6062
|
-
return this.previewMap()[key];
|
|
6063
|
-
}
|
|
6064
|
-
isTabDirty(key) {
|
|
6065
|
-
return !!this.previewMap()[key]?.isDirty;
|
|
6066
|
-
}
|
|
6067
|
-
clearPreview(key) {
|
|
6068
|
-
this.previewMap.update((current) => {
|
|
6069
|
-
if (!(key in current)) {
|
|
6070
|
-
return current;
|
|
6071
|
-
}
|
|
6072
|
-
const next = { ...current };
|
|
6073
|
-
delete next[key];
|
|
6074
|
-
return next;
|
|
6075
|
-
});
|
|
5788
|
+
super(...arguments);
|
|
5789
|
+
this.router = inject(Router);
|
|
6076
5790
|
}
|
|
6077
|
-
|
|
6078
|
-
const
|
|
6079
|
-
if (
|
|
5791
|
+
async execute(context) {
|
|
5792
|
+
const payload = context.getVariable('payload');
|
|
5793
|
+
if (Array.isArray(payload.commands)) {
|
|
5794
|
+
await this.router.navigate(payload.commands, payload.extras);
|
|
6080
5795
|
return;
|
|
6081
5796
|
}
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
return changed ? next : current;
|
|
6092
|
-
});
|
|
6093
|
-
}
|
|
6094
|
-
clearAll() {
|
|
6095
|
-
this.previewMap.set({});
|
|
5797
|
+
const path = payload.commands;
|
|
5798
|
+
if (!path) {
|
|
5799
|
+
return;
|
|
5800
|
+
}
|
|
5801
|
+
if (path.toLowerCase().startsWith('http') || payload.target === 'blank') {
|
|
5802
|
+
window.open(path, '_blank');
|
|
5803
|
+
return;
|
|
5804
|
+
}
|
|
5805
|
+
await axpNavigateAppPath(this.router, path, payload.extras);
|
|
6096
5806
|
}
|
|
6097
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type:
|
|
6098
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type:
|
|
5807
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowNavigateAction, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
5808
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowNavigateAction }); }
|
|
6099
5809
|
}
|
|
6100
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type:
|
|
6101
|
-
type: Injectable
|
|
6102
|
-
args: [{
|
|
6103
|
-
providedIn: 'root',
|
|
6104
|
-
}]
|
|
6105
|
-
}] });
|
|
6106
|
-
|
|
6107
|
-
//#region ---- Tab Route Policy ----
|
|
6108
|
-
const EXCLUDED_PATH_PREFIXES = ['/auth', '/error'];
|
|
6109
|
-
const EXCLUDED_PATH_PATTERNS = [
|
|
6110
|
-
/^\/assessment\/fill\//i,
|
|
6111
|
-
/^\/forms\//i,
|
|
6112
|
-
];
|
|
6113
|
-
/**
|
|
6114
|
-
* Returns true when the URL represents an in-app page that may appear in the tab bar.
|
|
6115
|
-
*/
|
|
6116
|
-
function axpIsTabEligibleUrl(url) {
|
|
6117
|
-
const pathOnly = url.split('?')[0].split('#')[0].trim();
|
|
6118
|
-
if (!pathOnly || pathOnly === '/') {
|
|
6119
|
-
return false;
|
|
6120
|
-
}
|
|
6121
|
-
const normalized = pathOnly.startsWith('/') ? pathOnly : `/${pathOnly}`;
|
|
6122
|
-
if (EXCLUDED_PATH_PREFIXES.some((prefix) => normalized.startsWith(prefix))) {
|
|
6123
|
-
return false;
|
|
6124
|
-
}
|
|
6125
|
-
if (EXCLUDED_PATH_PATTERNS.some((pattern) => pattern.test(normalized))) {
|
|
6126
|
-
return false;
|
|
6127
|
-
}
|
|
6128
|
-
return true;
|
|
6129
|
-
}
|
|
6130
|
-
//#endregion
|
|
6131
|
-
|
|
6132
|
-
//#region ---- Route Data ----
|
|
6133
|
-
/** True when the route declares itself as a scoped tab root (`data.tab = true`). */
|
|
6134
|
-
function axpHasTabScope(data) {
|
|
6135
|
-
return data?.[AXP_TAB_ROUTE_DATA_KEY] === true;
|
|
6136
|
-
}
|
|
6137
|
-
//#endregion
|
|
6138
|
-
//#region ---- Snapshot Resolution ----
|
|
6139
|
-
/** Resolved URL path for a snapshot (no query or fragment). */
|
|
6140
|
-
function axpPathFromSnapshot(snapshot) {
|
|
6141
|
-
const segments = snapshot.pathFromRoot
|
|
6142
|
-
.flatMap((entry) => entry.url.map((segment) => segment.path))
|
|
6143
|
-
.filter(Boolean);
|
|
6144
|
-
return segments.length ? `/${segments.join('/')}` : '/';
|
|
6145
|
-
}
|
|
6146
|
-
/** Returns the leaf snapshot under the router state root. */
|
|
6147
|
-
function axpGetLeafRouteSnapshot(root) {
|
|
6148
|
-
let route = root;
|
|
6149
|
-
while (route.firstChild) {
|
|
6150
|
-
route = route.firstChild;
|
|
6151
|
-
}
|
|
6152
|
-
return route;
|
|
6153
|
-
}
|
|
6154
|
-
/**
|
|
6155
|
-
* Resolves the tab dedupe key from an activated route tree.
|
|
6156
|
-
* Uses the nearest ancestor with `data.tab = true`; otherwise the leaf path.
|
|
6157
|
-
*/
|
|
6158
|
-
function axpResolveTabKeyFromSnapshot(leaf) {
|
|
6159
|
-
const chain = leaf.pathFromRoot;
|
|
6160
|
-
for (let index = chain.length - 1; index >= 0; index--) {
|
|
6161
|
-
const snapshot = chain[index];
|
|
6162
|
-
if (axpHasTabScope(snapshot.data)) {
|
|
6163
|
-
return axpPathFromSnapshot(snapshot);
|
|
6164
|
-
}
|
|
6165
|
-
}
|
|
6166
|
-
return axpPathFromSnapshot(leaf);
|
|
6167
|
-
}
|
|
6168
|
-
//#endregion
|
|
6169
|
-
//#region ---- Router / URL Resolution ----
|
|
6170
|
-
/**
|
|
6171
|
-
* Resolves a tab key for the given URL (or the current router URL).
|
|
6172
|
-
* Prefers the live route snapshot when it matches the target URL; otherwise matches route config.
|
|
6173
|
-
*/
|
|
6174
|
-
function axpResolveTabKey(router, url) {
|
|
6175
|
-
const normalized = normalizeRouterUrl(url ?? router.url);
|
|
6176
|
-
const pathOnly = normalized.split('?')[0].split('#')[0];
|
|
6177
|
-
const currentPath = normalizeRouterUrl(router.url).split('?')[0].split('#')[0];
|
|
6178
|
-
if (!url || pathOnly === currentPath) {
|
|
6179
|
-
const leaf = axpGetLeafRouteSnapshot(router.routerState.snapshot.root);
|
|
6180
|
-
return axpResolveTabKeyFromSnapshot(leaf);
|
|
6181
|
-
}
|
|
6182
|
-
const fromConfig = axpResolveTabKeyFromRouteConfig(router.config, pathOnly);
|
|
6183
|
-
if (fromConfig) {
|
|
6184
|
-
return fromConfig;
|
|
6185
|
-
}
|
|
6186
|
-
return pathOnly.startsWith('/') ? pathOnly : `/${pathOnly}`;
|
|
6187
|
-
}
|
|
6188
|
-
/**
|
|
6189
|
-
* Matches a URL path against registered routes and resolves the tab key from `data.tab`.
|
|
6190
|
-
*/
|
|
6191
|
-
function axpResolveTabKeyFromRouteConfig(routes, pathOnly) {
|
|
6192
|
-
const normalized = pathOnly.startsWith('/') ? pathOnly.replace(/\/+$/, '') || '/' : `/${pathOnly}`.replace(/\/+$/, '') || '/';
|
|
6193
|
-
const segments = normalized === '/' ? [] : normalized.split('/').filter(Boolean);
|
|
6194
|
-
const chain = matchRouteConfigChain(routes, segments, 0, {}, []);
|
|
6195
|
-
if (!chain?.length) {
|
|
6196
|
-
return null;
|
|
6197
|
-
}
|
|
6198
|
-
for (let index = chain.length - 1; index >= 0; index--) {
|
|
6199
|
-
const level = chain[index];
|
|
6200
|
-
if (axpHasTabScope(level.mergedData)) {
|
|
6201
|
-
return level.path;
|
|
6202
|
-
}
|
|
6203
|
-
}
|
|
6204
|
-
return chain[chain.length - 1]?.path ?? null;
|
|
6205
|
-
}
|
|
6206
|
-
function matchRouteConfigChain(routes, segments, startIndex, inheritedData, chain) {
|
|
6207
|
-
let bestMatch = null;
|
|
6208
|
-
for (const route of routes) {
|
|
6209
|
-
if (route.redirectTo) {
|
|
6210
|
-
continue;
|
|
6211
|
-
}
|
|
6212
|
-
const segmentMatch = matchRoutePath(route.path ?? '', segments, startIndex);
|
|
6213
|
-
if (!segmentMatch) {
|
|
6214
|
-
continue;
|
|
6215
|
-
}
|
|
6216
|
-
const pathSegments = [...segments.slice(0, startIndex), ...segments.slice(startIndex, startIndex + segmentMatch.consumed)];
|
|
6217
|
-
const path = pathSegments.length ? `/${pathSegments.join('/')}` : '/';
|
|
6218
|
-
const mergedData = { ...inheritedData, ...(route.data ?? {}) };
|
|
6219
|
-
const level = { mergedData, path };
|
|
6220
|
-
const nextChain = [...chain, level];
|
|
6221
|
-
const nextIndex = startIndex + segmentMatch.consumed;
|
|
6222
|
-
if (route.children?.length) {
|
|
6223
|
-
const childMatch = matchRouteConfigChain(route.children, segments, nextIndex, mergedData, nextChain);
|
|
6224
|
-
if (childMatch && (!bestMatch || childMatch.length > bestMatch.length)) {
|
|
6225
|
-
bestMatch = childMatch;
|
|
6226
|
-
}
|
|
6227
|
-
}
|
|
6228
|
-
if (nextIndex === segments.length) {
|
|
6229
|
-
if (!bestMatch || nextChain.length > bestMatch.length) {
|
|
6230
|
-
bestMatch = nextChain;
|
|
6231
|
-
}
|
|
6232
|
-
}
|
|
6233
|
-
}
|
|
6234
|
-
return bestMatch;
|
|
6235
|
-
}
|
|
6236
|
-
function matchRoutePath(routePath, segments, startIndex) {
|
|
6237
|
-
if (routePath === '**') {
|
|
6238
|
-
return { consumed: Math.max(0, segments.length - startIndex) };
|
|
6239
|
-
}
|
|
6240
|
-
const parts = routePath.split('/').filter(Boolean);
|
|
6241
|
-
if (!parts.length) {
|
|
6242
|
-
return { consumed: 0 };
|
|
6243
|
-
}
|
|
6244
|
-
let consumed = 0;
|
|
6245
|
-
for (const part of parts) {
|
|
6246
|
-
const segment = segments[startIndex + consumed];
|
|
6247
|
-
if (segment === undefined) {
|
|
6248
|
-
return null;
|
|
6249
|
-
}
|
|
6250
|
-
if (part.startsWith(':') || part === segment) {
|
|
6251
|
-
consumed++;
|
|
6252
|
-
continue;
|
|
6253
|
-
}
|
|
6254
|
-
return null;
|
|
6255
|
-
}
|
|
6256
|
-
return { consumed };
|
|
6257
|
-
}
|
|
6258
|
-
//#endregion
|
|
6259
|
-
|
|
6260
|
-
//#region ---- Constants ----
|
|
6261
|
-
const SESSION_STORAGE_PREFIX = 'AXP_TAB_NAV';
|
|
6262
|
-
//#endregion
|
|
6263
|
-
class AXPTabNavigationService {
|
|
6264
|
-
//#endregion
|
|
6265
|
-
//#region ---- Lifecycle ----
|
|
6266
|
-
constructor() {
|
|
6267
|
-
//#region ---- Services & Dependencies ----
|
|
6268
|
-
this.router = inject(Router);
|
|
6269
|
-
this.settingsService = inject(AXPSettingsService);
|
|
6270
|
-
this.deviceService = inject(AXPDeviceService);
|
|
6271
|
-
this.menuService = inject(AXPMenuService);
|
|
6272
|
-
this.homePageService = inject(AXPHomePageService);
|
|
6273
|
-
this.sessionService = inject(AXPSessionService);
|
|
6274
|
-
this.routeCacheRegistry = inject(AXPTabRouteCacheRegistry);
|
|
6275
|
-
this.tabPreviewService = inject(AXPTabPreviewService);
|
|
6276
|
-
this.unsavedChangesConfirm = inject(AXPUnsavedChangesConfirmService);
|
|
6277
|
-
this.destroyRef = inject(DestroyRef);
|
|
6278
|
-
//#endregion
|
|
6279
|
-
//#region ---- State ----
|
|
6280
|
-
this.navigationMode = signal('simple', ...(ngDevMode ? [{ debugName: "navigationMode" }] : /* istanbul ignore next */ []));
|
|
6281
|
-
this.maxOpenTabs = signal(5, ...(ngDevMode ? [{ debugName: "maxOpenTabs" }] : /* istanbul ignore next */ []));
|
|
6282
|
-
this.tabs = signal([], ...(ngDevMode ? [{ debugName: "tabs" }] : /* istanbul ignore next */ []));
|
|
6283
|
-
this.activeTabKey = signal(null, ...(ngDevMode ? [{ debugName: "activeTabKey" }] : /* istanbul ignore next */ []));
|
|
6284
|
-
this.suppressNavigationSync = false;
|
|
6285
|
-
//#endregion
|
|
6286
|
-
//#region ---- Computed ----
|
|
6287
|
-
/** Effective mode: mobile always uses simple navigation. */
|
|
6288
|
-
this.effectiveMode = computed(() => {
|
|
6289
|
-
if (this.deviceService.isSmall()) {
|
|
6290
|
-
return 'simple';
|
|
6291
|
-
}
|
|
6292
|
-
return this.navigationMode();
|
|
6293
|
-
}, ...(ngDevMode ? [{ debugName: "effectiveMode" }] : /* istanbul ignore next */ []));
|
|
6294
|
-
this.isTabBarVisible = computed(() => this.effectiveMode() === 'tabular' && this.tabs().length > 0, ...(ngDevMode ? [{ debugName: "isTabBarVisible" }] : /* istanbul ignore next */ []));
|
|
6295
|
-
this.tabList = this.tabs.asReadonly();
|
|
6296
|
-
this.activeKey = this.activeTabKey.asReadonly();
|
|
6297
|
-
void this.loadSettings();
|
|
6298
|
-
this.bindRouterEvents();
|
|
6299
|
-
this.bindSettingsChanges();
|
|
6300
|
-
this.bindSessionChanges();
|
|
6301
|
-
this.bindRouteCacheSync();
|
|
6302
|
-
}
|
|
6303
|
-
//#endregion
|
|
6304
|
-
//#region ---- Public API ----
|
|
6305
|
-
/** Navigates to a path, opening or activating a tab when tabular mode is active. */
|
|
6306
|
-
async navigate(path, extras) {
|
|
6307
|
-
const normalizedPath = normalizeRouterUrl(path);
|
|
6308
|
-
if (this.effectiveMode() === 'simple' || !axpIsTabEligibleUrl(normalizedPath)) {
|
|
6309
|
-
return axpNavigateAppPath(this.router, normalizedPath, extras);
|
|
6310
|
-
}
|
|
6311
|
-
await this.openOrActivateTab(normalizedPath);
|
|
6312
|
-
return true;
|
|
6313
|
-
}
|
|
6314
|
-
/** Opens a tab or activates an existing tab with the same key. */
|
|
6315
|
-
async openOrActivateTab(url, title) {
|
|
6316
|
-
if (this.effectiveMode() === 'simple') {
|
|
6317
|
-
await axpNavigateAppPath(this.router, url);
|
|
6318
|
-
return;
|
|
6319
|
-
}
|
|
6320
|
-
const normalizedUrl = normalizeRouterUrl(url);
|
|
6321
|
-
if (!axpIsTabEligibleUrl(normalizedUrl)) {
|
|
6322
|
-
await axpNavigateAppPath(this.router, normalizedUrl);
|
|
6323
|
-
return;
|
|
6324
|
-
}
|
|
6325
|
-
await this.ensureHomeTab();
|
|
6326
|
-
if (this.isHomePageUrl(normalizedUrl)) {
|
|
6327
|
-
await this.activateTabKey(AXP_HOME_TAB_KEY, normalizedUrl);
|
|
6328
|
-
return;
|
|
6329
|
-
}
|
|
6330
|
-
const resolvedKey = axpResolveTabKey(this.router, normalizedUrl);
|
|
6331
|
-
const existing = this.findTabForUrl(normalizedUrl);
|
|
6332
|
-
if (existing) {
|
|
6333
|
-
if (existing.key !== resolvedKey) {
|
|
6334
|
-
this.migrateTabKey(existing.key, resolvedKey);
|
|
6335
|
-
}
|
|
6336
|
-
this.updateTabUrl(resolvedKey, normalizedUrl);
|
|
6337
|
-
await this.activateTabKey(resolvedKey, normalizedUrl);
|
|
6338
|
-
return;
|
|
6339
|
-
}
|
|
6340
|
-
// Navigate first so scoped tab keys (`data.tab`) resolve from the live route snapshot.
|
|
6341
|
-
this.suppressNavigationSync = true;
|
|
6342
|
-
try {
|
|
6343
|
-
await axpNavigateAppPath(this.router, normalizedUrl);
|
|
6344
|
-
const key = axpResolveTabKey(this.router);
|
|
6345
|
-
const postNavExisting = this.findTabForUrl(normalizedUrl);
|
|
6346
|
-
if (postNavExisting) {
|
|
6347
|
-
if (postNavExisting.key !== key) {
|
|
6348
|
-
this.migrateTabKey(postNavExisting.key, key);
|
|
6349
|
-
}
|
|
6350
|
-
this.updateTabUrl(key, normalizedUrl);
|
|
6351
|
-
this.touchTab(key);
|
|
6352
|
-
this.activeTabKey.set(key);
|
|
6353
|
-
this.persistSession();
|
|
6354
|
-
return;
|
|
6355
|
-
}
|
|
6356
|
-
this.insertTab(this.createTabDescriptor(normalizedUrl, title, key));
|
|
6357
|
-
this.activeTabKey.set(key);
|
|
6358
|
-
this.persistSession();
|
|
6359
|
-
}
|
|
6360
|
-
finally {
|
|
6361
|
-
this.suppressNavigationSync = false;
|
|
6362
|
-
}
|
|
6363
|
-
}
|
|
6364
|
-
/**
|
|
6365
|
-
* Activates a tab and navigates to its URL. The previously active page is detached and cached by
|
|
6366
|
-
* the reuse strategy, so its state is preserved; this tab's cached instance is reattached when
|
|
6367
|
-
* available instead of being re-rendered.
|
|
6368
|
-
*
|
|
6369
|
-
* @param suspendDeactivateGuard When true (default for tab switches), unsaved-changes prompts are
|
|
6370
|
-
* skipped so switching tabs keeps a dirty page alive. Closing a tab passes false to keep the prompt.
|
|
6371
|
-
*/
|
|
6372
|
-
async activateTabKey(key, urlOverride, suspendDeactivateGuard = true) {
|
|
6373
|
-
const tab = this.tabs().find((item) => item.key === key);
|
|
6374
|
-
if (!tab) {
|
|
6375
|
-
return;
|
|
6376
|
-
}
|
|
6377
|
-
const targetUrl = urlOverride ?? tab.url;
|
|
6378
|
-
this.touchTab(key);
|
|
6379
|
-
this.activeTabKey.set(key);
|
|
6380
|
-
this.persistSession();
|
|
6381
|
-
const navigate = async () => {
|
|
6382
|
-
this.suppressNavigationSync = true;
|
|
6383
|
-
try {
|
|
6384
|
-
await this.router.navigateByUrl(targetUrl);
|
|
6385
|
-
}
|
|
6386
|
-
finally {
|
|
6387
|
-
this.suppressNavigationSync = false;
|
|
6388
|
-
}
|
|
6389
|
-
};
|
|
6390
|
-
if (suspendDeactivateGuard) {
|
|
6391
|
-
await this.routeCacheRegistry.runWithSuspendedDeactivateGuards(navigate);
|
|
6392
|
-
return;
|
|
6393
|
-
}
|
|
6394
|
-
await navigate();
|
|
6395
|
-
}
|
|
6396
|
-
/** Closes a tab and navigates to a neighbor when the active tab is closed. */
|
|
6397
|
-
async closeTab(key) {
|
|
6398
|
-
await this.closeTabs([key]);
|
|
6399
|
-
}
|
|
6400
|
-
/** Closes every closable tab except the given key (pinned tabs are kept). */
|
|
6401
|
-
async closeOtherTabs(keepKey) {
|
|
6402
|
-
const keys = this.tabs()
|
|
6403
|
-
.filter((tab) => tab.closable && !tab.pinned && tab.key !== keepKey)
|
|
6404
|
-
.map((tab) => tab.key);
|
|
6405
|
-
await this.closeTabs(keys);
|
|
6406
|
-
}
|
|
6407
|
-
/** Closes all closable tabs (pinned tabs are kept). */
|
|
6408
|
-
async closeAllTabs() {
|
|
6409
|
-
const keys = this.tabs()
|
|
6410
|
-
.filter((tab) => tab.closable && !tab.pinned)
|
|
6411
|
-
.map((tab) => tab.key);
|
|
6412
|
-
await this.closeTabs(keys);
|
|
6413
|
-
}
|
|
6414
|
-
/** Toggles whether a tab stays pinned in the strip (home tab is always pinned). */
|
|
6415
|
-
toggleTabPinned(key) {
|
|
6416
|
-
if (key === AXP_HOME_TAB_KEY) {
|
|
6417
|
-
return;
|
|
6418
|
-
}
|
|
6419
|
-
this.tabs.update((items) => {
|
|
6420
|
-
const updated = items.map((tab) => {
|
|
6421
|
-
if (tab.key !== key) {
|
|
6422
|
-
return tab;
|
|
6423
|
-
}
|
|
6424
|
-
const pinned = !tab.pinned;
|
|
6425
|
-
return {
|
|
6426
|
-
...tab,
|
|
6427
|
-
pinned,
|
|
6428
|
-
closable: !pinned,
|
|
6429
|
-
};
|
|
6430
|
-
});
|
|
6431
|
-
return this.normalizeTabOrder(updated);
|
|
6432
|
-
});
|
|
6433
|
-
this.persistSession();
|
|
6434
|
-
}
|
|
6435
|
-
/**
|
|
6436
|
-
* Reorders tabs after drag-and-drop. Home stays first; pinned and unpinned tabs cannot cross groups.
|
|
6437
|
-
*/
|
|
6438
|
-
reorderTabs(previousIndex, currentIndex) {
|
|
6439
|
-
if (previousIndex === currentIndex) {
|
|
6440
|
-
return;
|
|
6441
|
-
}
|
|
6442
|
-
const items = this.tabs();
|
|
6443
|
-
if (previousIndex < 0 || currentIndex < 0 || previousIndex >= items.length || currentIndex >= items.length) {
|
|
6444
|
-
return;
|
|
6445
|
-
}
|
|
6446
|
-
const moving = items[previousIndex];
|
|
6447
|
-
if (moving.key === AXP_HOME_TAB_KEY) {
|
|
6448
|
-
return;
|
|
6449
|
-
}
|
|
6450
|
-
const lastPinnedIndex = this.getLastPinnedTabIndex(items);
|
|
6451
|
-
let targetIndex = currentIndex;
|
|
6452
|
-
if (moving.pinned) {
|
|
6453
|
-
if (targetIndex === 0) {
|
|
6454
|
-
return;
|
|
6455
|
-
}
|
|
6456
|
-
targetIndex = Math.max(1, Math.min(targetIndex, lastPinnedIndex));
|
|
6457
|
-
}
|
|
6458
|
-
else {
|
|
6459
|
-
const firstUnpinnedIndex = lastPinnedIndex + 1;
|
|
6460
|
-
if (firstUnpinnedIndex >= items.length) {
|
|
6461
|
-
return;
|
|
6462
|
-
}
|
|
6463
|
-
targetIndex = Math.max(firstUnpinnedIndex, Math.min(targetIndex, items.length - 1));
|
|
6464
|
-
}
|
|
6465
|
-
if (previousIndex === targetIndex) {
|
|
6466
|
-
return;
|
|
6467
|
-
}
|
|
6468
|
-
const next = [...items];
|
|
6469
|
-
const [removed] = next.splice(previousIndex, 1);
|
|
6470
|
-
next.splice(targetIndex, 0, removed);
|
|
6471
|
-
this.tabs.set(next);
|
|
6472
|
-
this.persistSession();
|
|
6473
|
-
}
|
|
6474
|
-
/** Updates a tab title from the current page chrome (e.g. breadcrumb trail). */
|
|
6475
|
-
syncTabTitle(key, title) {
|
|
6476
|
-
if (this.effectiveMode() === 'simple') {
|
|
6477
|
-
return;
|
|
6478
|
-
}
|
|
6479
|
-
if (!this.tabs().some((tab) => tab.key === key)) {
|
|
6480
|
-
return;
|
|
6481
|
-
}
|
|
6482
|
-
const normalized = title.trim();
|
|
6483
|
-
if (!normalized || this.isGenericHomeTitle(normalized)) {
|
|
6484
|
-
return;
|
|
6485
|
-
}
|
|
6486
|
-
this.updateTabTitle(key, normalized);
|
|
6487
|
-
}
|
|
6488
|
-
/** Updates a tab icon from the current page chrome or menu metadata. */
|
|
6489
|
-
syncTabIcon(key, icon) {
|
|
6490
|
-
if (this.effectiveMode() === 'simple') {
|
|
6491
|
-
return;
|
|
6492
|
-
}
|
|
6493
|
-
if (!this.tabs().some((tab) => tab.key === key)) {
|
|
6494
|
-
return;
|
|
6495
|
-
}
|
|
6496
|
-
const normalized = icon.trim();
|
|
6497
|
-
if (!normalized) {
|
|
6498
|
-
return;
|
|
6499
|
-
}
|
|
6500
|
-
this.updateTabIcon(key, normalized);
|
|
6501
|
-
}
|
|
6502
|
-
//#endregion
|
|
6503
|
-
//#region ---- Bulk Tab Close ----
|
|
6504
|
-
/** Closes multiple tabs in one update; navigates once when the active tab is closed. */
|
|
6505
|
-
async closeTabs(keys) {
|
|
6506
|
-
const uniqueKeys = [...new Set(keys)];
|
|
6507
|
-
if (!uniqueKeys.length) {
|
|
6508
|
-
return;
|
|
6509
|
-
}
|
|
6510
|
-
const closableKeySet = new Set(uniqueKeys.filter((key) => {
|
|
6511
|
-
const tab = this.tabs().find((item) => item.key === key);
|
|
6512
|
-
return !!tab?.closable && !tab.pinned;
|
|
6513
|
-
}));
|
|
6514
|
-
if (!closableKeySet.size) {
|
|
6515
|
-
return;
|
|
6516
|
-
}
|
|
6517
|
-
const activeKey = this.activeTabKey();
|
|
6518
|
-
const backgroundKeys = [...closableKeySet].filter((key) => key !== activeKey);
|
|
6519
|
-
for (const key of backgroundKeys) {
|
|
6520
|
-
if (!(await this.confirmBackgroundTabClose(key))) {
|
|
6521
|
-
closableKeySet.delete(key);
|
|
6522
|
-
}
|
|
6523
|
-
}
|
|
6524
|
-
if (!closableKeySet.size) {
|
|
6525
|
-
return;
|
|
6526
|
-
}
|
|
6527
|
-
const currentTabs = this.tabs();
|
|
6528
|
-
const wasActiveClosed = !!activeKey && closableKeySet.has(activeKey);
|
|
6529
|
-
const closedActiveIndex = wasActiveClosed ? currentTabs.findIndex((item) => item.key === activeKey) : -1;
|
|
6530
|
-
const nextTabs = currentTabs.filter((item) => !closableKeySet.has(item.key));
|
|
6531
|
-
if (wasActiveClosed) {
|
|
6532
|
-
const navigated = await this.navigateAwayBeforeClosingActiveTab(closedActiveIndex, nextTabs);
|
|
6533
|
-
if (!navigated) {
|
|
6534
|
-
return;
|
|
6535
|
-
}
|
|
6536
|
-
}
|
|
6537
|
-
this.tabs.set(nextTabs);
|
|
6538
|
-
this.tabPreviewService.clearPreviews(closableKeySet);
|
|
6539
|
-
this.persistSession();
|
|
6540
|
-
}
|
|
6541
|
-
//#endregion
|
|
6542
|
-
//#region ---- Tab Close Helpers ----
|
|
6543
|
-
/**
|
|
6544
|
-
* Prompts before closing a cached background tab. Uses the page's {@link AXPTabCloseConfirm}
|
|
6545
|
-
* hook when available, otherwise falls back to the tab preview dirty flag.
|
|
6546
|
-
*/
|
|
6547
|
-
async confirmBackgroundTabClose(key) {
|
|
6548
|
-
const instance = this.routeCacheRegistry.getComponentInstance(key);
|
|
6549
|
-
if (instance && typeof instance.onBeforeTabClose === 'function') {
|
|
6550
|
-
return instance.onBeforeTabClose();
|
|
6551
|
-
}
|
|
6552
|
-
return this.unsavedChangesConfirm.confirmIfDirty(this.tabPreviewService.isTabDirty(key), {
|
|
6553
|
-
approveLeaveOnConfirm: false,
|
|
6554
|
-
});
|
|
6555
|
-
}
|
|
6556
|
-
//#endregion
|
|
6557
|
-
//#region ---- Tab Close Navigation ----
|
|
6558
|
-
/**
|
|
6559
|
-
* Navigates away from the active tab before it is removed so canDeactivate runs while the tab is still visible.
|
|
6560
|
-
* Returns false when navigation was cancelled (e.g. unsaved-changes confirmation declined).
|
|
6561
|
-
*/
|
|
6562
|
-
async navigateAwayBeforeClosingActiveTab(closedActiveIndex, remainingTabs) {
|
|
6563
|
-
const fallbackIndex = this.resolveFallbackTabIndex(closedActiveIndex, remainingTabs);
|
|
6564
|
-
const fallback = fallbackIndex >= 0 ? remainingTabs[fallbackIndex] : undefined;
|
|
6565
|
-
let targetKey;
|
|
6566
|
-
let targetUrl;
|
|
6567
|
-
if (fallback) {
|
|
6568
|
-
targetKey = fallback.key;
|
|
6569
|
-
targetUrl = fallback.url;
|
|
6570
|
-
}
|
|
6571
|
-
else {
|
|
6572
|
-
await this.ensureHomeTab();
|
|
6573
|
-
const home = this.tabs().find((item) => item.key === AXP_HOME_TAB_KEY);
|
|
6574
|
-
if (!home) {
|
|
6575
|
-
return false;
|
|
6576
|
-
}
|
|
6577
|
-
targetKey = home.key;
|
|
6578
|
-
targetUrl = home.url;
|
|
6579
|
-
}
|
|
6580
|
-
this.suppressNavigationSync = true;
|
|
6581
|
-
try {
|
|
6582
|
-
const navigated = await this.router.navigateByUrl(targetUrl);
|
|
6583
|
-
if (!navigated) {
|
|
6584
|
-
return false;
|
|
6585
|
-
}
|
|
6586
|
-
}
|
|
6587
|
-
finally {
|
|
6588
|
-
this.suppressNavigationSync = false;
|
|
6589
|
-
}
|
|
6590
|
-
this.activeTabKey.set(targetKey);
|
|
6591
|
-
this.touchTab(targetKey);
|
|
6592
|
-
return true;
|
|
6593
|
-
}
|
|
6594
|
-
/** Prefers the tab to the right after removal, then the tab to the left (browser-like). */
|
|
6595
|
-
resolveFallbackTabIndex(closedIndex, remainingTabs) {
|
|
6596
|
-
if (!remainingTabs.length) {
|
|
6597
|
-
return -1;
|
|
6598
|
-
}
|
|
6599
|
-
if (closedIndex < remainingTabs.length) {
|
|
6600
|
-
return closedIndex;
|
|
6601
|
-
}
|
|
6602
|
-
return closedIndex - 1;
|
|
6603
|
-
}
|
|
6604
|
-
//#endregion
|
|
6605
|
-
//#region ---- Route Cache Sync ----
|
|
6606
|
-
/**
|
|
6607
|
-
* Keeps the route cache registry aligned with the current navigation mode and the set of open
|
|
6608
|
-
* tabs. Cached page instances for closed/evicted tabs are destroyed by the registry.
|
|
6609
|
-
*/
|
|
6610
|
-
bindRouteCacheSync() {
|
|
6611
|
-
effect(() => {
|
|
6612
|
-
this.routeCacheRegistry.setEnabled(this.effectiveMode() === 'tabular');
|
|
6613
|
-
this.routeCacheRegistry.syncOpenKeys(this.tabs().map((tab) => tab.key), this.tabs().map((tab) => tab.url));
|
|
6614
|
-
});
|
|
6615
|
-
}
|
|
6616
|
-
//#endregion
|
|
6617
|
-
//#region ---- Router Sync ----
|
|
6618
|
-
bindRouterEvents() {
|
|
6619
|
-
this.router.events
|
|
6620
|
-
.pipe(filter((event) => event instanceof NavigationEnd), takeUntilDestroyed(this.destroyRef))
|
|
6621
|
-
.subscribe((event) => {
|
|
6622
|
-
void this.handleNavigationEnd(event.urlAfterRedirects);
|
|
6623
|
-
});
|
|
6624
|
-
}
|
|
6625
|
-
async handleNavigationEnd(url) {
|
|
6626
|
-
if (this.suppressNavigationSync) {
|
|
6627
|
-
return;
|
|
6628
|
-
}
|
|
6629
|
-
if (this.effectiveMode() === 'simple') {
|
|
6630
|
-
return;
|
|
6631
|
-
}
|
|
6632
|
-
const normalizedUrl = normalizeRouterUrl(url);
|
|
6633
|
-
if (!axpIsTabEligibleUrl(normalizedUrl)) {
|
|
6634
|
-
return;
|
|
6635
|
-
}
|
|
6636
|
-
await this.ensureHomeTab();
|
|
6637
|
-
if (this.isHomePageUrl(normalizedUrl)) {
|
|
6638
|
-
this.activateHomeTabForUrl(normalizedUrl);
|
|
6639
|
-
return;
|
|
6640
|
-
}
|
|
6641
|
-
const key = axpResolveTabKey(this.router, normalizedUrl);
|
|
6642
|
-
const existing = this.findTabForUrl(normalizedUrl);
|
|
6643
|
-
if (existing) {
|
|
6644
|
-
if (existing.key !== key) {
|
|
6645
|
-
this.migrateTabKey(existing.key, key);
|
|
6646
|
-
}
|
|
6647
|
-
this.updateTabUrl(key, normalizedUrl);
|
|
6648
|
-
this.touchTab(key);
|
|
6649
|
-
this.activeTabKey.set(key);
|
|
6650
|
-
this.persistSession();
|
|
6651
|
-
return;
|
|
6652
|
-
}
|
|
6653
|
-
this.insertTab(this.createTabDescriptor(normalizedUrl, undefined, key));
|
|
6654
|
-
this.activeTabKey.set(key);
|
|
6655
|
-
this.persistSession();
|
|
6656
|
-
}
|
|
6657
|
-
//#endregion
|
|
6658
|
-
//#region ---- Settings ----
|
|
6659
|
-
async loadSettings() {
|
|
6660
|
-
const [mode, maxTabs] = await Promise.all([
|
|
6661
|
-
this.settingsService.get(AXPCommonSettings.NavigationMode),
|
|
6662
|
-
this.settingsService.get(AXPCommonSettings.MaxOpenTabs),
|
|
6663
|
-
]);
|
|
6664
|
-
this.navigationMode.set(mode === 'tabular' ? 'tabular' : 'simple');
|
|
6665
|
-
this.maxOpenTabs.set(this.normalizeMaxTabs(maxTabs));
|
|
6666
|
-
await this.restoreSessionIfNeeded();
|
|
6667
|
-
}
|
|
6668
|
-
bindSettingsChanges() {
|
|
6669
|
-
this.settingsService.onChanged.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((event) => {
|
|
6670
|
-
if (event.keys.includes(AXPCommonSettings.NavigationMode)) {
|
|
6671
|
-
const raw = event.values[AXPCommonSettings.NavigationMode];
|
|
6672
|
-
const newMode = raw === 'tabular' ? 'tabular' : 'simple';
|
|
6673
|
-
if (newMode !== this.navigationMode()) {
|
|
6674
|
-
this.navigationMode.set(newMode);
|
|
6675
|
-
if (this.effectiveMode() === 'simple') {
|
|
6676
|
-
this.clearSession();
|
|
6677
|
-
}
|
|
6678
|
-
else {
|
|
6679
|
-
void this.ensureHomeTab().then(() => this.syncCurrentRouteAsTab());
|
|
6680
|
-
}
|
|
6681
|
-
}
|
|
6682
|
-
}
|
|
6683
|
-
if (event.keys.includes(AXPCommonSettings.MaxOpenTabs)) {
|
|
6684
|
-
this.maxOpenTabs.set(this.normalizeMaxTabs(event.values[AXPCommonSettings.MaxOpenTabs]));
|
|
6685
|
-
this.enforceMaxTabs();
|
|
6686
|
-
this.persistSession();
|
|
6687
|
-
}
|
|
6688
|
-
});
|
|
6689
|
-
}
|
|
6690
|
-
bindSessionChanges() {
|
|
6691
|
-
this.sessionService.user$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
|
|
6692
|
-
void this.restoreSessionIfNeeded();
|
|
6693
|
-
});
|
|
6694
|
-
}
|
|
6695
|
-
//#endregion
|
|
6696
|
-
//#region ---- Tab Management ----
|
|
6697
|
-
async ensureHomeTab() {
|
|
6698
|
-
const homePath = this.resolveHomePath();
|
|
6699
|
-
if (!homePath) {
|
|
6700
|
-
return;
|
|
6701
|
-
}
|
|
6702
|
-
const homeUrl = normalizeRouterUrl(homePath);
|
|
6703
|
-
const homeKey = AXP_HOME_TAB_KEY;
|
|
6704
|
-
const existing = this.tabs().find((tab) => tab.key === homeKey);
|
|
6705
|
-
if (existing) {
|
|
6706
|
-
this.tabs.update((items) => items.map((tab) => tab.key === homeKey
|
|
6707
|
-
? {
|
|
6708
|
-
...tab,
|
|
6709
|
-
url: homeUrl,
|
|
6710
|
-
title: this.isGenericHomeTitle(tab.title) ? this.resolveHomeTitle() : tab.title,
|
|
6711
|
-
icon: this.resolveTabIcon(homeUrl, 'fa-light fa-house'),
|
|
6712
|
-
}
|
|
6713
|
-
: tab));
|
|
6714
|
-
return;
|
|
6715
|
-
}
|
|
6716
|
-
const now = Date.now();
|
|
6717
|
-
const homeTab = {
|
|
6718
|
-
key: homeKey,
|
|
6719
|
-
url: homeUrl,
|
|
6720
|
-
title: this.resolveHomeTitle(),
|
|
6721
|
-
icon: this.resolveTabIcon(homeUrl, 'fa-light fa-house'),
|
|
6722
|
-
pinned: true,
|
|
6723
|
-
closable: false,
|
|
6724
|
-
openedAt: now,
|
|
6725
|
-
lastActivatedAt: now,
|
|
6726
|
-
};
|
|
6727
|
-
this.tabs.update((items) => this.normalizeTabOrder([homeTab, ...items.filter((tab) => tab.key !== homeKey)]));
|
|
6728
|
-
}
|
|
6729
|
-
insertTab(tab) {
|
|
6730
|
-
if (this.isHomePageUrl(tab.url)) {
|
|
6731
|
-
this.activateHomeTabForUrl(tab.url);
|
|
6732
|
-
return;
|
|
6733
|
-
}
|
|
6734
|
-
this.tabs.update((items) => {
|
|
6735
|
-
const withoutDuplicate = items.filter((item) => {
|
|
6736
|
-
if (item.key === tab.key) {
|
|
6737
|
-
return false;
|
|
6738
|
-
}
|
|
6739
|
-
return axpResolveTabKey(this.router, item.url) !== tab.key;
|
|
6740
|
-
});
|
|
6741
|
-
return this.normalizeTabOrder([...withoutDuplicate, tab]);
|
|
6742
|
-
});
|
|
6743
|
-
this.enforceMaxTabs();
|
|
6744
|
-
}
|
|
6745
|
-
/** Home first; preserves session order for the remaining tabs. */
|
|
6746
|
-
ensureHomeTabFirst(items) {
|
|
6747
|
-
const home = items.find((tab) => tab.key === AXP_HOME_TAB_KEY);
|
|
6748
|
-
const others = items.filter((tab) => tab.key !== AXP_HOME_TAB_KEY);
|
|
6749
|
-
return home ? [home, ...others] : items;
|
|
6750
|
-
}
|
|
6751
|
-
getLastPinnedTabIndex(items) {
|
|
6752
|
-
for (let index = items.length - 1; index >= 0; index -= 1) {
|
|
6753
|
-
if (items[index]?.pinned) {
|
|
6754
|
-
return index;
|
|
6755
|
-
}
|
|
6756
|
-
}
|
|
6757
|
-
return 0;
|
|
6758
|
-
}
|
|
6759
|
-
/** Home first, then other pinned tabs, then unpinned tabs (stable by openedAt within each group). */
|
|
6760
|
-
normalizeTabOrder(items) {
|
|
6761
|
-
const home = items.find((tab) => tab.key === AXP_HOME_TAB_KEY);
|
|
6762
|
-
const pinned = items.filter((tab) => tab.key !== AXP_HOME_TAB_KEY && tab.pinned);
|
|
6763
|
-
const unpinned = items.filter((tab) => tab.key !== AXP_HOME_TAB_KEY && !tab.pinned);
|
|
6764
|
-
const byOpenedAt = (a, b) => a.openedAt - b.openedAt;
|
|
6765
|
-
return [...(home ? [home] : []), ...pinned.sort(byOpenedAt), ...unpinned.sort(byOpenedAt)];
|
|
6766
|
-
}
|
|
6767
|
-
enforceMaxTabs() {
|
|
6768
|
-
const limit = this.maxOpenTabs();
|
|
6769
|
-
let current = this.tabs();
|
|
6770
|
-
while (current.length > limit) {
|
|
6771
|
-
const evictionCandidate = [...current]
|
|
6772
|
-
.filter((tab) => tab.closable && !tab.pinned && tab.key !== this.activeTabKey())
|
|
6773
|
-
.filter((tab) => !this.tabPreviewService.isTabDirty(tab.key))
|
|
6774
|
-
.sort((a, b) => a.lastActivatedAt - b.lastActivatedAt)[0];
|
|
6775
|
-
if (!evictionCandidate) {
|
|
6776
|
-
break;
|
|
6777
|
-
}
|
|
6778
|
-
current = current.filter((tab) => tab.key !== evictionCandidate.key);
|
|
6779
|
-
}
|
|
6780
|
-
this.tabs.set(current);
|
|
6781
|
-
}
|
|
6782
|
-
createTabDescriptor(url, title, keyOverride) {
|
|
6783
|
-
const now = Date.now();
|
|
6784
|
-
const key = keyOverride ?? axpResolveTabKey(this.router, url);
|
|
6785
|
-
return {
|
|
6786
|
-
key,
|
|
6787
|
-
url,
|
|
6788
|
-
title: title ?? this.resolveTabTitle(url),
|
|
6789
|
-
icon: this.resolveTabIcon(url),
|
|
6790
|
-
pinned: false,
|
|
6791
|
-
closable: true,
|
|
6792
|
-
openedAt: now,
|
|
6793
|
-
lastActivatedAt: now,
|
|
6794
|
-
};
|
|
6795
|
-
}
|
|
6796
|
-
/** Finds a tab by scoped key or legacy full-path key for the same URL. */
|
|
6797
|
-
findTabForUrl(url) {
|
|
6798
|
-
if (this.isHomePageUrl(url)) {
|
|
6799
|
-
return this.tabs().find((tab) => tab.key === AXP_HOME_TAB_KEY);
|
|
6800
|
-
}
|
|
6801
|
-
const resolvedKey = axpResolveTabKey(this.router, url);
|
|
6802
|
-
return this.tabs().find((tab) => tab.key !== AXP_HOME_TAB_KEY &&
|
|
6803
|
-
(tab.key === resolvedKey || axpResolveTabKey(this.router, tab.url) === resolvedKey));
|
|
6804
|
-
}
|
|
6805
|
-
/** True when the URL is the configured application home page (e.g. dashboard). */
|
|
6806
|
-
isHomePageUrl(url) {
|
|
6807
|
-
const homeUrl = this.resolveHomeUrl();
|
|
6808
|
-
if (!homeUrl) {
|
|
6809
|
-
return false;
|
|
6810
|
-
}
|
|
6811
|
-
return normalizeRouterUrl(url) === homeUrl;
|
|
6812
|
-
}
|
|
6813
|
-
resolveHomeUrl() {
|
|
6814
|
-
const homePath = this.resolveHomePath();
|
|
6815
|
-
if (!homePath) {
|
|
6816
|
-
return null;
|
|
6817
|
-
}
|
|
6818
|
-
return normalizeRouterUrl(homePath);
|
|
6819
|
-
}
|
|
6820
|
-
/** Activates the pinned home tab for a home-page URL instead of opening a duplicate tab. */
|
|
6821
|
-
activateHomeTabForUrl(url) {
|
|
6822
|
-
this.updateTabUrl(AXP_HOME_TAB_KEY, normalizeRouterUrl(url));
|
|
6823
|
-
this.touchTab(AXP_HOME_TAB_KEY);
|
|
6824
|
-
this.activeTabKey.set(AXP_HOME_TAB_KEY);
|
|
6825
|
-
this.persistSession();
|
|
6826
|
-
}
|
|
6827
|
-
/** Drops non-home tabs that duplicate the home page URL after session restore. */
|
|
6828
|
-
removeDuplicateHomeUrlTabs(tabs) {
|
|
6829
|
-
const homeUrl = this.resolveHomeUrl();
|
|
6830
|
-
if (!homeUrl) {
|
|
6831
|
-
return tabs;
|
|
6832
|
-
}
|
|
6833
|
-
return tabs.filter((tab) => tab.key === AXP_HOME_TAB_KEY || normalizeRouterUrl(tab.url) !== homeUrl);
|
|
6834
|
-
}
|
|
6835
|
-
/** Rewrites a tab key when route-scope resolution changes (e.g. profile sub-routes). */
|
|
6836
|
-
migrateTabKey(fromKey, toKey) {
|
|
6837
|
-
if (fromKey === toKey) {
|
|
6838
|
-
return;
|
|
6839
|
-
}
|
|
6840
|
-
if (this.activeTabKey() === fromKey) {
|
|
6841
|
-
this.activeTabKey.set(toKey);
|
|
6842
|
-
}
|
|
6843
|
-
this.tabs.update((items) => {
|
|
6844
|
-
const tab = items.find((item) => item.key === fromKey);
|
|
6845
|
-
if (!tab) {
|
|
6846
|
-
return items;
|
|
6847
|
-
}
|
|
6848
|
-
const without = items.filter((item) => item.key !== fromKey && item.key !== toKey);
|
|
6849
|
-
return this.normalizeTabOrder([...without, { ...tab, key: toKey }]);
|
|
6850
|
-
});
|
|
6851
|
-
}
|
|
6852
|
-
/** Collapses restored tabs that share the same scoped route key. */
|
|
6853
|
-
normalizeRestoredTabKeys(tabs) {
|
|
6854
|
-
const byResolvedKey = new Map();
|
|
6855
|
-
for (const tab of tabs) {
|
|
6856
|
-
if (tab.key === AXP_HOME_TAB_KEY) {
|
|
6857
|
-
byResolvedKey.set(AXP_HOME_TAB_KEY, tab);
|
|
6858
|
-
continue;
|
|
6859
|
-
}
|
|
6860
|
-
const resolvedKey = axpResolveTabKey(this.router, tab.url);
|
|
6861
|
-
const normalized = { ...tab, key: resolvedKey };
|
|
6862
|
-
const existing = byResolvedKey.get(resolvedKey);
|
|
6863
|
-
if (!existing || normalized.lastActivatedAt > existing.lastActivatedAt) {
|
|
6864
|
-
byResolvedKey.set(resolvedKey, normalized);
|
|
6865
|
-
}
|
|
6866
|
-
}
|
|
6867
|
-
return [...byResolvedKey.values()];
|
|
6868
|
-
}
|
|
6869
|
-
updateTabUrl(key, url) {
|
|
6870
|
-
this.tabs.update((items) => items.map((tab) => (tab.key === key ? { ...tab, url } : tab)));
|
|
6871
|
-
}
|
|
6872
|
-
updateTabTitle(key, title) {
|
|
6873
|
-
this.tabs.update((items) => items.map((tab) => (tab.key === key && tab.title !== title ? { ...tab, title } : tab)));
|
|
6874
|
-
this.persistSession();
|
|
6875
|
-
}
|
|
6876
|
-
updateTabIcon(key, icon) {
|
|
6877
|
-
this.tabs.update((items) => items.map((tab) => (tab.key === key && tab.icon !== icon ? { ...tab, icon } : tab)));
|
|
6878
|
-
this.persistSession();
|
|
6879
|
-
}
|
|
6880
|
-
touchTab(key) {
|
|
6881
|
-
const now = Date.now();
|
|
6882
|
-
this.tabs.update((items) => items.map((tab) => (tab.key === key ? { ...tab, lastActivatedAt: now } : tab)));
|
|
6883
|
-
}
|
|
6884
|
-
resolveTabTitle(url, fallback) {
|
|
6885
|
-
const match = axpFindMenuItemByRoute(this.menuService.items(), url);
|
|
6886
|
-
if (match?.item.text) {
|
|
6887
|
-
return match.item.text;
|
|
6888
|
-
}
|
|
6889
|
-
return fallback ?? axpFallbackTabTitle(url);
|
|
6890
|
-
}
|
|
6891
|
-
resolveTabIcon(url, fallback = 'fa-light fa-window') {
|
|
6892
|
-
const match = axpFindMenuItemByRoute(this.menuService.items(), url);
|
|
6893
|
-
if (match?.item.icon?.trim()) {
|
|
6894
|
-
return match.item.icon.trim();
|
|
6895
|
-
}
|
|
6896
|
-
return fallback;
|
|
6897
|
-
}
|
|
6898
|
-
resolveHomePath() {
|
|
6899
|
-
const current = this.homePageService.getCurrent();
|
|
6900
|
-
return current.path ? normalizeRouterUrl(current.path) : '';
|
|
6901
|
-
}
|
|
6902
|
-
resolveHomeTitle() {
|
|
6903
|
-
const current = this.homePageService.getCurrent();
|
|
6904
|
-
return current.title || 'Home';
|
|
6905
|
-
}
|
|
6906
|
-
isGenericHomeTitle(title) {
|
|
6907
|
-
if (title === '@general:terms.interface.navigation.home') {
|
|
6908
|
-
return true;
|
|
6909
|
-
}
|
|
6910
|
-
const homeTitle = this.resolveHomeTitle().trim();
|
|
6911
|
-
return title === homeTitle;
|
|
6912
|
-
}
|
|
6913
|
-
async syncCurrentRouteAsTab() {
|
|
6914
|
-
if (this.effectiveMode() === 'simple') {
|
|
6915
|
-
return;
|
|
6916
|
-
}
|
|
6917
|
-
await this.ensureHomeTab();
|
|
6918
|
-
await this.handleNavigationEnd(this.router.url);
|
|
6919
|
-
}
|
|
6920
|
-
normalizeMaxTabs(value) {
|
|
6921
|
-
const parsed = Number(value);
|
|
6922
|
-
if (!Number.isFinite(parsed)) {
|
|
6923
|
-
return 5;
|
|
6924
|
-
}
|
|
6925
|
-
return Math.min(20, Math.max(2, Math.round(parsed)));
|
|
6926
|
-
}
|
|
6927
|
-
//#endregion
|
|
6928
|
-
//#region ---- Session Persistence ----
|
|
6929
|
-
sessionStorageKey() {
|
|
6930
|
-
const userId = this.sessionService.user?.id;
|
|
6931
|
-
if (!userId) {
|
|
6932
|
-
return null;
|
|
6933
|
-
}
|
|
6934
|
-
return `${SESSION_STORAGE_PREFIX}(${userId})`;
|
|
6935
|
-
}
|
|
6936
|
-
persistSession() {
|
|
6937
|
-
const key = this.sessionStorageKey();
|
|
6938
|
-
if (!key || this.effectiveMode() === 'simple') {
|
|
6939
|
-
return;
|
|
6940
|
-
}
|
|
6941
|
-
const payload = {
|
|
6942
|
-
tabs: cloneDeep(this.tabs()),
|
|
6943
|
-
activeTabKey: this.activeTabKey(),
|
|
6944
|
-
};
|
|
6945
|
-
sessionStorage.setItem(key, JSON.stringify(payload));
|
|
6946
|
-
}
|
|
6947
|
-
clearSession() {
|
|
6948
|
-
const key = this.sessionStorageKey();
|
|
6949
|
-
if (key) {
|
|
6950
|
-
sessionStorage.removeItem(key);
|
|
6951
|
-
}
|
|
6952
|
-
this.tabs.set([]);
|
|
6953
|
-
this.activeTabKey.set(null);
|
|
6954
|
-
this.tabPreviewService.clearAll();
|
|
6955
|
-
}
|
|
6956
|
-
async restoreSessionIfNeeded() {
|
|
6957
|
-
if (this.effectiveMode() === 'simple') {
|
|
6958
|
-
return;
|
|
6959
|
-
}
|
|
6960
|
-
const storageKey = this.sessionStorageKey();
|
|
6961
|
-
if (!storageKey) {
|
|
6962
|
-
await this.ensureHomeTab();
|
|
6963
|
-
await this.syncCurrentRouteAsTab();
|
|
6964
|
-
return;
|
|
6965
|
-
}
|
|
6966
|
-
const raw = sessionStorage.getItem(storageKey);
|
|
6967
|
-
if (!raw) {
|
|
6968
|
-
await this.ensureHomeTab();
|
|
6969
|
-
await this.syncCurrentRouteAsTab();
|
|
6970
|
-
return;
|
|
6971
|
-
}
|
|
6972
|
-
try {
|
|
6973
|
-
const parsed = JSON.parse(raw);
|
|
6974
|
-
const restoredTabs = this.removeDuplicateHomeUrlTabs(this.normalizeRestoredTabKeys(defaultTo(parsed.tabs, []).filter((tab) => tab?.key && tab?.url)));
|
|
6975
|
-
this.tabs.set(this.ensureHomeTabFirst(restoredTabs));
|
|
6976
|
-
await this.ensureHomeTab();
|
|
6977
|
-
const activeKey = parsed.activeTabKey ?? this.tabs()[this.tabs().length - 1]?.key ?? AXP_HOME_TAB_KEY;
|
|
6978
|
-
this.activeTabKey.set(activeKey);
|
|
6979
|
-
const activeTab = this.tabs().find((tab) => tab.key === activeKey);
|
|
6980
|
-
if (activeTab && normalizeRouterUrl(this.router.url) !== normalizeRouterUrl(activeTab.url)) {
|
|
6981
|
-
this.suppressNavigationSync = true;
|
|
6982
|
-
try {
|
|
6983
|
-
await this.router.navigateByUrl(activeTab.url);
|
|
6984
|
-
}
|
|
6985
|
-
finally {
|
|
6986
|
-
this.suppressNavigationSync = false;
|
|
6987
|
-
}
|
|
6988
|
-
}
|
|
6989
|
-
}
|
|
6990
|
-
catch {
|
|
6991
|
-
await this.ensureHomeTab();
|
|
6992
|
-
await this.syncCurrentRouteAsTab();
|
|
6993
|
-
}
|
|
6994
|
-
}
|
|
6995
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTabNavigationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
6996
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTabNavigationService, providedIn: 'root' }); }
|
|
6997
|
-
}
|
|
6998
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTabNavigationService, decorators: [{
|
|
6999
|
-
type: Injectable,
|
|
7000
|
-
args: [{
|
|
7001
|
-
providedIn: 'root',
|
|
7002
|
-
}]
|
|
7003
|
-
}], ctorParameters: () => [] });
|
|
7004
|
-
|
|
7005
|
-
//TODO Loading, Redirect, Drawer, Show toast
|
|
7006
|
-
const AXPRedirectEvent = createWorkFlowEvent('Redirect Event Fired');
|
|
7007
|
-
const AXPRefreshEvent = createWorkFlowEvent('Refresh Event Fired');
|
|
7008
|
-
const AXPReloadEvent = createWorkFlowEvent('Reload Event Fired');
|
|
7009
|
-
class AXPWorkflowNavigateAction extends AXPWorkflowAction {
|
|
7010
|
-
constructor() {
|
|
7011
|
-
super(...arguments);
|
|
7012
|
-
this.router = inject(Router);
|
|
7013
|
-
this.tabNavigation = inject(AXPTabNavigationService);
|
|
7014
|
-
}
|
|
7015
|
-
async execute(context) {
|
|
7016
|
-
const payload = context.getVariable('payload');
|
|
7017
|
-
if (Array.isArray(payload.commands)) {
|
|
7018
|
-
await this.router.navigate(payload.commands, payload.extras);
|
|
7019
|
-
return;
|
|
7020
|
-
}
|
|
7021
|
-
const path = payload.commands;
|
|
7022
|
-
if (!path) {
|
|
7023
|
-
return;
|
|
7024
|
-
}
|
|
7025
|
-
if (path.toLowerCase().startsWith('http') || payload.target === 'blank') {
|
|
7026
|
-
window.open(path, '_blank');
|
|
7027
|
-
return;
|
|
7028
|
-
}
|
|
7029
|
-
await this.tabNavigation.navigate(path, payload.extras);
|
|
7030
|
-
}
|
|
7031
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowNavigateAction, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
7032
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowNavigateAction }); }
|
|
7033
|
-
}
|
|
7034
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowNavigateAction, decorators: [{
|
|
7035
|
-
type: Injectable
|
|
5810
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowNavigateAction, decorators: [{
|
|
5811
|
+
type: Injectable
|
|
7036
5812
|
}] });
|
|
7037
5813
|
class AXPDialogConfirmAction extends AXPWorkflowAction {
|
|
7038
5814
|
constructor() {
|
|
@@ -7093,7 +5869,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
7093
5869
|
class AXPWorkflowRouterNavigateAction extends AXPWorkflowAction {
|
|
7094
5870
|
constructor() {
|
|
7095
5871
|
super(...arguments);
|
|
7096
|
-
this.
|
|
5872
|
+
this.router = inject(Router);
|
|
7097
5873
|
}
|
|
7098
5874
|
async execute(context) {
|
|
7099
5875
|
const command = context.getVariable();
|
|
@@ -7106,7 +5882,7 @@ class AXPWorkflowRouterNavigateAction extends AXPWorkflowAction {
|
|
|
7106
5882
|
window.open(path, '_blank');
|
|
7107
5883
|
}
|
|
7108
5884
|
else {
|
|
7109
|
-
await this.
|
|
5885
|
+
await axpNavigateAppPath(this.router, path, command.options.extras);
|
|
7110
5886
|
}
|
|
7111
5887
|
}
|
|
7112
5888
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowRouterNavigateAction, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
@@ -7180,14 +5956,13 @@ class AXPCommonModule {
|
|
|
7180
5956
|
/**
|
|
7181
5957
|
* @ignore
|
|
7182
5958
|
*/
|
|
7183
|
-
constructor(instances, _keyboardShortcutsHelp
|
|
7184
|
-
formatterRegistry.register(AXPTranslateFormatter);
|
|
5959
|
+
constructor(instances, _keyboardShortcutsHelp) {
|
|
7185
5960
|
instances.forEach((f) => {
|
|
7186
5961
|
f();
|
|
7187
5962
|
});
|
|
7188
5963
|
}
|
|
7189
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPCommonModule, deps: [{ token: 'AXPCommonModuleFactory' }, { token: AXPKeyboardShortcutsHelpService }
|
|
7190
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: AXPCommonModule, imports: [
|
|
5964
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPCommonModule, deps: [{ token: 'AXPCommonModuleFactory' }, { token: AXPKeyboardShortcutsHelpService }], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
5965
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: AXPCommonModule, imports: [i2$1.AXPComponentSlotModule, i3$1.AXPWorkflowModule, AXPopupModule,
|
|
7191
5966
|
AXDateTimeModule,
|
|
7192
5967
|
AXToastModule,
|
|
7193
5968
|
AXPModuleManifestModule], exports: [RouterModule] }); }
|
|
@@ -7234,7 +6009,7 @@ class AXPCommonModule {
|
|
|
7234
6009
|
provide: AXP_SETTING_DEFINITION_PROVIDER,
|
|
7235
6010
|
useFactory: async () => {
|
|
7236
6011
|
const injector = inject(Injector);
|
|
7237
|
-
const provider = (await import('./acorex-platform-common-common-settings.provider-
|
|
6012
|
+
const provider = (await import('./acorex-platform-common-common-settings.provider-Ytey9uhY.mjs')).AXPCommonSettingProvider;
|
|
7238
6013
|
return new provider(injector);
|
|
7239
6014
|
},
|
|
7240
6015
|
multi: true,
|
|
@@ -7335,7 +6110,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
7335
6110
|
provide: AXP_SETTING_DEFINITION_PROVIDER,
|
|
7336
6111
|
useFactory: async () => {
|
|
7337
6112
|
const injector = inject(Injector);
|
|
7338
|
-
const provider = (await import('./acorex-platform-common-common-settings.provider-
|
|
6113
|
+
const provider = (await import('./acorex-platform-common-common-settings.provider-Ytey9uhY.mjs')).AXPCommonSettingProvider;
|
|
7339
6114
|
return new provider(injector);
|
|
7340
6115
|
},
|
|
7341
6116
|
multi: true,
|
|
@@ -7345,7 +6120,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
7345
6120
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
7346
6121
|
type: Inject,
|
|
7347
6122
|
args: ['AXPCommonModuleFactory']
|
|
7348
|
-
}] }, { type: AXPKeyboardShortcutsHelpService }
|
|
6123
|
+
}] }, { type: AXPKeyboardShortcutsHelpService }] });
|
|
7349
6124
|
|
|
7350
6125
|
const ENVIRONMENT = new InjectionToken('ENVIRONMENT');
|
|
7351
6126
|
const AXP_PLATFORM_CONFIG_TOKEN = new InjectionToken('AXP_PLATFORM_CONFIG_TOKEN', {
|
|
@@ -7560,6 +6335,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
7560
6335
|
type: Injectable
|
|
7561
6336
|
}] });
|
|
7562
6337
|
|
|
6338
|
+
/**
|
|
6339
|
+
* Status Definition Types
|
|
6340
|
+
* Defines the structure for status definitions and their transitions
|
|
6341
|
+
*/
|
|
6342
|
+
/**
|
|
6343
|
+
* Abstract class for status providers
|
|
6344
|
+
* Similar to AXPTaskBadgeProvider pattern
|
|
6345
|
+
*/
|
|
6346
|
+
class AXPStatusProvider {
|
|
6347
|
+
}
|
|
6348
|
+
//#endregion
|
|
6349
|
+
|
|
7563
6350
|
//#region ---- Injection Token ----
|
|
7564
6351
|
/**
|
|
7565
6352
|
* Injection token for status providers
|
|
@@ -7771,8 +6558,356 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
7771
6558
|
args: [{ providedIn: 'root' }]
|
|
7772
6559
|
}] });
|
|
7773
6560
|
|
|
7774
|
-
|
|
7775
|
-
|
|
6561
|
+
/**
|
|
6562
|
+
* Standard system status types
|
|
6563
|
+
* These are the predefined status values used across the platform
|
|
6564
|
+
*/
|
|
6565
|
+
var AXPSystemStatusType;
|
|
6566
|
+
(function (AXPSystemStatusType) {
|
|
6567
|
+
AXPSystemStatusType["Todo"] = "todo";
|
|
6568
|
+
AXPSystemStatusType["InProgress"] = "in-progress";
|
|
6569
|
+
AXPSystemStatusType["InReview"] = "in-review";
|
|
6570
|
+
AXPSystemStatusType["Open"] = "open";
|
|
6571
|
+
AXPSystemStatusType["Closed"] = "closed";
|
|
6572
|
+
AXPSystemStatusType["Blocked"] = "blocked";
|
|
6573
|
+
AXPSystemStatusType["Done"] = "done";
|
|
6574
|
+
AXPSystemStatusType["Cancelled"] = "cancelled";
|
|
6575
|
+
AXPSystemStatusType["Draft"] = "draft";
|
|
6576
|
+
AXPSystemStatusType["PendingReview"] = "pending-review";
|
|
6577
|
+
AXPSystemStatusType["WaitingSignOff"] = "waiting-sign-off";
|
|
6578
|
+
AXPSystemStatusType["WaitingApproval"] = "waiting-approval";
|
|
6579
|
+
AXPSystemStatusType["Approved"] = "approved";
|
|
6580
|
+
AXPSystemStatusType["Published"] = "published";
|
|
6581
|
+
AXPSystemStatusType["Archived"] = "archived";
|
|
6582
|
+
AXPSystemStatusType["Rejected"] = "rejected";
|
|
6583
|
+
AXPSystemStatusType["Active"] = "active";
|
|
6584
|
+
AXPSystemStatusType["Inactive"] = "inactive";
|
|
6585
|
+
AXPSystemStatusType["Pending"] = "pending";
|
|
6586
|
+
AXPSystemStatusType["Completed"] = "completed";
|
|
6587
|
+
AXPSystemStatusType["Suspended"] = "suspended";
|
|
6588
|
+
AXPSystemStatusType["Failed"] = "failed";
|
|
6589
|
+
AXPSystemStatusType["Review"] = "review";
|
|
6590
|
+
AXPSystemStatusType["Expired"] = "expired";
|
|
6591
|
+
})(AXPSystemStatusType || (AXPSystemStatusType = {}));
|
|
6592
|
+
const i18n = (key) => `@general:statuses.${key}`;
|
|
6593
|
+
/**
|
|
6594
|
+
* Standard system status definitions
|
|
6595
|
+
* Provides consistent status definitions across the platform
|
|
6596
|
+
* Uses AXPStatusDefinition interface from status-definition.types.ts
|
|
6597
|
+
*/
|
|
6598
|
+
const AXPSystemStatuses = Object.freeze({
|
|
6599
|
+
Open: {
|
|
6600
|
+
name: AXPSystemStatusType.Open,
|
|
6601
|
+
title: i18n('open.title'),
|
|
6602
|
+
icon: 'fa-light fa-circle',
|
|
6603
|
+
color: 'secondary',
|
|
6604
|
+
description: i18n('open.description'),
|
|
6605
|
+
order: 1,
|
|
6606
|
+
isInitial: true,
|
|
6607
|
+
isFinal: false,
|
|
6608
|
+
},
|
|
6609
|
+
Closed: {
|
|
6610
|
+
name: AXPSystemStatusType.Closed,
|
|
6611
|
+
title: i18n('closed.title'),
|
|
6612
|
+
icon: 'fa-light fa-circle',
|
|
6613
|
+
color: 'danger',
|
|
6614
|
+
description: i18n('closed.description'),
|
|
6615
|
+
order: 2,
|
|
6616
|
+
isInitial: false,
|
|
6617
|
+
isFinal: true,
|
|
6618
|
+
},
|
|
6619
|
+
Todo: {
|
|
6620
|
+
name: AXPSystemStatusType.Todo,
|
|
6621
|
+
title: i18n('todo.title'),
|
|
6622
|
+
icon: 'fa-light fa-circle',
|
|
6623
|
+
color: 'secondary',
|
|
6624
|
+
description: i18n('todo.description'),
|
|
6625
|
+
order: 1,
|
|
6626
|
+
isInitial: true,
|
|
6627
|
+
isFinal: false,
|
|
6628
|
+
},
|
|
6629
|
+
InProgress: {
|
|
6630
|
+
name: AXPSystemStatusType.InProgress,
|
|
6631
|
+
title: i18n('in-progress.title'),
|
|
6632
|
+
icon: 'fa-light fa-play',
|
|
6633
|
+
color: 'info',
|
|
6634
|
+
description: i18n('in-progress.description'),
|
|
6635
|
+
order: 2,
|
|
6636
|
+
isInitial: false,
|
|
6637
|
+
isFinal: false,
|
|
6638
|
+
},
|
|
6639
|
+
InReview: {
|
|
6640
|
+
name: AXPSystemStatusType.InReview,
|
|
6641
|
+
title: i18n('in-review.title'),
|
|
6642
|
+
icon: 'fa-light fa-eye',
|
|
6643
|
+
color: 'info',
|
|
6644
|
+
description: i18n('in-review.description'),
|
|
6645
|
+
order: 3,
|
|
6646
|
+
isInitial: false,
|
|
6647
|
+
isFinal: false,
|
|
6648
|
+
},
|
|
6649
|
+
Blocked: {
|
|
6650
|
+
name: AXPSystemStatusType.Blocked,
|
|
6651
|
+
title: i18n('blocked.title'),
|
|
6652
|
+
icon: 'fa-light fa-ban',
|
|
6653
|
+
color: 'danger',
|
|
6654
|
+
description: i18n('blocked.description'),
|
|
6655
|
+
order: 4,
|
|
6656
|
+
isInitial: false,
|
|
6657
|
+
isFinal: false,
|
|
6658
|
+
},
|
|
6659
|
+
Done: {
|
|
6660
|
+
name: AXPSystemStatusType.Done,
|
|
6661
|
+
title: i18n('done.title'),
|
|
6662
|
+
icon: 'fa-light fa-check-circle',
|
|
6663
|
+
color: 'success',
|
|
6664
|
+
description: i18n('done.description'),
|
|
6665
|
+
order: 5,
|
|
6666
|
+
isInitial: false,
|
|
6667
|
+
isFinal: true,
|
|
6668
|
+
},
|
|
6669
|
+
Cancelled: {
|
|
6670
|
+
name: AXPSystemStatusType.Cancelled,
|
|
6671
|
+
title: i18n('cancelled.title'),
|
|
6672
|
+
icon: 'fa-light fa-times-circle',
|
|
6673
|
+
color: 'neutral',
|
|
6674
|
+
description: i18n('cancelled.description'),
|
|
6675
|
+
order: 6,
|
|
6676
|
+
isInitial: false,
|
|
6677
|
+
isFinal: true,
|
|
6678
|
+
},
|
|
6679
|
+
Draft: {
|
|
6680
|
+
name: AXPSystemStatusType.Draft,
|
|
6681
|
+
title: i18n('draft.title'),
|
|
6682
|
+
icon: 'fa-light fa-file-pen',
|
|
6683
|
+
color: 'neutral',
|
|
6684
|
+
description: i18n('draft.description'),
|
|
6685
|
+
order: 1,
|
|
6686
|
+
isInitial: true,
|
|
6687
|
+
isFinal: false,
|
|
6688
|
+
},
|
|
6689
|
+
PendingReview: {
|
|
6690
|
+
name: AXPSystemStatusType.PendingReview,
|
|
6691
|
+
title: i18n('pending-review.title'),
|
|
6692
|
+
icon: 'fa-light fa-clock',
|
|
6693
|
+
color: 'warning',
|
|
6694
|
+
description: i18n('pending-review.description'),
|
|
6695
|
+
order: 2,
|
|
6696
|
+
isInitial: false,
|
|
6697
|
+
isFinal: false,
|
|
6698
|
+
},
|
|
6699
|
+
WaitingSignOff: {
|
|
6700
|
+
name: AXPSystemStatusType.WaitingSignOff,
|
|
6701
|
+
title: i18n('waiting-sign-off.title'),
|
|
6702
|
+
icon: 'fa-light fa-clock',
|
|
6703
|
+
color: 'warning',
|
|
6704
|
+
description: i18n('waiting-sign-off.description'),
|
|
6705
|
+
order: 3,
|
|
6706
|
+
isInitial: false,
|
|
6707
|
+
isFinal: false,
|
|
6708
|
+
},
|
|
6709
|
+
WaitingApproval: {
|
|
6710
|
+
name: AXPSystemStatusType.WaitingApproval,
|
|
6711
|
+
title: i18n('waiting-approval.title'),
|
|
6712
|
+
icon: 'fa-light fa-clock',
|
|
6713
|
+
color: 'warning',
|
|
6714
|
+
description: i18n('waiting-approval.description'),
|
|
6715
|
+
order: 1,
|
|
6716
|
+
isInitial: true,
|
|
6717
|
+
isFinal: false,
|
|
6718
|
+
},
|
|
6719
|
+
Approved: {
|
|
6720
|
+
name: AXPSystemStatusType.Approved,
|
|
6721
|
+
title: i18n('approved.title'),
|
|
6722
|
+
icon: 'fa-light fa-check-circle',
|
|
6723
|
+
color: 'success',
|
|
6724
|
+
description: i18n('approved.description'),
|
|
6725
|
+
order: 3,
|
|
6726
|
+
isInitial: false,
|
|
6727
|
+
isFinal: false,
|
|
6728
|
+
},
|
|
6729
|
+
Published: {
|
|
6730
|
+
name: AXPSystemStatusType.Published,
|
|
6731
|
+
title: i18n('published.title'),
|
|
6732
|
+
icon: 'fa-light fa-globe',
|
|
6733
|
+
color: 'primary',
|
|
6734
|
+
description: i18n('published.description'),
|
|
6735
|
+
order: 4,
|
|
6736
|
+
isInitial: false,
|
|
6737
|
+
isFinal: false,
|
|
6738
|
+
},
|
|
6739
|
+
Archived: {
|
|
6740
|
+
name: AXPSystemStatusType.Archived,
|
|
6741
|
+
title: i18n('archived.title'),
|
|
6742
|
+
icon: 'fa-light fa-archive',
|
|
6743
|
+
color: 'danger',
|
|
6744
|
+
description: i18n('archived.description'),
|
|
6745
|
+
order: 5,
|
|
6746
|
+
isInitial: false,
|
|
6747
|
+
isFinal: true,
|
|
6748
|
+
},
|
|
6749
|
+
Rejected: {
|
|
6750
|
+
name: AXPSystemStatusType.Rejected,
|
|
6751
|
+
title: i18n('rejected.title'),
|
|
6752
|
+
icon: 'fa-light fa-times-circle',
|
|
6753
|
+
color: 'danger',
|
|
6754
|
+
description: i18n('rejected.description'),
|
|
6755
|
+
order: 6,
|
|
6756
|
+
isInitial: false,
|
|
6757
|
+
isFinal: true,
|
|
6758
|
+
},
|
|
6759
|
+
Active: {
|
|
6760
|
+
name: AXPSystemStatusType.Active,
|
|
6761
|
+
title: i18n('active.title'),
|
|
6762
|
+
icon: 'fa-light fa-check-circle',
|
|
6763
|
+
color: 'success',
|
|
6764
|
+
description: i18n('active.description'),
|
|
6765
|
+
order: 1,
|
|
6766
|
+
isInitial: false,
|
|
6767
|
+
isFinal: false,
|
|
6768
|
+
},
|
|
6769
|
+
Inactive: {
|
|
6770
|
+
name: AXPSystemStatusType.Inactive,
|
|
6771
|
+
title: i18n('inactive.title'),
|
|
6772
|
+
icon: 'fa-light fa-circle',
|
|
6773
|
+
color: 'secondary',
|
|
6774
|
+
description: i18n('inactive.description'),
|
|
6775
|
+
order: 2,
|
|
6776
|
+
isInitial: false,
|
|
6777
|
+
isFinal: false,
|
|
6778
|
+
},
|
|
6779
|
+
Pending: {
|
|
6780
|
+
name: AXPSystemStatusType.Pending,
|
|
6781
|
+
title: i18n('pending.title'),
|
|
6782
|
+
icon: 'fa-light fa-clock',
|
|
6783
|
+
color: 'warning',
|
|
6784
|
+
description: i18n('pending.description'),
|
|
6785
|
+
order: 1,
|
|
6786
|
+
isInitial: true,
|
|
6787
|
+
isFinal: false,
|
|
6788
|
+
},
|
|
6789
|
+
Completed: {
|
|
6790
|
+
name: AXPSystemStatusType.Completed,
|
|
6791
|
+
title: i18n('completed.title'),
|
|
6792
|
+
icon: 'fa-light fa-check-circle',
|
|
6793
|
+
color: 'success',
|
|
6794
|
+
description: i18n('completed.description'),
|
|
6795
|
+
order: 10,
|
|
6796
|
+
isInitial: false,
|
|
6797
|
+
isFinal: true,
|
|
6798
|
+
},
|
|
6799
|
+
Suspended: {
|
|
6800
|
+
name: AXPSystemStatusType.Suspended,
|
|
6801
|
+
title: i18n('suspended.title'),
|
|
6802
|
+
icon: 'fa-light fa-pause-circle',
|
|
6803
|
+
color: 'neutral',
|
|
6804
|
+
description: i18n('suspended.description'),
|
|
6805
|
+
order: 5,
|
|
6806
|
+
isInitial: false,
|
|
6807
|
+
isFinal: false,
|
|
6808
|
+
},
|
|
6809
|
+
Failed: {
|
|
6810
|
+
name: AXPSystemStatusType.Failed,
|
|
6811
|
+
title: i18n('failed.title'),
|
|
6812
|
+
icon: 'fa-light fa-xmark-circle',
|
|
6813
|
+
color: 'danger',
|
|
6814
|
+
description: i18n('failed.description'),
|
|
6815
|
+
order: 10,
|
|
6816
|
+
isInitial: false,
|
|
6817
|
+
isFinal: true,
|
|
6818
|
+
},
|
|
6819
|
+
Review: {
|
|
6820
|
+
name: AXPSystemStatusType.Review,
|
|
6821
|
+
title: i18n('review.title'),
|
|
6822
|
+
icon: 'fa-light fa-eye',
|
|
6823
|
+
color: 'info',
|
|
6824
|
+
description: i18n('review.description'),
|
|
6825
|
+
order: 3,
|
|
6826
|
+
},
|
|
6827
|
+
Expired: {
|
|
6828
|
+
name: AXPSystemStatusType.Expired,
|
|
6829
|
+
title: i18n('expired.title'),
|
|
6830
|
+
icon: 'fa-light fa-clock',
|
|
6831
|
+
color: 'danger',
|
|
6832
|
+
description: i18n('expired.description'),
|
|
6833
|
+
order: 10,
|
|
6834
|
+
isInitial: false,
|
|
6835
|
+
isFinal: true,
|
|
6836
|
+
},
|
|
6837
|
+
});
|
|
6838
|
+
/**
|
|
6839
|
+
* Get system status definition by type
|
|
6840
|
+
* @param type - Status type
|
|
6841
|
+
* @returns System status definition or undefined
|
|
6842
|
+
*/
|
|
6843
|
+
function getSystemStatus(type) {
|
|
6844
|
+
return Object.values(AXPSystemStatuses).find(status => status.name === type);
|
|
6845
|
+
}
|
|
6846
|
+
/**
|
|
6847
|
+
* Resolves the visual appearance (color and icon) for status
|
|
6848
|
+
* using the system statuses from the core module.
|
|
6849
|
+
* @param statusType - Status type string
|
|
6850
|
+
* @returns Color and icon for the status
|
|
6851
|
+
*/
|
|
6852
|
+
function resolveStatusLook(statusType) {
|
|
6853
|
+
// Try to get system status first
|
|
6854
|
+
const systemStatusType = statusType;
|
|
6855
|
+
if (systemStatusType) {
|
|
6856
|
+
const systemStatus = getSystemStatus(systemStatusType);
|
|
6857
|
+
if (systemStatus) {
|
|
6858
|
+
// Convert hex color to semantic color type if needed
|
|
6859
|
+
// For now, return as-is and let the UI handle it
|
|
6860
|
+
return {
|
|
6861
|
+
color: systemStatus.color,
|
|
6862
|
+
icon: systemStatus.icon || ''
|
|
6863
|
+
};
|
|
6864
|
+
}
|
|
6865
|
+
}
|
|
6866
|
+
// Fallback for unknown statuses
|
|
6867
|
+
return {
|
|
6868
|
+
color: 'secondary',
|
|
6869
|
+
icon: ''
|
|
6870
|
+
};
|
|
6871
|
+
}
|
|
6872
|
+
/**
|
|
6873
|
+
* Resolves the complete status information (title, description, icon, color) for a status
|
|
6874
|
+
* using the system statuses from the core module.
|
|
6875
|
+
* @param statusType - Status type string
|
|
6876
|
+
* @returns Complete status information as AXPStatusDefinition
|
|
6877
|
+
*/
|
|
6878
|
+
function getStatusInfo(statusType) {
|
|
6879
|
+
const systemStatusType = statusType;
|
|
6880
|
+
if (systemStatusType) {
|
|
6881
|
+
const systemStatus = getSystemStatus(systemStatusType);
|
|
6882
|
+
if (systemStatus) {
|
|
6883
|
+
return systemStatus;
|
|
6884
|
+
}
|
|
6885
|
+
}
|
|
6886
|
+
// Fallback for unknown statuses
|
|
6887
|
+
return {
|
|
6888
|
+
name: statusType,
|
|
6889
|
+
title: statusType,
|
|
6890
|
+
description: `Status: ${statusType}`,
|
|
6891
|
+
icon: '',
|
|
6892
|
+
color: 'secondary'
|
|
6893
|
+
};
|
|
6894
|
+
}
|
|
6895
|
+
/**
|
|
6896
|
+
* Get system status definition directly (no conversion needed since it's already AXPStatusDefinition)
|
|
6897
|
+
* @param statusType - Status type
|
|
6898
|
+
* @param overrides - Optional overrides for the status definition
|
|
6899
|
+
* @returns AXPStatusDefinition
|
|
6900
|
+
*/
|
|
6901
|
+
function systemStatusToDefinition(statusType, overrides) {
|
|
6902
|
+
const systemStatus = getSystemStatus(statusType);
|
|
6903
|
+
if (!systemStatus) {
|
|
6904
|
+
throw new Error(`System status ${statusType} not found`);
|
|
6905
|
+
}
|
|
6906
|
+
return {
|
|
6907
|
+
...systemStatus,
|
|
6908
|
+
...overrides,
|
|
6909
|
+
};
|
|
6910
|
+
}
|
|
7776
6911
|
|
|
7777
6912
|
class AXPClipBoardService {
|
|
7778
6913
|
constructor() {
|
|
@@ -8455,5 +7590,5 @@ class AXPVersioningService {
|
|
|
8455
7590
|
* Generated bundle index. Do not edit.
|
|
8456
7591
|
*/
|
|
8457
7592
|
|
|
8458
|
-
export { ALL_DEFAULT_OPERATORS, AXMWorkflowErrorHandler, AXPAppVersionService, AXPBackgroundOperationCompletionService, AXPBackgroundOperationEntityStoreService, AXPBackgroundOperationHandler, AXPBackgroundOperationNotificationService, AXPBackgroundOperationProvider, AXPBackgroundOperationRootConfig, AXPBackgroundOperationService, AXPBackgroundOperationSlotComponent, AXPCleanNestedFilters, AXPClipBoardService, AXPCommonModule, AXPCommonSettings, AXPCustomOperatorService, AXPCustomOperatorServiceImpl, AXPDataProvider, AXPDebugService, AXPDefaultMultiLanguageConfigService, AXPDialogConfirmAction, AXPErrorHandlerRegistryService, AXPExportService, AXPFileActionsService, AXPFileStorageEvaluatorScopeProvider, AXPFileStorageService, AXPFileTypeProviderService, AXPFilterOperatorMiddlewareService, AXPFilterOperatorMiddlewareServiceImpl, AXPFooterTextSlotComponent, AXPGlobalErrorHandler, AXPHomePageModule, AXPHomePageService, AXPKeyboardShortcutsHelpComponent, AXPKeyboardShortcutsHelpService, AXPLockService, AXPMenuItemsDataSourceDefinition, AXPMenuKeyboardShortcutService, AXPMenuMiddlewareRegistry, AXPMenuProviderService, AXPMenuRouteAccessService, AXPMenuRouteGuard, AXPMenuSearchDefinitionProvider, AXPMenuSearchProvider, AXPMenuService, AXPMenuVisibilityService, AXPNavBarSlotComponent, AXPNavigateWorkflow, AXPNotFoundCatchAllRoute, AXPNotFoundError, AXPOutputContractTranscriptSegmentService, AXPPlatformCommonDataSourceKeys, AXPPlatformDefaultConfigs, AXPRedirectEvent, AXPRefreshEvent, AXPRegionalCalendarService, AXPReloadAction, AXPReloadEvent, AXPSearchCommandProvider, AXPSearchDefinitionActionBuilder, AXPSearchDefinitionBuilder, AXPSearchDefinitionProviderContext, AXPSearchDefinitionProviderService, AXPSearchService, AXPSettingDefaultValuesAggregatorService, AXPSettingDefinitionGroupBuilder, AXPSettingDefinitionProviderContext, AXPSettingDefinitionProviderService, AXPSettingDefinitionSectionBuilder, AXPSettingsEvaluatorScopeProvider, AXPSettingsService, AXPStatusDefinitionProviderService,
|
|
7593
|
+
export { ALL_DEFAULT_OPERATORS, AXMWorkflowErrorHandler, AXPAppVersionService, AXPBackgroundOperationCompletionService, AXPBackgroundOperationEntityStoreService, AXPBackgroundOperationHandler, AXPBackgroundOperationNotificationService, AXPBackgroundOperationProvider, AXPBackgroundOperationRootConfig, AXPBackgroundOperationService, AXPBackgroundOperationSlotComponent, AXPCleanNestedFilters, AXPClipBoardService, AXPCommonModule, AXPCommonSettings, AXPCustomOperatorService, AXPCustomOperatorServiceImpl, AXPDataProvider, AXPDebugService, AXPDefaultMultiLanguageConfigService, AXPDialogConfirmAction, AXPEntityCommandScope, AXPEntityQueryType, AXPEntityType, AXPErrorHandlerRegistryService, AXPExportService, AXPFileActionsService, AXPFileStorageEvaluatorScopeProvider, AXPFileStorageService, AXPFileTypeProviderService, AXPFilterOperatorMiddlewareService, AXPFilterOperatorMiddlewareServiceImpl, AXPFooterTextSlotComponent, AXPGlobalErrorHandler, AXPHomePageModule, AXPHomePageService, AXPKeyboardShortcutsHelpComponent, AXPKeyboardShortcutsHelpService, AXPLockService, AXPMenuItemsDataSourceDefinition, AXPMenuKeyboardShortcutService, AXPMenuMiddlewareRegistry, AXPMenuProviderService, AXPMenuRouteAccessService, AXPMenuRouteGuard, AXPMenuSearchDefinitionProvider, AXPMenuSearchProvider, AXPMenuService, AXPMenuVisibilityService, AXPNavBarSlotComponent, AXPNavigateWorkflow, AXPNotFoundCatchAllRoute, AXPNotFoundError, AXPOutputContractTranscriptSegmentService, AXPPlatformCommonDataSourceKeys, AXPPlatformDefaultConfigs, AXPRedirectEvent, AXPRefreshEvent, AXPRegionalCalendarService, AXPRegionalSetting, AXPRelationshipCardinality, AXPRelationshipKind, AXPReloadAction, AXPReloadEvent, AXPSearchCommandProvider, AXPSearchDefinitionActionBuilder, AXPSearchDefinitionBuilder, AXPSearchDefinitionProviderContext, AXPSearchDefinitionProviderService, AXPSearchService, AXPSettingDefaultValuesAggregatorService, AXPSettingDefinitionGroupBuilder, AXPSettingDefinitionProviderContext, AXPSettingDefinitionProviderService, AXPSettingDefinitionSectionBuilder, AXPSettingsEvaluatorScopeProvider, AXPSettingsService, AXPStatusDefinitionProviderService, AXPStatusProvider, AXPStickyDirective, AXPSystemStatusType, AXPSystemStatuses, AXPToastAction, AXPTokenDefinitionService, AXPTokenEvaluatorScopeProvider, AXPUnsavedChangesConfirmService, AXPUnsavedChangesPopstateService, AXPVersioningService, AXPWorkflowNavigateAction, AXPWorkflowRouterNavigateAction, AXP_AGENT_OUTPUT_CONTRACT_DEFAULT_STRUCTURED_RETRIES, AXP_APP_VERSION_PROVIDER, AXP_BACKGROUND_OPERATION_ENTITY_STORAGE, AXP_BACKGROUND_OPERATION_HANDLERS, AXP_BACKGROUND_OPERATION_PROVIDERS, AXP_FILE_ACTION_PROVIDER, AXP_FILE_TYPE_INFO_PROVIDER, AXP_HOME_PAGES, AXP_HOME_PAGE_DEFAULT_KEY, AXP_IN_APP_NOTIFICATION_SERVICE, AXP_LOCALE_MANAGEMENT_PORT, AXP_MENU_MIDDLEWARE, AXP_MENU_PROVIDER, AXP_NOT_FOUND_CATCH_ALL_ROUTE, AXP_NOT_FOUND_ROUTE, AXP_OUTPUT_CONTRACT_TRANSCRIPT_SEGMENT_PROVIDER, AXP_PLATFORM_CONFIG_TOKEN, AXP_PROTECTED_ROUTE_GUARDS, AXP_ROOT_CONFIG_TOKEN, AXP_SEARCH_DEFINITION_PROVIDER, AXP_SEARCH_PROVIDER, AXP_SETTING_DEFAULT_VALUES_PROVIDERS, AXP_SETTING_DEFINITION_PROVIDER, AXP_SETTING_VALUE_PROVIDER, AXP_STATUS_PROVIDERS, AXP_TOKEN_DEFINITION_PROVIDER, AXP_UNAUTHORIZED_ROUTE, AXVChangeType, BETWEEN_OPER, BOOLEAN_OPERATORS, CONTAINS_OPER, DATE_OPERATORS, DEFAULT_DATE_FILTER_PRESETS, DEFAULT_MULTILANGUAGE_FIELD_NAMES, ENDS_WITH_OPER, ENTITY_LAYOUT_SINGLE_TITLE_PATH, ENTITY_ROW_POPUP_TITLE_OPTION, ENTITY_ROW_TITLE_TEMPLATE_OPTION, ENVIRONMENT, EQ_OPER, GTE_OPER, GT_OPER, IN_OPER, IS_EMPTY_OPER, IS_NOT_EMPTY_OPER, LTE_OPER, LT_OPER, MULTILANGUAGE_CAPABLE_WIDGET_TYPES, NOT_CONTAINS_OPER, NOT_EQ_OPER, NUMBER_OPERATORS, STARTS_WITH_OPER, STRING_OPERATORS, UploadFromComputerActionProvider, applyDateFilterPreset, axpBackgroundOperationCapabilities, axpBackgroundOperationDisplayTime, axpBackgroundOperationEntityName, axpBackgroundOperationEstimateEndDate, axpBackgroundOperationFailurePolicy, axpBackgroundOperationFromEntityModel, axpBackgroundOperationRemainingMs, axpBackgroundOperationToEntityModel, axpBuildBackgroundOperation, axpCoerceBackgroundOperationDate, axpCreateBackgroundOperationId, axpFindMenuItemByRoute, axpIsActiveBackgroundOperationStatus, axpIsCompletedBackgroundOperationStatus, axpIsEntityDetailsPath, axpIsEntityDetailsViewRoute, axpIsEntityListPath, axpIsEntityListRoute, axpIsEntityRecordNotFound, axpMenuPathsMatch, axpNavigateAppPath, axpParseAppPath, axpRedirectToNotFound, axpRedirectToUnauthorized, axpReplaceEntityListRouteQueryParams, backgroundOperationEntityFactory, buildEntitySearchTitleContext, coerceAXPSettingRawValue, coerceAXPSettingRawValueByKind, configPlatform, createAllQueryView, createBackgroundOperationStore, createEntityCommandOptions, createEntityRowExpressionScope, createEntitySearchCommand, createMenuContext, createMenuMiddleware, createQueryView, createUnsavedChangesCanDeactivateGuard, downloadGraphicFromElement, evaluateEntityDisplayTemplate, findManualPresetIdByOperation, formatEntityRowDisplayTitle, getEntityInfo, getStatusInfo, getSystemStatus, inferSettingCoercionKind, isCardFieldBadgeDisplay, isMeaningfulEntityDisplayTitle, isUnresolvedEntityDisplayTemplate, normalizeEntityDisplayTemplate, normalizePositiveNumberOption, peelSettingValueKey, printGraphicFromElement, provideDynamicHomePage, provideMenuMiddleware, resolveCardFieldBadgeColor, resolveDateFilterPresets, resolveDefaultMasterListLayout, resolveEnabledMasterListLayouts, resolveEntityRowDisplayText, resolveEntityRowDisplayTitle, resolveEntityRowDisplayTitleFromColumnContext, resolveEntityRowTitleTemplate, resolveEntitySearchTitleFallback, resolveGraphicElementSize, resolveGraphicPrintContent, resolveQueryMatchingDisplayText, resolveSearchDisplayTemplate, resolveSearchableText, resolveStatusLook, sanitizeResolvedEntityDisplayTitle, searchInRecordValues, searchInSearchableValue, serializeSvgElement, shouldApplyDefaultMultiLanguageToEntityProperty, shouldApplyDefaultMultiLanguageToWidgetNode, shouldApplyDefaultMultiLanguageToWidgetProperty, systemStatusToDefinition, triggerBrowserDownload, withDefaultMultiLanguageOnWidgetNodeTree, withDefaultMultiLanguageOnWidgetProperty };
|
|
8459
7594
|
//# sourceMappingURL=acorex-platform-common.mjs.map
|