@taon-dev/ui 21.0.11 → 21.0.13

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 (111) hide show
  1. package/VERIFIED-BUILD-DATA.jsonc +3 -3
  2. package/browser/fesm2022/ui-browser.mjs +140 -27
  3. package/browser/fesm2022/ui-browser.mjs.map +1 -1
  4. package/browser/package.json +1 -1
  5. package/browser/types/ui-browser.d.ts +40 -11
  6. package/browser-prod/fesm2022/ui-browser-prod.mjs +140 -27
  7. package/browser-prod/fesm2022/ui-browser-prod.mjs.map +1 -1
  8. package/browser-prod/package.json +1 -1
  9. package/browser-prod/types/ui-browser-prod.d.ts +40 -11
  10. package/full-dts/build-info._auto-generated_.d.ts +1 -1
  11. package/full-dts/index._auto-generated_.d.ts +1 -0
  12. package/full-dts/taon-admin-layout/taon-admin-layout.component.d.ts +2 -0
  13. package/full-dts/taon-admin-layout/taon-admin-layout.models.d.ts +15 -5
  14. package/full-dts/taon-admin-layout/taon-admin-menu-context.d.ts +9 -0
  15. package/full-dts/taon-admin-layout/taon-admin-page-tabs.component.d.ts +2 -0
  16. package/full-dts/taon-draggable-button-panel/taon-draggable-button-panel.component.d.ts +8 -2
  17. package/html/lib/taon-admin-layout/taon-admin-layout.component.html +106 -67
  18. package/html/lib/taon-admin-layout/taon-admin-page-tabs.component.html +12 -7
  19. package/html/lib/taon-draggable-button-panel/taon-draggable-button-panel.component.html +100 -88
  20. package/lib/build-info._auto-generated_.d.ts +1 -1
  21. package/lib/build-info._auto-generated_.js +1 -1
  22. package/lib/index._auto-generated_.d.ts +1 -0
  23. package/lib/index._auto-generated_.js +1 -0
  24. package/lib/index._auto-generated_.js.map +1 -1
  25. package/lib/package.json +1 -1
  26. package/lib/taon-admin-layout/index.d.ts +1 -1
  27. package/lib/taon-admin-layout/index.js +2 -2
  28. package/lib/taon-admin-layout/taon-admin-layout.models.d.ts +12 -5
  29. package/lib/taon-admin-layout/taon-admin-layout.models.js +35 -16
  30. package/lib/taon-admin-layout/taon-admin-layout.models.js.map +1 -1
  31. package/lib/taon-admin-layout/taon-admin-menu-context.d.ts +8 -0
  32. package/lib/taon-admin-layout/taon-admin-menu-context.js +36 -0
  33. package/lib/taon-admin-layout/taon-admin-menu-context.js.map +1 -0
  34. package/lib/taon-datatable/index.d.ts +1 -1
  35. package/lib/taon-datatable/index.js +2 -2
  36. package/lib/taon-draggable-button-panel/index.d.ts +1 -1
  37. package/lib/taon-draggable-button-panel/index.js +2 -2
  38. package/lib-esm/lib/build-info._auto-generated_.d.ts +1 -1
  39. package/lib-esm/lib/build-info._auto-generated_.js +1 -1
  40. package/lib-esm/lib/index._auto-generated_.d.ts +1 -0
  41. package/lib-esm/lib/index._auto-generated_.js +1 -0
  42. package/lib-esm/lib/index._auto-generated_.js.map +1 -1
  43. package/lib-esm/lib/taon-admin-layout/index.d.ts +1 -1
  44. package/lib-esm/lib/taon-admin-layout/index.js +1 -1
  45. package/lib-esm/lib/taon-admin-layout/taon-admin-layout.models.d.ts +12 -5
  46. package/lib-esm/lib/taon-admin-layout/taon-admin-layout.models.js +35 -15
  47. package/lib-esm/lib/taon-admin-layout/taon-admin-layout.models.js.map +1 -1
  48. package/lib-esm/lib/taon-admin-layout/taon-admin-menu-context.d.ts +8 -0
  49. package/lib-esm/lib/taon-admin-layout/taon-admin-menu-context.js +27 -0
  50. package/lib-esm/lib/taon-admin-layout/taon-admin-menu-context.js.map +1 -0
  51. package/lib-esm/lib/taon-datatable/index.d.ts +1 -1
  52. package/lib-esm/lib/taon-datatable/index.js +1 -1
  53. package/lib-esm/lib/taon-draggable-button-panel/index.d.ts +1 -1
  54. package/lib-esm/lib/taon-draggable-button-panel/index.js +1 -1
  55. package/lib-prod/build-info._auto-generated_.d.ts +1 -1
  56. package/lib-prod/build-info._auto-generated_.js +1 -1
  57. package/lib-prod/index._auto-generated_.d.ts +1 -0
  58. package/lib-prod/index._auto-generated_.js +1 -0
  59. package/lib-prod/index._auto-generated_.js.map +1 -1
  60. package/lib-prod/package.json +1 -1
  61. package/lib-prod/taon-admin-layout/index.d.ts +1 -1
  62. package/lib-prod/taon-admin-layout/index.js +1 -1
  63. package/lib-prod/taon-admin-layout/taon-admin-layout.models.d.ts +12 -5
  64. package/lib-prod/taon-admin-layout/taon-admin-layout.models.js +35 -15
  65. package/lib-prod/taon-admin-layout/taon-admin-layout.models.js.map +1 -1
  66. package/lib-prod/taon-admin-layout/taon-admin-menu-context.d.ts +8 -0
  67. package/lib-prod/taon-admin-layout/taon-admin-menu-context.js +27 -0
  68. package/lib-prod/taon-admin-layout/taon-admin-menu-context.js.map +1 -0
  69. package/lib-prod/taon-datatable/index.d.ts +1 -1
  70. package/lib-prod/taon-datatable/index.js +1 -1
  71. package/lib-prod/taon-draggable-button-panel/index.d.ts +1 -1
  72. package/lib-prod/taon-draggable-button-panel/index.js +1 -1
  73. package/package.json +1 -1
  74. package/scss/lib/taon-admin-layout/taon-admin-layout.component.scss +5 -0
  75. package/scss/lib/taon-admin-layout/taon-admin-page-tabs.component.scss +8 -1
  76. package/scss/lib/taon-draggable-button-panel/taon-draggable-button-panel.component.scss +6 -3
  77. package/websql/fesm2022/ui-websql.mjs +140 -27
  78. package/websql/fesm2022/ui-websql.mjs.map +1 -1
  79. package/websql/package.json +1 -1
  80. package/websql/types/ui-websql.d.ts +40 -11
  81. package/websql-prod/fesm2022/ui-websql-prod.mjs +140 -27
  82. package/websql-prod/fesm2022/ui-websql-prod.mjs.map +1 -1
  83. package/websql-prod/package.json +1 -1
  84. package/websql-prod/types/ui-websql-prod.d.ts +40 -11
  85. package/lib-esm/app.d.ts +0 -0
  86. package/lib-esm/app.electron.d.ts +0 -0
  87. package/lib-esm/app.electron.js +0 -2
  88. package/lib-esm/app.electron.js.map +0 -1
  89. package/lib-esm/app.hosts.d.ts +0 -0
  90. package/lib-esm/app.hosts.js +0 -2
  91. package/lib-esm/app.hosts.js.map +0 -1
  92. package/lib-esm/app.js +0 -2
  93. package/lib-esm/app.js.map +0 -1
  94. package/lib-esm/app.vscode.d.ts +0 -0
  95. package/lib-esm/app.vscode.js +0 -2
  96. package/lib-esm/app.vscode.js.map +0 -1
  97. package/lib-prod/env/env.angular-node-app.d.ts +0 -68
  98. package/lib-prod/env/env.angular-node-app.js +0 -140
  99. package/lib-prod/env/env.angular-node-app.js.map +0 -1
  100. package/lib-prod/env/env.electron-app.d.ts +0 -68
  101. package/lib-prod/env/env.electron-app.js +0 -140
  102. package/lib-prod/env/env.electron-app.js.map +0 -1
  103. package/lib-prod/env/env.mobile-app.d.ts +0 -68
  104. package/lib-prod/env/env.mobile-app.js +0 -140
  105. package/lib-prod/env/env.mobile-app.js.map +0 -1
  106. package/lib-prod/env/env.vscode-plugin.d.ts +0 -68
  107. package/lib-prod/env/env.vscode-plugin.js +0 -140
  108. package/lib-prod/env/env.vscode-plugin.js.map +0 -1
  109. package/lib-prod/env/index.d.ts +0 -4
  110. package/lib-prod/env/index.js +0 -5
  111. package/lib-prod/env/index.js.map +0 -1
@@ -2,13 +2,15 @@ import { Translation } from '@taon-dev/i18n/browser-prod';
2
2
  import { UtilsJson__NS__getValue, ___NS__isFunction, ___NS__isBoolean, ___NS__isEqual, ___NS__upperCase } from 'tnp-core/browser-prod';
3
3
  import { BaseCommandLineFeature } from 'tnp-helpers/browser-prod';
4
4
  import * as i0 from '@angular/core';
5
- import { inject, ElementRef, Directive, signal, Input, ChangeDetectionStrategy, Component, HostListener, ViewChild, Injectable, DestroyRef, ChangeDetectorRef, EventEmitter, Output } from '@angular/core';
5
+ import { inject, ElementRef, Directive, signal, Injectable, Input, ChangeDetectionStrategy, Component, HostListener, ViewChild, DestroyRef, ChangeDetectorRef, EventEmitter, Output } from '@angular/core';
6
6
  import * as i2$1 from '@angular/common';
7
- import { CommonModule, NgIf } from '@angular/common';
8
- import * as i3 from '@angular/material/expansion';
7
+ import { CommonModule, NgTemplateOutlet, NgIf } from '@angular/common';
8
+ import * as i3$1 from '@angular/material/expansion';
9
9
  import { MatExpansionModule } from '@angular/material/expansion';
10
10
  import * as i4 from '@angular/material/icon';
11
11
  import { MatIconModule } from '@angular/material/icon';
12
+ import * as i3 from '@angular/material/tooltip';
13
+ import { MatTooltipModule } from '@angular/material/tooltip';
12
14
  import * as i1 from '@angular/router';
13
15
  import { NavigationEnd, RouterOutlet, RouterLink } from '@angular/router';
14
16
  import { filter, BehaviorSubject, Subject, switchMap, debounceTime, distinctUntilChanged, tap, of, from, map, catchError, finalize, firstValueFrom, take } from 'rxjs';
@@ -20,9 +22,9 @@ import { TaonAdminService, getHttpStatus, ClassHelpers__NS__getName, Symbols__NS
20
22
  import { HotToastRef, HotToastService } from '@ngneat/hot-toast';
21
23
  import * as i1$1 from '@angular/material/dialog';
22
24
  import { MAT_DIALOG_DATA, MatDialogModule, MatDialog, MatDialogRef } from '@angular/material/dialog';
23
- import * as i3$1 from '@angular/material/button';
25
+ import * as i3$2 from '@angular/material/button';
24
26
  import { MatButtonModule } from '@angular/material/button';
25
- import * as i3$2 from '@angular/material/button-toggle';
27
+ import * as i3$3 from '@angular/material/button-toggle';
26
28
  import { MatButtonToggleModule } from '@angular/material/button-toggle';
27
29
  import * as i2$2 from '@angular/forms';
28
30
  import { FormsModule, ReactiveFormsModule } from '@angular/forms';
@@ -35,8 +37,6 @@ import { MatSelectModule } from '@angular/material/select';
35
37
  import * as i7 from '@ng-matero/extensions/grid';
36
38
  import { MtxGridModule } from '@ng-matero/extensions/grid';
37
39
  import { CdkDrag, CdkDragHandle } from '@angular/cdk/drag-drop';
38
- import * as i4$2 from '@angular/material/tooltip';
39
- import { MatTooltipModule } from '@angular/material/tooltip';
40
40
 
41
41
  // THIS FILE IS GENERATED - DO NOT MODIFY
42
42
  // THIS FILE IS GENERATED - DO NOT MODIFY
@@ -68,7 +68,7 @@ const CURRENT_PACKAGE_TAON_VERSION = 'v21';
68
68
  /**
69
69
  * Autogenerated by current cli tool. Use *tnp release* to bump version.
70
70
  */
71
- const CURRENT_PACKAGE_VERSION = '21.0.11';
71
+ const CURRENT_PACKAGE_VERSION = '21.0.13';
72
72
  // THIS FILE IS GENERATED - DO NOT MODIFY
73
73
 
74
74
  const t$1 = Translation.for('src/lib/my-organization-proj.ts', { "src/lib/my-organization-proj.ts": { "en-US": async () => (await import('./ui-browser-prod-my-organization-proj.ts.en-US.i18n.data-CoSik70Y.mjs')).default, "pl-PL": async () => (await import('./ui-browser-prod-my-organization-proj.ts.pl-PL.i18n.data-BZPsT3Zk.mjs')).default } });
@@ -229,9 +229,40 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImpor
229
229
  ;
230
230
  // THIS FILE IS GENERATED - DO NOT MODIFY
231
231
 
232
+ class TaonAdminMenuContext {
233
+ constructor() {
234
+ this.context = signal(undefined, ...(ngDevMode ? [{ debugName: "context" }] : []));
235
+ }
236
+ isVisible(route) {
237
+ return (!route.data?.['hideInNavigation'] &&
238
+ this.callbacks(route)?.isVisible?.({ context: this.context() }) !== false);
239
+ }
240
+ isDisabled(route) {
241
+ return (this.callbacks(route)?.isDisabled?.({ context: this.context() }) === true);
242
+ }
243
+ disabledTooltip(route) {
244
+ return this.isDisabled(route)
245
+ ? (this.callbacks(route)?.isDisabledTooltip?.({
246
+ context: this.context(),
247
+ }) ?? '')
248
+ : '';
249
+ }
250
+ callbacks(route) {
251
+ // Angular Route.data erases the application's context type at the router boundary.
252
+ return route.data;
253
+ }
254
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonAdminMenuContext, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
255
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonAdminMenuContext }); }
256
+ }
257
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonAdminMenuContext, decorators: [{
258
+ type: Injectable
259
+ }] });
260
+
232
261
  class TaonAdminPageTabsComponent {
233
262
  constructor(router) {
234
263
  this.router = router;
264
+ this.menuContext = inject(TaonAdminMenuContext, { optional: true }) ??
265
+ new TaonAdminMenuContext();
235
266
  this.basePath = '';
236
267
  this.levels = signal([], ...(ngDevMode ? [{ debugName: "levels" }] : []));
237
268
  }
@@ -261,7 +292,9 @@ class TaonAdminPageTabsComponent {
261
292
  return expected.every((segment, index) => current[index] === segment);
262
293
  }
263
294
  async tabClicked(level, route) {
264
- if (!route.path) {
295
+ if (!route.path ||
296
+ !this.menuContext.isVisible(route) ||
297
+ this.menuContext.isDisabled(route)) {
265
298
  return;
266
299
  }
267
300
  await this.navigateTo([
@@ -472,11 +505,11 @@ class TaonAdminPageTabsComponent {
472
505
  .replace(/\b\w/g, char => char.toUpperCase());
473
506
  }
474
507
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonAdminPageTabsComponent, deps: [{ token: i1.Router }], target: i0.ɵɵFactoryTarget.Component }); }
475
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.9", type: TaonAdminPageTabsComponent, isStandalone: true, selector: "taon-admin-page-tabs", inputs: { routes: "routes", basePath: "basePath", outlet: "outlet" }, ngImport: i0, template: "@if (levels().length) {\n <div class=\"shrink-0 border-b border-gray-200 bg-white\">\n @for (level of levels(); track level.level) {\n <nav\n mat-tab-nav-bar\n [tabPanel]=\"tabPanel\">\n @for (route of level.routes; track route.path) {\n <a\n mat-tab-link\n class=\"cursor-pointer\"\n [active]=\"isActive(level, route)\"\n (click)=\"tabClicked(level, route)\">\n {{ label(route) }}\n </a>\n }\n </nav>\n }\n </div>\n}\n\n<mat-tab-nav-panel #tabPanel> </mat-tab-nav-panel>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i2.MatTabNav, selector: "[mat-tab-nav-bar]", inputs: ["fitInkBarToContent", "mat-stretch-tabs", "animationDuration", "backgroundColor", "disableRipple", "color", "tabPanel"], exportAs: ["matTabNavBar", "matTabNav"] }, { kind: "component", type: i2.MatTabNavPanel, selector: "mat-tab-nav-panel", inputs: ["id"], exportAs: ["matTabNavPanel"] }, { kind: "component", type: i2.MatTabLink, selector: "[mat-tab-link], [matTabLink]", inputs: ["active", "disabled", "disableRipple", "tabIndex", "id"], exportAs: ["matTabLink"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
508
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.9", type: TaonAdminPageTabsComponent, isStandalone: true, selector: "taon-admin-page-tabs", inputs: { routes: "routes", basePath: "basePath", outlet: "outlet" }, ngImport: i0, template: "@if (levels().length) {\n <div class=\"shrink-0 border-b border-gray-200 bg-white\">\n @for (level of levels(); track level.level) {\n <nav\n mat-tab-nav-bar\n [tabPanel]=\"tabPanel\">\n @for (route of level.routes; track route.path) {\n @if (menuContext.isVisible(route)) {\n <a\n mat-tab-link\n class=\"cursor-pointer\"\n [disabled]=\"menuContext.isDisabled(route)\"\n [class.admin-tab-disabled]=\"menuContext.isDisabled(route)\"\n [matTooltip]=\"menuContext.disabledTooltip(route)\"\n [active]=\"isActive(level, route)\"\n (click)=\"tabClicked(level, route)\">\n {{ label(route) }}\n </a>\n }\n }\n </nav>\n }\n </div>\n}\n\n<mat-tab-nav-panel #tabPanel> </mat-tab-nav-panel>\n", styles: [".mat-mdc-tab-link.admin-tab-disabled{pointer-events:auto;cursor:not-allowed;opacity:.5}\n"], dependencies: [{ kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i2.MatTabNav, selector: "[mat-tab-nav-bar]", inputs: ["fitInkBarToContent", "mat-stretch-tabs", "animationDuration", "backgroundColor", "disableRipple", "color", "tabPanel"], exportAs: ["matTabNavBar", "matTabNav"] }, { kind: "component", type: i2.MatTabNavPanel, selector: "mat-tab-nav-panel", inputs: ["id"], exportAs: ["matTabNavPanel"] }, { kind: "component", type: i2.MatTabLink, selector: "[mat-tab-link], [matTabLink]", inputs: ["active", "disabled", "disableRipple", "tabIndex", "id"], exportAs: ["matTabLink"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
476
509
  }
477
510
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonAdminPageTabsComponent, decorators: [{
478
511
  type: Component,
479
- args: [{ selector: 'taon-admin-page-tabs', standalone: true, imports: [MatTabsModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (levels().length) {\n <div class=\"shrink-0 border-b border-gray-200 bg-white\">\n @for (level of levels(); track level.level) {\n <nav\n mat-tab-nav-bar\n [tabPanel]=\"tabPanel\">\n @for (route of level.routes; track route.path) {\n <a\n mat-tab-link\n class=\"cursor-pointer\"\n [active]=\"isActive(level, route)\"\n (click)=\"tabClicked(level, route)\">\n {{ label(route) }}\n </a>\n }\n </nav>\n }\n </div>\n}\n\n<mat-tab-nav-panel #tabPanel> </mat-tab-nav-panel>\n" }]
512
+ args: [{ selector: 'taon-admin-page-tabs', standalone: true, imports: [MatTabsModule, MatTooltipModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (levels().length) {\n <div class=\"shrink-0 border-b border-gray-200 bg-white\">\n @for (level of levels(); track level.level) {\n <nav\n mat-tab-nav-bar\n [tabPanel]=\"tabPanel\">\n @for (route of level.routes; track route.path) {\n @if (menuContext.isVisible(route)) {\n <a\n mat-tab-link\n class=\"cursor-pointer\"\n [disabled]=\"menuContext.isDisabled(route)\"\n [class.admin-tab-disabled]=\"menuContext.isDisabled(route)\"\n [matTooltip]=\"menuContext.disabledTooltip(route)\"\n [active]=\"isActive(level, route)\"\n (click)=\"tabClicked(level, route)\">\n {{ label(route) }}\n </a>\n }\n }\n </nav>\n }\n </div>\n}\n\n<mat-tab-nav-panel #tabPanel> </mat-tab-nav-panel>\n", styles: [".mat-mdc-tab-link.admin-tab-disabled{pointer-events:auto;cursor:not-allowed;opacity:.5}\n"] }]
480
513
  }], ctorParameters: () => [{ type: i1.Router }], propDecorators: { routes: [{
481
514
  type: Input,
482
515
  args: [{ required: true }]
@@ -508,6 +541,9 @@ class TaonAdminLayoutComponent {
508
541
  constructor(router, hostElement) {
509
542
  this.router = router;
510
543
  this.hostElement = hostElement;
544
+ //#region fields and getters
545
+ this.menuContext = inject(TaonAdminMenuContext, { optional: true }) ??
546
+ new TaonAdminMenuContext();
511
547
  this.basePath = '';
512
548
  this.layoutWidth = signal(undefined, ...(ngDevMode ? [{ debugName: "layoutWidth" }] : []));
513
549
  this.layoutHeight = signal(undefined, ...(ngDevMode ? [{ debugName: "layoutHeight" }] : []));
@@ -706,7 +742,9 @@ class TaonAdminLayoutComponent {
706
742
  //#endregion
707
743
  //#region level 1
708
744
  async level1Clicked(route) {
709
- if (!route.path) {
745
+ if (!route.path ||
746
+ !this.menuContext.isVisible(route) ||
747
+ this.menuContext.isDisabled(route)) {
710
748
  return;
711
749
  }
712
750
  await this.navigateTo([route.path]);
@@ -716,7 +754,9 @@ class TaonAdminLayoutComponent {
716
754
  }
717
755
  }
718
756
  async panelExpanded(route, expanded) {
719
- if (!expanded) {
757
+ if (!expanded ||
758
+ !this.menuContext.isVisible(route) ||
759
+ this.menuContext.isDisabled(route)) {
720
760
  return;
721
761
  }
722
762
  await this.ensureLevel2Loaded(route);
@@ -730,7 +770,12 @@ class TaonAdminLayoutComponent {
730
770
  //#endregion
731
771
  //#region level 2
732
772
  async level2Clicked(level1, level2) {
733
- if (!level1.path || !level2.path) {
773
+ if (!level1.path ||
774
+ !level2.path ||
775
+ !this.menuContext.isVisible(level1) ||
776
+ this.menuContext.isDisabled(level1) ||
777
+ !this.menuContext.isVisible(level2) ||
778
+ this.menuContext.isDisabled(level2)) {
734
779
  return;
735
780
  }
736
781
  await this.navigateTo([level1.path, level2.path]);
@@ -889,7 +934,7 @@ class TaonAdminLayoutComponent {
889
934
  .replace(/\b\w/g, char => char.toUpperCase());
890
935
  }
891
936
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonAdminLayoutComponent, deps: [{ token: i1.Router }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); }
892
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.9", type: TaonAdminLayoutComponent, isStandalone: true, selector: "taon-admin-layout", inputs: { routes: "routes", basePath: "basePath", outlet: "outlet" }, host: { listeners: { "window:resize": "windowResized()" } }, ngImport: i0, template: "<div\n #layout\n class=\"admin-layout\"\n [class.admin-layout-dialog]=\"isDialogMode\"\n [style.width.px]=\"layoutWidth() || null\"\n [style.height.px]=\"layoutHeight() || null\">\n <!-- ============================================================= -->\n <!-- HEADER -->\n <!-- ============================================================= -->\n\n <header class=\"admin-header\">\n <button\n mat-icon-button\n type=\"button\"\n aria-label=\"Toggle admin menu\"\n class=\"admin-menu-button\"\n (click)=\"toggleAside()\">\n <mat-icon>menu</mat-icon>\n </button>\n\n <div class=\"admin-header-content\">\n <ng-content select=\"[header]\" />\n </div>\n </header>\n\n <!-- ============================================================= -->\n <!-- BODY -->\n <!-- ============================================================= -->\n\n <div class=\"admin-body\">\n <!-- =========================================================== -->\n <!-- MOBILE BACKDROP -->\n <!-- =========================================================== -->\n\n @if (asideOpened()) {\n <div\n class=\"admin-aside-backdrop\"\n (click)=\"closeAside()\"></div>\n }\n\n <!-- =========================================================== -->\n <!-- ASIDE -->\n <!-- =========================================================== -->\n\n <aside\n class=\"admin-aside\"\n [class.admin-aside-open]=\"asideOpened()\">\n <!-- ========================================================= -->\n <!-- SCROLLABLE ASIDE CONTENT -->\n <!-- ========================================================= -->\n\n <div class=\"admin-aside-content\">\n <mat-accordion class=\"admin-menu\">\n @for (level1 of routes; track level1.path) {\n @if (isExpandable(level1)) {\n <mat-expansion-panel\n [expanded]=\"isLevel1Active(level1)\"\n (expandedChange)=\"panelExpanded(level1, $event)\">\n <mat-expansion-panel-header (click)=\"level1Clicked(level1)\">\n <mat-panel-title>\n <div class=\"admin-menu-item\">\n @if (icon(level1)) {\n <mat-icon\n [ngStyle]=\"{\n color: color(level1),\n }\">\n {{ icon(level1) }}\n </mat-icon>\n }\n\n <span\n [class.font-semibold]=\"isLevel1Active(level1)\"\n [ngStyle]=\"{\n color: color(level1),\n }\">\n {{ label(level1) }}\n </span>\n </div>\n </mat-panel-title>\n </mat-expansion-panel-header>\n\n <div class=\"admin-level2\">\n @if (isLoading(level1)) {\n <div class=\"admin-loading\">Loading...</div>\n } @else {\n @for (\n level2 of getLevel2Routes(level1);\n track level2.path\n ) {\n <a\n class=\"admin-level2-item\"\n [class.admin-level2-item-active]=\"\n isLevel2Active(level1, level2)\n \"\n (click)=\"level2Clicked(level1, level2)\">\n @if (icon(level2)) {\n <mat-icon\n [ngStyle]=\"{\n color: color(level2),\n }\">\n {{ icon(level2) }}\n </mat-icon>\n }\n\n <span\n [ngStyle]=\"{\n color: color(level2),\n }\">\n {{ label(level2) }}\n </span>\n </a>\n }\n }\n </div>\n </mat-expansion-panel>\n } @else {\n <a\n class=\"admin-level1-item\"\n [class.admin-level1-item-active]=\"isLevel1Active(level1)\"\n (click)=\"level1Clicked(level1)\">\n @if (icon(level1)) {\n <mat-icon\n [ngStyle]=\"{\n color: color(level1),\n }\">\n {{ icon(level1) }}\n </mat-icon>\n }\n\n <span\n [ngStyle]=\"{\n color: color(level1),\n }\">\n {{ label(level1) }}\n </span>\n </a>\n }\n }\n </mat-accordion>\n </div>\n\n <!-- ========================================================= -->\n <!-- ASIDE FOOTER -->\n <!-- ========================================================= -->\n\n <footer class=\"admin-aside-footer\">\n <ng-content select=\"[asideFooter]\" />\n </footer>\n </aside>\n\n <!-- =========================================================== -->\n <!-- MAIN -->\n <!-- =========================================================== -->\n\n <main class=\"admin-main\">\n <!-- ========================================================= -->\n <!-- LEVEL 3+ TABS -->\n <!-- ========================================================= -->\n\n <taon-admin-page-tabs\n class=\"admin-tabs\"\n [routes]=\"routes\"\n [basePath]=\"basePath\"\n [outlet]=\"outlet\" />\n\n <!-- ========================================================= -->\n <!-- SCROLLABLE CONTENT -->\n <!-- ========================================================= -->\n\n <div class=\"admin-content\">\n <router-outlet />\n </div>\n\n <!-- ========================================================= -->\n <!-- CONTENT FOOTER -->\n <!-- ========================================================= -->\n\n <footer class=\"admin-content-footer\">\n <ng-content select=\"[contentFooter]\" />\n </footer>\n </main>\n </div>\n\n <!-- ============================================================= -->\n <!-- DIALOG RESIZE HANDLE -->\n <!-- ============================================================= -->\n\n @if (isDialogMode) {\n <div\n class=\"admin-resize-handle\"\n (pointerdown)=\"startResize($event)\"></div>\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%;min-width:0;min-height:0}.admin-layout{position:relative;display:flex;flex-direction:column;min-width:0;min-height:0;overflow:hidden;background:#fff}.admin-layout-dialog{min-width:500px;min-height:350px}.admin-header{display:flex;align-items:center;flex:0 0 64px;height:64px;border-bottom:1px solid var(--mat-sys-outline-variant);background:#fff}.admin-menu-button{flex:0 0 auto;margin-left:8px;margin-right:8px;cursor:pointer}.admin-header-content{flex:1 1 auto;min-width:0}.admin-body{position:relative;display:flex;flex:1 1 auto;min-width:0;min-height:0;overflow:hidden}.admin-aside{position:absolute;top:0;bottom:0;left:0;z-index:30;display:flex;flex-direction:column;width:288px;min-height:0;overflow:hidden;background:#fff;border-right:1px solid var(--mat-sys-outline-variant);transform:translate(-100%);transition:transform .2s ease,width .2s ease}.admin-aside-open{transform:translate(0)}.admin-aside-content{flex:1 1 auto;min-height:0;overflow-x:hidden;overflow-y:auto}.admin-aside-footer{flex:0 0 auto;padding:12px;border-top:1px solid var(--mat-sys-outline-variant);background:#fff}.admin-menu{display:block}.admin-menu mat-expansion-panel{margin-top:0!important;margin-bottom:0!important;border-radius:0!important;box-shadow:none!important;border-bottom:1px solid var(--mat-sys-outline-variant)}.admin-menu mat-expansion-panel:first-of-type,.admin-menu mat-expansion-panel:last-of-type,.admin-menu mat-expansion-panel-header{border-radius:0!important}.admin-menu-item{display:flex;align-items:center;gap:12px}.admin-level2{display:flex;flex-direction:column}.admin-level2-item{display:flex;align-items:center;gap:12px;padding:10px 24px;cursor:pointer}.admin-level2-item:hover,.admin-level2-item-active{background:#f5f5f5}.admin-level2-item-active{font-weight:600}.admin-level1-item{display:flex;align-items:center;height:56px;gap:12px;padding:0 24px;cursor:pointer}.admin-level1-item:hover,.admin-level1-item-active{background:#f5f5f5}.admin-level1-item-active{font-weight:600}.admin-loading{padding:8px 16px;opacity:.6}.admin-main{display:flex;flex-direction:column;flex:1 1 auto;min-width:0;min-height:0;overflow:hidden}.admin-tabs{display:block;flex:0 0 auto}.admin-content{flex:1 1 auto;min-width:0;min-height:0;padding:24px;overflow-x:auto;overflow-y:auto}.admin-content-footer{flex:0 0 auto;padding:12px 24px;border-top:1px solid var(--mat-sys-outline-variant);background:#fff}.admin-aside-backdrop{position:absolute;inset:0;z-index:20;background:#0000004d}@media(min-width:768px){.admin-aside{position:relative;top:auto;bottom:auto;left:auto;z-index:auto;width:0;transform:none}.admin-aside-open{width:288px;min-width:288px}.admin-aside-backdrop{display:none}}.admin-resize-handle{position:absolute;right:0;bottom:0;z-index:100;width:18px;height:18px;cursor:nwse-resize}.admin-resize-handle:after{content:\"\";position:absolute;right:4px;bottom:4px;width:8px;height:8px;border-right:2px solid #777;border-bottom:2px solid #777}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "directive", type: i3.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i3.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i3.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i3.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: TaonAdminPageTabsComponent, selector: "taon-admin-page-tabs", inputs: ["routes", "basePath", "outlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
937
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.9", type: TaonAdminLayoutComponent, isStandalone: true, selector: "taon-admin-layout", inputs: { routes: "routes", basePath: "basePath", outlet: "outlet" }, host: { listeners: { "window:resize": "windowResized()" } }, ngImport: i0, template: "<div\n #layout\n class=\"admin-layout\"\n [class.admin-layout-dialog]=\"isDialogMode\"\n [style.width.px]=\"layoutWidth() || null\"\n [style.height.px]=\"layoutHeight() || null\">\n <!-- ============================================================= -->\n <!-- HEADER -->\n <!-- ============================================================= -->\n\n <header class=\"admin-header\">\n <button\n mat-icon-button\n type=\"button\"\n aria-label=\"Toggle admin menu\"\n class=\"admin-menu-button\"\n (click)=\"toggleAside()\">\n <mat-icon>menu</mat-icon>\n </button>\n\n <div class=\"admin-header-content\">\n <ng-content select=\"[header]\" />\n </div>\n </header>\n\n <!-- ============================================================= -->\n <!-- BODY -->\n <!-- ============================================================= -->\n\n <div class=\"admin-body\">\n <!-- =========================================================== -->\n <!-- MOBILE BACKDROP -->\n <!-- =========================================================== -->\n\n @if (asideOpened()) {\n <div\n class=\"admin-aside-backdrop\"\n (click)=\"closeAside()\"></div>\n }\n\n <!-- =========================================================== -->\n <!-- ASIDE -->\n <!-- =========================================================== -->\n\n <aside\n class=\"admin-aside\"\n [class.admin-aside-open]=\"asideOpened()\">\n <!-- ========================================================= -->\n <!-- SCROLLABLE ASIDE CONTENT -->\n <!-- ========================================================= -->\n\n <div class=\"admin-aside-content\">\n <mat-accordion class=\"admin-menu\">\n @for (level1 of routes; track level1.path) {\n @if (menuContext.isVisible(level1)) {\n @if (isExpandable(level1)) {\n <mat-expansion-panel\n [disabled]=\"menuContext.isDisabled(level1)\"\n [expanded]=\"\n isLevel1Active(level1) && !menuContext.isDisabled(level1)\n \"\n (expandedChange)=\"panelExpanded(level1, $event)\">\n <mat-expansion-panel-header\n [class.admin-menu-item-disabled]=\"\n menuContext.isDisabled(level1)\n \"\n [matTooltip]=\"menuContext.disabledTooltip(level1)\"\n (click)=\"level1Clicked(level1)\">\n <mat-panel-title>\n <div class=\"admin-menu-item\">\n @if (icon(level1)) {\n <mat-icon\n [ngStyle]=\"{\n color: color(level1),\n }\">\n {{ icon(level1) }}\n </mat-icon>\n }\n\n <span\n [class.font-semibold]=\"isLevel1Active(level1)\"\n [ngStyle]=\"{\n color: color(level1),\n }\">\n {{ label(level1) }}\n </span>\n </div>\n </mat-panel-title>\n </mat-expansion-panel-header>\n\n <div class=\"admin-level2\">\n @if (isLoading(level1)) {\n <div class=\"admin-loading\">Loading...</div>\n } @else {\n @for (\n level2 of getLevel2Routes(level1);\n track level2.path\n ) {\n @if (menuContext.isVisible(level2)) {\n <a\n class=\"admin-level2-item\"\n role=\"button\"\n [attr.tabindex]=\"\n menuContext.isDisabled(level2) ? -1 : 0\n \"\n [attr.aria-disabled]=\"\n menuContext.isDisabled(level2)\n \"\n [class.admin-menu-item-disabled]=\"\n menuContext.isDisabled(level2)\n \"\n [matTooltip]=\"menuContext.disabledTooltip(level2)\"\n [class.admin-level2-item-active]=\"\n isLevel2Active(level1, level2)\n \"\n (click)=\"level2Clicked(level1, level2)\"\n (keydown.enter)=\"level2Clicked(level1, level2)\"\n (keydown.space)=\"\n $event.preventDefault();\n level2Clicked(level1, level2)\n \">\n @if (icon(level2)) {\n <mat-icon\n [ngStyle]=\"{\n color: color(level2),\n }\">\n {{ icon(level2) }}\n </mat-icon>\n }\n\n <span\n [ngStyle]=\"{\n color: color(level2),\n }\">\n {{ label(level2) }}\n </span>\n </a>\n }\n }\n }\n </div>\n </mat-expansion-panel>\n } @else {\n <a\n class=\"admin-level1-item\"\n role=\"button\"\n [attr.tabindex]=\"menuContext.isDisabled(level1) ? -1 : 0\"\n [attr.aria-disabled]=\"menuContext.isDisabled(level1)\"\n [class.admin-menu-item-disabled]=\"\n menuContext.isDisabled(level1)\n \"\n [matTooltip]=\"menuContext.disabledTooltip(level1)\"\n [class.admin-level1-item-active]=\"isLevel1Active(level1)\"\n (click)=\"level1Clicked(level1)\"\n (keydown.enter)=\"level1Clicked(level1)\"\n (keydown.space)=\"\n $event.preventDefault(); level1Clicked(level1)\n \">\n @if (icon(level1)) {\n <mat-icon\n [ngStyle]=\"{\n color: color(level1),\n }\">\n {{ icon(level1) }}\n </mat-icon>\n }\n\n <span\n [ngStyle]=\"{\n color: color(level1),\n }\">\n {{ label(level1) }}\n </span>\n </a>\n }\n }\n }\n </mat-accordion>\n </div>\n\n <!-- ========================================================= -->\n <!-- ASIDE FOOTER -->\n <!-- ========================================================= -->\n\n <footer class=\"admin-aside-footer\">\n <ng-content select=\"[asideFooter]\" />\n </footer>\n </aside>\n\n <!-- =========================================================== -->\n <!-- MAIN -->\n <!-- =========================================================== -->\n\n <main class=\"admin-main\">\n <!-- ========================================================= -->\n <!-- LEVEL 3+ TABS -->\n <!-- ========================================================= -->\n\n <taon-admin-page-tabs\n class=\"admin-tabs\"\n [routes]=\"routes\"\n [basePath]=\"basePath\"\n [outlet]=\"outlet\" />\n\n <!-- ========================================================= -->\n <!-- SCROLLABLE CONTENT -->\n <!-- ========================================================= -->\n\n <div class=\"admin-content\">\n <router-outlet />\n </div>\n\n <!-- ========================================================= -->\n <!-- CONTENT FOOTER -->\n <!-- ========================================================= -->\n\n <footer class=\"admin-content-footer\">\n <ng-content select=\"[contentFooter]\" />\n </footer>\n </main>\n </div>\n\n <!-- ============================================================= -->\n <!-- DIALOG RESIZE HANDLE -->\n <!-- ============================================================= -->\n\n @if (isDialogMode) {\n <div\n class=\"admin-resize-handle\"\n (pointerdown)=\"startResize($event)\"></div>\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%;min-width:0;min-height:0}.admin-layout{position:relative;display:flex;flex-direction:column;min-width:0;min-height:0;overflow:hidden;background:#fff}.admin-layout-dialog{min-width:500px;min-height:350px}.admin-header{display:flex;align-items:center;flex:0 0 64px;height:64px;border-bottom:1px solid var(--mat-sys-outline-variant);background:#fff}.admin-menu-button{flex:0 0 auto;margin-left:8px;margin-right:8px;cursor:pointer}.admin-header-content{flex:1 1 auto;min-width:0}.admin-body{position:relative;display:flex;flex:1 1 auto;min-width:0;min-height:0;overflow:hidden}.admin-aside{position:absolute;top:0;bottom:0;left:0;z-index:30;display:flex;flex-direction:column;width:288px;min-height:0;overflow:hidden;background:#fff;border-right:1px solid var(--mat-sys-outline-variant);transform:translate(-100%);transition:transform .2s ease,width .2s ease}.admin-aside-open{transform:translate(0)}.admin-aside-content{flex:1 1 auto;min-height:0;overflow-x:hidden;overflow-y:auto}.admin-aside-footer{flex:0 0 auto;padding:12px;border-top:1px solid var(--mat-sys-outline-variant);background:#fff}.admin-menu{display:block}.admin-menu mat-expansion-panel{margin-top:0!important;margin-bottom:0!important;border-radius:0!important;box-shadow:none!important;border-bottom:1px solid var(--mat-sys-outline-variant)}.admin-menu mat-expansion-panel:first-of-type,.admin-menu mat-expansion-panel:last-of-type,.admin-menu mat-expansion-panel-header{border-radius:0!important}.admin-menu-item{display:flex;align-items:center;gap:12px}.admin-level2{display:flex;flex-direction:column}.admin-level2-item{display:flex;align-items:center;gap:12px;padding:10px 24px;cursor:pointer}.admin-level2-item:hover,.admin-level2-item-active{background:#f5f5f5}.admin-level2-item-active{font-weight:600}.admin-level1-item{display:flex;align-items:center;height:56px;gap:12px;padding:0 24px;cursor:pointer}.admin-level1-item:hover,.admin-level1-item-active{background:#f5f5f5}.admin-level1-item-active{font-weight:600}.admin-loading{padding:8px 16px;opacity:.6}.admin-menu-item-disabled{opacity:.5;cursor:not-allowed}.admin-main{display:flex;flex-direction:column;flex:1 1 auto;min-width:0;min-height:0;overflow:hidden}.admin-tabs{display:block;flex:0 0 auto}.admin-content{flex:1 1 auto;min-width:0;min-height:0;padding:24px;overflow-x:auto;overflow-y:auto}.admin-content-footer{flex:0 0 auto;padding:12px 24px;border-top:1px solid var(--mat-sys-outline-variant);background:#fff}.admin-aside-backdrop{position:absolute;inset:0;z-index:20;background:#0000004d}@media(min-width:768px){.admin-aside{position:relative;top:auto;bottom:auto;left:auto;z-index:auto;width:0;transform:none}.admin-aside-open{width:288px;min-width:288px}.admin-aside-backdrop{display:none}}.admin-resize-handle{position:absolute;right:0;bottom:0;z-index:100;width:18px;height:18px;cursor:nwse-resize}.admin-resize-handle:after{content:\"\";position:absolute;right:4px;bottom:4px;width:8px;height:8px;border-right:2px solid #777;border-bottom:2px solid #777}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "directive", type: i3$1.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i3$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i3$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i3$1.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: TaonAdminPageTabsComponent, selector: "taon-admin-page-tabs", inputs: ["routes", "basePath", "outlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
893
938
  }
894
939
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonAdminLayoutComponent, decorators: [{
895
940
  type: Component,
@@ -899,8 +944,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImpor
899
944
  RouterLink,
900
945
  MatExpansionModule,
901
946
  MatIconModule,
947
+ MatTooltipModule,
902
948
  TaonAdminPageTabsComponent,
903
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n #layout\n class=\"admin-layout\"\n [class.admin-layout-dialog]=\"isDialogMode\"\n [style.width.px]=\"layoutWidth() || null\"\n [style.height.px]=\"layoutHeight() || null\">\n <!-- ============================================================= -->\n <!-- HEADER -->\n <!-- ============================================================= -->\n\n <header class=\"admin-header\">\n <button\n mat-icon-button\n type=\"button\"\n aria-label=\"Toggle admin menu\"\n class=\"admin-menu-button\"\n (click)=\"toggleAside()\">\n <mat-icon>menu</mat-icon>\n </button>\n\n <div class=\"admin-header-content\">\n <ng-content select=\"[header]\" />\n </div>\n </header>\n\n <!-- ============================================================= -->\n <!-- BODY -->\n <!-- ============================================================= -->\n\n <div class=\"admin-body\">\n <!-- =========================================================== -->\n <!-- MOBILE BACKDROP -->\n <!-- =========================================================== -->\n\n @if (asideOpened()) {\n <div\n class=\"admin-aside-backdrop\"\n (click)=\"closeAside()\"></div>\n }\n\n <!-- =========================================================== -->\n <!-- ASIDE -->\n <!-- =========================================================== -->\n\n <aside\n class=\"admin-aside\"\n [class.admin-aside-open]=\"asideOpened()\">\n <!-- ========================================================= -->\n <!-- SCROLLABLE ASIDE CONTENT -->\n <!-- ========================================================= -->\n\n <div class=\"admin-aside-content\">\n <mat-accordion class=\"admin-menu\">\n @for (level1 of routes; track level1.path) {\n @if (isExpandable(level1)) {\n <mat-expansion-panel\n [expanded]=\"isLevel1Active(level1)\"\n (expandedChange)=\"panelExpanded(level1, $event)\">\n <mat-expansion-panel-header (click)=\"level1Clicked(level1)\">\n <mat-panel-title>\n <div class=\"admin-menu-item\">\n @if (icon(level1)) {\n <mat-icon\n [ngStyle]=\"{\n color: color(level1),\n }\">\n {{ icon(level1) }}\n </mat-icon>\n }\n\n <span\n [class.font-semibold]=\"isLevel1Active(level1)\"\n [ngStyle]=\"{\n color: color(level1),\n }\">\n {{ label(level1) }}\n </span>\n </div>\n </mat-panel-title>\n </mat-expansion-panel-header>\n\n <div class=\"admin-level2\">\n @if (isLoading(level1)) {\n <div class=\"admin-loading\">Loading...</div>\n } @else {\n @for (\n level2 of getLevel2Routes(level1);\n track level2.path\n ) {\n <a\n class=\"admin-level2-item\"\n [class.admin-level2-item-active]=\"\n isLevel2Active(level1, level2)\n \"\n (click)=\"level2Clicked(level1, level2)\">\n @if (icon(level2)) {\n <mat-icon\n [ngStyle]=\"{\n color: color(level2),\n }\">\n {{ icon(level2) }}\n </mat-icon>\n }\n\n <span\n [ngStyle]=\"{\n color: color(level2),\n }\">\n {{ label(level2) }}\n </span>\n </a>\n }\n }\n </div>\n </mat-expansion-panel>\n } @else {\n <a\n class=\"admin-level1-item\"\n [class.admin-level1-item-active]=\"isLevel1Active(level1)\"\n (click)=\"level1Clicked(level1)\">\n @if (icon(level1)) {\n <mat-icon\n [ngStyle]=\"{\n color: color(level1),\n }\">\n {{ icon(level1) }}\n </mat-icon>\n }\n\n <span\n [ngStyle]=\"{\n color: color(level1),\n }\">\n {{ label(level1) }}\n </span>\n </a>\n }\n }\n </mat-accordion>\n </div>\n\n <!-- ========================================================= -->\n <!-- ASIDE FOOTER -->\n <!-- ========================================================= -->\n\n <footer class=\"admin-aside-footer\">\n <ng-content select=\"[asideFooter]\" />\n </footer>\n </aside>\n\n <!-- =========================================================== -->\n <!-- MAIN -->\n <!-- =========================================================== -->\n\n <main class=\"admin-main\">\n <!-- ========================================================= -->\n <!-- LEVEL 3+ TABS -->\n <!-- ========================================================= -->\n\n <taon-admin-page-tabs\n class=\"admin-tabs\"\n [routes]=\"routes\"\n [basePath]=\"basePath\"\n [outlet]=\"outlet\" />\n\n <!-- ========================================================= -->\n <!-- SCROLLABLE CONTENT -->\n <!-- ========================================================= -->\n\n <div class=\"admin-content\">\n <router-outlet />\n </div>\n\n <!-- ========================================================= -->\n <!-- CONTENT FOOTER -->\n <!-- ========================================================= -->\n\n <footer class=\"admin-content-footer\">\n <ng-content select=\"[contentFooter]\" />\n </footer>\n </main>\n </div>\n\n <!-- ============================================================= -->\n <!-- DIALOG RESIZE HANDLE -->\n <!-- ============================================================= -->\n\n @if (isDialogMode) {\n <div\n class=\"admin-resize-handle\"\n (pointerdown)=\"startResize($event)\"></div>\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%;min-width:0;min-height:0}.admin-layout{position:relative;display:flex;flex-direction:column;min-width:0;min-height:0;overflow:hidden;background:#fff}.admin-layout-dialog{min-width:500px;min-height:350px}.admin-header{display:flex;align-items:center;flex:0 0 64px;height:64px;border-bottom:1px solid var(--mat-sys-outline-variant);background:#fff}.admin-menu-button{flex:0 0 auto;margin-left:8px;margin-right:8px;cursor:pointer}.admin-header-content{flex:1 1 auto;min-width:0}.admin-body{position:relative;display:flex;flex:1 1 auto;min-width:0;min-height:0;overflow:hidden}.admin-aside{position:absolute;top:0;bottom:0;left:0;z-index:30;display:flex;flex-direction:column;width:288px;min-height:0;overflow:hidden;background:#fff;border-right:1px solid var(--mat-sys-outline-variant);transform:translate(-100%);transition:transform .2s ease,width .2s ease}.admin-aside-open{transform:translate(0)}.admin-aside-content{flex:1 1 auto;min-height:0;overflow-x:hidden;overflow-y:auto}.admin-aside-footer{flex:0 0 auto;padding:12px;border-top:1px solid var(--mat-sys-outline-variant);background:#fff}.admin-menu{display:block}.admin-menu mat-expansion-panel{margin-top:0!important;margin-bottom:0!important;border-radius:0!important;box-shadow:none!important;border-bottom:1px solid var(--mat-sys-outline-variant)}.admin-menu mat-expansion-panel:first-of-type,.admin-menu mat-expansion-panel:last-of-type,.admin-menu mat-expansion-panel-header{border-radius:0!important}.admin-menu-item{display:flex;align-items:center;gap:12px}.admin-level2{display:flex;flex-direction:column}.admin-level2-item{display:flex;align-items:center;gap:12px;padding:10px 24px;cursor:pointer}.admin-level2-item:hover,.admin-level2-item-active{background:#f5f5f5}.admin-level2-item-active{font-weight:600}.admin-level1-item{display:flex;align-items:center;height:56px;gap:12px;padding:0 24px;cursor:pointer}.admin-level1-item:hover,.admin-level1-item-active{background:#f5f5f5}.admin-level1-item-active{font-weight:600}.admin-loading{padding:8px 16px;opacity:.6}.admin-main{display:flex;flex-direction:column;flex:1 1 auto;min-width:0;min-height:0;overflow:hidden}.admin-tabs{display:block;flex:0 0 auto}.admin-content{flex:1 1 auto;min-width:0;min-height:0;padding:24px;overflow-x:auto;overflow-y:auto}.admin-content-footer{flex:0 0 auto;padding:12px 24px;border-top:1px solid var(--mat-sys-outline-variant);background:#fff}.admin-aside-backdrop{position:absolute;inset:0;z-index:20;background:#0000004d}@media(min-width:768px){.admin-aside{position:relative;top:auto;bottom:auto;left:auto;z-index:auto;width:0;transform:none}.admin-aside-open{width:288px;min-width:288px}.admin-aside-backdrop{display:none}}.admin-resize-handle{position:absolute;right:0;bottom:0;z-index:100;width:18px;height:18px;cursor:nwse-resize}.admin-resize-handle:after{content:\"\";position:absolute;right:4px;bottom:4px;width:8px;height:8px;border-right:2px solid #777;border-bottom:2px solid #777}\n"] }]
949
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n #layout\n class=\"admin-layout\"\n [class.admin-layout-dialog]=\"isDialogMode\"\n [style.width.px]=\"layoutWidth() || null\"\n [style.height.px]=\"layoutHeight() || null\">\n <!-- ============================================================= -->\n <!-- HEADER -->\n <!-- ============================================================= -->\n\n <header class=\"admin-header\">\n <button\n mat-icon-button\n type=\"button\"\n aria-label=\"Toggle admin menu\"\n class=\"admin-menu-button\"\n (click)=\"toggleAside()\">\n <mat-icon>menu</mat-icon>\n </button>\n\n <div class=\"admin-header-content\">\n <ng-content select=\"[header]\" />\n </div>\n </header>\n\n <!-- ============================================================= -->\n <!-- BODY -->\n <!-- ============================================================= -->\n\n <div class=\"admin-body\">\n <!-- =========================================================== -->\n <!-- MOBILE BACKDROP -->\n <!-- =========================================================== -->\n\n @if (asideOpened()) {\n <div\n class=\"admin-aside-backdrop\"\n (click)=\"closeAside()\"></div>\n }\n\n <!-- =========================================================== -->\n <!-- ASIDE -->\n <!-- =========================================================== -->\n\n <aside\n class=\"admin-aside\"\n [class.admin-aside-open]=\"asideOpened()\">\n <!-- ========================================================= -->\n <!-- SCROLLABLE ASIDE CONTENT -->\n <!-- ========================================================= -->\n\n <div class=\"admin-aside-content\">\n <mat-accordion class=\"admin-menu\">\n @for (level1 of routes; track level1.path) {\n @if (menuContext.isVisible(level1)) {\n @if (isExpandable(level1)) {\n <mat-expansion-panel\n [disabled]=\"menuContext.isDisabled(level1)\"\n [expanded]=\"\n isLevel1Active(level1) && !menuContext.isDisabled(level1)\n \"\n (expandedChange)=\"panelExpanded(level1, $event)\">\n <mat-expansion-panel-header\n [class.admin-menu-item-disabled]=\"\n menuContext.isDisabled(level1)\n \"\n [matTooltip]=\"menuContext.disabledTooltip(level1)\"\n (click)=\"level1Clicked(level1)\">\n <mat-panel-title>\n <div class=\"admin-menu-item\">\n @if (icon(level1)) {\n <mat-icon\n [ngStyle]=\"{\n color: color(level1),\n }\">\n {{ icon(level1) }}\n </mat-icon>\n }\n\n <span\n [class.font-semibold]=\"isLevel1Active(level1)\"\n [ngStyle]=\"{\n color: color(level1),\n }\">\n {{ label(level1) }}\n </span>\n </div>\n </mat-panel-title>\n </mat-expansion-panel-header>\n\n <div class=\"admin-level2\">\n @if (isLoading(level1)) {\n <div class=\"admin-loading\">Loading...</div>\n } @else {\n @for (\n level2 of getLevel2Routes(level1);\n track level2.path\n ) {\n @if (menuContext.isVisible(level2)) {\n <a\n class=\"admin-level2-item\"\n role=\"button\"\n [attr.tabindex]=\"\n menuContext.isDisabled(level2) ? -1 : 0\n \"\n [attr.aria-disabled]=\"\n menuContext.isDisabled(level2)\n \"\n [class.admin-menu-item-disabled]=\"\n menuContext.isDisabled(level2)\n \"\n [matTooltip]=\"menuContext.disabledTooltip(level2)\"\n [class.admin-level2-item-active]=\"\n isLevel2Active(level1, level2)\n \"\n (click)=\"level2Clicked(level1, level2)\"\n (keydown.enter)=\"level2Clicked(level1, level2)\"\n (keydown.space)=\"\n $event.preventDefault();\n level2Clicked(level1, level2)\n \">\n @if (icon(level2)) {\n <mat-icon\n [ngStyle]=\"{\n color: color(level2),\n }\">\n {{ icon(level2) }}\n </mat-icon>\n }\n\n <span\n [ngStyle]=\"{\n color: color(level2),\n }\">\n {{ label(level2) }}\n </span>\n </a>\n }\n }\n }\n </div>\n </mat-expansion-panel>\n } @else {\n <a\n class=\"admin-level1-item\"\n role=\"button\"\n [attr.tabindex]=\"menuContext.isDisabled(level1) ? -1 : 0\"\n [attr.aria-disabled]=\"menuContext.isDisabled(level1)\"\n [class.admin-menu-item-disabled]=\"\n menuContext.isDisabled(level1)\n \"\n [matTooltip]=\"menuContext.disabledTooltip(level1)\"\n [class.admin-level1-item-active]=\"isLevel1Active(level1)\"\n (click)=\"level1Clicked(level1)\"\n (keydown.enter)=\"level1Clicked(level1)\"\n (keydown.space)=\"\n $event.preventDefault(); level1Clicked(level1)\n \">\n @if (icon(level1)) {\n <mat-icon\n [ngStyle]=\"{\n color: color(level1),\n }\">\n {{ icon(level1) }}\n </mat-icon>\n }\n\n <span\n [ngStyle]=\"{\n color: color(level1),\n }\">\n {{ label(level1) }}\n </span>\n </a>\n }\n }\n }\n </mat-accordion>\n </div>\n\n <!-- ========================================================= -->\n <!-- ASIDE FOOTER -->\n <!-- ========================================================= -->\n\n <footer class=\"admin-aside-footer\">\n <ng-content select=\"[asideFooter]\" />\n </footer>\n </aside>\n\n <!-- =========================================================== -->\n <!-- MAIN -->\n <!-- =========================================================== -->\n\n <main class=\"admin-main\">\n <!-- ========================================================= -->\n <!-- LEVEL 3+ TABS -->\n <!-- ========================================================= -->\n\n <taon-admin-page-tabs\n class=\"admin-tabs\"\n [routes]=\"routes\"\n [basePath]=\"basePath\"\n [outlet]=\"outlet\" />\n\n <!-- ========================================================= -->\n <!-- SCROLLABLE CONTENT -->\n <!-- ========================================================= -->\n\n <div class=\"admin-content\">\n <router-outlet />\n </div>\n\n <!-- ========================================================= -->\n <!-- CONTENT FOOTER -->\n <!-- ========================================================= -->\n\n <footer class=\"admin-content-footer\">\n <ng-content select=\"[contentFooter]\" />\n </footer>\n </main>\n </div>\n\n <!-- ============================================================= -->\n <!-- DIALOG RESIZE HANDLE -->\n <!-- ============================================================= -->\n\n @if (isDialogMode) {\n <div\n class=\"admin-resize-handle\"\n (pointerdown)=\"startResize($event)\"></div>\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%;min-width:0;min-height:0}.admin-layout{position:relative;display:flex;flex-direction:column;min-width:0;min-height:0;overflow:hidden;background:#fff}.admin-layout-dialog{min-width:500px;min-height:350px}.admin-header{display:flex;align-items:center;flex:0 0 64px;height:64px;border-bottom:1px solid var(--mat-sys-outline-variant);background:#fff}.admin-menu-button{flex:0 0 auto;margin-left:8px;margin-right:8px;cursor:pointer}.admin-header-content{flex:1 1 auto;min-width:0}.admin-body{position:relative;display:flex;flex:1 1 auto;min-width:0;min-height:0;overflow:hidden}.admin-aside{position:absolute;top:0;bottom:0;left:0;z-index:30;display:flex;flex-direction:column;width:288px;min-height:0;overflow:hidden;background:#fff;border-right:1px solid var(--mat-sys-outline-variant);transform:translate(-100%);transition:transform .2s ease,width .2s ease}.admin-aside-open{transform:translate(0)}.admin-aside-content{flex:1 1 auto;min-height:0;overflow-x:hidden;overflow-y:auto}.admin-aside-footer{flex:0 0 auto;padding:12px;border-top:1px solid var(--mat-sys-outline-variant);background:#fff}.admin-menu{display:block}.admin-menu mat-expansion-panel{margin-top:0!important;margin-bottom:0!important;border-radius:0!important;box-shadow:none!important;border-bottom:1px solid var(--mat-sys-outline-variant)}.admin-menu mat-expansion-panel:first-of-type,.admin-menu mat-expansion-panel:last-of-type,.admin-menu mat-expansion-panel-header{border-radius:0!important}.admin-menu-item{display:flex;align-items:center;gap:12px}.admin-level2{display:flex;flex-direction:column}.admin-level2-item{display:flex;align-items:center;gap:12px;padding:10px 24px;cursor:pointer}.admin-level2-item:hover,.admin-level2-item-active{background:#f5f5f5}.admin-level2-item-active{font-weight:600}.admin-level1-item{display:flex;align-items:center;height:56px;gap:12px;padding:0 24px;cursor:pointer}.admin-level1-item:hover,.admin-level1-item-active{background:#f5f5f5}.admin-level1-item-active{font-weight:600}.admin-loading{padding:8px 16px;opacity:.6}.admin-menu-item-disabled{opacity:.5;cursor:not-allowed}.admin-main{display:flex;flex-direction:column;flex:1 1 auto;min-width:0;min-height:0;overflow:hidden}.admin-tabs{display:block;flex:0 0 auto}.admin-content{flex:1 1 auto;min-width:0;min-height:0;padding:24px;overflow-x:auto;overflow-y:auto}.admin-content-footer{flex:0 0 auto;padding:12px 24px;border-top:1px solid var(--mat-sys-outline-variant);background:#fff}.admin-aside-backdrop{position:absolute;inset:0;z-index:20;background:#0000004d}@media(min-width:768px){.admin-aside{position:relative;top:auto;bottom:auto;left:auto;z-index:auto;width:0;transform:none}.admin-aside-open{width:288px;min-width:288px}.admin-aside-backdrop{display:none}}.admin-resize-handle{position:absolute;right:0;bottom:0;z-index:100;width:18px;height:18px;cursor:nwse-resize}.admin-resize-handle:after{content:\"\";position:absolute;right:4px;bottom:4px;width:8px;height:8px;border-right:2px solid #777;border-bottom:2px solid #777}\n"] }]
904
950
  }], ctorParameters: () => [{ type: i1.Router }, { type: i0.ElementRef }], propDecorators: { routes: [{
905
951
  type: Input,
906
952
  args: [{ required: true }]
@@ -914,8 +960,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImpor
914
960
  }] } });
915
961
 
916
962
  const TaonBaselineBackofficeOutletName = 'admin';
963
+ //#region @browser
917
964
  function adminLazyRoute(options) {
918
- const { path, loader, menuItem, icon, color, expandable, hideInNavigation } = options;
965
+ const { path, loader, menuItem, icon, color, expandable, hideInNavigation, isDisabled, isDisabledTooltip, isVisible, } = options;
919
966
  return {
920
967
  path,
921
968
  data: {
@@ -924,11 +971,15 @@ function adminLazyRoute(options) {
924
971
  icon,
925
972
  expandable,
926
973
  hideInNavigation,
974
+ isDisabled,
975
+ isDisabledTooltip,
976
+ isVisible,
927
977
  },
928
978
  loadChildren: loader,
929
979
  loadAdminChildren: loader,
930
980
  };
931
981
  }
982
+ //#endregion
932
983
 
933
984
  class TaonBaseAdminLayoutPage {
934
985
  get hasActiveChildRoute() {
@@ -1022,7 +1073,7 @@ class TaonErrorDetailsDialogComponent {
1022
1073
  return `${protocol}://file${location}`;
1023
1074
  }
1024
1075
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonErrorDetailsDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1025
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.9", type: TaonErrorDetailsDialogComponent, isStandalone: true, selector: "taon-error-details-dialog", ngImport: i0, template: "<h2 mat-dialog-title>Error details</h2>\n\n<mat-dialog-content>\n <div class=\"error-message\">\n {{ data.message }}\n </div>\n\n <div class=\"editor-selector\">\n <span class=\"editor-selector-label\"> I am using editor: </span>\n\n <mat-button-toggle-group\n [value]=\"editor\"\n (change)=\"setEditor($event.value)\"\n aria-label=\"Code editor\">\n <mat-button-toggle value=\"vscode\"> VS Code </mat-button-toggle>\n\n <mat-button-toggle value=\"vscodium\"> VSCodium </mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n\n <pre\n class=\"error-details\"><code>@for (stackLine of stackLines; track $index) {\n@if (stackLine.file) {<a\n class=\"stack-link\"\n title=\"Open {{ stackLine.file }}:{{ stackLine.line }}:{{ stackLine.column }}\"\n (click)=\"openStackLine(stackLine)\">{{ stackLine.text }}</a>} @else {{{ stackLine.text }}}{{ '\\n' }}\n}</code></pre>\n</mat-dialog-content>\n\n<mat-dialog-actions align=\"end\">\n <button\n mat-button\n mat-dialog-close>\n Close\n </button>\n</mat-dialog-actions>\n", styles: [".error-message{margin-bottom:16px;font-weight:500}.editor-selector{display:flex;align-items:center;gap:12px;margin-bottom:12px}.editor-selector-label{font-size:13px}.error-details{box-sizing:border-box;max-width:100%;max-height:60vh;margin:0;padding:16px;overflow-y:auto;overflow-x:hidden;border-radius:6px;background:#0000000f;font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:12px;line-height:1.5;white-space:pre-wrap;overflow-wrap:anywhere}.stack-link{cursor:pointer;text-decoration:underline;text-decoration-style:dotted;text-underline-offset:2px}.stack-link:hover{background:#00000014}.stack-line{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word}\n"], dependencies: [{ kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1$1.MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: i1$1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1$1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1$1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatButtonToggleModule }, { kind: "directive", type: i3$2.MatButtonToggleGroup, selector: "mat-button-toggle-group", inputs: ["appearance", "name", "vertical", "value", "multiple", "disabled", "disabledInteractive", "hideSingleSelectionIndicator", "hideMultipleSelectionIndicator"], outputs: ["valueChange", "change"], exportAs: ["matButtonToggleGroup"] }, { kind: "component", type: i3$2.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }] }); }
1076
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.9", type: TaonErrorDetailsDialogComponent, isStandalone: true, selector: "taon-error-details-dialog", ngImport: i0, template: "<h2 mat-dialog-title>Error details</h2>\n\n<mat-dialog-content>\n <div class=\"error-message\">\n {{ data.message }}\n </div>\n\n <div class=\"editor-selector\">\n <span class=\"editor-selector-label\"> I am using editor: </span>\n\n <mat-button-toggle-group\n [value]=\"editor\"\n (change)=\"setEditor($event.value)\"\n aria-label=\"Code editor\">\n <mat-button-toggle value=\"vscode\"> VS Code </mat-button-toggle>\n\n <mat-button-toggle value=\"vscodium\"> VSCodium </mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n\n <pre\n class=\"error-details\"><code>@for (stackLine of stackLines; track $index) {\n@if (stackLine.file) {<a\n class=\"stack-link\"\n title=\"Open {{ stackLine.file }}:{{ stackLine.line }}:{{ stackLine.column }}\"\n (click)=\"openStackLine(stackLine)\">{{ stackLine.text }}</a>} @else {{{ stackLine.text }}}{{ '\\n' }}\n}</code></pre>\n</mat-dialog-content>\n\n<mat-dialog-actions align=\"end\">\n <button\n mat-button\n mat-dialog-close>\n Close\n </button>\n</mat-dialog-actions>\n", styles: [".error-message{margin-bottom:16px;font-weight:500}.editor-selector{display:flex;align-items:center;gap:12px;margin-bottom:12px}.editor-selector-label{font-size:13px}.error-details{box-sizing:border-box;max-width:100%;max-height:60vh;margin:0;padding:16px;overflow-y:auto;overflow-x:hidden;border-radius:6px;background:#0000000f;font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:12px;line-height:1.5;white-space:pre-wrap;overflow-wrap:anywhere}.stack-link{cursor:pointer;text-decoration:underline;text-decoration-style:dotted;text-underline-offset:2px}.stack-link:hover{background:#00000014}.stack-line{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word}\n"], dependencies: [{ kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1$1.MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: i1$1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1$1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1$1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatButtonToggleModule }, { kind: "directive", type: i3$3.MatButtonToggleGroup, selector: "mat-button-toggle-group", inputs: ["appearance", "name", "vertical", "value", "multiple", "disabled", "disabledInteractive", "hideSingleSelectionIndicator", "hideMultipleSelectionIndicator"], outputs: ["valueChange", "change"], exportAs: ["matButtonToggleGroup"] }, { kind: "component", type: i3$3.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }] }); }
1026
1077
  }
1027
1078
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonErrorDetailsDialogComponent, decorators: [{
1028
1079
  type: Component,
@@ -1143,13 +1194,13 @@ class TaonBackofficeNotificationsComponent {
1143
1194
  console.log({ error });
1144
1195
  }
1145
1196
  const statusCode = jsonMsg.status ?? error.statusCode;
1146
- const httpStatus = getHttpStatus(statusCode, jsonMsg.message);
1147
- if (this.taonAdminService.shouldSkipShowingError(httpStatus)) {
1197
+ const code = jsonMsg?.code || getHttpStatus(statusCode);
1198
+ if (this.taonAdminService.shouldSkipShowingError(code)) {
1148
1199
  console.log(`[@taon-dev/sudo] Skipping notificaiton for message "${jsonMsg.message}" (code=${statusCode})`);
1149
1200
  }
1150
1201
  else {
1151
1202
  this.notification.error({
1152
- title: jsonMsg?.message || t.gettext('Unknown error'),
1203
+ title: t.translate(jsonMsg.message) || t.gettext('Unknown error'),
1153
1204
  details: jsonMsg?.details,
1154
1205
  });
1155
1206
  }
@@ -1177,7 +1228,7 @@ class TaonConfirmDialogComponent {
1177
1228
  this.dialogRef.close(true);
1178
1229
  }
1179
1230
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonConfirmDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1180
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.9", type: TaonConfirmDialogComponent, isStandalone: true, selector: "taon-confirm-dialog", ngImport: i0, template: "<div\n class=\"flex min-w-[320px] max-w-[560px] flex-col gap-6 p-6 text-gray-900 dark:text-gray-100 sm:p-7\">\n <!-- Header -->\n <h2 class=\"m-0 text-xl leading-snug font-medium sm:text-2xl\">\n {{ data.title }}\n </h2>\n\n <!-- Message -->\n @if (data.message) {\n <p\n class=\"m-0 whitespace-pre-line text-base leading-relaxed text-gray-600 dark:text-gray-300 sm:text-lg\">\n {{ data.message }}\n </p>\n }\n\n <!-- Actions -->\n <div class=\"flex flex-wrap items-center justify-end gap-3\">\n <button\n mat-button\n type=\"button\"\n (click)=\"cancel()\">\n {{ data.cancelText || 'Cancel' }}\n </button>\n\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n class=\"!rounded-full !px-6\"\n (click)=\"confirm()\">\n {{ data.confirmText || 'OK' }}\n </button>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: MatDialogModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }] }); }
1231
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.9", type: TaonConfirmDialogComponent, isStandalone: true, selector: "taon-confirm-dialog", ngImport: i0, template: "<div\n class=\"flex min-w-[320px] max-w-[560px] flex-col gap-6 p-6 text-gray-900 dark:text-gray-100 sm:p-7\">\n <!-- Header -->\n <h2 class=\"m-0 text-xl leading-snug font-medium sm:text-2xl\">\n {{ data.title }}\n </h2>\n\n <!-- Message -->\n @if (data.message) {\n <p\n class=\"m-0 whitespace-pre-line text-base leading-relaxed text-gray-600 dark:text-gray-300 sm:text-lg\">\n {{ data.message }}\n </p>\n }\n\n <!-- Actions -->\n <div class=\"flex flex-wrap items-center justify-end gap-3\">\n <button\n mat-button\n type=\"button\"\n (click)=\"cancel()\">\n {{ data.cancelText || 'Cancel' }}\n </button>\n\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n class=\"!rounded-full !px-6\"\n (click)=\"confirm()\">\n {{ data.confirmText || 'OK' }}\n </button>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: MatDialogModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }] }); }
1181
1232
  }
1182
1233
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonConfirmDialogComponent, decorators: [{
1183
1234
  type: Component,
@@ -1504,7 +1555,7 @@ class TaonDatatableComponent {
1504
1555
  }
1505
1556
  }
1506
1557
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonDatatableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1507
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.9", type: TaonDatatableComponent, isStandalone: true, selector: "taon-datatable", inputs: { pageNumber: "pageNumber", pageSize: "pageSize", pageSizeOptions: "pageSizeOptions", allowedColumns: "allowedColumns", expansionTemplate: "expansionTemplate", entityCrudController: "entityCrudController", columns: "columns", hideSearch: "hideSearch", hideAddButton: "hideAddButton", safe: "safe", searchMode: "searchMode", callQueryMethod: "callQueryMethod", data: "data", showPaginator: "showPaginator" }, outputs: { expansionChange: "expansionChange", addingItem: "addingItem" }, ngImport: i0, template: "<div class=\"flex w-full flex-col gap-3\">\n <!-- #region toolbar -->\n @if (!hideSearch) {\n <div\n class=\"flex w-full flex-col gap-2 rounded-lg border border-gray-200 bg-white p-3 shadow-sm dark:border-gray-700 dark:bg-gray-900 md:flex-row md:items-center\">\n <!-- global search -->\n <mat-form-field\n appearance=\"outline\"\n subscriptSizing=\"dynamic\"\n class=\"min-w-0 flex-1\">\n <mat-icon matPrefix> search </mat-icon>\n\n <input\n matInput\n placeholder=\"Search...\"\n [value]=\"searchValue\"\n (input)=\"searchChanged($any($event.target).value)\" />\n\n @if (searchValue) {\n <button\n mat-icon-button\n matSuffix\n type=\"button\"\n (click)=\"clearSearch()\">\n <mat-icon>close</mat-icon>\n </button>\n }\n </mat-form-field>\n\n <!-- advanced search -->\n @if (searchMode === 'advanced') {\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"toggleAdvancedSearch()\">\n <mat-icon> tune </mat-icon>\n\n Filters\n\n @if (advancedSearchOpened) {\n <mat-icon> expand_less </mat-icon>\n } @else {\n <mat-icon> expand_more </mat-icon>\n }\n </button>\n }\n\n <!-- refresh -->\n <button\n mat-icon-button\n type=\"button\"\n title=\"Refresh\"\n (click)=\"reload()\">\n <mat-icon> refresh </mat-icon>\n </button>\n\n <!-- add -->\n @if (!hideAddButton) {\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n (click)=\"addingItem.emit()\">\n <mat-icon> add </mat-icon>\n\n Add\n </button>\n }\n </div>\n }\n <!-- #endregion -->\n\n <!-- #region advanced filters -->\n @if (!hideSearch && searchMode === 'advanced' && advancedSearchOpened) {\n <div\n class=\"grid grid-cols-1 gap-3 rounded-lg border border-gray-200 bg-gray-50 p-4 shadow-sm dark:border-gray-700 dark:bg-gray-800 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\">\n @for (column of filterableColumns; track column.field) {\n <mat-form-field\n appearance=\"outline\"\n subscriptSizing=\"dynamic\"\n class=\"w-full\">\n <mat-label>\n {{ column.header }}\n </mat-label>\n\n <input\n matInput\n [(ngModel)]=\"columnFilters[$any(column.field)]\"\n (keyup.enter)=\"applyAdvancedFilters()\" />\n </mat-form-field>\n }\n\n <div\n class=\"flex items-center justify-end gap-2 md:col-span-2 lg:col-span-3 xl:col-span-4\">\n <button\n mat-button\n type=\"button\"\n (click)=\"clearSearch()\">\n <mat-icon> filter_alt_off </mat-icon>\n\n Clear\n </button>\n\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n (click)=\"applyAdvancedFilters()\">\n <mat-icon> filter_alt </mat-icon>\n\n Apply filters\n </button>\n </div>\n </div>\n }\n <!-- #endregion -->\n\n <!-- #region grid -->\n <div\n class=\"relative min-h-[250px] overflow-hidden rounded-lg border border-gray-200 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-900\">\n @if (isLoading) {\n <div\n class=\"absolute inset-0 z-10 flex items-center justify-center bg-white/60 backdrop-blur-[1px] dark:bg-gray-900/60\">\n <div\n class=\"flex items-center gap-2 rounded-md bg-white px-4 py-2 shadow dark:bg-gray-800\">\n <mat-icon class=\"animate-spin\"> refresh </mat-icon>\n\n Loading...\n </div>\n </div>\n }\n\n <mtx-grid\n [data]=\"rows\"\n [columns]=\"columns\"\n [expandable]=\"expandable\"\n [expansionTemplate]=\"lazyExpansionTemplate\"\n [length]=\"totalElements\"\n [pageOnFront]=\"false\"\n [pageIndex]=\"pageNumber - 1\"\n [pageSize]=\"pageSize\"\n [pageSizeOptions]=\"pageSizeOptions\"\n [showPaginator]=\"showPaginator\"\n (expansionChange)=\"onExpansionChange($event)\"\n (page)=\"getNextPage($event)\"\n (sortChange)=\"sortChanged($event)\">\n </mtx-grid>\n\n <ng-template\n #lazyExpansionTemplate\n let-row>\n @if (isExpanded(row)) {\n <ng-container\n [ngTemplateOutlet]=\"expansionTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: row }\">\n </ng-container>\n }\n </ng-template>\n </div>\n <!-- #endregion -->\n</div>\n", styles: [":host{display:block;width:100%;min-height:250px}:host ::ng-deep .mat-mdc-form-field{margin:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i4$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i6.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MtxGridModule }, { kind: "component", type: i7.MtxGrid, selector: "mtx-grid", inputs: ["displayedColumns", "columns", "data", "length", "loading", "trackBy", "columnResizable", "emptyValuePlaceholder", "pageOnFront", "showPaginator", "pageDisabled", "showFirstLastButtons", "pageIndex", "pageSize", "pageSizeOptions", "hidePageSize", "paginationTemplate", "sortOnFront", "sortActive", "sortDirection", "sortDisableClear", "sortDisabled", "sortStart", "rowHover", "rowStriped", "expandable", "expansionTemplate", "multiSelectable", "multiSelectionWithClick", "rowSelectable", "hideRowSelectionCheckbox", "disableRowClickSelection", "rowSelectionFormatter", "rowClassFormatter", "rowSelected", "cellSelectable", "showToolbar", "toolbarTitle", "toolbarTemplate", "columnHideable", "columnHideableChecked", "columnSortable", "columnPinnable", "columnPinOptions", "showColumnMenuButton", "columnMenuButtonText", "columnMenuButtonType", "columnMenuButtonColor", "columnMenuButtonClass", "columnMenuButtonIcon", "columnMenuButtonFontIcon", "columnMenuButtonSvgIcon", "showColumnMenuHeader", "columnMenuHeaderText", "columnMenuHeaderTemplate", "showColumnMenuFooter", "columnMenuFooterText", "columnMenuFooterTemplate", "noResultText", "noResultTemplate", "headerTemplate", "headerExtraTemplate", "cellTemplate", "useContentRowTemplate", "useContentHeaderRowTemplate", "useContentFooterRowTemplate", "showSummary", "summaryTemplate", "showSidebar", "sidebarTemplate", "showStatusbar", "statusbarTemplate"], outputs: ["page", "sortChange", "rowClick", "rowContextMenu", "expansionChange", "rowSelectedChange", "cellSelectedChange", "columnChange"], exportAs: ["mtxGrid"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1558
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.9", type: TaonDatatableComponent, isStandalone: true, selector: "taon-datatable", inputs: { pageNumber: "pageNumber", pageSize: "pageSize", pageSizeOptions: "pageSizeOptions", allowedColumns: "allowedColumns", expansionTemplate: "expansionTemplate", entityCrudController: "entityCrudController", columns: "columns", hideSearch: "hideSearch", hideAddButton: "hideAddButton", safe: "safe", searchMode: "searchMode", callQueryMethod: "callQueryMethod", data: "data", showPaginator: "showPaginator" }, outputs: { expansionChange: "expansionChange", addingItem: "addingItem" }, ngImport: i0, template: "<div class=\"flex w-full flex-col gap-3\">\n <!-- #region toolbar -->\n @if (!hideSearch) {\n <div\n class=\"flex w-full flex-col gap-2 rounded-lg border border-gray-200 bg-white p-3 shadow-sm dark:border-gray-700 dark:bg-gray-900 md:flex-row md:items-center\">\n <!-- global search -->\n <mat-form-field\n appearance=\"outline\"\n subscriptSizing=\"dynamic\"\n class=\"min-w-0 flex-1\">\n <mat-icon matPrefix> search </mat-icon>\n\n <input\n matInput\n placeholder=\"Search...\"\n [value]=\"searchValue\"\n (input)=\"searchChanged($any($event.target).value)\" />\n\n @if (searchValue) {\n <button\n mat-icon-button\n matSuffix\n type=\"button\"\n (click)=\"clearSearch()\">\n <mat-icon>close</mat-icon>\n </button>\n }\n </mat-form-field>\n\n <!-- advanced search -->\n @if (searchMode === 'advanced') {\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"toggleAdvancedSearch()\">\n <mat-icon> tune </mat-icon>\n\n Filters\n\n @if (advancedSearchOpened) {\n <mat-icon> expand_less </mat-icon>\n } @else {\n <mat-icon> expand_more </mat-icon>\n }\n </button>\n }\n\n <!-- refresh -->\n <button\n mat-icon-button\n type=\"button\"\n title=\"Refresh\"\n (click)=\"reload()\">\n <mat-icon> refresh </mat-icon>\n </button>\n\n <!-- add -->\n @if (!hideAddButton) {\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n (click)=\"addingItem.emit()\">\n <mat-icon> add </mat-icon>\n\n Add\n </button>\n }\n </div>\n }\n <!-- #endregion -->\n\n <!-- #region advanced filters -->\n @if (!hideSearch && searchMode === 'advanced' && advancedSearchOpened) {\n <div\n class=\"grid grid-cols-1 gap-3 rounded-lg border border-gray-200 bg-gray-50 p-4 shadow-sm dark:border-gray-700 dark:bg-gray-800 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\">\n @for (column of filterableColumns; track column.field) {\n <mat-form-field\n appearance=\"outline\"\n subscriptSizing=\"dynamic\"\n class=\"w-full\">\n <mat-label>\n {{ column.header }}\n </mat-label>\n\n <input\n matInput\n [(ngModel)]=\"columnFilters[$any(column.field)]\"\n (keyup.enter)=\"applyAdvancedFilters()\" />\n </mat-form-field>\n }\n\n <div\n class=\"flex items-center justify-end gap-2 md:col-span-2 lg:col-span-3 xl:col-span-4\">\n <button\n mat-button\n type=\"button\"\n (click)=\"clearSearch()\">\n <mat-icon> filter_alt_off </mat-icon>\n\n Clear\n </button>\n\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n (click)=\"applyAdvancedFilters()\">\n <mat-icon> filter_alt </mat-icon>\n\n Apply filters\n </button>\n </div>\n </div>\n }\n <!-- #endregion -->\n\n <!-- #region grid -->\n <div\n class=\"relative min-h-[250px] overflow-hidden rounded-lg border border-gray-200 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-900\">\n @if (isLoading) {\n <div\n class=\"absolute inset-0 z-10 flex items-center justify-center bg-white/60 backdrop-blur-[1px] dark:bg-gray-900/60\">\n <div\n class=\"flex items-center gap-2 rounded-md bg-white px-4 py-2 shadow dark:bg-gray-800\">\n <mat-icon class=\"animate-spin\"> refresh </mat-icon>\n\n Loading...\n </div>\n </div>\n }\n\n <mtx-grid\n [data]=\"rows\"\n [columns]=\"columns\"\n [expandable]=\"expandable\"\n [expansionTemplate]=\"lazyExpansionTemplate\"\n [length]=\"totalElements\"\n [pageOnFront]=\"false\"\n [pageIndex]=\"pageNumber - 1\"\n [pageSize]=\"pageSize\"\n [pageSizeOptions]=\"pageSizeOptions\"\n [showPaginator]=\"showPaginator\"\n (expansionChange)=\"onExpansionChange($event)\"\n (page)=\"getNextPage($event)\"\n (sortChange)=\"sortChanged($event)\">\n </mtx-grid>\n\n <ng-template\n #lazyExpansionTemplate\n let-row>\n @if (isExpanded(row)) {\n <ng-container\n [ngTemplateOutlet]=\"expansionTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: row }\">\n </ng-container>\n }\n </ng-template>\n </div>\n <!-- #endregion -->\n</div>\n", styles: [":host{display:block;width:100%;min-height:250px}:host ::ng-deep .mat-mdc-form-field{margin:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3$2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i4$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i6.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MtxGridModule }, { kind: "component", type: i7.MtxGrid, selector: "mtx-grid", inputs: ["displayedColumns", "columns", "data", "length", "loading", "trackBy", "columnResizable", "emptyValuePlaceholder", "pageOnFront", "showPaginator", "pageDisabled", "showFirstLastButtons", "pageIndex", "pageSize", "pageSizeOptions", "hidePageSize", "paginationTemplate", "sortOnFront", "sortActive", "sortDirection", "sortDisableClear", "sortDisabled", "sortStart", "rowHover", "rowStriped", "expandable", "expansionTemplate", "multiSelectable", "multiSelectionWithClick", "rowSelectable", "hideRowSelectionCheckbox", "disableRowClickSelection", "rowSelectionFormatter", "rowClassFormatter", "rowSelected", "cellSelectable", "showToolbar", "toolbarTitle", "toolbarTemplate", "columnHideable", "columnHideableChecked", "columnSortable", "columnPinnable", "columnPinOptions", "showColumnMenuButton", "columnMenuButtonText", "columnMenuButtonType", "columnMenuButtonColor", "columnMenuButtonClass", "columnMenuButtonIcon", "columnMenuButtonFontIcon", "columnMenuButtonSvgIcon", "showColumnMenuHeader", "columnMenuHeaderText", "columnMenuHeaderTemplate", "showColumnMenuFooter", "columnMenuFooterText", "columnMenuFooterTemplate", "noResultText", "noResultTemplate", "headerTemplate", "headerExtraTemplate", "cellTemplate", "useContentRowTemplate", "useContentHeaderRowTemplate", "useContentFooterRowTemplate", "showSummary", "summaryTemplate", "showSidebar", "sidebarTemplate", "showStatusbar", "statusbarTemplate"], outputs: ["page", "sortChange", "rowClick", "rowContextMenu", "expansionChange", "rowSelectedChange", "cellSelectedChange", "columnChange"], exportAs: ["mtxGrid"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1508
1559
  }
1509
1560
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonDatatableComponent, decorators: [{
1510
1561
  type: Component,
@@ -1599,12 +1650,19 @@ var TaonDraggableButtonPanelState;
1599
1650
  //#region imports
1600
1651
  //#endregion
1601
1652
  class TaonDraggableButtonPanelComponent {
1653
+ set context(context) {
1654
+ this.menuContext.context.set(context);
1655
+ }
1602
1656
  set state(state) {
1603
1657
  this.currentState.set(state);
1604
1658
  }
1659
+ //#endregion
1660
+ //#region constructor
1605
1661
  constructor(router) {
1606
1662
  this.router = router;
1663
+ //#region fields and getters
1607
1664
  this.State = TaonDraggableButtonPanelState;
1665
+ this.menuContext = inject(TaonAdminMenuContext);
1608
1666
  this.basePath = '';
1609
1667
  this.buttonIcon = 'admin_panel_settings';
1610
1668
  this.title = 'Admin';
@@ -1624,7 +1682,10 @@ class TaonDraggableButtonPanelComponent {
1624
1682
  }
1625
1683
  };
1626
1684
  this.currentState = signal(TaonDraggableButtonPanelState.CLICKABLE_BUTTON, ...(ngDevMode ? [{ debugName: "currentState" }] : []));
1685
+ this.autoHideIcon = true;
1627
1686
  }
1687
+ //#endregion
1688
+ //#region methods / ng on init
1628
1689
  async ngOnInit() {
1629
1690
  window.addEventListener('resize', this.resizeListener);
1630
1691
  /**
@@ -1673,10 +1734,14 @@ class TaonDraggableButtonPanelComponent {
1673
1734
  this.restoreStateFromUrl();
1674
1735
  });
1675
1736
  }
1737
+ //#endregion
1738
+ //#region methods / ng on destroy
1676
1739
  ngOnDestroy() {
1677
1740
  window.removeEventListener('resize', this.resizeListener);
1678
1741
  this.routerSubscription?.unsubscribe();
1679
1742
  }
1743
+ //#endregion
1744
+ //#region methods / toogle fullscreen
1680
1745
  toogleFullscreen(event) {
1681
1746
  event.stopImmediatePropagation();
1682
1747
  event.stopPropagation();
@@ -1696,6 +1761,8 @@ class TaonDraggableButtonPanelComponent {
1696
1761
  void this.setState(TaonDraggableButtonPanelState.WINDOW);
1697
1762
  }
1698
1763
  }
1764
+ //#endregion
1765
+ //#region methods / effective state
1699
1766
  effectiveState() {
1700
1767
  if (this.forceFullScreen() &&
1701
1768
  this.currentState() !== TaonDraggableButtonPanelState.CLICKABLE_BUTTON) {
@@ -1703,9 +1770,11 @@ class TaonDraggableButtonPanelComponent {
1703
1770
  }
1704
1771
  return this.currentState();
1705
1772
  }
1773
+ //#endregion
1706
1774
  // ===========================================================================
1707
1775
  // INITIAL ROUTING
1708
1776
  // ===========================================================================
1777
+ //#region methods / wait for initial navigation
1709
1778
  async waitForInitialNavigation() {
1710
1779
  /**
1711
1780
  * Router already completed at least one navigation.
@@ -1719,6 +1788,8 @@ class TaonDraggableButtonPanelComponent {
1719
1788
  */
1720
1789
  await firstValueFrom(this.router.events.pipe(filter(event => event instanceof NavigationEnd), take(1)));
1721
1790
  }
1791
+ //#endregion
1792
+ //#region methods / ensure outlet base path
1722
1793
  async ensureOutletBasePath() {
1723
1794
  if (!this.outlet || !this.basePath) {
1724
1795
  return;
@@ -1775,9 +1846,11 @@ class TaonDraggableButtonPanelComponent {
1775
1846
  replaceUrl: true,
1776
1847
  });
1777
1848
  }
1849
+ //#endregion
1778
1850
  // ===========================================================================
1779
1851
  // STATE
1780
1852
  // ===========================================================================
1853
+ //#region methods / set state
1781
1854
  async setState(next) {
1782
1855
  if (!this.isTransitionAllowed(next)) {
1783
1856
  return;
@@ -1786,23 +1859,44 @@ class TaonDraggableButtonPanelComponent {
1786
1859
  this.stateChange.emit(next);
1787
1860
  await this.saveStateToUrl(next);
1788
1861
  }
1862
+ //#endregion
1863
+ //#region methods / open
1789
1864
  open() {
1790
1865
  void this.setState(TaonDraggableButtonPanelState.WINDOW);
1791
1866
  }
1867
+ toggle() {
1868
+ if (this.currentState() === this.State.CLICKABLE_BUTTON) {
1869
+ this.open();
1870
+ }
1871
+ else {
1872
+ this.returnToButton();
1873
+ }
1874
+ }
1875
+ //#endregion
1876
+ //#region methods / return to button
1792
1877
  returnToButton() {
1878
+ this.resetDragPosition();
1793
1879
  void this.setState(TaonDraggableButtonPanelState.CLICKABLE_BUTTON);
1794
1880
  }
1881
+ //#endregion
1882
+ //#region methods / window mode
1795
1883
  windowMode() {
1796
1884
  void this.setState(TaonDraggableButtonPanelState.WINDOW);
1797
1885
  }
1886
+ //#endregion
1887
+ //#region methods / full screen draggable
1798
1888
  fullScreenDraggable() {
1799
1889
  this.resetDragPosition();
1800
1890
  void this.setState(TaonDraggableButtonPanelState.FULL_SCREEN_DRAGGABLE);
1801
1891
  }
1892
+ //#endregion
1893
+ //#region methods / lock full screen
1802
1894
  lockFullScreen() {
1803
1895
  this.resetDragPosition();
1804
1896
  void this.setState(TaonDraggableButtonPanelState.FULL_SCREEN_LOCKED);
1805
1897
  }
1898
+ //#endregion
1899
+ //#region methods / is dragging enabled
1806
1900
  isDraggingEnabled() {
1807
1901
  if (this.forceFullScreen()) {
1808
1902
  return false;
@@ -1811,9 +1905,11 @@ class TaonDraggableButtonPanelComponent {
1811
1905
  this.currentState() ===
1812
1906
  TaonDraggableButtonPanelState.FULL_SCREEN_DRAGGABLE);
1813
1907
  }
1908
+ //#endregion
1814
1909
  // ===========================================================================
1815
1910
  // URL STATE
1816
1911
  // ===========================================================================
1912
+ //#region methods / restore state from url
1817
1913
  restoreStateFromUrl() {
1818
1914
  if (!this.outlet) {
1819
1915
  return;
@@ -1856,6 +1952,8 @@ class TaonDraggableButtonPanelComponent {
1856
1952
  });
1857
1953
  }
1858
1954
  }
1955
+ //#endregion
1956
+ //#region methods / save state to url
1859
1957
  async saveStateToUrl(state) {
1860
1958
  if (!this.outlet) {
1861
1959
  return;
@@ -1883,18 +1981,24 @@ class TaonDraggableButtonPanelComponent {
1883
1981
  replaceUrl: true,
1884
1982
  });
1885
1983
  }
1984
+ //#endregion
1985
+ //#region methods / is valid state
1886
1986
  isValidState(value) {
1887
1987
  return Object.values(TaonDraggableButtonPanelState).includes(value);
1888
1988
  }
1989
+ //#endregion
1889
1990
  // ===========================================================================
1890
1991
  // DRAG
1891
1992
  // ===========================================================================
1993
+ //#region methods / reset drag position
1892
1994
  resetDragPosition() {
1893
1995
  this.drag?.reset();
1894
1996
  }
1997
+ //#endregion
1895
1998
  // ===========================================================================
1896
1999
  // TRANSITIONS
1897
2000
  // ===========================================================================
2001
+ //#region methods / is transition allowed
1898
2002
  isTransitionAllowed(next) {
1899
2003
  const current = this.currentState();
1900
2004
  const transitions = {
@@ -1920,7 +2024,7 @@ class TaonDraggableButtonPanelComponent {
1920
2024
  return transitions[current].includes(next);
1921
2025
  }
1922
2026
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonDraggableButtonPanelComponent, deps: [{ token: i1.Router }], target: i0.ɵɵFactoryTarget.Component }); }
1923
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.9", type: TaonDraggableButtonPanelComponent, isStandalone: true, selector: "taon-draggable-button-panel", inputs: { basePath: "basePath", buttonIcon: "buttonIcon", title: "title", outlet: "outlet", stateParamName: "stateParamName", state: "state" }, outputs: { stateChange: "stateChange" }, viewQueries: [{ propertyName: "drag", first: true, predicate: CdkDrag, descendants: true }], ngImport: i0, template: "@if (currentState() === State.CLICKABLE_BUTTON) {\n <!-- #region BUTTON - works on desktop AND mobile -->\n <button\n class=\"taon-admin-panel-toogle-open-button\"\n mat-icon-button\n type=\"button\"\n [matTooltip]=\"title\"\n (click)=\"open()\">\n <mat-icon>\n {{ buttonIcon }}\n </mat-icon>\n </button>\n <!-- #endregion -->\n} @else {\n <section\n cdkDrag\n [cdkDragDisabled]=\"!isDraggingEnabled()\"\n class=\"taon-draggable-panel\"\n [class.taon-window]=\"effectiveState() === State.WINDOW\"\n [class.taon-window-full]=\"effectiveState() !== State.WINDOW\"\n [class.taon-fullscreen]=\"\n effectiveState() === State.FULL_SCREEN_DRAGGABLE ||\n effectiveState() === State.FULL_SCREEN_LOCKED\n \">\n <header\n cdkDragHandle\n (dblclick)=\"toogleFullscreen($event)\"\n class=\"taon-window-bar\">\n <div class=\"flex min-w-0 flex-1 items-center gap-2\">\n <mat-icon class=\"text-gray-500\"> admin_panel_settings </mat-icon>\n\n <span class=\"truncate font-medium\">\n {{ title }}\n </span>\n </div>\n\n <!-- #region window buttons -->\n <div class=\"flex shrink-0 items-center\">\n <!-- Desktop-only window/fullscreen controls -->\n @if (!forceFullScreen()) {\n @if (currentState() !== State.WINDOW) {\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Window mode\"\n (click)=\"windowMode()\">\n <mat-icon>filter_none</mat-icon>\n </button>\n }\n\n @if (currentState() === State.WINDOW) {\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Full screen\"\n (click)=\"fullScreenDraggable()\">\n <mat-icon>fullscreen</mat-icon>\n </button>\n }\n\n @if (currentState() !== State.FULL_SCREEN_LOCKED) {\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Lock full screen\"\n (click)=\"lockFullScreen()\">\n <mat-icon>lock_open</mat-icon>\n </button>\n } @else {\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Unlock\"\n (click)=\"fullScreenDraggable()\">\n <mat-icon>lock</mat-icon>\n </button>\n }\n }\n\n <!-- ALWAYS available -->\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Close admin panel\"\n (click)=\"returnToButton()\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n <!-- #endregion -->\n </header>\n\n <div class=\"min-h-0 flex-1 overflow-hidden\">\n <ng-content />\n </div>\n </section>\n}\n", styles: [":host{display:contents}.taon-draggable-panel{position:fixed;display:flex;flex-direction:column;min-width:320px;min-height:240px;overflow:hidden;background:#fff;border:1px solid #e5e7eb;box-shadow:0 20px 50px #00000026;z-index:1000}.taon-window{width:70vw;height:70vh;left:15vw;top:25vh;border-radius:6px}.taon-fullscreen{width:100vw;height:100dvh;left:0;top:0;border-radius:0}.taon-window-bar{height:48px;display:flex;align-items:center;flex-shrink:0;padding-left:12px;border-bottom:1px solid #e5e7eb;background:#fafafa;-webkit-user-select:none;user-select:none;cursor:move}\n"], dependencies: [{ kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i4$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2027
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.9", type: TaonDraggableButtonPanelComponent, isStandalone: true, selector: "taon-draggable-button-panel", inputs: { context: "context", basePath: "basePath", buttonIcon: "buttonIcon", title: "title", outlet: "outlet", stateParamName: "stateParamName", state: "state", autoHideIcon: "autoHideIcon", iconTemplate: "iconTemplate", headerCenterTemplate: "headerCenterTemplate" }, outputs: { stateChange: "stateChange" }, providers: [TaonAdminMenuContext], viewQueries: [{ propertyName: "drag", first: true, predicate: CdkDrag, descendants: true }], ngImport: i0, template: "@if (currentState() === State.CLICKABLE_BUTTON || !autoHideIcon) {\n @if (iconTemplate) {\n <ng-container [ngTemplateOutlet]=\"iconTemplate\" />\n } @else {\n <button\n class=\"taon-admin-panel-toogle-open-button\"\n [class.taon-admin-panel-toogle-button-dimmer-opacity]=\"\n effectiveState() !== State.CLICKABLE_BUTTON\n \"\n mat-icon-button\n type=\"button\"\n [matTooltip]=\"title\"\n (click)=\"toggle()\">\n <mat-icon>{{ buttonIcon }}</mat-icon>\n </button>\n }\n}\n\n<section\n cdkDrag\n [hidden]=\"currentState() === State.CLICKABLE_BUTTON\"\n [cdkDragDisabled]=\"!isDraggingEnabled()\"\n class=\"taon-draggable-panel\"\n [class.taon-window]=\"effectiveState() === State.WINDOW\"\n [class.taon-window-full]=\"effectiveState() !== State.WINDOW\"\n [class.taon-fullscreen]=\"\n effectiveState() === State.FULL_SCREEN_DRAGGABLE ||\n effectiveState() === State.FULL_SCREEN_LOCKED\n \">\n <header\n cdkDragHandle\n (dblclick)=\"toogleFullscreen($event)\"\n class=\"taon-window-bar\">\n <!-- LEFT -->\n <div class=\"flex min-w-0 flex-1 items-center gap-2\">\n <mat-icon class=\"text-gray-500\"> admin_panel_settings </mat-icon>\n\n <span class=\"truncate font-medium\">\n {{ title }}\n </span>\n </div>\n\n <!-- EXACT CENTER -->\n <div class=\"flex min-w-0 items-center justify-center\">\n @if (headerCenterTemplate) {\n <ng-container [ngTemplateOutlet]=\"headerCenterTemplate\" />\n }\n </div>\n\n <!-- #region window buttons -->\n <div class=\"flex shrink-0 items-center\">\n <!-- Desktop-only window/fullscreen controls -->\n @if (!forceFullScreen()) {\n @if (currentState() !== State.WINDOW) {\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Window mode\"\n (click)=\"windowMode()\">\n <mat-icon>filter_none</mat-icon>\n </button>\n }\n\n @if (currentState() === State.WINDOW) {\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Full screen\"\n (click)=\"fullScreenDraggable()\">\n <mat-icon>fullscreen</mat-icon>\n </button>\n }\n\n @if (currentState() !== State.FULL_SCREEN_LOCKED) {\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Lock full screen\"\n (click)=\"lockFullScreen()\">\n <mat-icon>lock_open</mat-icon>\n </button>\n } @else {\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Unlock\"\n (click)=\"fullScreenDraggable()\">\n <mat-icon>lock</mat-icon>\n </button>\n }\n }\n\n <!-- ALWAYS available -->\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Close admin panel\"\n (click)=\"returnToButton()\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n <!-- #endregion -->\n </header>\n\n <div class=\"min-h-0 flex-1 overflow-hidden\">\n <ng-content />\n </div>\n</section>\n", styles: [":host{display:contents}.taon-admin-panel-toogle-button-dimmer-opacity{opacity:.6}.taon-draggable-panel{position:fixed;display:flex;flex-direction:column;min-width:320px;min-height:240px;overflow:hidden;background:#fff;border:1px solid #e5e7eb;box-shadow:0 20px 50px #00000026;z-index:1000}.taon-window{width:70vw;height:70vh;left:15vw;top:25vh;border-radius:6px}.taon-fullscreen{width:100vw;height:100dvh;left:0;top:0;border-radius:0}.taon-window-bar{height:48px;display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;flex-shrink:0;padding-left:12px;border-bottom:1px solid #e5e7eb;background:#fafafa;-webkit-user-select:none;user-select:none;cursor:move}\n"], dependencies: [{ kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1924
2028
  }
1925
2029
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonDraggableButtonPanelComponent, decorators: [{
1926
2030
  type: Component,
@@ -1928,11 +2032,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImpor
1928
2032
  CdkDrag,
1929
2033
  CdkDragHandle,
1930
2034
  NgIf,
2035
+ NgTemplateOutlet,
1931
2036
  MatButtonModule,
1932
2037
  MatIconModule,
1933
2038
  MatTooltipModule,
1934
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (currentState() === State.CLICKABLE_BUTTON) {\n <!-- #region BUTTON - works on desktop AND mobile -->\n <button\n class=\"taon-admin-panel-toogle-open-button\"\n mat-icon-button\n type=\"button\"\n [matTooltip]=\"title\"\n (click)=\"open()\">\n <mat-icon>\n {{ buttonIcon }}\n </mat-icon>\n </button>\n <!-- #endregion -->\n} @else {\n <section\n cdkDrag\n [cdkDragDisabled]=\"!isDraggingEnabled()\"\n class=\"taon-draggable-panel\"\n [class.taon-window]=\"effectiveState() === State.WINDOW\"\n [class.taon-window-full]=\"effectiveState() !== State.WINDOW\"\n [class.taon-fullscreen]=\"\n effectiveState() === State.FULL_SCREEN_DRAGGABLE ||\n effectiveState() === State.FULL_SCREEN_LOCKED\n \">\n <header\n cdkDragHandle\n (dblclick)=\"toogleFullscreen($event)\"\n class=\"taon-window-bar\">\n <div class=\"flex min-w-0 flex-1 items-center gap-2\">\n <mat-icon class=\"text-gray-500\"> admin_panel_settings </mat-icon>\n\n <span class=\"truncate font-medium\">\n {{ title }}\n </span>\n </div>\n\n <!-- #region window buttons -->\n <div class=\"flex shrink-0 items-center\">\n <!-- Desktop-only window/fullscreen controls -->\n @if (!forceFullScreen()) {\n @if (currentState() !== State.WINDOW) {\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Window mode\"\n (click)=\"windowMode()\">\n <mat-icon>filter_none</mat-icon>\n </button>\n }\n\n @if (currentState() === State.WINDOW) {\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Full screen\"\n (click)=\"fullScreenDraggable()\">\n <mat-icon>fullscreen</mat-icon>\n </button>\n }\n\n @if (currentState() !== State.FULL_SCREEN_LOCKED) {\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Lock full screen\"\n (click)=\"lockFullScreen()\">\n <mat-icon>lock_open</mat-icon>\n </button>\n } @else {\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Unlock\"\n (click)=\"fullScreenDraggable()\">\n <mat-icon>lock</mat-icon>\n </button>\n }\n }\n\n <!-- ALWAYS available -->\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Close admin panel\"\n (click)=\"returnToButton()\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n <!-- #endregion -->\n </header>\n\n <div class=\"min-h-0 flex-1 overflow-hidden\">\n <ng-content />\n </div>\n </section>\n}\n", styles: [":host{display:contents}.taon-draggable-panel{position:fixed;display:flex;flex-direction:column;min-width:320px;min-height:240px;overflow:hidden;background:#fff;border:1px solid #e5e7eb;box-shadow:0 20px 50px #00000026;z-index:1000}.taon-window{width:70vw;height:70vh;left:15vw;top:25vh;border-radius:6px}.taon-fullscreen{width:100vw;height:100dvh;left:0;top:0;border-radius:0}.taon-window-bar{height:48px;display:flex;align-items:center;flex-shrink:0;padding-left:12px;border-bottom:1px solid #e5e7eb;background:#fafafa;-webkit-user-select:none;user-select:none;cursor:move}\n"] }]
1935
- }], ctorParameters: () => [{ type: i1.Router }], propDecorators: { drag: [{
2039
+ ], changeDetection: ChangeDetectionStrategy.OnPush, providers: [TaonAdminMenuContext], template: "@if (currentState() === State.CLICKABLE_BUTTON || !autoHideIcon) {\n @if (iconTemplate) {\n <ng-container [ngTemplateOutlet]=\"iconTemplate\" />\n } @else {\n <button\n class=\"taon-admin-panel-toogle-open-button\"\n [class.taon-admin-panel-toogle-button-dimmer-opacity]=\"\n effectiveState() !== State.CLICKABLE_BUTTON\n \"\n mat-icon-button\n type=\"button\"\n [matTooltip]=\"title\"\n (click)=\"toggle()\">\n <mat-icon>{{ buttonIcon }}</mat-icon>\n </button>\n }\n}\n\n<section\n cdkDrag\n [hidden]=\"currentState() === State.CLICKABLE_BUTTON\"\n [cdkDragDisabled]=\"!isDraggingEnabled()\"\n class=\"taon-draggable-panel\"\n [class.taon-window]=\"effectiveState() === State.WINDOW\"\n [class.taon-window-full]=\"effectiveState() !== State.WINDOW\"\n [class.taon-fullscreen]=\"\n effectiveState() === State.FULL_SCREEN_DRAGGABLE ||\n effectiveState() === State.FULL_SCREEN_LOCKED\n \">\n <header\n cdkDragHandle\n (dblclick)=\"toogleFullscreen($event)\"\n class=\"taon-window-bar\">\n <!-- LEFT -->\n <div class=\"flex min-w-0 flex-1 items-center gap-2\">\n <mat-icon class=\"text-gray-500\"> admin_panel_settings </mat-icon>\n\n <span class=\"truncate font-medium\">\n {{ title }}\n </span>\n </div>\n\n <!-- EXACT CENTER -->\n <div class=\"flex min-w-0 items-center justify-center\">\n @if (headerCenterTemplate) {\n <ng-container [ngTemplateOutlet]=\"headerCenterTemplate\" />\n }\n </div>\n\n <!-- #region window buttons -->\n <div class=\"flex shrink-0 items-center\">\n <!-- Desktop-only window/fullscreen controls -->\n @if (!forceFullScreen()) {\n @if (currentState() !== State.WINDOW) {\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Window mode\"\n (click)=\"windowMode()\">\n <mat-icon>filter_none</mat-icon>\n </button>\n }\n\n @if (currentState() === State.WINDOW) {\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Full screen\"\n (click)=\"fullScreenDraggable()\">\n <mat-icon>fullscreen</mat-icon>\n </button>\n }\n\n @if (currentState() !== State.FULL_SCREEN_LOCKED) {\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Lock full screen\"\n (click)=\"lockFullScreen()\">\n <mat-icon>lock_open</mat-icon>\n </button>\n } @else {\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Unlock\"\n (click)=\"fullScreenDraggable()\">\n <mat-icon>lock</mat-icon>\n </button>\n }\n }\n\n <!-- ALWAYS available -->\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Close admin panel\"\n (click)=\"returnToButton()\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n <!-- #endregion -->\n </header>\n\n <div class=\"min-h-0 flex-1 overflow-hidden\">\n <ng-content />\n </div>\n</section>\n", styles: [":host{display:contents}.taon-admin-panel-toogle-button-dimmer-opacity{opacity:.6}.taon-draggable-panel{position:fixed;display:flex;flex-direction:column;min-width:320px;min-height:240px;overflow:hidden;background:#fff;border:1px solid #e5e7eb;box-shadow:0 20px 50px #00000026;z-index:1000}.taon-window{width:70vw;height:70vh;left:15vw;top:25vh;border-radius:6px}.taon-fullscreen{width:100vw;height:100dvh;left:0;top:0;border-radius:0}.taon-window-bar{height:48px;display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;flex-shrink:0;padding-left:12px;border-bottom:1px solid #e5e7eb;background:#fafafa;-webkit-user-select:none;user-select:none;cursor:move}\n"] }]
2040
+ }], ctorParameters: () => [{ type: i1.Router }], propDecorators: { context: [{
2041
+ type: Input
2042
+ }], drag: [{
1936
2043
  type: ViewChild,
1937
2044
  args: [CdkDrag]
1938
2045
  }], basePath: [{
@@ -1949,6 +2056,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImpor
1949
2056
  type: Input
1950
2057
  }], stateChange: [{
1951
2058
  type: Output
2059
+ }], autoHideIcon: [{
2060
+ type: Input
2061
+ }], iconTemplate: [{
2062
+ type: Input
2063
+ }], headerCenterTemplate: [{
2064
+ type: Input
1952
2065
  }] } });
1953
2066
 
1954
2067
  //#endregion
@@ -1980,5 +2093,5 @@ var index = {};
1980
2093
  * Generated bundle index. Do not edit.
1981
2094
  */
1982
2095
 
1983
- export { APP_ID, BUILD_BASE_HREF, BUILD_FRAMEWORK_CLI_NAME, CURRENT_PACKAGE_TAON_VERSION, CURRENT_PACKAGE_VERSION, PROJECT_NPM_NAME, TaonAdminLayoutComponent, TaonAdminPageTabsComponent, TaonBackofficeNotificationsComponent, TaonBackofficeNotificationsService, TaonBaseAdminLayoutPage, TaonBaselineBackofficeOutletName, TaonConfirmDialogComponent, TaonDatatableComponent, TaonDatatableRoutes, TaonDraggableButtonPanelComponent, TaonDraggableButtonPanelRoutes, TaonDraggableButtonPanelState, TaonErrorDetailsDialogComponent, TaonErrorToastComponent, TaonHorizontalWheelScrollDirective, adminLazyRoute, myOrgProj, startCli };
2096
+ export { APP_ID, BUILD_BASE_HREF, BUILD_FRAMEWORK_CLI_NAME, CURRENT_PACKAGE_TAON_VERSION, CURRENT_PACKAGE_VERSION, PROJECT_NPM_NAME, TaonAdminLayoutComponent, TaonAdminMenuContext, TaonAdminPageTabsComponent, TaonBackofficeNotificationsComponent, TaonBackofficeNotificationsService, TaonBaseAdminLayoutPage, TaonBaselineBackofficeOutletName, TaonConfirmDialogComponent, TaonDatatableComponent, TaonDatatableRoutes, TaonDraggableButtonPanelComponent, TaonDraggableButtonPanelRoutes, TaonDraggableButtonPanelState, TaonErrorDetailsDialogComponent, TaonErrorToastComponent, TaonHorizontalWheelScrollDirective, adminLazyRoute, myOrgProj, startCli };
1984
2097
  //# sourceMappingURL=ui-browser-prod.mjs.map