@acorex/platform 21.0.0-next.85 → 21.0.0-next.88
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-auth.mjs +1 -1
- package/fesm2022/acorex-platform-auth.mjs.map +1 -1
- package/fesm2022/{acorex-platform-common-common-settings.provider-Ytey9uhY.mjs → acorex-platform-common-common-settings.provider-VfmZct9H.mjs} +43 -3
- package/fesm2022/acorex-platform-common-common-settings.provider-VfmZct9H.mjs.map +1 -0
- package/fesm2022/acorex-platform-common.mjs +1709 -870
- package/fesm2022/acorex-platform-common.mjs.map +1 -1
- package/fesm2022/acorex-platform-contracts.mjs +1160 -9
- package/fesm2022/acorex-platform-contracts.mjs.map +1 -1
- package/fesm2022/acorex-platform-core.mjs +210 -131
- package/fesm2022/acorex-platform-core.mjs.map +1 -1
- package/fesm2022/acorex-platform-domain-contracts.mjs +58 -0
- package/fesm2022/acorex-platform-domain-contracts.mjs.map +1 -0
- package/fesm2022/acorex-platform-domain.mjs +2 -50
- package/fesm2022/acorex-platform-domain.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-components.mjs +59 -23
- package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-designer.mjs +3 -2
- package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-entity-attachments-page.component-CwG6n_Yb.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-entity-contracts.mjs +1114 -0
- package/fesm2022/acorex-platform-layout-entity-contracts.mjs.map +1 -0
- package/fesm2022/acorex-platform-layout-entity-file-list-popup.component--IeMZLIr.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-entity.mjs +661 -742
- package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-views.mjs +333 -119
- package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-widget-core.mjs +11 -105
- package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-CFyfix31.mjs +513 -0
- package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-CFyfix31.mjs.map +1 -0
- package/fesm2022/{acorex-platform-layout-widgets-tabular-data-edit-popup.component-DjpZU6gz.mjs → acorex-platform-layout-widgets-tabular-data-edit-popup.component-D4MOHEpI.mjs} +4 -4
- package/fesm2022/acorex-platform-layout-widgets-tabular-data-edit-popup.component-D4MOHEpI.mjs.map +1 -0
- package/fesm2022/acorex-platform-layout-widgets.mjs +868 -57
- package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
- package/fesm2022/acorex-platform-runtime.mjs.map +1 -1
- package/fesm2022/{acorex-platform-themes-default-entity-master-single-view.component-Br9p5aXT.mjs → acorex-platform-themes-default-entity-master-single-view.component-DrUwqKKA.mjs} +2 -3
- package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-DrUwqKKA.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-default.mjs +740 -96
- package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
- package/fesm2022/{acorex-platform-themes-shared-settings.provider-BjuzSe0T.mjs → acorex-platform-themes-shared-settings.provider-BUwPqwUS.mjs} +3 -3
- package/fesm2022/acorex-platform-themes-shared-settings.provider-BUwPqwUS.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-shared.mjs +4 -3
- package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
- package/fesm2022/acorex-platform-workflow.mjs +6 -8
- package/fesm2022/acorex-platform-workflow.mjs.map +1 -1
- package/package.json +9 -5
- package/types/acorex-platform-auth.d.ts +2 -1
- package/types/acorex-platform-common.d.ts +419 -1083
- package/types/acorex-platform-contracts.d.ts +1791 -260
- package/types/acorex-platform-core.d.ts +2153 -2806
- package/types/acorex-platform-domain-contracts.d.ts +416 -0
- package/types/acorex-platform-domain.d.ts +4 -415
- package/types/acorex-platform-layout-builder.d.ts +2 -1
- package/types/acorex-platform-layout-components.d.ts +15 -4
- package/types/acorex-platform-layout-designer.d.ts +2 -1
- package/types/acorex-platform-layout-entity-contracts.d.ts +864 -0
- package/types/acorex-platform-layout-entity.d.ts +88 -269
- package/types/acorex-platform-layout-views.d.ts +48 -14
- package/types/acorex-platform-layout-widget-core.d.ts +9 -5
- package/types/acorex-platform-layout-widgets.d.ts +89 -13
- package/types/acorex-platform-runtime.d.ts +1 -1
- package/types/acorex-platform-themes-default.d.ts +173 -70
- package/types/acorex-platform-workflow.d.ts +5 -18
- package/fesm2022/acorex-platform-common-common-settings.provider-Ytey9uhY.mjs.map +0 -1
- package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-J0zcGKBX.mjs +0 -116
- package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-J0zcGKBX.mjs.map +0 -1
- package/fesm2022/acorex-platform-layout-widgets-tabular-data-edit-popup.component-DjpZU6gz.mjs.map +0 -1
- package/fesm2022/acorex-platform-native.mjs +0 -155
- package/fesm2022/acorex-platform-native.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-Br9p5aXT.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-shared-settings.provider-BjuzSe0T.mjs.map +0 -1
- package/native/README.md +0 -4
- package/types/acorex-platform-native.d.ts +0 -28
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i2 from '@angular/common';
|
|
2
2
|
import { CommonModule } from '@angular/common';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
|
-
import { inject, viewChild, afterNextRender, effect, ChangeDetectionStrategy, ViewEncapsulation, Component, signal, computed, Injectable, input, output, ViewContainerRef, Directive, DestroyRef, ElementRef
|
|
4
|
+
import { inject, viewChild, afterNextRender, effect, ChangeDetectionStrategy, ViewEncapsulation, Component, signal, untracked, computed, Injectable, input, output, ViewContainerRef, Directive, DestroyRef, ElementRef } from '@angular/core';
|
|
5
5
|
import { AXCommonModule } from '@acorex/cdk/common';
|
|
6
6
|
import * as i4 from '@acorex/components/breadcrumbs';
|
|
7
7
|
import { AXBreadcrumbsModule } from '@acorex/components/breadcrumbs';
|
|
@@ -12,9 +12,9 @@ import { AXDecoratorModule } from '@acorex/components/decorators';
|
|
|
12
12
|
import * as i5 from '@acorex/components/dropdown';
|
|
13
13
|
import { AXDropdownModule } from '@acorex/components/dropdown';
|
|
14
14
|
import { AXDropdownButtonModule } from '@acorex/components/dropdown-button';
|
|
15
|
-
import { AXPStickyDirective, AXPCommonSettings, AXPHomePageService, AXPSettingsService, axpIsEntityDetailsViewRoute, axpNavigateAppPath, AXPEntityCommandScope,
|
|
15
|
+
import { AXPTabNavigationService, AXPStickyDirective, AXPCommonSettings, AXPHomePageService, AXPSettingsService, AXPTabPreviewService, AXPUnsavedChangesConfirmService, normalizeRouterUrl, AXP_HOME_TAB_KEY, axpResolveTabKey, axpIsEntityDetailsViewRoute, axpNavigateAppPath, AXPEntityCommandScope, AXPUnsavedChangesPopstateService, createUnsavedChangesCanDeactivateGuard } from '@acorex/platform/common';
|
|
16
16
|
import * as i8 from '@acorex/platform/core';
|
|
17
|
-
import { AXPDeviceService, AXPComponentSlotModule, getSmart, AXPExpressionEvaluatorService,
|
|
17
|
+
import { AXPDeviceService, AXPComponentSlotModule, getSmart, AXPExpressionEvaluatorService, AXPContextEvalFactory, normalizeKeyboardShortcuts, AXPContextStore, AXPBroadcastEventService, AXPKeyboardShortcutRegistry, AXPKeyboardShortcutPriority } from '@acorex/platform/core';
|
|
18
18
|
import * as i1 from '@acorex/cdk/drawer';
|
|
19
19
|
import { AXDrawerDirectiveModule, AXDrawerContainerDirective } from '@acorex/cdk/drawer';
|
|
20
20
|
import { AXDrawerModule } from '@acorex/components/drawer';
|
|
@@ -28,11 +28,11 @@ import { AXPWorkflowService } from '@acorex/platform/workflow';
|
|
|
28
28
|
import { AXToastService } from '@acorex/components/toast';
|
|
29
29
|
import { AXFormatService, AXFormatModule } from '@acorex/core/format';
|
|
30
30
|
import { AXPSessionService } from '@acorex/platform/auth';
|
|
31
|
+
import { Router, ActivatedRoute } from '@angular/router';
|
|
31
32
|
import { isEmpty, cloneDeep, get } from 'lodash-es';
|
|
32
33
|
import { AXBasePageComponent } from '@acorex/components/page';
|
|
33
34
|
import * as i7 from '@acorex/platform/layout/widget-core';
|
|
34
35
|
import { AXPPageStatus, AXPWidgetContainerComponent, AXPWidgetCoreModule } from '@acorex/platform/layout/widget-core';
|
|
35
|
-
import { Router, ActivatedRoute } from '@angular/router';
|
|
36
36
|
import { signalStore, withState, withComputed, withMethods, patchState } from '@ngrx/signals';
|
|
37
37
|
import { AXBadgeModule } from '@acorex/components/badge';
|
|
38
38
|
import { AXButtonGroupModule } from '@acorex/components/button-group';
|
|
@@ -56,6 +56,7 @@ class AXPPageLayoutComponent {
|
|
|
56
56
|
this.page = inject(AXPPageLayoutBase, { optional: true });
|
|
57
57
|
this.layoutService = inject(AXPLayoutThemeService);
|
|
58
58
|
this.deviceService = inject(AXPDeviceService);
|
|
59
|
+
this.tabNavigation = inject(AXPTabNavigationService);
|
|
59
60
|
this.startSideDrawer = viewChild('startSideDrawer', ...(ngDevMode ? [{ debugName: "startSideDrawer" }] : /* istanbul ignore next */ []));
|
|
60
61
|
this.workflow = inject(AXPWorkflowService);
|
|
61
62
|
this.commandService = inject(AXPCommandService);
|
|
@@ -219,11 +220,55 @@ class AXPPageLayoutBaseComponent {
|
|
|
219
220
|
this.homePageService = inject(AXPHomePageService);
|
|
220
221
|
this.policyService = inject(AXPPolicyEngineService);
|
|
221
222
|
this.settingsService = inject(AXPSettingsService);
|
|
223
|
+
this.tabNavigation = inject(AXPTabNavigationService);
|
|
224
|
+
this.tabPreviewService = inject(AXPTabPreviewService);
|
|
225
|
+
this.unsavedChangesConfirm = inject(AXPUnsavedChangesConfirmService);
|
|
226
|
+
this.router = inject(Router);
|
|
222
227
|
//#endregion
|
|
223
228
|
//#region ---------------- Signal to force recomputation ----------------
|
|
224
229
|
this._updateTrigger = signal(0, ...(ngDevMode ? [{ debugName: "_updateTrigger" }] : /* istanbul ignore next */ []));
|
|
230
|
+
/** Tab key captured at page init — stable while the route instance is cached. */
|
|
231
|
+
this.pageTabKey = signal(null, ...(ngDevMode ? [{ debugName: "pageTabKey" }] : /* istanbul ignore next */ []));
|
|
225
232
|
this.updateTrigger = this._updateTrigger.asReadonly();
|
|
226
233
|
//#endregion
|
|
234
|
+
//#region ---------------- Tab Title Sync ----------------
|
|
235
|
+
/** Refreshes tab title when this cached page becomes the active tab again. */
|
|
236
|
+
this.#syncTabTitleOnActivate = effect(() => {
|
|
237
|
+
const activeKey = this.tabNavigation.activeKey();
|
|
238
|
+
const pageKey = this.pageTabKey();
|
|
239
|
+
if (!activeKey || !pageKey || activeKey !== pageKey) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (this.tabNavigation.effectiveMode() !== 'tabular') {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
untracked(() => {
|
|
246
|
+
this.recompute();
|
|
247
|
+
});
|
|
248
|
+
}, ...(ngDevMode ? [{ debugName: "#syncTabTitleOnActivate" }] : /* istanbul ignore next */ []));
|
|
249
|
+
/** Publishes structured tab preview (hover card + dirty badge) for this cached page. */
|
|
250
|
+
this.#syncTabStructuredPreview = effect(() => {
|
|
251
|
+
this.updateTrigger();
|
|
252
|
+
const tabKey = this.pageTabKey();
|
|
253
|
+
const titleValue = this.title();
|
|
254
|
+
const descriptionValue = this.description();
|
|
255
|
+
const icon = this.titleIcon();
|
|
256
|
+
const breadcrumbItems = this.breadcrumbs();
|
|
257
|
+
if (!tabKey || this.tabNavigation.effectiveMode() !== 'tabular') {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
const isDirty = this.hasUnsavedChanges();
|
|
261
|
+
untracked(() => {
|
|
262
|
+
void this.publishTabStructuredPreview(tabKey, {
|
|
263
|
+
titleValue,
|
|
264
|
+
descriptionValue,
|
|
265
|
+
icon,
|
|
266
|
+
breadcrumbItems,
|
|
267
|
+
isDirty,
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
}, ...(ngDevMode ? [{ debugName: "#syncTabStructuredPreview" }] : /* istanbul ignore next */ []));
|
|
271
|
+
//#endregion
|
|
227
272
|
//#region ---------------- Title ----------------
|
|
228
273
|
this.title = signal('', ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
229
274
|
this.titleIcon = signal(null, ...(ngDevMode ? [{ debugName: "titleIcon" }] : /* istanbul ignore next */ []));
|
|
@@ -232,6 +277,12 @@ class AXPPageLayoutBaseComponent {
|
|
|
232
277
|
this.title.set(await this.getPageTitle());
|
|
233
278
|
const icon = await this.getPageIcon();
|
|
234
279
|
this.titleIcon.set(icon);
|
|
280
|
+
if (this.tabNavigation.effectiveMode() === 'tabular') {
|
|
281
|
+
const tabKey = this.pageTabKey();
|
|
282
|
+
if (tabKey && icon) {
|
|
283
|
+
this.tabNavigation.syncTabIcon(tabKey, icon);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
235
286
|
}, ...(ngDevMode ? [{ debugName: "#titleEffect" }] : /* istanbul ignore next */ []));
|
|
236
287
|
this.hasTitle = computed(() => {
|
|
237
288
|
const value = this.title();
|
|
@@ -285,9 +336,20 @@ class AXPPageLayoutBaseComponent {
|
|
|
285
336
|
this.#breadcrumbsEffect = effect(async () => {
|
|
286
337
|
this.updateTrigger();
|
|
287
338
|
const breadCrumbs = await this.getPageBreadcrumbs();
|
|
288
|
-
|
|
339
|
+
const trail = [...this.defaultBreadCrumbs, ...breadCrumbs];
|
|
340
|
+
this.breadcrumbs.set(trail);
|
|
341
|
+
if (this.tabNavigation.effectiveMode() === 'tabular') {
|
|
342
|
+
const tabKey = this.pageTabKey();
|
|
343
|
+
let tabTitle = await this.resolveBreadcrumbTabTitle(breadCrumbs);
|
|
344
|
+
if (!tabTitle) {
|
|
345
|
+
tabTitle = await this.resolvePageTitleForTab();
|
|
346
|
+
}
|
|
347
|
+
if (tabKey && tabTitle) {
|
|
348
|
+
this.tabNavigation.syncTabTitle(tabKey, tabTitle);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
289
351
|
}, ...(ngDevMode ? [{ debugName: "#breadcrumbsEffect" }] : /* istanbul ignore next */ []));
|
|
290
|
-
this.hasBreadcrumbs = computed(() => this.breadcrumbs().length > 1, ...(ngDevMode ? [{ debugName: "hasBreadcrumbs" }] : /* istanbul ignore next */ []));
|
|
352
|
+
this.hasBreadcrumbs = computed(() => this.breadcrumbs().length > 1 && this.tabNavigation.effectiveMode() !== 'tabular', ...(ngDevMode ? [{ debugName: "hasBreadcrumbs" }] : /* istanbul ignore next */ []));
|
|
291
353
|
//#endregion
|
|
292
354
|
//#region ---------------- Badge ----------------
|
|
293
355
|
this.badge = signal(null, ...(ngDevMode ? [{ debugName: "badge" }] : /* istanbul ignore next */ []));
|
|
@@ -343,10 +405,75 @@ class AXPPageLayoutBaseComponent {
|
|
|
343
405
|
//#endregion
|
|
344
406
|
//#region ---------------- Lifecycle hooks ----------------
|
|
345
407
|
async ngOnInit() {
|
|
408
|
+
this.pageTabKey.set(this.resolvePageTabKey());
|
|
346
409
|
this.translateService.langChanges$.subscribe(() => {
|
|
347
410
|
this.recompute();
|
|
348
411
|
});
|
|
349
412
|
}
|
|
413
|
+
/** Maps the home-page route to the pinned home tab key instead of a path-based key. */
|
|
414
|
+
resolvePageTabKey() {
|
|
415
|
+
const homePath = this.homePageService.getCurrent().path;
|
|
416
|
+
if (homePath && normalizeRouterUrl(homePath) === normalizeRouterUrl(this.router.url)) {
|
|
417
|
+
return AXP_HOME_TAB_KEY;
|
|
418
|
+
}
|
|
419
|
+
return axpResolveTabKey(this.router);
|
|
420
|
+
}
|
|
421
|
+
//#endregion
|
|
422
|
+
//#region ---------------- Tab Title Sync ----------------
|
|
423
|
+
/** Refreshes tab title when this cached page becomes the active tab again. */
|
|
424
|
+
#syncTabTitleOnActivate;
|
|
425
|
+
/** Publishes structured tab preview (hover card + dirty badge) for this cached page. */
|
|
426
|
+
#syncTabStructuredPreview;
|
|
427
|
+
async publishTabStructuredPreview(tabKey, input) {
|
|
428
|
+
const tab = this.tabNavigation.tabList().find((item) => item.key === tabKey);
|
|
429
|
+
const breadcrumbTrail = [];
|
|
430
|
+
for (const item of input.breadcrumbItems.slice(1)) {
|
|
431
|
+
const segment = await this.resolveBreadcrumbItemTitle(item);
|
|
432
|
+
if (segment && segment !== breadcrumbTrail[breadcrumbTrail.length - 1]) {
|
|
433
|
+
breadcrumbTrail.push(segment);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
const fallbackTitle = await this.translateTextIfKey(breadcrumbTrail.join(' - ') || tab?.title || '');
|
|
437
|
+
const title = await this.resolveMultiLanguageText(input.titleValue, fallbackTitle);
|
|
438
|
+
const subtitle = await this.resolveMultiLanguageText(input.descriptionValue, '');
|
|
439
|
+
this.tabPreviewService.setPreview(tabKey, {
|
|
440
|
+
title,
|
|
441
|
+
subtitle: subtitle || undefined,
|
|
442
|
+
url: tab?.url ?? '',
|
|
443
|
+
icon: input.icon ?? tab?.icon,
|
|
444
|
+
isDirty: input.isDirty,
|
|
445
|
+
breadcrumbTrail,
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
async resolveMultiLanguageText(value, fallback) {
|
|
449
|
+
const resolvedFallback = await this.translateTextIfKey(fallback);
|
|
450
|
+
if (value == null || value === '') {
|
|
451
|
+
return resolvedFallback;
|
|
452
|
+
}
|
|
453
|
+
if (typeof value === 'string') {
|
|
454
|
+
const trimmed = value.trim();
|
|
455
|
+
if (!trimmed) {
|
|
456
|
+
return resolvedFallback;
|
|
457
|
+
}
|
|
458
|
+
if (trimmed.startsWith('@')) {
|
|
459
|
+
return (await this.translateService.translateAsync(trimmed)) || resolvedFallback;
|
|
460
|
+
}
|
|
461
|
+
return trimmed;
|
|
462
|
+
}
|
|
463
|
+
const resolved = this.translateService.resolve(value);
|
|
464
|
+
return resolved || resolvedFallback;
|
|
465
|
+
}
|
|
466
|
+
/** Resolves i18n keys (`@scope:key`); returns plain text unchanged. */
|
|
467
|
+
async translateTextIfKey(text) {
|
|
468
|
+
const trimmed = text.trim();
|
|
469
|
+
if (!trimmed) {
|
|
470
|
+
return '';
|
|
471
|
+
}
|
|
472
|
+
if (trimmed.startsWith('@')) {
|
|
473
|
+
return (await this.translateService.translateAsync(trimmed)) || trimmed;
|
|
474
|
+
}
|
|
475
|
+
return trimmed;
|
|
476
|
+
}
|
|
350
477
|
#titleEffect;
|
|
351
478
|
getPageTitle() {
|
|
352
479
|
return '';
|
|
@@ -362,10 +489,78 @@ class AXPPageLayoutBaseComponent {
|
|
|
362
489
|
getPageBreadcrumbs() {
|
|
363
490
|
return [];
|
|
364
491
|
}
|
|
492
|
+
/** Joins all page breadcrumbs (Home is excluded upstream) as the tab label. */
|
|
493
|
+
async resolveBreadcrumbTabTitle(pageBreadcrumbs) {
|
|
494
|
+
const segments = [];
|
|
495
|
+
for (const item of pageBreadcrumbs) {
|
|
496
|
+
const title = await this.resolveBreadcrumbItemTitle(item);
|
|
497
|
+
if (title && title !== segments[segments.length - 1]) {
|
|
498
|
+
segments.push(title);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
if (!segments.length) {
|
|
502
|
+
return null;
|
|
503
|
+
}
|
|
504
|
+
return segments.join(' - ');
|
|
505
|
+
}
|
|
506
|
+
/** Resolves a breadcrumb label the same way as page chrome (`t(item.title)`). */
|
|
507
|
+
async resolveBreadcrumbItemTitle(item) {
|
|
508
|
+
const raw = item?.title;
|
|
509
|
+
if (raw == null || raw === '') {
|
|
510
|
+
return null;
|
|
511
|
+
}
|
|
512
|
+
if (typeof raw === 'string') {
|
|
513
|
+
const trimmed = raw.trim();
|
|
514
|
+
if (!trimmed) {
|
|
515
|
+
return null;
|
|
516
|
+
}
|
|
517
|
+
if (trimmed.startsWith('@')) {
|
|
518
|
+
return (await this.translateService.translateAsync(trimmed)) || trimmed;
|
|
519
|
+
}
|
|
520
|
+
return trimmed;
|
|
521
|
+
}
|
|
522
|
+
if (typeof raw === 'object' && !Array.isArray(raw)) {
|
|
523
|
+
if (this.translateService.isValidMultiLanguageObject(raw)) {
|
|
524
|
+
const resolved = this.translateService.resolve(raw);
|
|
525
|
+
return resolved || null;
|
|
526
|
+
}
|
|
527
|
+
const resolved = this.translateService.resolve(raw);
|
|
528
|
+
return resolved || null;
|
|
529
|
+
}
|
|
530
|
+
return String(raw);
|
|
531
|
+
}
|
|
532
|
+
/** Fallback tab label from the page title when breadcrumbs are not ready yet. */
|
|
533
|
+
async resolvePageTitleForTab() {
|
|
534
|
+
const raw = await Promise.resolve(this.getPageTitle());
|
|
535
|
+
if (raw == null || raw === '') {
|
|
536
|
+
return null;
|
|
537
|
+
}
|
|
538
|
+
if (typeof raw === 'string') {
|
|
539
|
+
const trimmed = raw.trim();
|
|
540
|
+
if (trimmed.startsWith('@')) {
|
|
541
|
+
return (await this.translateService.translateAsync(trimmed)) || trimmed;
|
|
542
|
+
}
|
|
543
|
+
return trimmed;
|
|
544
|
+
}
|
|
545
|
+
if (typeof raw === 'object' && !Array.isArray(raw)) {
|
|
546
|
+
const resolved = this.translateService.resolve(raw);
|
|
547
|
+
return resolved || null;
|
|
548
|
+
}
|
|
549
|
+
return null;
|
|
550
|
+
}
|
|
365
551
|
#badgeEffect;
|
|
366
552
|
hasUnsavedChanges() {
|
|
367
553
|
return false;
|
|
368
554
|
}
|
|
555
|
+
/**
|
|
556
|
+
* Confirms closing this page as a background tab (no router navigation).
|
|
557
|
+
* Override when dirty detection differs from {@link hasUnsavedChanges}.
|
|
558
|
+
*/
|
|
559
|
+
onBeforeTabClose() {
|
|
560
|
+
return this.unsavedChangesConfirm.confirmIfDirty(this.hasUnsavedChanges(), {
|
|
561
|
+
approveLeaveOnConfirm: false,
|
|
562
|
+
});
|
|
563
|
+
}
|
|
369
564
|
async getPageBadge() {
|
|
370
565
|
if (!this.hasUnsavedChanges()) {
|
|
371
566
|
return null;
|
|
@@ -676,45 +871,40 @@ const AXPLayoutDetailsViewViewModel = signalStore(withState(() => {
|
|
|
676
871
|
const evaluatedByTab = store.evaluatedTabContentsById();
|
|
677
872
|
return evaluatedByTab[tab.id] ?? [];
|
|
678
873
|
}),
|
|
679
|
-
})), withMethods((store, evaluatorService = inject(AXPExpressionEvaluatorService), router = inject(Router), route = inject(ActivatedRoute), deviceService = inject(AXPDeviceService), formatService = inject(AXFormatService), toastService = inject(AXToastService), translateService = inject(AXTranslationService), settingsService = inject(AXPSettingsService)) => {
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
const queryParams = router.routerState.snapshot.root.queryParams;
|
|
684
|
-
const targetPageId = pageId || queryParams['page'];
|
|
685
|
-
if (targetPageId) {
|
|
686
|
-
return adapter.pages.find((p) => p.id === targetPageId) ?? null;
|
|
874
|
+
})), withMethods((store, evaluatorService = inject(AXPExpressionEvaluatorService), contextEvalFactory = inject(AXPContextEvalFactory), router = inject(Router), route = inject(ActivatedRoute), deviceService = inject(AXPDeviceService), formatService = inject(AXFormatService), toastService = inject(AXToastService), translateService = inject(AXTranslationService), settingsService = inject(AXPSettingsService)) => {
|
|
875
|
+
const resolvePageFromId = (adapter, pageId) => {
|
|
876
|
+
if (pageId) {
|
|
877
|
+
return adapter.pages.find((p) => p.id === pageId) ?? null;
|
|
687
878
|
}
|
|
688
879
|
if (deviceService.isLarge() || adapter.pages.length === 1) {
|
|
689
|
-
return adapter.pages.find((p) => p.isPrimary) ?? adapter.pages[0];
|
|
880
|
+
return adapter.pages.find((p) => p.isPrimary) ?? adapter.pages[0] ?? null;
|
|
690
881
|
}
|
|
691
882
|
return null;
|
|
692
883
|
};
|
|
693
|
-
|
|
884
|
+
/** Loads page data, context, and evaluated widgets for the target sidebar page. */
|
|
885
|
+
const activatePageById = async (pageId, forceRefresh) => {
|
|
694
886
|
const adapter = store.adapter();
|
|
695
887
|
if (!adapter) {
|
|
696
|
-
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
const queryParams = router.routerState.snapshot.root.queryParams;
|
|
891
|
+
const targetPageId = pageId ?? queryParams['page'];
|
|
892
|
+
const currentPage = resolvePageFromId(adapter, targetPageId);
|
|
893
|
+
if (!forceRefresh && currentPage?.id === store.currentPage()?.id && store.status() === AXPPageStatus.Rendered) {
|
|
894
|
+
return;
|
|
697
895
|
}
|
|
698
|
-
const generation = ++pageLoadGeneration;
|
|
699
896
|
patchState(store, {
|
|
700
897
|
status: AXPPageStatus.Processing,
|
|
701
898
|
});
|
|
702
|
-
const currentPage = resolveTargetPage(adapter, pageId);
|
|
703
899
|
let context = {};
|
|
704
900
|
if (currentPage) {
|
|
705
901
|
const currentPageIndex = adapter.pages.findIndex((p) => p.id === currentPage.id);
|
|
706
902
|
const loadResult = await adapter.pages[currentPageIndex]?.load?.({ forceRefresh });
|
|
707
|
-
if (generation !== pageLoadGeneration) {
|
|
708
|
-
return;
|
|
709
|
-
}
|
|
710
903
|
context = loadResult?.data ?? {};
|
|
711
904
|
}
|
|
712
905
|
const currentTab = currentPage?.tabs?.[0] ?? null;
|
|
713
906
|
const rootCtx = store.rootContext();
|
|
714
907
|
const { evaluatedPageContent, evaluatedTabContentsById } = await buildEvaluatedPageContentsForRootContext(currentPage, rootCtx, evaluatorService);
|
|
715
|
-
if (generation !== pageLoadGeneration) {
|
|
716
|
-
return;
|
|
717
|
-
}
|
|
718
908
|
patchState(store, {
|
|
719
909
|
previousContext: cloneDeep(context),
|
|
720
910
|
context: cloneDeep(context),
|
|
@@ -747,16 +937,16 @@ const AXPLayoutDetailsViewViewModel = signalStore(withState(() => {
|
|
|
747
937
|
if (!adapter) {
|
|
748
938
|
return;
|
|
749
939
|
}
|
|
750
|
-
const
|
|
751
|
-
const
|
|
752
|
-
|
|
753
|
-
if (targetPage?.id === loadedPageId && isRendered) {
|
|
940
|
+
const pageId = params['page'];
|
|
941
|
+
const targetPage = resolvePageFromId(adapter, pageId);
|
|
942
|
+
if (store.currentPage()?.id === targetPage?.id) {
|
|
754
943
|
return;
|
|
755
944
|
}
|
|
756
|
-
|
|
945
|
+
// `load()` → `activatePageById` owns initial hydration when both sides are still null.
|
|
946
|
+
if (store.currentPage() == null && targetPage == null) {
|
|
757
947
|
return;
|
|
758
948
|
}
|
|
759
|
-
void
|
|
949
|
+
void activatePageById(targetPage?.id);
|
|
760
950
|
});
|
|
761
951
|
onCleanup(() => subscription.unsubscribe());
|
|
762
952
|
});
|
|
@@ -817,7 +1007,12 @@ const AXPLayoutDetailsViewViewModel = signalStore(withState(() => {
|
|
|
817
1007
|
evaluated = await evaluatorService.evaluate(raw, evalScope);
|
|
818
1008
|
}
|
|
819
1009
|
else if (typeof sourceAfterPreLocale === 'string') {
|
|
820
|
-
|
|
1010
|
+
if (sourceAfterPreLocale.trim().startsWith('@')) {
|
|
1011
|
+
evaluated = await translateService.translateAsync(sourceAfterPreLocale);
|
|
1012
|
+
}
|
|
1013
|
+
else {
|
|
1014
|
+
evaluated = await formatService.format(sourceAfterPreLocale, 'string', formatContext);
|
|
1015
|
+
}
|
|
821
1016
|
}
|
|
822
1017
|
else {
|
|
823
1018
|
evaluated = await formatService.format(raw, 'string', formatContext);
|
|
@@ -851,15 +1046,12 @@ const AXPLayoutDetailsViewViewModel = signalStore(withState(() => {
|
|
|
851
1046
|
});
|
|
852
1047
|
},
|
|
853
1048
|
async loadPage(pageId, forceRefresh) {
|
|
854
|
-
await
|
|
1049
|
+
await activatePageById(pageId, forceRefresh);
|
|
855
1050
|
},
|
|
856
1051
|
async load(adapter) {
|
|
857
|
-
// Setup expression evaluator scope
|
|
858
1052
|
const scope = {
|
|
859
1053
|
context: {
|
|
860
|
-
|
|
861
|
-
return getSmart(store.context(), path);
|
|
862
|
-
},
|
|
1054
|
+
...contextEvalFactory.createLiveScope(() => store.context()),
|
|
863
1055
|
isDirty: () => store.isDirty(),
|
|
864
1056
|
},
|
|
865
1057
|
};
|
|
@@ -940,10 +1132,7 @@ const AXPLayoutDetailsViewViewModel = signalStore(withState(() => {
|
|
|
940
1132
|
if (page) {
|
|
941
1133
|
const pageParam = route.snapshot.queryParamMap.get('page');
|
|
942
1134
|
const isSamePage = previousPage?.id === page.id && pageParam === page.id;
|
|
943
|
-
if (isSamePage) {
|
|
944
|
-
return;
|
|
945
|
-
}
|
|
946
|
-
if (axpIsEntityDetailsViewRoute(router)) {
|
|
1135
|
+
if (!isSamePage && axpIsEntityDetailsViewRoute(router)) {
|
|
947
1136
|
const queryParams = { page: page.id };
|
|
948
1137
|
if (previousPage?.id && previousPage.id !== page.id) {
|
|
949
1138
|
queryParams['filters'] = null;
|
|
@@ -956,16 +1145,14 @@ const AXPLayoutDetailsViewViewModel = signalStore(withState(() => {
|
|
|
956
1145
|
replaceUrl: false,
|
|
957
1146
|
});
|
|
958
1147
|
}
|
|
959
|
-
void
|
|
1148
|
+
void activatePageById(page.id);
|
|
960
1149
|
}
|
|
961
1150
|
else {
|
|
962
|
-
pageLoadGeneration += 1;
|
|
963
1151
|
patchState(store, {
|
|
964
1152
|
currentPage: null,
|
|
965
1153
|
currentTab: null,
|
|
966
1154
|
evaluatedPageContent: [],
|
|
967
1155
|
evaluatedTabContentsById: {},
|
|
968
|
-
status: AXPPageStatus.Rendered,
|
|
969
1156
|
});
|
|
970
1157
|
if (axpIsEntityDetailsViewRoute(router)) {
|
|
971
1158
|
const currentParams = { ...route.snapshot.queryParams };
|
|
@@ -1093,6 +1280,7 @@ const AXPLayoutDetailsViewViewModel = signalStore(withState(() => {
|
|
|
1093
1280
|
},
|
|
1094
1281
|
async discard() {
|
|
1095
1282
|
patchState(store, {
|
|
1283
|
+
context: cloneDeep(store.previousContext()),
|
|
1096
1284
|
formDirty: false,
|
|
1097
1285
|
});
|
|
1098
1286
|
},
|
|
@@ -1193,6 +1381,66 @@ const AXPLayoutDetailsViewViewModel = signalStore(withState(() => {
|
|
|
1193
1381
|
};
|
|
1194
1382
|
}));
|
|
1195
1383
|
|
|
1384
|
+
//#region ---- Details View Action Shortcut Utilities ----
|
|
1385
|
+
/** Platform defaults for known entity detail commands when `shortcuts` is omitted on the action. */
|
|
1386
|
+
const DEFAULT_ENTITY_DETAIL_ACTION_SHORTCUTS = {
|
|
1387
|
+
'delete-entity': ['ctrl+shift+delete'],
|
|
1388
|
+
};
|
|
1389
|
+
const DEFAULT_FOOTER_COMMAND_SHORTCUTS = {
|
|
1390
|
+
'update-entity': ['ctrl+s'],
|
|
1391
|
+
discard: ['escape'],
|
|
1392
|
+
};
|
|
1393
|
+
/**
|
|
1394
|
+
* Returns the base workflow/command name from a composite entity action command id (`cmd&actionName`).
|
|
1395
|
+
*/
|
|
1396
|
+
function getDetailsViewActionCommandBaseName(commandName) {
|
|
1397
|
+
if (!commandName) {
|
|
1398
|
+
return '';
|
|
1399
|
+
}
|
|
1400
|
+
return commandName.split('&')[0];
|
|
1401
|
+
}
|
|
1402
|
+
/**
|
|
1403
|
+
* Resolves keyboard shortcuts for a header entity action.
|
|
1404
|
+
* Explicit `shortcuts` wins; pass `[]` to disable. Otherwise known commands use platform defaults.
|
|
1405
|
+
*/
|
|
1406
|
+
function resolveDetailsViewActionShortcuts(commandBaseName, explicit) {
|
|
1407
|
+
if (explicit !== undefined) {
|
|
1408
|
+
const chords = flattenDetailsViewActionShortcuts(explicit);
|
|
1409
|
+
return chords.length ? chords : undefined;
|
|
1410
|
+
}
|
|
1411
|
+
const defaults = DEFAULT_ENTITY_DETAIL_ACTION_SHORTCUTS[commandBaseName];
|
|
1412
|
+
return defaults?.length ? [...defaults] : undefined;
|
|
1413
|
+
}
|
|
1414
|
+
/**
|
|
1415
|
+
* Resolves save/discard footer command shortcuts from page settings.
|
|
1416
|
+
*/
|
|
1417
|
+
function resolveDetailsViewFooterCommandShortcuts(commandName, explicit) {
|
|
1418
|
+
if (explicit !== undefined) {
|
|
1419
|
+
const chords = flattenDetailsViewActionShortcuts(explicit);
|
|
1420
|
+
return chords.length ? chords : undefined;
|
|
1421
|
+
}
|
|
1422
|
+
const defaults = DEFAULT_FOOTER_COMMAND_SHORTCUTS[commandName];
|
|
1423
|
+
return defaults?.length ? [...defaults] : undefined;
|
|
1424
|
+
}
|
|
1425
|
+
function flattenDetailsViewActionShortcuts(shortcuts) {
|
|
1426
|
+
const chords = normalizeKeyboardShortcuts(shortcuts).flatMap((shortcut) => shortcut.keys);
|
|
1427
|
+
return dedupeDetailsViewActionShortcuts(chords);
|
|
1428
|
+
}
|
|
1429
|
+
function dedupeDetailsViewActionShortcuts(shortcuts) {
|
|
1430
|
+
const seen = new Set();
|
|
1431
|
+
const result = [];
|
|
1432
|
+
for (const shortcut of shortcuts) {
|
|
1433
|
+
const token = shortcut.trim().toLowerCase();
|
|
1434
|
+
if (!token || seen.has(token)) {
|
|
1435
|
+
continue;
|
|
1436
|
+
}
|
|
1437
|
+
seen.add(token);
|
|
1438
|
+
result.push(shortcut.trim());
|
|
1439
|
+
}
|
|
1440
|
+
return result;
|
|
1441
|
+
}
|
|
1442
|
+
//#endregion
|
|
1443
|
+
|
|
1196
1444
|
/**
|
|
1197
1445
|
* Registry of rendered page component instances by component key.
|
|
1198
1446
|
* Used so page execute can delegate to the actual component instance in the view.
|
|
@@ -1331,77 +1579,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
1331
1579
|
}]
|
|
1332
1580
|
}], propDecorators: { componentKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "componentKey", required: true }] }], rootContext: [{ type: i0.Input, args: [{ isSignal: true, alias: "rootContext", required: false }] }], pageConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageConfig", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], componentReady: [{ type: i0.Output, args: ["componentReady"] }] } });
|
|
1333
1581
|
|
|
1334
|
-
//#region ---- Details View Action Shortcut Utilities ----
|
|
1335
|
-
/** Platform defaults for known entity detail commands when `shortcuts` is omitted on the action. */
|
|
1336
|
-
const DEFAULT_ENTITY_DETAIL_ACTION_SHORTCUTS = {
|
|
1337
|
-
'delete-entity': ['ctrl+shift+delete'],
|
|
1338
|
-
};
|
|
1339
|
-
const DEFAULT_FOOTER_COMMAND_SHORTCUTS = {
|
|
1340
|
-
'update-entity': ['ctrl+s'],
|
|
1341
|
-
discard: ['escape'],
|
|
1342
|
-
};
|
|
1343
|
-
/**
|
|
1344
|
-
* Returns the base workflow/command name from a composite entity action command id (`cmd&actionName`).
|
|
1345
|
-
*/
|
|
1346
|
-
function getDetailsViewActionCommandBaseName(commandName) {
|
|
1347
|
-
if (!commandName) {
|
|
1348
|
-
return '';
|
|
1349
|
-
}
|
|
1350
|
-
return commandName.split('&')[0];
|
|
1351
|
-
}
|
|
1352
|
-
/**
|
|
1353
|
-
* Resolves keyboard shortcuts for a header entity action.
|
|
1354
|
-
* Explicit `shortcuts` wins; pass `[]` to disable. Otherwise known commands use platform defaults.
|
|
1355
|
-
*/
|
|
1356
|
-
function resolveDetailsViewActionShortcuts(commandBaseName, explicit) {
|
|
1357
|
-
if (explicit !== undefined) {
|
|
1358
|
-
const chords = flattenDetailsViewActionShortcuts(explicit);
|
|
1359
|
-
return chords.length ? chords : undefined;
|
|
1360
|
-
}
|
|
1361
|
-
const defaults = DEFAULT_ENTITY_DETAIL_ACTION_SHORTCUTS[commandBaseName];
|
|
1362
|
-
return defaults?.length ? [...defaults] : undefined;
|
|
1363
|
-
}
|
|
1364
|
-
/**
|
|
1365
|
-
* Resolves save/discard footer command shortcuts from page settings.
|
|
1366
|
-
*/
|
|
1367
|
-
function resolveDetailsViewFooterCommandShortcuts(commandName, explicit) {
|
|
1368
|
-
if (explicit !== undefined) {
|
|
1369
|
-
const chords = flattenDetailsViewActionShortcuts(explicit);
|
|
1370
|
-
return chords.length ? chords : undefined;
|
|
1371
|
-
}
|
|
1372
|
-
const defaults = DEFAULT_FOOTER_COMMAND_SHORTCUTS[commandName];
|
|
1373
|
-
return defaults?.length ? [...defaults] : undefined;
|
|
1374
|
-
}
|
|
1375
|
-
function flattenDetailsViewActionShortcuts(shortcuts) {
|
|
1376
|
-
const chords = normalizeKeyboardShortcuts(shortcuts).flatMap((shortcut) => shortcut.keys);
|
|
1377
|
-
return dedupeDetailsViewActionShortcuts(chords);
|
|
1378
|
-
}
|
|
1379
|
-
function dedupeDetailsViewActionShortcuts(shortcuts) {
|
|
1380
|
-
const seen = new Set();
|
|
1381
|
-
const result = [];
|
|
1382
|
-
for (const shortcut of shortcuts) {
|
|
1383
|
-
const token = shortcut.trim().toLowerCase();
|
|
1384
|
-
if (!token || seen.has(token)) {
|
|
1385
|
-
continue;
|
|
1386
|
-
}
|
|
1387
|
-
seen.add(token);
|
|
1388
|
-
result.push(shortcut.trim());
|
|
1389
|
-
}
|
|
1390
|
-
return result;
|
|
1391
|
-
}
|
|
1392
|
-
//#endregion
|
|
1393
|
-
|
|
1394
1582
|
class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
|
|
1395
1583
|
constructor() {
|
|
1396
1584
|
super(...arguments);
|
|
1397
1585
|
this.adapter = input.required(...(ngDevMode ? [{ debugName: "adapter" }] : /* istanbul ignore next */ []));
|
|
1398
1586
|
//
|
|
1399
1587
|
this.vm = inject(AXPLayoutDetailsViewViewModel);
|
|
1400
|
-
this.router = inject(Router);
|
|
1401
1588
|
this.route = inject(ActivatedRoute);
|
|
1402
1589
|
this.eventService = inject(AXPBroadcastEventService);
|
|
1403
1590
|
this.deviceService = inject(AXPDeviceService);
|
|
1404
|
-
this.unsavedChangesConfirm = inject(AXPUnsavedChangesConfirmService);
|
|
1405
1591
|
this.unsavedChangesPopstate = inject(AXPUnsavedChangesPopstateService);
|
|
1406
1592
|
this.pageComponentInstanceRegistry = inject(AXPPageComponentInstanceRegistryService);
|
|
1407
1593
|
this.shortcutRegistry = inject(AXPKeyboardShortcutRegistry);
|
|
@@ -1650,11 +1836,17 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
|
|
|
1650
1836
|
}
|
|
1651
1837
|
finalizeDiscard() {
|
|
1652
1838
|
const container = this.widgetContainer();
|
|
1653
|
-
if (
|
|
1839
|
+
if (container) {
|
|
1840
|
+
container.revertAndSettle();
|
|
1841
|
+
this.vm.applySavedState(container.contextService.data());
|
|
1654
1842
|
return;
|
|
1655
1843
|
}
|
|
1656
|
-
|
|
1657
|
-
|
|
1844
|
+
void this.vm.discard();
|
|
1845
|
+
}
|
|
1846
|
+
/** Reverts widget form state and view-model context to the last saved baseline. */
|
|
1847
|
+
async discardUnsavedChanges() {
|
|
1848
|
+
this.finalizeDiscard();
|
|
1849
|
+
this.recompute();
|
|
1658
1850
|
}
|
|
1659
1851
|
/**
|
|
1660
1852
|
* `ax-tabs` selects on click before we can confirm. Revert synchronously, then confirm + switch.
|
|
@@ -1750,6 +1942,15 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
|
|
|
1750
1942
|
async getBackButton() {
|
|
1751
1943
|
return this.vm.getBackButton();
|
|
1752
1944
|
}
|
|
1945
|
+
hasUnsavedChanges() {
|
|
1946
|
+
if (this.vm.currentPage()?.isReadonly) {
|
|
1947
|
+
return false;
|
|
1948
|
+
}
|
|
1949
|
+
if (this.isActivePageComponentDirty()) {
|
|
1950
|
+
return true;
|
|
1951
|
+
}
|
|
1952
|
+
return this.liveFormIsDirty();
|
|
1953
|
+
}
|
|
1753
1954
|
async getPageBadge() {
|
|
1754
1955
|
if (this.vm.currentPage()?.isReadonly) {
|
|
1755
1956
|
return null;
|
|
@@ -1794,6 +1995,12 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
|
|
|
1794
1995
|
}
|
|
1795
1996
|
return this.confirmLeaveIfDirty();
|
|
1796
1997
|
}
|
|
1998
|
+
/** Background tab close — confirm without discarding (the cached instance is destroyed). */
|
|
1999
|
+
onBeforeTabClose() {
|
|
2000
|
+
return this.unsavedChangesConfirm.confirmIfDirty(this.shouldPromptLeave(), {
|
|
2001
|
+
approveLeaveOnConfirm: false,
|
|
2002
|
+
});
|
|
2003
|
+
}
|
|
1797
2004
|
/** Active entity component page instance, when the current details sub-page is component-based. */
|
|
1798
2005
|
getActivePageComponentInstance() {
|
|
1799
2006
|
const componentKey = this.getActivePageComponentKey();
|
|
@@ -1818,6 +2025,12 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
|
|
|
1818
2025
|
static isComponentPageContent(item) {
|
|
1819
2026
|
return 'type' in item && item.type === 'component';
|
|
1820
2027
|
}
|
|
2028
|
+
pageContentTrackKey(item, index) {
|
|
2029
|
+
if (AXPLayoutDetailsViewComponent.isComponentPageContent(item)) {
|
|
2030
|
+
return `component:${item.componentKey}`;
|
|
2031
|
+
}
|
|
2032
|
+
return item.name ?? `widget:${index}`;
|
|
2033
|
+
}
|
|
1821
2034
|
/** Whether the active component-based entity page has unsaved edits. */
|
|
1822
2035
|
isActivePageComponentDirty() {
|
|
1823
2036
|
return this.getActivePageComponentInstance()?.hasUnsavedChanges() ?? false;
|
|
@@ -1833,8 +2046,13 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
|
|
|
1833
2046
|
return this.liveFormIsDirty();
|
|
1834
2047
|
}
|
|
1835
2048
|
/** Single gate for leaving the page or switching details sub-pages when dirty. */
|
|
1836
|
-
confirmLeaveIfDirty() {
|
|
1837
|
-
|
|
2049
|
+
async confirmLeaveIfDirty() {
|
|
2050
|
+
const shouldPrompt = this.shouldPromptLeave();
|
|
2051
|
+
const confirmed = await this.unsavedChangesConfirm.confirmIfDirty(shouldPrompt);
|
|
2052
|
+
if (confirmed && shouldPrompt) {
|
|
2053
|
+
await this.discardUnsavedChanges();
|
|
2054
|
+
}
|
|
2055
|
+
return confirmed;
|
|
1838
2056
|
}
|
|
1839
2057
|
//#endregion
|
|
1840
2058
|
//#region ---- Command Execution ----
|
|
@@ -1853,9 +2071,7 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
|
|
|
1853
2071
|
return;
|
|
1854
2072
|
}
|
|
1855
2073
|
if (command.name == rejectCommand) {
|
|
1856
|
-
await this.
|
|
1857
|
-
this.finalizeDiscard();
|
|
1858
|
-
this.recompute();
|
|
2074
|
+
await this.discardUnsavedChanges();
|
|
1859
2075
|
return;
|
|
1860
2076
|
}
|
|
1861
2077
|
// Handle status update command
|
|
@@ -1918,9 +2134,7 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
|
|
|
1918
2134
|
return;
|
|
1919
2135
|
}
|
|
1920
2136
|
if (isDiscardCommand(command.name, rejectCommand)) {
|
|
1921
|
-
await this.
|
|
1922
|
-
this.finalizeDiscard();
|
|
1923
|
-
this.recompute();
|
|
2137
|
+
await this.discardUnsavedChanges();
|
|
1924
2138
|
return;
|
|
1925
2139
|
}
|
|
1926
2140
|
if (!this.isActivePageComponentDirty()) {
|
|
@@ -2129,7 +2343,7 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
|
|
|
2129
2343
|
useExisting: AXPLayoutDetailsViewComponent,
|
|
2130
2344
|
},
|
|
2131
2345
|
AXPLayoutDetailsViewViewModel,
|
|
2132
|
-
], viewQueries: [{ propertyName: "form", first: true, predicate: ["form"], descendants: true, isSignal: true }, { propertyName: "widgetContainer", first: true, predicate: AXPWidgetContainerComponent, descendants: true, isSignal: true }, { propertyName: "sidebarTabs", first: true, predicate: ["sidebarTabs"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<axp-page-layout *translate=\"let t\">\n @if (vm.adapter()?.pages?.length! > 1 && vm.isLarge()) {\n <axp-layout-start-side>\n <axp-layout-header>\n <axp-layout-title>{{\n (formatService.format(vm.showPages() ? (vm.adapter()?.title ?? '') : (vm.adapter()?.label ?? ''), 'string', vm.rootContext())\n | translate\n | async)\n }}</axp-layout-title>\n <axp-layout-description>{{\n (formatService.format(vm.adapter()?.description ?? '', 'string', vm.rootContext()) | translate | async)\n }}</axp-layout-description>\n </axp-layout-header>\n <axp-layout-content>\n <ax-tabs\n #sidebarTabs\n class=\"axp-vertical-tabs\"\n [look]=\"'with-line-color'\"\n [location]=\"'end'\"\n [fitParent]=\"true\"\n (onActiveTabChanged)=\"handleSidebarTabChanged($event)\"\n >\n @for (item of vm.adapter()?.pages; track item.id) {\n <ax-tab-item\n [key]=\"item.id\"\n [text]=\"(formatService.format(item.label, 'string', vm.rootContext()) | translate | async)!\"\n [active]=\"vm.currentPage()?.id === item.id\"\n >\n <ax-prefix>\n <ax-icon icon=\"far ${{ item.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-tab-item>\n }\n </ax-tabs>\n </axp-layout-content>\n </axp-layout-start-side>\n }\n <!-- Content Section -->\n <axp-page-content class=\"ax-flex ax-flex-row ax-gap-4\">\n @if (vm.showPages()) {\n <axp-layout-list class=\"ax-w-full\">\n @for (item of vm.adapter()?.pages; track $index) {\n <axp-layout-list-item (click)=\"handleSelectPage(item)\">\n <axp-layout-prefix>\n <ax-icon icon=\"far ${{ item.icon }}\" class=\"ax-text-gray-500\"></ax-icon>\n </axp-layout-prefix>\n <axp-layout-content>\n <a class=\"ax-font-semibold\">{{\n formatService.format(item.label, 'string', vm.rootContext()) | translate | async\n }}</a>\n </axp-layout-content>\n <axp-layout-suffix>\n <ax-icon icon=\" far fa-chevron-right\" class=\"ax-text-gray-400\"></ax-icon>\n </axp-layout-suffix>\n </axp-layout-list-item>\n }\n </axp-layout-list>\n } @else {\n @if (vm.isBusy()) {\n <div class=\"ax-flex ax-h-full ax-w-full ax-items-center ax-justify-center\">\n <ax-loading></ax-loading>\n </div>\n } @else {\n <ax-form class=\"ax-h-full ax-w-full\" #form>\n <axp-widgets-container\n #widgetsContainerRef\n [context]=\"vm.context()\"\n (onContextChanged)=\"handleOnContextChanged($event)\"\n >\n <div class=\"ax-flex ax-flex-col ax-w-full ax-gap-4 ax-h-full\">\n @for (item of vm.currentPageContent(); track $index) {\n @if ('type' in item && item.type === 'component') {\n <!-- Component-based content -->\n <div\n axp-page-component-renderer\n [componentKey]=\"$any(item).componentKey\"\n [rootContext]=\"vm.rootContext()\"\n [pageConfig]=\"$any(item).pageConfig\"\n [options]=\"$any(item).options\"\n (componentReady)=\"recompute()\"\n ></div>\n } @else {\n <!-- Widget-based content -->\n <ng-container axp-widget-renderer [node]=\"$any(item)\" [mode]=\"$any(item).mode ?? 'view'\"></ng-container>\n }\n }\n @if (vm.currentPage()?.tabs?.length) {\n <div class=\"axp-horizontal-tabs\">\n <ax-tabs [location]=\"'bottom'\" [fitParent]=\"false\" look=\"classic\">\n @for (tab of vm.currentPageTabs(); track $index) {\n <ax-tab-item\n (onClick)=\"vm.setCurrentTab(tab)\"\n [text]=\"(tab.title | translate | async)!\"\n [active]=\"vm.currentTab()?.id === tab.id\"\n >\n <ax-prefix>\n <ax-icon icon=\"far ${{ tab.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-tab-item>\n }\n </ax-tabs>\n <div class=\"content\">\n @if (vm.currentTab()) {\n @for (content of vm.activeTabContent(); track content.name) {\n <ng-container axp-widget-renderer [node]=\"content\" [mode]=\"content.mode ?? 'view'\">\n </ng-container>\n }\n }\n </div>\n </div>\n }\n </div>\n </axp-widgets-container>\n </ax-form>\n }\n }\n </axp-page-content>\n\n <!-- Footer Section -->\n @if (hasFooter()) {\n <axp-page-footer class=\"--animated\">\n <axp-layout-suffix>\n <!-- secondary footer actions -->\n @if (hasVisibleFooterSecondaryActions()) {\n <ax-button\n [class.ax-sm]=\"deviceService.isSmall()\"\n [iconOnly]=\"deviceService.isSmall()\"\n [text]=\"'@general:terms.interface.actions' | translate | async\"\n [look]=\"deviceService.isSmall() ? 'blank' : 'solid'\"\n [color]=\"'default'\"\n >\n <ax-prefix>\n <i class=\"fa-solid fa-ellipsis-vertical\"></i>\n </ax-prefix>\n <ax-dropdown-panel #panel>\n <ax-button-item-list>\n @for (action of footerSecondaryActions(); track $index) {\n @if (isFooterActionVisible(action)) {\n <ax-button-item\n [text]=\"(t(action.title) | async)!\"\n [color]=\"action.color\"\n [disabled]=\"isFooterActionDisabled(action)\"\n (onClick)=\"execute(action.command!)\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-light {{ action.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n @if (action.break) {\n <ax-divider></ax-divider>\n }\n }\n }\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-button>\n }\n\n @if (hasVisibleFooterPrimaryActions()) {\n @if (showSaveDiscardFooter()) {\n @if (rejectFooterCommand(); as reject) {\n <ax-button\n [class.ax-sm]=\"deviceService.isSmall()\"\n [disabled]=\"vm.isSaving()\"\n [text]=\"(t(reject.title) | async)!\"\n [look]=\"'solid'\"\n [color]=\"reject.color\"\n (onClick)=\"execute(reject.command)\"\n >\n <ax-prefix>\n <i class=\"{{ reject.icon }}\"></i>\n </ax-prefix>\n </ax-button>\n }\n @if (acceptFooterCommand(); as accept) {\n <ax-button\n [class.ax-sm]=\"deviceService.isSmall()\"\n [disabled]=\"vm.isSaving()\"\n [text]=\"(t(accept.title) | async)!\"\n [look]=\"'solid'\"\n [color]=\"accept.color\"\n (onClick)=\"execute(accept.command)\"\n >\n <ax-prefix>\n <i class=\"{{ accept.icon }}\"></i>\n </ax-prefix>\n </ax-button>\n }\n }\n @for (action of footerPrimaryActions(); track $index) {\n @if (isFooterActionVisible(action)) {\n <ax-button\n [class.ax-sm]=\"deviceService.isSmall()\"\n [disabled]=\"isFooterActionDisabled(action)\"\n [text]=\"(t(action.title) | async)!\"\n [look]=\"'solid'\"\n [color]=\"action.color\"\n (onClick)=\"execute(action.command!)\"\n >\n <ax-prefix>\n <i class=\"{{ action.icon }}\"></i>\n </ax-prefix>\n @if (action?.items) {\n <ax-dropdown-panel #panel>\n <ax-button-item-list>\n @for (sub of action?.items; track $index) {\n @if (isFooterActionVisible(sub)) {\n <ax-button-item\n [text]=\"(t(sub.title) | async)!\"\n [color]=\"sub.color\"\n [disabled]=\"isFooterActionDisabled(sub)\"\n (onClick)=\"execute(sub.command!)\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-light {{ sub.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n @if (sub.break) {\n <ax-divider></ax-divider>\n }\n }\n }\n </ax-button-item-list>\n </ax-dropdown-panel>\n }\n </ax-button>\n }\n }\n }\n </axp-layout-suffix>\n </axp-page-footer>\n }\n</axp-page-layout>\n", styles: ["axp-layout-details-view .axp-vertical-tabs{--ax-comp-tabs-default-border-radius: 0}axp-layout-details-view .axp-vertical-tabs ax-tab-item{margin-top:0!important;margin-bottom:0!important;padding-top:.75rem!important;padding-bottom:.75rem!important;font-weight:600!important}axp-layout-details-view .axp-vertical-tabs ax-tab-item>div{display:flex!important;align-items:center!important}axp-layout-details-view .axp-vertical-tabs ax-tab-item>div .ax-tab-item-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}axp-layout-details-view .axp-horizontal-tabs{display:flex;width:100%;flex-direction:column}axp-layout-details-view .axp-horizontal-tabs .content{margin-top:1rem;margin-bottom:1rem;display:block;width:100%}axp-layout-details-view axp-layout-section axp-layout-content{display:flex;width:100%;flex-direction:column}axp-layout-details-view axp-layout-section axp-layout-content>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse));border-style:dashed;--tw-divide-opacity: 1;border-color:rgba(var(--ax-sys-color-border-lightest-surface),var(--tw-divide-opacity, 1))}axp-layout-details-view axp-layout-section axp-layout-content .--item-container{align-items:center;gap:1rem;padding:1rem}@media(min-width:1024px){axp-layout-details-view axp-layout-section axp-layout-content .--item-container{padding-left:1.5rem;padding-right:1.5rem}}@media(min-width:1280px){axp-layout-details-view axp-layout-section axp-layout-content .--item-container{padding-left:2rem;padding-right:2rem}}@media(min-width:1536px){axp-layout-details-view axp-layout-section axp-layout-content .--item-container{padding-left:2.5rem;padding-right:2.5rem}}axp-layout-details-view axp-layout-section axp-layout-content .--item-container{display:grid;grid-template-columns:repeat(12,minmax(0,1fr))}axp-layout-details-view axp-layout-section axp-layout-content .--item-container axp-layout-description{margin-top:.5rem!important;font-size:.75rem!important;line-height:1rem!important}axp-layout-details-view axp-page-content ax-form form{height:100%}axp-layout-details-view axp-layout-start-side{border-inline-end-width:1px}\n"], dependencies: [{ kind: "ngmodule", type:
|
|
2346
|
+
], viewQueries: [{ propertyName: "form", first: true, predicate: ["form"], descendants: true, isSignal: true }, { propertyName: "widgetContainer", first: true, predicate: AXPWidgetContainerComponent, descendants: true, isSignal: true }, { propertyName: "sidebarTabs", first: true, predicate: ["sidebarTabs"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<axp-page-layout *translate=\"let t\">\n @if (vm.adapter()?.pages?.length! > 1 && vm.isLarge()) {\n <axp-layout-start-side>\n <axp-layout-header>\n <axp-layout-title>{{\n formatService.format(\n vm.showPages() ? (vm.adapter()?.title ?? '') : (vm.adapter()?.label ?? ''),\n 'string',\n vm.rootContext()\n )\n | translate\n | async\n }}</axp-layout-title>\n <axp-layout-description>{{\n formatService.format(vm.adapter()?.description ?? '', 'string', vm.rootContext()) | translate | async\n }}</axp-layout-description>\n </axp-layout-header>\n <axp-layout-content>\n <ax-tabs\n #sidebarTabs\n class=\"axp-vertical-tabs\"\n [look]=\"'with-line-color'\"\n [location]=\"'end'\"\n [fitParent]=\"true\"\n (onActiveTabChanged)=\"handleSidebarTabChanged($event)\"\n >\n @for (item of vm.adapter()?.pages; track item.id) {\n <ax-tab-item\n [key]=\"item.id\"\n [text]=\"(formatService.format(item.label, 'string', vm.rootContext()) | translate | async)!\"\n [active]=\"vm.currentPage()?.id === item.id\"\n >\n <ax-prefix>\n <ax-icon icon=\"far ${{ item.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-tab-item>\n }\n </ax-tabs>\n </axp-layout-content>\n </axp-layout-start-side>\n }\n <!-- Content Section -->\n <axp-page-content class=\"ax-flex ax-flex-row ax-gap-4\">\n @if (vm.showPages()) {\n <axp-layout-list class=\"ax-w-full\">\n @for (item of vm.adapter()?.pages; track $index) {\n <axp-layout-list-item (click)=\"handleSelectPage(item)\">\n <axp-layout-prefix>\n <ax-icon icon=\"far ${{ item.icon }}\" class=\"ax-text-gray-500\"></ax-icon>\n </axp-layout-prefix>\n <axp-layout-content>\n <a class=\"ax-font-semibold\">{{\n formatService.format(item.label, 'string', vm.rootContext()) | translate | async\n }}</a>\n </axp-layout-content>\n <axp-layout-suffix>\n <ax-icon icon=\" far fa-chevron-right\" class=\"ax-text-gray-400\"></ax-icon>\n </axp-layout-suffix>\n </axp-layout-list-item>\n }\n </axp-layout-list>\n } @else {\n @if (vm.isBusy()) {\n <div class=\"ax-flex ax-h-full ax-w-full ax-items-center ax-justify-center\">\n <ax-loading></ax-loading>\n </div>\n } @else {\n <ax-form class=\"ax-h-full ax-w-full\" #form>\n <axp-widgets-container\n #widgetsContainerRef\n [context]=\"vm.context()\"\n (onContextChanged)=\"handleOnContextChanged($event)\"\n >\n <div class=\"ax-flex ax-flex-col ax-w-full ax-gap-4 ax-h-full\">\n @for (item of vm.currentPageContent(); track pageContentTrackKey($any(item), $index)) {\n @if ('type' in item && item.type === 'component') {\n <!-- Component-based content -->\n <div\n axp-page-component-renderer\n [componentKey]=\"$any(item).componentKey\"\n [rootContext]=\"vm.rootContext()\"\n [pageConfig]=\"$any(item).pageConfig\"\n [options]=\"$any(item).options\"\n (componentReady)=\"recompute()\"\n ></div>\n } @else {\n <!-- Widget-based content -->\n <ng-container axp-widget-renderer [node]=\"$any(item)\" [mode]=\"$any(item).mode ?? 'view'\"></ng-container>\n }\n }\n @if (vm.currentPage()?.tabs?.length) {\n <div class=\"axp-horizontal-tabs\">\n <ax-tabs [location]=\"'bottom'\" [fitParent]=\"false\" look=\"classic\">\n @for (tab of vm.currentPageTabs(); track $index) {\n <ax-tab-item\n (onClick)=\"vm.setCurrentTab(tab)\"\n [text]=\"(tab.title | translate | async)!\"\n [active]=\"vm.currentTab()?.id === tab.id\"\n >\n <ax-prefix>\n <ax-icon icon=\"far ${{ tab.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-tab-item>\n }\n </ax-tabs>\n <div class=\"content\">\n @if (vm.currentTab()) {\n @for (content of vm.activeTabContent(); track content.name) {\n <ng-container axp-widget-renderer [node]=\"content\" [mode]=\"content.mode ?? 'view'\">\n </ng-container>\n }\n }\n </div>\n </div>\n }\n </div>\n </axp-widgets-container>\n </ax-form>\n }\n }\n </axp-page-content>\n\n <!-- Footer Section -->\n @if (hasFooter()) {\n <axp-page-footer class=\"--animated\">\n <axp-layout-suffix>\n <!-- secondary footer actions -->\n @if (hasVisibleFooterSecondaryActions()) {\n <ax-button\n [class.ax-sm]=\"deviceService.isSmall()\"\n [iconOnly]=\"deviceService.isSmall()\"\n [text]=\"'@general:terms.interface.actions' | translate | async\"\n [look]=\"deviceService.isSmall() ? 'blank' : 'solid'\"\n [color]=\"'default'\"\n >\n <ax-prefix>\n <i class=\"fa-solid fa-ellipsis-vertical\"></i>\n </ax-prefix>\n <ax-dropdown-panel #panel>\n <ax-button-item-list>\n @for (action of footerSecondaryActions(); track $index) {\n @if (isFooterActionVisible(action)) {\n <ax-button-item\n [text]=\"(t(action.title) | async)!\"\n [color]=\"action.color\"\n [disabled]=\"isFooterActionDisabled(action)\"\n (onClick)=\"execute(action.command!)\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-light {{ action.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n @if (action.break) {\n <ax-divider></ax-divider>\n }\n }\n }\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-button>\n }\n\n @if (hasVisibleFooterPrimaryActions()) {\n @if (showSaveDiscardFooter()) {\n @if (rejectFooterCommand(); as reject) {\n <ax-button\n [class.ax-sm]=\"deviceService.isSmall()\"\n [disabled]=\"vm.isSaving()\"\n [text]=\"(t(reject.title) | async)!\"\n [look]=\"'solid'\"\n [color]=\"reject.color\"\n (onClick)=\"execute(reject.command)\"\n >\n <ax-prefix>\n <i class=\"{{ reject.icon }}\"></i>\n </ax-prefix>\n </ax-button>\n }\n @if (acceptFooterCommand(); as accept) {\n <ax-button\n [class.ax-sm]=\"deviceService.isSmall()\"\n [disabled]=\"vm.isSaving()\"\n [text]=\"(t(accept.title) | async)!\"\n [look]=\"'solid'\"\n [color]=\"accept.color\"\n (onClick)=\"execute(accept.command)\"\n >\n <ax-prefix>\n <i class=\"{{ accept.icon }}\"></i>\n </ax-prefix>\n </ax-button>\n }\n }\n @for (action of footerPrimaryActions(); track $index) {\n @if (isFooterActionVisible(action)) {\n <ax-button\n [class.ax-sm]=\"deviceService.isSmall()\"\n [disabled]=\"isFooterActionDisabled(action)\"\n [text]=\"(t(action.title) | async)!\"\n [look]=\"'solid'\"\n [color]=\"action.color\"\n (onClick)=\"execute(action.command!)\"\n >\n <ax-prefix>\n <i class=\"{{ action.icon }}\"></i>\n </ax-prefix>\n @if (action?.items) {\n <ax-dropdown-panel #panel>\n <ax-button-item-list>\n @for (sub of action?.items; track $index) {\n @if (isFooterActionVisible(sub)) {\n <ax-button-item\n [text]=\"(t(sub.title) | async)!\"\n [color]=\"sub.color\"\n [disabled]=\"isFooterActionDisabled(sub)\"\n (onClick)=\"execute(sub.command!)\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-light {{ sub.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n @if (sub.break) {\n <ax-divider></ax-divider>\n }\n }\n }\n </ax-button-item-list>\n </ax-dropdown-panel>\n }\n </ax-button>\n }\n }\n }\n </axp-layout-suffix>\n </axp-page-footer>\n }\n</axp-page-layout>\n", styles: ["axp-layout-details-view .axp-vertical-tabs{--ax-comp-tabs-default-border-radius: 0}axp-layout-details-view .axp-vertical-tabs ax-tab-item{margin-top:0!important;margin-bottom:0!important;padding-top:.75rem!important;padding-bottom:.75rem!important;font-weight:600!important}axp-layout-details-view .axp-vertical-tabs ax-tab-item>div{display:flex!important;align-items:center!important}axp-layout-details-view .axp-vertical-tabs ax-tab-item>div .ax-tab-item-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}axp-layout-details-view .axp-horizontal-tabs{display:flex;width:100%;flex-direction:column}axp-layout-details-view .axp-horizontal-tabs .content{margin-top:1rem;margin-bottom:1rem;display:block;width:100%}axp-layout-details-view axp-layout-section axp-layout-content{display:flex;width:100%;flex-direction:column}axp-layout-details-view axp-layout-section axp-layout-content>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse));border-style:dashed;--tw-divide-opacity: 1;border-color:rgba(var(--ax-sys-color-border-lightest-surface),var(--tw-divide-opacity, 1))}axp-layout-details-view axp-layout-section axp-layout-content .--item-container{align-items:center;gap:1rem;padding:1rem}@media(min-width:1024px){axp-layout-details-view axp-layout-section axp-layout-content .--item-container{padding-left:1.5rem;padding-right:1.5rem}}@media(min-width:1280px){axp-layout-details-view axp-layout-section axp-layout-content .--item-container{padding-left:2rem;padding-right:2rem}}@media(min-width:1536px){axp-layout-details-view axp-layout-section axp-layout-content .--item-container{padding-left:2.5rem;padding-right:2.5rem}}axp-layout-details-view axp-layout-section axp-layout-content .--item-container{display:grid;grid-template-columns:repeat(12,minmax(0,1fr))}axp-layout-details-view axp-layout-section axp-layout-content .--item-container axp-layout-description{margin-top:.5rem!important;font-size:.75rem!important;line-height:1rem!important}axp-layout-details-view axp-page-content ax-form form{height:100%}axp-layout-details-view axp-layout-start-side{border-inline-end-width:1px}\n"], dependencies: [{ kind: "ngmodule", type:
|
|
2133
2347
|
// Angular
|
|
2134
2348
|
CommonModule }, { kind: "ngmodule", type:
|
|
2135
2349
|
// ACoreX
|
|
@@ -2175,7 +2389,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
2175
2389
|
useExisting: AXPLayoutDetailsViewComponent,
|
|
2176
2390
|
},
|
|
2177
2391
|
AXPLayoutDetailsViewViewModel,
|
|
2178
|
-
], template: "<axp-page-layout *translate=\"let t\">\n @if (vm.adapter()?.pages?.length! > 1 && vm.isLarge()) {\n <axp-layout-start-side>\n <axp-layout-header>\n <axp-layout-title>{{\n (formatService.format(vm.showPages() ? (vm.adapter()?.title ?? '') : (vm.adapter()?.label ?? ''), 'string', vm.rootContext())\n | translate\n | async)\n }}</axp-layout-title>\n <axp-layout-description>{{\n (formatService.format(vm.adapter()?.description ?? '', 'string', vm.rootContext()) | translate | async)\n }}</axp-layout-description>\n </axp-layout-header>\n <axp-layout-content>\n <ax-tabs\n #sidebarTabs\n class=\"axp-vertical-tabs\"\n [look]=\"'with-line-color'\"\n [location]=\"'end'\"\n [fitParent]=\"true\"\n (onActiveTabChanged)=\"handleSidebarTabChanged($event)\"\n >\n @for (item of vm.adapter()?.pages; track item.id) {\n <ax-tab-item\n [key]=\"item.id\"\n [text]=\"(formatService.format(item.label, 'string', vm.rootContext()) | translate | async)!\"\n [active]=\"vm.currentPage()?.id === item.id\"\n >\n <ax-prefix>\n <ax-icon icon=\"far ${{ item.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-tab-item>\n }\n </ax-tabs>\n </axp-layout-content>\n </axp-layout-start-side>\n }\n <!-- Content Section -->\n <axp-page-content class=\"ax-flex ax-flex-row ax-gap-4\">\n @if (vm.showPages()) {\n <axp-layout-list class=\"ax-w-full\">\n @for (item of vm.adapter()?.pages; track $index) {\n <axp-layout-list-item (click)=\"handleSelectPage(item)\">\n <axp-layout-prefix>\n <ax-icon icon=\"far ${{ item.icon }}\" class=\"ax-text-gray-500\"></ax-icon>\n </axp-layout-prefix>\n <axp-layout-content>\n <a class=\"ax-font-semibold\">{{\n formatService.format(item.label, 'string', vm.rootContext()) | translate | async\n }}</a>\n </axp-layout-content>\n <axp-layout-suffix>\n <ax-icon icon=\" far fa-chevron-right\" class=\"ax-text-gray-400\"></ax-icon>\n </axp-layout-suffix>\n </axp-layout-list-item>\n }\n </axp-layout-list>\n } @else {\n @if (vm.isBusy()) {\n <div class=\"ax-flex ax-h-full ax-w-full ax-items-center ax-justify-center\">\n <ax-loading></ax-loading>\n </div>\n } @else {\n <ax-form class=\"ax-h-full ax-w-full\" #form>\n <axp-widgets-container\n #widgetsContainerRef\n [context]=\"vm.context()\"\n (onContextChanged)=\"handleOnContextChanged($event)\"\n >\n <div class=\"ax-flex ax-flex-col ax-w-full ax-gap-4 ax-h-full\">\n @for (item of vm.currentPageContent(); track $index) {\n @if ('type' in item && item.type === 'component') {\n <!-- Component-based content -->\n <div\n axp-page-component-renderer\n [componentKey]=\"$any(item).componentKey\"\n [rootContext]=\"vm.rootContext()\"\n [pageConfig]=\"$any(item).pageConfig\"\n [options]=\"$any(item).options\"\n (componentReady)=\"recompute()\"\n ></div>\n } @else {\n <!-- Widget-based content -->\n <ng-container axp-widget-renderer [node]=\"$any(item)\" [mode]=\"$any(item).mode ?? 'view'\"></ng-container>\n }\n }\n @if (vm.currentPage()?.tabs?.length) {\n <div class=\"axp-horizontal-tabs\">\n <ax-tabs [location]=\"'bottom'\" [fitParent]=\"false\" look=\"classic\">\n @for (tab of vm.currentPageTabs(); track $index) {\n <ax-tab-item\n (onClick)=\"vm.setCurrentTab(tab)\"\n [text]=\"(tab.title | translate | async)!\"\n [active]=\"vm.currentTab()?.id === tab.id\"\n >\n <ax-prefix>\n <ax-icon icon=\"far ${{ tab.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-tab-item>\n }\n </ax-tabs>\n <div class=\"content\">\n @if (vm.currentTab()) {\n @for (content of vm.activeTabContent(); track content.name) {\n <ng-container axp-widget-renderer [node]=\"content\" [mode]=\"content.mode ?? 'view'\">\n </ng-container>\n }\n }\n </div>\n </div>\n }\n </div>\n </axp-widgets-container>\n </ax-form>\n }\n }\n </axp-page-content>\n\n <!-- Footer Section -->\n @if (hasFooter()) {\n <axp-page-footer class=\"--animated\">\n <axp-layout-suffix>\n <!-- secondary footer actions -->\n @if (hasVisibleFooterSecondaryActions()) {\n <ax-button\n [class.ax-sm]=\"deviceService.isSmall()\"\n [iconOnly]=\"deviceService.isSmall()\"\n [text]=\"'@general:terms.interface.actions' | translate | async\"\n [look]=\"deviceService.isSmall() ? 'blank' : 'solid'\"\n [color]=\"'default'\"\n >\n <ax-prefix>\n <i class=\"fa-solid fa-ellipsis-vertical\"></i>\n </ax-prefix>\n <ax-dropdown-panel #panel>\n <ax-button-item-list>\n @for (action of footerSecondaryActions(); track $index) {\n @if (isFooterActionVisible(action)) {\n <ax-button-item\n [text]=\"(t(action.title) | async)!\"\n [color]=\"action.color\"\n [disabled]=\"isFooterActionDisabled(action)\"\n (onClick)=\"execute(action.command!)\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-light {{ action.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n @if (action.break) {\n <ax-divider></ax-divider>\n }\n }\n }\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-button>\n }\n\n @if (hasVisibleFooterPrimaryActions()) {\n @if (showSaveDiscardFooter()) {\n @if (rejectFooterCommand(); as reject) {\n <ax-button\n [class.ax-sm]=\"deviceService.isSmall()\"\n [disabled]=\"vm.isSaving()\"\n [text]=\"(t(reject.title) | async)!\"\n [look]=\"'solid'\"\n [color]=\"reject.color\"\n (onClick)=\"execute(reject.command)\"\n >\n <ax-prefix>\n <i class=\"{{ reject.icon }}\"></i>\n </ax-prefix>\n </ax-button>\n }\n @if (acceptFooterCommand(); as accept) {\n <ax-button\n [class.ax-sm]=\"deviceService.isSmall()\"\n [disabled]=\"vm.isSaving()\"\n [text]=\"(t(accept.title) | async)!\"\n [look]=\"'solid'\"\n [color]=\"accept.color\"\n (onClick)=\"execute(accept.command)\"\n >\n <ax-prefix>\n <i class=\"{{ accept.icon }}\"></i>\n </ax-prefix>\n </ax-button>\n }\n }\n @for (action of footerPrimaryActions(); track $index) {\n @if (isFooterActionVisible(action)) {\n <ax-button\n [class.ax-sm]=\"deviceService.isSmall()\"\n [disabled]=\"isFooterActionDisabled(action)\"\n [text]=\"(t(action.title) | async)!\"\n [look]=\"'solid'\"\n [color]=\"action.color\"\n (onClick)=\"execute(action.command!)\"\n >\n <ax-prefix>\n <i class=\"{{ action.icon }}\"></i>\n </ax-prefix>\n @if (action?.items) {\n <ax-dropdown-panel #panel>\n <ax-button-item-list>\n @for (sub of action?.items; track $index) {\n @if (isFooterActionVisible(sub)) {\n <ax-button-item\n [text]=\"(t(sub.title) | async)!\"\n [color]=\"sub.color\"\n [disabled]=\"isFooterActionDisabled(sub)\"\n (onClick)=\"execute(sub.command!)\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-light {{ sub.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n @if (sub.break) {\n <ax-divider></ax-divider>\n }\n }\n }\n </ax-button-item-list>\n </ax-dropdown-panel>\n }\n </ax-button>\n }\n }\n }\n </axp-layout-suffix>\n </axp-page-footer>\n }\n</axp-page-layout>\n", styles: ["axp-layout-details-view .axp-vertical-tabs{--ax-comp-tabs-default-border-radius: 0}axp-layout-details-view .axp-vertical-tabs ax-tab-item{margin-top:0!important;margin-bottom:0!important;padding-top:.75rem!important;padding-bottom:.75rem!important;font-weight:600!important}axp-layout-details-view .axp-vertical-tabs ax-tab-item>div{display:flex!important;align-items:center!important}axp-layout-details-view .axp-vertical-tabs ax-tab-item>div .ax-tab-item-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}axp-layout-details-view .axp-horizontal-tabs{display:flex;width:100%;flex-direction:column}axp-layout-details-view .axp-horizontal-tabs .content{margin-top:1rem;margin-bottom:1rem;display:block;width:100%}axp-layout-details-view axp-layout-section axp-layout-content{display:flex;width:100%;flex-direction:column}axp-layout-details-view axp-layout-section axp-layout-content>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse));border-style:dashed;--tw-divide-opacity: 1;border-color:rgba(var(--ax-sys-color-border-lightest-surface),var(--tw-divide-opacity, 1))}axp-layout-details-view axp-layout-section axp-layout-content .--item-container{align-items:center;gap:1rem;padding:1rem}@media(min-width:1024px){axp-layout-details-view axp-layout-section axp-layout-content .--item-container{padding-left:1.5rem;padding-right:1.5rem}}@media(min-width:1280px){axp-layout-details-view axp-layout-section axp-layout-content .--item-container{padding-left:2rem;padding-right:2rem}}@media(min-width:1536px){axp-layout-details-view axp-layout-section axp-layout-content .--item-container{padding-left:2.5rem;padding-right:2.5rem}}axp-layout-details-view axp-layout-section axp-layout-content .--item-container{display:grid;grid-template-columns:repeat(12,minmax(0,1fr))}axp-layout-details-view axp-layout-section axp-layout-content .--item-container axp-layout-description{margin-top:.5rem!important;font-size:.75rem!important;line-height:1rem!important}axp-layout-details-view axp-page-content ax-form form{height:100%}axp-layout-details-view axp-layout-start-side{border-inline-end-width:1px}\n"] }]
|
|
2392
|
+
], template: "<axp-page-layout *translate=\"let t\">\n @if (vm.adapter()?.pages?.length! > 1 && vm.isLarge()) {\n <axp-layout-start-side>\n <axp-layout-header>\n <axp-layout-title>{{\n formatService.format(\n vm.showPages() ? (vm.adapter()?.title ?? '') : (vm.adapter()?.label ?? ''),\n 'string',\n vm.rootContext()\n )\n | translate\n | async\n }}</axp-layout-title>\n <axp-layout-description>{{\n formatService.format(vm.adapter()?.description ?? '', 'string', vm.rootContext()) | translate | async\n }}</axp-layout-description>\n </axp-layout-header>\n <axp-layout-content>\n <ax-tabs\n #sidebarTabs\n class=\"axp-vertical-tabs\"\n [look]=\"'with-line-color'\"\n [location]=\"'end'\"\n [fitParent]=\"true\"\n (onActiveTabChanged)=\"handleSidebarTabChanged($event)\"\n >\n @for (item of vm.adapter()?.pages; track item.id) {\n <ax-tab-item\n [key]=\"item.id\"\n [text]=\"(formatService.format(item.label, 'string', vm.rootContext()) | translate | async)!\"\n [active]=\"vm.currentPage()?.id === item.id\"\n >\n <ax-prefix>\n <ax-icon icon=\"far ${{ item.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-tab-item>\n }\n </ax-tabs>\n </axp-layout-content>\n </axp-layout-start-side>\n }\n <!-- Content Section -->\n <axp-page-content class=\"ax-flex ax-flex-row ax-gap-4\">\n @if (vm.showPages()) {\n <axp-layout-list class=\"ax-w-full\">\n @for (item of vm.adapter()?.pages; track $index) {\n <axp-layout-list-item (click)=\"handleSelectPage(item)\">\n <axp-layout-prefix>\n <ax-icon icon=\"far ${{ item.icon }}\" class=\"ax-text-gray-500\"></ax-icon>\n </axp-layout-prefix>\n <axp-layout-content>\n <a class=\"ax-font-semibold\">{{\n formatService.format(item.label, 'string', vm.rootContext()) | translate | async\n }}</a>\n </axp-layout-content>\n <axp-layout-suffix>\n <ax-icon icon=\" far fa-chevron-right\" class=\"ax-text-gray-400\"></ax-icon>\n </axp-layout-suffix>\n </axp-layout-list-item>\n }\n </axp-layout-list>\n } @else {\n @if (vm.isBusy()) {\n <div class=\"ax-flex ax-h-full ax-w-full ax-items-center ax-justify-center\">\n <ax-loading></ax-loading>\n </div>\n } @else {\n <ax-form class=\"ax-h-full ax-w-full\" #form>\n <axp-widgets-container\n #widgetsContainerRef\n [context]=\"vm.context()\"\n (onContextChanged)=\"handleOnContextChanged($event)\"\n >\n <div class=\"ax-flex ax-flex-col ax-w-full ax-gap-4 ax-h-full\">\n @for (item of vm.currentPageContent(); track pageContentTrackKey($any(item), $index)) {\n @if ('type' in item && item.type === 'component') {\n <!-- Component-based content -->\n <div\n axp-page-component-renderer\n [componentKey]=\"$any(item).componentKey\"\n [rootContext]=\"vm.rootContext()\"\n [pageConfig]=\"$any(item).pageConfig\"\n [options]=\"$any(item).options\"\n (componentReady)=\"recompute()\"\n ></div>\n } @else {\n <!-- Widget-based content -->\n <ng-container axp-widget-renderer [node]=\"$any(item)\" [mode]=\"$any(item).mode ?? 'view'\"></ng-container>\n }\n }\n @if (vm.currentPage()?.tabs?.length) {\n <div class=\"axp-horizontal-tabs\">\n <ax-tabs [location]=\"'bottom'\" [fitParent]=\"false\" look=\"classic\">\n @for (tab of vm.currentPageTabs(); track $index) {\n <ax-tab-item\n (onClick)=\"vm.setCurrentTab(tab)\"\n [text]=\"(tab.title | translate | async)!\"\n [active]=\"vm.currentTab()?.id === tab.id\"\n >\n <ax-prefix>\n <ax-icon icon=\"far ${{ tab.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-tab-item>\n }\n </ax-tabs>\n <div class=\"content\">\n @if (vm.currentTab()) {\n @for (content of vm.activeTabContent(); track content.name) {\n <ng-container axp-widget-renderer [node]=\"content\" [mode]=\"content.mode ?? 'view'\">\n </ng-container>\n }\n }\n </div>\n </div>\n }\n </div>\n </axp-widgets-container>\n </ax-form>\n }\n }\n </axp-page-content>\n\n <!-- Footer Section -->\n @if (hasFooter()) {\n <axp-page-footer class=\"--animated\">\n <axp-layout-suffix>\n <!-- secondary footer actions -->\n @if (hasVisibleFooterSecondaryActions()) {\n <ax-button\n [class.ax-sm]=\"deviceService.isSmall()\"\n [iconOnly]=\"deviceService.isSmall()\"\n [text]=\"'@general:terms.interface.actions' | translate | async\"\n [look]=\"deviceService.isSmall() ? 'blank' : 'solid'\"\n [color]=\"'default'\"\n >\n <ax-prefix>\n <i class=\"fa-solid fa-ellipsis-vertical\"></i>\n </ax-prefix>\n <ax-dropdown-panel #panel>\n <ax-button-item-list>\n @for (action of footerSecondaryActions(); track $index) {\n @if (isFooterActionVisible(action)) {\n <ax-button-item\n [text]=\"(t(action.title) | async)!\"\n [color]=\"action.color\"\n [disabled]=\"isFooterActionDisabled(action)\"\n (onClick)=\"execute(action.command!)\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-light {{ action.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n @if (action.break) {\n <ax-divider></ax-divider>\n }\n }\n }\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-button>\n }\n\n @if (hasVisibleFooterPrimaryActions()) {\n @if (showSaveDiscardFooter()) {\n @if (rejectFooterCommand(); as reject) {\n <ax-button\n [class.ax-sm]=\"deviceService.isSmall()\"\n [disabled]=\"vm.isSaving()\"\n [text]=\"(t(reject.title) | async)!\"\n [look]=\"'solid'\"\n [color]=\"reject.color\"\n (onClick)=\"execute(reject.command)\"\n >\n <ax-prefix>\n <i class=\"{{ reject.icon }}\"></i>\n </ax-prefix>\n </ax-button>\n }\n @if (acceptFooterCommand(); as accept) {\n <ax-button\n [class.ax-sm]=\"deviceService.isSmall()\"\n [disabled]=\"vm.isSaving()\"\n [text]=\"(t(accept.title) | async)!\"\n [look]=\"'solid'\"\n [color]=\"accept.color\"\n (onClick)=\"execute(accept.command)\"\n >\n <ax-prefix>\n <i class=\"{{ accept.icon }}\"></i>\n </ax-prefix>\n </ax-button>\n }\n }\n @for (action of footerPrimaryActions(); track $index) {\n @if (isFooterActionVisible(action)) {\n <ax-button\n [class.ax-sm]=\"deviceService.isSmall()\"\n [disabled]=\"isFooterActionDisabled(action)\"\n [text]=\"(t(action.title) | async)!\"\n [look]=\"'solid'\"\n [color]=\"action.color\"\n (onClick)=\"execute(action.command!)\"\n >\n <ax-prefix>\n <i class=\"{{ action.icon }}\"></i>\n </ax-prefix>\n @if (action?.items) {\n <ax-dropdown-panel #panel>\n <ax-button-item-list>\n @for (sub of action?.items; track $index) {\n @if (isFooterActionVisible(sub)) {\n <ax-button-item\n [text]=\"(t(sub.title) | async)!\"\n [color]=\"sub.color\"\n [disabled]=\"isFooterActionDisabled(sub)\"\n (onClick)=\"execute(sub.command!)\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-light {{ sub.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n @if (sub.break) {\n <ax-divider></ax-divider>\n }\n }\n }\n </ax-button-item-list>\n </ax-dropdown-panel>\n }\n </ax-button>\n }\n }\n }\n </axp-layout-suffix>\n </axp-page-footer>\n }\n</axp-page-layout>\n", styles: ["axp-layout-details-view .axp-vertical-tabs{--ax-comp-tabs-default-border-radius: 0}axp-layout-details-view .axp-vertical-tabs ax-tab-item{margin-top:0!important;margin-bottom:0!important;padding-top:.75rem!important;padding-bottom:.75rem!important;font-weight:600!important}axp-layout-details-view .axp-vertical-tabs ax-tab-item>div{display:flex!important;align-items:center!important}axp-layout-details-view .axp-vertical-tabs ax-tab-item>div .ax-tab-item-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}axp-layout-details-view .axp-horizontal-tabs{display:flex;width:100%;flex-direction:column}axp-layout-details-view .axp-horizontal-tabs .content{margin-top:1rem;margin-bottom:1rem;display:block;width:100%}axp-layout-details-view axp-layout-section axp-layout-content{display:flex;width:100%;flex-direction:column}axp-layout-details-view axp-layout-section axp-layout-content>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse));border-style:dashed;--tw-divide-opacity: 1;border-color:rgba(var(--ax-sys-color-border-lightest-surface),var(--tw-divide-opacity, 1))}axp-layout-details-view axp-layout-section axp-layout-content .--item-container{align-items:center;gap:1rem;padding:1rem}@media(min-width:1024px){axp-layout-details-view axp-layout-section axp-layout-content .--item-container{padding-left:1.5rem;padding-right:1.5rem}}@media(min-width:1280px){axp-layout-details-view axp-layout-section axp-layout-content .--item-container{padding-left:2rem;padding-right:2rem}}@media(min-width:1536px){axp-layout-details-view axp-layout-section axp-layout-content .--item-container{padding-left:2.5rem;padding-right:2.5rem}}axp-layout-details-view axp-layout-section axp-layout-content .--item-container{display:grid;grid-template-columns:repeat(12,minmax(0,1fr))}axp-layout-details-view axp-layout-section axp-layout-content .--item-container axp-layout-description{margin-top:.5rem!important;font-size:.75rem!important;line-height:1rem!important}axp-layout-details-view axp-page-content ax-form form{height:100%}axp-layout-details-view axp-layout-start-side{border-inline-end-width:1px}\n"] }]
|
|
2179
2393
|
}], propDecorators: { adapter: [{ type: i0.Input, args: [{ isSignal: true, alias: "adapter", required: true }] }], form: [{ type: i0.ViewChild, args: ['form', { isSignal: true }] }], widgetContainer: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AXPWidgetContainerComponent), { isSignal: true }] }], sidebarTabs: [{ type: i0.ViewChild, args: ['sidebarTabs', { isSignal: true }] }] } });
|
|
2180
2394
|
|
|
2181
2395
|
//#region ---- Details View Can Deactivate Guard ----
|