@acorex/platform 21.0.0-next.88 → 21.0.0-next.90

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 (55) hide show
  1. package/fesm2022/acorex-platform-auth.mjs +8 -2
  2. package/fesm2022/acorex-platform-auth.mjs.map +1 -1
  3. package/fesm2022/{acorex-platform-common-common-settings.provider-VfmZct9H.mjs → acorex-platform-common-common-settings.provider-DilV4dql.mjs} +15 -42
  4. package/fesm2022/acorex-platform-common-common-settings.provider-DilV4dql.mjs.map +1 -0
  5. package/fesm2022/acorex-platform-common.mjs +3299 -3200
  6. package/fesm2022/acorex-platform-common.mjs.map +1 -1
  7. package/fesm2022/acorex-platform-contracts.mjs +1 -0
  8. package/fesm2022/acorex-platform-contracts.mjs.map +1 -1
  9. package/fesm2022/acorex-platform-core.mjs +643 -30
  10. package/fesm2022/acorex-platform-core.mjs.map +1 -1
  11. package/fesm2022/acorex-platform-layout-builder.mjs +98 -189
  12. package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
  13. package/fesm2022/{acorex-platform-layout-components-binding-expression-editor-popup.component-CXEdvDTf.mjs → acorex-platform-layout-components-binding-expression-editor-popup.component-RttFwaw9.mjs} +17 -4
  14. package/fesm2022/acorex-platform-layout-components-binding-expression-editor-popup.component-RttFwaw9.mjs.map +1 -0
  15. package/fesm2022/acorex-platform-layout-components.mjs +125 -26
  16. package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
  17. package/fesm2022/acorex-platform-layout-designer.mjs +4 -4
  18. package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
  19. package/fesm2022/acorex-platform-layout-entity-contracts.mjs +106 -12
  20. package/fesm2022/acorex-platform-layout-entity-contracts.mjs.map +1 -1
  21. package/fesm2022/acorex-platform-layout-entity.mjs +512 -155
  22. package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
  23. package/fesm2022/acorex-platform-layout-views.mjs +921 -173
  24. package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
  25. package/fesm2022/acorex-platform-layout-widget-core.mjs +380 -103
  26. package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -1
  27. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-edit-popup.component-D4MOHEpI.mjs → acorex-platform-layout-widgets-tabular-data-edit-popup.component-BvFykMTH.mjs} +2 -2
  28. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-edit-popup.component-D4MOHEpI.mjs.map → acorex-platform-layout-widgets-tabular-data-edit-popup.component-BvFykMTH.mjs.map} +1 -1
  29. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-view-popup.component-gX-3Kx9I.mjs → acorex-platform-layout-widgets-tabular-data-view-popup.component-CntopmDS.mjs} +2 -2
  30. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-view-popup.component-gX-3Kx9I.mjs.map → acorex-platform-layout-widgets-tabular-data-view-popup.component-CntopmDS.mjs.map} +1 -1
  31. package/fesm2022/acorex-platform-layout-widgets.mjs +575 -239
  32. package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
  33. package/fesm2022/acorex-platform-themes-default.mjs +232 -25
  34. package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
  35. package/fesm2022/{acorex-platform-themes-shared-settings.provider-BUwPqwUS.mjs → acorex-platform-themes-shared-settings.provider-UyKdkeyk.mjs} +48 -1
  36. package/fesm2022/acorex-platform-themes-shared-settings.provider-UyKdkeyk.mjs.map +1 -0
  37. package/fesm2022/acorex-platform-themes-shared.mjs +103 -24
  38. package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
  39. package/package.json +1 -1
  40. package/types/acorex-platform-auth.d.ts +1 -0
  41. package/types/acorex-platform-common.d.ts +202 -151
  42. package/types/acorex-platform-contracts.d.ts +1 -0
  43. package/types/acorex-platform-core.d.ts +162 -7
  44. package/types/acorex-platform-layout-builder.d.ts +10 -20
  45. package/types/acorex-platform-layout-components.d.ts +14 -1
  46. package/types/acorex-platform-layout-entity-contracts.d.ts +31 -2
  47. package/types/acorex-platform-layout-entity.d.ts +91 -16
  48. package/types/acorex-platform-layout-views.d.ts +107 -20
  49. package/types/acorex-platform-layout-widget-core.d.ts +71 -9
  50. package/types/acorex-platform-layout-widgets.d.ts +48 -26
  51. package/types/acorex-platform-themes-default.d.ts +60 -6
  52. package/types/acorex-platform-themes-shared.d.ts +4 -0
  53. package/fesm2022/acorex-platform-common-common-settings.provider-VfmZct9H.mjs.map +0 -1
  54. package/fesm2022/acorex-platform-layout-components-binding-expression-editor-popup.component-CXEdvDTf.mjs.map +0 -1
  55. package/fesm2022/acorex-platform-themes-shared-settings.provider-BUwPqwUS.mjs.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import * as i2 from '@angular/common';
2
2
  import { CommonModule } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
- import { inject, viewChild, afterNextRender, effect, ChangeDetectionStrategy, ViewEncapsulation, Component, signal, untracked, computed, Injectable, input, output, ViewContainerRef, Directive, DestroyRef, ElementRef } 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
5
  import { AXCommonModule } from '@acorex/cdk/common';
6
6
  import * as i4 from '@acorex/components/breadcrumbs';
7
7
  import { AXBreadcrumbsModule } from '@acorex/components/breadcrumbs';
@@ -12,9 +12,9 @@ import { AXDecoratorModule } from '@acorex/components/decorators';
12
12
  import * as i5 from '@acorex/components/dropdown';
13
13
  import { AXDropdownModule } from '@acorex/components/dropdown';
14
14
  import { AXDropdownButtonModule } from '@acorex/components/dropdown-button';
15
- import { AXPTabNavigationService, AXPStickyDirective, AXPCommonSettings, AXPHomePageService, AXPSettingsService, AXPTabPreviewService, AXPUnsavedChangesConfirmService, normalizeRouterUrl, AXP_HOME_TAB_KEY, axpResolveTabKey, axpIsEntityDetailsViewRoute, axpNavigateAppPath, AXPEntityCommandScope, AXPUnsavedChangesPopstateService, createUnsavedChangesCanDeactivateGuard } from '@acorex/platform/common';
15
+ import { AXPTabNavigationService, AXPStickyDirective, AXPCommonSettings, AXPHomePageService, AXPSettingsService, AXPTabPreviewService, AXPUnsavedChangesConfirmService, AXPBrowserTitleService, axpJoinTabTitleSegments, normalizeRouterUrl, AXP_HOME_TAB_KEY, axpResolveTabKey, axpIsEntityDetailsViewRoute, axpNavigateAppPath, AXPEntityCommandScope, AXPNotFoundError, AXP_NOT_FOUND_ROUTE, AXPUnsavedChangesPopstateService, AXP_ENTITY_DELETED_WORKFLOW_EVENT_TYPE, createUnsavedChangesCanDeactivateGuard } from '@acorex/platform/common';
16
16
  import * as i8 from '@acorex/platform/core';
17
- import { AXPDeviceService, AXPComponentSlotModule, getSmart, AXPExpressionEvaluatorService, AXPContextEvalFactory, normalizeKeyboardShortcuts, AXPContextStore, AXPBroadcastEventService, AXPKeyboardShortcutRegistry, AXPKeyboardShortcutPriority } from '@acorex/platform/core';
17
+ import { AXPDeviceService, AXPComponentSlotModule, getSmart, AXPDebugService, OVERLAY_DETAILS_VIEW_TRACE_NS, AXPExpressionEvaluatorService, AXPContextEvalFactory, normalizeKeyboardShortcuts, AXPContextStore, AXPBroadcastEventService, AXPKeyboardShortcutRegistry, FORM_DIRTY_TRACE_NS, summarizeFormContextDirtyDiff, AXPKeyboardShortcutPriority } from '@acorex/platform/core';
18
18
  import * as i1 from '@acorex/cdk/drawer';
19
19
  import { AXDrawerDirectiveModule, AXDrawerContainerDirective } from '@acorex/cdk/drawer';
20
20
  import { AXDrawerModule } from '@acorex/components/drawer';
@@ -28,25 +28,25 @@ import { AXPWorkflowService } from '@acorex/platform/workflow';
28
28
  import { AXToastService } from '@acorex/components/toast';
29
29
  import { AXFormatService, AXFormatModule } from '@acorex/core/format';
30
30
  import { AXPSessionService } from '@acorex/platform/auth';
31
- import { Router, ActivatedRoute } from '@angular/router';
31
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
32
+ import { Router, NavigationEnd, ActivatedRoute } from '@angular/router';
32
33
  import { isEmpty, cloneDeep, get } from 'lodash-es';
34
+ import { filter } from 'rxjs/operators';
33
35
  import { AXBasePageComponent } from '@acorex/components/page';
34
- import * as i7 from '@acorex/platform/layout/widget-core';
36
+ import * as i6$1 from '@acorex/platform/layout/widget-core';
35
37
  import { AXPPageStatus, AXPWidgetContainerComponent, AXPWidgetCoreModule } from '@acorex/platform/layout/widget-core';
36
38
  import { signalStore, withState, withComputed, withMethods, patchState } from '@ngrx/signals';
37
39
  import { AXBadgeModule } from '@acorex/components/badge';
38
40
  import { AXButtonGroupModule } from '@acorex/components/button-group';
39
- import * as i5$1 from '@acorex/components/loading';
40
- import { AXLoadingModule } from '@acorex/components/loading';
41
41
  import { AXMenuModule } from '@acorex/components/menu';
42
42
  import { AXSearchBoxModule } from '@acorex/components/search-box';
43
43
  import { AXDateTimeModule } from '@acorex/core/date-time';
44
44
  import { AXFileModule } from '@acorex/core/file';
45
- import * as i8$1 from '@acorex/components/form';
45
+ import * as i7 from '@acorex/components/form';
46
46
  import { AXFormModule } from '@acorex/components/form';
47
- import * as i6$1 from '@acorex/components/tabs';
47
+ import * as i5$1 from '@acorex/components/tabs';
48
48
  import { AXTabsModule } from '@acorex/components/tabs';
49
- import { Subject, takeUntil } from 'rxjs';
49
+ import { Subject, takeUntil, filter as filter$1 } from 'rxjs';
50
50
 
51
51
  class AXPPageLayoutBase {
52
52
  }
@@ -146,7 +146,7 @@ class AXPPageLayoutComponent {
146
146
  });
147
147
  }
148
148
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPageLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
149
- 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\"\n class=\"ax-z-50 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\n id=\"axp-page-badge\"\n class=\"__title-badge --{{ page?.badge()?.color }}\"\n [attr.title]=\"page?.badge()?.description ?? page?.badge()?.title\"\n [attr.aria-label]=\"page?.badge()?.title\"\n >\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:
149
+ 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:
150
150
  //
151
151
  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:
152
152
  //
@@ -174,7 +174,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
174
174
  AXPStickyDirective,
175
175
  AXPThemeLayoutContainerComponent,
176
176
  AXPStatusChipComponent,
177
- ], 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\"\n class=\"ax-z-50 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\n id=\"axp-page-badge\"\n class=\"__title-badge --{{ page?.badge()?.color }}\"\n [attr.title]=\"page?.badge()?.description ?? page?.badge()?.title\"\n [attr.aria-label]=\"page?.badge()?.title\"\n >\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"] }]
177
+ ], 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"] }]
178
178
  }], propDecorators: { startSideDrawer: [{ type: i0.ViewChild, args: ['startSideDrawer', { isSignal: true }] }] } });
179
179
 
180
180
  //#region ---- Page Unsaved Changes Utilities ----
@@ -223,7 +223,9 @@ class AXPPageLayoutBaseComponent {
223
223
  this.tabNavigation = inject(AXPTabNavigationService);
224
224
  this.tabPreviewService = inject(AXPTabPreviewService);
225
225
  this.unsavedChangesConfirm = inject(AXPUnsavedChangesConfirmService);
226
+ this.browserTitleService = inject(AXPBrowserTitleService);
226
227
  this.router = inject(Router);
228
+ this.pageLayoutDestroyRef = inject(DestroyRef);
227
229
  //#endregion
228
230
  //#region ---------------- Signal to force recomputation ----------------
229
231
  this._updateTrigger = signal(0, ...(ngDevMode ? [{ debugName: "_updateTrigger" }] : /* istanbul ignore next */ []));
@@ -298,6 +300,19 @@ class AXPPageLayoutBaseComponent {
298
300
  return false;
299
301
  }, ...(ngDevMode ? [{ debugName: "hasTitle" }] : /* istanbul ignore next */ []));
300
302
  this.hasTitleIcon = computed(() => this.titleIcon() !== null, ...(ngDevMode ? [{ debugName: "hasTitleIcon" }] : /* istanbul ignore next */ []));
303
+ this.#browserTitleEffect = effect(() => {
304
+ if (this.tabNavigation.effectiveMode() === 'tabular') {
305
+ return;
306
+ }
307
+ if (!this.isActivePageForBrowserTitle()) {
308
+ return;
309
+ }
310
+ this.updateTrigger();
311
+ this.title();
312
+ untracked(() => {
313
+ void this.publishBrowserTitleIfActive();
314
+ });
315
+ }, ...(ngDevMode ? [{ debugName: "#browserTitleEffect" }] : /* istanbul ignore next */ []));
301
316
  //#endregion
302
317
  //#region ---------------- Description ----------------
303
318
  this.description = signal(null, ...(ngDevMode ? [{ debugName: "description" }] : /* istanbul ignore next */ []));
@@ -340,16 +355,17 @@ class AXPPageLayoutBaseComponent {
340
355
  this.breadcrumbs.set(trail);
341
356
  if (this.tabNavigation.effectiveMode() === 'tabular') {
342
357
  const tabKey = this.pageTabKey();
343
- let tabTitle = await this.resolveBreadcrumbTabTitle(breadCrumbs);
358
+ const tabTitleSegments = await this.resolveBreadcrumbTabTitleSegments(breadCrumbs);
359
+ let tabTitle = tabTitleSegments.length ? axpJoinTabTitleSegments(tabTitleSegments) : null;
344
360
  if (!tabTitle) {
345
361
  tabTitle = await this.resolvePageTitleForTab();
346
362
  }
347
363
  if (tabKey && tabTitle) {
348
- this.tabNavigation.syncTabTitle(tabKey, tabTitle);
364
+ this.tabNavigation.syncTabTitle(tabKey, tabTitleSegments.length ? tabTitleSegments : tabTitle);
349
365
  }
350
366
  }
351
367
  }, ...(ngDevMode ? [{ debugName: "#breadcrumbsEffect" }] : /* istanbul ignore next */ []));
352
- this.hasBreadcrumbs = computed(() => this.breadcrumbs().length > 1 && this.tabNavigation.effectiveMode() !== 'tabular', ...(ngDevMode ? [{ debugName: "hasBreadcrumbs" }] : /* istanbul ignore next */ []));
368
+ this.hasBreadcrumbs = computed(() => this.breadcrumbs().length > 1, ...(ngDevMode ? [{ debugName: "hasBreadcrumbs" }] : /* istanbul ignore next */ []));
353
369
  //#endregion
354
370
  //#region ---------------- Badge ----------------
355
371
  this.badge = signal(null, ...(ngDevMode ? [{ debugName: "badge" }] : /* istanbul ignore next */ []));
@@ -409,6 +425,11 @@ class AXPPageLayoutBaseComponent {
409
425
  this.translateService.langChanges$.subscribe(() => {
410
426
  this.recompute();
411
427
  });
428
+ this.router.events
429
+ .pipe(filter((event) => event instanceof NavigationEnd), takeUntilDestroyed(this.pageLayoutDestroyRef))
430
+ .subscribe(() => {
431
+ void this.publishBrowserTitleIfActive();
432
+ });
412
433
  }
413
434
  /** Maps the home-page route to the pinned home tab key instead of a path-based key. */
414
435
  resolvePageTabKey() {
@@ -418,6 +439,10 @@ class AXPPageLayoutBaseComponent {
418
439
  }
419
440
  return axpResolveTabKey(this.router);
420
441
  }
442
+ /** Stable tab key for this cached page instance (tabular mode). */
443
+ getPageTabKey() {
444
+ return this.pageTabKey();
445
+ }
421
446
  //#endregion
422
447
  //#region ---------------- Tab Title Sync ----------------
423
448
  /** Refreshes tab title when this cached page becomes the active tab again. */
@@ -481,6 +506,33 @@ class AXPPageLayoutBaseComponent {
481
506
  getPageIcon() {
482
507
  return null;
483
508
  }
509
+ #browserTitleEffect;
510
+ /** Publishes the page title to the browser tab when this instance is the active route. */
511
+ async publishBrowserTitleIfActive() {
512
+ if (this.tabNavigation.effectiveMode() === 'tabular') {
513
+ return;
514
+ }
515
+ if (!this.isActivePageForBrowserTitle()) {
516
+ return;
517
+ }
518
+ const routeKey = this.pageTabKey();
519
+ if (!routeKey) {
520
+ return;
521
+ }
522
+ const pageTitle = await this.resolvePageTitleForTab();
523
+ if (!this.isActivePageForBrowserTitle()) {
524
+ return;
525
+ }
526
+ this.browserTitleService.setSimpleModePageTitle(pageTitle, routeKey);
527
+ }
528
+ /** True when this cached page instance matches the current router location. */
529
+ isActivePageForBrowserTitle() {
530
+ const pageKey = this.pageTabKey();
531
+ if (!pageKey) {
532
+ return false;
533
+ }
534
+ return pageKey === axpResolveTabKey(this.router);
535
+ }
484
536
  #descriptionEffect;
485
537
  getPageDescription() {
486
538
  return null;
@@ -489,8 +541,8 @@ class AXPPageLayoutBaseComponent {
489
541
  getPageBreadcrumbs() {
490
542
  return [];
491
543
  }
492
- /** Joins all page breadcrumbs (Home is excluded upstream) as the tab label. */
493
- async resolveBreadcrumbTabTitle(pageBreadcrumbs) {
544
+ /** Collects resolved breadcrumb labels (Home excluded upstream) for tab chrome. */
545
+ async resolveBreadcrumbTabTitleSegments(pageBreadcrumbs) {
494
546
  const segments = [];
495
547
  for (const item of pageBreadcrumbs) {
496
548
  const title = await this.resolveBreadcrumbItemTitle(item);
@@ -498,10 +550,15 @@ class AXPPageLayoutBaseComponent {
498
550
  segments.push(title);
499
551
  }
500
552
  }
553
+ return segments;
554
+ }
555
+ /** Joins breadcrumb segments as the stored tab title string. */
556
+ async resolveBreadcrumbTabTitle(pageBreadcrumbs) {
557
+ const segments = await this.resolveBreadcrumbTabTitleSegments(pageBreadcrumbs);
501
558
  if (!segments.length) {
502
559
  return null;
503
560
  }
504
- return segments.join(' - ');
561
+ return axpJoinTabTitleSegments(segments);
505
562
  }
506
563
  /** Resolves a breadcrumb label the same way as page chrome (`t(item.title)`). */
507
564
  async resolveBreadcrumbItemTitle(item) {
@@ -781,6 +838,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
781
838
  }]
782
839
  }], propDecorators: { layout: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AXPPopupLayoutComponent), { isSignal: true }] }] } });
783
840
 
841
+ const AXP_LAYOUT_DETAILS_VIEW_ADAPTER_LOADER = new InjectionToken('AXP_LAYOUT_DETAILS_VIEW_ADAPTER_LOADER');
842
+ //#endregion
843
+
844
+ /** Trace namespace for details-view page / URL sync diagnostics. */
845
+ const DETAILS_VIEW_PAGE_TRACE_NS = 'details-view-page';
846
+
784
847
  //#region ---- Root-context widget expression resolution ----
785
848
  /**
786
849
  * Evaluates `{{ ... }}` strings inside `options.customFilterDefinitions` using `rootContext`
@@ -820,10 +883,11 @@ const AXPLayoutDetailsViewViewModel = signalStore(withState(() => {
820
883
  const state = {
821
884
  adapter: null,
822
885
  rootContext: {},
886
+ savedContext: {},
823
887
  previousContext: {},
824
888
  context: {},
825
889
  scope: null,
826
- status: AXPPageStatus.Processing,
890
+ status: AXPPageStatus.Idle,
827
891
  currentPage: null,
828
892
  currentTab: null,
829
893
  pageSelectedRows: [],
@@ -848,6 +912,10 @@ const AXPLayoutDetailsViewViewModel = signalStore(withState(() => {
848
912
  //
849
913
  isBusy: computed(() => store.status() == AXPPageStatus.Processing),
850
914
  isSaving: computed(() => store.status() == AXPPageStatus.Submitting),
915
+ isOverlayBusy: computed(() => store.status() === AXPPageStatus.Processing || store.status() === AXPPageStatus.Submitting),
916
+ isPageReady: computed(() => store.adapter() != null &&
917
+ store.currentPage() != null &&
918
+ store.status() === AXPPageStatus.Rendered),
851
919
  isDirty: computed(() => {
852
920
  if (store.currentPage()?.isReadonly) {
853
921
  return false;
@@ -871,7 +939,7 @@ const AXPLayoutDetailsViewViewModel = signalStore(withState(() => {
871
939
  const evaluatedByTab = store.evaluatedTabContentsById();
872
940
  return evaluatedByTab[tab.id] ?? [];
873
941
  }),
874
- })), withMethods((store, evaluatorService = inject(AXPExpressionEvaluatorService), contextEvalFactory = inject(AXPContextEvalFactory), router = inject(Router), route = inject(ActivatedRoute), deviceService = inject(AXPDeviceService), formatService = inject(AXFormatService), toastService = inject(AXToastService), translateService = inject(AXTranslationService), settingsService = inject(AXPSettingsService)) => {
942
+ })), 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)) => {
875
943
  const resolvePageFromId = (adapter, pageId) => {
876
944
  if (pageId) {
877
945
  return adapter.pages.find((p) => p.id === pageId) ?? null;
@@ -881,49 +949,141 @@ const AXPLayoutDetailsViewViewModel = signalStore(withState(() => {
881
949
  }
882
950
  return null;
883
951
  };
952
+ /** Serializes page activation so route sync and UI cannot run overlapping loads. */
953
+ let pageActivationChain = Promise.resolve();
954
+ /** Serializes route-driven adapter loads (initial navigation and layout refresh). */
955
+ let routeLoadChain = Promise.resolve();
956
+ /** `page` query param from the browser URL (reliable on hard refresh). */
957
+ const readPageQueryParam = () => {
958
+ const page = router.parseUrl(router.url).queryParams['page'];
959
+ return typeof page === 'string' && page.length > 0 ? page : null;
960
+ };
961
+ /** Snapshot of all page-query sources for trace logs. */
962
+ const pageQueryTraceContext = (extra = {}) => ({
963
+ routerUrl: router.url,
964
+ pageFromRouterUrl: readPageQueryParam(),
965
+ pageFromRouteSnapshot: route.snapshot.queryParamMap.get('page'),
966
+ pageFromRootSnapshot: router.routerState.snapshot.root.queryParams['page'] ?? null,
967
+ currentPageId: store.currentPage()?.id ?? null,
968
+ adapterPageIds: store.adapter()?.pages.map((p) => p.id) ?? [],
969
+ ...extra,
970
+ });
884
971
  /** Loads page data, context, and evaluated widgets for the target sidebar page. */
885
972
  const activatePageById = async (pageId, forceRefresh) => {
973
+ const activation = pageActivationChain.then(() => runPageActivation(pageId, forceRefresh));
974
+ pageActivationChain = activation.catch(() => undefined);
975
+ await activation;
976
+ };
977
+ const runPageActivation = async (pageId, forceRefresh) => {
886
978
  const adapter = store.adapter();
887
979
  if (!adapter) {
888
980
  return;
889
981
  }
890
- const queryParams = router.routerState.snapshot.root.queryParams;
891
- const targetPageId = pageId ?? queryParams['page'];
982
+ const targetPageId = pageId ?? readPageQueryParam() ?? undefined;
892
983
  const currentPage = resolvePageFromId(adapter, targetPageId);
984
+ AXPDebugService.instance?.trace(DETAILS_VIEW_PAGE_TRACE_NS, 'viewmodel', 'runPageActivation', () => pageQueryTraceContext({
985
+ passedPageId: pageId ?? null,
986
+ targetPageId: targetPageId ?? null,
987
+ resolvedPageId: currentPage?.id ?? null,
988
+ forceRefresh: forceRefresh ?? false,
989
+ vmStatus: store.status(),
990
+ }));
893
991
  if (!forceRefresh && currentPage?.id === store.currentPage()?.id && store.status() === AXPPageStatus.Rendered) {
992
+ AXPDebugService.instance?.trace(DETAILS_VIEW_PAGE_TRACE_NS, 'viewmodel', 'runPageActivation skipped (already rendered)', () => pageQueryTraceContext({
993
+ resolvedPageId: currentPage?.id ?? null,
994
+ }));
894
995
  return;
895
996
  }
997
+ const previousPageId = store.currentPage()?.id;
998
+ const isSidebarSwitch = previousPageId != null && previousPageId !== currentPage?.id;
999
+ if (isSidebarSwitch) {
1000
+ layout.setNavigationLoading(true);
1001
+ }
1002
+ const overlayAlreadyOn = layout.overlayTaskCount() > 0;
1003
+ if (!overlayAlreadyOn) {
1004
+ AXPDebugService.instance?.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'viewmodel', 'activatePage overlay ON', () => ({
1005
+ pageId: currentPage?.id,
1006
+ forceRefresh,
1007
+ isSidebarSwitch,
1008
+ }));
1009
+ layout.setOverlayLoading(true);
1010
+ }
1011
+ else {
1012
+ AXPDebugService.instance?.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'viewmodel', 'activatePage skipped overlay ON (task count > 0)', () => ({
1013
+ pageId: currentPage?.id,
1014
+ forceRefresh,
1015
+ isSidebarSwitch,
1016
+ taskCount: layout.overlayTaskCount(),
1017
+ }));
1018
+ }
896
1019
  patchState(store, {
897
1020
  status: AXPPageStatus.Processing,
898
1021
  });
899
- let context = {};
900
- if (currentPage) {
901
- const currentPageIndex = adapter.pages.findIndex((p) => p.id === currentPage.id);
902
- const loadResult = await adapter.pages[currentPageIndex]?.load?.({ forceRefresh });
903
- context = loadResult?.data ?? {};
904
- }
905
- const currentTab = currentPage?.tabs?.[0] ?? null;
906
- const rootCtx = store.rootContext();
907
- const { evaluatedPageContent, evaluatedTabContentsById } = await buildEvaluatedPageContentsForRootContext(currentPage, rootCtx, evaluatorService);
908
- patchState(store, {
909
- previousContext: cloneDeep(context),
910
- context: cloneDeep(context),
911
- status: AXPPageStatus.Rendered,
912
- currentPage,
913
- currentTab,
914
- evaluatedPageContent,
915
- evaluatedTabContentsById,
916
- formDirty: false,
917
- });
918
- if (currentPage && axpIsEntityDetailsViewRoute(router)) {
919
- const pageParam = route.snapshot.queryParamMap.get('page');
920
- if (pageParam !== currentPage.id) {
921
- router.navigate([], {
922
- relativeTo: route,
923
- queryParams: { page: currentPage.id },
924
- queryParamsHandling: 'merge',
925
- replaceUrl: true,
926
- });
1022
+ try {
1023
+ let context = {};
1024
+ if (currentPage) {
1025
+ const currentPageIndex = adapter.pages.findIndex((p) => p.id === currentPage.id);
1026
+ const pageLoad = adapter.pages[currentPageIndex]?.load;
1027
+ if (typeof pageLoad === 'function') {
1028
+ const loadResult = await pageLoad({ forceRefresh });
1029
+ context = loadResult?.data ?? {};
1030
+ }
1031
+ else {
1032
+ // Component/list sidebar pages have no `load`; keep entity root context so widget
1033
+ // state is not cleared (Change History, etc. use `rootContext` instead).
1034
+ context = cloneDeep(store.rootContext());
1035
+ }
1036
+ }
1037
+ const currentTab = currentPage?.tabs?.[0] ?? null;
1038
+ const shouldSyncRootContext = forceRefresh === true && currentPage?.isPrimary === true && !isEmpty(context);
1039
+ const rootCtx = shouldSyncRootContext
1040
+ ? context
1041
+ : store.rootContext();
1042
+ const { evaluatedPageContent, evaluatedTabContentsById } = await buildEvaluatedPageContentsForRootContext(currentPage, rootCtx, evaluatorService);
1043
+ patchState(store, {
1044
+ previousContext: cloneDeep(context),
1045
+ context: cloneDeep(context),
1046
+ savedContext: cloneDeep(context),
1047
+ ...(shouldSyncRootContext ? { rootContext: cloneDeep(context) } : {}),
1048
+ status: AXPPageStatus.Rendered,
1049
+ currentPage,
1050
+ currentTab,
1051
+ evaluatedPageContent,
1052
+ evaluatedTabContentsById,
1053
+ formDirty: false,
1054
+ pageSelectedRows: [],
1055
+ });
1056
+ AXPDebugService.instance?.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'viewmodel', 'activatePage VM ready (Rendered)', () => ({
1057
+ pageId: currentPage?.id,
1058
+ forceRefresh,
1059
+ }));
1060
+ const urlPageParam = readPageQueryParam();
1061
+ if (currentPage && axpIsEntityDetailsViewRoute(router)) {
1062
+ if (!urlPageParam) {
1063
+ AXPDebugService.instance?.trace(DETAILS_VIEW_PAGE_TRACE_NS, 'viewmodel', 'URL sync — add missing ?page=', () => pageQueryTraceContext({
1064
+ navigateToPageId: currentPage.id,
1065
+ }));
1066
+ router.navigate([], {
1067
+ relativeTo: route,
1068
+ queryParams: { page: currentPage.id },
1069
+ queryParamsHandling: 'merge',
1070
+ replaceUrl: true,
1071
+ });
1072
+ }
1073
+ else {
1074
+ AXPDebugService.instance?.trace(DETAILS_VIEW_PAGE_TRACE_NS, 'viewmodel', 'URL sync — skipped (page already in URL)', () => pageQueryTraceContext({
1075
+ urlPageParam,
1076
+ resolvedPageId: currentPage.id,
1077
+ }));
1078
+ }
1079
+ }
1080
+ }
1081
+ finally {
1082
+ if (isSidebarSwitch) {
1083
+ layout.setNavigationLoading(false);
1084
+ }
1085
+ if (store.status() === AXPPageStatus.Processing) {
1086
+ patchState(store, { status: AXPPageStatus.Rendered });
927
1087
  }
928
1088
  }
929
1089
  };
@@ -937,16 +1097,33 @@ const AXPLayoutDetailsViewViewModel = signalStore(withState(() => {
937
1097
  if (!adapter) {
938
1098
  return;
939
1099
  }
1100
+ // `load()` → `loadPage()` owns the first activation while `currentPage` is still null.
1101
+ if (store.currentPage() == null) {
1102
+ AXPDebugService.instance?.trace(DETAILS_VIEW_PAGE_TRACE_NS, 'viewmodel', 'queryParams effect — skipped (currentPage null)', () => pageQueryTraceContext({
1103
+ paramsPage: params['page'] ?? null,
1104
+ }));
1105
+ return;
1106
+ }
940
1107
  const pageId = params['page'];
941
- const targetPage = resolvePageFromId(adapter, pageId);
942
- if (store.currentPage()?.id === targetPage?.id) {
1108
+ if (!pageId) {
1109
+ AXPDebugService.instance?.trace(DETAILS_VIEW_PAGE_TRACE_NS, 'viewmodel', 'queryParams effect — skipped (no page param)', () => pageQueryTraceContext({
1110
+ params,
1111
+ }));
943
1112
  return;
944
1113
  }
945
- // `load()` `activatePageById` owns initial hydration when both sides are still null.
946
- if (store.currentPage() == null && targetPage == null) {
1114
+ const targetPage = adapter.pages.find((p) => p.id === pageId);
1115
+ if (!targetPage || store.currentPage()?.id === targetPage.id) {
1116
+ AXPDebugService.instance?.trace(DETAILS_VIEW_PAGE_TRACE_NS, 'viewmodel', 'queryParams effect — skipped (same or unknown page)', () => pageQueryTraceContext({
1117
+ paramsPage: pageId,
1118
+ targetPageFound: !!targetPage,
1119
+ }));
947
1120
  return;
948
1121
  }
949
- void activatePageById(targetPage?.id);
1122
+ AXPDebugService.instance?.trace(DETAILS_VIEW_PAGE_TRACE_NS, 'viewmodel', 'queryParams effect — activatePageById', () => pageQueryTraceContext({
1123
+ paramsPage: pageId,
1124
+ activatePageId: targetPage.id,
1125
+ }));
1126
+ void activatePageById(targetPage.id);
950
1127
  });
951
1128
  onCleanup(() => subscription.unsubscribe());
952
1129
  });
@@ -1048,7 +1225,7 @@ const AXPLayoutDetailsViewViewModel = signalStore(withState(() => {
1048
1225
  async loadPage(pageId, forceRefresh) {
1049
1226
  await activatePageById(pageId, forceRefresh);
1050
1227
  },
1051
- async load(adapter) {
1228
+ async load(adapter, forceRefresh, pageIdFromUrl) {
1052
1229
  const scope = {
1053
1230
  context: {
1054
1231
  ...contextEvalFactory.createLiveScope(() => store.context()),
@@ -1060,7 +1237,81 @@ const AXPLayoutDetailsViewViewModel = signalStore(withState(() => {
1060
1237
  });
1061
1238
  //
1062
1239
  await this.loadAdapter(adapter);
1063
- await this.loadPage(undefined, true);
1240
+ const pageId = pageIdFromUrl ?? readPageQueryParam() ?? undefined;
1241
+ AXPDebugService.instance?.trace(DETAILS_VIEW_PAGE_TRACE_NS, 'viewmodel', 'load — activate page', () => pageQueryTraceContext({
1242
+ pageIdFromUrl: pageIdFromUrl ?? null,
1243
+ resolvedPageId: pageId ?? null,
1244
+ forceRefresh: forceRefresh ?? false,
1245
+ }));
1246
+ await this.loadPage(pageId, forceRefresh);
1247
+ },
1248
+ async loadFromRoute(forceRefresh) {
1249
+ const run = async () => {
1250
+ const pageIdFromUrl = readPageQueryParam() ?? undefined;
1251
+ AXPDebugService.instance?.trace(DETAILS_VIEW_PAGE_TRACE_NS, 'viewmodel', 'loadFromRoute start', () => pageQueryTraceContext({
1252
+ pageIdFromUrl: pageIdFromUrl ?? null,
1253
+ forceRefresh: forceRefresh ?? false,
1254
+ }));
1255
+ const moduleName = route.parent?.snapshot.paramMap.get('module');
1256
+ const entityName = route.snapshot.paramMap.get('entity');
1257
+ const id = route.snapshot.paramMap.get('id');
1258
+ if (!moduleName || !entityName || !id) {
1259
+ return;
1260
+ }
1261
+ layout.setNavigationLoading(true);
1262
+ if (layout.overlayTaskCount() === 0) {
1263
+ AXPDebugService.instance?.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'viewmodel', 'loadFromRoute overlay ON', () => ({
1264
+ moduleName,
1265
+ entityName,
1266
+ id,
1267
+ forceRefresh,
1268
+ }));
1269
+ layout.setOverlayLoading(true);
1270
+ }
1271
+ else {
1272
+ AXPDebugService.instance?.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'viewmodel', 'loadFromRoute skipped overlay ON (task count > 0)', () => ({
1273
+ moduleName,
1274
+ entityName,
1275
+ id,
1276
+ forceRefresh,
1277
+ taskCount: layout.overlayTaskCount(),
1278
+ }));
1279
+ }
1280
+ try {
1281
+ if (forceRefresh) {
1282
+ patchState(store, {
1283
+ currentPage: null,
1284
+ status: AXPPageStatus.Idle,
1285
+ });
1286
+ }
1287
+ const adapter = await adapterLoader.load({ moduleName, entityName, id });
1288
+ AXPDebugService.instance?.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'viewmodel', 'adapter loaded (overlay still ON — details-view releases)', () => ({
1289
+ moduleName,
1290
+ entityName,
1291
+ id,
1292
+ }));
1293
+ await this.load(adapter, forceRefresh, pageIdFromUrl);
1294
+ }
1295
+ catch (error) {
1296
+ AXPDebugService.instance?.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'viewmodel', 'loadFromRoute overlay OFF (error)', () => ({
1297
+ moduleName,
1298
+ entityName,
1299
+ id,
1300
+ }));
1301
+ layout.setOverlayLoading(false);
1302
+ if (error instanceof AXPNotFoundError) {
1303
+ void router.navigateByUrl(AXP_NOT_FOUND_ROUTE);
1304
+ return;
1305
+ }
1306
+ throw error;
1307
+ }
1308
+ finally {
1309
+ layout.setNavigationLoading(false);
1310
+ }
1311
+ };
1312
+ const load = routeLoadChain.then(run);
1313
+ routeLoadChain = load.catch(() => undefined);
1314
+ await load;
1064
1315
  },
1065
1316
  updateContext(context) {
1066
1317
  patchState(store, {
@@ -1129,10 +1380,23 @@ const AXPLayoutDetailsViewViewModel = signalStore(withState(() => {
1129
1380
  },
1130
1381
  setCurrentPage(page) {
1131
1382
  const previousPage = store.currentPage();
1383
+ AXPDebugService.instance?.trace(DETAILS_VIEW_PAGE_TRACE_NS, 'viewmodel', 'setCurrentPage', () => pageQueryTraceContext({
1384
+ requestedPageId: page?.id ?? null,
1385
+ previousPageId: previousPage?.id ?? null,
1386
+ }));
1132
1387
  if (page) {
1133
- const pageParam = route.snapshot.queryParamMap.get('page');
1388
+ if (!previousPage) {
1389
+ AXPDebugService.instance?.trace(DETAILS_VIEW_PAGE_TRACE_NS, 'viewmodel', 'setCurrentPage — ignored (initial load in progress)', () => pageQueryTraceContext({
1390
+ requestedPageId: page.id,
1391
+ }));
1392
+ return;
1393
+ }
1394
+ const pageParam = readPageQueryParam();
1134
1395
  const isSamePage = previousPage?.id === page.id && pageParam === page.id;
1135
1396
  if (!isSamePage && axpIsEntityDetailsViewRoute(router)) {
1397
+ AXPDebugService.instance?.trace(DETAILS_VIEW_PAGE_TRACE_NS, 'viewmodel', 'setCurrentPage — navigate', () => pageQueryTraceContext({
1398
+ navigateToPageId: page.id,
1399
+ }));
1136
1400
  const queryParams = { page: page.id };
1137
1401
  if (previousPage?.id && previousPage.id !== page.id) {
1138
1402
  queryParams['filters'] = null;
@@ -1144,6 +1408,7 @@ const AXPLayoutDetailsViewViewModel = signalStore(withState(() => {
1144
1408
  // Push history for desktop sidebar switches and mobile page-list picks.
1145
1409
  replaceUrl: false,
1146
1410
  });
1411
+ return;
1147
1412
  }
1148
1413
  void activatePageById(page.id);
1149
1414
  }
@@ -1274,13 +1539,16 @@ const AXPLayoutDetailsViewViewModel = signalStore(withState(() => {
1274
1539
  const snapshot = cloneDeep(context);
1275
1540
  patchState(store, {
1276
1541
  context: snapshot,
1542
+ savedContext: snapshot,
1277
1543
  previousContext: snapshot,
1278
1544
  formDirty: false,
1279
1545
  });
1280
1546
  },
1281
1547
  async discard() {
1548
+ const saved = cloneDeep(store.savedContext());
1282
1549
  patchState(store, {
1283
- context: cloneDeep(store.previousContext()),
1550
+ context: saved,
1551
+ previousContext: saved,
1284
1552
  formDirty: false,
1285
1553
  });
1286
1554
  },
@@ -1295,10 +1563,6 @@ const AXPLayoutDetailsViewViewModel = signalStore(withState(() => {
1295
1563
  });
1296
1564
  try {
1297
1565
  const result = await store.currentPage()?.execute?.(command, payload);
1298
- // Clear submitting before post-save refresh so the footer is not left disabled.
1299
- patchState(store, {
1300
- status: AXPPageStatus.Rendered,
1301
- });
1302
1566
  const enableOperationToasts = await settingsService.get(AXPCommonSettings.EnableOperationToasts);
1303
1567
  if (result?.success) {
1304
1568
  try {
@@ -1309,6 +1573,7 @@ const AXPLayoutDetailsViewViewModel = signalStore(withState(() => {
1309
1573
  }
1310
1574
  patchState(store, {
1311
1575
  previousContext: store.context(),
1576
+ savedContext: cloneDeep(store.context()),
1312
1577
  formDirty: false,
1313
1578
  });
1314
1579
  if (enableOperationToasts) {
@@ -1322,9 +1587,6 @@ const AXPLayoutDetailsViewViewModel = signalStore(withState(() => {
1322
1587
  timeOutProgress: true,
1323
1588
  });
1324
1589
  }
1325
- patchState(store, {
1326
- formDirty: false,
1327
- });
1328
1590
  return true;
1329
1591
  }
1330
1592
  const rawText = result?.message?.text;
@@ -1350,9 +1612,6 @@ const AXPLayoutDetailsViewViewModel = signalStore(withState(() => {
1350
1612
  const errorText = error instanceof Error
1351
1613
  ? error.message
1352
1614
  : await translateService.translateAsync('@general:messages.generic.error.description');
1353
- patchState(store, {
1354
- status: AXPPageStatus.Rendered,
1355
- });
1356
1615
  toastService.show({
1357
1616
  color: 'danger',
1358
1617
  title: await translateService.translateAsync('@general:messages.generic.error.title'),
@@ -1365,17 +1624,15 @@ const AXPLayoutDetailsViewViewModel = signalStore(withState(() => {
1365
1624
  return false;
1366
1625
  }
1367
1626
  finally {
1368
- if (store.status() === AXPPageStatus.Submitting) {
1369
- patchState(store, {
1370
- status: AXPPageStatus.Rendered,
1371
- });
1372
- }
1627
+ patchState(store, {
1628
+ status: AXPPageStatus.Rendered,
1629
+ });
1373
1630
  }
1374
1631
  },
1375
- goToListPage() {
1632
+ async goToListPage() {
1376
1633
  const exitUrl = store.adapter()?.exitUrl;
1377
1634
  if (exitUrl) {
1378
- void axpNavigateAppPath(router, exitUrl);
1635
+ await axpNavigateAppPath(router, exitUrl);
1379
1636
  }
1380
1637
  },
1381
1638
  };
@@ -1390,6 +1647,8 @@ const DEFAULT_FOOTER_COMMAND_SHORTCUTS = {
1390
1647
  'update-entity': ['ctrl+s'],
1391
1648
  discard: ['escape'],
1392
1649
  };
1650
+ const SAVE_FOOTER_COMMAND_SUFFIX = ':Save';
1651
+ const DISCARD_FOOTER_COMMAND_SUFFIX = ':Discard';
1393
1652
  /**
1394
1653
  * Returns the base workflow/command name from a composite entity action command id (`cmd&actionName`).
1395
1654
  */
@@ -1420,7 +1679,34 @@ function resolveDetailsViewFooterCommandShortcuts(commandName, explicit) {
1420
1679
  return chords.length ? chords : undefined;
1421
1680
  }
1422
1681
  const defaults = DEFAULT_FOOTER_COMMAND_SHORTCUTS[commandName];
1423
- return defaults?.length ? [...defaults] : undefined;
1682
+ if (defaults?.length) {
1683
+ return [...defaults];
1684
+ }
1685
+ return resolvePageFooterActionDefaultShortcuts(commandName);
1686
+ }
1687
+ /**
1688
+ * Platform defaults for component-page footer save/discard commands (e.g. `MetaDataBuilder:Save`).
1689
+ */
1690
+ function resolvePageFooterActionDefaultShortcuts(commandName) {
1691
+ if (isSaveFooterCommand(commandName)) {
1692
+ return [...DEFAULT_FOOTER_COMMAND_SHORTCUTS['update-entity']];
1693
+ }
1694
+ if (isDiscardFooterCommand(commandName)) {
1695
+ return [...DEFAULT_FOOTER_COMMAND_SHORTCUTS['discard']];
1696
+ }
1697
+ return undefined;
1698
+ }
1699
+ function isSaveFooterCommand(commandName) {
1700
+ return commandName === 'update-entity' || commandName.endsWith(SAVE_FOOTER_COMMAND_SUFFIX);
1701
+ }
1702
+ function isDiscardFooterCommand(commandName) {
1703
+ return commandName === 'discard' || commandName.endsWith(DISCARD_FOOTER_COMMAND_SUFFIX);
1704
+ }
1705
+ /**
1706
+ * Resolves keyboard shortcuts for a footer action on entity detail sub-pages.
1707
+ */
1708
+ function resolvePageFooterActionShortcuts(commandName, explicit) {
1709
+ return resolveDetailsViewFooterCommandShortcuts(commandName, explicit);
1424
1710
  }
1425
1711
  function flattenDetailsViewActionShortcuts(shortcuts) {
1426
1712
  const chords = normalizeKeyboardShortcuts(shortcuts).flatMap((shortcut) => shortcut.keys);
@@ -1579,10 +1865,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
1579
1865
  }]
1580
1866
  }], 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"] }] } });
1581
1867
 
1868
+ /** Registered id of the data-list inside related-entity `page-list` sidebar pages. */
1869
+ const PAGE_LIST_TABLE_WIDGET_ID = 'page-list_table';
1582
1870
  class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
1583
1871
  constructor() {
1584
1872
  super(...arguments);
1585
- this.adapter = input.required(...(ngDevMode ? [{ debugName: "adapter" }] : /* istanbul ignore next */ []));
1586
1873
  //
1587
1874
  this.vm = inject(AXPLayoutDetailsViewViewModel);
1588
1875
  this.route = inject(ActivatedRoute);
@@ -1591,13 +1878,23 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
1591
1878
  this.unsavedChangesPopstate = inject(AXPUnsavedChangesPopstateService);
1592
1879
  this.pageComponentInstanceRegistry = inject(AXPPageComponentInstanceRegistryService);
1593
1880
  this.shortcutRegistry = inject(AXPKeyboardShortcutRegistry);
1881
+ this.workflowService = inject(AXPWorkflowService);
1594
1882
  this.destroyRef = inject(DestroyRef);
1595
1883
  this.elementRef = inject((ElementRef));
1596
- /** Prevents sidebar tab init events from pushing history before {@link ngOnInit} load completes. */
1597
- this.detailsLayoutReady = false;
1884
+ this.debug = inject(AXPDebugService);
1885
+ /** Cancels in-flight list selection bind when the sidebar page or widget tree changes. */
1886
+ this.pageListSelectionBindGeneration = 0;
1887
+ /** Prevents sidebar tab init events from pushing history before the first page activation completes. */
1888
+ this.detailsLayoutReady = signal(false, ...(ngDevMode ? [{ debugName: "detailsLayoutReady" }] : /* istanbul ignore next */ []));
1598
1889
  this.destroyed$ = new Subject();
1599
1890
  this.footerActionsGeneration = 0;
1600
1891
  this.keyboardShortcutRegistrationGeneration = 0;
1892
+ /** Page id the overlay release logic is bound to; reset on sidebar/page switch. */
1893
+ this.overlayTargetPageId = null;
1894
+ /** After a page switch, wait for widget registry to clear before accepting remounted widgets. */
1895
+ this.overlaySawZeroWidgetsAfterPageChange = false;
1896
+ /** Cancels in-flight overlay release when the page changes. */
1897
+ this.overlayReleaseGeneration = 0;
1601
1898
  /** Header actions with resolved shortcut chords, keyed by command name. */
1602
1899
  this.actionShortcutEntries = signal(new Map(), ...(ngDevMode ? [{ debugName: "actionShortcutEntries" }] : /* istanbul ignore next */ []));
1603
1900
  this.form = viewChild('form', ...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
@@ -1616,6 +1913,11 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
1616
1913
  return;
1617
1914
  }
1618
1915
  untracked(() => queueMicrotask(() => {
1916
+ this.debug.trace(DETAILS_VIEW_PAGE_TRACE_NS, 'details-view', 'sidebarTabs select (sync indicator)', () => ({
1917
+ currentPageId: currentId,
1918
+ tabIndex: index,
1919
+ routerUrl: this.router.url,
1920
+ }));
1619
1921
  this.sidebarTabChangeSuppressed = true;
1620
1922
  try {
1621
1923
  this.sidebarTabs()?.select(index);
@@ -1625,6 +1927,150 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
1625
1927
  }
1626
1928
  }));
1627
1929
  }, ...(ngDevMode ? [{ debugName: "#sidebarTabsIndicatorEffect" }] : /* istanbul ignore next */ []));
1930
+ /**
1931
+ * Binds list selection whenever `page-list_table` registers (including after remount).
1932
+ * `find()` / `waitForWidget()` can return a destroyed instance; registration always carries the live widget.
1933
+ */
1934
+ this.#pageListSelectionBindEffect = effect((onCleanup) => {
1935
+ const pageId = this.vm.currentPage()?.id;
1936
+ const isPageReady = this.vm.isPageReady();
1937
+ if (!pageId || this.vm.showPages() || !isPageReady || this.getActivePageComponentKey()) {
1938
+ return;
1939
+ }
1940
+ this.vm.currentPageContent();
1941
+ if (!this.pageHasEntityListWidget()) {
1942
+ return;
1943
+ }
1944
+ const generation = ++this.pageListSelectionBindGeneration;
1945
+ const container = this.widgetContainer();
1946
+ if (!container) {
1947
+ return;
1948
+ }
1949
+ const builder = container.builderService;
1950
+ onCleanup(() => {
1951
+ this.onSelectionChangeSubscription?.unsubscribe();
1952
+ this.onSelectionChangeSubscription = undefined;
1953
+ });
1954
+ const registrationSub = builder.widgetRegistered$.subscribe(({ id, widget }) => {
1955
+ if (id !== PAGE_LIST_TABLE_WIDGET_ID) {
1956
+ return;
1957
+ }
1958
+ this.attachSelectionToListWidget(pageId, generation, widget);
1959
+ });
1960
+ onCleanup(() => registrationSub.unsubscribe());
1961
+ const existing = builder.getWidget(PAGE_LIST_TABLE_WIDGET_ID);
1962
+ if (existing) {
1963
+ untracked(() => this.attachSelectionToListWidget(pageId, generation, existing));
1964
+ }
1965
+ }, ...(ngDevMode ? [{ debugName: "#pageListSelectionBindEffect" }] : /* istanbul ignore next */ []));
1966
+ /** Clears the page overlay reactively once VM + widget hydration are both idle. */
1967
+ this.#overlayReleaseEffect = effect(() => {
1968
+ const showPages = this.vm.showPages();
1969
+ const pageId = this.vm.currentPage()?.id;
1970
+ const isVmBusy = this.vm.isOverlayBusy();
1971
+ const isPageReady = this.vm.isPageReady();
1972
+ const container = this.widgetContainer();
1973
+ this.vm.currentPageContent();
1974
+ this.vm.activeTabContent();
1975
+ this.vm.context();
1976
+ container?.builderService.registeredWidgetsCount();
1977
+ container?.builderService.isHydrating();
1978
+ container?.isBusy();
1979
+ container?.isWidgetsReady();
1980
+ const widgetCount = container?.builderService.registeredWidgetsCount() ?? 0;
1981
+ const widgetsBusy = container?.isBusy() ?? false;
1982
+ const widgetsReady = container?.isWidgetsReady() ?? false;
1983
+ untracked(() => {
1984
+ const widgetSnapshot = container?.builderService.getWidgetStateSnapshot();
1985
+ if (pageId !== this.overlayTargetPageId) {
1986
+ const isInitialOverlayBind = this.overlayTargetPageId == null;
1987
+ this.debug.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'details-view', 'page bind changed', () => ({
1988
+ pageId,
1989
+ previousPageId: this.overlayTargetPageId,
1990
+ isInitialOverlayBind,
1991
+ overlayReleaseGeneration: this.overlayReleaseGeneration + 1,
1992
+ }));
1993
+ this.overlayTargetPageId = pageId ?? null;
1994
+ // First bind: widgets mount without a prior page registry. Sidebar switch: require clear first.
1995
+ this.overlaySawZeroWidgetsAfterPageChange = isInitialOverlayBind;
1996
+ this.overlayReleaseGeneration += 1;
1997
+ this.overlayReleasePromise = undefined;
1998
+ this.clearOverlayFallback();
1999
+ }
2000
+ if (showPages) {
2001
+ this.debug.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'details-view', 'showPages — release overlay', () => ({ isVmBusy }));
2002
+ this.overlayReleaseGeneration += 1;
2003
+ this.overlayReleasePromise = undefined;
2004
+ this.clearOverlayFallback();
2005
+ if (!isVmBusy) {
2006
+ this.clearPageOverlay('showPages — release overlay');
2007
+ }
2008
+ return;
2009
+ }
2010
+ if (isVmBusy || !isPageReady || !pageId) {
2011
+ this.debug.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'details-view', 'waiting on VM', () => ({
2012
+ pageId,
2013
+ isVmBusy,
2014
+ isPageReady,
2015
+ }));
2016
+ this.overlayReleaseGeneration += 1;
2017
+ this.overlayReleasePromise = undefined;
2018
+ this.clearOverlayFallback();
2019
+ return;
2020
+ }
2021
+ if (this.getActivePageComponentKey()) {
2022
+ this.debug.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'details-view', 'component page — release overlay', () => ({
2023
+ componentKey: this.getActivePageComponentKey(),
2024
+ }));
2025
+ this.overlayReleaseGeneration += 1;
2026
+ this.overlayReleasePromise = undefined;
2027
+ this.clearOverlayFallback();
2028
+ this.clearPageOverlay('component page — release overlay');
2029
+ return;
2030
+ }
2031
+ if (!this.pageHasWidgetNodes()) {
2032
+ this.debug.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'details-view', 'no widget nodes — release overlay', () => ({ pageId }));
2033
+ this.overlayReleaseGeneration += 1;
2034
+ this.overlayReleasePromise = undefined;
2035
+ this.clearOverlayFallback();
2036
+ this.clearPageOverlay('no widget nodes — release overlay');
2037
+ return;
2038
+ }
2039
+ if (widgetCount === 0) {
2040
+ this.overlaySawZeroWidgetsAfterPageChange = true;
2041
+ this.debug.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'details-view', 'widget registry cleared (remount gate open)', () => ({ pageId }));
2042
+ }
2043
+ if (!this.overlaySawZeroWidgetsAfterPageChange) {
2044
+ this.debug.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'details-view', 'blocked: remount gate (await widgetCount=0)', () => ({
2045
+ pageId,
2046
+ widgetCount,
2047
+ ...widgetSnapshot,
2048
+ }));
2049
+ this.scheduleOverlayFallback();
2050
+ return;
2051
+ }
2052
+ if (!widgetsReady || widgetsBusy) {
2053
+ this.debug.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'details-view', 'blocked: widgets not ready', () => ({
2054
+ pageId,
2055
+ widgetsReady,
2056
+ widgetsBusy,
2057
+ overlaySawZero: this.overlaySawZeroWidgetsAfterPageChange,
2058
+ releaseInFlight: !!this.overlayReleasePromise,
2059
+ ...widgetSnapshot,
2060
+ hydratingWidgets: widgetSnapshot?.widgets?.filter((w) => w.isHydrating).map((w) => w.id),
2061
+ renderingWidgets: widgetSnapshot?.widgets?.filter((w) => w.status === 'rendering').map((w) => w.id),
2062
+ }));
2063
+ this.scheduleOverlayFallback();
2064
+ return;
2065
+ }
2066
+ this.debug.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'details-view', 'request overlay release', () => ({
2067
+ pageId,
2068
+ overlayReleaseGeneration: this.overlayReleaseGeneration,
2069
+ ...widgetSnapshot,
2070
+ }));
2071
+ void this.requestOverlayRelease(pageId);
2072
+ });
2073
+ }, ...(ngDevMode ? [{ debugName: "#overlayReleaseEffect" }] : /* istanbul ignore next */ []));
1628
2074
  this.footerPrimaryActions = signal([], ...(ngDevMode ? [{ debugName: "footerPrimaryActions" }] : /* istanbul ignore next */ []));
1629
2075
  this.footerSecondaryActions = signal([], ...(ngDevMode ? [{ debugName: "footerSecondaryActions" }] : /* istanbul ignore next */ []));
1630
2076
  this.footerActions = computed(() => [...this.footerPrimaryActions(), ...this.footerSecondaryActions()], ...(ngDevMode ? [{ debugName: "footerActions" }] : /* istanbul ignore next */ []));
@@ -1648,7 +2094,14 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
1648
2094
  if (this.vm.currentPage()?.isReadonly || this.getActivePageComponentKey()) {
1649
2095
  return false;
1650
2096
  }
1651
- return this.widgetContainer()?.isFormDirty() ?? false;
2097
+ if (this.vm.isOverlayBusy()) {
2098
+ return false;
2099
+ }
2100
+ const container = this.widgetContainer();
2101
+ if (!container?.isSavedCommitted()) {
2102
+ return false;
2103
+ }
2104
+ return container.isFormDirty();
1652
2105
  }, ...(ngDevMode ? [{ debugName: "liveFormIsDirty" }] : /* istanbul ignore next */ []));
1653
2106
  this.hasVisibleFooterPrimaryActions = computed(() => {
1654
2107
  if (this.showSaveDiscardFooter()) {
@@ -1665,11 +2118,25 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
1665
2118
  if (this.vm.currentPage()?.isReadonly || this.getActivePageComponentKey()) {
1666
2119
  return;
1667
2120
  }
1668
- const isDirty = this.liveFormIsDirty();
2121
+ if (this.vm.isOverlayBusy()) {
2122
+ return;
2123
+ }
2124
+ const container = this.widgetContainer();
2125
+ if (!container?.isSavedCommitted()) {
2126
+ if (this.vm.formDirty()) {
2127
+ untracked(() => this.vm.setFormDirty(false));
2128
+ }
2129
+ return;
2130
+ }
2131
+ const isDirty = container.isFormDirty();
1669
2132
  if (this.vm.formDirty() === isDirty) {
1670
2133
  return;
1671
2134
  }
1672
2135
  untracked(() => {
2136
+ this.debug.trace(FORM_DIRTY_TRACE_NS, 'details-view', 'formDirty effect', () => ({
2137
+ isDirty,
2138
+ vmFormDirty: this.vm.formDirty(),
2139
+ }));
1673
2140
  this.vm.setFormDirty(isDirty);
1674
2141
  this.recompute();
1675
2142
  });
@@ -1685,18 +2152,21 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
1685
2152
  const generation = ++this.footerActionsGeneration;
1686
2153
  this.updateTrigger();
1687
2154
  this.liveFormIsDirty();
1688
- this.vm.isSaving();
2155
+ this.vm.isOverlayBusy();
1689
2156
  void this.refreshFooterActions(generation);
1690
2157
  }, ...(ngDevMode ? [{ debugName: "#FooterActionsEffect" }] : /* istanbul ignore next */ []));
1691
2158
  this.#keyboardShortcutsEffect = effect(() => {
1692
2159
  const generation = ++this.keyboardShortcutRegistrationGeneration;
1693
2160
  this.updateTrigger();
1694
2161
  this.liveFormIsDirty();
1695
- this.vm.isSaving();
2162
+ this.isActivePageComponentDirty();
2163
+ this.vm.isOverlayBusy();
1696
2164
  this.vm.currentPage()?.id;
1697
2165
  untracked(() => void this.refreshKeyboardShortcutRegistrations(generation));
1698
2166
  }, ...(ngDevMode ? [{ debugName: "#keyboardShortcutsEffect" }] : /* istanbul ignore next */ []));
1699
2167
  }
2168
+ /** Safety cap when widget ready detection does not settle. */
2169
+ static { this.overlayMaxWaitMs = 30_000; }
1700
2170
  /** Save/discard use live {@link AXPLayoutDetailsViewViewModel.isDirty}; other actions use evaluated `visible`. */
1701
2171
  static isTruthyActionFlag(value) {
1702
2172
  if (value === true) {
@@ -1723,6 +2193,13 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
1723
2193
  }
1724
2194
  /** Syncs the line indicator when the active page changes programmatically. */
1725
2195
  #sidebarTabsIndicatorEffect;
2196
+ /**
2197
+ * Binds list selection whenever `page-list_table` registers (including after remount).
2198
+ * `find()` / `waitForWidget()` can return a destroyed instance; registration always carries the live widget.
2199
+ */
2200
+ #pageListSelectionBindEffect;
2201
+ /** Clears the page overlay reactively once VM + widget hydration are both idle. */
2202
+ #overlayReleaseEffect;
1726
2203
  isSaveDiscardFooterAction(action) {
1727
2204
  const commandName = action.command?.name;
1728
2205
  const acceptCommand = this.vm.currentPage()?.settings?.commands?.accept?.command?.name;
@@ -1741,35 +2218,43 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
1741
2218
  return AXPLayoutDetailsViewComponent.isTruthyActionFlag(action.visible);
1742
2219
  }
1743
2220
  isFooterActionDisabled(action) {
1744
- if (this.vm.isSaving()) {
2221
+ if (this.vm.isOverlayBusy()) {
1745
2222
  return true;
1746
2223
  }
1747
2224
  return AXPLayoutDetailsViewComponent.isTruthyActionFlag(action.disabled);
1748
2225
  }
1749
2226
  /**
1750
- * Append timestamp query param to trigger router to re-run guards/resolvers
2227
+ * Re-fetches the entity adapter and reloads the active page.
1751
2228
  */
1752
2229
  async refreshLayout() {
1753
- const router = this.router;
1754
- const route = this.route;
1755
- const now = Date.now();
1756
2230
  this.unsavedChangesConfirm.markLeaveConfirmed();
1757
- await router.navigate([], {
1758
- relativeTo: route,
1759
- queryParams: { _ts: now },
1760
- queryParamsHandling: 'merge',
1761
- replaceUrl: true,
1762
- });
2231
+ this.detailsLayoutReady.set(false);
2232
+ try {
2233
+ await this.vm.loadFromRoute(true);
2234
+ }
2235
+ finally {
2236
+ this.detailsLayoutReady.set(true);
2237
+ }
1763
2238
  }
1764
2239
  async ngOnInit() {
1765
2240
  await super.ngOnInit();
2241
+ this.debug.trace(DETAILS_VIEW_PAGE_TRACE_NS, 'details-view', 'ngOnInit — before loadFromRoute', () => ({
2242
+ routerUrl: this.router.url,
2243
+ pageFromRouterUrl: this.router.parseUrl(this.router.url).queryParams['page'] ?? null,
2244
+ pageFromRouteSnapshot: this.route.snapshot.queryParamMap.get('page'),
2245
+ }));
1766
2246
  this.unsavedChangesPopstate.registerSource({
1767
2247
  id: 'axp-layout-details-view',
1768
- isDirty: () => this.shouldPromptLeave(),
2248
+ isDirty: () => this.hasUnsavedChanges(),
1769
2249
  });
2250
+ this.destroyRef.onDestroy(() => this.unsavedChangesPopstate.unregisterSource('axp-layout-details-view'));
1770
2251
  //
1771
- await this.vm.load(this.adapter());
1772
- this.detailsLayoutReady = true;
2252
+ await this.vm.loadFromRoute();
2253
+ this.debug.trace(DETAILS_VIEW_PAGE_TRACE_NS, 'details-view', 'ngOnInit — after loadFromRoute', () => ({
2254
+ routerUrl: this.router.url,
2255
+ currentPageId: this.vm.currentPage()?.id ?? null,
2256
+ }));
2257
+ this.detailsLayoutReady.set(true);
1773
2258
  void this.refreshKeyboardShortcutRegistrations(++this.keyboardShortcutRegistrationGeneration);
1774
2259
  this.eventService
1775
2260
  //TODO: Use the constant from the entity module
@@ -1780,23 +2265,45 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
1780
2265
  this.refreshLayout();
1781
2266
  }
1782
2267
  });
2268
+ this.workflowService.events$
2269
+ .pipe(filter$1((event) => event.type === AXP_ENTITY_DELETED_WORKFLOW_EVENT_TYPE), takeUntil(this.destroyed$))
2270
+ .subscribe((event) => {
2271
+ void this.handleEntityDeleted(event.payload);
2272
+ });
1783
2273
  }
1784
- async ngAfterViewInit() {
1785
- const listWidget = (await this.widgetContainer()?.find('page-list_table'));
1786
- if (listWidget?.api && typeof listWidget.api === 'function') {
1787
- const onSelectionChange = listWidget.api()['onSelectionChange'];
1788
- if (onSelectionChange) {
1789
- this.onSelectionChangeSubscription = onSelectionChange.subscribe((e) => {
1790
- this.vm.updatePageSelectedRows(e);
1791
- this.recompute();
1792
- });
2274
+ /** Navigates to {@link AXPLayoutDetailsViewAdapter.exitUrl} when the open record was deleted. */
2275
+ async handleEntityDeleted(payload) {
2276
+ if (!payload?.entity || !payload.ids?.length) {
2277
+ return;
2278
+ }
2279
+ const adapterName = this.vm.adapter()?.name;
2280
+ const recordId = this.vm.context()?.['id'];
2281
+ if (!adapterName || recordId == null) {
2282
+ return;
2283
+ }
2284
+ const deletedIds = payload.ids.map((id) => String(id));
2285
+ if (payload.entity !== adapterName || !deletedIds.includes(String(recordId))) {
2286
+ return;
2287
+ }
2288
+ this.unsavedChangesConfirm.markLeaveConfirmed();
2289
+ if (this.tabNavigation.effectiveMode() === 'tabular') {
2290
+ const tabKey = this.getPageTabKey();
2291
+ await this.vm.goToListPage();
2292
+ if (tabKey) {
2293
+ this.unsavedChangesConfirm.markLeaveConfirmed();
2294
+ await this.tabNavigation.closeTab(tabKey);
1793
2295
  }
2296
+ return;
1794
2297
  }
2298
+ await this.vm.goToListPage();
1795
2299
  }
1796
2300
  ngOnDestroy() {
1797
- this.unsavedChangesPopstate.unregisterSource('axp-layout-details-view');
1798
2301
  this.onSelectionChangeSubscription?.unsubscribe();
1799
2302
  ++this.keyboardShortcutRegistrationGeneration;
2303
+ this.clearOverlayFallback();
2304
+ this.overlayReleaseGeneration += 1;
2305
+ this.overlayReleasePromise = undefined;
2306
+ this.clearPageOverlay('ngOnDestroy');
1800
2307
  this.destroyed$.next();
1801
2308
  this.destroyed$.complete();
1802
2309
  }
@@ -1816,29 +2323,196 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
1816
2323
  this.footerPrimaryActions.set(footerPrimaryActions);
1817
2324
  this.footerSecondaryActions.set(footerSecondaryActions);
1818
2325
  }
2326
+ /** Whether the active page hosts a related-entity list (`page-list` widget). */
2327
+ pageHasEntityListWidget() {
2328
+ return this.vm.currentPageContent().some((item) => {
2329
+ if ('type' in item && item.type === 'component') {
2330
+ return false;
2331
+ }
2332
+ return item.name === 'page-list';
2333
+ });
2334
+ }
2335
+ /** Subscribes to the live data-list selection stream for Selected-scope header actions. */
2336
+ attachSelectionToListWidget(pageId, generation, widget) {
2337
+ if (generation !== this.pageListSelectionBindGeneration || this.vm.currentPage()?.id !== pageId) {
2338
+ return;
2339
+ }
2340
+ const selectionStream = widget.api()['onSelectionChange'];
2341
+ if (!selectionStream || typeof selectionStream.subscribe !== 'function') {
2342
+ return;
2343
+ }
2344
+ this.onSelectionChangeSubscription?.unsubscribe();
2345
+ this.onSelectionChangeSubscription = selectionStream.subscribe((rows) => {
2346
+ this.vm.updatePageSelectedRows(rows);
2347
+ this.recompute();
2348
+ });
2349
+ }
1819
2350
  handleOnContextChanged(e) {
1820
- this.vm.setFormDirty(e.isFormDirty ?? false);
2351
+ if (!this.vm.currentPage()?.isReadonly &&
2352
+ !this.getActivePageComponentKey() &&
2353
+ this.widgetContainer()?.isSavedCommitted()) {
2354
+ const isDirty = e.isFormDirty ?? false;
2355
+ const container = this.widgetContainer();
2356
+ this.debug.trace(FORM_DIRTY_TRACE_NS, 'details-view', 'handleOnContextChanged', () => ({
2357
+ isFormDirty: isDirty,
2358
+ vmFormDirty: this.vm.formDirty(),
2359
+ changePath: e.path,
2360
+ changeOrigin: e.origin,
2361
+ userEditedSinceCommit: container?.contextService.hasUserEditedSinceCommit(),
2362
+ dirtyPaths: container
2363
+ ? summarizeFormContextDirtyDiff(container.contextService.data(), container.contextService.saved())
2364
+ : [],
2365
+ }));
2366
+ if (this.vm.formDirty() !== isDirty) {
2367
+ this.vm.setFormDirty(isDirty);
2368
+ }
2369
+ }
1821
2370
  this.recompute();
1822
2371
  }
1823
2372
  getLiveFormContext() {
1824
2373
  const container = this.widgetContainer();
1825
2374
  return (container?.contextService.data() ?? this.vm.context());
1826
2375
  }
2376
+ /** Whether the active page renders widget nodes (not only hosted components). */
2377
+ pageHasWidgetNodes() {
2378
+ const pageWidgets = this.vm.currentPageContent();
2379
+ if (pageWidgets.some((item) => !('type' in item && item.type === 'component'))) {
2380
+ return true;
2381
+ }
2382
+ return this.vm.activeTabContent().length > 0;
2383
+ }
2384
+ /** Clears all overlay loading tasks once the page (or hosted view) is ready. */
2385
+ clearPageOverlay(reason) {
2386
+ this.debug.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'details-view', reason, () => ({
2387
+ taskCount: this.layoutService.overlayTaskCount(),
2388
+ }));
2389
+ this.layoutService.clearOverlayLoading();
2390
+ }
2391
+ scheduleOverlayFallback() {
2392
+ if (this.overlayFallbackTimer) {
2393
+ return;
2394
+ }
2395
+ this.debug.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'details-view', 'schedule fallback timer', () => ({
2396
+ timeoutMs: AXPLayoutDetailsViewComponent.overlayMaxWaitMs,
2397
+ pageId: this.overlayTargetPageId,
2398
+ }));
2399
+ this.overlayFallbackTimer = setTimeout(() => {
2400
+ this.overlayFallbackTimer = undefined;
2401
+ const isOverlayLoading = this.layoutService.isOverlayLoading();
2402
+ const isVmBusy = this.vm.isOverlayBusy();
2403
+ this.debug.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'details-view', 'fallback timer fired', () => ({
2404
+ isOverlayLoading,
2405
+ isVmBusy,
2406
+ pageId: this.overlayTargetPageId,
2407
+ }));
2408
+ if (!isOverlayLoading) {
2409
+ return;
2410
+ }
2411
+ if (!isVmBusy) {
2412
+ this.debug.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'details-view', 'fallback forcing overlay off', () => ({
2413
+ taskCount: this.layoutService.overlayTaskCount(),
2414
+ }));
2415
+ this.clearPageOverlay('fallback forcing overlay off');
2416
+ }
2417
+ }, AXPLayoutDetailsViewComponent.overlayMaxWaitMs);
2418
+ }
2419
+ clearOverlayFallback() {
2420
+ if (this.overlayFallbackTimer) {
2421
+ clearTimeout(this.overlayFallbackTimer);
2422
+ this.overlayFallbackTimer = undefined;
2423
+ }
2424
+ }
2425
+ /**
2426
+ * Hides overlay after the widget container reports continuous idle (same gate as saved baseline).
2427
+ */
2428
+ requestOverlayRelease(pageId) {
2429
+ if (this.overlayReleasePromise) {
2430
+ this.debug.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'details-view', 'release skipped — already in flight', () => ({ pageId }));
2431
+ return;
2432
+ }
2433
+ const generation = this.overlayReleaseGeneration;
2434
+ const container = this.widgetContainer();
2435
+ if (!container) {
2436
+ this.debug.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'details-view', 'release skipped — no container', () => ({ pageId }));
2437
+ this.scheduleOverlayFallback();
2438
+ return;
2439
+ }
2440
+ this.debug.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'details-view', 'waitUntilSettled started', () => ({ pageId, generation }));
2441
+ this.overlayReleasePromise = container
2442
+ .waitUntilSettled()
2443
+ .then(() => {
2444
+ const latest = this.widgetContainer();
2445
+ const snapshot = latest?.builderService.getWidgetStateSnapshot();
2446
+ if (generation !== this.overlayReleaseGeneration) {
2447
+ this.debug.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'details-view', 'release aborted — generation mismatch', () => ({
2448
+ pageId,
2449
+ generation,
2450
+ currentGeneration: this.overlayReleaseGeneration,
2451
+ }));
2452
+ return;
2453
+ }
2454
+ if (pageId !== this.overlayTargetPageId || pageId !== this.vm.currentPage()?.id) {
2455
+ this.debug.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'details-view', 'release aborted — page changed', () => ({
2456
+ pageId,
2457
+ overlayTargetPageId: this.overlayTargetPageId,
2458
+ currentPageId: this.vm.currentPage()?.id,
2459
+ }));
2460
+ return;
2461
+ }
2462
+ if (this.vm.isOverlayBusy()) {
2463
+ this.debug.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'details-view', 'release aborted — VM still busy', () => ({ pageId }));
2464
+ return;
2465
+ }
2466
+ if (!latest?.isWidgetsReady() || latest.isBusy()) {
2467
+ this.debug.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'details-view', 'release aborted — widgets not ready after settle', () => ({
2468
+ pageId,
2469
+ widgetsReady: latest?.isWidgetsReady(),
2470
+ widgetsBusy: latest?.isBusy(),
2471
+ ...snapshot,
2472
+ }));
2473
+ return;
2474
+ }
2475
+ this.debug.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'details-view', 'release overlay OFF', () => ({
2476
+ pageId,
2477
+ generation,
2478
+ taskCount: this.layoutService.overlayTaskCount(),
2479
+ ...snapshot,
2480
+ }));
2481
+ this.clearOverlayFallback();
2482
+ this.clearPageOverlay('widgets settled — release overlay');
2483
+ })
2484
+ .finally(() => {
2485
+ this.overlayReleasePromise = undefined;
2486
+ });
2487
+ }
1827
2488
  async finalizeSave() {
1828
2489
  const container = this.widgetContainer();
1829
2490
  if (!container) {
1830
2491
  this.vm.setFormDirty(false);
1831
2492
  return;
1832
2493
  }
1833
- const savedContext = this.vm.context();
1834
- await container.replaceContext(savedContext);
1835
- this.vm.applySavedState(container.contextService.data());
2494
+ this.debug.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'details-view', 'finalizeSave overlay ON');
2495
+ this.layoutService.setOverlayLoading(true);
2496
+ try {
2497
+ const savedContext = this.vm.context();
2498
+ await container.replaceContext(savedContext);
2499
+ this.vm.applySavedState(container.contextService.data());
2500
+ if (!this.getActivePageComponentKey()) {
2501
+ await container.settleSavedBaseline();
2502
+ }
2503
+ }
2504
+ finally {
2505
+ this.debug.trace(OVERLAY_DETAILS_VIEW_TRACE_NS, 'details-view', 'finalizeSave overlay OFF');
2506
+ this.layoutService.setOverlayLoading(false);
2507
+ }
1836
2508
  }
1837
2509
  finalizeDiscard() {
2510
+ this.debug.trace(FORM_DIRTY_TRACE_NS, 'details-view', 'finalizeDiscard');
1838
2511
  const container = this.widgetContainer();
1839
2512
  if (container) {
1840
2513
  container.revertAndSettle();
1841
- this.vm.applySavedState(container.contextService.data());
2514
+ const saved = container.contextService.saved();
2515
+ this.vm.applySavedState(saved);
1842
2516
  return;
1843
2517
  }
1844
2518
  void this.vm.discard();
@@ -1852,15 +2526,21 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
1852
2526
  * `ax-tabs` selects on click before we can confirm. Revert synchronously, then confirm + switch.
1853
2527
  */
1854
2528
  handleSidebarTabChanged(event) {
1855
- if (!this.detailsLayoutReady || this.sidebarTabChangeSuppressed) {
2529
+ if (!this.detailsLayoutReady() || this.sidebarTabChangeSuppressed) {
1856
2530
  return;
1857
2531
  }
1858
2532
  const pages = this.vm.adapter()?.pages ?? [];
1859
2533
  const target = pages[event.index];
1860
2534
  const currentId = this.vm.currentPage()?.id;
1861
- if (!target || target.id === currentId) {
2535
+ if (!currentId || !target || target.id === currentId) {
1862
2536
  return;
1863
2537
  }
2538
+ this.debug.trace(DETAILS_VIEW_PAGE_TRACE_NS, 'details-view', 'handleSidebarTabChanged', () => ({
2539
+ routerUrl: this.router.url,
2540
+ targetPageId: target.id,
2541
+ currentPageId: currentId ?? null,
2542
+ tabIndex: event.index,
2543
+ }));
1864
2544
  const activeIndex = pages.findIndex((page) => page.id === currentId);
1865
2545
  if (activeIndex >= 0) {
1866
2546
  this.sidebarTabChangeSuppressed = true;
@@ -1943,13 +2623,7 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
1943
2623
  return this.vm.getBackButton();
1944
2624
  }
1945
2625
  hasUnsavedChanges() {
1946
- if (this.vm.currentPage()?.isReadonly) {
1947
- return false;
1948
- }
1949
- if (this.isActivePageComponentDirty()) {
1950
- return true;
1951
- }
1952
- return this.liveFormIsDirty();
2626
+ return this.shouldPromptLeave();
1953
2627
  }
1954
2628
  async getPageBadge() {
1955
2629
  if (this.vm.currentPage()?.isReadonly) {
@@ -1997,6 +2671,9 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
1997
2671
  }
1998
2672
  /** Background tab close — confirm without discarding (the cached instance is destroyed). */
1999
2673
  onBeforeTabClose() {
2674
+ if (this.unsavedChangesConfirm.hasLeaveConfirmed()) {
2675
+ return Promise.resolve(true);
2676
+ }
2000
2677
  return this.unsavedChangesConfirm.confirmIfDirty(this.shouldPromptLeave(), {
2001
2678
  approveLeaveOnConfirm: false,
2002
2679
  });
@@ -2026,15 +2703,33 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
2026
2703
  return 'type' in item && item.type === 'component';
2027
2704
  }
2028
2705
  pageContentTrackKey(item, index) {
2706
+ const pageId = this.vm.currentPage()?.id ?? 'unknown';
2029
2707
  if (AXPLayoutDetailsViewComponent.isComponentPageContent(item)) {
2030
- return `component:${item.componentKey}`;
2708
+ return `${pageId}:component:${item.componentKey}`;
2031
2709
  }
2032
- return item.name ?? `widget:${index}`;
2710
+ return `${pageId}:${item.name ?? `widget:${index}`}`;
2033
2711
  }
2034
2712
  /** Whether the active component-based entity page has unsaved edits. */
2035
2713
  isActivePageComponentDirty() {
2036
2714
  return this.getActivePageComponentInstance()?.hasUnsavedChanges() ?? false;
2037
2715
  }
2716
+ /**
2717
+ * Widget-form dirty state for leave guards (popstate, canDeactivate).
2718
+ * Unlike {@link liveFormIsDirty}, does not suppress dirty while overlay is busy.
2719
+ */
2720
+ hasWidgetFormUnsavedChanges() {
2721
+ if (this.getActivePageComponentKey()) {
2722
+ return false;
2723
+ }
2724
+ const container = this.widgetContainer();
2725
+ if (!container) {
2726
+ return this.vm.formDirty();
2727
+ }
2728
+ if (container.isSavedCommitted()) {
2729
+ return container.isFormDirty();
2730
+ }
2731
+ return container.contextService.hasUserEditedSinceCommit() || this.vm.formDirty();
2732
+ }
2038
2733
  /** Whether the current details page has user-origin unsaved edits (form or component page). */
2039
2734
  shouldPromptLeave() {
2040
2735
  if (this.vm.currentPage()?.isReadonly) {
@@ -2043,7 +2738,7 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
2043
2738
  if (this.isActivePageComponentDirty()) {
2044
2739
  return true;
2045
2740
  }
2046
- return this.liveFormIsDirty();
2741
+ return this.hasWidgetFormUnsavedChanges();
2047
2742
  }
2048
2743
  /** Single gate for leaving the page or switching details sub-pages when dirty. */
2049
2744
  async confirmLeaveIfDirty() {
@@ -2168,17 +2863,59 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
2168
2863
  this.registerCoreKeyboardShortcuts();
2169
2864
  this.registerActionKeyboardShortcuts(entries);
2170
2865
  }
2866
+ /** i18n scope label for the active details sub-page shortcut group in the help dialog. */
2867
+ getKeyboardShortcutScope() {
2868
+ const page = this.vm.currentPage();
2869
+ if (page?.isPrimary) {
2870
+ return '@general:keyboard-shortcuts.groups.entity-details';
2871
+ }
2872
+ const label = page?.label;
2873
+ if (typeof label === 'string' && label.length > 0) {
2874
+ return label;
2875
+ }
2876
+ return page?.id ?? '@general:keyboard-shortcuts.groups.entity-details';
2877
+ }
2878
+ /** Primary overview page backed by the entity widget form (not a component-hosted sub-page). */
2879
+ isWidgetFormPrimaryPage() {
2880
+ return !!this.vm.currentPage()?.isPrimary && !this.getActivePageComponentKey();
2881
+ }
2882
+ registerDetailsViewShortcutScope(id, shortcuts) {
2883
+ const scope = this.getKeyboardShortcutScope();
2884
+ this.shortcutRegistry.register({
2885
+ owner: this.destroyRef,
2886
+ elementRef: this.elementRef,
2887
+ id,
2888
+ priority: AXPKeyboardShortcutPriority.Page,
2889
+ scope,
2890
+ shortcuts,
2891
+ });
2892
+ }
2171
2893
  async collectActionShortcutEntries() {
2172
2894
  const map = new Map();
2173
2895
  if (this.vm.showPages()) {
2174
2896
  return map;
2175
2897
  }
2176
- const [primaryActions, secondaryActions] = await Promise.all([
2177
- this.getAllPrimaryActions(),
2178
- this.getAllSecondaryActions(),
2898
+ const includeAdapterActions = this.isWidgetFormPrimaryPage();
2899
+ const [adapterPrimaryActions, adapterSecondaryActions, pagePrimaryActions, pageSecondaryActions] = await Promise.all([
2900
+ includeAdapterActions ? this.vm.primaryActions() : Promise.resolve([]),
2901
+ includeAdapterActions ? this.vm.secondaryActions() : Promise.resolve([]),
2902
+ this.vm.currentPagePrimaryActions(),
2903
+ this.vm.currentPageSecondaryActions(),
2179
2904
  ]);
2180
- for (const action of [...primaryActions, ...secondaryActions]) {
2181
- if (action.zone !== 'header' || this.isSaveDiscardFooterAction(action)) {
2905
+ for (const action of [
2906
+ ...adapterPrimaryActions,
2907
+ ...adapterSecondaryActions,
2908
+ ...pagePrimaryActions,
2909
+ ...pageSecondaryActions,
2910
+ ]) {
2911
+ const zone = action.zone ?? 'header';
2912
+ if (zone !== 'header' && zone !== 'footer') {
2913
+ continue;
2914
+ }
2915
+ if (zone === 'footer' && this.isWidgetFormPrimaryPage() && this.isSaveDiscardFooterAction(action)) {
2916
+ continue;
2917
+ }
2918
+ if (zone === 'header' && this.isSaveDiscardFooterAction(action)) {
2182
2919
  continue;
2183
2920
  }
2184
2921
  const commandName = action.command?.name;
@@ -2187,7 +2924,9 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
2187
2924
  continue;
2188
2925
  }
2189
2926
  const commandBaseName = getDetailsViewActionCommandBaseName(commandName);
2190
- const chords = resolveDetailsViewActionShortcuts(commandBaseName, action.shortcuts);
2927
+ const chords = zone === 'footer'
2928
+ ? resolvePageFooterActionShortcuts(commandBaseName, action.shortcuts)
2929
+ : resolveDetailsViewActionShortcuts(commandBaseName, action.shortcuts);
2191
2930
  if (!chords?.length) {
2192
2931
  continue;
2193
2932
  }
@@ -2195,13 +2934,13 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
2195
2934
  command,
2196
2935
  title: typeof action.title === 'string' ? action.title : '',
2197
2936
  chords,
2198
- visible: this.isHeaderActionAvailable(action),
2937
+ visible: this.isShortcutActionAvailable(action),
2199
2938
  disabled: AXPLayoutDetailsViewComponent.isTruthyActionFlag(action.disabled),
2200
2939
  });
2201
2940
  }
2202
2941
  return map;
2203
2942
  }
2204
- isHeaderActionAvailable(action) {
2943
+ isShortcutActionAvailable(action) {
2205
2944
  if (AXPLayoutDetailsViewComponent.isTruthyActionFlag(action.hidden)) {
2206
2945
  return false;
2207
2946
  }
@@ -2210,15 +2949,15 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
2210
2949
  }
2211
2950
  return true;
2212
2951
  }
2213
- /** Whether save/discard core shortcuts are listed in the help dialog (page supports them). */
2952
+ /** Whether save/discard core shortcuts are listed in the help dialog (widget overview page only). */
2214
2953
  isCoreSaveShortcutAvailable() {
2215
- return !this.vm.currentPage()?.isReadonly && !!this.acceptFooterCommand();
2954
+ return this.isWidgetFormPrimaryPage() && !this.vm.currentPage()?.isReadonly && !!this.acceptFooterCommand();
2216
2955
  }
2217
2956
  isCoreDiscardShortcutAvailable() {
2218
- return !this.vm.currentPage()?.isReadonly && !!this.rejectFooterCommand();
2957
+ return this.isWidgetFormPrimaryPage() && !this.vm.currentPage()?.isReadonly && !!this.rejectFooterCommand();
2219
2958
  }
2220
2959
  canExecuteSaveShortcut() {
2221
- if (this.vm.currentPage()?.isReadonly || this.vm.isSaving()) {
2960
+ if (this.vm.currentPage()?.isReadonly || this.vm.isOverlayBusy()) {
2222
2961
  return false;
2223
2962
  }
2224
2963
  if (!this.acceptFooterCommand()) {
@@ -2227,7 +2966,7 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
2227
2966
  return this.shouldPromptLeave();
2228
2967
  }
2229
2968
  canExecuteDiscardShortcut() {
2230
- if (this.vm.currentPage()?.isReadonly || this.vm.isSaving()) {
2969
+ if (this.vm.currentPage()?.isReadonly || this.vm.isOverlayBusy()) {
2231
2970
  return false;
2232
2971
  }
2233
2972
  if (this.unsavedChangesConfirm.isConfirmPending()) {
@@ -2252,8 +2991,31 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
2252
2991
  }
2253
2992
  await this.execute({ name: rejectCommandName });
2254
2993
  }
2994
+ async executeActionShortcut(entry) {
2995
+ const commandName = entry.command.name;
2996
+ if (isDiscardFooterCommand(commandName)) {
2997
+ if (this.vm.currentPage()?.isReadonly || this.vm.isOverlayBusy()) {
2998
+ return;
2999
+ }
3000
+ if (this.unsavedChangesConfirm.isConfirmPending()) {
3001
+ return;
3002
+ }
3003
+ if (!this.shouldPromptLeave()) {
3004
+ return;
3005
+ }
3006
+ const confirmed = await this.unsavedChangesConfirm.confirmIfDirty(true, {
3007
+ title: '@general:messages.unsaved-changes.title',
3008
+ message: '@general:messages.discard-changes.message',
3009
+ approveLeaveOnConfirm: false,
3010
+ });
3011
+ if (!confirmed) {
3012
+ return;
3013
+ }
3014
+ }
3015
+ await this.execute(entry.command);
3016
+ }
2255
3017
  isActionShortcutActive(entryId) {
2256
- if (this.vm.currentPage()?.isReadonly || this.vm.isSaving()) {
3018
+ if (this.vm.currentPage()?.isReadonly || this.vm.isOverlayBusy()) {
2257
3019
  return false;
2258
3020
  }
2259
3021
  const entry = this.actionShortcutEntries().get(entryId);
@@ -2263,18 +3025,23 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
2263
3025
  return entry.visible && !entry.disabled;
2264
3026
  }
2265
3027
  registerCoreKeyboardShortcuts() {
3028
+ if (!this.isWidgetFormPrimaryPage()) {
3029
+ this.registerDetailsViewShortcutScope('layout-details-view-core', []);
3030
+ return;
3031
+ }
2266
3032
  const accept = this.vm.currentPage()?.settings?.commands?.accept;
2267
3033
  const reject = this.vm.currentPage()?.settings?.commands?.reject;
2268
3034
  const acceptCommandName = accept?.command.name ?? 'update-entity';
2269
3035
  const rejectCommandName = reject?.command.name ?? 'discard';
2270
3036
  const saveKeys = resolveDetailsViewFooterCommandShortcuts(acceptCommandName, accept?.shortcuts);
2271
3037
  const discardKeys = resolveDetailsViewFooterCommandShortcuts(rejectCommandName, reject?.shortcuts);
3038
+ const scope = this.getKeyboardShortcutScope();
2272
3039
  const shortcuts = [];
2273
3040
  if (saveKeys?.length && this.isCoreSaveShortcutAvailable()) {
2274
3041
  shortcuts.push({
2275
3042
  keys: saveKeys,
2276
3043
  title: accept?.title ?? '@general:actions.save.title',
2277
- group: '@general:keyboard-shortcuts.groups.entity-details',
3044
+ group: scope,
2278
3045
  allowInEditableFields: true,
2279
3046
  when: () => this.canExecuteSaveShortcut(),
2280
3047
  handler: () => {
@@ -2286,7 +3053,7 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
2286
3053
  shortcuts.push({
2287
3054
  keys: discardKeys,
2288
3055
  title: reject?.title ?? '@general:actions.discard.title',
2289
- group: '@general:keyboard-shortcuts.groups.entity-details',
3056
+ group: scope,
2290
3057
  allowInEditableFields: true,
2291
3058
  when: () => this.canExecuteDiscardShortcut(),
2292
3059
  handler: () => {
@@ -2294,62 +3061,44 @@ class AXPLayoutDetailsViewComponent extends AXPPageLayoutBaseComponent {
2294
3061
  },
2295
3062
  });
2296
3063
  }
2297
- if (shortcuts.length === 0) {
2298
- return;
2299
- }
2300
- this.shortcutRegistry.register({
2301
- owner: this.destroyRef,
2302
- elementRef: this.elementRef,
2303
- id: 'layout-details-view-core',
2304
- priority: AXPKeyboardShortcutPriority.Page,
2305
- scope: '@general:keyboard-shortcuts.groups.entity-details',
2306
- shortcuts,
2307
- });
3064
+ this.registerDetailsViewShortcutScope('layout-details-view-core', shortcuts);
2308
3065
  }
2309
3066
  registerActionKeyboardShortcuts(entries) {
3067
+ const scope = this.getKeyboardShortcutScope();
2310
3068
  const shortcuts = [];
2311
3069
  for (const [entryId, entry] of entries) {
2312
3070
  for (const chord of entry.chords) {
2313
3071
  shortcuts.push({
2314
3072
  keys: [chord],
2315
3073
  title: entry.title,
2316
- group: '@general:keyboard-shortcuts.groups.entity-details',
3074
+ group: scope,
3075
+ allowInEditableFields: entry.command.name ? isSaveFooterCommand(entry.command.name) || isDiscardFooterCommand(entry.command.name) : false,
2317
3076
  when: () => this.isActionShortcutActive(entryId),
2318
3077
  handler: () => {
2319
3078
  if (!this.isActionShortcutActive(entryId)) {
2320
3079
  return;
2321
3080
  }
2322
- void this.execute(entry.command);
3081
+ void this.executeActionShortcut(entry);
2323
3082
  },
2324
3083
  });
2325
3084
  }
2326
3085
  }
2327
- if (shortcuts.length === 0) {
2328
- return;
2329
- }
2330
- this.shortcutRegistry.register({
2331
- owner: this.destroyRef,
2332
- elementRef: this.elementRef,
2333
- id: 'layout-details-view-actions',
2334
- priority: AXPKeyboardShortcutPriority.Page,
2335
- scope: '@general:keyboard-shortcuts.groups.entity-details',
2336
- shortcuts,
2337
- });
3086
+ this.registerDetailsViewShortcutScope('layout-details-view-actions', shortcuts);
2338
3087
  }
2339
3088
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutDetailsViewComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
2340
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPLayoutDetailsViewComponent, isStandalone: true, selector: "axp-layout-details-view", inputs: { adapter: { classPropertyName: "adapter", publicName: "adapter", isSignal: true, isRequired: true, transformFunction: null } }, providers: [
3089
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPLayoutDetailsViewComponent, isStandalone: true, selector: "axp-layout-details-view", providers: [
2341
3090
  {
2342
3091
  provide: AXPPageLayoutBase,
2343
3092
  useExisting: AXPLayoutDetailsViewComponent,
2344
3093
  },
2345
3094
  AXPLayoutDetailsViewViewModel,
2346
- ], viewQueries: [{ propertyName: "form", first: true, predicate: ["form"], descendants: true, isSignal: true }, { propertyName: "widgetContainer", first: true, predicate: AXPWidgetContainerComponent, descendants: true, isSignal: true }, { propertyName: "sidebarTabs", first: true, predicate: ["sidebarTabs"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<axp-page-layout *translate=\"let t\">\n @if (vm.adapter()?.pages?.length! > 1 && vm.isLarge()) {\n <axp-layout-start-side>\n <axp-layout-header>\n <axp-layout-title>{{\n formatService.format(\n vm.showPages() ? (vm.adapter()?.title ?? '') : (vm.adapter()?.label ?? ''),\n 'string',\n vm.rootContext()\n )\n | translate\n | async\n }}</axp-layout-title>\n <axp-layout-description>{{\n formatService.format(vm.adapter()?.description ?? '', 'string', vm.rootContext()) | translate | async\n }}</axp-layout-description>\n </axp-layout-header>\n <axp-layout-content>\n <ax-tabs\n #sidebarTabs\n class=\"axp-vertical-tabs\"\n [look]=\"'with-line-color'\"\n [location]=\"'end'\"\n [fitParent]=\"true\"\n (onActiveTabChanged)=\"handleSidebarTabChanged($event)\"\n >\n @for (item of vm.adapter()?.pages; track item.id) {\n <ax-tab-item\n [key]=\"item.id\"\n [text]=\"(formatService.format(item.label, 'string', vm.rootContext()) | translate | async)!\"\n [active]=\"vm.currentPage()?.id === item.id\"\n >\n <ax-prefix>\n <ax-icon icon=\"far ${{ item.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-tab-item>\n }\n </ax-tabs>\n </axp-layout-content>\n </axp-layout-start-side>\n }\n <!-- Content Section -->\n <axp-page-content class=\"ax-flex ax-flex-row ax-gap-4\">\n @if (vm.showPages()) {\n <axp-layout-list class=\"ax-w-full\">\n @for (item of vm.adapter()?.pages; track $index) {\n <axp-layout-list-item (click)=\"handleSelectPage(item)\">\n <axp-layout-prefix>\n <ax-icon icon=\"far ${{ item.icon }}\" class=\"ax-text-gray-500\"></ax-icon>\n </axp-layout-prefix>\n <axp-layout-content>\n <a class=\"ax-font-semibold\">{{\n formatService.format(item.label, 'string', vm.rootContext()) | translate | async\n }}</a>\n </axp-layout-content>\n <axp-layout-suffix>\n <ax-icon icon=\" far fa-chevron-right\" class=\"ax-text-gray-400\"></ax-icon>\n </axp-layout-suffix>\n </axp-layout-list-item>\n }\n </axp-layout-list>\n } @else {\n @if (vm.isBusy()) {\n <div class=\"ax-flex ax-h-full ax-w-full ax-items-center ax-justify-center\">\n <ax-loading></ax-loading>\n </div>\n } @else {\n <ax-form class=\"ax-h-full ax-w-full\" #form>\n <axp-widgets-container\n #widgetsContainerRef\n [context]=\"vm.context()\"\n (onContextChanged)=\"handleOnContextChanged($event)\"\n >\n <div class=\"ax-flex ax-flex-col ax-w-full ax-gap-4 ax-h-full\">\n @for (item of vm.currentPageContent(); track pageContentTrackKey($any(item), $index)) {\n @if ('type' in item && item.type === 'component') {\n <!-- Component-based content -->\n <div\n axp-page-component-renderer\n [componentKey]=\"$any(item).componentKey\"\n [rootContext]=\"vm.rootContext()\"\n [pageConfig]=\"$any(item).pageConfig\"\n [options]=\"$any(item).options\"\n (componentReady)=\"recompute()\"\n ></div>\n } @else {\n <!-- Widget-based content -->\n <ng-container axp-widget-renderer [node]=\"$any(item)\" [mode]=\"$any(item).mode ?? 'view'\"></ng-container>\n }\n }\n @if (vm.currentPage()?.tabs?.length) {\n <div class=\"axp-horizontal-tabs\">\n <ax-tabs [location]=\"'bottom'\" [fitParent]=\"false\" look=\"classic\">\n @for (tab of vm.currentPageTabs(); track $index) {\n <ax-tab-item\n (onClick)=\"vm.setCurrentTab(tab)\"\n [text]=\"(tab.title | translate | async)!\"\n [active]=\"vm.currentTab()?.id === tab.id\"\n >\n <ax-prefix>\n <ax-icon icon=\"far ${{ tab.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-tab-item>\n }\n </ax-tabs>\n <div class=\"content\">\n @if (vm.currentTab()) {\n @for (content of vm.activeTabContent(); track content.name) {\n <ng-container axp-widget-renderer [node]=\"content\" [mode]=\"content.mode ?? 'view'\">\n </ng-container>\n }\n }\n </div>\n </div>\n }\n </div>\n </axp-widgets-container>\n </ax-form>\n }\n }\n </axp-page-content>\n\n <!-- Footer Section -->\n @if (hasFooter()) {\n <axp-page-footer class=\"--animated\">\n <axp-layout-suffix>\n <!-- secondary footer actions -->\n @if (hasVisibleFooterSecondaryActions()) {\n <ax-button\n [class.ax-sm]=\"deviceService.isSmall()\"\n [iconOnly]=\"deviceService.isSmall()\"\n [text]=\"'@general:terms.interface.actions' | translate | async\"\n [look]=\"deviceService.isSmall() ? 'blank' : 'solid'\"\n [color]=\"'default'\"\n >\n <ax-prefix>\n <i class=\"fa-solid fa-ellipsis-vertical\"></i>\n </ax-prefix>\n <ax-dropdown-panel #panel>\n <ax-button-item-list>\n @for (action of footerSecondaryActions(); track $index) {\n @if (isFooterActionVisible(action)) {\n <ax-button-item\n [text]=\"(t(action.title) | async)!\"\n [color]=\"action.color\"\n [disabled]=\"isFooterActionDisabled(action)\"\n (onClick)=\"execute(action.command!)\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-light {{ action.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n @if (action.break) {\n <ax-divider></ax-divider>\n }\n }\n }\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-button>\n }\n\n @if (hasVisibleFooterPrimaryActions()) {\n @if (showSaveDiscardFooter()) {\n @if (rejectFooterCommand(); as reject) {\n <ax-button\n [class.ax-sm]=\"deviceService.isSmall()\"\n [disabled]=\"vm.isSaving()\"\n [text]=\"(t(reject.title) | async)!\"\n [look]=\"'solid'\"\n [color]=\"reject.color\"\n (onClick)=\"execute(reject.command)\"\n >\n <ax-prefix>\n <i class=\"{{ reject.icon }}\"></i>\n </ax-prefix>\n </ax-button>\n }\n @if (acceptFooterCommand(); as accept) {\n <ax-button\n [class.ax-sm]=\"deviceService.isSmall()\"\n [disabled]=\"vm.isSaving()\"\n [text]=\"(t(accept.title) | async)!\"\n [look]=\"'solid'\"\n [color]=\"accept.color\"\n (onClick)=\"execute(accept.command)\"\n >\n <ax-prefix>\n <i class=\"{{ accept.icon }}\"></i>\n </ax-prefix>\n </ax-button>\n }\n }\n @for (action of footerPrimaryActions(); track $index) {\n @if (isFooterActionVisible(action)) {\n <ax-button\n [class.ax-sm]=\"deviceService.isSmall()\"\n [disabled]=\"isFooterActionDisabled(action)\"\n [text]=\"(t(action.title) | async)!\"\n [look]=\"'solid'\"\n [color]=\"action.color\"\n (onClick)=\"execute(action.command!)\"\n >\n <ax-prefix>\n <i class=\"{{ action.icon }}\"></i>\n </ax-prefix>\n @if (action?.items) {\n <ax-dropdown-panel #panel>\n <ax-button-item-list>\n @for (sub of action?.items; track $index) {\n @if (isFooterActionVisible(sub)) {\n <ax-button-item\n [text]=\"(t(sub.title) | async)!\"\n [color]=\"sub.color\"\n [disabled]=\"isFooterActionDisabled(sub)\"\n (onClick)=\"execute(sub.command!)\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-light {{ sub.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n @if (sub.break) {\n <ax-divider></ax-divider>\n }\n }\n }\n </ax-button-item-list>\n </ax-dropdown-panel>\n }\n </ax-button>\n }\n }\n }\n </axp-layout-suffix>\n </axp-page-footer>\n }\n</axp-page-layout>\n", styles: ["axp-layout-details-view .axp-vertical-tabs{--ax-comp-tabs-default-border-radius: 0}axp-layout-details-view .axp-vertical-tabs ax-tab-item{margin-top:0!important;margin-bottom:0!important;padding-top:.75rem!important;padding-bottom:.75rem!important;font-weight:600!important}axp-layout-details-view .axp-vertical-tabs ax-tab-item>div{display:flex!important;align-items:center!important}axp-layout-details-view .axp-vertical-tabs ax-tab-item>div .ax-tab-item-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}axp-layout-details-view .axp-horizontal-tabs{display:flex;width:100%;flex-direction:column}axp-layout-details-view .axp-horizontal-tabs .content{margin-top:1rem;margin-bottom:1rem;display:block;width:100%}axp-layout-details-view axp-layout-section axp-layout-content{display:flex;width:100%;flex-direction:column}axp-layout-details-view axp-layout-section axp-layout-content>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse));border-style:dashed;--tw-divide-opacity: 1;border-color:rgba(var(--ax-sys-color-border-lightest-surface),var(--tw-divide-opacity, 1))}axp-layout-details-view axp-layout-section axp-layout-content .--item-container{align-items:center;gap:1rem;padding:1rem}@media(min-width:1024px){axp-layout-details-view axp-layout-section axp-layout-content .--item-container{padding-left:1.5rem;padding-right:1.5rem}}@media(min-width:1280px){axp-layout-details-view axp-layout-section axp-layout-content .--item-container{padding-left:2rem;padding-right:2rem}}@media(min-width:1536px){axp-layout-details-view axp-layout-section axp-layout-content .--item-container{padding-left:2.5rem;padding-right:2.5rem}}axp-layout-details-view axp-layout-section axp-layout-content .--item-container{display:grid;grid-template-columns:repeat(12,minmax(0,1fr))}axp-layout-details-view axp-layout-section axp-layout-content .--item-container axp-layout-description{margin-top:.5rem!important;font-size:.75rem!important;line-height:1rem!important}axp-layout-details-view axp-page-content ax-form form{height:100%}axp-layout-details-view axp-layout-start-side{border-inline-end-width:1px}\n"], dependencies: [{ kind: "ngmodule", type:
3095
+ ], 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:
2347
3096
  // Angular
2348
3097
  CommonModule }, { kind: "ngmodule", type:
2349
3098
  // ACoreX
2350
- 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: AXLoadingModule }, { kind: "component", type: i5$1.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { 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: i6$1.AXTabsComponent, selector: "ax-tabs", inputs: ["look", "location", "fitParent", "minWidth", "content"], outputs: ["onActiveTabChanged"] }, { kind: "component", type: i6$1.AXTabItemComponent, selector: "ax-tab-item", inputs: ["disabled", "text", "key", "headerTemplate", "active"], outputs: ["disabledChange", "onClick", "onBlur", "onFocus", "activeChange"] }, { kind: "component", type:
3099
+ 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:
2351
3100
  //
2352
- 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: i7.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "directive", type: i7.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: i8$1.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 }); }
3101
+ 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 }); }
2353
3102
  }
2354
3103
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutDetailsViewComponent, decorators: [{
2355
3104
  type: Component,
@@ -2364,7 +3113,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
2364
3113
  AXDropdownModule,
2365
3114
  AXTranslationModule,
2366
3115
  AXDecoratorModule,
2367
- AXLoadingModule,
2368
3116
  AXBreadcrumbsModule,
2369
3117
  AXBadgeModule,
2370
3118
  AXSearchBoxModule,
@@ -2389,8 +3137,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
2389
3137
  useExisting: AXPLayoutDetailsViewComponent,
2390
3138
  },
2391
3139
  AXPLayoutDetailsViewViewModel,
2392
- ], template: "<axp-page-layout *translate=\"let t\">\n @if (vm.adapter()?.pages?.length! > 1 && vm.isLarge()) {\n <axp-layout-start-side>\n <axp-layout-header>\n <axp-layout-title>{{\n formatService.format(\n vm.showPages() ? (vm.adapter()?.title ?? '') : (vm.adapter()?.label ?? ''),\n 'string',\n vm.rootContext()\n )\n | translate\n | async\n }}</axp-layout-title>\n <axp-layout-description>{{\n formatService.format(vm.adapter()?.description ?? '', 'string', vm.rootContext()) | translate | async\n }}</axp-layout-description>\n </axp-layout-header>\n <axp-layout-content>\n <ax-tabs\n #sidebarTabs\n class=\"axp-vertical-tabs\"\n [look]=\"'with-line-color'\"\n [location]=\"'end'\"\n [fitParent]=\"true\"\n (onActiveTabChanged)=\"handleSidebarTabChanged($event)\"\n >\n @for (item of vm.adapter()?.pages; track item.id) {\n <ax-tab-item\n [key]=\"item.id\"\n [text]=\"(formatService.format(item.label, 'string', vm.rootContext()) | translate | async)!\"\n [active]=\"vm.currentPage()?.id === item.id\"\n >\n <ax-prefix>\n <ax-icon icon=\"far ${{ item.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-tab-item>\n }\n </ax-tabs>\n </axp-layout-content>\n </axp-layout-start-side>\n }\n <!-- Content Section -->\n <axp-page-content class=\"ax-flex ax-flex-row ax-gap-4\">\n @if (vm.showPages()) {\n <axp-layout-list class=\"ax-w-full\">\n @for (item of vm.adapter()?.pages; track $index) {\n <axp-layout-list-item (click)=\"handleSelectPage(item)\">\n <axp-layout-prefix>\n <ax-icon icon=\"far ${{ item.icon }}\" class=\"ax-text-gray-500\"></ax-icon>\n </axp-layout-prefix>\n <axp-layout-content>\n <a class=\"ax-font-semibold\">{{\n formatService.format(item.label, 'string', vm.rootContext()) | translate | async\n }}</a>\n </axp-layout-content>\n <axp-layout-suffix>\n <ax-icon icon=\" far fa-chevron-right\" class=\"ax-text-gray-400\"></ax-icon>\n </axp-layout-suffix>\n </axp-layout-list-item>\n }\n </axp-layout-list>\n } @else {\n @if (vm.isBusy()) {\n <div class=\"ax-flex ax-h-full ax-w-full ax-items-center ax-justify-center\">\n <ax-loading></ax-loading>\n </div>\n } @else {\n <ax-form class=\"ax-h-full ax-w-full\" #form>\n <axp-widgets-container\n #widgetsContainerRef\n [context]=\"vm.context()\"\n (onContextChanged)=\"handleOnContextChanged($event)\"\n >\n <div class=\"ax-flex ax-flex-col ax-w-full ax-gap-4 ax-h-full\">\n @for (item of vm.currentPageContent(); track pageContentTrackKey($any(item), $index)) {\n @if ('type' in item && item.type === 'component') {\n <!-- Component-based content -->\n <div\n axp-page-component-renderer\n [componentKey]=\"$any(item).componentKey\"\n [rootContext]=\"vm.rootContext()\"\n [pageConfig]=\"$any(item).pageConfig\"\n [options]=\"$any(item).options\"\n (componentReady)=\"recompute()\"\n ></div>\n } @else {\n <!-- Widget-based content -->\n <ng-container axp-widget-renderer [node]=\"$any(item)\" [mode]=\"$any(item).mode ?? 'view'\"></ng-container>\n }\n }\n @if (vm.currentPage()?.tabs?.length) {\n <div class=\"axp-horizontal-tabs\">\n <ax-tabs [location]=\"'bottom'\" [fitParent]=\"false\" look=\"classic\">\n @for (tab of vm.currentPageTabs(); track $index) {\n <ax-tab-item\n (onClick)=\"vm.setCurrentTab(tab)\"\n [text]=\"(tab.title | translate | async)!\"\n [active]=\"vm.currentTab()?.id === tab.id\"\n >\n <ax-prefix>\n <ax-icon icon=\"far ${{ tab.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-tab-item>\n }\n </ax-tabs>\n <div class=\"content\">\n @if (vm.currentTab()) {\n @for (content of vm.activeTabContent(); track content.name) {\n <ng-container axp-widget-renderer [node]=\"content\" [mode]=\"content.mode ?? 'view'\">\n </ng-container>\n }\n }\n </div>\n </div>\n }\n </div>\n </axp-widgets-container>\n </ax-form>\n }\n }\n </axp-page-content>\n\n <!-- Footer Section -->\n @if (hasFooter()) {\n <axp-page-footer class=\"--animated\">\n <axp-layout-suffix>\n <!-- secondary footer actions -->\n @if (hasVisibleFooterSecondaryActions()) {\n <ax-button\n [class.ax-sm]=\"deviceService.isSmall()\"\n [iconOnly]=\"deviceService.isSmall()\"\n [text]=\"'@general:terms.interface.actions' | translate | async\"\n [look]=\"deviceService.isSmall() ? 'blank' : 'solid'\"\n [color]=\"'default'\"\n >\n <ax-prefix>\n <i class=\"fa-solid fa-ellipsis-vertical\"></i>\n </ax-prefix>\n <ax-dropdown-panel #panel>\n <ax-button-item-list>\n @for (action of footerSecondaryActions(); track $index) {\n @if (isFooterActionVisible(action)) {\n <ax-button-item\n [text]=\"(t(action.title) | async)!\"\n [color]=\"action.color\"\n [disabled]=\"isFooterActionDisabled(action)\"\n (onClick)=\"execute(action.command!)\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-light {{ action.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n @if (action.break) {\n <ax-divider></ax-divider>\n }\n }\n }\n </ax-button-item-list>\n </ax-dropdown-panel>\n </ax-button>\n }\n\n @if (hasVisibleFooterPrimaryActions()) {\n @if (showSaveDiscardFooter()) {\n @if (rejectFooterCommand(); as reject) {\n <ax-button\n [class.ax-sm]=\"deviceService.isSmall()\"\n [disabled]=\"vm.isSaving()\"\n [text]=\"(t(reject.title) | async)!\"\n [look]=\"'solid'\"\n [color]=\"reject.color\"\n (onClick)=\"execute(reject.command)\"\n >\n <ax-prefix>\n <i class=\"{{ reject.icon }}\"></i>\n </ax-prefix>\n </ax-button>\n }\n @if (acceptFooterCommand(); as accept) {\n <ax-button\n [class.ax-sm]=\"deviceService.isSmall()\"\n [disabled]=\"vm.isSaving()\"\n [text]=\"(t(accept.title) | async)!\"\n [look]=\"'solid'\"\n [color]=\"accept.color\"\n (onClick)=\"execute(accept.command)\"\n >\n <ax-prefix>\n <i class=\"{{ accept.icon }}\"></i>\n </ax-prefix>\n </ax-button>\n }\n }\n @for (action of footerPrimaryActions(); track $index) {\n @if (isFooterActionVisible(action)) {\n <ax-button\n [class.ax-sm]=\"deviceService.isSmall()\"\n [disabled]=\"isFooterActionDisabled(action)\"\n [text]=\"(t(action.title) | async)!\"\n [look]=\"'solid'\"\n [color]=\"action.color\"\n (onClick)=\"execute(action.command!)\"\n >\n <ax-prefix>\n <i class=\"{{ action.icon }}\"></i>\n </ax-prefix>\n @if (action?.items) {\n <ax-dropdown-panel #panel>\n <ax-button-item-list>\n @for (sub of action?.items; track $index) {\n @if (isFooterActionVisible(sub)) {\n <ax-button-item\n [text]=\"(t(sub.title) | async)!\"\n [color]=\"sub.color\"\n [disabled]=\"isFooterActionDisabled(sub)\"\n (onClick)=\"execute(sub.command!)\"\n >\n <ax-prefix>\n <ax-icon icon=\"fa-light {{ sub.icon }}\"></ax-icon>\n </ax-prefix>\n </ax-button-item>\n @if (sub.break) {\n <ax-divider></ax-divider>\n }\n }\n }\n </ax-button-item-list>\n </ax-dropdown-panel>\n }\n </ax-button>\n }\n }\n }\n </axp-layout-suffix>\n </axp-page-footer>\n }\n</axp-page-layout>\n", styles: ["axp-layout-details-view .axp-vertical-tabs{--ax-comp-tabs-default-border-radius: 0}axp-layout-details-view .axp-vertical-tabs ax-tab-item{margin-top:0!important;margin-bottom:0!important;padding-top:.75rem!important;padding-bottom:.75rem!important;font-weight:600!important}axp-layout-details-view .axp-vertical-tabs ax-tab-item>div{display:flex!important;align-items:center!important}axp-layout-details-view .axp-vertical-tabs ax-tab-item>div .ax-tab-item-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}axp-layout-details-view .axp-horizontal-tabs{display:flex;width:100%;flex-direction:column}axp-layout-details-view .axp-horizontal-tabs .content{margin-top:1rem;margin-bottom:1rem;display:block;width:100%}axp-layout-details-view axp-layout-section axp-layout-content{display:flex;width:100%;flex-direction:column}axp-layout-details-view axp-layout-section axp-layout-content>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse));border-style:dashed;--tw-divide-opacity: 1;border-color:rgba(var(--ax-sys-color-border-lightest-surface),var(--tw-divide-opacity, 1))}axp-layout-details-view axp-layout-section axp-layout-content .--item-container{align-items:center;gap:1rem;padding:1rem}@media(min-width:1024px){axp-layout-details-view axp-layout-section axp-layout-content .--item-container{padding-left:1.5rem;padding-right:1.5rem}}@media(min-width:1280px){axp-layout-details-view axp-layout-section axp-layout-content .--item-container{padding-left:2rem;padding-right:2rem}}@media(min-width:1536px){axp-layout-details-view axp-layout-section axp-layout-content .--item-container{padding-left:2.5rem;padding-right:2.5rem}}axp-layout-details-view axp-layout-section axp-layout-content .--item-container{display:grid;grid-template-columns:repeat(12,minmax(0,1fr))}axp-layout-details-view axp-layout-section axp-layout-content .--item-container axp-layout-description{margin-top:.5rem!important;font-size:.75rem!important;line-height:1rem!important}axp-layout-details-view axp-page-content ax-form form{height:100%}axp-layout-details-view axp-layout-start-side{border-inline-end-width:1px}\n"] }]
2393
- }], propDecorators: { adapter: [{ type: i0.Input, args: [{ isSignal: true, alias: "adapter", required: true }] }], form: [{ type: i0.ViewChild, args: ['form', { isSignal: true }] }], widgetContainer: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AXPWidgetContainerComponent), { isSignal: true }] }], sidebarTabs: [{ type: i0.ViewChild, args: ['sidebarTabs', { isSignal: true }] }] } });
3140
+ ], 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"] }]
3141
+ }], 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 }] }] } });
2394
3142
 
2395
3143
  //#region ---- Details View Can Deactivate Guard ----
2396
3144
  /**
@@ -2403,5 +3151,5 @@ const axpDetailsViewCanDeactivateGuard = createUnsavedChangesCanDeactivateGuard(
2403
3151
  * Generated bundle index. Do not edit.
2404
3152
  */
2405
3153
 
2406
- export { AXPLayoutDetailsViewComponent, AXPLayoutDetailsViewViewModel, AXPPageComponentInstanceRegistryService, AXPPageComponentRendererDirective, AXPPageLayoutBase, AXPPageLayoutBaseComponent, AXPPageLayoutComponent, AXPPopupLayoutBase, AXPPopupLayoutBaseComponent, AXPPopupLayoutComponent, axpDetailsViewCanDeactivateGuard, buildNotSavedPageBadge, getDetailsViewActionCommandBaseName, isDiscardCommand, resolveDetailsViewActionShortcuts, resolveDetailsViewFooterCommandShortcuts };
3154
+ 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 };
2407
3155
  //# sourceMappingURL=acorex-platform-layout-views.mjs.map