@acorex-platform/framework-client 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/README.md +7 -0
  2. package/auth/README.md +3 -0
  3. package/common/README.md +3 -0
  4. package/core/README.md +4 -0
  5. package/fesm2022/acorex-platform-framework-client-auth.mjs +1383 -0
  6. package/fesm2022/acorex-platform-framework-client-auth.mjs.map +1 -0
  7. package/fesm2022/acorex-platform-framework-client-common-common-settings.provider-CbzOYCnI.mjs +190 -0
  8. package/fesm2022/acorex-platform-framework-client-common-common-settings.provider-CbzOYCnI.mjs.map +1 -0
  9. package/fesm2022/acorex-platform-framework-client-common.mjs +9467 -0
  10. package/fesm2022/acorex-platform-framework-client-common.mjs.map +1 -0
  11. package/fesm2022/acorex-platform-framework-client-core.mjs +4655 -0
  12. package/fesm2022/acorex-platform-framework-client-core.mjs.map +1 -0
  13. package/fesm2022/acorex-platform-framework-client-domain.mjs +3189 -0
  14. package/fesm2022/acorex-platform-framework-client-domain.mjs.map +1 -0
  15. package/fesm2022/acorex-platform-framework-client-layout-builder.mjs +3439 -0
  16. package/fesm2022/acorex-platform-framework-client-layout-builder.mjs.map +1 -0
  17. package/fesm2022/acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-CWVU3wXL.mjs +134 -0
  18. package/fesm2022/acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-CWVU3wXL.mjs.map +1 -0
  19. package/fesm2022/acorex-platform-framework-client-layout-components.mjs +9777 -0
  20. package/fesm2022/acorex-platform-framework-client-layout-components.mjs.map +1 -0
  21. package/fesm2022/acorex-platform-framework-client-layout-designer.mjs +2799 -0
  22. package/fesm2022/acorex-platform-framework-client-layout-designer.mjs.map +1 -0
  23. package/fesm2022/acorex-platform-framework-client-layout-entity-attachments-page.component-CxBxxpmA.mjs +376 -0
  24. package/fesm2022/acorex-platform-framework-client-layout-entity-attachments-page.component-CxBxxpmA.mjs.map +1 -0
  25. package/fesm2022/acorex-platform-framework-client-layout-entity-file-list-popup.component-l0JAHKIH.mjs +100 -0
  26. package/fesm2022/acorex-platform-framework-client-layout-entity-file-list-popup.component-l0JAHKIH.mjs.map +1 -0
  27. package/fesm2022/acorex-platform-framework-client-layout-entity.mjs +26299 -0
  28. package/fesm2022/acorex-platform-framework-client-layout-entity.mjs.map +1 -0
  29. package/fesm2022/acorex-platform-framework-client-layout-views.mjs +2960 -0
  30. package/fesm2022/acorex-platform-framework-client-layout-views.mjs.map +1 -0
  31. package/fesm2022/acorex-platform-framework-client-layout-widget-core.mjs +4711 -0
  32. package/fesm2022/acorex-platform-framework-client-layout-widget-core.mjs.map +1 -0
  33. package/fesm2022/acorex-platform-framework-client-layout-widgets-button-widget-designer.component-CpkWwygb.mjs +72 -0
  34. package/fesm2022/acorex-platform-framework-client-layout-widgets-button-widget-designer.component-CpkWwygb.mjs.map +1 -0
  35. package/fesm2022/acorex-platform-framework-client-layout-widgets-image-preview.popup-C_EPAvCU.mjs +29 -0
  36. package/fesm2022/acorex-platform-framework-client-layout-widgets-image-preview.popup-C_EPAvCU.mjs.map +1 -0
  37. package/fesm2022/acorex-platform-framework-client-layout-widgets-page-widget-designer.component-CmtTq2Mt.mjs +172 -0
  38. package/fesm2022/acorex-platform-framework-client-layout-widgets-page-widget-designer.component-CmtTq2Mt.mjs.map +1 -0
  39. package/fesm2022/acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-DSx0E3NV.mjs +513 -0
  40. package/fesm2022/acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-DSx0E3NV.mjs.map +1 -0
  41. package/fesm2022/acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-DdfcGV1C.mjs +274 -0
  42. package/fesm2022/acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-DdfcGV1C.mjs.map +1 -0
  43. package/fesm2022/acorex-platform-framework-client-layout-widgets-tabular-data-view-popup.component-CntopmDS.mjs +64 -0
  44. package/fesm2022/acorex-platform-framework-client-layout-widgets-tabular-data-view-popup.component-CntopmDS.mjs.map +1 -0
  45. package/fesm2022/acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-B5kSWxGk.mjs +34 -0
  46. package/fesm2022/acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-B5kSWxGk.mjs.map +1 -0
  47. package/fesm2022/acorex-platform-framework-client-layout-widgets.mjs +28553 -0
  48. package/fesm2022/acorex-platform-framework-client-layout-widgets.mjs.map +1 -0
  49. package/fesm2022/acorex-platform-framework-client-native.mjs +155 -0
  50. package/fesm2022/acorex-platform-framework-client-native.mjs.map +1 -0
  51. package/fesm2022/acorex-platform-framework-client-runtime.mjs +911 -0
  52. package/fesm2022/acorex-platform-framework-client-runtime.mjs.map +1 -0
  53. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-create-view.component-DnTHwsDJ.mjs +160 -0
  54. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-create-view.component-DnTHwsDJ.mjs.map +1 -0
  55. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-modify-view.component-BpR9g6QS.mjs +121 -0
  56. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-modify-view.component-BpR9g6QS.mjs.map +1 -0
  57. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-single-view.component-D1a7UI71.mjs +236 -0
  58. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-single-view.component-D1a7UI71.mjs.map +1 -0
  59. package/fesm2022/acorex-platform-framework-client-themes-default-error-401.component-D--_MIar.mjs +48 -0
  60. package/fesm2022/acorex-platform-framework-client-themes-default-error-401.component-D--_MIar.mjs.map +1 -0
  61. package/fesm2022/acorex-platform-framework-client-themes-default-error-404.component-Byyqk8pH.mjs +42 -0
  62. package/fesm2022/acorex-platform-framework-client-themes-default-error-404.component-Byyqk8pH.mjs.map +1 -0
  63. package/fesm2022/acorex-platform-framework-client-themes-default-error-offline.component-DR6G8gPC.mjs +19 -0
  64. package/fesm2022/acorex-platform-framework-client-themes-default-error-offline.component-DR6G8gPC.mjs.map +1 -0
  65. package/fesm2022/acorex-platform-framework-client-themes-default.mjs +4160 -0
  66. package/fesm2022/acorex-platform-framework-client-themes-default.mjs.map +1 -0
  67. package/fesm2022/acorex-platform-framework-client-themes-shared-icon-chooser-column.component-CXSGiTCK.mjs +55 -0
  68. package/fesm2022/acorex-platform-framework-client-themes-shared-icon-chooser-column.component-CXSGiTCK.mjs.map +1 -0
  69. package/fesm2022/acorex-platform-framework-client-themes-shared-icon-chooser-view.component-BY5clQ1J.mjs +57 -0
  70. package/fesm2022/acorex-platform-framework-client-themes-shared-icon-chooser-view.component-BY5clQ1J.mjs.map +1 -0
  71. package/fesm2022/acorex-platform-framework-client-themes-shared-settings.provider-DEL2YOTP.mjs +234 -0
  72. package/fesm2022/acorex-platform-framework-client-themes-shared-settings.provider-DEL2YOTP.mjs.map +1 -0
  73. package/fesm2022/acorex-platform-framework-client-themes-shared-theme-color-chooser-column.component-BXl8u4Yp.mjs +94 -0
  74. package/fesm2022/acorex-platform-framework-client-themes-shared-theme-color-chooser-column.component-BXl8u4Yp.mjs.map +1 -0
  75. package/fesm2022/acorex-platform-framework-client-themes-shared-theme-color-chooser-view.component-i2sOOkVj.mjs +86 -0
  76. package/fesm2022/acorex-platform-framework-client-themes-shared-theme-color-chooser-view.component-i2sOOkVj.mjs.map +1 -0
  77. package/fesm2022/acorex-platform-framework-client-themes-shared.mjs +2375 -0
  78. package/fesm2022/acorex-platform-framework-client-themes-shared.mjs.map +1 -0
  79. package/fesm2022/acorex-platform-framework-client-workflow.mjs +2836 -0
  80. package/fesm2022/acorex-platform-framework-client-workflow.mjs.map +1 -0
  81. package/fesm2022/acorex-platform-framework-client.mjs +6 -0
  82. package/fesm2022/acorex-platform-framework-client.mjs.map +1 -0
  83. package/layout/builder/README.md +1578 -0
  84. package/layout/components/README.md +3 -0
  85. package/layout/designer/README.md +4 -0
  86. package/layout/entity/README.md +4 -0
  87. package/layout/views/README.md +3 -0
  88. package/layout/widget-core/README.md +4 -0
  89. package/layout/widgets/README.md +3 -0
  90. package/native/README.md +4 -0
  91. package/package.json +98 -0
  92. package/runtime/README.md +3 -0
  93. package/themes/default/README.md +3 -0
  94. package/themes/shared/README.md +3 -0
  95. package/types/acorex-platform-framework-client-auth.d.ts +690 -0
  96. package/types/acorex-platform-framework-client-common.d.ts +3287 -0
  97. package/types/acorex-platform-framework-client-core.d.ts +1849 -0
  98. package/types/acorex-platform-framework-client-domain.d.ts +1968 -0
  99. package/types/acorex-platform-framework-client-layout-builder.d.ts +1058 -0
  100. package/types/acorex-platform-framework-client-layout-components.d.ts +3362 -0
  101. package/types/acorex-platform-framework-client-layout-designer.d.ts +501 -0
  102. package/types/acorex-platform-framework-client-layout-entity.d.ts +4374 -0
  103. package/types/acorex-platform-framework-client-layout-views.d.ts +975 -0
  104. package/types/acorex-platform-framework-client-layout-widget-core.d.ts +1080 -0
  105. package/types/acorex-platform-framework-client-layout-widgets.d.ts +5263 -0
  106. package/types/acorex-platform-framework-client-native.d.ts +28 -0
  107. package/types/acorex-platform-framework-client-runtime.d.ts +301 -0
  108. package/types/acorex-platform-framework-client-themes-default.d.ts +889 -0
  109. package/types/acorex-platform-framework-client-themes-shared.d.ts +202 -0
  110. package/types/acorex-platform-framework-client-workflow.d.ts +1345 -0
  111. package/types/acorex-platform-framework-client.d.ts +2 -0
  112. package/workflow/README.md +4 -0
@@ -0,0 +1,2960 @@
1
+ import * as i2 from '@angular/common';
2
+ import { CommonModule } from '@angular/common';
3
+ import * as i0 from '@angular/core';
4
+ import { inject, viewChild, afterNextRender, effect, ChangeDetectionStrategy, ViewEncapsulation, Component, DestroyRef, signal, untracked, computed, InjectionToken, Injectable, input, output, ViewContainerRef, Directive, ElementRef } from '@angular/core';
5
+ import { AXCommonModule } from '@acorex/cdk/common';
6
+ import * as i4 from '@acorex/components/breadcrumbs';
7
+ import { AXBreadcrumbsModule } from '@acorex/components/breadcrumbs';
8
+ import * as i6 from '@acorex/components/button';
9
+ import { AXButtonModule } from '@acorex/components/button';
10
+ import * as i3 from '@acorex/components/decorators';
11
+ import { AXDecoratorModule } from '@acorex/components/decorators';
12
+ import * as i5 from '@acorex/components/dropdown';
13
+ import { AXDropdownModule } from '@acorex/components/dropdown';
14
+ import { AXDropdownButtonModule } from '@acorex/components/dropdown-button';
15
+ import { AXPTabNavigationService, AXPStickyDirective, AXPCommonSettings, AXPHomePageService, AXPSettingsService, AXPTabPreviewService, AXPUnsavedChangesConfirmService, AXPBrowserTitleService, axpJoinTabTitleSegments, normalizeRouterUrl, AXP_HOME_TAB_KEY, axpResolveTabKey, axpIsEntityDetailsViewRoute, axpNavigateAppPath, AXPNotFoundError, AXP_NOT_FOUND_ROUTE, AXPUnsavedChangesPopstateService, AXP_ENTITY_DELETED_WORKFLOW_EVENT_TYPE, createUnsavedChangesCanDeactivateGuard } from '@acorex-platform/framework-client/common';
16
+ import * as i8 from '@acorex-platform/framework-client/core';
17
+ import { AXPDeviceService, AXPComponentSlotModule, AXPDebugService, OVERLAY_DETAILS_VIEW_TRACE_NS, AXPExpressionEvaluatorService, AXPContextEvalFactory, normalizeKeyboardShortcuts, AXPContextStore, AXPBroadcastEventService, AXPKeyboardShortcutRegistry } from '@acorex-platform/framework-client/core';
18
+ import * as i1 from '@acorex/cdk/drawer';
19
+ import { AXDrawerDirectiveModule, AXDrawerContainerDirective } from '@acorex/cdk/drawer';
20
+ import { AXDrawerModule } from '@acorex/components/drawer';
21
+ import * as i3$1 from '@acorex/core/translation';
22
+ import { AXTranslationModule, AXTranslationService } from '@acorex/core/translation';
23
+ import { AXPThemeLayoutBlockComponent, AXPThemeLayoutActionsComponent, AXPThemeLayoutHeaderComponent, AXPThemeLayoutContainerComponent, AXPPageComponentRegistryService, AXPThemeLayoutListComponent, AXPThemeLayoutListItemComponent, AXPThemeLayoutStartSideComponent } from '@acorex-platform/framework-client/layout/components';
24
+ import { AXPStatusChipComponent } from '@acorex-platform/framework-client/layout/widgets';
25
+ import { AXPCommandService, AXPPolicyEngineService } from '@acorex-platform/framework-client/runtime';
26
+ import { AXPLayoutThemeService } from '@acorex-platform/framework-client/themes/shared';
27
+ import { AXPWorkflowService } from '@acorex-platform/framework-client/workflow';
28
+ import { AXToastService } from '@acorex/components/toast';
29
+ import { AXFormatService, AXFormatModule } from '@acorex/core/format';
30
+ import { AXPSessionService } from '@acorex-platform/framework-client/auth';
31
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
32
+ import { Router, NavigationEnd, ActivatedRoute } from '@angular/router';
33
+ import { isEmpty, cloneDeep, get } from 'lodash-es';
34
+ import { filter } from 'rxjs/operators';
35
+ import { AXBasePageComponent } from '@acorex/components/page';
36
+ import { getSmart, AXPKeyboardShortcutPriority } from '@acorex-platform/framework-shared/core';
37
+ import { AXPPageStatus } from '@acorex-platform/framework-shared/widget-core';
38
+ import { AXPEntityCommandScope } from '@acorex-platform/framework-shared/entity';
39
+ import { signalStore, withState, withComputed, withMethods, patchState } from '@ngrx/signals';
40
+ import { AXBadgeModule } from '@acorex/components/badge';
41
+ import { AXButtonGroupModule } from '@acorex/components/button-group';
42
+ import { AXMenuModule } from '@acorex/components/menu';
43
+ import { AXSearchBoxModule } from '@acorex/components/search-box';
44
+ import { AXDateTimeModule } from '@acorex/core/date-time';
45
+ import { AXFileModule } from '@acorex/core/file';
46
+ import * as i7 from '@acorex/components/form';
47
+ import { AXFormModule } from '@acorex/components/form';
48
+ import * as i5$1 from '@acorex/components/tabs';
49
+ import { AXTabsModule } from '@acorex/components/tabs';
50
+ import * as i6$1 from '@acorex-platform/framework-client/layout/widget-core';
51
+ import { AXPWidgetContainerComponent, AXPWidgetCoreModule } from '@acorex-platform/framework-client/layout/widget-core';
52
+ import { Subject, takeUntil, filter as filter$1 } from 'rxjs';
53
+
54
+ class AXPPageLayoutBase {
55
+ }
56
+
57
+ class AXPPageLayoutComponent {
58
+ constructor() {
59
+ this.page = inject(AXPPageLayoutBase, { optional: true });
60
+ this.layoutService = inject(AXPLayoutThemeService);
61
+ this.deviceService = inject(AXPDeviceService);
62
+ this.tabNavigation = inject(AXPTabNavigationService);
63
+ this.startSideDrawer = viewChild('startSideDrawer', ...(ngDevMode ? [{ debugName: "startSideDrawer" }] : /* istanbul ignore next */ []));
64
+ this.workflow = inject(AXPWorkflowService);
65
+ this.commandService = inject(AXPCommandService);
66
+ this.#initialize = afterNextRender(async () => {
67
+ this.checkScrollableContent();
68
+ });
69
+ this.#effect = effect(() => {
70
+ this.page?.title();
71
+ this.page?.description();
72
+ this.checkScrollableContent();
73
+ }, ...(ngDevMode ? [{ debugName: "#effect" }] : /* istanbul ignore next */ []));
74
+ }
75
+ #initialize;
76
+ #effect;
77
+ checkScrollableContent() {
78
+ const content = document.querySelector('axp-page-content');
79
+ if (content && content.scrollHeight >= content.clientHeight) {
80
+ content.classList.add('--scrollable');
81
+ }
82
+ }
83
+ handleActionClick(item) {
84
+ if (item.command && (item.items?.length ?? 0) == 0) {
85
+ this.execute(item.command);
86
+ }
87
+ }
88
+ toggleStartSide() {
89
+ this.startSideDrawer()?.toggle();
90
+ }
91
+ handleSecondaryActionClick(item) {
92
+ if (item.command) {
93
+ this.execute(item.command);
94
+ }
95
+ }
96
+ handleBackdropClick(e) {
97
+ e.nativeEvent.preventDefault();
98
+ e.nativeEvent.stopPropagation();
99
+ e.component.hide();
100
+ return false;
101
+ }
102
+ async execute(command) {
103
+ const reserved = ['navigate'];
104
+ if (reserved.includes(command.name)) {
105
+ if (!(await this.canLeaveCurrentPage())) {
106
+ return;
107
+ }
108
+ await this.workflow.execute(command.name, { options: command.options });
109
+ return;
110
+ }
111
+ if (this.commandService.exists(command.name)) {
112
+ await this.commandService.execute(command.name, command.options ?? {});
113
+ }
114
+ else {
115
+ await this.page?.execute(command);
116
+ }
117
+ }
118
+ handleOnBackdropClick(event) {
119
+ event.stopPropagation();
120
+ this.startSideDrawer()?.hide();
121
+ }
122
+ /**
123
+ * Breadcrumbs and other in-page `navigate` commands bypass `routerLink`, so prompt here
124
+ * when the hosted page implements {@link AXPUnsavedChangesCanDeactivate}.
125
+ * A successful confirm sets `leaveConfirmed` for the router guard on the subsequent navigation.
126
+ */
127
+ async canLeaveCurrentPage() {
128
+ const page = this.page;
129
+ if (!page || typeof page.canDeactivate !== 'function') {
130
+ return true;
131
+ }
132
+ return page.canDeactivate();
133
+ }
134
+ //#region ---------------- Status Helpers ----------------
135
+ getEntityData() {
136
+ // Get entity data from page context if available
137
+ if (this.page && 'getEntityData' in this.page && typeof this.page.getEntityData === 'function') {
138
+ return this.page.getEntityData();
139
+ }
140
+ return null;
141
+ }
142
+ async handleStatusTransition(transition) {
143
+ // Convert status transition to command and execute
144
+ await this.execute({
145
+ name: 'update-status',
146
+ options: {
147
+ transition,
148
+ },
149
+ });
150
+ }
151
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPageLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
152
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPPageLayoutComponent, isStandalone: true, selector: "axp-page-layout", providers: [], viewQueries: [{ propertyName: "startSideDrawer", first: true, predicate: ["startSideDrawer"], descendants: true, isSignal: true }], hostDirectives: [{ directive: i1.AXDrawerContainerDirective }], ngImport: i0, template: "<!-------- Begin Start Side Drawer -------->\n<div id=\"axp-drawer-start\" [mode]=\"deviceService.isSmall() ? 'overlay' : 'push'\" [transition]=\"250\" axDrawerItem\n [backDrop]=\"true\" (onBackdropClick)=\"handleBackdropClick($event)\" #startSideDrawer=\"axDrawerItem\"\n [backdropClass]=\"'ax-bg-darkest/75'\" [collapsed]=\"deviceService.isSmall()\" drawerLocation=\"start\" class=\"ax-h-full\">\n <ng-content select=\"axp-layout-start-side\"></ng-content>\n</div>\n<!-------- Finish Start Side Drawer -------->\n<!--------------------------------------------------------------->\n<!-------- Begin Main Layout Container -------->\n<axp-layout-container id=\"axp-page-layout\" #container>\n <!-------- Begin Page Header -------->\n <axp-layout-header id=\"axp-page-header\" *translate=\"let t\" #sticky=\"axpSticky\" [axpSticky]=\"'axp-is-sticky'\"\n [stickyOffset]=\"50\" [stickyParent]=\"container.hostElement\">\n <!-------- Begin Back Button (Mobile Only) -------->\n @if (page?.hasBackButton() && deviceService.isSmall()) {\n <ax-button id=\"axp-btn-back\" [look]=\"'blank'\" class=\"ax-sm ax-w-fit ax-ms-1\" color=\"secondary\"\n (click)=\"page?.onBackButtonClick()\" [text]=\"(page?.backButton()?.title | translate | async) ?? ''\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-chevron-left rtl:ax-rotate-180\"></ax-icon>\n </ax-prefix>\n </ax-button>\n }\n <!-------- Finish Back Button -------->\n <!--------------------------------------------------------------->\n <!-------- Begin Title Bar -------->\n <axp-layout-title-bar id=\"axp-title-bar\">\n <!-------- Begin Title Section -------->\n <div class=\"ax-flex ax-flex-col ax-transition ax-shrink-1 ax-min-w-0\">\n <!-------- Begin Breadcrumbs -------->\n @if (page?.hasBreadcrumbs() && !deviceService.isSmall()) {\n <ax-breadcrumbs id=\"axp-breadcrumb\" class=\"ax-mb-1\">\n @for (item of page?.breadcrumbs(); track $index) {\n <ax-breadcrumbs-item [attr.id]=\"'axp-breadcrumb-item-' + $index\" [active]=\"$last\"\n (click)=\"!$last && item.command ? execute(item.command) : null\">\n @if (item.icon) {\n <i [class]=\"item.icon\"></i>\n }\n {{ t(item.title) | async }}\n </ax-breadcrumbs-item>\n }\n <ng-template #divider>\n <i class=\"fa-regular fa-chevron-right rtl:ax-rotate-180 fa-sm\"></i>\n </ng-template>\n </ax-breadcrumbs>\n }\n <!-------- Finish Breadcrumbs -------->\n <!--------------------------------------------------------------->\n <!-------- Begin Title Container -------->\n <div class=\"__title-container\">\n <!-------- Begin Page Title -------->\n @if (page?.hasTitle()) {\n <axp-layout-title id=\"axp-page-title\">\n @if (page?.hasTitleIcon()) {\n <ax-icon [icon]=\"page?.titleIcon()!\" class=\"ax-me-2\"></ax-icon>\n }\n {{ page?.title() | translate | async }}\n </axp-layout-title>\n }\n <!-------- Finish Page Title -------->\n <!--------------------------------------------------------------->\n <!-------- Begin Title Actions -------->\n @if (page?.hasTitleActions()) {\n <ax-button id=\"axp-btn-title-actions\" [look]=\"'blank'\" class=\"ax-sm -ax-mx-2\">\n <ax-prefix>\n <i class=\"fa-solid fa-caret-down fa-fw\"></i>\n </ax-prefix>\n <ax-dropdown-panel #panel>\n <ax-button-item-list>\n @for (sub of page?.titleActions(); track $index) {\n <ng-container>\n <ax-button-item [attr.id]=\"'axp-btn-title-action-' + (sub.name || $index)\"\n [text]=\"(t(sub.title) | async)!\" [color]=\"sub.color\" [disabled]=\"sub.disabled\"\n [selected]=\"sub.command?.metadata?.['isSelected']\" (onClick)=\"handleActionClick(sub)\">\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 </ng-container>\n }\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-button>\n }\n <!-------- Finish Title Actions -------->\n <!--------------------------------------------------------------->\n <!-------- Begin Page Status -------->\n\n @if (page?.hasStatus()) {\n <axp-status-chip [value]=\"page?.status()?.value ?? null\" [readonly]=\"page?.status()?.readonly || false\"\n [definitionKey]=\"page?.status()?.definitionKey ?? null\" [entityData]=\"getEntityData()\"\n (transitionExecuted)=\"handleStatusTransition($event)\" class=\"__title-status\"></axp-status-chip>\n }\n <!-------- Finish Page Status -------->\n\n <!-------- Begin Title Badge -------->\n @if (page?.hasBadge()) {\n <div id=\"axp-page-badge\" class=\"__title-badge --{{ page?.badge()?.color }}\"\n [attr.title]=\"page?.badge()?.description ?? page?.badge()?.title\" [attr.aria-label]=\"page?.badge()?.title\">\n <i class=\"fa-solid fa-circle\" aria-hidden=\"true\"></i>\n <span class=\"__title-badge-label\">{{ page?.badge()?.title }}</span>\n </div>\n }\n <!-------- Finish Title Badge -------->\n\n </div>\n <!-------- Finish Title Container -------->\n <!--------------------------------------------------------------->\n <!-------- Begin Page Description -------->\n @if (page?.hasDescription()) {\n <axp-layout-description id=\"axp-page-description\">\n {{ page?.description() | translate | async }}</axp-layout-description>\n }\n <!-------- Finish Page Description -------->\n </div>\n <!-------- Finish Title Section -------->\n <!--------------------------------------------------------------->\n <!-------- Begin Page Actions -------->\n <axp-layout-actions id=\"axp-page-actions\" class=\"ax-shrink-0\">\n <axp-component-slot name=\"page-header-actions\"></axp-component-slot>\n <!-------- Begin Primary Actions -------->\n @if (page?.hasPrimaryActions()) {\n @for (item of page?.primaryMenuItems(); track $index) {\n @if (item.visible != false) {\n <ax-button [attr.id]=\"'axp-btn-primary-' + (item.name || item.title)\" [class.ax-sm]=\"deviceService.isSmall()\"\n [iconOnly]=\"deviceService.isSmall()\" [disabled]=\"item.disabled\" [text]=\"(t(item.title) | async)!\"\n [look]=\"'solid'\" [color]=\"item.color\" (onClick)=\"handleActionClick(item)\">\n <ax-prefix>\n <i class=\"{{ item.icon }}\"></i>\n </ax-prefix>\n @if (item?.items) {\n <ax-dropdown-panel #panel>\n <ax-button-item-list>\n @for (sub of item?.items; track $index) {\n @if (sub.visible != false) {\n <ax-button-item [attr.id]=\"'axp-btn-primary-' + (item.name || item.title) + '-' + (sub.name || sub.title)\"\n [text]=\"(t(sub.title) | async)!\" [color]=\"sub.color\" [disabled]=\"sub.disabled\"\n (onClick)=\"handleActionClick(sub)\">\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 <!-------- Finish Primary Actions -------->\n <!--------------------------------------------------------------->\n <!-------- Begin Secondary Actions -------->\n @if (page?.hasSecondaryActions()) {\n <ax-button id=\"axp-btn-secondary\" [class.ax-sm]=\"deviceService.isSmall()\" [iconOnly]=\"deviceService.isSmall()\"\n [text]=\"'@general:terms.interface.actions' | translate | async\"\n [look]=\"deviceService.isSmall() ? 'blank' : 'solid'\" [color]=\"'default'\">\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 (item of page?.secondaryMenuItems(); track $index) {\n @if (item.visible != false) {\n <ax-button-item [attr.id]=\"'axp-btn-secondary-' + (item.name || item.title)\"\n [text]=\"(item.title | translate | async)!\" [color]=\"item.color\" [disabled]=\"item.disabled\"\n (onClick)=\"handleSecondaryActionClick(item)\">\n <ax-prefix>\n <ax-icon icon=\"fa-light {{ item.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n @if (item.break) {\n <ax-divider></ax-divider>\n }\n }\n }\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-button>\n }\n <!-------- Finish Secondary Actions -------->\n </axp-layout-actions>\n <!-------- Finish Page Actions -------->\n </axp-layout-title-bar>\n <!-------- Finish Title Bar -------->\n <!--------------------------------------------------------------->\n <!-------- Begin Page Toolbar / Navbar -------->\n @if (!sticky.isSticky) {}\n <ng-content select=\"axp-page-toolbar\"></ng-content>\n <!-------- Finish Page Toolbar / Navbar -------->\n </axp-layout-header>\n <!-------- Finish Page Header -------->\n <!--------------------------------------------------------------->\n <!-------- Begin Page Content -------->\n <ng-content select=\"axp-page-content\"></ng-content>\n <!-------- Finish Page Content -------->\n <!--------------------------------------------------------------->\n <!-------- Begin Page Footer -------->\n <axp-page-footer-container id=\"axp-page-footer-container\"\n [ngStyle]=\"{ display: tt.isEmpty() && prefixSlot.isEmpty() && suffixSlot.isEmpty() ? 'none' : 'block' }\">\n <axp-layout-container #tt>\n <ng-content select=\"axp-page-footer\"></ng-content>\n </axp-layout-container>\n <axp-page-footer id=\"axp-page-footer\">\n <axp-layout-prefix>\n <axp-component-slot name=\"page-footer-start\" #prefixSlot=\"slot\"></axp-component-slot>\n </axp-layout-prefix>\n <axp-layout-suffix>\n <axp-component-slot name=\"page-footer-end\" #suffixSlot=\"slot\"></axp-component-slot>\n </axp-layout-suffix>\n </axp-page-footer>\n </axp-page-footer-container>\n <!-------- Finish Page Footer -------->\n</axp-layout-container>\n<!-------- Finish Main Layout Container -------->", styles: ["@charset \"UTF-8\";.axp-status-icon{font-size:.875rem;line-height:1}.axp-status-chip{display:inline-flex}.axp-status-chip--primary{background-color:rgb(var(--ax-sys-color-primary-lighter-surface));color:rgb(var(--ax-sys-color-on-primary-lighter-surface))}.axp-status-icon--primary{color:rgb(var(--ax-sys-color-primary-dark-surface))}.axp-status-chip--secondary{background-color:rgb(var(--ax-sys-color-secondary-lighter-surface));color:rgb(var(--ax-sys-color-on-secondary-lighter-surface))}.axp-status-icon--secondary{color:rgb(var(--ax-sys-color-secondary-dark-surface))}.axp-status-chip--success{background-color:rgb(var(--ax-sys-color-success-lighter-surface));color:rgb(var(--ax-sys-color-on-success-lighter-surface))}.axp-status-icon--success{color:rgb(var(--ax-sys-color-success-dark-surface))}.axp-status-chip--warning{background-color:rgb(var(--ax-sys-color-warning-lighter-surface));color:rgb(var(--ax-sys-color-on-warning-lighter-surface))}.axp-status-icon--warning{color:rgb(var(--ax-sys-color-warning-dark-surface))}.axp-status-chip--danger{background-color:rgb(var(--ax-sys-color-danger-lighter-surface));color:rgb(var(--ax-sys-color-on-danger-lighter-surface))}.axp-status-icon--danger{color:rgb(var(--ax-sys-color-danger-dark-surface))}.axp-status-chip--info{background-color:rgb(var(--ax-sys-color-accent3-lighter-surface));color:rgb(var(--ax-sys-color-on-accent3-lighter-surface))}.axp-status-icon--info{color:rgb(var(--ax-sys-color-accent3-dark-surface))}.axp-status-chip--accent1{background-color:rgb(var(--ax-sys-color-accent1-lighter-surface));color:rgb(var(--ax-sys-color-on-accent1-lighter-surface))}.axp-status-icon--accent1{color:rgb(var(--ax-sys-color-accent1-dark-surface))}.axp-status-chip--accent2{background-color:rgb(var(--ax-sys-color-accent2-lighter-surface));color:rgb(var(--ax-sys-color-on-accent2-lighter-surface))}.axp-status-icon--accent2{color:rgb(var(--ax-sys-color-accent2-dark-surface))}.axp-status-chip--accent3{background-color:rgb(var(--ax-sys-color-accent3-lighter-surface));color:rgb(var(--ax-sys-color-on-accent3-lighter-surface))}.axp-status-icon--accent3{color:rgb(var(--ax-sys-color-accent3-dark-surface))}.axp-status-chip--neutral{background-color:rgb(var(--ax-sys-color-neutral-lighter-surface));color:rgb(var(--ax-sys-color-on-neutral-lighter-surface))}.axp-status-icon--neutral{color:rgb(var(--ax-sys-color-neutral-dark-surface))}axp-layout-title{display:block;width:100%;font-weight:600}axp-layout-description{display:block;width:100%;font-size:.875rem;line-height:1.25rem;opacity:.65}axp-layout-actions{display:flex;align-items:center;gap:.75rem}axp-layout-actions axp-layout-actions-primary{display:flex;align-items:center;gap:.75rem}axp-layout-footer,axp-layout-toolbar,axp-layout-header,axp-page-header,axp-page-footer,axp-page-toolbar{display:flex;width:100%;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:.5rem}axp-layout-footer>axp-layout-prefix,axp-layout-footer>axp-layout-suffix,axp-layout-toolbar>axp-layout-prefix,axp-layout-toolbar>axp-layout-suffix,axp-layout-header>axp-layout-prefix,axp-layout-header>axp-layout-suffix,axp-page-header>axp-layout-prefix,axp-page-header>axp-layout-suffix,axp-page-footer>axp-layout-prefix,axp-page-footer>axp-layout-suffix,axp-page-toolbar>axp-layout-prefix,axp-page-toolbar>axp-layout-suffix{display:flex;flex-direction:row;align-items:center;gap:.5rem}axp-layout-footer>axp-layout-prefix,axp-layout-toolbar>axp-layout-prefix,axp-layout-header>axp-layout-prefix,axp-page-header>axp-layout-prefix,axp-page-footer>axp-layout-prefix,axp-page-toolbar>axp-layout-prefix{order:-9999}axp-layout-footer>axp-layout-suffix,axp-layout-toolbar>axp-layout-suffix,axp-layout-header>axp-layout-suffix,axp-page-header>axp-layout-suffix,axp-page-footer>axp-layout-suffix,axp-page-toolbar>axp-layout-suffix{order:9999;margin-inline-start:auto}axp-layout-list{display:flex;flex-direction:column}axp-layout-list>: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}axp-layout-list:focus{outline:none}axp-layout-list-group{display:flex;flex-direction:column;padding-top:.5rem;padding-bottom:.5rem}axp-layout-list-group>axp-layout-title{padding-bottom:.5rem;font-size:.875rem;line-height:1.25rem;font-weight:600}axp-layout-list-group>axp-layout-header{display:flex;justify-content:space-between;padding-top:.5rem;padding-bottom:.5rem}axp-layout-list-item{margin-top:.25rem;display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:.75rem;padding:.5rem;font-size:.875rem;line-height:1.25rem}axp-layout-list-item:focus{outline:none}axp-layout-list-item.axp-state-focused,axp-layout-list-item:hover{cursor:pointer;border-radius:.25rem;--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-light-surface),var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgba(var(--ax-sys-color-on-lightest-surface),var(--tw-text-opacity, 1))}axp-layout-list-item>axp-layout-prefix{margin-inline-end:auto;display:flex;flex-direction:row;gap:.5rem}axp-layout-list-item>axp-layout-content{flex:1 1 0%}axp-layout-list-item>axp-layout-content axp-layout-description{margin-top:.25rem!important;font-size:.75rem!important;line-height:1rem!important}axp-layout-list-item>axp-layout-suffix{margin-inline-start:auto;display:flex;flex-direction:row;gap:.5rem}axp-layout-sections{display:flex;flex-direction:column;gap:1rem}axp-layout-sections axp-layout-section{display:block;border-radius:.375rem;border-width:1px;--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}axp-layout-sections axp-layout-section>axp-layout-footer,axp-layout-sections axp-layout-section>axp-layout-header{padding:.5rem 1rem}@media(min-width:1280px){axp-layout-sections axp-layout-section>axp-layout-footer,axp-layout-sections axp-layout-section>axp-layout-header{padding-top:1rem;padding-bottom:1rem}}axp-layout-sections axp-layout-section>axp-layout-header{display:flex;flex-direction:column;gap:0px;border-top-left-radius:.375rem;border-top-right-radius:.375rem;border-bottom-width:1px}axp-layout-sections axp-layout-section>axp-layout-header axp-layout-title{font-weight:700;--tw-text-opacity: 1;color:rgba(var(--ax-sys-color-on-lightest-surface),var(--tw-text-opacity, 1))}axp-layout-sections axp-layout-section>axp-layout-header axp-layout-description{margin-top:.25rem;font-size:.875rem;line-height:1.25rem}axp-layout-sections axp-layout-section>axp-layout-footer{display:flex;align-items:center;justify-content:space-between;border-bottom-right-radius:.375rem;border-bottom-left-radius:.375rem;border-top-width:1px;padding:1rem}axp-layout-sections axp-layout-section>axp-layout-footer>axp-layout-suffix{margin-inline-start:auto;display:flex;gap:.5rem}axp-layout-sections axp-layout-section>axp-layout-footer{opacity:0;animation:fadeInDown .5s ease-out forwards}@keyframes fadeInDown{0%{opacity:0}to{opacity:1}}axp-page-layout{display:flex;width:100%;height:100%;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}axp-page-layout:is(.ax-dark *){background-color:rgb(var(--ax-sys-color-dark-surface));color:rgb(var(--ax-sys-color-on-dark-surface));border-color:rgb(var(--ax-sys-color-border-dark-surface))}axp-page-layout axp-layout-start-side,axp-page-layout axp-layout-end-side{display:flex;min-height:100%;min-width:16rem;flex-direction:column;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}axp-page-layout axp-layout-start-side:is(.ax-dark *),axp-page-layout axp-layout-end-side:is(.ax-dark *){background-color:rgb(var(--ax-sys-color-darker-surface));color:rgb(var(--ax-sys-color-on-darker-surface));border-color:rgb(var(--ax-sys-color-border-darker-surface))}axp-page-layout axp-layout-start-side axp-layout-header,axp-page-layout axp-layout-end-side axp-layout-header{display:flex;flex-direction:column;gap:.5rem;padding:1rem}axp-page-layout axp-layout-start-side axp-layout-header>axp-layout-title,axp-page-layout axp-layout-end-side axp-layout-header>axp-layout-title{font-size:1.125rem;line-height:1.75rem;font-weight:500}axp-page-layout axp-layout-start-side>axp-layout-content,axp-page-layout axp-layout-end-side>axp-layout-content{flex:1 1 0%}axp-page-layout>axp-layout-container{position:relative;display:flex;width:100%;height:100%;flex-direction:column;overflow:auto}axp-page-layout>axp-layout-container>axp-layout-header{position:sticky;top:0;z-index:20;display:flex;flex-direction:column;align-items:flex-start;gap:0px;padding-top:.5rem;padding-bottom:.5rem;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}axp-page-layout>axp-layout-container>axp-layout-header:is(.ax-dark *){background-color:rgb(var(--ax-sys-color-dark-surface));color:rgb(var(--ax-sys-color-on-dark-surface));border-color:rgb(var(--ax-sys-color-border-dark-surface))}@media(min-width:768px){axp-page-layout>axp-layout-container>axp-layout-header{padding-top:1rem;padding-bottom:1rem}}axp-page-layout>axp-layout-container>axp-layout-header.axp-is-sticky{--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-border-lightest-surface),var(--tw-border-opacity, 1));--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}axp-page-layout>axp-layout-container>axp-layout-header ax-breadcrumbs ax-breadcrumbs-item{font-size:.75rem;line-height:1rem}axp-page-layout>axp-layout-container>axp-layout-header ax-breadcrumbs ax-breadcrumbs-item.ax-state-active .ax-breadcrumb-item-content{cursor:default;--tw-text-opacity: 1;color:rgba(var(--ax-sys-color-on-lightest-surface),var(--tw-text-opacity, 1));opacity:.75}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar{display:flex;width:100%;align-items:center;justify-content:space-between;padding-left:1rem;padding-right:1rem}@media(min-width:1280px){axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar{border-width:0px}}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar ax-layout-nav-button{display:flex;align-items:center;justify-content:space-between}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container{display:flex;align-items:baseline;gap:.75rem}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container axp-layout-title{display:inline-block;width:fit-content;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:1.25rem;line-height:1.75rem;font-weight:500;line-height:1}@media(min-width:1024px){axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container axp-layout-title{font-size:1.5rem;line-height:2rem}}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge{display:inline-flex;--tw-translate-y: -3px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));align-items:center;align-self:baseline;border-radius:.375rem;padding:.25rem .5rem;font-size:.75rem;line-height:1rem;font-weight:600;line-height:1}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge i{margin-inline-end:.25rem;line-height:1;font-size:.5rem}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge.--warning{background-color:rgb(var(--ax-sys-color-warning-lightest-surface));color:rgb(var(--ax-sys-color-on-warning-lightest-surface));border-color:rgb(var(--ax-sys-color-border-warning-lightest-surface))}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge.--warning i{color:rgb(var(--ax-sys-color-warning-500))}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge.--danger{background-color:rgb(var(--ax-sys-color-danger-lightest-surface));color:rgb(var(--ax-sys-color-on-danger-lightest-surface));border-color:rgb(var(--ax-sys-color-border-danger-lightest-surface))}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge.--danger i{color:rgb(var(--ax-sys-color-danger-500))}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge.--success{background-color:rgb(var(--ax-sys-color-success-lightest-surface));color:rgb(var(--ax-sys-color-on-success-lightest-surface));border-color:rgb(var(--ax-sys-color-border-success-lightest-surface))}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge.--success i{color:rgb(var(--ax-sys-color-success-500))}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge.--primary{background-color:rgb(var(--ax-sys-color-primary-lightest-surface));color:rgb(var(--ax-sys-color-on-primary-lightest-surface));border-color:rgb(var(--ax-sys-color-border-primary-lightest-surface))}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge.--primary i{color:rgb(var(--ax-sys-color-primary-500))}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge.--secondary i{color:rgb(var(--ax-sys-color-secondary-500))}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge.--default i{color:rgb(var(--ax-sys-color-on-surface)/.45)}@media(max-width:600px){axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge{--tw-translate-y: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));background-color:transparent;padding:0;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge .__title-badge-label{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge i{margin:0;font-size:.625rem}}axp-page-layout>axp-layout-container>axp-layout-header axp-page-toolbar{margin-top:.5rem;padding-left:1rem;padding-right:1rem}axp-page-layout>axp-layout-container>axp-page-content{display:flex;width:100%;flex:1 1 0%;flex-direction:column;padding:.75rem 1rem}axp-page-layout>axp-layout-container>axp-page-content.--scrollable{padding-bottom:1rem}axp-page-layout>axp-layout-container>axp-page-footer-container{border-top-width:1px;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}axp-page-layout>axp-layout-container>axp-page-footer-container:is(.ax-dark *){background-color:rgb(var(--ax-sys-color-lighter-surface));color:rgb(var(--ax-sys-color-on-lighter-surface));border-color:rgb(var(--ax-sys-color-border-lighter-surface))}axp-page-layout>axp-layout-container>axp-page-footer-container{position:sticky;bottom:0;z-index:20;padding:.75rem;--tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / .05);--tw-shadow-colored: inset 0 2px 4px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}@keyframes ax-fadeInUp{0%{transform:translate3d(0,100%,0);opacity:0}}axp-page-layout>axp-layout-container>axp-page-footer-container.--animated{animation:1s both ax-fadeInUp;transition-duration:.3s;transition-timing-function:cubic-bezier(0,0,.2,1);animation-duration:.3s;animation-timing-function:cubic-bezier(0,0,.2,1)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type:
153
+ //
154
+ AXCommonModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i3.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i3.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXBreadcrumbsModule }, { kind: "component", type: i4.AXBreadCrumbsComponent, selector: "ax-breadcrumbs" }, { kind: "component", type: i4.AXBreadCrumbsItemComponent, selector: "ax-breadcrumbs-item", inputs: ["disabled", "active"] }, { kind: "ngmodule", type: AXDropdownButtonModule }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "component", type: i5.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i6.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: i6.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i6.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items", "closeParentOnClick", "lockOnLoading"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "directive", type: i3$1.AXTranslatorDirective, selector: "[translate]" }, { kind: "ngmodule", type: AXDrawerModule }, { kind: "ngmodule", type: AXDrawerDirectiveModule }, { kind: "directive", type: i1.AXDrawerItemDirective, selector: "[axDrawerItem]", inputs: ["location", "collapsed", "backDrop", "mode", "transition", "closeOnBackdropClick", "backdropClass", "singleOpenMode"], outputs: ["collapseStateChanged", "locationChange", "collapsedChange", "backDropChange", "modeChange", "transitionChange", "closeOnBackdropClickChange", "backdropClassChange", "onBackdropClick", "singleOpenModeChange"], exportAs: ["axDrawerItem"] }, { kind: "ngmodule", type:
155
+ //
156
+ AXPComponentSlotModule }, { kind: "directive", type: i8.AXPComponentSlotDirective, selector: "axp-component-slot", inputs: ["name", "host", "context"], exportAs: ["slot"] }, { kind: "component", type: AXPThemeLayoutBlockComponent, selector: " axp-page-content, axp-page-footer-container, axp-page-footer, axp-page-header, axp-page-header-container, axp-page-toolbar, axp-layout-content, axp-layout-page-content, axp-layout-sections, axp-layout-body, axp-layout-page-body, axp-layout-prefix, axp-layout-suffix, axp-layout-title-bar, axp-layout-title, axp-layout-title-actions, axp-layout-nav-button, axp-layout-description, axp-layout-breadcrumbs, axp-layout-list-action, " }, { kind: "component", type: AXPThemeLayoutActionsComponent, selector: "axp-layout-actions" }, { kind: "component", type: AXPThemeLayoutHeaderComponent, selector: "axp-layout-header" }, { kind: "directive", type: AXPStickyDirective, selector: "[axpSticky]", inputs: ["axpSticky", "stickyOffset", "stickyParent", "stickyTarget"], outputs: ["isStickyChange"], exportAs: ["axpSticky"] }, { kind: "component", type: AXPThemeLayoutContainerComponent, selector: "axp-layout-container" }, { kind: "component", type: AXPStatusChipComponent, selector: "axp-status-chip", inputs: ["status", "value", "readonly", "availableTransitions", "definitionKey", "entityData"], outputs: ["openChange", "transitionExecuted", "transitionError"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }, { kind: "pipe", type: i3$1.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
157
+ }
158
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPageLayoutComponent, decorators: [{
159
+ type: Component,
160
+ args: [{ standalone: true, imports: [
161
+ CommonModule,
162
+ //
163
+ AXCommonModule,
164
+ AXDecoratorModule,
165
+ AXBreadcrumbsModule,
166
+ AXDropdownButtonModule,
167
+ AXDropdownModule,
168
+ AXButtonModule,
169
+ AXTranslationModule,
170
+ AXDrawerModule,
171
+ AXDrawerDirectiveModule,
172
+ //
173
+ AXPComponentSlotModule,
174
+ AXPThemeLayoutBlockComponent,
175
+ AXPThemeLayoutActionsComponent,
176
+ AXPThemeLayoutHeaderComponent,
177
+ AXPStickyDirective,
178
+ AXPThemeLayoutContainerComponent,
179
+ AXPStatusChipComponent,
180
+ ], selector: 'axp-page-layout', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, hostDirectives: [AXDrawerContainerDirective], providers: [], template: "<!-------- Begin Start Side Drawer -------->\n<div id=\"axp-drawer-start\" [mode]=\"deviceService.isSmall() ? 'overlay' : 'push'\" [transition]=\"250\" axDrawerItem\n [backDrop]=\"true\" (onBackdropClick)=\"handleBackdropClick($event)\" #startSideDrawer=\"axDrawerItem\"\n [backdropClass]=\"'ax-bg-darkest/75'\" [collapsed]=\"deviceService.isSmall()\" drawerLocation=\"start\" class=\"ax-h-full\">\n <ng-content select=\"axp-layout-start-side\"></ng-content>\n</div>\n<!-------- Finish Start Side Drawer -------->\n<!--------------------------------------------------------------->\n<!-------- Begin Main Layout Container -------->\n<axp-layout-container id=\"axp-page-layout\" #container>\n <!-------- Begin Page Header -------->\n <axp-layout-header id=\"axp-page-header\" *translate=\"let t\" #sticky=\"axpSticky\" [axpSticky]=\"'axp-is-sticky'\"\n [stickyOffset]=\"50\" [stickyParent]=\"container.hostElement\">\n <!-------- Begin Back Button (Mobile Only) -------->\n @if (page?.hasBackButton() && deviceService.isSmall()) {\n <ax-button id=\"axp-btn-back\" [look]=\"'blank'\" class=\"ax-sm ax-w-fit ax-ms-1\" color=\"secondary\"\n (click)=\"page?.onBackButtonClick()\" [text]=\"(page?.backButton()?.title | translate | async) ?? ''\">\n <ax-prefix>\n <ax-icon icon=\"fa-solid fa-chevron-left rtl:ax-rotate-180\"></ax-icon>\n </ax-prefix>\n </ax-button>\n }\n <!-------- Finish Back Button -------->\n <!--------------------------------------------------------------->\n <!-------- Begin Title Bar -------->\n <axp-layout-title-bar id=\"axp-title-bar\">\n <!-------- Begin Title Section -------->\n <div class=\"ax-flex ax-flex-col ax-transition ax-shrink-1 ax-min-w-0\">\n <!-------- Begin Breadcrumbs -------->\n @if (page?.hasBreadcrumbs() && !deviceService.isSmall()) {\n <ax-breadcrumbs id=\"axp-breadcrumb\" class=\"ax-mb-1\">\n @for (item of page?.breadcrumbs(); track $index) {\n <ax-breadcrumbs-item [attr.id]=\"'axp-breadcrumb-item-' + $index\" [active]=\"$last\"\n (click)=\"!$last && item.command ? execute(item.command) : null\">\n @if (item.icon) {\n <i [class]=\"item.icon\"></i>\n }\n {{ t(item.title) | async }}\n </ax-breadcrumbs-item>\n }\n <ng-template #divider>\n <i class=\"fa-regular fa-chevron-right rtl:ax-rotate-180 fa-sm\"></i>\n </ng-template>\n </ax-breadcrumbs>\n }\n <!-------- Finish Breadcrumbs -------->\n <!--------------------------------------------------------------->\n <!-------- Begin Title Container -------->\n <div class=\"__title-container\">\n <!-------- Begin Page Title -------->\n @if (page?.hasTitle()) {\n <axp-layout-title id=\"axp-page-title\">\n @if (page?.hasTitleIcon()) {\n <ax-icon [icon]=\"page?.titleIcon()!\" class=\"ax-me-2\"></ax-icon>\n }\n {{ page?.title() | translate | async }}\n </axp-layout-title>\n }\n <!-------- Finish Page Title -------->\n <!--------------------------------------------------------------->\n <!-------- Begin Title Actions -------->\n @if (page?.hasTitleActions()) {\n <ax-button id=\"axp-btn-title-actions\" [look]=\"'blank'\" class=\"ax-sm -ax-mx-2\">\n <ax-prefix>\n <i class=\"fa-solid fa-caret-down fa-fw\"></i>\n </ax-prefix>\n <ax-dropdown-panel #panel>\n <ax-button-item-list>\n @for (sub of page?.titleActions(); track $index) {\n <ng-container>\n <ax-button-item [attr.id]=\"'axp-btn-title-action-' + (sub.name || $index)\"\n [text]=\"(t(sub.title) | async)!\" [color]=\"sub.color\" [disabled]=\"sub.disabled\"\n [selected]=\"sub.command?.metadata?.['isSelected']\" (onClick)=\"handleActionClick(sub)\">\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 </ng-container>\n }\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-button>\n }\n <!-------- Finish Title Actions -------->\n <!--------------------------------------------------------------->\n <!-------- Begin Page Status -------->\n\n @if (page?.hasStatus()) {\n <axp-status-chip [value]=\"page?.status()?.value ?? null\" [readonly]=\"page?.status()?.readonly || false\"\n [definitionKey]=\"page?.status()?.definitionKey ?? null\" [entityData]=\"getEntityData()\"\n (transitionExecuted)=\"handleStatusTransition($event)\" class=\"__title-status\"></axp-status-chip>\n }\n <!-------- Finish Page Status -------->\n\n <!-------- Begin Title Badge -------->\n @if (page?.hasBadge()) {\n <div id=\"axp-page-badge\" class=\"__title-badge --{{ page?.badge()?.color }}\"\n [attr.title]=\"page?.badge()?.description ?? page?.badge()?.title\" [attr.aria-label]=\"page?.badge()?.title\">\n <i class=\"fa-solid fa-circle\" aria-hidden=\"true\"></i>\n <span class=\"__title-badge-label\">{{ page?.badge()?.title }}</span>\n </div>\n }\n <!-------- Finish Title Badge -------->\n\n </div>\n <!-------- Finish Title Container -------->\n <!--------------------------------------------------------------->\n <!-------- Begin Page Description -------->\n @if (page?.hasDescription()) {\n <axp-layout-description id=\"axp-page-description\">\n {{ page?.description() | translate | async }}</axp-layout-description>\n }\n <!-------- Finish Page Description -------->\n </div>\n <!-------- Finish Title Section -------->\n <!--------------------------------------------------------------->\n <!-------- Begin Page Actions -------->\n <axp-layout-actions id=\"axp-page-actions\" class=\"ax-shrink-0\">\n <axp-component-slot name=\"page-header-actions\"></axp-component-slot>\n <!-------- Begin Primary Actions -------->\n @if (page?.hasPrimaryActions()) {\n @for (item of page?.primaryMenuItems(); track $index) {\n @if (item.visible != false) {\n <ax-button [attr.id]=\"'axp-btn-primary-' + (item.name || item.title)\" [class.ax-sm]=\"deviceService.isSmall()\"\n [iconOnly]=\"deviceService.isSmall()\" [disabled]=\"item.disabled\" [text]=\"(t(item.title) | async)!\"\n [look]=\"'solid'\" [color]=\"item.color\" (onClick)=\"handleActionClick(item)\">\n <ax-prefix>\n <i class=\"{{ item.icon }}\"></i>\n </ax-prefix>\n @if (item?.items) {\n <ax-dropdown-panel #panel>\n <ax-button-item-list>\n @for (sub of item?.items; track $index) {\n @if (sub.visible != false) {\n <ax-button-item [attr.id]=\"'axp-btn-primary-' + (item.name || item.title) + '-' + (sub.name || sub.title)\"\n [text]=\"(t(sub.title) | async)!\" [color]=\"sub.color\" [disabled]=\"sub.disabled\"\n (onClick)=\"handleActionClick(sub)\">\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 <!-------- Finish Primary Actions -------->\n <!--------------------------------------------------------------->\n <!-------- Begin Secondary Actions -------->\n @if (page?.hasSecondaryActions()) {\n <ax-button id=\"axp-btn-secondary\" [class.ax-sm]=\"deviceService.isSmall()\" [iconOnly]=\"deviceService.isSmall()\"\n [text]=\"'@general:terms.interface.actions' | translate | async\"\n [look]=\"deviceService.isSmall() ? 'blank' : 'solid'\" [color]=\"'default'\">\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 (item of page?.secondaryMenuItems(); track $index) {\n @if (item.visible != false) {\n <ax-button-item [attr.id]=\"'axp-btn-secondary-' + (item.name || item.title)\"\n [text]=\"(item.title | translate | async)!\" [color]=\"item.color\" [disabled]=\"item.disabled\"\n (onClick)=\"handleSecondaryActionClick(item)\">\n <ax-prefix>\n <ax-icon icon=\"fa-light {{ item.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n @if (item.break) {\n <ax-divider></ax-divider>\n }\n }\n }\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-button>\n }\n <!-------- Finish Secondary Actions -------->\n </axp-layout-actions>\n <!-------- Finish Page Actions -------->\n </axp-layout-title-bar>\n <!-------- Finish Title Bar -------->\n <!--------------------------------------------------------------->\n <!-------- Begin Page Toolbar / Navbar -------->\n @if (!sticky.isSticky) {}\n <ng-content select=\"axp-page-toolbar\"></ng-content>\n <!-------- Finish Page Toolbar / Navbar -------->\n </axp-layout-header>\n <!-------- Finish Page Header -------->\n <!--------------------------------------------------------------->\n <!-------- Begin Page Content -------->\n <ng-content select=\"axp-page-content\"></ng-content>\n <!-------- Finish Page Content -------->\n <!--------------------------------------------------------------->\n <!-------- Begin Page Footer -------->\n <axp-page-footer-container id=\"axp-page-footer-container\"\n [ngStyle]=\"{ display: tt.isEmpty() && prefixSlot.isEmpty() && suffixSlot.isEmpty() ? 'none' : 'block' }\">\n <axp-layout-container #tt>\n <ng-content select=\"axp-page-footer\"></ng-content>\n </axp-layout-container>\n <axp-page-footer id=\"axp-page-footer\">\n <axp-layout-prefix>\n <axp-component-slot name=\"page-footer-start\" #prefixSlot=\"slot\"></axp-component-slot>\n </axp-layout-prefix>\n <axp-layout-suffix>\n <axp-component-slot name=\"page-footer-end\" #suffixSlot=\"slot\"></axp-component-slot>\n </axp-layout-suffix>\n </axp-page-footer>\n </axp-page-footer-container>\n <!-------- Finish Page Footer -------->\n</axp-layout-container>\n<!-------- Finish Main Layout Container -------->", styles: ["@charset \"UTF-8\";.axp-status-icon{font-size:.875rem;line-height:1}.axp-status-chip{display:inline-flex}.axp-status-chip--primary{background-color:rgb(var(--ax-sys-color-primary-lighter-surface));color:rgb(var(--ax-sys-color-on-primary-lighter-surface))}.axp-status-icon--primary{color:rgb(var(--ax-sys-color-primary-dark-surface))}.axp-status-chip--secondary{background-color:rgb(var(--ax-sys-color-secondary-lighter-surface));color:rgb(var(--ax-sys-color-on-secondary-lighter-surface))}.axp-status-icon--secondary{color:rgb(var(--ax-sys-color-secondary-dark-surface))}.axp-status-chip--success{background-color:rgb(var(--ax-sys-color-success-lighter-surface));color:rgb(var(--ax-sys-color-on-success-lighter-surface))}.axp-status-icon--success{color:rgb(var(--ax-sys-color-success-dark-surface))}.axp-status-chip--warning{background-color:rgb(var(--ax-sys-color-warning-lighter-surface));color:rgb(var(--ax-sys-color-on-warning-lighter-surface))}.axp-status-icon--warning{color:rgb(var(--ax-sys-color-warning-dark-surface))}.axp-status-chip--danger{background-color:rgb(var(--ax-sys-color-danger-lighter-surface));color:rgb(var(--ax-sys-color-on-danger-lighter-surface))}.axp-status-icon--danger{color:rgb(var(--ax-sys-color-danger-dark-surface))}.axp-status-chip--info{background-color:rgb(var(--ax-sys-color-accent3-lighter-surface));color:rgb(var(--ax-sys-color-on-accent3-lighter-surface))}.axp-status-icon--info{color:rgb(var(--ax-sys-color-accent3-dark-surface))}.axp-status-chip--accent1{background-color:rgb(var(--ax-sys-color-accent1-lighter-surface));color:rgb(var(--ax-sys-color-on-accent1-lighter-surface))}.axp-status-icon--accent1{color:rgb(var(--ax-sys-color-accent1-dark-surface))}.axp-status-chip--accent2{background-color:rgb(var(--ax-sys-color-accent2-lighter-surface));color:rgb(var(--ax-sys-color-on-accent2-lighter-surface))}.axp-status-icon--accent2{color:rgb(var(--ax-sys-color-accent2-dark-surface))}.axp-status-chip--accent3{background-color:rgb(var(--ax-sys-color-accent3-lighter-surface));color:rgb(var(--ax-sys-color-on-accent3-lighter-surface))}.axp-status-icon--accent3{color:rgb(var(--ax-sys-color-accent3-dark-surface))}.axp-status-chip--neutral{background-color:rgb(var(--ax-sys-color-neutral-lighter-surface));color:rgb(var(--ax-sys-color-on-neutral-lighter-surface))}.axp-status-icon--neutral{color:rgb(var(--ax-sys-color-neutral-dark-surface))}axp-layout-title{display:block;width:100%;font-weight:600}axp-layout-description{display:block;width:100%;font-size:.875rem;line-height:1.25rem;opacity:.65}axp-layout-actions{display:flex;align-items:center;gap:.75rem}axp-layout-actions axp-layout-actions-primary{display:flex;align-items:center;gap:.75rem}axp-layout-footer,axp-layout-toolbar,axp-layout-header,axp-page-header,axp-page-footer,axp-page-toolbar{display:flex;width:100%;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:.5rem}axp-layout-footer>axp-layout-prefix,axp-layout-footer>axp-layout-suffix,axp-layout-toolbar>axp-layout-prefix,axp-layout-toolbar>axp-layout-suffix,axp-layout-header>axp-layout-prefix,axp-layout-header>axp-layout-suffix,axp-page-header>axp-layout-prefix,axp-page-header>axp-layout-suffix,axp-page-footer>axp-layout-prefix,axp-page-footer>axp-layout-suffix,axp-page-toolbar>axp-layout-prefix,axp-page-toolbar>axp-layout-suffix{display:flex;flex-direction:row;align-items:center;gap:.5rem}axp-layout-footer>axp-layout-prefix,axp-layout-toolbar>axp-layout-prefix,axp-layout-header>axp-layout-prefix,axp-page-header>axp-layout-prefix,axp-page-footer>axp-layout-prefix,axp-page-toolbar>axp-layout-prefix{order:-9999}axp-layout-footer>axp-layout-suffix,axp-layout-toolbar>axp-layout-suffix,axp-layout-header>axp-layout-suffix,axp-page-header>axp-layout-suffix,axp-page-footer>axp-layout-suffix,axp-page-toolbar>axp-layout-suffix{order:9999;margin-inline-start:auto}axp-layout-list{display:flex;flex-direction:column}axp-layout-list>: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}axp-layout-list:focus{outline:none}axp-layout-list-group{display:flex;flex-direction:column;padding-top:.5rem;padding-bottom:.5rem}axp-layout-list-group>axp-layout-title{padding-bottom:.5rem;font-size:.875rem;line-height:1.25rem;font-weight:600}axp-layout-list-group>axp-layout-header{display:flex;justify-content:space-between;padding-top:.5rem;padding-bottom:.5rem}axp-layout-list-item{margin-top:.25rem;display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:.75rem;padding:.5rem;font-size:.875rem;line-height:1.25rem}axp-layout-list-item:focus{outline:none}axp-layout-list-item.axp-state-focused,axp-layout-list-item:hover{cursor:pointer;border-radius:.25rem;--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-light-surface),var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgba(var(--ax-sys-color-on-lightest-surface),var(--tw-text-opacity, 1))}axp-layout-list-item>axp-layout-prefix{margin-inline-end:auto;display:flex;flex-direction:row;gap:.5rem}axp-layout-list-item>axp-layout-content{flex:1 1 0%}axp-layout-list-item>axp-layout-content axp-layout-description{margin-top:.25rem!important;font-size:.75rem!important;line-height:1rem!important}axp-layout-list-item>axp-layout-suffix{margin-inline-start:auto;display:flex;flex-direction:row;gap:.5rem}axp-layout-sections{display:flex;flex-direction:column;gap:1rem}axp-layout-sections axp-layout-section{display:block;border-radius:.375rem;border-width:1px;--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}axp-layout-sections axp-layout-section>axp-layout-footer,axp-layout-sections axp-layout-section>axp-layout-header{padding:.5rem 1rem}@media(min-width:1280px){axp-layout-sections axp-layout-section>axp-layout-footer,axp-layout-sections axp-layout-section>axp-layout-header{padding-top:1rem;padding-bottom:1rem}}axp-layout-sections axp-layout-section>axp-layout-header{display:flex;flex-direction:column;gap:0px;border-top-left-radius:.375rem;border-top-right-radius:.375rem;border-bottom-width:1px}axp-layout-sections axp-layout-section>axp-layout-header axp-layout-title{font-weight:700;--tw-text-opacity: 1;color:rgba(var(--ax-sys-color-on-lightest-surface),var(--tw-text-opacity, 1))}axp-layout-sections axp-layout-section>axp-layout-header axp-layout-description{margin-top:.25rem;font-size:.875rem;line-height:1.25rem}axp-layout-sections axp-layout-section>axp-layout-footer{display:flex;align-items:center;justify-content:space-between;border-bottom-right-radius:.375rem;border-bottom-left-radius:.375rem;border-top-width:1px;padding:1rem}axp-layout-sections axp-layout-section>axp-layout-footer>axp-layout-suffix{margin-inline-start:auto;display:flex;gap:.5rem}axp-layout-sections axp-layout-section>axp-layout-footer{opacity:0;animation:fadeInDown .5s ease-out forwards}@keyframes fadeInDown{0%{opacity:0}to{opacity:1}}axp-page-layout{display:flex;width:100%;height:100%;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}axp-page-layout:is(.ax-dark *){background-color:rgb(var(--ax-sys-color-dark-surface));color:rgb(var(--ax-sys-color-on-dark-surface));border-color:rgb(var(--ax-sys-color-border-dark-surface))}axp-page-layout axp-layout-start-side,axp-page-layout axp-layout-end-side{display:flex;min-height:100%;min-width:16rem;flex-direction:column;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}axp-page-layout axp-layout-start-side:is(.ax-dark *),axp-page-layout axp-layout-end-side:is(.ax-dark *){background-color:rgb(var(--ax-sys-color-darker-surface));color:rgb(var(--ax-sys-color-on-darker-surface));border-color:rgb(var(--ax-sys-color-border-darker-surface))}axp-page-layout axp-layout-start-side axp-layout-header,axp-page-layout axp-layout-end-side axp-layout-header{display:flex;flex-direction:column;gap:.5rem;padding:1rem}axp-page-layout axp-layout-start-side axp-layout-header>axp-layout-title,axp-page-layout axp-layout-end-side axp-layout-header>axp-layout-title{font-size:1.125rem;line-height:1.75rem;font-weight:500}axp-page-layout axp-layout-start-side>axp-layout-content,axp-page-layout axp-layout-end-side>axp-layout-content{flex:1 1 0%}axp-page-layout>axp-layout-container{position:relative;display:flex;width:100%;height:100%;flex-direction:column;overflow:auto}axp-page-layout>axp-layout-container>axp-layout-header{position:sticky;top:0;z-index:20;display:flex;flex-direction:column;align-items:flex-start;gap:0px;padding-top:.5rem;padding-bottom:.5rem;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}axp-page-layout>axp-layout-container>axp-layout-header:is(.ax-dark *){background-color:rgb(var(--ax-sys-color-dark-surface));color:rgb(var(--ax-sys-color-on-dark-surface));border-color:rgb(var(--ax-sys-color-border-dark-surface))}@media(min-width:768px){axp-page-layout>axp-layout-container>axp-layout-header{padding-top:1rem;padding-bottom:1rem}}axp-page-layout>axp-layout-container>axp-layout-header.axp-is-sticky{--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-border-lightest-surface),var(--tw-border-opacity, 1));--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}axp-page-layout>axp-layout-container>axp-layout-header ax-breadcrumbs ax-breadcrumbs-item{font-size:.75rem;line-height:1rem}axp-page-layout>axp-layout-container>axp-layout-header ax-breadcrumbs ax-breadcrumbs-item.ax-state-active .ax-breadcrumb-item-content{cursor:default;--tw-text-opacity: 1;color:rgba(var(--ax-sys-color-on-lightest-surface),var(--tw-text-opacity, 1));opacity:.75}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar{display:flex;width:100%;align-items:center;justify-content:space-between;padding-left:1rem;padding-right:1rem}@media(min-width:1280px){axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar{border-width:0px}}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar ax-layout-nav-button{display:flex;align-items:center;justify-content:space-between}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container{display:flex;align-items:baseline;gap:.75rem}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container axp-layout-title{display:inline-block;width:fit-content;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:1.25rem;line-height:1.75rem;font-weight:500;line-height:1}@media(min-width:1024px){axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container axp-layout-title{font-size:1.5rem;line-height:2rem}}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge{display:inline-flex;--tw-translate-y: -3px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));align-items:center;align-self:baseline;border-radius:.375rem;padding:.25rem .5rem;font-size:.75rem;line-height:1rem;font-weight:600;line-height:1}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge i{margin-inline-end:.25rem;line-height:1;font-size:.5rem}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge.--warning{background-color:rgb(var(--ax-sys-color-warning-lightest-surface));color:rgb(var(--ax-sys-color-on-warning-lightest-surface));border-color:rgb(var(--ax-sys-color-border-warning-lightest-surface))}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge.--warning i{color:rgb(var(--ax-sys-color-warning-500))}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge.--danger{background-color:rgb(var(--ax-sys-color-danger-lightest-surface));color:rgb(var(--ax-sys-color-on-danger-lightest-surface));border-color:rgb(var(--ax-sys-color-border-danger-lightest-surface))}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge.--danger i{color:rgb(var(--ax-sys-color-danger-500))}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge.--success{background-color:rgb(var(--ax-sys-color-success-lightest-surface));color:rgb(var(--ax-sys-color-on-success-lightest-surface));border-color:rgb(var(--ax-sys-color-border-success-lightest-surface))}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge.--success i{color:rgb(var(--ax-sys-color-success-500))}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge.--primary{background-color:rgb(var(--ax-sys-color-primary-lightest-surface));color:rgb(var(--ax-sys-color-on-primary-lightest-surface));border-color:rgb(var(--ax-sys-color-border-primary-lightest-surface))}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge.--primary i{color:rgb(var(--ax-sys-color-primary-500))}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge.--secondary i{color:rgb(var(--ax-sys-color-secondary-500))}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge.--default i{color:rgb(var(--ax-sys-color-on-surface)/.45)}@media(max-width:600px){axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge{--tw-translate-y: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));background-color:transparent;padding:0;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge .__title-badge-label{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}axp-page-layout>axp-layout-container>axp-layout-header axp-layout-title-bar .__title-container .__title-badge i{margin:0;font-size:.625rem}}axp-page-layout>axp-layout-container>axp-layout-header axp-page-toolbar{margin-top:.5rem;padding-left:1rem;padding-right:1rem}axp-page-layout>axp-layout-container>axp-page-content{display:flex;width:100%;flex:1 1 0%;flex-direction:column;padding:.75rem 1rem}axp-page-layout>axp-layout-container>axp-page-content.--scrollable{padding-bottom:1rem}axp-page-layout>axp-layout-container>axp-page-footer-container{border-top-width:1px;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}axp-page-layout>axp-layout-container>axp-page-footer-container:is(.ax-dark *){background-color:rgb(var(--ax-sys-color-lighter-surface));color:rgb(var(--ax-sys-color-on-lighter-surface));border-color:rgb(var(--ax-sys-color-border-lighter-surface))}axp-page-layout>axp-layout-container>axp-page-footer-container{position:sticky;bottom:0;z-index:20;padding:.75rem;--tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / .05);--tw-shadow-colored: inset 0 2px 4px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}@keyframes ax-fadeInUp{0%{transform:translate3d(0,100%,0);opacity:0}}axp-page-layout>axp-layout-container>axp-page-footer-container.--animated{animation:1s both ax-fadeInUp;transition-duration:.3s;transition-timing-function:cubic-bezier(0,0,.2,1);animation-duration:.3s;animation-timing-function:cubic-bezier(0,0,.2,1)}\n"] }]
181
+ }], propDecorators: { startSideDrawer: [{ type: i0.ViewChild, args: ['startSideDrawer', { isSignal: true }] }] } });
182
+
183
+ //#region ---- Page Unsaved Changes Utilities ----
184
+ /**
185
+ * Builds the standard "Not Saved" page badge when the global show-page-badge setting is enabled.
186
+ */
187
+ async function buildNotSavedPageBadge(translateService, settingsService) {
188
+ const showPageBadge = await settingsService.get(AXPCommonSettings.ShowPageBadge);
189
+ if (!showPageBadge) {
190
+ return null;
191
+ }
192
+ return {
193
+ title: await translateService.translateAsync('@general:terms.interface.save-status.not-saved.title'),
194
+ color: 'warning',
195
+ description: await translateService.translateAsync('@general:terms.interface.save-status.not-saved.description'),
196
+ };
197
+ }
198
+ /**
199
+ * Whether a command name represents a discard/revert action on a details sub-page.
200
+ */
201
+ function isDiscardCommand(commandName, rejectCommandName) {
202
+ if (!commandName) {
203
+ return false;
204
+ }
205
+ if (rejectCommandName && commandName === rejectCommandName) {
206
+ return true;
207
+ }
208
+ if (commandName === 'discard') {
209
+ return true;
210
+ }
211
+ return /:discard$/i.test(commandName) || commandName.endsWith('Discard');
212
+ }
213
+ //#endregion
214
+
215
+ class AXPPageLayoutBaseComponent {
216
+ constructor() {
217
+ //#region ---------------- Services ----------------
218
+ this.layoutService = inject(AXPLayoutThemeService);
219
+ this.sessionService = inject(AXPSessionService);
220
+ this.translateService = inject(AXTranslationService);
221
+ this.formatService = inject(AXFormatService);
222
+ this.toastService = inject(AXToastService);
223
+ this.homePageService = inject(AXPHomePageService);
224
+ this.policyService = inject(AXPPolicyEngineService);
225
+ this.settingsService = inject(AXPSettingsService);
226
+ this.tabNavigation = inject(AXPTabNavigationService);
227
+ this.tabPreviewService = inject(AXPTabPreviewService);
228
+ this.unsavedChangesConfirm = inject(AXPUnsavedChangesConfirmService);
229
+ this.browserTitleService = inject(AXPBrowserTitleService);
230
+ this.router = inject(Router);
231
+ this.pageLayoutDestroyRef = inject(DestroyRef);
232
+ //#endregion
233
+ //#region ---------------- Signal to force recomputation ----------------
234
+ this._updateTrigger = signal(0, ...(ngDevMode ? [{ debugName: "_updateTrigger" }] : /* istanbul ignore next */ []));
235
+ /** Tab key captured at page init — stable while the route instance is cached. */
236
+ this.pageTabKey = signal(null, ...(ngDevMode ? [{ debugName: "pageTabKey" }] : /* istanbul ignore next */ []));
237
+ this.updateTrigger = this._updateTrigger.asReadonly();
238
+ //#endregion
239
+ //#region ---------------- Tab Title Sync ----------------
240
+ /** Refreshes tab title when this cached page becomes the active tab again. */
241
+ this.#syncTabTitleOnActivate = effect(() => {
242
+ const activeKey = this.tabNavigation.activeKey();
243
+ const pageKey = this.pageTabKey();
244
+ if (!activeKey || !pageKey || activeKey !== pageKey) {
245
+ return;
246
+ }
247
+ if (this.tabNavigation.effectiveMode() !== 'tabular') {
248
+ return;
249
+ }
250
+ untracked(() => {
251
+ this.recompute();
252
+ });
253
+ }, ...(ngDevMode ? [{ debugName: "#syncTabTitleOnActivate" }] : /* istanbul ignore next */ []));
254
+ /** Publishes structured tab preview (hover card + dirty badge) for this cached page. */
255
+ this.#syncTabStructuredPreview = effect(() => {
256
+ this.updateTrigger();
257
+ const tabKey = this.pageTabKey();
258
+ const titleValue = this.title();
259
+ const descriptionValue = this.description();
260
+ const icon = this.titleIcon();
261
+ const breadcrumbItems = this.breadcrumbs();
262
+ if (!tabKey || this.tabNavigation.effectiveMode() !== 'tabular') {
263
+ return;
264
+ }
265
+ const isDirty = this.hasUnsavedChanges();
266
+ untracked(() => {
267
+ void this.publishTabStructuredPreview(tabKey, {
268
+ titleValue,
269
+ descriptionValue,
270
+ icon,
271
+ breadcrumbItems,
272
+ isDirty,
273
+ });
274
+ });
275
+ }, ...(ngDevMode ? [{ debugName: "#syncTabStructuredPreview" }] : /* istanbul ignore next */ []));
276
+ //#endregion
277
+ //#region ---------------- Title ----------------
278
+ this.title = signal('', ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
279
+ this.titleIcon = signal(null, ...(ngDevMode ? [{ debugName: "titleIcon" }] : /* istanbul ignore next */ []));
280
+ this.#titleEffect = effect(async () => {
281
+ this.updateTrigger();
282
+ this.title.set(await this.getPageTitle());
283
+ const icon = await this.getPageIcon();
284
+ this.titleIcon.set(icon);
285
+ if (this.tabNavigation.effectiveMode() === 'tabular') {
286
+ const tabKey = this.pageTabKey();
287
+ if (tabKey && icon) {
288
+ this.tabNavigation.syncTabIcon(tabKey, icon);
289
+ }
290
+ }
291
+ }, ...(ngDevMode ? [{ debugName: "#titleEffect" }] : /* istanbul ignore next */ []));
292
+ this.hasTitle = computed(() => {
293
+ const value = this.title();
294
+ if (value === null || value === undefined) {
295
+ return false;
296
+ }
297
+ if (typeof value === 'string') {
298
+ return value.length > 0;
299
+ }
300
+ if (typeof value === 'object' && !Array.isArray(value)) {
301
+ return !isEmpty(value);
302
+ }
303
+ return false;
304
+ }, ...(ngDevMode ? [{ debugName: "hasTitle" }] : /* istanbul ignore next */ []));
305
+ this.hasTitleIcon = computed(() => this.titleIcon() !== null, ...(ngDevMode ? [{ debugName: "hasTitleIcon" }] : /* istanbul ignore next */ []));
306
+ this.#browserTitleEffect = effect(() => {
307
+ if (this.tabNavigation.effectiveMode() === 'tabular') {
308
+ return;
309
+ }
310
+ if (!this.isActivePageForBrowserTitle()) {
311
+ return;
312
+ }
313
+ this.updateTrigger();
314
+ this.title();
315
+ untracked(() => {
316
+ void this.publishBrowserTitleIfActive();
317
+ });
318
+ }, ...(ngDevMode ? [{ debugName: "#browserTitleEffect" }] : /* istanbul ignore next */ []));
319
+ //#endregion
320
+ //#region ---------------- Description ----------------
321
+ this.description = signal(null, ...(ngDevMode ? [{ debugName: "description" }] : /* istanbul ignore next */ []));
322
+ this.hasDescription = computed(() => {
323
+ const value = this.description();
324
+ if (value === null || value === undefined) {
325
+ return false;
326
+ }
327
+ if (typeof value === 'string') {
328
+ return value.length > 0;
329
+ }
330
+ if (typeof value === 'object' && !Array.isArray(value)) {
331
+ return !isEmpty(value);
332
+ }
333
+ return false;
334
+ }, ...(ngDevMode ? [{ debugName: "hasDescription" }] : /* istanbul ignore next */ []));
335
+ this.#descriptionEffect = effect(async () => {
336
+ this.updateTrigger();
337
+ this.description.set(await this.getPageDescription());
338
+ }, ...(ngDevMode ? [{ debugName: "#descriptionEffect" }] : /* istanbul ignore next */ []));
339
+ //#endregion
340
+ //#region ---------------- Breadcrumbs ----------------
341
+ this.defaultBreadCrumbs = [
342
+ {
343
+ title: '@general:terms.interface.navigation.home',
344
+ icon: 'fa-solid fa-home ax-text-xs ax-me-1',
345
+ command: {
346
+ name: 'navigate',
347
+ options: {
348
+ path: this.homePageService.getCurrent().path,
349
+ },
350
+ },
351
+ },
352
+ ];
353
+ this.breadcrumbs = signal(this.defaultBreadCrumbs, ...(ngDevMode ? [{ debugName: "breadcrumbs" }] : /* istanbul ignore next */ []));
354
+ this.#breadcrumbsEffect = effect(async () => {
355
+ this.updateTrigger();
356
+ const breadCrumbs = await this.getPageBreadcrumbs();
357
+ const trail = [...this.defaultBreadCrumbs, ...breadCrumbs];
358
+ this.breadcrumbs.set(trail);
359
+ if (this.tabNavigation.effectiveMode() === 'tabular') {
360
+ const tabKey = this.pageTabKey();
361
+ const tabTitleSegments = await this.resolveBreadcrumbTabTitleSegments(breadCrumbs);
362
+ let tabTitle = tabTitleSegments.length ? axpJoinTabTitleSegments(tabTitleSegments) : null;
363
+ if (!tabTitle) {
364
+ tabTitle = await this.resolvePageTitleForTab();
365
+ }
366
+ if (tabKey && tabTitle) {
367
+ this.tabNavigation.syncTabTitle(tabKey, tabTitleSegments.length ? tabTitleSegments : tabTitle);
368
+ }
369
+ }
370
+ }, ...(ngDevMode ? [{ debugName: "#breadcrumbsEffect" }] : /* istanbul ignore next */ []));
371
+ this.hasBreadcrumbs = computed(() => this.breadcrumbs().length > 1, ...(ngDevMode ? [{ debugName: "hasBreadcrumbs" }] : /* istanbul ignore next */ []));
372
+ //#endregion
373
+ //#region ---------------- Badge ----------------
374
+ this.badge = signal(null, ...(ngDevMode ? [{ debugName: "badge" }] : /* istanbul ignore next */ []));
375
+ this.hasBadge = computed(() => this.badge() !== null, ...(ngDevMode ? [{ debugName: "hasBadge" }] : /* istanbul ignore next */ []));
376
+ this.#badgeEffect = effect(async () => {
377
+ this.updateTrigger();
378
+ this.badge.set(await this.getPageBadge());
379
+ }, ...(ngDevMode ? [{ debugName: "#badgeEffect" }] : /* istanbul ignore next */ []));
380
+ //#endregion
381
+ //#region ---------------- Status ----------------
382
+ this.status = signal(null, ...(ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []));
383
+ this.hasStatus = computed(() => this.status() !== null, ...(ngDevMode ? [{ debugName: "hasStatus" }] : /* istanbul ignore next */ []));
384
+ this.#statusEffect = effect(async () => {
385
+ this.updateTrigger();
386
+ this.status.set(await this.getPageStatus());
387
+ }, ...(ngDevMode ? [{ debugName: "#statusEffect" }] : /* istanbul ignore next */ []));
388
+ //#endregion
389
+ //#region ---------------- Layout ----------------
390
+ this.layout = viewChild(AXPPageLayoutComponent, ...(ngDevMode ? [{ debugName: "layout" }] : /* istanbul ignore next */ []));
391
+ //#endregion
392
+ //#region ---------------- Actions ----------------
393
+ this.primaryMenuItems = signal([], ...(ngDevMode ? [{ debugName: "primaryMenuItems" }] : /* istanbul ignore next */ []));
394
+ this.hasPrimaryActions = computed(() => this.primaryMenuItems().filter((item) => item.visible != false).length > 0, ...(ngDevMode ? [{ debugName: "hasPrimaryActions" }] : /* istanbul ignore next */ []));
395
+ this.#primaryMenuItemsEffect = effect(async () => {
396
+ this.updateTrigger();
397
+ const items = await this.getPrimaryMenuItems();
398
+ this.primaryMenuItems.set(items);
399
+ }, ...(ngDevMode ? [{ debugName: "#primaryMenuItemsEffect" }] : /* istanbul ignore next */ []));
400
+ this.secondaryMenuItems = signal([], ...(ngDevMode ? [{ debugName: "secondaryMenuItems" }] : /* istanbul ignore next */ []));
401
+ this.hasSecondaryActions = computed(() => this.secondaryMenuItems().filter((item) => item.visible != false).length > 0, ...(ngDevMode ? [{ debugName: "hasSecondaryActions" }] : /* istanbul ignore next */ []));
402
+ this.#secondaryMenuItemsEffect = effect(async () => {
403
+ this.updateTrigger();
404
+ const items = await this.getSecondaryMenuItems();
405
+ this.secondaryMenuItems.set(items);
406
+ }, ...(ngDevMode ? [{ debugName: "#secondaryMenuItemsEffect" }] : /* istanbul ignore next */ []));
407
+ this.hasActions = computed(() => this.hasPrimaryActions() || this.hasSecondaryActions(), ...(ngDevMode ? [{ debugName: "hasActions" }] : /* istanbul ignore next */ []));
408
+ this.titleActions = signal([], ...(ngDevMode ? [{ debugName: "titleActions" }] : /* istanbul ignore next */ []));
409
+ this.hasTitleActions = computed(() => this.titleActions().length > 0, ...(ngDevMode ? [{ debugName: "hasTitleActions" }] : /* istanbul ignore next */ []));
410
+ this.#titleActionsEffect = effect(async () => {
411
+ this.updateTrigger();
412
+ this.titleActions.set(await this.getTitleActions());
413
+ }, ...(ngDevMode ? [{ debugName: "#titleActionsEffect" }] : /* istanbul ignore next */ []));
414
+ this.backButton = signal(null, ...(ngDevMode ? [{ debugName: "backButton" }] : /* istanbul ignore next */ []));
415
+ this.hasBackButton = computed(() => this.backButton() !== null, ...(ngDevMode ? [{ debugName: "hasBackButton" }] : /* istanbul ignore next */ []));
416
+ this.#backButtonEffect = effect(async () => {
417
+ this.updateTrigger();
418
+ this.backButton.set(await this.getBackButton());
419
+ }, ...(ngDevMode ? [{ debugName: "#backButtonEffect" }] : /* istanbul ignore next */ []));
420
+ }
421
+ recompute() {
422
+ this._updateTrigger.set(this._updateTrigger() + 1);
423
+ }
424
+ //#endregion
425
+ //#region ---------------- Lifecycle hooks ----------------
426
+ async ngOnInit() {
427
+ this.pageTabKey.set(this.resolvePageTabKey());
428
+ this.translateService.langChanges$.subscribe(() => {
429
+ this.recompute();
430
+ });
431
+ this.router.events
432
+ .pipe(filter((event) => event instanceof NavigationEnd), takeUntilDestroyed(this.pageLayoutDestroyRef))
433
+ .subscribe(() => {
434
+ void this.publishBrowserTitleIfActive();
435
+ });
436
+ }
437
+ /** Maps the home-page route to the pinned home tab key instead of a path-based key. */
438
+ resolvePageTabKey() {
439
+ const homePath = this.homePageService.getCurrent().path;
440
+ if (homePath && normalizeRouterUrl(homePath) === normalizeRouterUrl(this.router.url)) {
441
+ return AXP_HOME_TAB_KEY;
442
+ }
443
+ return axpResolveTabKey(this.router);
444
+ }
445
+ /** Stable tab key for this cached page instance (tabular mode). */
446
+ getPageTabKey() {
447
+ return this.pageTabKey();
448
+ }
449
+ //#endregion
450
+ //#region ---------------- Tab Title Sync ----------------
451
+ /** Refreshes tab title when this cached page becomes the active tab again. */
452
+ #syncTabTitleOnActivate;
453
+ /** Publishes structured tab preview (hover card + dirty badge) for this cached page. */
454
+ #syncTabStructuredPreview;
455
+ async publishTabStructuredPreview(tabKey, input) {
456
+ const tab = this.tabNavigation.tabList().find((item) => item.key === tabKey);
457
+ const breadcrumbTrail = [];
458
+ for (const item of input.breadcrumbItems.slice(1)) {
459
+ const segment = await this.resolveBreadcrumbItemTitle(item);
460
+ if (segment && segment !== breadcrumbTrail[breadcrumbTrail.length - 1]) {
461
+ breadcrumbTrail.push(segment);
462
+ }
463
+ }
464
+ const fallbackTitle = await this.translateTextIfKey(breadcrumbTrail.join(' - ') || tab?.title || '');
465
+ const title = await this.resolveMultiLanguageText(input.titleValue, fallbackTitle);
466
+ const subtitle = await this.resolveMultiLanguageText(input.descriptionValue, '');
467
+ this.tabPreviewService.setPreview(tabKey, {
468
+ title,
469
+ subtitle: subtitle || undefined,
470
+ url: tab?.url ?? '',
471
+ icon: input.icon ?? tab?.icon,
472
+ isDirty: input.isDirty,
473
+ breadcrumbTrail,
474
+ });
475
+ }
476
+ async resolveMultiLanguageText(value, fallback) {
477
+ const resolvedFallback = await this.translateTextIfKey(fallback);
478
+ if (value == null || value === '') {
479
+ return resolvedFallback;
480
+ }
481
+ if (typeof value === 'string') {
482
+ const trimmed = value.trim();
483
+ if (!trimmed) {
484
+ return resolvedFallback;
485
+ }
486
+ if (trimmed.startsWith('@')) {
487
+ return (await this.translateService.translateAsync(trimmed)) || resolvedFallback;
488
+ }
489
+ return trimmed;
490
+ }
491
+ const resolved = this.translateService.resolve(value);
492
+ return resolved || resolvedFallback;
493
+ }
494
+ /** Resolves i18n keys (`@scope:key`); returns plain text unchanged. */
495
+ async translateTextIfKey(text) {
496
+ const trimmed = text.trim();
497
+ if (!trimmed) {
498
+ return '';
499
+ }
500
+ if (trimmed.startsWith('@')) {
501
+ return (await this.translateService.translateAsync(trimmed)) || trimmed;
502
+ }
503
+ return trimmed;
504
+ }
505
+ #titleEffect;
506
+ getPageTitle() {
507
+ return '';
508
+ }
509
+ getPageIcon() {
510
+ return null;
511
+ }
512
+ #browserTitleEffect;
513
+ /** Publishes the page title to the browser tab when this instance is the active route. */
514
+ async publishBrowserTitleIfActive() {
515
+ if (this.tabNavigation.effectiveMode() === 'tabular') {
516
+ return;
517
+ }
518
+ if (!this.isActivePageForBrowserTitle()) {
519
+ return;
520
+ }
521
+ const routeKey = this.pageTabKey();
522
+ if (!routeKey) {
523
+ return;
524
+ }
525
+ const pageTitle = await this.resolvePageTitleForTab();
526
+ if (!this.isActivePageForBrowserTitle()) {
527
+ return;
528
+ }
529
+ this.browserTitleService.setSimpleModePageTitle(pageTitle, routeKey);
530
+ }
531
+ /** True when this cached page instance matches the current router location. */
532
+ isActivePageForBrowserTitle() {
533
+ const pageKey = this.pageTabKey();
534
+ if (!pageKey) {
535
+ return false;
536
+ }
537
+ return pageKey === axpResolveTabKey(this.router);
538
+ }
539
+ #descriptionEffect;
540
+ getPageDescription() {
541
+ return null;
542
+ }
543
+ #breadcrumbsEffect;
544
+ getPageBreadcrumbs() {
545
+ return [];
546
+ }
547
+ /** Collects resolved breadcrumb labels (Home excluded upstream) for tab chrome. */
548
+ async resolveBreadcrumbTabTitleSegments(pageBreadcrumbs) {
549
+ const segments = [];
550
+ for (const item of pageBreadcrumbs) {
551
+ const title = await this.resolveBreadcrumbItemTitle(item);
552
+ if (title && title !== segments[segments.length - 1]) {
553
+ segments.push(title);
554
+ }
555
+ }
556
+ return segments;
557
+ }
558
+ /** Joins breadcrumb segments as the stored tab title string. */
559
+ async resolveBreadcrumbTabTitle(pageBreadcrumbs) {
560
+ const segments = await this.resolveBreadcrumbTabTitleSegments(pageBreadcrumbs);
561
+ if (!segments.length) {
562
+ return null;
563
+ }
564
+ return axpJoinTabTitleSegments(segments);
565
+ }
566
+ /** Resolves a breadcrumb label the same way as page chrome (`t(item.title)`). */
567
+ async resolveBreadcrumbItemTitle(item) {
568
+ const raw = item?.title;
569
+ if (raw == null || raw === '') {
570
+ return null;
571
+ }
572
+ if (typeof raw === 'string') {
573
+ const trimmed = raw.trim();
574
+ if (!trimmed) {
575
+ return null;
576
+ }
577
+ if (trimmed.startsWith('@')) {
578
+ return (await this.translateService.translateAsync(trimmed)) || trimmed;
579
+ }
580
+ return trimmed;
581
+ }
582
+ if (typeof raw === 'object' && !Array.isArray(raw)) {
583
+ if (this.translateService.isValidMultiLanguageObject(raw)) {
584
+ const resolved = this.translateService.resolve(raw);
585
+ return resolved || null;
586
+ }
587
+ const resolved = this.translateService.resolve(raw);
588
+ return resolved || null;
589
+ }
590
+ return String(raw);
591
+ }
592
+ /** Fallback tab label from the page title when breadcrumbs are not ready yet. */
593
+ async resolvePageTitleForTab() {
594
+ const raw = await Promise.resolve(this.getPageTitle());
595
+ if (raw == null || raw === '') {
596
+ return null;
597
+ }
598
+ if (typeof raw === 'string') {
599
+ const trimmed = raw.trim();
600
+ if (trimmed.startsWith('@')) {
601
+ return (await this.translateService.translateAsync(trimmed)) || trimmed;
602
+ }
603
+ return trimmed;
604
+ }
605
+ if (typeof raw === 'object' && !Array.isArray(raw)) {
606
+ const resolved = this.translateService.resolve(raw);
607
+ return resolved || null;
608
+ }
609
+ return null;
610
+ }
611
+ #badgeEffect;
612
+ hasUnsavedChanges() {
613
+ return false;
614
+ }
615
+ /**
616
+ * Confirms closing this page as a background tab (no router navigation).
617
+ * Override when dirty detection differs from {@link hasUnsavedChanges}.
618
+ */
619
+ onBeforeTabClose() {
620
+ return this.unsavedChangesConfirm.confirmIfDirty(this.hasUnsavedChanges(), {
621
+ approveLeaveOnConfirm: false,
622
+ });
623
+ }
624
+ async getPageBadge() {
625
+ if (!this.hasUnsavedChanges()) {
626
+ return null;
627
+ }
628
+ return buildNotSavedPageBadge(this.translateService, this.settingsService);
629
+ }
630
+ async resolvePageBadge() {
631
+ return this.getPageBadge();
632
+ }
633
+ #statusEffect;
634
+ getPageStatus() {
635
+ return null;
636
+ }
637
+ //TODO: Remove this after migration to the new layout
638
+ toggleEndSide() {
639
+ //this.layout()?.toggleStartSide()
640
+ }
641
+ //TODO: Remove this after migration to the new layout
642
+ toggleStartSide() {
643
+ this.layout()?.toggleStartSide();
644
+ }
645
+ #primaryMenuItemsEffect;
646
+ getPrimaryMenuItems() {
647
+ return [];
648
+ }
649
+ #secondaryMenuItemsEffect;
650
+ getSecondaryMenuItems() {
651
+ return [];
652
+ }
653
+ #titleActionsEffect;
654
+ getTitleActions() {
655
+ return [];
656
+ }
657
+ getBackButton() {
658
+ return null;
659
+ }
660
+ #backButtonEffect;
661
+ //#endregion
662
+ //#region ---------------- Handle Commands ----------------
663
+ execute(command) { }
664
+ /**
665
+ * Optional partial context to merge into the create/update wizard when leaving a page step.
666
+ * Override when the page keeps edits in local state instead of the shared form context.
667
+ */
668
+ getWizardStepContext() {
669
+ return null;
670
+ }
671
+ startSideToggle() {
672
+ this.layout();
673
+ }
674
+ onBackButtonClick() { }
675
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPageLayoutBaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
676
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.9", type: AXPPageLayoutBaseComponent, isStandalone: true, selector: "ng-component", viewQueries: [{ propertyName: "layout", first: true, predicate: AXPPageLayoutComponent, descendants: true, isSignal: true }], ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
677
+ }
678
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPageLayoutBaseComponent, decorators: [{
679
+ type: Component,
680
+ args: [{
681
+ standalone: true,
682
+ template: '',
683
+ changeDetection: ChangeDetectionStrategy.OnPush,
684
+ }]
685
+ }], propDecorators: { layout: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AXPPageLayoutComponent), { isSignal: true }] }] } });
686
+
687
+ class AXPPopupLayoutBase {
688
+ }
689
+
690
+ class AXPPopupLayoutComponent {
691
+ constructor() {
692
+ this.popup = inject(AXPPopupLayoutBase);
693
+ this.layoutService = inject(AXPLayoutThemeService);
694
+ this.workflow = inject(AXPWorkflowService);
695
+ this.#initialize = afterNextRender(async () => {
696
+ this.checkScrollableContent();
697
+ });
698
+ this.#effect = effect(() => {
699
+ this.popup?.title();
700
+ this.checkScrollableContent();
701
+ }, ...(ngDevMode ? [{ debugName: "#effect" }] : /* istanbul ignore next */ []));
702
+ }
703
+ #initialize;
704
+ #effect;
705
+ checkScrollableContent() {
706
+ const content = document.querySelector('axp-popup-content');
707
+ if (content && content.scrollHeight >= content.clientHeight) {
708
+ content.classList.add('--scrollable');
709
+ }
710
+ }
711
+ handleActionClick(item) {
712
+ if (item.command && (item.items?.length ?? 0) == 0) {
713
+ this.popup?.execute(item.command);
714
+ }
715
+ }
716
+ handleSecondaryActionClick(item) {
717
+ if (item.command) {
718
+ this.popup?.execute(item.command);
719
+ }
720
+ }
721
+ handleBackdropClick(e) {
722
+ e.nativeEvent.preventDefault();
723
+ e.nativeEvent.stopPropagation();
724
+ e.component.hide();
725
+ return false;
726
+ }
727
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPopupLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
728
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPPopupLayoutComponent, isStandalone: true, selector: "axp-popup-layout", ngImport: i0, template: "<axp-layout-container id=\"axp-popup-layout\" #container>\n <ng-content select=\"axp-page-content\"></ng-content>\n <axp-page-footer id=\"axp-popup-footer\" *translate=\"let t\">\n <ax-prefix> </ax-prefix>\n @if (popup.hasPrimaryActions()) {\n <ax-suffix>\n @for (item of popup.primaryMenuItems(); track $index) {\n <ax-button\n [attr.id]=\"'axp-btn-primary-' + (item.name || item.title)\"\n [text]=\"(t(item.title) | async)!\"\n [look]=\"'solid'\"\n [color]=\"item.color\"\n (onClick)=\"handleActionClick(item)\"\n >\n <ax-prefix>\n <i class=\"{{ item.icon }}\"></i>\n </ax-prefix>\n </ax-button>\n }\n </ax-suffix>\n }\n </axp-page-footer>\n</axp-layout-container>\n", styles: [".axp-status-icon{font-size:.875rem;line-height:1}.axp-status-chip{display:inline-flex}.axp-status-chip--primary{background-color:rgb(var(--ax-sys-color-primary-lighter-surface));color:rgb(var(--ax-sys-color-on-primary-lighter-surface))}.axp-status-icon--primary{color:rgb(var(--ax-sys-color-primary-dark-surface))}.axp-status-chip--secondary{background-color:rgb(var(--ax-sys-color-secondary-lighter-surface));color:rgb(var(--ax-sys-color-on-secondary-lighter-surface))}.axp-status-icon--secondary{color:rgb(var(--ax-sys-color-secondary-dark-surface))}.axp-status-chip--success{background-color:rgb(var(--ax-sys-color-success-lighter-surface));color:rgb(var(--ax-sys-color-on-success-lighter-surface))}.axp-status-icon--success{color:rgb(var(--ax-sys-color-success-dark-surface))}.axp-status-chip--warning{background-color:rgb(var(--ax-sys-color-warning-lighter-surface));color:rgb(var(--ax-sys-color-on-warning-lighter-surface))}.axp-status-icon--warning{color:rgb(var(--ax-sys-color-warning-dark-surface))}.axp-status-chip--danger{background-color:rgb(var(--ax-sys-color-danger-lighter-surface));color:rgb(var(--ax-sys-color-on-danger-lighter-surface))}.axp-status-icon--danger{color:rgb(var(--ax-sys-color-danger-dark-surface))}.axp-status-chip--info{background-color:rgb(var(--ax-sys-color-accent3-lighter-surface));color:rgb(var(--ax-sys-color-on-accent3-lighter-surface))}.axp-status-icon--info{color:rgb(var(--ax-sys-color-accent3-dark-surface))}.axp-status-chip--accent1{background-color:rgb(var(--ax-sys-color-accent1-lighter-surface));color:rgb(var(--ax-sys-color-on-accent1-lighter-surface))}.axp-status-icon--accent1{color:rgb(var(--ax-sys-color-accent1-dark-surface))}.axp-status-chip--accent2{background-color:rgb(var(--ax-sys-color-accent2-lighter-surface));color:rgb(var(--ax-sys-color-on-accent2-lighter-surface))}.axp-status-icon--accent2{color:rgb(var(--ax-sys-color-accent2-dark-surface))}.axp-status-chip--accent3{background-color:rgb(var(--ax-sys-color-accent3-lighter-surface));color:rgb(var(--ax-sys-color-on-accent3-lighter-surface))}.axp-status-icon--accent3{color:rgb(var(--ax-sys-color-accent3-dark-surface))}.axp-status-chip--neutral{background-color:rgb(var(--ax-sys-color-neutral-lighter-surface));color:rgb(var(--ax-sys-color-on-neutral-lighter-surface))}.axp-status-icon--neutral{color:rgb(var(--ax-sys-color-neutral-dark-surface))}axp-layout-title{display:block;width:100%;font-weight:600}axp-layout-description{display:block;width:100%;font-size:.875rem;line-height:1.25rem;opacity:.65}axp-layout-actions{display:flex;align-items:center;gap:.75rem}axp-layout-actions axp-layout-actions-primary{display:flex;align-items:center;gap:.75rem}axp-layout-footer,axp-layout-toolbar,axp-layout-header,axp-page-header,axp-page-footer,axp-page-toolbar{display:flex;width:100%;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:.5rem}axp-layout-footer>axp-layout-prefix,axp-layout-footer>axp-layout-suffix,axp-layout-toolbar>axp-layout-prefix,axp-layout-toolbar>axp-layout-suffix,axp-layout-header>axp-layout-prefix,axp-layout-header>axp-layout-suffix,axp-page-header>axp-layout-prefix,axp-page-header>axp-layout-suffix,axp-page-footer>axp-layout-prefix,axp-page-footer>axp-layout-suffix,axp-page-toolbar>axp-layout-prefix,axp-page-toolbar>axp-layout-suffix{display:flex;flex-direction:row;align-items:center;gap:.5rem}axp-layout-footer>axp-layout-prefix,axp-layout-toolbar>axp-layout-prefix,axp-layout-header>axp-layout-prefix,axp-page-header>axp-layout-prefix,axp-page-footer>axp-layout-prefix,axp-page-toolbar>axp-layout-prefix{order:-9999}axp-layout-footer>axp-layout-suffix,axp-layout-toolbar>axp-layout-suffix,axp-layout-header>axp-layout-suffix,axp-page-header>axp-layout-suffix,axp-page-footer>axp-layout-suffix,axp-page-toolbar>axp-layout-suffix{order:9999;margin-inline-start:auto}axp-layout-list{display:flex;flex-direction:column}axp-layout-list>: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}axp-layout-list:focus{outline:none}axp-layout-list-group{display:flex;flex-direction:column;padding-top:.5rem;padding-bottom:.5rem}axp-layout-list-group>axp-layout-title{padding-bottom:.5rem;font-size:.875rem;line-height:1.25rem;font-weight:600}axp-layout-list-group>axp-layout-header{display:flex;justify-content:space-between;padding-top:.5rem;padding-bottom:.5rem}axp-layout-list-item{margin-top:.25rem;display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:.75rem;padding:.5rem;font-size:.875rem;line-height:1.25rem}axp-layout-list-item:focus{outline:none}axp-layout-list-item.axp-state-focused,axp-layout-list-item:hover{cursor:pointer;border-radius:.25rem;--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-light-surface),var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgba(var(--ax-sys-color-on-lightest-surface),var(--tw-text-opacity, 1))}axp-layout-list-item>axp-layout-prefix{margin-inline-end:auto;display:flex;flex-direction:row;gap:.5rem}axp-layout-list-item>axp-layout-content{flex:1 1 0%}axp-layout-list-item>axp-layout-content axp-layout-description{margin-top:.25rem!important;font-size:.75rem!important;line-height:1rem!important}axp-layout-list-item>axp-layout-suffix{margin-inline-start:auto;display:flex;flex-direction:row;gap:.5rem}axp-layout-sections{display:flex;flex-direction:column;gap:1rem}axp-layout-sections axp-layout-section{display:block;border-radius:.375rem;border-width:1px;--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}axp-layout-sections axp-layout-section>axp-layout-footer,axp-layout-sections axp-layout-section>axp-layout-header{padding:.5rem 1rem}@media(min-width:1280px){axp-layout-sections axp-layout-section>axp-layout-footer,axp-layout-sections axp-layout-section>axp-layout-header{padding-top:1rem;padding-bottom:1rem}}axp-layout-sections axp-layout-section>axp-layout-header{display:flex;flex-direction:column;gap:0px;border-top-left-radius:.375rem;border-top-right-radius:.375rem;border-bottom-width:1px}axp-layout-sections axp-layout-section>axp-layout-header axp-layout-title{font-weight:700;--tw-text-opacity: 1;color:rgba(var(--ax-sys-color-on-lightest-surface),var(--tw-text-opacity, 1))}axp-layout-sections axp-layout-section>axp-layout-header axp-layout-description{margin-top:.25rem;font-size:.875rem;line-height:1.25rem}axp-layout-sections axp-layout-section>axp-layout-footer{display:flex;align-items:center;justify-content:space-between;border-bottom-right-radius:.375rem;border-bottom-left-radius:.375rem;border-top-width:1px;padding:1rem}axp-layout-sections axp-layout-section>axp-layout-footer>axp-layout-suffix{margin-inline-start:auto;display:flex;gap:.5rem}axp-layout-sections axp-layout-section>axp-layout-footer{opacity:0;animation:fadeInDown .5s ease-out forwards}@keyframes fadeInDown{0%{opacity:0}to{opacity:1}}axp-popup-layout{display:flex;width:100%;height:100%;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}axp-popup-layout>axp-layout-container{position:relative;display:flex;width:100%;height:100%;flex-direction:column;overflow:auto;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}axp-popup-layout>axp-layout-container>axp-layout-header{position:sticky;top:0;z-index:10;display:flex;flex-direction:column;align-items:flex-start;gap:0px;--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-lightest-surface),var(--tw-bg-opacity, 1));padding-top:.5rem;padding-bottom:.5rem;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}@media(min-width:768px){axp-popup-layout>axp-layout-container>axp-layout-header{padding-top:1rem;padding-bottom:1rem}}axp-popup-layout>axp-layout-container>axp-page-content{display:flex;width:100%;flex:1 1 0%;flex-direction:column}axp-popup-layout>axp-layout-container>axp-page-footer{border-top-width:1px;--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-border-lightest-surface),var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-lightest-surface),var(--tw-bg-opacity, 1))}axp-popup-layout>axp-layout-container>axp-page-footer:is(.ax-dark *){--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-border-dark-surface),var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-dark-surface),var(--tw-bg-opacity, 1))}axp-popup-layout>axp-layout-container>axp-page-footer{position:sticky;bottom:0;padding:.75rem;--tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / .05);--tw-shadow-colored: inset 0 2px 4px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}@keyframes ax-fadeInUp{0%{transform:translate3d(0,100%,0);opacity:0}}axp-popup-layout>axp-layout-container>axp-page-footer.--animated{animation:1s both ax-fadeInUp;transition-duration:.3s;transition-timing-function:cubic-bezier(0,0,.2,1);animation-duration:.3s;animation-timing-function:cubic-bezier(0,0,.2,1)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type:
729
+ //
730
+ AXCommonModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i3.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXBreadcrumbsModule }, { kind: "ngmodule", type: AXDropdownButtonModule }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i6.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "directive", type: i3$1.AXTranslatorDirective, selector: "[translate]" }, { kind: "ngmodule", type: AXDrawerModule }, { kind: "ngmodule", type: AXDrawerDirectiveModule }, { kind: "component", type:
731
+ //
732
+ AXPThemeLayoutBlockComponent, selector: " axp-page-content, axp-page-footer-container, axp-page-footer, axp-page-header, axp-page-header-container, axp-page-toolbar, axp-layout-content, axp-layout-page-content, axp-layout-sections, axp-layout-body, axp-layout-page-body, axp-layout-prefix, axp-layout-suffix, axp-layout-title-bar, axp-layout-title, axp-layout-title-actions, axp-layout-nav-button, axp-layout-description, axp-layout-breadcrumbs, axp-layout-list-action, " }, { kind: "component", type: AXPThemeLayoutContainerComponent, selector: "axp-layout-container" }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
733
+ }
734
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPopupLayoutComponent, decorators: [{
735
+ type: Component,
736
+ args: [{ standalone: true, imports: [
737
+ CommonModule,
738
+ //
739
+ AXCommonModule,
740
+ AXDecoratorModule,
741
+ AXBreadcrumbsModule,
742
+ AXDropdownButtonModule,
743
+ AXDropdownModule,
744
+ AXButtonModule,
745
+ AXTranslationModule,
746
+ AXDrawerModule,
747
+ AXDrawerDirectiveModule,
748
+ //
749
+ AXPThemeLayoutBlockComponent,
750
+ AXPThemeLayoutContainerComponent,
751
+ ], selector: 'axp-popup-layout', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<axp-layout-container id=\"axp-popup-layout\" #container>\n <ng-content select=\"axp-page-content\"></ng-content>\n <axp-page-footer id=\"axp-popup-footer\" *translate=\"let t\">\n <ax-prefix> </ax-prefix>\n @if (popup.hasPrimaryActions()) {\n <ax-suffix>\n @for (item of popup.primaryMenuItems(); track $index) {\n <ax-button\n [attr.id]=\"'axp-btn-primary-' + (item.name || item.title)\"\n [text]=\"(t(item.title) | async)!\"\n [look]=\"'solid'\"\n [color]=\"item.color\"\n (onClick)=\"handleActionClick(item)\"\n >\n <ax-prefix>\n <i class=\"{{ item.icon }}\"></i>\n </ax-prefix>\n </ax-button>\n }\n </ax-suffix>\n }\n </axp-page-footer>\n</axp-layout-container>\n", styles: [".axp-status-icon{font-size:.875rem;line-height:1}.axp-status-chip{display:inline-flex}.axp-status-chip--primary{background-color:rgb(var(--ax-sys-color-primary-lighter-surface));color:rgb(var(--ax-sys-color-on-primary-lighter-surface))}.axp-status-icon--primary{color:rgb(var(--ax-sys-color-primary-dark-surface))}.axp-status-chip--secondary{background-color:rgb(var(--ax-sys-color-secondary-lighter-surface));color:rgb(var(--ax-sys-color-on-secondary-lighter-surface))}.axp-status-icon--secondary{color:rgb(var(--ax-sys-color-secondary-dark-surface))}.axp-status-chip--success{background-color:rgb(var(--ax-sys-color-success-lighter-surface));color:rgb(var(--ax-sys-color-on-success-lighter-surface))}.axp-status-icon--success{color:rgb(var(--ax-sys-color-success-dark-surface))}.axp-status-chip--warning{background-color:rgb(var(--ax-sys-color-warning-lighter-surface));color:rgb(var(--ax-sys-color-on-warning-lighter-surface))}.axp-status-icon--warning{color:rgb(var(--ax-sys-color-warning-dark-surface))}.axp-status-chip--danger{background-color:rgb(var(--ax-sys-color-danger-lighter-surface));color:rgb(var(--ax-sys-color-on-danger-lighter-surface))}.axp-status-icon--danger{color:rgb(var(--ax-sys-color-danger-dark-surface))}.axp-status-chip--info{background-color:rgb(var(--ax-sys-color-accent3-lighter-surface));color:rgb(var(--ax-sys-color-on-accent3-lighter-surface))}.axp-status-icon--info{color:rgb(var(--ax-sys-color-accent3-dark-surface))}.axp-status-chip--accent1{background-color:rgb(var(--ax-sys-color-accent1-lighter-surface));color:rgb(var(--ax-sys-color-on-accent1-lighter-surface))}.axp-status-icon--accent1{color:rgb(var(--ax-sys-color-accent1-dark-surface))}.axp-status-chip--accent2{background-color:rgb(var(--ax-sys-color-accent2-lighter-surface));color:rgb(var(--ax-sys-color-on-accent2-lighter-surface))}.axp-status-icon--accent2{color:rgb(var(--ax-sys-color-accent2-dark-surface))}.axp-status-chip--accent3{background-color:rgb(var(--ax-sys-color-accent3-lighter-surface));color:rgb(var(--ax-sys-color-on-accent3-lighter-surface))}.axp-status-icon--accent3{color:rgb(var(--ax-sys-color-accent3-dark-surface))}.axp-status-chip--neutral{background-color:rgb(var(--ax-sys-color-neutral-lighter-surface));color:rgb(var(--ax-sys-color-on-neutral-lighter-surface))}.axp-status-icon--neutral{color:rgb(var(--ax-sys-color-neutral-dark-surface))}axp-layout-title{display:block;width:100%;font-weight:600}axp-layout-description{display:block;width:100%;font-size:.875rem;line-height:1.25rem;opacity:.65}axp-layout-actions{display:flex;align-items:center;gap:.75rem}axp-layout-actions axp-layout-actions-primary{display:flex;align-items:center;gap:.75rem}axp-layout-footer,axp-layout-toolbar,axp-layout-header,axp-page-header,axp-page-footer,axp-page-toolbar{display:flex;width:100%;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:.5rem}axp-layout-footer>axp-layout-prefix,axp-layout-footer>axp-layout-suffix,axp-layout-toolbar>axp-layout-prefix,axp-layout-toolbar>axp-layout-suffix,axp-layout-header>axp-layout-prefix,axp-layout-header>axp-layout-suffix,axp-page-header>axp-layout-prefix,axp-page-header>axp-layout-suffix,axp-page-footer>axp-layout-prefix,axp-page-footer>axp-layout-suffix,axp-page-toolbar>axp-layout-prefix,axp-page-toolbar>axp-layout-suffix{display:flex;flex-direction:row;align-items:center;gap:.5rem}axp-layout-footer>axp-layout-prefix,axp-layout-toolbar>axp-layout-prefix,axp-layout-header>axp-layout-prefix,axp-page-header>axp-layout-prefix,axp-page-footer>axp-layout-prefix,axp-page-toolbar>axp-layout-prefix{order:-9999}axp-layout-footer>axp-layout-suffix,axp-layout-toolbar>axp-layout-suffix,axp-layout-header>axp-layout-suffix,axp-page-header>axp-layout-suffix,axp-page-footer>axp-layout-suffix,axp-page-toolbar>axp-layout-suffix{order:9999;margin-inline-start:auto}axp-layout-list{display:flex;flex-direction:column}axp-layout-list>: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}axp-layout-list:focus{outline:none}axp-layout-list-group{display:flex;flex-direction:column;padding-top:.5rem;padding-bottom:.5rem}axp-layout-list-group>axp-layout-title{padding-bottom:.5rem;font-size:.875rem;line-height:1.25rem;font-weight:600}axp-layout-list-group>axp-layout-header{display:flex;justify-content:space-between;padding-top:.5rem;padding-bottom:.5rem}axp-layout-list-item{margin-top:.25rem;display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:.75rem;padding:.5rem;font-size:.875rem;line-height:1.25rem}axp-layout-list-item:focus{outline:none}axp-layout-list-item.axp-state-focused,axp-layout-list-item:hover{cursor:pointer;border-radius:.25rem;--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-light-surface),var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgba(var(--ax-sys-color-on-lightest-surface),var(--tw-text-opacity, 1))}axp-layout-list-item>axp-layout-prefix{margin-inline-end:auto;display:flex;flex-direction:row;gap:.5rem}axp-layout-list-item>axp-layout-content{flex:1 1 0%}axp-layout-list-item>axp-layout-content axp-layout-description{margin-top:.25rem!important;font-size:.75rem!important;line-height:1rem!important}axp-layout-list-item>axp-layout-suffix{margin-inline-start:auto;display:flex;flex-direction:row;gap:.5rem}axp-layout-sections{display:flex;flex-direction:column;gap:1rem}axp-layout-sections axp-layout-section{display:block;border-radius:.375rem;border-width:1px;--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}axp-layout-sections axp-layout-section>axp-layout-footer,axp-layout-sections axp-layout-section>axp-layout-header{padding:.5rem 1rem}@media(min-width:1280px){axp-layout-sections axp-layout-section>axp-layout-footer,axp-layout-sections axp-layout-section>axp-layout-header{padding-top:1rem;padding-bottom:1rem}}axp-layout-sections axp-layout-section>axp-layout-header{display:flex;flex-direction:column;gap:0px;border-top-left-radius:.375rem;border-top-right-radius:.375rem;border-bottom-width:1px}axp-layout-sections axp-layout-section>axp-layout-header axp-layout-title{font-weight:700;--tw-text-opacity: 1;color:rgba(var(--ax-sys-color-on-lightest-surface),var(--tw-text-opacity, 1))}axp-layout-sections axp-layout-section>axp-layout-header axp-layout-description{margin-top:.25rem;font-size:.875rem;line-height:1.25rem}axp-layout-sections axp-layout-section>axp-layout-footer{display:flex;align-items:center;justify-content:space-between;border-bottom-right-radius:.375rem;border-bottom-left-radius:.375rem;border-top-width:1px;padding:1rem}axp-layout-sections axp-layout-section>axp-layout-footer>axp-layout-suffix{margin-inline-start:auto;display:flex;gap:.5rem}axp-layout-sections axp-layout-section>axp-layout-footer{opacity:0;animation:fadeInDown .5s ease-out forwards}@keyframes fadeInDown{0%{opacity:0}to{opacity:1}}axp-popup-layout{display:flex;width:100%;height:100%;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}axp-popup-layout>axp-layout-container{position:relative;display:flex;width:100%;height:100%;flex-direction:column;overflow:auto;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}axp-popup-layout>axp-layout-container>axp-layout-header{position:sticky;top:0;z-index:10;display:flex;flex-direction:column;align-items:flex-start;gap:0px;--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-lightest-surface),var(--tw-bg-opacity, 1));padding-top:.5rem;padding-bottom:.5rem;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}@media(min-width:768px){axp-popup-layout>axp-layout-container>axp-layout-header{padding-top:1rem;padding-bottom:1rem}}axp-popup-layout>axp-layout-container>axp-page-content{display:flex;width:100%;flex:1 1 0%;flex-direction:column}axp-popup-layout>axp-layout-container>axp-page-footer{border-top-width:1px;--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-border-lightest-surface),var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-lightest-surface),var(--tw-bg-opacity, 1))}axp-popup-layout>axp-layout-container>axp-page-footer:is(.ax-dark *){--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-border-dark-surface),var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-dark-surface),var(--tw-bg-opacity, 1))}axp-popup-layout>axp-layout-container>axp-page-footer{position:sticky;bottom:0;padding:.75rem;--tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / .05);--tw-shadow-colored: inset 0 2px 4px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}@keyframes ax-fadeInUp{0%{transform:translate3d(0,100%,0);opacity:0}}axp-popup-layout>axp-layout-container>axp-page-footer.--animated{animation:1s both ax-fadeInUp;transition-duration:.3s;transition-timing-function:cubic-bezier(0,0,.2,1);animation-duration:.3s;animation-timing-function:cubic-bezier(0,0,.2,1)}\n"] }]
752
+ }] });
753
+
754
+ class AXPPopupLayoutBaseComponent extends AXBasePageComponent {
755
+ constructor() {
756
+ super(...arguments);
757
+ //#region ---------------- Services ----------------
758
+ this.layoutService = inject(AXPLayoutThemeService);
759
+ this.sessionService = inject(AXPSessionService);
760
+ this.translateService = inject(AXTranslationService);
761
+ this.formatService = inject(AXFormatService);
762
+ this.toastService = inject(AXToastService);
763
+ this.homePageService = inject(AXPHomePageService);
764
+ //#endregion
765
+ //#region ---------------- Signal to force recomputation ----------------
766
+ this._updateTrigger = signal(0, ...(ngDevMode ? [{ debugName: "_updateTrigger" }] : /* istanbul ignore next */ []));
767
+ this.updateTrigger = this._updateTrigger.asReadonly();
768
+ //#endregion
769
+ //#region ---------------- Title ----------------
770
+ this.title = signal('', ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
771
+ this.#titleEffect = effect(async () => {
772
+ this.updateTrigger();
773
+ this.title.set(await this.getPopupTitle());
774
+ }, ...(ngDevMode ? [{ debugName: "#titleEffect" }] : /* istanbul ignore next */ []));
775
+ this.hasTitle = computed(() => this.title() !== '', ...(ngDevMode ? [{ debugName: "hasTitle" }] : /* istanbul ignore next */ []));
776
+ //#endregion
777
+ //#region ---------------- Layout ----------------
778
+ // Placeholder for popup layout reference if needed
779
+ this.layout = viewChild(AXPPopupLayoutComponent, ...(ngDevMode ? [{ debugName: "layout" }] : /* istanbul ignore next */ []));
780
+ //#endregion
781
+ //#region ---------------- Actions ----------------
782
+ this.primaryMenuItems = signal([], ...(ngDevMode ? [{ debugName: "primaryMenuItems" }] : /* istanbul ignore next */ []));
783
+ this.hasPrimaryActions = computed(() => this.primaryMenuItems().length > 0, ...(ngDevMode ? [{ debugName: "hasPrimaryActions" }] : /* istanbul ignore next */ []));
784
+ this.#primaryMenuItemsEffect = effect(async () => {
785
+ this.updateTrigger();
786
+ this.primaryMenuItems.set(await this.getPrimaryMenuItems());
787
+ }, ...(ngDevMode ? [{ debugName: "#primaryMenuItemsEffect" }] : /* istanbul ignore next */ []));
788
+ this.secondaryMenuItems = signal([], ...(ngDevMode ? [{ debugName: "secondaryMenuItems" }] : /* istanbul ignore next */ []));
789
+ this.hasSecondaryActions = computed(() => this.secondaryMenuItems().length > 0, ...(ngDevMode ? [{ debugName: "hasSecondaryActions" }] : /* istanbul ignore next */ []));
790
+ this.#secondaryMenuItemsEffect = effect(async () => {
791
+ this.updateTrigger();
792
+ this.secondaryMenuItems.set(await this.getSecondaryMenuItems());
793
+ }, ...(ngDevMode ? [{ debugName: "#secondaryMenuItemsEffect" }] : /* istanbul ignore next */ []));
794
+ this.hasActions = computed(() => this.hasPrimaryActions() || this.hasSecondaryActions(), ...(ngDevMode ? [{ debugName: "hasActions" }] : /* istanbul ignore next */ []));
795
+ this.titleActions = signal([], ...(ngDevMode ? [{ debugName: "titleActions" }] : /* istanbul ignore next */ []));
796
+ this.hasTitleActions = computed(() => this.titleActions().length > 0, ...(ngDevMode ? [{ debugName: "hasTitleActions" }] : /* istanbul ignore next */ []));
797
+ this.#titleActionsEffect = effect(async () => {
798
+ this.updateTrigger();
799
+ this.titleActions.set(await this.getTitleActions());
800
+ }, ...(ngDevMode ? [{ debugName: "#titleActionsEffect" }] : /* istanbul ignore next */ []));
801
+ }
802
+ recompute() {
803
+ this._updateTrigger.set(this._updateTrigger() + 1);
804
+ }
805
+ //#endregion
806
+ //#region ---------------- Lifecycle hooks ----------------
807
+ async ngOnInit() {
808
+ super.ngOnInit();
809
+ this.translateService.langChanges$.subscribe(() => {
810
+ this.recompute();
811
+ });
812
+ }
813
+ #titleEffect;
814
+ getPopupTitle() {
815
+ return '';
816
+ }
817
+ #primaryMenuItemsEffect;
818
+ getPrimaryMenuItems() {
819
+ return [];
820
+ }
821
+ #secondaryMenuItemsEffect;
822
+ getSecondaryMenuItems() {
823
+ return [];
824
+ }
825
+ #titleActionsEffect;
826
+ getTitleActions() {
827
+ return [];
828
+ }
829
+ //#endregion
830
+ //#region ---------------- Handle Commands ----------------
831
+ execute(command) { }
832
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPopupLayoutBaseComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
833
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.9", type: AXPPopupLayoutBaseComponent, isStandalone: true, selector: "ng-component", viewQueries: [{ propertyName: "layout", first: true, predicate: AXPPopupLayoutComponent, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
834
+ }
835
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPopupLayoutBaseComponent, decorators: [{
836
+ type: Component,
837
+ args: [{
838
+ standalone: true,
839
+ template: '',
840
+ changeDetection: ChangeDetectionStrategy.OnPush,
841
+ }]
842
+ }], propDecorators: { layout: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AXPPopupLayoutComponent), { isSignal: true }] }] } });
843
+
844
+ const AXP_LAYOUT_DETAILS_VIEW_ADAPTER_LOADER = new InjectionToken('AXP_LAYOUT_DETAILS_VIEW_ADAPTER_LOADER');
845
+ //#endregion
846
+
847
+ /** Trace namespace for details-view page / URL sync diagnostics. */
848
+ const DETAILS_VIEW_PAGE_TRACE_NS = 'details-view-page';
849
+
850
+ //#region ---- Root-context widget expression resolution ----
851
+ /**
852
+ * Evaluates `{{ ... }}` strings inside `options.customFilterDefinitions` using `rootContext`
853
+ * (details adapter load data), so filter UI does not depend on widget-renderer contextService.
854
+ */
855
+ async function buildEvaluatedPageContentsForRootContext(currentPage, rootContext, evaluatorService) {
856
+ if (!currentPage) {
857
+ return { evaluatedPageContent: [], evaluatedTabContentsById: {} };
858
+ }
859
+ const scope = {
860
+ context: {
861
+ eval: (path) => getSmart(rootContext, path),
862
+ },
863
+ };
864
+ const resolveNodes = async (nodes) => {
865
+ if (!nodes?.length) {
866
+ return [];
867
+ }
868
+ const cloned = cloneDeep(nodes);
869
+ for (const node of cloned) {
870
+ const defs = node.options?.['customFilterDefinitions'];
871
+ if (Array.isArray(defs) && defs.length > 0) {
872
+ node.options['customFilterDefinitions'] = await evaluatorService.evaluate(defs, scope);
873
+ }
874
+ }
875
+ return cloned;
876
+ };
877
+ const evaluatedPageContent = await resolveNodes(currentPage.content);
878
+ const evaluatedTabContentsById = {};
879
+ for (const tab of currentPage.tabs ?? []) {
880
+ evaluatedTabContentsById[tab.id] = await resolveNodes(tab.content);
881
+ }
882
+ return { evaluatedPageContent, evaluatedTabContentsById };
883
+ }
884
+ //#endregion
885
+ const AXPLayoutDetailsViewViewModel = signalStore(withState(() => {
886
+ const state = {
887
+ adapter: null,
888
+ rootContext: {},
889
+ savedContext: {},
890
+ previousContext: {},
891
+ context: {},
892
+ scope: null,
893
+ status: AXPPageStatus.Idle,
894
+ currentPage: null,
895
+ currentTab: null,
896
+ pageSelectedRows: [],
897
+ /** Page main column widgets with `customFilterDefinitions` evaluated against `rootContext` */
898
+ evaluatedPageContent: [],
899
+ /** Tab panel widgets keyed by tab id (same evaluation as `evaluatedPageContent`) */
900
+ evaluatedTabContentsById: {},
901
+ /** Dirty flag synced from {@link AXPWidgetContainerComponent.isFormDirty}. */
902
+ formDirty: false,
903
+ };
904
+ return state;
905
+ }), withComputed((store, deviceService = inject(AXPDeviceService)) => ({
906
+ icon: computed(() => store.adapter()?.icon ?? null),
907
+ content: computed(() => {
908
+ const pages = store.adapter()?.pages ?? [];
909
+ const primaryPage = pages.find((p) => p.isPrimary) ?? pages[0];
910
+ return primaryPage?.content ?? [];
911
+ }),
912
+ //
913
+ showPages: computed(() => !store.currentPage() && !deviceService.isLarge()),
914
+ isLarge: computed(() => deviceService.isLarge()),
915
+ //
916
+ isBusy: computed(() => store.status() == AXPPageStatus.Processing),
917
+ isSaving: computed(() => store.status() == AXPPageStatus.Submitting),
918
+ isOverlayBusy: computed(() => store.status() === AXPPageStatus.Processing || store.status() === AXPPageStatus.Submitting),
919
+ isPageReady: computed(() => store.adapter() != null &&
920
+ store.currentPage() != null &&
921
+ store.status() === AXPPageStatus.Rendered),
922
+ isDirty: computed(() => {
923
+ if (store.currentPage()?.isReadonly) {
924
+ return false;
925
+ }
926
+ return store.formDirty();
927
+ }),
928
+ changesCount: computed(() => {
929
+ if (store.currentPage()?.isReadonly) {
930
+ return 0;
931
+ }
932
+ return store.formDirty() ? 1 : 0;
933
+ }),
934
+ isLoaded: computed(() => store.adapter() != null),
935
+ currentPageSelectedRows: computed(() => store.pageSelectedRows()),
936
+ /** Evaluated widget nodes for the active tab only (avoids double-mount from keep-alive cache). */
937
+ activeTabContent: computed(() => {
938
+ const tab = store.currentTab();
939
+ if (!tab?.id) {
940
+ return [];
941
+ }
942
+ const evaluatedByTab = store.evaluatedTabContentsById();
943
+ return evaluatedByTab[tab.id] ?? [];
944
+ }),
945
+ })), 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), layout = inject(AXPLayoutThemeService), adapterLoader = inject(AXP_LAYOUT_DETAILS_VIEW_ADAPTER_LOADER)) => {
946
+ const resolvePageFromId = (adapter, pageId) => {
947
+ if (pageId) {
948
+ return adapter.pages.find((p) => p.id === pageId) ?? null;
949
+ }
950
+ if (deviceService.isLarge() || adapter.pages.length === 1) {
951
+ return adapter.pages.find((p) => p.isPrimary) ?? adapter.pages[0] ?? null;
952
+ }
953
+ return null;
954
+ };
955
+ /** Serializes page activation so route sync and UI cannot run overlapping loads. */
956
+ let pageActivationChain = Promise.resolve();
957
+ /** Serializes route-driven adapter loads (initial navigation and layout refresh). */
958
+ let routeLoadChain = Promise.resolve();
959
+ /** `page` query param from the browser URL (reliable on hard refresh). */
960
+ const readPageQueryParam = () => {
961
+ const page = router.parseUrl(router.url).queryParams['page'];
962
+ return typeof page === 'string' && page.length > 0 ? page : null;
963
+ };
964
+ /** Loads page data, context, and evaluated widgets for the target sidebar page. */
965
+ const activatePageById = async (pageId, forceRefresh) => {
966
+ const activation = pageActivationChain.then(() => runPageActivation(pageId, forceRefresh));
967
+ pageActivationChain = activation.catch(() => undefined);
968
+ await activation;
969
+ };
970
+ const runPageActivation = async (pageId, forceRefresh) => {
971
+ const adapter = store.adapter();
972
+ if (!adapter) {
973
+ return;
974
+ }
975
+ const targetPageId = pageId ?? readPageQueryParam() ?? undefined;
976
+ const currentPage = resolvePageFromId(adapter, targetPageId);
977
+ if (!forceRefresh && currentPage?.id === store.currentPage()?.id && store.status() === AXPPageStatus.Rendered) {
978
+ return;
979
+ }
980
+ const previousPageId = store.currentPage()?.id;
981
+ const isSidebarSwitch = previousPageId != null && previousPageId !== currentPage?.id;
982
+ if (isSidebarSwitch) {
983
+ layout.setNavigationLoading(true);
984
+ }
985
+ const overlayAlreadyOn = layout.overlayTaskCount() > 0;
986
+ if (!overlayAlreadyOn) {
987
+ layout.setOverlayLoading(true);
988
+ }
989
+ patchState(store, {
990
+ status: AXPPageStatus.Processing,
991
+ });
992
+ try {
993
+ let context = {};
994
+ if (currentPage) {
995
+ const currentPageIndex = adapter.pages.findIndex((p) => p.id === currentPage.id);
996
+ const pageLoad = adapter.pages[currentPageIndex]?.load;
997
+ if (typeof pageLoad === 'function') {
998
+ const loadResult = await pageLoad({ forceRefresh });
999
+ context = loadResult?.data ?? {};
1000
+ }
1001
+ else {
1002
+ // Component/list sidebar pages have no `load`; keep entity root context so widget
1003
+ // state is not cleared (Change History, etc. use `rootContext` instead).
1004
+ context = cloneDeep(store.rootContext());
1005
+ }
1006
+ }
1007
+ const currentTab = currentPage?.tabs?.[0] ?? null;
1008
+ const shouldSyncRootContext = forceRefresh === true && currentPage?.isPrimary === true && !isEmpty(context);
1009
+ const rootCtx = shouldSyncRootContext
1010
+ ? context
1011
+ : store.rootContext();
1012
+ const { evaluatedPageContent, evaluatedTabContentsById } = await buildEvaluatedPageContentsForRootContext(currentPage, rootCtx, evaluatorService);
1013
+ patchState(store, {
1014
+ previousContext: cloneDeep(context),
1015
+ context: cloneDeep(context),
1016
+ savedContext: cloneDeep(context),
1017
+ ...(shouldSyncRootContext ? { rootContext: cloneDeep(context) } : {}),
1018
+ status: AXPPageStatus.Rendered,
1019
+ currentPage,
1020
+ currentTab,
1021
+ evaluatedPageContent,
1022
+ evaluatedTabContentsById,
1023
+ formDirty: false,
1024
+ pageSelectedRows: [],
1025
+ });
1026
+ const urlPageParam = readPageQueryParam();
1027
+ if (currentPage && axpIsEntityDetailsViewRoute(router)) {
1028
+ if (!urlPageParam) {
1029
+ router.navigate([], {
1030
+ relativeTo: route,
1031
+ queryParams: { page: currentPage.id },
1032
+ queryParamsHandling: 'merge',
1033
+ replaceUrl: true,
1034
+ });
1035
+ }
1036
+ }
1037
+ }
1038
+ finally {
1039
+ if (isSidebarSwitch) {
1040
+ layout.setNavigationLoading(false);
1041
+ }
1042
+ if (store.status() === AXPPageStatus.Processing) {
1043
+ patchState(store, { status: AXPPageStatus.Rendered });
1044
+ }
1045
+ }
1046
+ };
1047
+ // Sync sidebar page from route query params
1048
+ effect((onCleanup) => {
1049
+ if (!store.adapter()) {
1050
+ return;
1051
+ }
1052
+ const subscription = route.queryParams.subscribe((params) => {
1053
+ const adapter = store.adapter();
1054
+ if (!adapter) {
1055
+ return;
1056
+ }
1057
+ // `load()` → `loadPage()` owns the first activation while `currentPage` is still null.
1058
+ if (store.currentPage() == null) {
1059
+ return;
1060
+ }
1061
+ const pageId = params['page'];
1062
+ if (!pageId) {
1063
+ return;
1064
+ }
1065
+ const targetPage = adapter.pages.find((p) => p.id === pageId);
1066
+ if (!targetPage || store.currentPage()?.id === targetPage.id) {
1067
+ return;
1068
+ }
1069
+ void activatePageById(targetPage.id);
1070
+ });
1071
+ onCleanup(() => subscription.unsubscribe());
1072
+ });
1073
+ /**
1074
+ * Breadcrumb titles are rendered with `{{ t(item.title) | async }}`; values must resolve to primitives
1075
+ * or `@` keys. Normalize MLS **before** evaluate/format **and** **after**, because `raw` and the evaluator
1076
+ * output can both carry MLS maps or locale-shaped objects.
1077
+ */
1078
+ function applyLocaleIfMultilingual(value) {
1079
+ if (value !== null &&
1080
+ value !== undefined &&
1081
+ typeof value === 'object' &&
1082
+ !Array.isArray(value) &&
1083
+ translateService.isValidMultiLanguageObject(value)) {
1084
+ return translateService.resolve(value);
1085
+ }
1086
+ return value;
1087
+ }
1088
+ /** Post-eval: collapse MLS maps and plain locale objects after evaluate/format (avoids `[object Object]` in `t()`). */
1089
+ function finalizeBreadcrumbTitleEvaluated(evaluated, fallback) {
1090
+ const chosen = evaluated ?? fallback;
1091
+ if (chosen !== null &&
1092
+ chosen !== undefined &&
1093
+ typeof chosen === 'object' &&
1094
+ !Array.isArray(chosen) &&
1095
+ translateService.isValidMultiLanguageObject(chosen)) {
1096
+ return translateService.resolve(chosen);
1097
+ }
1098
+ if (typeof chosen === 'string') {
1099
+ return chosen;
1100
+ }
1101
+ if (chosen !== null && chosen !== undefined && typeof chosen === 'object' && !Array.isArray(chosen)) {
1102
+ return translateService.resolve(chosen);
1103
+ }
1104
+ if (typeof fallback === 'string') {
1105
+ return fallback;
1106
+ }
1107
+ if (fallback !== null &&
1108
+ fallback !== undefined &&
1109
+ translateService.isValidMultiLanguageObject(fallback)) {
1110
+ return translateService.resolve(fallback);
1111
+ }
1112
+ return translateService.resolve(fallback);
1113
+ }
1114
+ async function resolveBreadcrumbTitleFields(raw, evalScope, formatContext) {
1115
+ /** Pre-eval: locale strip for recognizable MLS payloads before branching on expressions. */
1116
+ const sourceAfterPreLocale = applyLocaleIfMultilingual(raw);
1117
+ const isStructuredMlsObject = translateService.isValidMultiLanguageObject(raw) &&
1118
+ typeof raw === 'object' &&
1119
+ raw !== null &&
1120
+ !Array.isArray(raw);
1121
+ /** Full-string `{{ ... }}` uses `AXPExpressionEvaluatorService.isExpression` (aligned with evaluator API). */
1122
+ let evaluated;
1123
+ if (typeof sourceAfterPreLocale === 'string' && evaluatorService.isExpression(sourceAfterPreLocale)) {
1124
+ evaluated = await evaluatorService.evaluate(sourceAfterPreLocale, evalScope);
1125
+ }
1126
+ else if (isStructuredMlsObject) {
1127
+ evaluated = await evaluatorService.evaluate(raw, evalScope);
1128
+ }
1129
+ else if (typeof sourceAfterPreLocale === 'string') {
1130
+ if (sourceAfterPreLocale.trim().startsWith('@')) {
1131
+ evaluated = await translateService.translateAsync(sourceAfterPreLocale);
1132
+ }
1133
+ else {
1134
+ evaluated = await formatService.format(sourceAfterPreLocale, 'string', formatContext);
1135
+ }
1136
+ }
1137
+ else {
1138
+ evaluated = await formatService.format(raw, 'string', formatContext);
1139
+ }
1140
+ /** Post-eval: MLS-aware resolution again — evaluate may return MLS or map-like wrappers. */
1141
+ return finalizeBreadcrumbTitleEvaluated(evaluated, raw);
1142
+ }
1143
+ /**
1144
+ * Resolves MLS objects, evaluates format templates, or applies string formatting against page context (same semantics as legacy details layout header meta).
1145
+ */
1146
+ function convertToMultiLanguageString(value) {
1147
+ if (!value || isEmpty(value)) {
1148
+ return null;
1149
+ }
1150
+ if (translateService.isValidMultiLanguageObject(value)) {
1151
+ return translateService.resolve(value);
1152
+ }
1153
+ return formatService.format(value, 'string', store.context()) ?? '';
1154
+ }
1155
+ return {
1156
+ async loadAdapter(adapter) {
1157
+ patchState(store, { adapter });
1158
+ // Load adapter context if adapter has load function
1159
+ let adapterContext = {};
1160
+ if (adapter.load) {
1161
+ const adapterResult = await adapter.load();
1162
+ adapterContext = adapterResult?.data ?? {};
1163
+ }
1164
+ patchState(store, {
1165
+ rootContext: cloneDeep(adapterContext),
1166
+ });
1167
+ },
1168
+ async loadPage(pageId, forceRefresh) {
1169
+ await activatePageById(pageId, forceRefresh);
1170
+ },
1171
+ async load(adapter, forceRefresh, pageIdFromUrl) {
1172
+ const scope = {
1173
+ context: {
1174
+ ...contextEvalFactory.createLiveScope(() => store.context()),
1175
+ isDirty: () => store.isDirty(),
1176
+ },
1177
+ };
1178
+ patchState(store, {
1179
+ scope,
1180
+ });
1181
+ //
1182
+ await this.loadAdapter(adapter);
1183
+ const pageId = pageIdFromUrl ?? readPageQueryParam() ?? undefined;
1184
+ await this.loadPage(pageId, forceRefresh);
1185
+ },
1186
+ async loadFromRoute(forceRefresh) {
1187
+ const run = async () => {
1188
+ const pageIdFromUrl = readPageQueryParam() ?? undefined;
1189
+ const moduleName = route.parent?.snapshot.paramMap.get('module');
1190
+ const entityName = route.snapshot.paramMap.get('entity');
1191
+ const id = route.snapshot.paramMap.get('id');
1192
+ AXPDebugService.instance?.trace(DETAILS_VIEW_PAGE_TRACE_NS, 'viewmodel', 'loadFromRoute start', () => ({
1193
+ moduleName,
1194
+ entityName,
1195
+ id,
1196
+ pageIdFromUrl: pageIdFromUrl ?? null,
1197
+ forceRefresh: forceRefresh ?? false,
1198
+ note: 'Entity/route load started',
1199
+ }));
1200
+ if (!moduleName || !entityName || !id) {
1201
+ return;
1202
+ }
1203
+ layout.setNavigationLoading(true);
1204
+ if (layout.overlayTaskCount() === 0) {
1205
+ layout.setOverlayLoading(true);
1206
+ }
1207
+ try {
1208
+ if (forceRefresh) {
1209
+ patchState(store, {
1210
+ currentPage: null,
1211
+ status: AXPPageStatus.Idle,
1212
+ });
1213
+ }
1214
+ const adapter = await adapterLoader.load({ moduleName, entityName, id });
1215
+ await this.load(adapter, forceRefresh, pageIdFromUrl);
1216
+ }
1217
+ catch (error) {
1218
+ AXPDebugService.instance?.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'viewmodel', 'loadFromRoute overlay OFF (error)', () => ({
1219
+ moduleName,
1220
+ entityName,
1221
+ id,
1222
+ note: 'Load error — overlay OFF',
1223
+ }));
1224
+ layout.setOverlayLoading(false);
1225
+ if (error instanceof AXPNotFoundError) {
1226
+ void router.navigateByUrl(AXP_NOT_FOUND_ROUTE);
1227
+ return;
1228
+ }
1229
+ throw error;
1230
+ }
1231
+ finally {
1232
+ layout.setNavigationLoading(false);
1233
+ }
1234
+ };
1235
+ const load = routeLoadChain.then(run);
1236
+ routeLoadChain = load.catch(() => undefined);
1237
+ await load;
1238
+ },
1239
+ updateContext(context) {
1240
+ patchState(store, {
1241
+ context,
1242
+ });
1243
+ },
1244
+ setFormDirty(dirty) {
1245
+ if (store.formDirty() === dirty) {
1246
+ return;
1247
+ }
1248
+ patchState(store, {
1249
+ formDirty: dirty,
1250
+ });
1251
+ },
1252
+ updatePageSelectedRows(rows) {
1253
+ patchState(store, {
1254
+ pageSelectedRows: rows,
1255
+ });
1256
+ },
1257
+ async currentPagePrimaryActions() {
1258
+ // Use getActions() if available for reactive updates, otherwise fall back to static actions
1259
+ const currentPage = store.currentPage();
1260
+ const allActions = currentPage?.getActions ? await currentPage.getActions() : (currentPage?.actions ?? []);
1261
+ const raw = allActions.filter((a) => a.priority == 'primary');
1262
+ const acceptAction = currentPage?.settings?.commands?.accept;
1263
+ const rejectAction = currentPage?.settings?.commands?.reject;
1264
+ if (rejectAction) {
1265
+ raw.push({
1266
+ priority: 'primary',
1267
+ zone: 'footer',
1268
+ name: 'reject',
1269
+ title: rejectAction.title,
1270
+ color: rejectAction.color,
1271
+ icon: rejectAction.icon,
1272
+ command: rejectAction.command,
1273
+ visible: rejectAction.visible,
1274
+ });
1275
+ }
1276
+ if (acceptAction) {
1277
+ raw.push({
1278
+ priority: 'primary',
1279
+ zone: 'footer',
1280
+ name: 'accept',
1281
+ title: acceptAction.title,
1282
+ color: acceptAction.color,
1283
+ icon: acceptAction.icon,
1284
+ command: acceptAction.command,
1285
+ visible: acceptAction.visible,
1286
+ });
1287
+ }
1288
+ const processed = await evaluatorService.evaluate(raw, store.scope());
1289
+ return processed;
1290
+ },
1291
+ async currentPageSecondaryActions() {
1292
+ const raw = store
1293
+ .currentPage()
1294
+ ?.actions?.filter((a) => a.priority == 'secondary' && a.zone == 'header' && a.scope == AXPEntityCommandScope.TypeLevel) ?? [];
1295
+ const processed = await evaluatorService.evaluate(raw, store.scope());
1296
+ return processed;
1297
+ },
1298
+ currentPageContent() {
1299
+ return store.evaluatedPageContent();
1300
+ },
1301
+ currentPageTabs() {
1302
+ return store.currentPage()?.tabs?.filter((tab) => tab.visible !== false);
1303
+ },
1304
+ setCurrentPage(page) {
1305
+ const previousPage = store.currentPage();
1306
+ if (page) {
1307
+ if (!previousPage) {
1308
+ return;
1309
+ }
1310
+ const pageParam = readPageQueryParam();
1311
+ const isSamePage = previousPage?.id === page.id && pageParam === page.id;
1312
+ if (!isSamePage && axpIsEntityDetailsViewRoute(router)) {
1313
+ const queryParams = { page: page.id };
1314
+ if (previousPage?.id && previousPage.id !== page.id) {
1315
+ queryParams['filters'] = null;
1316
+ }
1317
+ router.navigate([], {
1318
+ relativeTo: route,
1319
+ queryParams,
1320
+ queryParamsHandling: 'merge',
1321
+ // Push history for desktop sidebar switches and mobile page-list picks.
1322
+ replaceUrl: false,
1323
+ });
1324
+ return;
1325
+ }
1326
+ void activatePageById(page.id);
1327
+ }
1328
+ else {
1329
+ patchState(store, {
1330
+ currentPage: null,
1331
+ currentTab: null,
1332
+ evaluatedPageContent: [],
1333
+ evaluatedTabContentsById: {},
1334
+ });
1335
+ if (axpIsEntityDetailsViewRoute(router)) {
1336
+ const currentParams = { ...route.snapshot.queryParams };
1337
+ delete currentParams['page'];
1338
+ router.navigate([], {
1339
+ relativeTo: route,
1340
+ queryParams: currentParams,
1341
+ replaceUrl: true,
1342
+ });
1343
+ }
1344
+ }
1345
+ },
1346
+ setCurrentTab(tab) {
1347
+ patchState(store, {
1348
+ currentTab: tab,
1349
+ });
1350
+ },
1351
+ //#region ---- Page layout metadata (title, description, back button, badge, status) ----
1352
+ backButtonTitle() {
1353
+ return store.showPages() ? (store.adapter()?.label ?? '') : (store.adapter()?.title ?? '');
1354
+ },
1355
+ async getPageTitle() {
1356
+ if (store.showPages()) {
1357
+ const raw = store.adapter()?.title;
1358
+ return convertToMultiLanguageString(raw) ?? '';
1359
+ }
1360
+ const raw = store.currentPage()?.title;
1361
+ return convertToMultiLanguageString(raw) ?? '';
1362
+ },
1363
+ async getPageDescription() {
1364
+ if (store.showPages()) {
1365
+ const raw = store.adapter()?.description;
1366
+ return convertToMultiLanguageString(raw);
1367
+ }
1368
+ const raw = store.currentPage()?.description;
1369
+ return convertToMultiLanguageString(raw);
1370
+ },
1371
+ async getBackButton() {
1372
+ const adapter = store.adapter();
1373
+ if (!adapter) {
1374
+ return null;
1375
+ }
1376
+ if ((adapter.pages?.length ?? 0) > 1) {
1377
+ const titleRaw = (store.showPages() ? (adapter.label ?? '') : (adapter.title ?? ''));
1378
+ return { title: convertToMultiLanguageString(titleRaw) ?? '@general:terms.common.back' };
1379
+ }
1380
+ const label = (adapter.label ?? '');
1381
+ return { title: convertToMultiLanguageString(label) ?? '@general:terms.common.back' };
1382
+ },
1383
+ async getPageBadge() {
1384
+ if (store.currentPage()?.isReadonly) {
1385
+ return null;
1386
+ }
1387
+ const showPageBadge = await settingsService.get(AXPCommonSettings.ShowPageBadge);
1388
+ if (!showPageBadge) {
1389
+ return null;
1390
+ }
1391
+ const adapter = store.adapter();
1392
+ if (!adapter?.getPageBadge) {
1393
+ return null;
1394
+ }
1395
+ return adapter.getPageBadge(store.context(), store.isDirty());
1396
+ },
1397
+ async getPageStatus() {
1398
+ const adapter = store.adapter();
1399
+ if (!adapter?.getPageStatus) {
1400
+ return null;
1401
+ }
1402
+ return adapter.getPageStatus(store.context(), store.currentPage());
1403
+ },
1404
+ //#endregion
1405
+ async primaryActions() {
1406
+ const raw = store.adapter()?.actions?.filter((a) => a.priority == 'primary') ?? [];
1407
+ const processed = await evaluatorService.evaluate(raw, store.scope());
1408
+ return processed;
1409
+ },
1410
+ async secondaryActions() {
1411
+ const raw = store
1412
+ .adapter()
1413
+ ?.actions?.filter((a) => a.priority == 'secondary' && a.scope == AXPEntityCommandScope.TypeLevel) ?? [];
1414
+ const processed = await evaluatorService.evaluate(raw, store.scope());
1415
+ return processed;
1416
+ },
1417
+ async breadcrumbs() {
1418
+ const rootContext = store.rootContext();
1419
+ const scope = {
1420
+ ...rootContext,
1421
+ context: {
1422
+ eval: (path) => get(rootContext, path),
1423
+ },
1424
+ };
1425
+ const breadcrumbs = await Promise.all(store.adapter()?.breadcrumbs?.map(async (b) => {
1426
+ const raw = (b?.title ?? '');
1427
+ return {
1428
+ ...b,
1429
+ title: await resolveBreadcrumbTitleFields(raw, scope, rootContext),
1430
+ };
1431
+ }) ?? []);
1432
+ // Add current page title as the last breadcrumb item
1433
+ const currentPageLabel = store.currentPage()?.label;
1434
+ const pages = store.adapter()?.pages ?? [];
1435
+ const primaryPage = pages.find((p) => p.isPrimary) ?? pages[0];
1436
+ const isMainPage = primaryPage?.id === store.currentPage()?.id;
1437
+ if (currentPageLabel && !isMainPage) {
1438
+ const pageScope = {
1439
+ ...store.context(),
1440
+ context: {
1441
+ eval: (path) => get(store.context(), path),
1442
+ },
1443
+ };
1444
+ breadcrumbs.push({
1445
+ title: await resolveBreadcrumbTitleFields(currentPageLabel, pageScope, store.context()),
1446
+ });
1447
+ }
1448
+ return breadcrumbs;
1449
+ },
1450
+ /** Aligns view-model context with the container after revert/commit (saved baseline). */
1451
+ applySavedState(context) {
1452
+ const snapshot = cloneDeep(context);
1453
+ patchState(store, {
1454
+ context: snapshot,
1455
+ savedContext: snapshot,
1456
+ previousContext: snapshot,
1457
+ formDirty: false,
1458
+ });
1459
+ },
1460
+ async discard() {
1461
+ const saved = cloneDeep(store.savedContext());
1462
+ patchState(store, {
1463
+ context: saved,
1464
+ previousContext: saved,
1465
+ formDirty: false,
1466
+ });
1467
+ },
1468
+ async save(command, form, liveContext) {
1469
+ const formResult = await form.validate();
1470
+ if (!formResult.result) {
1471
+ return false;
1472
+ }
1473
+ const payload = liveContext ?? store.context();
1474
+ patchState(store, {
1475
+ status: AXPPageStatus.Submitting,
1476
+ });
1477
+ try {
1478
+ const result = await store.currentPage()?.execute?.(command, payload);
1479
+ const enableOperationToasts = await settingsService.get(AXPCommonSettings.EnableOperationToasts);
1480
+ if (result?.success) {
1481
+ try {
1482
+ await this.loadPage(store.currentPage()?.id, true);
1483
+ }
1484
+ catch {
1485
+ //
1486
+ }
1487
+ patchState(store, {
1488
+ previousContext: store.context(),
1489
+ savedContext: cloneDeep(store.context()),
1490
+ formDirty: false,
1491
+ });
1492
+ if (enableOperationToasts) {
1493
+ toastService.show({
1494
+ color: 'success',
1495
+ title: await translateService.translateAsync('@general:messages.generic.success.title'),
1496
+ content: await translateService.translateAsync('@general:messages.generic.success.description'),
1497
+ location: 'bottom-center',
1498
+ closeButton: true,
1499
+ timeOut: 3000,
1500
+ timeOutProgress: true,
1501
+ });
1502
+ }
1503
+ return true;
1504
+ }
1505
+ const rawText = result?.message?.text;
1506
+ const errorText = rawText
1507
+ ? typeof rawText === 'string'
1508
+ ? rawText.startsWith('@')
1509
+ ? await translateService.translateAsync(rawText)
1510
+ : rawText
1511
+ : translateService.resolve(rawText)
1512
+ : await translateService.translateAsync('@general:messages.generic.error.description');
1513
+ toastService.show({
1514
+ color: 'danger',
1515
+ title: await translateService.translateAsync('@general:messages.generic.error.title'),
1516
+ content: errorText,
1517
+ location: 'bottom-center',
1518
+ closeButton: true,
1519
+ timeOut: 5000,
1520
+ timeOutProgress: true,
1521
+ });
1522
+ return false;
1523
+ }
1524
+ catch (error) {
1525
+ const errorText = error instanceof Error
1526
+ ? error.message
1527
+ : await translateService.translateAsync('@general:messages.generic.error.description');
1528
+ toastService.show({
1529
+ color: 'danger',
1530
+ title: await translateService.translateAsync('@general:messages.generic.error.title'),
1531
+ content: errorText,
1532
+ location: 'bottom-center',
1533
+ closeButton: true,
1534
+ timeOut: 5000,
1535
+ timeOutProgress: true,
1536
+ });
1537
+ return false;
1538
+ }
1539
+ finally {
1540
+ patchState(store, {
1541
+ status: AXPPageStatus.Rendered,
1542
+ });
1543
+ }
1544
+ },
1545
+ async goToListPage() {
1546
+ const exitUrl = store.adapter()?.exitUrl;
1547
+ if (exitUrl) {
1548
+ await axpNavigateAppPath(router, exitUrl);
1549
+ }
1550
+ },
1551
+ };
1552
+ }));
1553
+
1554
+ //#region ---- Details View Action Shortcut Utilities ----
1555
+ /** Platform defaults for known entity detail commands when `shortcuts` is omitted on the action. */
1556
+ const DEFAULT_ENTITY_DETAIL_ACTION_SHORTCUTS = {
1557
+ 'delete-entity': ['ctrl+shift+delete'],
1558
+ };
1559
+ const DEFAULT_FOOTER_COMMAND_SHORTCUTS = {
1560
+ 'update-entity': ['ctrl+s'],
1561
+ discard: ['escape'],
1562
+ };
1563
+ const SAVE_FOOTER_COMMAND_SUFFIX = ':Save';
1564
+ const DISCARD_FOOTER_COMMAND_SUFFIX = ':Discard';
1565
+ /**
1566
+ * Returns the base workflow/command name from a composite entity action command id (`cmd&actionName`).
1567
+ */
1568
+ function getDetailsViewActionCommandBaseName(commandName) {
1569
+ if (!commandName) {
1570
+ return '';
1571
+ }
1572
+ return commandName.split('&')[0];
1573
+ }
1574
+ /**
1575
+ * Resolves keyboard shortcuts for a header entity action.
1576
+ * Explicit `shortcuts` wins; pass `[]` to disable. Otherwise known commands use platform defaults.
1577
+ */
1578
+ function resolveDetailsViewActionShortcuts(commandBaseName, explicit) {
1579
+ if (explicit !== undefined) {
1580
+ const chords = flattenDetailsViewActionShortcuts(explicit);
1581
+ return chords.length ? chords : undefined;
1582
+ }
1583
+ const defaults = DEFAULT_ENTITY_DETAIL_ACTION_SHORTCUTS[commandBaseName];
1584
+ return defaults?.length ? [...defaults] : undefined;
1585
+ }
1586
+ /**
1587
+ * Resolves save/discard footer command shortcuts from page settings.
1588
+ */
1589
+ function resolveDetailsViewFooterCommandShortcuts(commandName, explicit) {
1590
+ if (explicit !== undefined) {
1591
+ const chords = flattenDetailsViewActionShortcuts(explicit);
1592
+ return chords.length ? chords : undefined;
1593
+ }
1594
+ const defaults = DEFAULT_FOOTER_COMMAND_SHORTCUTS[commandName];
1595
+ if (defaults?.length) {
1596
+ return [...defaults];
1597
+ }
1598
+ return resolvePageFooterActionDefaultShortcuts(commandName);
1599
+ }
1600
+ /**
1601
+ * Platform defaults for component-page footer save/discard commands (e.g. `MetaDataBuilder:Save`).
1602
+ */
1603
+ function resolvePageFooterActionDefaultShortcuts(commandName) {
1604
+ if (isSaveFooterCommand(commandName)) {
1605
+ return [...DEFAULT_FOOTER_COMMAND_SHORTCUTS['update-entity']];
1606
+ }
1607
+ if (isDiscardFooterCommand(commandName)) {
1608
+ return [...DEFAULT_FOOTER_COMMAND_SHORTCUTS['discard']];
1609
+ }
1610
+ return undefined;
1611
+ }
1612
+ function isSaveFooterCommand(commandName) {
1613
+ return commandName === 'update-entity' || commandName.endsWith(SAVE_FOOTER_COMMAND_SUFFIX);
1614
+ }
1615
+ function isDiscardFooterCommand(commandName) {
1616
+ return commandName === 'discard' || commandName.endsWith(DISCARD_FOOTER_COMMAND_SUFFIX);
1617
+ }
1618
+ /**
1619
+ * Resolves keyboard shortcuts for a footer action on entity detail sub-pages.
1620
+ */
1621
+ function resolvePageFooterActionShortcuts(commandName, explicit) {
1622
+ return resolveDetailsViewFooterCommandShortcuts(commandName, explicit);
1623
+ }
1624
+ function flattenDetailsViewActionShortcuts(shortcuts) {
1625
+ const chords = normalizeKeyboardShortcuts(shortcuts).flatMap((shortcut) => shortcut.keys);
1626
+ return dedupeDetailsViewActionShortcuts(chords);
1627
+ }
1628
+ function dedupeDetailsViewActionShortcuts(shortcuts) {
1629
+ const seen = new Set();
1630
+ const result = [];
1631
+ for (const shortcut of shortcuts) {
1632
+ const token = shortcut.trim().toLowerCase();
1633
+ if (!token || seen.has(token)) {
1634
+ continue;
1635
+ }
1636
+ seen.add(token);
1637
+ result.push(shortcut.trim());
1638
+ }
1639
+ return result;
1640
+ }
1641
+ //#endregion
1642
+
1643
+ /**
1644
+ * Registry of rendered page component instances by component key.
1645
+ * Used so page execute can delegate to the actual component instance in the view.
1646
+ */
1647
+ class AXPPageComponentInstanceRegistryService {
1648
+ constructor() {
1649
+ this.instances = new Map();
1650
+ }
1651
+ register(key, instance) {
1652
+ this.instances.set(key, instance);
1653
+ }
1654
+ unregister(key) {
1655
+ this.instances.delete(key);
1656
+ }
1657
+ get(key) {
1658
+ return this.instances.get(key) ?? null;
1659
+ }
1660
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPageComponentInstanceRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1661
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPageComponentInstanceRegistryService, providedIn: 'root' }); }
1662
+ }
1663
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPageComponentInstanceRegistryService, decorators: [{
1664
+ type: Injectable,
1665
+ args: [{
1666
+ providedIn: 'root',
1667
+ }]
1668
+ }] });
1669
+
1670
+ //#region ---- Page Component Renderer Directive ----
1671
+ /**
1672
+ * Directive for rendering page components dynamically
1673
+ * Similar to component-slot pattern but specifically for page components
1674
+ */
1675
+ class AXPPageComponentRendererDirective {
1676
+ constructor() {
1677
+ this.componentKey = input.required(...(ngDevMode ? [{ debugName: "componentKey" }] : /* istanbul ignore next */ []));
1678
+ this.rootContext = input({}, ...(ngDevMode ? [{ debugName: "rootContext" }] : /* istanbul ignore next */ []));
1679
+ this.pageConfig = input(...(ngDevMode ? [undefined, { debugName: "pageConfig" }] : /* istanbul ignore next */ []));
1680
+ this.options = input({}, ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
1681
+ /** Emitted when the component instance is registered and ready */
1682
+ this.componentReady = output();
1683
+ this.viewContainerRef = inject(ViewContainerRef);
1684
+ this.pageComponentRegistry = inject(AXPPageComponentRegistryService);
1685
+ this.instanceRegistry = inject(AXPPageComponentInstanceRegistryService);
1686
+ this.contextStore = inject(AXPContextStore);
1687
+ this.componentRef = null;
1688
+ this.#contextEffect = effect(() => {
1689
+ const context = this.rootContext();
1690
+ if (this.componentRef && context) {
1691
+ this.componentRef.setInput('rootContext', context);
1692
+ }
1693
+ }, ...(ngDevMode ? [{ debugName: "#contextEffect" }] : /* istanbul ignore next */ []));
1694
+ this.#pageConfigEffect = effect(() => {
1695
+ const config = this.pageConfig();
1696
+ if (this.componentRef && config) {
1697
+ this.componentRef.setInput('pageConfig', config);
1698
+ }
1699
+ }, ...(ngDevMode ? [{ debugName: "#pageConfigEffect" }] : /* istanbul ignore next */ []));
1700
+ this.#optionsEffect = effect(() => {
1701
+ const options = this.options();
1702
+ if (this.componentRef && options) {
1703
+ this.componentRef.setInput('options', options);
1704
+ }
1705
+ }, ...(ngDevMode ? [{ debugName: "#optionsEffect" }] : /* istanbul ignore next */ []));
1706
+ }
1707
+ /** Returns the dynamically mounted page component instance, if loaded. */
1708
+ getMountedPageInstance() {
1709
+ return this.componentRef?.instance ?? null;
1710
+ }
1711
+ #contextEffect;
1712
+ #pageConfigEffect;
1713
+ #optionsEffect;
1714
+ async ngOnInit() {
1715
+ await this.loadComponent();
1716
+ }
1717
+ ngOnDestroy() {
1718
+ if (this.componentRef) {
1719
+ this.syncWizardStepContextBeforeTeardown();
1720
+ this.instanceRegistry.unregister(this.componentKey());
1721
+ this.componentRef.destroy();
1722
+ this.componentRef = null;
1723
+ }
1724
+ }
1725
+ /** Persists page edits into the shared form context before the step component is destroyed. */
1726
+ syncWizardStepContextBeforeTeardown() {
1727
+ const instance = this.componentRef?.instance;
1728
+ const partial = instance?.getWizardStepContext?.();
1729
+ if (!partial || typeof partial !== 'object') {
1730
+ return;
1731
+ }
1732
+ for (const [path, value] of Object.entries(partial)) {
1733
+ this.contextStore.update(path, value, { origin: 'system' });
1734
+ }
1735
+ }
1736
+ async loadComponent() {
1737
+ try {
1738
+ // Clear any existing component
1739
+ this.viewContainerRef.clear();
1740
+ // Get component loader from registry
1741
+ const loader = await this.pageComponentRegistry.find(this.componentKey());
1742
+ if (!loader) {
1743
+ console.error(`Component with key "${this.componentKey()}" not found in registry`);
1744
+ return;
1745
+ }
1746
+ // Load component type
1747
+ const ComponentType = await loader();
1748
+ // Create component instance
1749
+ this.componentRef = this.viewContainerRef.createComponent(ComponentType);
1750
+ this.instanceRegistry.register(this.componentKey(), this.componentRef.instance);
1751
+ const context = this.rootContext();
1752
+ if (context) {
1753
+ this.componentRef.setInput('rootContext', context);
1754
+ }
1755
+ const options = this.options();
1756
+ if (options) {
1757
+ this.componentRef.setInput('options', options);
1758
+ }
1759
+ const pageConfig = this.pageConfig();
1760
+ if (pageConfig) {
1761
+ this.componentRef.setInput('pageConfig', pageConfig);
1762
+ }
1763
+ // Notify that component is ready (allows parent to re-fetch actions)
1764
+ this.componentReady.emit(this.componentKey());
1765
+ }
1766
+ catch (error) {
1767
+ console.error(`Failed to load component "${this.componentKey()}":`, error);
1768
+ }
1769
+ }
1770
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPageComponentRendererDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1771
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: AXPPageComponentRendererDirective, isStandalone: true, selector: "[axp-page-component-renderer]", inputs: { componentKey: { classPropertyName: "componentKey", publicName: "componentKey", isSignal: true, isRequired: true, transformFunction: null }, rootContext: { classPropertyName: "rootContext", publicName: "rootContext", isSignal: true, isRequired: false, transformFunction: null }, pageConfig: { classPropertyName: "pageConfig", publicName: "pageConfig", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { componentReady: "componentReady" }, ngImport: i0 }); }
1772
+ }
1773
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPageComponentRendererDirective, decorators: [{
1774
+ type: Directive,
1775
+ args: [{
1776
+ selector: '[axp-page-component-renderer]',
1777
+ standalone: true,
1778
+ }]
1779
+ }], 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"] }] } });
1780
+
1781
+ /** Registered id of the data-list inside related-entity `page-list` sidebar pages. */
1782
+ const PAGE_LIST_TABLE_WIDGET_ID = 'page-list_table';
1783
+ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
1784
+ constructor() {
1785
+ super(...arguments);
1786
+ //
1787
+ this.vm = inject(AXPLayoutDetailsViewViewModel);
1788
+ this.route = inject(ActivatedRoute);
1789
+ this.eventService = inject(AXPBroadcastEventService);
1790
+ this.deviceService = inject(AXPDeviceService);
1791
+ this.unsavedChangesPopstate = inject(AXPUnsavedChangesPopstateService);
1792
+ this.pageComponentInstanceRegistry = inject(AXPPageComponentInstanceRegistryService);
1793
+ this.shortcutRegistry = inject(AXPKeyboardShortcutRegistry);
1794
+ this.workflowService = inject(AXPWorkflowService);
1795
+ this.destroyRef = inject(DestroyRef);
1796
+ this.elementRef = inject((ElementRef));
1797
+ this.debug = inject(AXPDebugService);
1798
+ /** Cancels in-flight list selection bind when the sidebar page or widget tree changes. */
1799
+ this.pageListSelectionBindGeneration = 0;
1800
+ /** Prevents sidebar tab init events from pushing history before the first page activation completes. */
1801
+ this.detailsLayoutReady = signal(false, ...(ngDevMode ? [{ debugName: "detailsLayoutReady" }] : /* istanbul ignore next */ []));
1802
+ this.destroyed$ = new Subject();
1803
+ this.footerActionsGeneration = 0;
1804
+ this.keyboardShortcutRegistrationGeneration = 0;
1805
+ /** Page id the overlay release logic is bound to; reset on sidebar/page switch. */
1806
+ this.overlayTargetPageId = null;
1807
+ /** After a page switch, wait for widget registry to clear before accepting remounted widgets. */
1808
+ this.overlaySawZeroWidgetsAfterPageChange = false;
1809
+ /** Cancels in-flight overlay release when the page changes. */
1810
+ this.overlayReleaseGeneration = 0;
1811
+ /** Header actions with resolved shortcut chords, keyed by command name. */
1812
+ this.actionShortcutEntries = signal(new Map(), ...(ngDevMode ? [{ debugName: "actionShortcutEntries" }] : /* istanbul ignore next */ []));
1813
+ this.form = viewChild('form', ...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
1814
+ this.widgetContainer = viewChild(AXPWidgetContainerComponent, ...(ngDevMode ? [{ debugName: "widgetContainer" }] : /* istanbul ignore next */ []));
1815
+ this.sidebarTabs = viewChild('sidebarTabs', ...(ngDevMode ? [{ debugName: "sidebarTabs" }] : /* istanbul ignore next */ []));
1816
+ this.sidebarTabChangeSuppressed = false;
1817
+ /** Syncs the line indicator when the active page changes programmatically. */
1818
+ this.#sidebarTabsIndicatorEffect = effect(() => {
1819
+ const pages = this.vm.adapter()?.pages;
1820
+ const currentId = this.vm.currentPage()?.id;
1821
+ if (!pages?.length || !currentId) {
1822
+ return;
1823
+ }
1824
+ const index = pages.findIndex((page) => page.id === currentId);
1825
+ if (index < 0) {
1826
+ return;
1827
+ }
1828
+ untracked(() => queueMicrotask(() => {
1829
+ this.sidebarTabChangeSuppressed = true;
1830
+ try {
1831
+ this.sidebarTabs()?.select(index);
1832
+ }
1833
+ finally {
1834
+ this.sidebarTabChangeSuppressed = false;
1835
+ }
1836
+ }));
1837
+ }, ...(ngDevMode ? [{ debugName: "#sidebarTabsIndicatorEffect" }] : /* istanbul ignore next */ []));
1838
+ /**
1839
+ * Binds list selection whenever `page-list_table` registers (including after remount).
1840
+ * `find()` / `waitForWidget()` can return a destroyed instance; registration always carries the live widget.
1841
+ */
1842
+ this.#pageListSelectionBindEffect = effect((onCleanup) => {
1843
+ const pageId = this.vm.currentPage()?.id;
1844
+ const isPageReady = this.vm.isPageReady();
1845
+ if (!pageId || this.vm.showPages() || !isPageReady || this.getActivePageComponentKey()) {
1846
+ return;
1847
+ }
1848
+ this.vm.currentPageContent();
1849
+ if (!this.pageHasEntityListWidget()) {
1850
+ return;
1851
+ }
1852
+ const generation = ++this.pageListSelectionBindGeneration;
1853
+ const container = this.widgetContainer();
1854
+ if (!container) {
1855
+ return;
1856
+ }
1857
+ const builder = container.builderService;
1858
+ onCleanup(() => {
1859
+ this.onSelectionChangeSubscription?.unsubscribe();
1860
+ this.onSelectionChangeSubscription = undefined;
1861
+ });
1862
+ const registrationSub = builder.widgetRegistered$.subscribe(({ id, widget }) => {
1863
+ if (id !== PAGE_LIST_TABLE_WIDGET_ID) {
1864
+ return;
1865
+ }
1866
+ this.attachSelectionToListWidget(pageId, generation, widget);
1867
+ });
1868
+ onCleanup(() => registrationSub.unsubscribe());
1869
+ const existing = builder.getWidget(PAGE_LIST_TABLE_WIDGET_ID);
1870
+ if (existing) {
1871
+ untracked(() => this.attachSelectionToListWidget(pageId, generation, existing));
1872
+ }
1873
+ }, ...(ngDevMode ? [{ debugName: "#pageListSelectionBindEffect" }] : /* istanbul ignore next */ []));
1874
+ /** Clears the page overlay reactively once VM + widget hydration are both idle. */
1875
+ this.#overlayReleaseEffect = effect(() => {
1876
+ const showPages = this.vm.showPages();
1877
+ const pageId = this.vm.currentPage()?.id;
1878
+ const isVmBusy = this.vm.isOverlayBusy();
1879
+ const isPageReady = this.vm.isPageReady();
1880
+ const container = this.widgetContainer();
1881
+ this.vm.currentPageContent();
1882
+ this.vm.activeTabContent();
1883
+ this.vm.context();
1884
+ container?.builderService.registeredWidgetsCount();
1885
+ container?.builderService.isHydrating();
1886
+ container?.isBusy();
1887
+ container?.isWidgetsReady();
1888
+ const widgetCount = container?.builderService.registeredWidgetsCount() ?? 0;
1889
+ const widgetsBusy = container?.isBusy() ?? false;
1890
+ const widgetsReady = container?.isWidgetsReady() ?? false;
1891
+ untracked(() => {
1892
+ if (pageId !== this.overlayTargetPageId) {
1893
+ const isInitialOverlayBind = this.overlayTargetPageId == null;
1894
+ this.overlayTargetPageId = pageId ?? null;
1895
+ // First bind: widgets mount without a prior page registry. Sidebar switch: require clear first.
1896
+ this.overlaySawZeroWidgetsAfterPageChange = isInitialOverlayBind;
1897
+ this.overlayReleaseGeneration += 1;
1898
+ this.overlayReleasePromise = undefined;
1899
+ this.clearOverlayFallback();
1900
+ }
1901
+ if (showPages) {
1902
+ this.overlayReleaseGeneration += 1;
1903
+ this.overlayReleasePromise = undefined;
1904
+ this.clearOverlayFallback();
1905
+ if (!isVmBusy) {
1906
+ this.clearPageOverlay('showPages — release overlay');
1907
+ }
1908
+ return;
1909
+ }
1910
+ if (isVmBusy || !isPageReady || !pageId) {
1911
+ this.overlayReleaseGeneration += 1;
1912
+ this.overlayReleasePromise = undefined;
1913
+ this.clearOverlayFallback();
1914
+ return;
1915
+ }
1916
+ if (this.getActivePageComponentKey()) {
1917
+ this.overlayReleaseGeneration += 1;
1918
+ this.overlayReleasePromise = undefined;
1919
+ this.clearOverlayFallback();
1920
+ this.clearPageOverlay('component page — release overlay');
1921
+ return;
1922
+ }
1923
+ if (!this.pageHasWidgetNodes()) {
1924
+ this.overlayReleaseGeneration += 1;
1925
+ this.overlayReleasePromise = undefined;
1926
+ this.clearOverlayFallback();
1927
+ this.clearPageOverlay('no widget nodes — release overlay');
1928
+ return;
1929
+ }
1930
+ if (widgetCount === 0) {
1931
+ this.overlaySawZeroWidgetsAfterPageChange = true;
1932
+ }
1933
+ if (!this.overlaySawZeroWidgetsAfterPageChange) {
1934
+ this.scheduleOverlayFallback();
1935
+ return;
1936
+ }
1937
+ if (!widgetsReady || widgetsBusy) {
1938
+ this.scheduleOverlayFallback();
1939
+ return;
1940
+ }
1941
+ void this.requestOverlayRelease(pageId);
1942
+ });
1943
+ }, ...(ngDevMode ? [{ debugName: "#overlayReleaseEffect" }] : /* istanbul ignore next */ []));
1944
+ this.footerPrimaryActions = signal([], ...(ngDevMode ? [{ debugName: "footerPrimaryActions" }] : /* istanbul ignore next */ []));
1945
+ this.footerSecondaryActions = signal([], ...(ngDevMode ? [{ debugName: "footerSecondaryActions" }] : /* istanbul ignore next */ []));
1946
+ this.footerActions = computed(() => [...this.footerPrimaryActions(), ...this.footerSecondaryActions()], ...(ngDevMode ? [{ debugName: "footerActions" }] : /* istanbul ignore next */ []));
1947
+ this.rejectFooterCommand = computed(() => {
1948
+ if (this.vm.currentPage()?.isReadonly) {
1949
+ return null;
1950
+ }
1951
+ return this.vm.currentPage()?.settings?.commands?.reject ?? null;
1952
+ }, ...(ngDevMode ? [{ debugName: "rejectFooterCommand" }] : /* istanbul ignore next */ []));
1953
+ this.acceptFooterCommand = computed(() => {
1954
+ if (this.vm.currentPage()?.isReadonly) {
1955
+ return null;
1956
+ }
1957
+ return this.vm.currentPage()?.settings?.commands?.accept ?? null;
1958
+ }, ...(ngDevMode ? [{ debugName: "acceptFooterCommand" }] : /* istanbul ignore next */ []));
1959
+ this.hasSaveDiscardFooterCommands = computed(() => !!(this.rejectFooterCommand() || this.acceptFooterCommand()), ...(ngDevMode ? [{ debugName: "hasSaveDiscardFooterCommands" }] : /* istanbul ignore next */ []));
1960
+ /** Entity save/discard — driven live by widget/container dirty state, not evaluated action metadata. */
1961
+ this.showSaveDiscardFooter = computed(() => this.hasSaveDiscardFooterCommands() && this.liveFormIsDirty(), ...(ngDevMode ? [{ debugName: "showSaveDiscardFooter" }] : /* istanbul ignore next */ []));
1962
+ /** Live dirty flag for widget-based entity forms (not component-hosted pages). */
1963
+ this.liveFormIsDirty = computed(() => {
1964
+ if (this.vm.currentPage()?.isReadonly || this.getActivePageComponentKey()) {
1965
+ return false;
1966
+ }
1967
+ if (this.vm.isOverlayBusy()) {
1968
+ return false;
1969
+ }
1970
+ const container = this.widgetContainer();
1971
+ if (!container?.isSavedCommitted()) {
1972
+ return false;
1973
+ }
1974
+ return container.isFormDirty();
1975
+ }, ...(ngDevMode ? [{ debugName: "liveFormIsDirty" }] : /* istanbul ignore next */ []));
1976
+ this.hasVisibleFooterPrimaryActions = computed(() => {
1977
+ if (this.showSaveDiscardFooter()) {
1978
+ return true;
1979
+ }
1980
+ return this.footerPrimaryActions().some((action) => !this.isSaveDiscardFooterAction(action) && this.isFooterActionVisible(action));
1981
+ }, ...(ngDevMode ? [{ debugName: "hasVisibleFooterPrimaryActions" }] : /* istanbul ignore next */ []));
1982
+ this.hasVisibleFooterSecondaryActions = computed(() => this.footerSecondaryActions().some((action) => this.isFooterActionVisible(action)), ...(ngDevMode ? [{ debugName: "hasVisibleFooterSecondaryActions" }] : /* istanbul ignore next */ []));
1983
+ this.hasFooter = computed(() => {
1984
+ return ((this.hasVisibleFooterPrimaryActions() || this.hasVisibleFooterSecondaryActions()) &&
1985
+ !this.vm.currentPage()?.isReadonly);
1986
+ }, ...(ngDevMode ? [{ debugName: "hasFooter" }] : /* istanbul ignore next */ []));
1987
+ this.#formDirtyEffect = effect(() => {
1988
+ if (this.vm.currentPage()?.isReadonly || this.getActivePageComponentKey()) {
1989
+ return;
1990
+ }
1991
+ if (this.vm.isOverlayBusy()) {
1992
+ return;
1993
+ }
1994
+ const container = this.widgetContainer();
1995
+ if (!container?.isSavedCommitted()) {
1996
+ if (this.vm.formDirty()) {
1997
+ untracked(() => this.vm.setFormDirty(false));
1998
+ }
1999
+ return;
2000
+ }
2001
+ const isDirty = container.isFormDirty();
2002
+ if (this.vm.formDirty() === isDirty) {
2003
+ return;
2004
+ }
2005
+ untracked(() => {
2006
+ this.vm.setFormDirty(isDirty);
2007
+ this.recompute();
2008
+ });
2009
+ }, ...(ngDevMode ? [{ debugName: "#formDirtyEffect" }] : /* istanbul ignore next */ []));
2010
+ /** Refresh title badge (Not Saved) when dirty state changes. */
2011
+ this.#headerBadgeEffect = effect(() => {
2012
+ this.liveFormIsDirty();
2013
+ this.vm.currentPage()?.id;
2014
+ this.isActivePageComponentDirty();
2015
+ untracked(() => this.recompute());
2016
+ }, ...(ngDevMode ? [{ debugName: "#headerBadgeEffect" }] : /* istanbul ignore next */ []));
2017
+ this.#FooterActionsEffect = effect(() => {
2018
+ const generation = ++this.footerActionsGeneration;
2019
+ this.updateTrigger();
2020
+ this.liveFormIsDirty();
2021
+ this.vm.isOverlayBusy();
2022
+ void this.refreshFooterActions(generation);
2023
+ }, ...(ngDevMode ? [{ debugName: "#FooterActionsEffect" }] : /* istanbul ignore next */ []));
2024
+ this.#keyboardShortcutsEffect = effect(() => {
2025
+ const generation = ++this.keyboardShortcutRegistrationGeneration;
2026
+ this.updateTrigger();
2027
+ this.liveFormIsDirty();
2028
+ this.isActivePageComponentDirty();
2029
+ this.vm.isOverlayBusy();
2030
+ this.vm.currentPage()?.id;
2031
+ untracked(() => void this.refreshKeyboardShortcutRegistrations(generation));
2032
+ }, ...(ngDevMode ? [{ debugName: "#keyboardShortcutsEffect" }] : /* istanbul ignore next */ []));
2033
+ }
2034
+ /** Safety cap when widget ready detection does not settle. */
2035
+ static { this.overlayMaxWaitMs = 30_000; }
2036
+ /** Save/discard use live {@link AXPLayoutDetailsViewViewModel.isDirty}; other actions use evaluated `visible`. */
2037
+ static isTruthyActionFlag(value) {
2038
+ if (value === true) {
2039
+ return true;
2040
+ }
2041
+ if (value === false || value == null) {
2042
+ return false;
2043
+ }
2044
+ if (typeof value === 'string') {
2045
+ const normalized = value.trim().toLowerCase();
2046
+ if (normalized === 'false' || normalized === '0' || normalized === '') {
2047
+ return false;
2048
+ }
2049
+ if (normalized === 'true') {
2050
+ return true;
2051
+ }
2052
+ // Unevaluated template expressions must not keep footer actions visible or disabled.
2053
+ if (normalized.startsWith('{{')) {
2054
+ return false;
2055
+ }
2056
+ return true;
2057
+ }
2058
+ return Boolean(value);
2059
+ }
2060
+ /** Syncs the line indicator when the active page changes programmatically. */
2061
+ #sidebarTabsIndicatorEffect;
2062
+ /**
2063
+ * Binds list selection whenever `page-list_table` registers (including after remount).
2064
+ * `find()` / `waitForWidget()` can return a destroyed instance; registration always carries the live widget.
2065
+ */
2066
+ #pageListSelectionBindEffect;
2067
+ /** Clears the page overlay reactively once VM + widget hydration are both idle. */
2068
+ #overlayReleaseEffect;
2069
+ isSaveDiscardFooterAction(action) {
2070
+ const commandName = action.command?.name;
2071
+ const acceptCommand = this.vm.currentPage()?.settings?.commands?.accept?.command?.name;
2072
+ const rejectCommand = this.vm.currentPage()?.settings?.commands?.reject?.command?.name;
2073
+ return (action.name === 'accept' ||
2074
+ action.name === 'reject' ||
2075
+ commandName === 'update-entity' ||
2076
+ commandName === 'discard' ||
2077
+ (!!acceptCommand && commandName === acceptCommand) ||
2078
+ (!!rejectCommand && commandName === rejectCommand));
2079
+ }
2080
+ isFooterActionVisible(action) {
2081
+ if (this.isSaveDiscardFooterAction(action)) {
2082
+ return false;
2083
+ }
2084
+ return AXPLayoutDetailsViewComponent.isTruthyActionFlag(action.visible);
2085
+ }
2086
+ isFooterActionDisabled(action) {
2087
+ if (this.vm.isOverlayBusy()) {
2088
+ return true;
2089
+ }
2090
+ return AXPLayoutDetailsViewComponent.isTruthyActionFlag(action.disabled);
2091
+ }
2092
+ /**
2093
+ * Re-fetches the entity adapter and reloads the active page.
2094
+ */
2095
+ async refreshLayout() {
2096
+ this.unsavedChangesConfirm.markLeaveConfirmed();
2097
+ this.detailsLayoutReady.set(false);
2098
+ try {
2099
+ await this.vm.loadFromRoute(true);
2100
+ }
2101
+ finally {
2102
+ this.detailsLayoutReady.set(true);
2103
+ }
2104
+ }
2105
+ async ngOnInit() {
2106
+ await super.ngOnInit();
2107
+ this.unsavedChangesPopstate.registerSource({
2108
+ id: 'axp-layout-details-view',
2109
+ isDirty: () => this.hasUnsavedChanges(),
2110
+ });
2111
+ this.destroyRef.onDestroy(() => this.unsavedChangesPopstate.unregisterSource('axp-layout-details-view'));
2112
+ //
2113
+ await this.vm.loadFromRoute();
2114
+ this.detailsLayoutReady.set(true);
2115
+ void this.refreshKeyboardShortcutRegistrations(++this.keyboardShortcutRegistrationGeneration);
2116
+ this.eventService
2117
+ //TODO: Use the constant from the entity module
2118
+ .listen('entity:refresh-layout')
2119
+ .pipe(takeUntil(this.destroyed$))
2120
+ .subscribe((e) => {
2121
+ if (e.data.name == this.vm.adapter()?.name) {
2122
+ this.refreshLayout();
2123
+ }
2124
+ });
2125
+ this.workflowService.events$
2126
+ .pipe(filter$1((event) => event.type === AXP_ENTITY_DELETED_WORKFLOW_EVENT_TYPE), takeUntil(this.destroyed$))
2127
+ .subscribe((event) => {
2128
+ void this.handleEntityDeleted(event.payload);
2129
+ });
2130
+ }
2131
+ /** Navigates to {@link AXPLayoutDetailsViewAdapter.exitUrl} when the open record was deleted. */
2132
+ async handleEntityDeleted(payload) {
2133
+ if (!payload?.entity || !payload.ids?.length) {
2134
+ return;
2135
+ }
2136
+ const adapterName = this.vm.adapter()?.name;
2137
+ const recordId = this.vm.context()?.['id'];
2138
+ if (!adapterName || recordId == null) {
2139
+ return;
2140
+ }
2141
+ const deletedIds = payload.ids.map((id) => String(id));
2142
+ if (payload.entity !== adapterName || !deletedIds.includes(String(recordId))) {
2143
+ return;
2144
+ }
2145
+ this.unsavedChangesConfirm.markLeaveConfirmed();
2146
+ if (this.tabNavigation.effectiveMode() === 'tabular') {
2147
+ const tabKey = this.getPageTabKey();
2148
+ await this.vm.goToListPage();
2149
+ if (tabKey) {
2150
+ this.unsavedChangesConfirm.markLeaveConfirmed();
2151
+ await this.tabNavigation.closeTab(tabKey);
2152
+ }
2153
+ return;
2154
+ }
2155
+ await this.vm.goToListPage();
2156
+ }
2157
+ ngOnDestroy() {
2158
+ this.onSelectionChangeSubscription?.unsubscribe();
2159
+ ++this.keyboardShortcutRegistrationGeneration;
2160
+ this.clearOverlayFallback();
2161
+ this.overlayReleaseGeneration += 1;
2162
+ this.overlayReleasePromise = undefined;
2163
+ this.clearPageOverlay('ngOnDestroy');
2164
+ this.destroyed$.next();
2165
+ this.destroyed$.complete();
2166
+ }
2167
+ #formDirtyEffect;
2168
+ /** Refresh title badge (Not Saved) when dirty state changes. */
2169
+ #headerBadgeEffect;
2170
+ #FooterActionsEffect;
2171
+ #keyboardShortcutsEffect;
2172
+ async refreshFooterActions(generation) {
2173
+ const [footerPrimaryActions, footerSecondaryActions] = await Promise.all([
2174
+ this.getFooterPrimaryActions(),
2175
+ this.getFooterSecondaryActions(),
2176
+ ]);
2177
+ if (generation !== this.footerActionsGeneration) {
2178
+ return;
2179
+ }
2180
+ this.footerPrimaryActions.set(footerPrimaryActions);
2181
+ this.footerSecondaryActions.set(footerSecondaryActions);
2182
+ }
2183
+ /** Whether the active page hosts a related-entity list (`page-list` widget). */
2184
+ pageHasEntityListWidget() {
2185
+ return this.vm.currentPageContent().some((item) => {
2186
+ if ('type' in item && item.type === 'component') {
2187
+ return false;
2188
+ }
2189
+ return item.name === 'page-list';
2190
+ });
2191
+ }
2192
+ /** Subscribes to the live data-list selection stream for Selected-scope header actions. */
2193
+ attachSelectionToListWidget(pageId, generation, widget) {
2194
+ if (generation !== this.pageListSelectionBindGeneration || this.vm.currentPage()?.id !== pageId) {
2195
+ return;
2196
+ }
2197
+ const selectionStream = widget.api()['onSelectionChange'];
2198
+ if (!selectionStream || typeof selectionStream.subscribe !== 'function') {
2199
+ return;
2200
+ }
2201
+ this.onSelectionChangeSubscription?.unsubscribe();
2202
+ this.onSelectionChangeSubscription = selectionStream.subscribe((rows) => {
2203
+ this.vm.updatePageSelectedRows(rows);
2204
+ this.recompute();
2205
+ });
2206
+ }
2207
+ handleOnContextChanged(e) {
2208
+ if (!this.vm.currentPage()?.isReadonly &&
2209
+ !this.getActivePageComponentKey() &&
2210
+ this.widgetContainer()?.isSavedCommitted()) {
2211
+ const isDirty = e.isFormDirty ?? false;
2212
+ if (this.vm.formDirty() !== isDirty) {
2213
+ this.vm.setFormDirty(isDirty);
2214
+ }
2215
+ }
2216
+ this.recompute();
2217
+ }
2218
+ getLiveFormContext() {
2219
+ const container = this.widgetContainer();
2220
+ return (container?.contextService.data() ?? this.vm.context());
2221
+ }
2222
+ /** Whether the active page renders widget nodes (not only hosted components). */
2223
+ pageHasWidgetNodes() {
2224
+ const pageWidgets = this.vm.currentPageContent();
2225
+ if (pageWidgets.some((item) => !('type' in item && item.type === 'component'))) {
2226
+ return true;
2227
+ }
2228
+ return this.vm.activeTabContent().length > 0;
2229
+ }
2230
+ /** Clears all overlay loading tasks once the page (or hosted view) is ready. */
2231
+ clearPageOverlay(reason) {
2232
+ this.layoutService.clearOverlayLoading();
2233
+ }
2234
+ scheduleOverlayFallback() {
2235
+ if (this.overlayFallbackTimer) {
2236
+ return;
2237
+ }
2238
+ this.overlayFallbackTimer = setTimeout(() => {
2239
+ this.overlayFallbackTimer = undefined;
2240
+ const isOverlayLoading = this.layoutService.isOverlayLoading();
2241
+ const isVmBusy = this.vm.isOverlayBusy();
2242
+ if (!isOverlayLoading) {
2243
+ return;
2244
+ }
2245
+ if (!isVmBusy) {
2246
+ this.debug.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'details-view', 'fallback forcing overlay off', () => ({
2247
+ taskCount: this.layoutService.overlayTaskCount(),
2248
+ note: 'Overlay fallback forcing off',
2249
+ }));
2250
+ this.clearPageOverlay('fallback forcing overlay off');
2251
+ }
2252
+ }, AXPLayoutDetailsViewComponent.overlayMaxWaitMs);
2253
+ }
2254
+ clearOverlayFallback() {
2255
+ if (this.overlayFallbackTimer) {
2256
+ clearTimeout(this.overlayFallbackTimer);
2257
+ this.overlayFallbackTimer = undefined;
2258
+ }
2259
+ }
2260
+ /**
2261
+ * Hides overlay after the widget container reports continuous idle (same gate as saved baseline).
2262
+ */
2263
+ requestOverlayRelease(pageId) {
2264
+ if (this.overlayReleasePromise) {
2265
+ return;
2266
+ }
2267
+ const generation = this.overlayReleaseGeneration;
2268
+ const container = this.widgetContainer();
2269
+ if (!container) {
2270
+ this.scheduleOverlayFallback();
2271
+ return;
2272
+ }
2273
+ this.overlayReleasePromise = container
2274
+ .waitUntilSettled()
2275
+ .then(() => {
2276
+ const latest = this.widgetContainer();
2277
+ if (generation !== this.overlayReleaseGeneration) {
2278
+ return;
2279
+ }
2280
+ if (pageId !== this.overlayTargetPageId || pageId !== this.vm.currentPage()?.id) {
2281
+ return;
2282
+ }
2283
+ if (this.vm.isOverlayBusy()) {
2284
+ return;
2285
+ }
2286
+ if (!latest?.isWidgetsReady() || latest.isBusy()) {
2287
+ return;
2288
+ }
2289
+ this.debug.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'details-view', 'release overlay OFF', () => ({
2290
+ pageId,
2291
+ generation,
2292
+ taskCount: this.layoutService.overlayTaskCount(),
2293
+ note: 'Final overlay release OFF',
2294
+ }));
2295
+ this.clearOverlayFallback();
2296
+ this.clearPageOverlay('widgets settled — release overlay');
2297
+ })
2298
+ .finally(() => {
2299
+ this.overlayReleasePromise = undefined;
2300
+ });
2301
+ }
2302
+ async finalizeSave() {
2303
+ const container = this.widgetContainer();
2304
+ if (!container) {
2305
+ this.vm.setFormDirty(false);
2306
+ return;
2307
+ }
2308
+ this.layoutService.setOverlayLoading(true);
2309
+ try {
2310
+ const savedContext = this.vm.context();
2311
+ await container.replaceContext(savedContext);
2312
+ this.vm.applySavedState(container.contextService.data());
2313
+ if (!this.getActivePageComponentKey()) {
2314
+ await container.settleSavedBaseline();
2315
+ }
2316
+ }
2317
+ finally {
2318
+ this.layoutService.setOverlayLoading(false);
2319
+ }
2320
+ }
2321
+ finalizeDiscard() {
2322
+ const container = this.widgetContainer();
2323
+ if (container) {
2324
+ container.revertAndSettle();
2325
+ const saved = container.contextService.saved();
2326
+ this.vm.applySavedState(saved);
2327
+ return;
2328
+ }
2329
+ void this.vm.discard();
2330
+ }
2331
+ /** Reverts widget form state and view-model context to the last saved baseline. */
2332
+ async discardUnsavedChanges() {
2333
+ this.finalizeDiscard();
2334
+ this.recompute();
2335
+ }
2336
+ /**
2337
+ * `ax-tabs` selects on click before we can confirm. Revert synchronously, then confirm + switch.
2338
+ */
2339
+ handleSidebarTabChanged(event) {
2340
+ if (!this.detailsLayoutReady() || this.sidebarTabChangeSuppressed) {
2341
+ return;
2342
+ }
2343
+ const pages = this.vm.adapter()?.pages ?? [];
2344
+ const target = pages[event.index];
2345
+ const currentId = this.vm.currentPage()?.id;
2346
+ if (!currentId || !target || target.id === currentId) {
2347
+ return;
2348
+ }
2349
+ const activeIndex = pages.findIndex((page) => page.id === currentId);
2350
+ if (activeIndex >= 0) {
2351
+ this.sidebarTabChangeSuppressed = true;
2352
+ try {
2353
+ this.sidebarTabs()?.select(activeIndex);
2354
+ }
2355
+ finally {
2356
+ this.sidebarTabChangeSuppressed = false;
2357
+ }
2358
+ }
2359
+ void this.completePageSwitch(target);
2360
+ }
2361
+ async handleSelectPage(page) {
2362
+ if (page.id === this.vm.currentPage()?.id) {
2363
+ return;
2364
+ }
2365
+ if (!(await this.confirmLeaveIfDirty())) {
2366
+ return;
2367
+ }
2368
+ this.vm.setCurrentPage(page);
2369
+ }
2370
+ async completePageSwitch(page) {
2371
+ if (!(await this.confirmLeaveIfDirty())) {
2372
+ return;
2373
+ }
2374
+ this.vm.setCurrentPage(page);
2375
+ }
2376
+ async getAllPrimaryActions() {
2377
+ if (!this.vm.showPages()) {
2378
+ const [primaryActions, pagePrimaryActions] = await Promise.all([
2379
+ this.vm.primaryActions(),
2380
+ this.vm.currentPagePrimaryActions(),
2381
+ ]);
2382
+ return [...primaryActions, ...pagePrimaryActions];
2383
+ }
2384
+ else {
2385
+ return [];
2386
+ }
2387
+ }
2388
+ async getPrimaryMenuItems() {
2389
+ return (await this.getAllPrimaryActions()).filter((a) => {
2390
+ return (a.priority == 'primary' &&
2391
+ ((a.scope == AXPEntityCommandScope.Selected && this.vm.currentPageSelectedRows().length) ||
2392
+ ((a.scope == AXPEntityCommandScope.TypeLevel || !a.scope) &&
2393
+ a.zone != 'footer' &&
2394
+ !this.vm.currentPageSelectedRows().length)));
2395
+ });
2396
+ }
2397
+ async getFooterPrimaryActions() {
2398
+ return (await this.getAllPrimaryActions()).filter((item) => item.zone == 'footer');
2399
+ }
2400
+ async getAllSecondaryActions() {
2401
+ if (!this.vm.showPages()) {
2402
+ const [secondaryActions, pageSecondaryActions] = await Promise.all([
2403
+ this.vm.secondaryActions(),
2404
+ this.vm.currentPageSecondaryActions(),
2405
+ ]);
2406
+ return [...secondaryActions, ...pageSecondaryActions];
2407
+ }
2408
+ else {
2409
+ return [];
2410
+ }
2411
+ }
2412
+ async getSecondaryMenuItems() {
2413
+ return (await this.getAllSecondaryActions()).filter((item) => item.zone == 'header');
2414
+ }
2415
+ async getFooterSecondaryActions() {
2416
+ return (await this.getAllSecondaryActions()).filter((item) => item.zone == 'footer');
2417
+ }
2418
+ async getPageBreadcrumbs() {
2419
+ return (await this.vm.breadcrumbs()) ?? [];
2420
+ }
2421
+ async getPageTitle() {
2422
+ return this.vm.getPageTitle();
2423
+ }
2424
+ async getPageDescription() {
2425
+ return this.vm.getPageDescription();
2426
+ }
2427
+ async getBackButton() {
2428
+ return this.vm.getBackButton();
2429
+ }
2430
+ hasUnsavedChanges() {
2431
+ return this.shouldPromptLeave();
2432
+ }
2433
+ async getPageBadge() {
2434
+ if (this.vm.currentPage()?.isReadonly) {
2435
+ return null;
2436
+ }
2437
+ const componentInstance = this.getActivePageComponentInstance();
2438
+ if (componentInstance) {
2439
+ return componentInstance.resolvePageBadge();
2440
+ }
2441
+ if (!this.liveFormIsDirty()) {
2442
+ return null;
2443
+ }
2444
+ return buildNotSavedPageBadge(this.translateService, this.settingsService);
2445
+ }
2446
+ async getPageStatus() {
2447
+ return this.vm.getPageStatus();
2448
+ }
2449
+ /**
2450
+ * Get entity data for status chip component
2451
+ */
2452
+ getEntityData() {
2453
+ return this.vm.context() ?? null;
2454
+ }
2455
+ async onBackButtonClick() {
2456
+ if (!(await this.confirmLeaveIfDirty())) {
2457
+ return;
2458
+ }
2459
+ // When back button is clicked in small layout, go back to page list
2460
+ if (!this.vm.showPages() && this.vm.adapter()?.pages?.length > 1) {
2461
+ this.vm.setCurrentPage(null);
2462
+ }
2463
+ else {
2464
+ this.vm.goToListPage();
2465
+ }
2466
+ }
2467
+ //#region ---- Leave Page Gate ----
2468
+ /**
2469
+ * Router guard hook — same unsaved-changes confirm as dialog builder {@link confirmCloseWhenDirty}.
2470
+ */
2471
+ async canDeactivate() {
2472
+ if (this.unsavedChangesConfirm.hasLeaveConfirmed()) {
2473
+ return true;
2474
+ }
2475
+ return this.confirmLeaveIfDirty();
2476
+ }
2477
+ /** Background tab close — confirm without discarding (the cached instance is destroyed). */
2478
+ onBeforeTabClose() {
2479
+ if (this.unsavedChangesConfirm.hasLeaveConfirmed()) {
2480
+ return Promise.resolve(true);
2481
+ }
2482
+ return this.unsavedChangesConfirm.confirmIfDirty(this.shouldPromptLeave(), {
2483
+ approveLeaveOnConfirm: false,
2484
+ });
2485
+ }
2486
+ /** Active entity component page instance, when the current details sub-page is component-based. */
2487
+ getActivePageComponentInstance() {
2488
+ const componentKey = this.getActivePageComponentKey();
2489
+ if (!componentKey) {
2490
+ return null;
2491
+ }
2492
+ return this.pageComponentInstanceRegistry.get(componentKey);
2493
+ }
2494
+ /** Component key for the current details sub-page, when content is component-based. */
2495
+ getActivePageComponentKey() {
2496
+ const content = this.vm.currentPage()?.content;
2497
+ if (!content?.length) {
2498
+ return null;
2499
+ }
2500
+ for (const item of content) {
2501
+ if (AXPLayoutDetailsViewComponent.isComponentPageContent(item)) {
2502
+ return item.componentKey;
2503
+ }
2504
+ }
2505
+ return null;
2506
+ }
2507
+ static isComponentPageContent(item) {
2508
+ return 'type' in item && item.type === 'component';
2509
+ }
2510
+ pageContentTrackKey(item, index) {
2511
+ const pageId = this.vm.currentPage()?.id ?? 'unknown';
2512
+ if (AXPLayoutDetailsViewComponent.isComponentPageContent(item)) {
2513
+ return `${pageId}:component:${item.componentKey}`;
2514
+ }
2515
+ return `${pageId}:${item.name ?? `widget:${index}`}`;
2516
+ }
2517
+ /** Whether the active component-based entity page has unsaved edits. */
2518
+ isActivePageComponentDirty() {
2519
+ return this.getActivePageComponentInstance()?.hasUnsavedChanges() ?? false;
2520
+ }
2521
+ /**
2522
+ * Widget-form dirty state for leave guards (popstate, canDeactivate).
2523
+ * Unlike {@link liveFormIsDirty}, does not suppress dirty while overlay is busy.
2524
+ */
2525
+ hasWidgetFormUnsavedChanges() {
2526
+ if (this.getActivePageComponentKey()) {
2527
+ return false;
2528
+ }
2529
+ const container = this.widgetContainer();
2530
+ if (!container) {
2531
+ return this.vm.formDirty();
2532
+ }
2533
+ if (container.isSavedCommitted()) {
2534
+ return container.isFormDirty();
2535
+ }
2536
+ return container.contextService.hasUserEditedSinceCommit() || this.vm.formDirty();
2537
+ }
2538
+ /** Whether the current details page has user-origin unsaved edits (form or component page). */
2539
+ shouldPromptLeave() {
2540
+ if (this.vm.currentPage()?.isReadonly) {
2541
+ return false;
2542
+ }
2543
+ if (this.isActivePageComponentDirty()) {
2544
+ return true;
2545
+ }
2546
+ return this.hasWidgetFormUnsavedChanges();
2547
+ }
2548
+ /** Single gate for leaving the page or switching details sub-pages when dirty. */
2549
+ async confirmLeaveIfDirty() {
2550
+ const shouldPrompt = this.shouldPromptLeave();
2551
+ const confirmed = await this.unsavedChangesConfirm.confirmIfDirty(shouldPrompt);
2552
+ if (confirmed && shouldPrompt) {
2553
+ await this.discardUnsavedChanges();
2554
+ }
2555
+ return confirmed;
2556
+ }
2557
+ //#endregion
2558
+ //#region ---- Command Execution ----
2559
+ async execute(command) {
2560
+ if (!this.vm.adapter()) {
2561
+ return;
2562
+ }
2563
+ const acceptCommand = this.vm.currentPage()?.settings?.commands?.accept?.command.name;
2564
+ const rejectCommand = this.vm.currentPage()?.settings?.commands?.reject?.command.name;
2565
+ if (command.name == acceptCommand) {
2566
+ const saved = await this.vm.save(command, this.form(), this.getLiveFormContext());
2567
+ if (saved) {
2568
+ await this.finalizeSave();
2569
+ }
2570
+ this.recompute();
2571
+ return;
2572
+ }
2573
+ if (command.name == rejectCommand) {
2574
+ await this.discardUnsavedChanges();
2575
+ return;
2576
+ }
2577
+ // Handle status update command
2578
+ if (command.name === 'update-status') {
2579
+ const transition = command.options?.['transition'];
2580
+ if (transition?.toStatus?.name) {
2581
+ const status = await this.vm.getPageStatus();
2582
+ if (status?.dataPath) {
2583
+ // Update entity context with new status value
2584
+ const entity = this.vm.context();
2585
+ if (entity) {
2586
+ entity[status.dataPath] = transition.toStatus.name;
2587
+ this.vm.updateContext(entity);
2588
+ this.recompute();
2589
+ }
2590
+ }
2591
+ }
2592
+ return;
2593
+ }
2594
+ // Get all actions
2595
+ const [adapterPrimaryActions, adapterSecondaryActions, pagePrimaryActions, pageSecondaryActions] = await Promise.all([
2596
+ this.vm.primaryActions(),
2597
+ this.vm.secondaryActions(),
2598
+ this.vm.currentPagePrimaryActions(),
2599
+ this.vm.currentPageSecondaryActions(),
2600
+ ]);
2601
+ // Combine adapter actions (primary + secondary)
2602
+ const adapterActions = [...adapterPrimaryActions, ...adapterSecondaryActions];
2603
+ // Combine current page actions (primary + secondary)
2604
+ const pageActions = [...pagePrimaryActions, ...pageSecondaryActions];
2605
+ const commandExistsInActions = (actions, commandName) => {
2606
+ const check = (list) => {
2607
+ if (!Array.isArray(list) || list.length === 0)
2608
+ return false;
2609
+ return list.some((action) => action?.command?.name === commandName ||
2610
+ (Array.isArray(action?.items) && check(action.items)));
2611
+ };
2612
+ return check(actions);
2613
+ };
2614
+ // Check if command exists in adapter actions first
2615
+ if (commandExistsInActions(adapterActions, command.name)) {
2616
+ await this.vm.adapter()?.execute?.(command, this.vm.context());
2617
+ }
2618
+ // Check if command exists in current page actions
2619
+ else if (commandExistsInActions(pageActions, command.name)) {
2620
+ await this.vm
2621
+ .currentPage()
2622
+ ?.execute?.(command, this.vm.pageSelectedRows().length ? this.vm.pageSelectedRows() : this.vm.context());
2623
+ await this.syncDirtyStateAfterPageComponentCommand(command);
2624
+ }
2625
+ }
2626
+ /**
2627
+ * After a component-based page command (e.g. custom discard/save), align view-model dirty
2628
+ * state and widget context with the hosted page when it no longer reports unsaved changes.
2629
+ */
2630
+ async syncDirtyStateAfterPageComponentCommand(command) {
2631
+ const rejectCommand = this.vm.currentPage()?.settings?.commands?.reject?.command?.name;
2632
+ await this.waitForComponentDirtySettle();
2633
+ if (!this.getActivePageComponentKey()) {
2634
+ return;
2635
+ }
2636
+ if (isDiscardCommand(command.name, rejectCommand)) {
2637
+ await this.discardUnsavedChanges();
2638
+ return;
2639
+ }
2640
+ if (!this.isActivePageComponentDirty()) {
2641
+ const container = this.widgetContainer();
2642
+ if (container) {
2643
+ await container.settleSavedBaseline();
2644
+ this.vm.applySavedState(container.contextService.data());
2645
+ }
2646
+ else {
2647
+ this.vm.setFormDirty(false);
2648
+ }
2649
+ }
2650
+ this.recompute();
2651
+ }
2652
+ /** Waits for builder/effect microtasks to finish after a component page command. */
2653
+ waitForComponentDirtySettle() {
2654
+ return new Promise((resolve) => {
2655
+ queueMicrotask(() => {
2656
+ requestAnimationFrame(() => resolve());
2657
+ });
2658
+ });
2659
+ }
2660
+ //#endregion
2661
+ //#region ---- Keyboard Shortcuts ----
2662
+ async refreshKeyboardShortcutRegistrations(generation) {
2663
+ const entries = await this.collectActionShortcutEntries();
2664
+ if (generation !== this.keyboardShortcutRegistrationGeneration || this.destroyed$.closed) {
2665
+ return;
2666
+ }
2667
+ this.actionShortcutEntries.set(entries);
2668
+ this.registerCoreKeyboardShortcuts();
2669
+ this.registerActionKeyboardShortcuts(entries);
2670
+ }
2671
+ /** i18n scope label for the active details sub-page shortcut group in the help dialog. */
2672
+ getKeyboardShortcutScope() {
2673
+ const page = this.vm.currentPage();
2674
+ if (page?.isPrimary) {
2675
+ return '@general:keyboard-shortcuts.groups.entity-details';
2676
+ }
2677
+ const label = page?.label;
2678
+ if (typeof label === 'string' && label.length > 0) {
2679
+ return label;
2680
+ }
2681
+ return page?.id ?? '@general:keyboard-shortcuts.groups.entity-details';
2682
+ }
2683
+ /** Primary overview page backed by the entity widget form (not a component-hosted sub-page). */
2684
+ isWidgetFormPrimaryPage() {
2685
+ return !!this.vm.currentPage()?.isPrimary && !this.getActivePageComponentKey();
2686
+ }
2687
+ registerDetailsViewShortcutScope(id, shortcuts) {
2688
+ const scope = this.getKeyboardShortcutScope();
2689
+ this.shortcutRegistry.register({
2690
+ owner: this.destroyRef,
2691
+ elementRef: this.elementRef,
2692
+ id,
2693
+ priority: AXPKeyboardShortcutPriority.Page,
2694
+ scope,
2695
+ shortcuts,
2696
+ });
2697
+ }
2698
+ async collectActionShortcutEntries() {
2699
+ const map = new Map();
2700
+ if (this.vm.showPages()) {
2701
+ return map;
2702
+ }
2703
+ const includeAdapterActions = this.isWidgetFormPrimaryPage();
2704
+ const [adapterPrimaryActions, adapterSecondaryActions, pagePrimaryActions, pageSecondaryActions] = await Promise.all([
2705
+ includeAdapterActions ? this.vm.primaryActions() : Promise.resolve([]),
2706
+ includeAdapterActions ? this.vm.secondaryActions() : Promise.resolve([]),
2707
+ this.vm.currentPagePrimaryActions(),
2708
+ this.vm.currentPageSecondaryActions(),
2709
+ ]);
2710
+ for (const action of [
2711
+ ...adapterPrimaryActions,
2712
+ ...adapterSecondaryActions,
2713
+ ...pagePrimaryActions,
2714
+ ...pageSecondaryActions,
2715
+ ]) {
2716
+ const zone = action.zone ?? 'header';
2717
+ if (zone !== 'header' && zone !== 'footer') {
2718
+ continue;
2719
+ }
2720
+ if (zone === 'footer' && this.isWidgetFormPrimaryPage() && this.isSaveDiscardFooterAction(action)) {
2721
+ continue;
2722
+ }
2723
+ if (zone === 'header' && this.isSaveDiscardFooterAction(action)) {
2724
+ continue;
2725
+ }
2726
+ const commandName = action.command?.name;
2727
+ const command = action.command;
2728
+ if (!commandName || !command) {
2729
+ continue;
2730
+ }
2731
+ const commandBaseName = getDetailsViewActionCommandBaseName(commandName);
2732
+ const chords = zone === 'footer'
2733
+ ? resolvePageFooterActionShortcuts(commandBaseName, action.shortcuts)
2734
+ : resolveDetailsViewActionShortcuts(commandBaseName, action.shortcuts);
2735
+ if (!chords?.length) {
2736
+ continue;
2737
+ }
2738
+ map.set(commandName, {
2739
+ command,
2740
+ title: typeof action.title === 'string' ? action.title : '',
2741
+ chords,
2742
+ visible: this.isShortcutActionAvailable(action),
2743
+ disabled: AXPLayoutDetailsViewComponent.isTruthyActionFlag(action.disabled),
2744
+ });
2745
+ }
2746
+ return map;
2747
+ }
2748
+ isShortcutActionAvailable(action) {
2749
+ if (AXPLayoutDetailsViewComponent.isTruthyActionFlag(action.hidden)) {
2750
+ return false;
2751
+ }
2752
+ if (action.visible !== undefined && !AXPLayoutDetailsViewComponent.isTruthyActionFlag(action.visible)) {
2753
+ return false;
2754
+ }
2755
+ return true;
2756
+ }
2757
+ /** Whether save/discard core shortcuts are listed in the help dialog (widget overview page only). */
2758
+ isCoreSaveShortcutAvailable() {
2759
+ return this.isWidgetFormPrimaryPage() && !this.vm.currentPage()?.isReadonly && !!this.acceptFooterCommand();
2760
+ }
2761
+ isCoreDiscardShortcutAvailable() {
2762
+ return this.isWidgetFormPrimaryPage() && !this.vm.currentPage()?.isReadonly && !!this.rejectFooterCommand();
2763
+ }
2764
+ canExecuteSaveShortcut() {
2765
+ if (this.vm.currentPage()?.isReadonly || this.vm.isOverlayBusy()) {
2766
+ return false;
2767
+ }
2768
+ if (!this.acceptFooterCommand()) {
2769
+ return false;
2770
+ }
2771
+ return this.shouldPromptLeave();
2772
+ }
2773
+ canExecuteDiscardShortcut() {
2774
+ if (this.vm.currentPage()?.isReadonly || this.vm.isOverlayBusy()) {
2775
+ return false;
2776
+ }
2777
+ if (this.unsavedChangesConfirm.isConfirmPending()) {
2778
+ return false;
2779
+ }
2780
+ if (!this.rejectFooterCommand()) {
2781
+ return false;
2782
+ }
2783
+ return this.shouldPromptLeave();
2784
+ }
2785
+ async executeDiscardViaShortcut(rejectCommandName) {
2786
+ if (!this.canExecuteDiscardShortcut()) {
2787
+ return;
2788
+ }
2789
+ const confirmed = await this.unsavedChangesConfirm.confirmIfDirty(true, {
2790
+ title: '@general:messages.unsaved-changes.title',
2791
+ message: '@general:messages.discard-changes.message',
2792
+ approveLeaveOnConfirm: false,
2793
+ });
2794
+ if (!confirmed) {
2795
+ return;
2796
+ }
2797
+ await this.execute({ name: rejectCommandName });
2798
+ }
2799
+ async executeActionShortcut(entry) {
2800
+ const commandName = entry.command.name;
2801
+ if (isDiscardFooterCommand(commandName)) {
2802
+ if (this.vm.currentPage()?.isReadonly || this.vm.isOverlayBusy()) {
2803
+ return;
2804
+ }
2805
+ if (this.unsavedChangesConfirm.isConfirmPending()) {
2806
+ return;
2807
+ }
2808
+ if (!this.shouldPromptLeave()) {
2809
+ return;
2810
+ }
2811
+ const confirmed = await this.unsavedChangesConfirm.confirmIfDirty(true, {
2812
+ title: '@general:messages.unsaved-changes.title',
2813
+ message: '@general:messages.discard-changes.message',
2814
+ approveLeaveOnConfirm: false,
2815
+ });
2816
+ if (!confirmed) {
2817
+ return;
2818
+ }
2819
+ }
2820
+ await this.execute(entry.command);
2821
+ }
2822
+ isActionShortcutActive(entryId) {
2823
+ if (this.vm.currentPage()?.isReadonly || this.vm.isOverlayBusy()) {
2824
+ return false;
2825
+ }
2826
+ const entry = this.actionShortcutEntries().get(entryId);
2827
+ if (!entry) {
2828
+ return false;
2829
+ }
2830
+ return entry.visible && !entry.disabled;
2831
+ }
2832
+ registerCoreKeyboardShortcuts() {
2833
+ if (!this.isWidgetFormPrimaryPage()) {
2834
+ this.registerDetailsViewShortcutScope('layout-details-view-core', []);
2835
+ return;
2836
+ }
2837
+ const accept = this.vm.currentPage()?.settings?.commands?.accept;
2838
+ const reject = this.vm.currentPage()?.settings?.commands?.reject;
2839
+ const acceptCommandName = accept?.command.name ?? 'update-entity';
2840
+ const rejectCommandName = reject?.command.name ?? 'discard';
2841
+ const saveKeys = resolveDetailsViewFooterCommandShortcuts(acceptCommandName, accept?.shortcuts);
2842
+ const discardKeys = resolveDetailsViewFooterCommandShortcuts(rejectCommandName, reject?.shortcuts);
2843
+ const scope = this.getKeyboardShortcutScope();
2844
+ const shortcuts = [];
2845
+ if (saveKeys?.length && this.isCoreSaveShortcutAvailable()) {
2846
+ shortcuts.push({
2847
+ keys: saveKeys,
2848
+ title: accept?.title ?? '@general:actions.save.title',
2849
+ group: scope,
2850
+ allowInEditableFields: true,
2851
+ when: () => this.canExecuteSaveShortcut(),
2852
+ handler: () => {
2853
+ void this.execute({ name: acceptCommandName });
2854
+ },
2855
+ });
2856
+ }
2857
+ if (discardKeys?.length && this.isCoreDiscardShortcutAvailable()) {
2858
+ shortcuts.push({
2859
+ keys: discardKeys,
2860
+ title: reject?.title ?? '@general:actions.discard.title',
2861
+ group: scope,
2862
+ allowInEditableFields: true,
2863
+ when: () => this.canExecuteDiscardShortcut(),
2864
+ handler: () => {
2865
+ void this.executeDiscardViaShortcut(rejectCommandName);
2866
+ },
2867
+ });
2868
+ }
2869
+ this.registerDetailsViewShortcutScope('layout-details-view-core', shortcuts);
2870
+ }
2871
+ registerActionKeyboardShortcuts(entries) {
2872
+ const scope = this.getKeyboardShortcutScope();
2873
+ const shortcuts = [];
2874
+ for (const [entryId, entry] of entries) {
2875
+ for (const chord of entry.chords) {
2876
+ shortcuts.push({
2877
+ keys: [chord],
2878
+ title: entry.title,
2879
+ group: scope,
2880
+ allowInEditableFields: entry.command.name ? isSaveFooterCommand(entry.command.name) || isDiscardFooterCommand(entry.command.name) : false,
2881
+ when: () => this.isActionShortcutActive(entryId),
2882
+ handler: () => {
2883
+ if (!this.isActionShortcutActive(entryId)) {
2884
+ return;
2885
+ }
2886
+ void this.executeActionShortcut(entry);
2887
+ },
2888
+ });
2889
+ }
2890
+ }
2891
+ this.registerDetailsViewShortcutScope('layout-details-view-actions', shortcuts);
2892
+ }
2893
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutDetailsViewComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
2894
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPLayoutDetailsViewComponent, isStandalone: true, selector: "axp-layout-details-view", providers: [
2895
+ {
2896
+ provide: AXPPageLayoutBase,
2897
+ useExisting: AXPLayoutDetailsViewComponent,
2898
+ },
2899
+ AXPLayoutDetailsViewViewModel,
2900
+ ], 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 #sidebarTabs class=\"axp-vertical-tabs\" [look]=\"'with-line-color'\" [location]=\"'end'\" [fitParent]=\"true\"\n (onActiveTabChanged)=\"handleSidebarTabChanged($event)\">\n @for (item of vm.adapter()?.pages; track item.id) {\n <ax-tab-item [key]=\"item.id\"\n [text]=\"(formatService.format(item.label, 'string', vm.rootContext()) | translate | async)!\"\n [active]=\"vm.currentPage()?.id === item.id\">\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 <ax-form class=\"ax-h-full ax-w-full\" #form>\n <axp-widgets-container #widgetsContainerRef [context]=\"vm.context()\"\n (onContextChanged)=\"handleOnContextChanged($event)\">\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 axp-page-component-renderer [componentKey]=\"$any(item).componentKey\" [rootContext]=\"vm.rootContext()\"\n [pageConfig]=\"$any(item).pageConfig\" [options]=\"$any(item).options\" (componentReady)=\"recompute()\"></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 (onClick)=\"vm.setCurrentTab(tab)\" [text]=\"(tab.title | translate | async)!\"\n [active]=\"vm.currentTab()?.id === tab.id\">\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 </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 [class.ax-sm]=\"deviceService.isSmall()\" [iconOnly]=\"deviceService.isSmall()\"\n [text]=\"'@general:terms.interface.actions' | translate | async\"\n [look]=\"deviceService.isSmall() ? 'blank' : 'solid'\" [color]=\"'default'\">\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 [text]=\"(t(action.title) | async)!\" [color]=\"action.color\"\n [disabled]=\"isFooterActionDisabled(action)\" (onClick)=\"execute(action.command!)\">\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 [class.ax-sm]=\"deviceService.isSmall()\" [disabled]=\"vm.isOverlayBusy()\"\n [text]=\"(t(reject.title) | async)!\" [look]=\"'solid'\" [color]=\"reject.color\" (onClick)=\"execute(reject.command)\">\n <ax-prefix>\n <i class=\"{{ reject.icon }}\"></i>\n </ax-prefix>\n </ax-button>\n }\n @if (acceptFooterCommand(); as accept) {\n <ax-button [class.ax-sm]=\"deviceService.isSmall()\" [disabled]=\"vm.isOverlayBusy()\"\n [text]=\"(t(accept.title) | async)!\" [look]=\"'solid'\" [color]=\"accept.color\" (onClick)=\"execute(accept.command)\">\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 [class.ax-sm]=\"deviceService.isSmall()\" [disabled]=\"isFooterActionDisabled(action)\"\n [text]=\"(t(action.title) | async)!\" [look]=\"'solid'\" [color]=\"action.color\"\n (onClick)=\"execute(action.command!)\">\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 [text]=\"(t(sub.title) | async)!\" [color]=\"sub.color\"\n [disabled]=\"isFooterActionDisabled(sub)\" (onClick)=\"execute(sub.command!)\">\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>", 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:
2901
+ // Angular
2902
+ CommonModule }, { kind: "ngmodule", type:
2903
+ // ACoreX
2904
+ AXMenuModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i6.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: i6.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i6.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items", "closeParentOnClick", "lockOnLoading"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXButtonGroupModule }, { kind: "ngmodule", type: AXDropdownButtonModule }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "component", type: i5.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "directive", type: i3$1.AXTranslatorDirective, selector: "[translate]" }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i3.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i3.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXBreadcrumbsModule }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "ngmodule", type: AXSearchBoxModule }, { kind: "ngmodule", type: AXFormatModule }, { kind: "ngmodule", type: AXFileModule }, { kind: "ngmodule", type: AXDateTimeModule }, { kind: "ngmodule", type: AXTabsModule }, { kind: "component", type: i5$1.AXTabsComponent, selector: "ax-tabs", inputs: ["look", "location", "fitParent", "minWidth", "content"], outputs: ["onActiveTabChanged"] }, { kind: "component", type: i5$1.AXTabItemComponent, selector: "ax-tab-item", inputs: ["disabled", "text", "key", "headerTemplate", "active"], outputs: ["disabledChange", "onClick", "onBlur", "onFocus", "activeChange"] }, { kind: "component", type:
2905
+ //
2906
+ AXPThemeLayoutListComponent, selector: "axp-layout-list" }, { kind: "component", type: AXPThemeLayoutListItemComponent, selector: "axp-layout-list-item" }, { kind: "component", type: AXPPageLayoutComponent, selector: "axp-page-layout" }, { kind: "component", type: AXPThemeLayoutBlockComponent, selector: " axp-page-content, axp-page-footer-container, axp-page-footer, axp-page-header, axp-page-header-container, axp-page-toolbar, axp-layout-content, axp-layout-page-content, axp-layout-sections, axp-layout-body, axp-layout-page-body, axp-layout-prefix, axp-layout-suffix, axp-layout-title-bar, axp-layout-title, axp-layout-title-actions, axp-layout-nav-button, axp-layout-description, axp-layout-breadcrumbs, axp-layout-list-action, " }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i6$1.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "directive", type: i6$1.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged", "onLoad"], exportAs: ["widgetRenderer"] }, { kind: "component", type: AXPThemeLayoutStartSideComponent, selector: "axp-layout-page-start-side, axp-layout-start-side" }, { kind: "component", type: AXPThemeLayoutHeaderComponent, selector: "axp-layout-header" }, { kind: "ngmodule", type: AXFormModule }, { kind: "component", type: i7.AXFormComponent, selector: "ax-form", inputs: ["disabled", "readonly", "labelMode", "look", "messageStyle", "updateOn", "inUserInteractionActive"], outputs: ["onValidate", "updateOnChange"] }, { kind: "directive", type: AXPPageComponentRendererDirective, selector: "[axp-page-component-renderer]", inputs: ["componentKey", "rootContext", "pageConfig", "options"], outputs: ["componentReady"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }, { kind: "pipe", type: i3$1.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
2907
+ }
2908
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutDetailsViewComponent, decorators: [{
2909
+ type: Component,
2910
+ args: [{ selector: 'axp-layout-details-view', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
2911
+ // Angular
2912
+ CommonModule,
2913
+ // ACoreX
2914
+ AXMenuModule,
2915
+ AXButtonModule,
2916
+ AXButtonGroupModule,
2917
+ AXDropdownButtonModule,
2918
+ AXDropdownModule,
2919
+ AXTranslationModule,
2920
+ AXDecoratorModule,
2921
+ AXBreadcrumbsModule,
2922
+ AXBadgeModule,
2923
+ AXSearchBoxModule,
2924
+ AXFormatModule,
2925
+ AXFileModule,
2926
+ AXDateTimeModule,
2927
+ AXSearchBoxModule,
2928
+ AXTabsModule,
2929
+ //
2930
+ AXPThemeLayoutListComponent,
2931
+ AXPThemeLayoutListItemComponent,
2932
+ AXPPageLayoutComponent,
2933
+ AXPThemeLayoutBlockComponent,
2934
+ AXPWidgetCoreModule,
2935
+ AXPThemeLayoutStartSideComponent,
2936
+ AXPThemeLayoutHeaderComponent,
2937
+ AXFormModule,
2938
+ AXPPageComponentRendererDirective,
2939
+ ], encapsulation: ViewEncapsulation.None, providers: [
2940
+ {
2941
+ provide: AXPPageLayoutBase,
2942
+ useExisting: AXPLayoutDetailsViewComponent,
2943
+ },
2944
+ AXPLayoutDetailsViewViewModel,
2945
+ ], 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 #sidebarTabs class=\"axp-vertical-tabs\" [look]=\"'with-line-color'\" [location]=\"'end'\" [fitParent]=\"true\"\n (onActiveTabChanged)=\"handleSidebarTabChanged($event)\">\n @for (item of vm.adapter()?.pages; track item.id) {\n <ax-tab-item [key]=\"item.id\"\n [text]=\"(formatService.format(item.label, 'string', vm.rootContext()) | translate | async)!\"\n [active]=\"vm.currentPage()?.id === item.id\">\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 <ax-form class=\"ax-h-full ax-w-full\" #form>\n <axp-widgets-container #widgetsContainerRef [context]=\"vm.context()\"\n (onContextChanged)=\"handleOnContextChanged($event)\">\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 axp-page-component-renderer [componentKey]=\"$any(item).componentKey\" [rootContext]=\"vm.rootContext()\"\n [pageConfig]=\"$any(item).pageConfig\" [options]=\"$any(item).options\" (componentReady)=\"recompute()\"></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 (onClick)=\"vm.setCurrentTab(tab)\" [text]=\"(tab.title | translate | async)!\"\n [active]=\"vm.currentTab()?.id === tab.id\">\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 </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 [class.ax-sm]=\"deviceService.isSmall()\" [iconOnly]=\"deviceService.isSmall()\"\n [text]=\"'@general:terms.interface.actions' | translate | async\"\n [look]=\"deviceService.isSmall() ? 'blank' : 'solid'\" [color]=\"'default'\">\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 [text]=\"(t(action.title) | async)!\" [color]=\"action.color\"\n [disabled]=\"isFooterActionDisabled(action)\" (onClick)=\"execute(action.command!)\">\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 [class.ax-sm]=\"deviceService.isSmall()\" [disabled]=\"vm.isOverlayBusy()\"\n [text]=\"(t(reject.title) | async)!\" [look]=\"'solid'\" [color]=\"reject.color\" (onClick)=\"execute(reject.command)\">\n <ax-prefix>\n <i class=\"{{ reject.icon }}\"></i>\n </ax-prefix>\n </ax-button>\n }\n @if (acceptFooterCommand(); as accept) {\n <ax-button [class.ax-sm]=\"deviceService.isSmall()\" [disabled]=\"vm.isOverlayBusy()\"\n [text]=\"(t(accept.title) | async)!\" [look]=\"'solid'\" [color]=\"accept.color\" (onClick)=\"execute(accept.command)\">\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 [class.ax-sm]=\"deviceService.isSmall()\" [disabled]=\"isFooterActionDisabled(action)\"\n [text]=\"(t(action.title) | async)!\" [look]=\"'solid'\" [color]=\"action.color\"\n (onClick)=\"execute(action.command!)\">\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 [text]=\"(t(sub.title) | async)!\" [color]=\"sub.color\"\n [disabled]=\"isFooterActionDisabled(sub)\" (onClick)=\"execute(sub.command!)\">\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>", 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"] }]
2946
+ }], propDecorators: { 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 }] }] } });
2947
+
2948
+ //#region ---- Details View Can Deactivate Guard ----
2949
+ /**
2950
+ * Prompts for unsaved changes before leaving the entity details route (breadcrumbs, menu, back to list).
2951
+ */
2952
+ const axpDetailsViewCanDeactivateGuard = createUnsavedChangesCanDeactivateGuard();
2953
+ //#endregion
2954
+
2955
+ /**
2956
+ * Generated bundle index. Do not edit.
2957
+ */
2958
+
2959
+ export { AXPLayoutDetailsViewComponent, AXPLayoutDetailsViewViewModel, AXPPageComponentInstanceRegistryService, AXPPageComponentRendererDirective, AXPPageLayoutBase, AXPPageLayoutBaseComponent, AXPPageLayoutComponent, AXPPopupLayoutBase, AXPPopupLayoutBaseComponent, AXPPopupLayoutComponent, AXP_LAYOUT_DETAILS_VIEW_ADAPTER_LOADER, axpDetailsViewCanDeactivateGuard, buildNotSavedPageBadge, getDetailsViewActionCommandBaseName, isDiscardCommand, isDiscardFooterCommand, isSaveFooterCommand, resolveDetailsViewActionShortcuts, resolveDetailsViewFooterCommandShortcuts, resolvePageFooterActionDefaultShortcuts, resolvePageFooterActionShortcuts };
2960
+ //# sourceMappingURL=acorex-platform-framework-client-layout-views.mjs.map