@taon-dev/ui 21.0.4 → 21.0.5

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 (77) hide show
  1. package/VERIFIED-BUILD-DATA.jsonc +3 -3
  2. package/browser/fesm2022/{ui-browser-taon-backoffice-notifications.component.ts.en-US.i18n.data-uyZjV7Qx.mjs → ui-browser-taon-backoffice-notifications.component.ts.en-US.i18n.data-DdHr5V8R.mjs} +2 -2
  3. package/browser/fesm2022/{ui-browser-taon-backoffice-notifications.component.ts.en-US.i18n.data-uyZjV7Qx.mjs.map → ui-browser-taon-backoffice-notifications.component.ts.en-US.i18n.data-DdHr5V8R.mjs.map} +1 -1
  4. package/browser/fesm2022/{ui-browser-taon-backoffice-notifications.component.ts.pl-PL.i18n.data-s0tK5HDk.mjs → ui-browser-taon-backoffice-notifications.component.ts.pl-PL.i18n.data-CD5ZqDCD.mjs} +2 -2
  5. package/browser/fesm2022/{ui-browser-taon-backoffice-notifications.component.ts.pl-PL.i18n.data-s0tK5HDk.mjs.map → ui-browser-taon-backoffice-notifications.component.ts.pl-PL.i18n.data-CD5ZqDCD.mjs.map} +1 -1
  6. package/browser/fesm2022/ui-browser.mjs +289 -148
  7. package/browser/fesm2022/ui-browser.mjs.map +1 -1
  8. package/browser/package.json +1 -1
  9. package/browser/types/ui-browser.d.ts +42 -35
  10. package/browser-prod/fesm2022/{ui-browser-prod-taon-backoffice-notifications.component.ts.en-US.i18n.data-uyZjV7Qx.mjs → ui-browser-prod-taon-backoffice-notifications.component.ts.en-US.i18n.data-DdHr5V8R.mjs} +2 -2
  11. package/browser-prod/fesm2022/{ui-browser-prod-taon-backoffice-notifications.component.ts.en-US.i18n.data-uyZjV7Qx.mjs.map → ui-browser-prod-taon-backoffice-notifications.component.ts.en-US.i18n.data-DdHr5V8R.mjs.map} +1 -1
  12. package/browser-prod/fesm2022/{ui-browser-prod-taon-backoffice-notifications.component.ts.pl-PL.i18n.data-s0tK5HDk.mjs → ui-browser-prod-taon-backoffice-notifications.component.ts.pl-PL.i18n.data-CD5ZqDCD.mjs} +2 -2
  13. package/browser-prod/fesm2022/{ui-browser-prod-taon-backoffice-notifications.component.ts.pl-PL.i18n.data-s0tK5HDk.mjs.map → ui-browser-prod-taon-backoffice-notifications.component.ts.pl-PL.i18n.data-CD5ZqDCD.mjs.map} +1 -1
  14. package/browser-prod/fesm2022/ui-browser-prod.mjs +290 -149
  15. package/browser-prod/fesm2022/ui-browser-prod.mjs.map +1 -1
  16. package/browser-prod/package.json +1 -1
  17. package/browser-prod/types/ui-browser-prod.d.ts +42 -35
  18. package/html/app/main/main.container.html +6 -0
  19. package/html/lib/taon-admin-layout/taon-admin-layout.component.html +72 -57
  20. package/html/lib/taon-datatable/taon-datatable.component.html +19 -5
  21. package/html/lib/taon-draggable-button-panel/taon-draggable-button-panel.component.html +1 -0
  22. package/lib/build-info._auto-generated_.d.ts +1 -1
  23. package/lib/build-info._auto-generated_.js +1 -1
  24. package/lib/package.json +1 -1
  25. package/lib/taon-admin-layout/index.js +2 -2
  26. package/lib/taon-admin-layout/taon-admin-layout.component.d.ts +35 -32
  27. package/lib/taon-admin-layout/taon-admin-layout.models.d.ts +1 -1
  28. package/lib/taon-admin-layout/taon-admin-layout.models.js.map +1 -1
  29. package/lib/taon-backoffice-notifications/i18n/taon-backoffice-notifications.component.ts.en-US.i18n.data.js +1 -1
  30. package/lib/taon-backoffice-notifications/i18n/taon-backoffice-notifications.component.ts.pl-PL.i18n.data.js +1 -1
  31. package/lib/taon-backoffice-notifications/taon-backoffice-notifications.component.d.ts +1 -0
  32. package/lib/taon-datatable/index.js +2 -2
  33. package/lib/taon-datatable/taon-datatable.component.d.ts +3 -0
  34. package/lib/taon-draggable-button-panel/index.js +2 -2
  35. package/lib-esm/lib/build-info._auto-generated_.d.ts +1 -1
  36. package/lib-esm/lib/build-info._auto-generated_.js +1 -1
  37. package/lib-esm/lib/taon-admin-layout/index.d.ts +1 -1
  38. package/lib-esm/lib/taon-admin-layout/index.js +1 -1
  39. package/lib-esm/lib/taon-admin-layout/taon-admin-layout.models.d.ts +1 -1
  40. package/lib-esm/lib/taon-admin-layout/taon-admin-layout.models.js.map +1 -1
  41. package/lib-esm/lib/taon-backoffice-notifications/i18n/taon-backoffice-notifications.component.ts.en-US.i18n.data.js +1 -1
  42. package/lib-esm/lib/taon-backoffice-notifications/i18n/taon-backoffice-notifications.component.ts.pl-PL.i18n.data.js +1 -1
  43. package/lib-esm/lib/taon-datatable/index.d.ts +1 -1
  44. package/lib-esm/lib/taon-datatable/index.js +1 -1
  45. package/lib-esm/lib/taon-draggable-button-panel/index.d.ts +1 -1
  46. package/lib-esm/lib/taon-draggable-button-panel/index.js +1 -1
  47. package/lib-prod/build-info._auto-generated_.d.ts +1 -1
  48. package/lib-prod/build-info._auto-generated_.js +1 -1
  49. package/lib-prod/package.json +1 -1
  50. package/lib-prod/taon-admin-layout/index.d.ts +1 -1
  51. package/lib-prod/taon-admin-layout/index.js +1 -1
  52. package/lib-prod/taon-admin-layout/taon-admin-layout.models.d.ts +1 -1
  53. package/lib-prod/taon-admin-layout/taon-admin-layout.models.js.map +1 -1
  54. package/lib-prod/taon-backoffice-notifications/i18n/taon-backoffice-notifications.component.ts.en-US.i18n.data.js +1 -1
  55. package/lib-prod/taon-backoffice-notifications/i18n/taon-backoffice-notifications.component.ts.pl-PL.i18n.data.js +1 -1
  56. package/lib-prod/taon-datatable/index.d.ts +1 -1
  57. package/lib-prod/taon-datatable/index.js +1 -1
  58. package/lib-prod/taon-draggable-button-panel/index.d.ts +1 -1
  59. package/lib-prod/taon-draggable-button-panel/index.js +1 -1
  60. package/package.json +1 -1
  61. package/scss/lib/taon-admin-layout/taon-admin-layout.component.scss +282 -26
  62. package/websql/fesm2022/{ui-websql-taon-backoffice-notifications.component.ts.en-US.i18n.data-uyZjV7Qx.mjs → ui-websql-taon-backoffice-notifications.component.ts.en-US.i18n.data-DdHr5V8R.mjs} +2 -2
  63. package/websql/fesm2022/{ui-websql-taon-backoffice-notifications.component.ts.en-US.i18n.data-uyZjV7Qx.mjs.map → ui-websql-taon-backoffice-notifications.component.ts.en-US.i18n.data-DdHr5V8R.mjs.map} +1 -1
  64. package/websql/fesm2022/{ui-websql-taon-backoffice-notifications.component.ts.pl-PL.i18n.data-s0tK5HDk.mjs → ui-websql-taon-backoffice-notifications.component.ts.pl-PL.i18n.data-CD5ZqDCD.mjs} +2 -2
  65. package/websql/fesm2022/{ui-websql-taon-backoffice-notifications.component.ts.pl-PL.i18n.data-s0tK5HDk.mjs.map → ui-websql-taon-backoffice-notifications.component.ts.pl-PL.i18n.data-CD5ZqDCD.mjs.map} +1 -1
  66. package/websql/fesm2022/ui-websql.mjs +289 -148
  67. package/websql/fesm2022/ui-websql.mjs.map +1 -1
  68. package/websql/package.json +1 -1
  69. package/websql/types/ui-websql.d.ts +42 -35
  70. package/websql-prod/fesm2022/{ui-websql-prod-taon-backoffice-notifications.component.ts.en-US.i18n.data-uyZjV7Qx.mjs → ui-websql-prod-taon-backoffice-notifications.component.ts.en-US.i18n.data-DdHr5V8R.mjs} +2 -2
  71. package/websql-prod/fesm2022/{ui-websql-prod-taon-backoffice-notifications.component.ts.en-US.i18n.data-uyZjV7Qx.mjs.map → ui-websql-prod-taon-backoffice-notifications.component.ts.en-US.i18n.data-DdHr5V8R.mjs.map} +1 -1
  72. package/websql-prod/fesm2022/{ui-websql-prod-taon-backoffice-notifications.component.ts.pl-PL.i18n.data-s0tK5HDk.mjs → ui-websql-prod-taon-backoffice-notifications.component.ts.pl-PL.i18n.data-CD5ZqDCD.mjs} +2 -2
  73. package/websql-prod/fesm2022/{ui-websql-prod-taon-backoffice-notifications.component.ts.pl-PL.i18n.data-s0tK5HDk.mjs.map → ui-websql-prod-taon-backoffice-notifications.component.ts.pl-PL.i18n.data-CD5ZqDCD.mjs.map} +1 -1
  74. package/websql-prod/fesm2022/ui-websql-prod.mjs +290 -149
  75. package/websql-prod/fesm2022/ui-websql-prod.mjs.map +1 -1
  76. package/websql-prod/package.json +1 -1
  77. package/websql-prod/types/ui-websql-prod.d.ts +42 -35
@@ -2,7 +2,7 @@ import { decodeEnv, UtilsJson__NS__getValue, ___NS__isFunction, ___NS__isBoolean
2
2
  import { Translation } from '@taon-dev/i18n/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, ViewChild, Injectable, DestroyRef, ChangeDetectorRef, EventEmitter, Output } from '@angular/core';
5
+ import { inject, ElementRef, Directive, signal, Input, ChangeDetectionStrategy, Component, HostListener, ViewChild, Injectable, DestroyRef, ChangeDetectorRef, EventEmitter, Output } from '@angular/core';
6
6
  import * as i2$1 from '@angular/common';
7
7
  import { CommonModule, NgIf } from '@angular/common';
8
8
  import * as i3 from '@angular/material/expansion';
@@ -16,26 +16,26 @@ import * as i2 from '@angular/material/tabs';
16
16
  import { MatTabsModule } from '@angular/material/tabs';
17
17
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
18
18
  import { Resource__NS__listenErrors, getDefaultModel } from 'ng2-rest/browser-prod';
19
+ import { TaonAdminService, getHttpStatus, ClassHelpers__NS__getName, Symbols__NS__old__NS__X_TOTAL_COUNT } from 'taon/browser-prod';
19
20
  import { HotToastRef, HotToastService } from '@ngneat/hot-toast';
20
21
  import * as i1$1 from '@angular/material/dialog';
21
22
  import { MAT_DIALOG_DATA, MatDialogModule, MatDialog, MatDialogRef } from '@angular/material/dialog';
22
- import * as i2$2 from '@angular/material/button';
23
+ import * as i3$1 from '@angular/material/button';
23
24
  import { MatButtonModule } from '@angular/material/button';
24
- import * as i3$1 from '@angular/material/button-toggle';
25
+ import * as i3$2 from '@angular/material/button-toggle';
25
26
  import { MatButtonToggleModule } from '@angular/material/button-toggle';
26
- import * as i1$2 from '@angular/forms';
27
+ import * as i2$2 from '@angular/forms';
27
28
  import { FormsModule, ReactiveFormsModule } from '@angular/forms';
28
- import * as i3$2 from '@angular/material/form-field';
29
+ import * as i4$1 from '@angular/material/form-field';
29
30
  import { MatFormFieldModule } from '@angular/material/form-field';
30
- import * as i5 from '@angular/material/input';
31
+ import * as i6 from '@angular/material/input';
31
32
  import { MatInputModule } from '@angular/material/input';
32
33
  import { MatPaginatorModule } from '@angular/material/paginator';
33
34
  import { MatSelectModule } from '@angular/material/select';
34
- import * as i6 from '@ng-matero/extensions/grid';
35
+ import * as i7 from '@ng-matero/extensions/grid';
35
36
  import { MtxGridModule } from '@ng-matero/extensions/grid';
36
- import { ClassHelpers__NS__getName, Symbols__NS__old } from 'taon/browser-prod';
37
37
  import { CdkDrag, CdkDragHandle } from '@angular/cdk/drag-drop';
38
- import * as i4$1 from '@angular/material/tooltip';
38
+ import * as i4$2 from '@angular/material/tooltip';
39
39
  import { MatTooltipModule } from '@angular/material/tooltip';
40
40
 
41
41
  // THIS FILE IS GENERATED - DO NOT MODIFY
@@ -902,7 +902,7 @@ const CURRENT_PACKAGE_TAON_VERSION = 'v21';
902
902
  /**
903
903
  * Autogenerated by current cli tool. Use *tnp release* to bump version.
904
904
  */
905
- const CURRENT_PACKAGE_VERSION = '21.0.4';
905
+ const CURRENT_PACKAGE_VERSION = '21.0.5';
906
906
  // THIS FILE IS GENERATED - DO NOT MODIFY
907
907
 
908
908
  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 } });
@@ -1323,33 +1323,68 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImpor
1323
1323
  //#region imports
1324
1324
  //#endregion
1325
1325
  class TaonAdminLayoutComponent {
1326
+ get isDialogMode() {
1327
+ const windowElement = this.getWindowElement();
1328
+ if (!windowElement) {
1329
+ return false;
1330
+ }
1331
+ return !windowElement.classList.contains('taon-fullscreen');
1332
+ }
1326
1333
  get basePathSegments() {
1327
1334
  return this.basePath.split('/').filter(Boolean);
1328
1335
  }
1336
+ get normalizedBasePath() {
1337
+ const value = '/' + this.basePath.split('/').filter(Boolean).join('/');
1338
+ return value === '/' ? '/' : value;
1339
+ }
1329
1340
  //#endregion
1330
1341
  //#region constructor
1331
- constructor(router) {
1342
+ constructor(router, hostElement) {
1332
1343
  this.router = router;
1344
+ this.hostElement = hostElement;
1333
1345
  this.basePath = '';
1334
- /**
1335
- * Children discovered for each level-1 route.
1336
- *
1337
- * Session -> [Tracking, Providers]
1338
- * Users -> [Manager]
1339
- */
1346
+ this.layoutWidth = signal(undefined, ...(ngDevMode ? [{ debugName: "layoutWidth" }] : []));
1347
+ this.layoutHeight = signal(undefined, ...(ngDevMode ? [{ debugName: "layoutHeight" }] : []));
1340
1348
  this.level2Routes = signal(new Map(), ...(ngDevMode ? [{ debugName: "level2Routes" }] : []));
1341
1349
  this.currentUrl = signal('', ...(ngDevMode ? [{ debugName: "currentUrl" }] : []));
1342
1350
  this.loadingRoutes = signal(new Set(), ...(ngDevMode ? [{ debugName: "loadingRoutes" }] : []));
1343
1351
  this.asideOpened = signal(window.innerWidth >= 768, ...(ngDevMode ? [{ debugName: "asideOpened" }] : []));
1352
+ this.resizing = false;
1353
+ this.resizeStartX = 0;
1354
+ this.resizeStartY = 0;
1355
+ this.resizeStartWidth = 0;
1356
+ this.resizeStartHeight = 0;
1357
+ this.minDialogWidth = 500;
1358
+ this.minDialogHeight = 350;
1359
+ this.resize = (event) => {
1360
+ if (!this.resizing) {
1361
+ return;
1362
+ }
1363
+ const windowElement = this.getWindowElement();
1364
+ if (!windowElement) {
1365
+ return;
1366
+ }
1367
+ const windowRect = windowElement.getBoundingClientRect();
1368
+ const deltaX = event.clientX - this.resizeStartX;
1369
+ const deltaY = event.clientY - this.resizeStartY;
1370
+ const maxWidth = window.innerWidth - windowRect.left;
1371
+ const maxHeight = window.innerHeight - windowRect.top;
1372
+ const width = Math.min(maxWidth, Math.max(this.minDialogWidth, this.resizeStartWidth + deltaX));
1373
+ const height = Math.min(maxHeight, Math.max(this.minDialogHeight, this.resizeStartHeight + deltaY));
1374
+ windowElement.style.width = `${width}px`;
1375
+ windowElement.style.height = `${height}px`;
1376
+ };
1377
+ this.stopResize = () => {
1378
+ if (!this.resizing) {
1379
+ return;
1380
+ }
1381
+ this.resizing = false;
1382
+ window.removeEventListener('pointermove', this.resize);
1383
+ window.removeEventListener('pointerup', this.stopResize);
1384
+ };
1344
1385
  }
1345
1386
  //#endregion
1346
- //#region methods
1347
- toggleAside() {
1348
- this.asideOpened.update(opened => !opened);
1349
- }
1350
- closeAside() {
1351
- this.asideOpened.set(false);
1352
- }
1387
+ //#region lifecycle
1353
1388
  async ngOnInit() {
1354
1389
  await this.loadCurrentLevel1Route();
1355
1390
  this.router.events
@@ -1358,9 +1393,152 @@ class TaonAdminLayoutComponent {
1358
1393
  await this.loadCurrentLevel1Route();
1359
1394
  });
1360
1395
  }
1361
- // ---------------------------------------------------------------------------
1362
- // LEVEL 1
1363
- // ---------------------------------------------------------------------------
1396
+ ngAfterViewInit() {
1397
+ setTimeout(() => {
1398
+ const windowElement = this.getWindowElement();
1399
+ // console.log('[windowElement] should not be undefined', windowElement);
1400
+ this.updateLayoutSize();
1401
+ this.startWindowObservers();
1402
+ }, 500); // TODO QUICK_FIX
1403
+ }
1404
+ // private waitForWindowElement(
1405
+ // callback: (windowElement: HTMLElement) => void,
1406
+ // ): void {
1407
+ // const check = () => {
1408
+ // console.log('Waiting for window element...');
1409
+ // const windowElement = this.getWindowElement();
1410
+ // if (windowElement) {
1411
+ // callback(windowElement);
1412
+ // return;
1413
+ // }
1414
+ // requestAnimationFrame(check);
1415
+ // };
1416
+ // check();
1417
+ // }
1418
+ ngOnDestroy() {
1419
+ this.resizeObserver?.disconnect();
1420
+ this.mutationObserver?.disconnect();
1421
+ window.removeEventListener('pointermove', this.resize);
1422
+ window.removeEventListener('pointerup', this.stopResize);
1423
+ }
1424
+ //#endregion
1425
+ //#region window / layout
1426
+ getWindowElement() {
1427
+ return (document.querySelector('.taon-window') ??
1428
+ document.querySelector('.taon-window-full') ??
1429
+ undefined);
1430
+ }
1431
+ getDraggablePanelElement() {
1432
+ return (document.querySelector('.taon-draggable-panel') ?? undefined);
1433
+ }
1434
+ startWindowObservers() {
1435
+ this.resizeObserver?.disconnect();
1436
+ this.mutationObserver?.disconnect();
1437
+ const host = this.hostElement.nativeElement;
1438
+ const windowElement = this.getWindowElement();
1439
+ // console.log('WINDOW', this.getWindowElement());
1440
+ // console.log('PANEL', this.getDraggablePanelElement());
1441
+ // ================================================================
1442
+ // SIZE CHANGES
1443
+ // ================================================================
1444
+ const observedElement = windowElement ?? host.parentElement;
1445
+ if (observedElement) {
1446
+ this.resizeObserver = new ResizeObserver(() => {
1447
+ this.updateLayoutSize();
1448
+ });
1449
+ this.resizeObserver.observe(observedElement);
1450
+ }
1451
+ // ================================================================
1452
+ // DIALOG <-> FULLSCREEN STATE CHANGES
1453
+ // ================================================================
1454
+ if (windowElement) {
1455
+ this.mutationObserver = new MutationObserver(mutations => {
1456
+ const classChanged = mutations.some(mutation => mutation.type === 'attributes' &&
1457
+ mutation.attributeName === 'class');
1458
+ // console.log('Class changed:', classChanged);
1459
+ if (!classChanged) {
1460
+ return;
1461
+ }
1462
+ this.windowModeChanged(windowElement);
1463
+ });
1464
+ this.mutationObserver.observe(windowElement, {
1465
+ attributes: true,
1466
+ attributeFilter: ['class'],
1467
+ });
1468
+ }
1469
+ }
1470
+ windowModeChanged(windowElement) {
1471
+ const fullscreen = windowElement.classList.contains('taon-fullscreen');
1472
+ if (fullscreen) {
1473
+ this.clearWindowResize(windowElement);
1474
+ }
1475
+ requestAnimationFrame(() => {
1476
+ this.updateLayoutSize();
1477
+ });
1478
+ }
1479
+ clearWindowResize(windowElement) {
1480
+ windowElement.style.removeProperty('width');
1481
+ windowElement.style.removeProperty('height');
1482
+ }
1483
+ windowResized() {
1484
+ this.updateLayoutSize();
1485
+ }
1486
+ updateLayoutSize() {
1487
+ const host = this.hostElement.nativeElement;
1488
+ const hostRect = host.getBoundingClientRect();
1489
+ const windowElement = this.getWindowElement();
1490
+ const dialogMode = !!windowElement && !windowElement.classList.contains('taon-fullscreen');
1491
+ // ================================================================
1492
+ // NORMAL / FULLSCREEN
1493
+ // ================================================================
1494
+ if (!dialogMode) {
1495
+ this.layoutWidth.set(Math.floor(hostRect.width));
1496
+ this.layoutHeight.set(Math.max(0, Math.floor(window.innerHeight - hostRect.top)));
1497
+ return;
1498
+ }
1499
+ // ================================================================
1500
+ // DIALOG
1501
+ // ================================================================
1502
+ const windowRect = windowElement.getBoundingClientRect();
1503
+ this.layoutWidth.set(Math.max(0, Math.floor(windowRect.right - hostRect.left)));
1504
+ this.layoutHeight.set(Math.max(0, Math.floor(windowRect.bottom - hostRect.top)));
1505
+ }
1506
+ //#endregion
1507
+ //#region dialog resizing
1508
+ startResize(event) {
1509
+ if (!this.isDialogMode) {
1510
+ return;
1511
+ }
1512
+ const windowElement = this.getWindowElement();
1513
+ if (!windowElement) {
1514
+ return;
1515
+ }
1516
+ event.preventDefault();
1517
+ event.stopPropagation();
1518
+ const windowRect = windowElement.getBoundingClientRect();
1519
+ this.resizing = true;
1520
+ this.resizeStartX = event.clientX;
1521
+ this.resizeStartY = event.clientY;
1522
+ this.resizeStartWidth = windowRect.width;
1523
+ this.resizeStartHeight = windowRect.height;
1524
+ window.addEventListener('pointermove', this.resize);
1525
+ window.addEventListener('pointerup', this.stopResize);
1526
+ }
1527
+ //#endregion
1528
+ //#region aside
1529
+ toggleAside() {
1530
+ this.asideOpened.update(opened => !opened);
1531
+ }
1532
+ closeAside() {
1533
+ this.asideOpened.set(false);
1534
+ }
1535
+ closeAsideOnMobile() {
1536
+ if (window.innerWidth < 768) {
1537
+ this.closeAside();
1538
+ }
1539
+ }
1540
+ //#endregion
1541
+ //#region level 1
1364
1542
  async level1Clicked(route) {
1365
1543
  if (!route.path) {
1366
1544
  return;
@@ -1371,6 +1549,20 @@ class TaonAdminLayoutComponent {
1371
1549
  this.closeAsideOnMobile();
1372
1550
  }
1373
1551
  }
1552
+ async panelExpanded(route, expanded) {
1553
+ if (!expanded) {
1554
+ return;
1555
+ }
1556
+ await this.ensureLevel2Loaded(route);
1557
+ }
1558
+ isLevel1Active(route) {
1559
+ if (!route.path) {
1560
+ return false;
1561
+ }
1562
+ return this.isPathActive([route.path]);
1563
+ }
1564
+ //#endregion
1565
+ //#region level 2
1374
1566
  async level2Clicked(level1, level2) {
1375
1567
  if (!level1.path || !level2.path) {
1376
1568
  return;
@@ -1378,6 +1570,20 @@ class TaonAdminLayoutComponent {
1378
1570
  await this.navigateTo([level1.path, level2.path]);
1379
1571
  this.closeAsideOnMobile();
1380
1572
  }
1573
+ getLevel2Routes(route) {
1574
+ return this.level2Routes().get(route) ?? [];
1575
+ }
1576
+ isLevel2Active(level1, level2) {
1577
+ if (!level1.path || !level2.path) {
1578
+ return false;
1579
+ }
1580
+ return this.isPathActive([level1.path, level2.path]);
1581
+ }
1582
+ level2Link(level1, level2) {
1583
+ return [this.normalizedBasePath, level1.path, level2.path];
1584
+ }
1585
+ //#endregion
1586
+ //#region navigation
1381
1587
  outletBaseMatrixParams() {
1382
1588
  if (!this.outlet) {
1383
1589
  return {};
@@ -1390,11 +1596,6 @@ class TaonAdminLayoutComponent {
1390
1596
  }
1391
1597
  async navigateTo(routeSegments) {
1392
1598
  const segments = [...this.basePathSegments, ...routeSegments];
1393
- /**
1394
- * Named / auxiliary outlet:
1395
- *
1396
- * /products/123(admin:main/session/providers)
1397
- */
1398
1599
  if (this.outlet) {
1399
1600
  const [firstSegment, ...remainingSegments] = segments;
1400
1601
  if (!firstSegment) {
@@ -1404,61 +1605,15 @@ class TaonAdminLayoutComponent {
1404
1605
  return this.router.navigate([
1405
1606
  {
1406
1607
  outlets: {
1407
- [this.outlet]: [
1408
- firstSegment,
1409
- /**
1410
- * Angular interprets an object following
1411
- * a segment as matrix parameters.
1412
- */
1413
- matrixParams,
1414
- ...remainingSegments,
1415
- ],
1608
+ [this.outlet]: [firstSegment, matrixParams, ...remainingSegments],
1416
1609
  },
1417
1610
  },
1418
1611
  ]);
1419
1612
  }
1420
- /**
1421
- * Normal primary outlet:
1422
- *
1423
- * /main/session/providers
1424
- */
1425
1613
  return this.router.navigate(['/', ...segments]);
1426
1614
  }
1427
- closeAsideOnMobile() {
1428
- if (window.innerWidth < 768) {
1429
- this.closeAside();
1430
- }
1431
- }
1432
- async panelExpanded(route, expanded) {
1433
- if (!expanded) {
1434
- return;
1435
- }
1436
- await this.ensureLevel2Loaded(route);
1437
- }
1438
- isLevel1Active(route) {
1439
- if (!route.path) {
1440
- return false;
1441
- }
1442
- return this.isPathActive([route.path]);
1443
- }
1444
- // ---------------------------------------------------------------------------
1445
- // LEVEL 2
1446
- // ---------------------------------------------------------------------------
1447
- getLevel2Routes(route) {
1448
- return this.level2Routes().get(route) ?? [];
1449
- }
1450
- isLevel2Active(level1, level2) {
1451
- if (!level1.path || !level2.path) {
1452
- return false;
1453
- }
1454
- return this.isPathActive([level1.path, level2.path]);
1455
- }
1456
- level2Link(level1, level2) {
1457
- return [this.normalizedBasePath, level1.path, level2.path];
1458
- }
1459
- // ---------------------------------------------------------------------------
1460
- // ROUTE LOADING
1461
- // ---------------------------------------------------------------------------
1615
+ //#endregion
1616
+ //#region route loading
1462
1617
  async ensureLevel2Loaded(level1) {
1463
1618
  if (this.level2Routes().has(level1)) {
1464
1619
  return;
@@ -1469,23 +1624,6 @@ class TaonAdminLayoutComponent {
1469
1624
  this.setLoading(level1, true);
1470
1625
  try {
1471
1626
  const lazyRoutes = await this.loadRouteChildren(level1);
1472
- /**
1473
- * Your lazy modules have this shape:
1474
- *
1475
- * [
1476
- * {
1477
- * path: '',
1478
- * component: SessionComponent,
1479
- * children: [
1480
- * { path: 'tracking' },
1481
- * { path: 'providers' }
1482
- * ]
1483
- * }
1484
- * ]
1485
- *
1486
- * The path:'' wrapper DOES NOT represent a navigation
1487
- * level, so unwrap it.
1488
- */
1489
1627
  const level2 = this.unwrapEmptyPathRoutes(lazyRoutes);
1490
1628
  const map = new Map(this.level2Routes());
1491
1629
  map.set(level1, level2);
@@ -1505,21 +1643,6 @@ class TaonAdminLayoutComponent {
1505
1643
  }
1506
1644
  return [];
1507
1645
  }
1508
- /**
1509
- * Removes structural path:'' wrappers.
1510
- *
1511
- * [
1512
- * {
1513
- * path: '',
1514
- * component: SessionComponent,
1515
- * children: [...]
1516
- * }
1517
- * ]
1518
- *
1519
- * becomes:
1520
- *
1521
- * [...]
1522
- */
1523
1646
  unwrapEmptyPathRoutes(routes) {
1524
1647
  const result = [];
1525
1648
  for (const route of routes) {
@@ -1527,9 +1650,6 @@ class TaonAdminLayoutComponent {
1527
1650
  result.push(...this.unwrapEmptyPathRoutes(route.children));
1528
1651
  continue;
1529
1652
  }
1530
- /**
1531
- * redirects etc. shouldn't become menu entries.
1532
- */
1533
1653
  if (route.redirectTo !== undefined || route.data?.['hideInNavigation']) {
1534
1654
  continue;
1535
1655
  }
@@ -1537,9 +1657,8 @@ class TaonAdminLayoutComponent {
1537
1657
  }
1538
1658
  return result;
1539
1659
  }
1540
- // ---------------------------------------------------------------------------
1541
- // DIRECT URL SUPPORT
1542
- // ---------------------------------------------------------------------------
1660
+ //#endregion
1661
+ //#region direct url support
1543
1662
  async loadCurrentLevel1Route() {
1544
1663
  const relativeSegments = this.currentRelativeSegments();
1545
1664
  const level1Path = relativeSegments[0];
@@ -1551,9 +1670,8 @@ class TaonAdminLayoutComponent {
1551
1670
  await this.ensureLevel2Loaded(level1);
1552
1671
  }
1553
1672
  }
1554
- // ---------------------------------------------------------------------------
1555
- // HELPERS
1556
- // ---------------------------------------------------------------------------
1673
+ //#endregion
1674
+ //#region helpers
1557
1675
  label(route) {
1558
1676
  return route.data?.['menuItem'] ?? this.startCase(route.path ?? '');
1559
1677
  }
@@ -1566,6 +1684,9 @@ class TaonAdminLayoutComponent {
1566
1684
  isLoading(route) {
1567
1685
  return this.loadingRoutes().has(route);
1568
1686
  }
1687
+ isExpandable(route) {
1688
+ return route.data?.['expandable'] !== false;
1689
+ }
1569
1690
  isPathActive(routeSegments) {
1570
1691
  const current = this.currentRelativeSegments();
1571
1692
  if (current.length < routeSegments.length) {
@@ -1586,16 +1707,6 @@ class TaonAdminLayoutComponent {
1586
1707
  }
1587
1708
  return group.segments.map(segment => segment.path);
1588
1709
  }
1589
- isExpandable(route) {
1590
- return route.data?.['expandable'] !== false;
1591
- }
1592
- get normalizedBasePath() {
1593
- const value = '/' + this.basePath.split('/').filter(Boolean).join('/');
1594
- return value === '/' ? '/' : value;
1595
- }
1596
- cleanUrl(url) {
1597
- return url.split('?')[0].split('#')[0];
1598
- }
1599
1710
  setLoading(route, loading) {
1600
1711
  const set = new Set(this.loadingRoutes());
1601
1712
  if (loading) {
@@ -1611,8 +1722,8 @@ class TaonAdminLayoutComponent {
1611
1722
  .replace(/[-_]+/g, ' ')
1612
1723
  .replace(/\b\w/g, char => char.toUpperCase());
1613
1724
  }
1614
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonAdminLayoutComponent, deps: [{ token: i1.Router }], target: i0.ɵɵFactoryTarget.Component }); }
1615
- 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" }, ngImport: i0, template: "<div class=\"flex h-dvh min-h-0 flex-col overflow-hidden\">\n <!-- ============================================================= -->\n <!-- HEADER -->\n <!-- ============================================================= -->\n\n <header\n class=\"flex h-16 shrink-0 items-center border-b border-gray-200 bg-white\">\n <!-- hamburger ALWAYS visible -->\n <button\n mat-icon-button\n type=\"button\"\n aria-label=\"Toggle admin menu\"\n class=\"mx-2 shrink-0 cursor-pointer\"\n (click)=\"toggleAside()\">\n <mat-icon>menu</mat-icon>\n </button>\n\n <div class=\"min-w-0 flex-1\">\n <ng-content select=\"[header]\" />\n </div>\n </header>\n\n <!-- ============================================================= -->\n <!-- BODY -->\n <!-- ============================================================= -->\n\n <div class=\"relative flex min-h-0 flex-1 overflow-hidden\">\n <!-- =========================================================== -->\n <!-- MOBILE BACKDROP -->\n <!-- =========================================================== -->\n\n @if (asideOpened()) {\n <div\n class=\"absolute inset-0 z-20 bg-black/30 md:hidden\"\n (click)=\"closeAside()\"></div>\n }\n\n <!-- =========================================================== -->\n <!-- ASIDE -->\n <!-- =========================================================== -->\n\n <aside\n class=\"absolute inset-y-0 left-0 z-30 flex min-h-0 flex-col border-r border-gray-200 bg-white transition-[width,transform] duration-200 md:relative md:inset-auto md:z-auto md:translate-x-0\"\n [class.w-72]=\"asideOpened()\"\n [class.-translate-x-full]=\"!asideOpened()\"\n [class.translate-x-0]=\"asideOpened()\"\n [class.md:w-72]=\"asideOpened()\"\n [class.md:w-0]=\"!asideOpened()\"\n [class.md:overflow-hidden]=\"!asideOpened()\">\n <!-- ========================================================= -->\n <!-- SCROLLABLE ASIDE CONTENT -->\n <!-- ========================================================= -->\n\n <div class=\"min-h-0 flex-1 overflow-y-auto\">\n <mat-accordion class=\"admin-menu\">\n @for (level1 of routes; track level1.path) {\n <!-- =================================================== -->\n <!-- EXPANDABLE LEVEL 1 -->\n <!-- =================================================== -->\n\n @if (isExpandable(level1)) {\n <mat-expansion-panel\n [expanded]=\"isLevel1Active(level1)\"\n (expandedChange)=\"panelExpanded(level1, $event)\">\n <!-- ================================================= -->\n <!-- LEVEL 1 -->\n <!-- ================================================= -->\n\n <mat-expansion-panel-header (click)=\"level1Clicked(level1)\">\n <mat-panel-title>\n <div class=\"flex items-center gap-3\">\n @if (icon(level1)) {\n <mat-icon [ngStyle]=\"{ color: color(level1) }\">\n {{ icon(level1) }}\n </mat-icon>\n }\n\n <span\n [class.font-semibold]=\"isLevel1Active(level1)\"\n [ngStyle]=\"{ color: color(level1) }\">\n {{ label(level1) }}\n </span>\n </div>\n </mat-panel-title>\n </mat-expansion-panel-header>\n\n <!-- ================================================= -->\n <!-- LEVEL 2 -->\n <!-- ================================================= -->\n\n <div class=\"flex flex-col\">\n @if (isLoading(level1)) {\n <div class=\"px-4 py-2 text-sm text-gray-400\">\n Loading...\n </div>\n } @else {\n @for (\n level2 of getLevel2Routes(level1);\n track level2.path\n ) {\n <a\n (click)=\"level2Clicked(level1, level2)\"\n class=\"flex cursor-pointer items-center gap-3 px-6 py-2.5 text-sm transition-colors hover:bg-gray-100\"\n [class.bg-gray-100]=\"isLevel2Active(level1, level2)\"\n [class.font-semibold]=\"isLevel2Active(level1, level2)\">\n @if (icon(level2)) {\n <mat-icon [ngStyle]=\"{ color: color(level2) }\">\n {{ icon(level2) }}\n </mat-icon>\n }\n\n <span [ngStyle]=\"{ color: color(level2) }\">\n {{ label(level2) }}\n </span>\n </a>\n }\n }\n </div>\n </mat-expansion-panel>\n } @else {\n <!-- ================================================= -->\n <!-- NON-EXPANDABLE LEVEL 1 -->\n <!-- ================================================= -->\n\n <a\n (click)=\"level1Clicked(level1)\"\n class=\"flex h-14 cursor-pointer items-center gap-3 px-6 transition-colors hover:bg-gray-100\"\n [class.bg-gray-100]=\"isLevel1Active(level1)\"\n [class.font-semibold]=\"isLevel1Active(level1)\">\n @if (icon(level1)) {\n <mat-icon [ngStyle]=\"{ color: color(level1) }\">\n {{ icon(level1) }}\n </mat-icon>\n }\n\n <span [ngStyle]=\"{ color: color(level1) }\">\n {{ label(level1) }}\n </span>\n </a>\n }\n }\n </mat-accordion>\n </div>\n\n <!-- ========================================================= -->\n <!-- ASIDE FOOTER - ALWAYS VISIBLE -->\n <!-- ========================================================= -->\n\n <footer class=\"shrink-0 border-t border-gray-200 bg-white p-3\">\n <ng-content select=\"[asideFooter]\" />\n </footer>\n </aside>\n\n <!-- =========================================================== -->\n <!-- MAIN -->\n <!-- =========================================================== -->\n\n <main class=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden\">\n <!-- ========================================================= -->\n <!-- LEVEL 3+ TABS -->\n <!-- ========================================================= -->\n\n <taon-admin-page-tabs\n class=\"shrink-0\"\n [routes]=\"routes\"\n [basePath]=\"basePath\"\n [outlet]=\"outlet\" />\n\n <!-- ========================================================= -->\n <!-- ROUTER CONTENT -->\n <!-- ========================================================= -->\n\n <div class=\"min-h-0 flex-1 overflow-y-auto p-4 md:p-6\">\n <router-outlet />\n </div>\n </main>\n </div>\n</div>\n", styles: [":host{display:block;height:100%;min-height:0}.admin-menu mat-expansion-panel{border-radius:0!important;box-shadow:none!important;border-bottom:1px solid var(--mat-sys-outline-variant);margin-top:0!important;margin-bottom:0!important}.admin-menu mat-expansion-panel:first-of-type,.admin-menu mat-expansion-panel:last-of-type{border-radius:0!important}.admin-menu mat-expansion-panel-header{border-radius:0!important}.admin-aside{position:absolute;inset-block:0;left:0;z-index:30;display:flex;flex-direction:column;width:18rem;min-height:0;background:#fff;border-right:1px solid var(--mat-sys-outline-variant);transform:translate(-100%);transition:transform .2s ease,width .2s ease;overflow:hidden}.admin-aside-open{transform:translate(0)}@media(min-width:768px){.admin-aside{position:relative;inset:auto;z-index:auto;width:0;transform:none}.admin-aside-open{width:18rem}}\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 }); }
1725
+ 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 }); }
1726
+ 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 }); }
1616
1727
  }
1617
1728
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonAdminLayoutComponent, decorators: [{
1618
1729
  type: Component,
@@ -1623,14 +1734,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImpor
1623
1734
  MatExpansionModule,
1624
1735
  MatIconModule,
1625
1736
  TaonAdminPageTabsComponent,
1626
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"flex h-dvh min-h-0 flex-col overflow-hidden\">\n <!-- ============================================================= -->\n <!-- HEADER -->\n <!-- ============================================================= -->\n\n <header\n class=\"flex h-16 shrink-0 items-center border-b border-gray-200 bg-white\">\n <!-- hamburger ALWAYS visible -->\n <button\n mat-icon-button\n type=\"button\"\n aria-label=\"Toggle admin menu\"\n class=\"mx-2 shrink-0 cursor-pointer\"\n (click)=\"toggleAside()\">\n <mat-icon>menu</mat-icon>\n </button>\n\n <div class=\"min-w-0 flex-1\">\n <ng-content select=\"[header]\" />\n </div>\n </header>\n\n <!-- ============================================================= -->\n <!-- BODY -->\n <!-- ============================================================= -->\n\n <div class=\"relative flex min-h-0 flex-1 overflow-hidden\">\n <!-- =========================================================== -->\n <!-- MOBILE BACKDROP -->\n <!-- =========================================================== -->\n\n @if (asideOpened()) {\n <div\n class=\"absolute inset-0 z-20 bg-black/30 md:hidden\"\n (click)=\"closeAside()\"></div>\n }\n\n <!-- =========================================================== -->\n <!-- ASIDE -->\n <!-- =========================================================== -->\n\n <aside\n class=\"absolute inset-y-0 left-0 z-30 flex min-h-0 flex-col border-r border-gray-200 bg-white transition-[width,transform] duration-200 md:relative md:inset-auto md:z-auto md:translate-x-0\"\n [class.w-72]=\"asideOpened()\"\n [class.-translate-x-full]=\"!asideOpened()\"\n [class.translate-x-0]=\"asideOpened()\"\n [class.md:w-72]=\"asideOpened()\"\n [class.md:w-0]=\"!asideOpened()\"\n [class.md:overflow-hidden]=\"!asideOpened()\">\n <!-- ========================================================= -->\n <!-- SCROLLABLE ASIDE CONTENT -->\n <!-- ========================================================= -->\n\n <div class=\"min-h-0 flex-1 overflow-y-auto\">\n <mat-accordion class=\"admin-menu\">\n @for (level1 of routes; track level1.path) {\n <!-- =================================================== -->\n <!-- EXPANDABLE LEVEL 1 -->\n <!-- =================================================== -->\n\n @if (isExpandable(level1)) {\n <mat-expansion-panel\n [expanded]=\"isLevel1Active(level1)\"\n (expandedChange)=\"panelExpanded(level1, $event)\">\n <!-- ================================================= -->\n <!-- LEVEL 1 -->\n <!-- ================================================= -->\n\n <mat-expansion-panel-header (click)=\"level1Clicked(level1)\">\n <mat-panel-title>\n <div class=\"flex items-center gap-3\">\n @if (icon(level1)) {\n <mat-icon [ngStyle]=\"{ color: color(level1) }\">\n {{ icon(level1) }}\n </mat-icon>\n }\n\n <span\n [class.font-semibold]=\"isLevel1Active(level1)\"\n [ngStyle]=\"{ color: color(level1) }\">\n {{ label(level1) }}\n </span>\n </div>\n </mat-panel-title>\n </mat-expansion-panel-header>\n\n <!-- ================================================= -->\n <!-- LEVEL 2 -->\n <!-- ================================================= -->\n\n <div class=\"flex flex-col\">\n @if (isLoading(level1)) {\n <div class=\"px-4 py-2 text-sm text-gray-400\">\n Loading...\n </div>\n } @else {\n @for (\n level2 of getLevel2Routes(level1);\n track level2.path\n ) {\n <a\n (click)=\"level2Clicked(level1, level2)\"\n class=\"flex cursor-pointer items-center gap-3 px-6 py-2.5 text-sm transition-colors hover:bg-gray-100\"\n [class.bg-gray-100]=\"isLevel2Active(level1, level2)\"\n [class.font-semibold]=\"isLevel2Active(level1, level2)\">\n @if (icon(level2)) {\n <mat-icon [ngStyle]=\"{ color: color(level2) }\">\n {{ icon(level2) }}\n </mat-icon>\n }\n\n <span [ngStyle]=\"{ color: color(level2) }\">\n {{ label(level2) }}\n </span>\n </a>\n }\n }\n </div>\n </mat-expansion-panel>\n } @else {\n <!-- ================================================= -->\n <!-- NON-EXPANDABLE LEVEL 1 -->\n <!-- ================================================= -->\n\n <a\n (click)=\"level1Clicked(level1)\"\n class=\"flex h-14 cursor-pointer items-center gap-3 px-6 transition-colors hover:bg-gray-100\"\n [class.bg-gray-100]=\"isLevel1Active(level1)\"\n [class.font-semibold]=\"isLevel1Active(level1)\">\n @if (icon(level1)) {\n <mat-icon [ngStyle]=\"{ color: color(level1) }\">\n {{ icon(level1) }}\n </mat-icon>\n }\n\n <span [ngStyle]=\"{ color: color(level1) }\">\n {{ label(level1) }}\n </span>\n </a>\n }\n }\n </mat-accordion>\n </div>\n\n <!-- ========================================================= -->\n <!-- ASIDE FOOTER - ALWAYS VISIBLE -->\n <!-- ========================================================= -->\n\n <footer class=\"shrink-0 border-t border-gray-200 bg-white p-3\">\n <ng-content select=\"[asideFooter]\" />\n </footer>\n </aside>\n\n <!-- =========================================================== -->\n <!-- MAIN -->\n <!-- =========================================================== -->\n\n <main class=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden\">\n <!-- ========================================================= -->\n <!-- LEVEL 3+ TABS -->\n <!-- ========================================================= -->\n\n <taon-admin-page-tabs\n class=\"shrink-0\"\n [routes]=\"routes\"\n [basePath]=\"basePath\"\n [outlet]=\"outlet\" />\n\n <!-- ========================================================= -->\n <!-- ROUTER CONTENT -->\n <!-- ========================================================= -->\n\n <div class=\"min-h-0 flex-1 overflow-y-auto p-4 md:p-6\">\n <router-outlet />\n </div>\n </main>\n </div>\n</div>\n", styles: [":host{display:block;height:100%;min-height:0}.admin-menu mat-expansion-panel{border-radius:0!important;box-shadow:none!important;border-bottom:1px solid var(--mat-sys-outline-variant);margin-top:0!important;margin-bottom:0!important}.admin-menu mat-expansion-panel:first-of-type,.admin-menu mat-expansion-panel:last-of-type{border-radius:0!important}.admin-menu mat-expansion-panel-header{border-radius:0!important}.admin-aside{position:absolute;inset-block:0;left:0;z-index:30;display:flex;flex-direction:column;width:18rem;min-height:0;background:#fff;border-right:1px solid var(--mat-sys-outline-variant);transform:translate(-100%);transition:transform .2s ease,width .2s ease;overflow:hidden}.admin-aside-open{transform:translate(0)}@media(min-width:768px){.admin-aside{position:relative;inset:auto;z-index:auto;width:0;transform:none}.admin-aside-open{width:18rem}}\n"] }]
1627
- }], ctorParameters: () => [{ type: i1.Router }], propDecorators: { routes: [{
1737
+ ], 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"] }]
1738
+ }], ctorParameters: () => [{ type: i1.Router }, { type: i0.ElementRef }], propDecorators: { routes: [{
1628
1739
  type: Input,
1629
1740
  args: [{ required: true }]
1630
1741
  }], basePath: [{
1631
1742
  type: Input
1632
1743
  }], outlet: [{
1633
1744
  type: Input
1745
+ }], windowResized: [{
1746
+ type: HostListener,
1747
+ args: ['window:resize']
1634
1748
  }] } });
1635
1749
 
1636
1750
  const TaonBaselineBackofficeOutletName = 'admin';
@@ -1742,7 +1856,7 @@ class TaonErrorDetailsDialogComponent {
1742
1856
  return `${protocol}://file${location}`;
1743
1857
  }
1744
1858
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonErrorDetailsDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1745
- 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: i2$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$1.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$1.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }] }); }
1859
+ 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"] }] }); }
1746
1860
  }
1747
1861
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonErrorDetailsDialogComponent, decorators: [{
1748
1862
  type: Component,
@@ -1842,7 +1956,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImpor
1842
1956
 
1843
1957
  //#region imports
1844
1958
  //#endregion
1845
- const t = Translation.for('src/lib/taon-backoffice-notifications/taon-backoffice-notifications.component.ts', { "src/lib/taon-backoffice-notifications/taon-backoffice-notifications.component.ts": { "en-US": async () => (await import('./ui-browser-prod-taon-backoffice-notifications.component.ts.en-US.i18n.data-uyZjV7Qx.mjs')).default, "pl-PL": async () => (await import('./ui-browser-prod-taon-backoffice-notifications.component.ts.pl-PL.i18n.data-s0tK5HDk.mjs')).default } });
1959
+ const t = Translation.for('src/lib/taon-backoffice-notifications/taon-backoffice-notifications.component.ts', { "src/lib/taon-backoffice-notifications/taon-backoffice-notifications.component.ts": { "en-US": async () => (await import('./ui-browser-prod-taon-backoffice-notifications.component.ts.en-US.i18n.data-DdHr5V8R.mjs')).default, "pl-PL": async () => (await import('./ui-browser-prod-taon-backoffice-notifications.component.ts.pl-PL.i18n.data-CD5ZqDCD.mjs')).default } });
1846
1960
  class TaonBackofficeNotificationsComponent {
1847
1961
  //#endregion
1848
1962
  //#region constructor
@@ -1851,6 +1965,7 @@ class TaonBackofficeNotificationsComponent {
1851
1965
  this.t = t.for(this);
1852
1966
  this.notification = inject(TaonBackofficeNotificationsService);
1853
1967
  this.destroyRef = inject(DestroyRef);
1968
+ this.taonAdminService = inject(TaonAdminService);
1854
1969
  Resource__NS__listenErrors
1855
1970
  .pipe(takeUntilDestroyed(this.destroyRef))
1856
1971
  .subscribe((error) => {
@@ -1861,10 +1976,17 @@ class TaonBackofficeNotificationsComponent {
1861
1976
  if (!jsonMsg?.message) {
1862
1977
  console.log({ error });
1863
1978
  }
1864
- this.notification.error({
1865
- title: jsonMsg?.message || t.gettext('Unknown error'),
1866
- details: jsonMsg?.details,
1867
- });
1979
+ const statusCode = jsonMsg.status ?? error.statusCode;
1980
+ const httpStatus = getHttpStatus(statusCode, jsonMsg.message);
1981
+ if (this.taonAdminService.shouldSkipShowingError(httpStatus)) {
1982
+ console.log(`[@taon-dev/sudo] Skipping notificaiton for message "${jsonMsg.message}" (code=${statusCode})`);
1983
+ }
1984
+ else {
1985
+ this.notification.error({
1986
+ title: jsonMsg?.message || t.gettext('Unknown error'),
1987
+ details: jsonMsg?.details,
1988
+ });
1989
+ }
1868
1990
  });
1869
1991
  }
1870
1992
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonBackofficeNotificationsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
@@ -1889,7 +2011,7 @@ class TaonConfirmDialogComponent {
1889
2011
  this.dialogRef.close(true);
1890
2012
  }
1891
2013
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonConfirmDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1892
- 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: i2$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"] }] }); }
2014
+ 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"] }] }); }
1893
2015
  }
1894
2016
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonConfirmDialogComponent, decorators: [{
1895
2017
  type: Component,
@@ -1913,6 +2035,7 @@ const defaultColumns = [
1913
2035
  ];
1914
2036
  class TaonDatatableComponent {
1915
2037
  constructor() {
2038
+ //#region fields & getters
1916
2039
  this.cdr = inject(ChangeDetectorRef);
1917
2040
  this.destroyRef = inject(DestroyRef);
1918
2041
  //#region inputs
@@ -1941,6 +2064,7 @@ class TaonDatatableComponent {
1941
2064
  this.reload$ = new BehaviorSubject(undefined);
1942
2065
  this.searchChange$ = new Subject();
1943
2066
  this.data$ = this.reload$.pipe(switchMap(() => this.loadData()));
2067
+ this.expandedRows = new Set();
1944
2068
  }
1945
2069
  set data(value) {
1946
2070
  this._data = value ?? undefined;
@@ -1964,7 +2088,24 @@ class TaonDatatableComponent {
1964
2088
  return this.columns.filter(column => Boolean(column.field));
1965
2089
  }
1966
2090
  //#endregion
1967
- //#region init
2091
+ //#endregion
2092
+ //#region expansion
2093
+ onExpansionChange(event) {
2094
+ // console.log(event);
2095
+ // adapt this depending on exact MtxGrid event shape
2096
+ if (event.expanded) {
2097
+ this.expandedRows.add(event.data);
2098
+ }
2099
+ else {
2100
+ this.expandedRows.delete(event.data);
2101
+ }
2102
+ this.expansionChange.emit(event);
2103
+ }
2104
+ isExpanded(row) {
2105
+ return this.expandedRows.has(row);
2106
+ }
2107
+ //#endregion
2108
+ //#region hooks
1968
2109
  ngOnInit() {
1969
2110
  this.prepareColumns();
1970
2111
  // console.log({ columns: this.columns });
@@ -2131,7 +2272,7 @@ class TaonDatatableComponent {
2131
2272
  : this.entityCrudController.paginationQuery)(query).request()
2132
2273
  .observable).pipe(tap(response => {
2133
2274
  this.totalElements =
2134
- Number(response.headers.get(Symbols__NS__old.X_TOTAL_COUNT)) || 0;
2275
+ Number(response.headers.get(Symbols__NS__old__NS__X_TOTAL_COUNT)) || 0;
2135
2276
  }), map(response => this.prepareRows(response.body.json)), catchError(error => {
2136
2277
  console.error(`[taon-datatable] Unable to load data from ` +
2137
2278
  `${ClassHelpers__NS__getName(this.entityCrudController)} ${this.safe ? 'paginationQuerySafe' : 'paginationQuery'}`, error);
@@ -2197,7 +2338,7 @@ class TaonDatatableComponent {
2197
2338
  }
2198
2339
  }
2199
2340
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonDatatableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2200
- 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 <!-- 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\n <!-- 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\n <!-- 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]=\"expansionTemplate\"\n [length]=\"totalElements\"\n [pageOnFront]=\"false\"\n [pageIndex]=\"pageNumber - 1\"\n [pageSize]=\"pageSize\"\n [pageSizeOptions]=\"pageSizeOptions\"\n [showPaginator]=\"showPaginator\"\n (expansionChange)=\"expansionChange.emit($event)\"\n (page)=\"getNextPage($event)\"\n (sortChange)=\"sortChanged($event)\">\n </mtx-grid>\n </div>\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: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$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: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$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: i2$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: i2$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: i3$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3$2.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i3$2.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: i5.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: i6.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 }); }
2341
+ 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 }); }
2201
2342
  }
2202
2343
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonDatatableComponent, decorators: [{
2203
2344
  type: Component,
@@ -2212,7 +2353,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImpor
2212
2353
  MatPaginatorModule,
2213
2354
  MatSelectModule,
2214
2355
  MtxGridModule,
2215
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"flex w-full flex-col gap-3\">\n <!-- 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\n <!-- 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\n <!-- 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]=\"expansionTemplate\"\n [length]=\"totalElements\"\n [pageOnFront]=\"false\"\n [pageIndex]=\"pageNumber - 1\"\n [pageSize]=\"pageSize\"\n [pageSizeOptions]=\"pageSizeOptions\"\n [showPaginator]=\"showPaginator\"\n (expansionChange)=\"expansionChange.emit($event)\"\n (page)=\"getNextPage($event)\"\n (sortChange)=\"sortChanged($event)\">\n </mtx-grid>\n </div>\n</div>\n", styles: [":host{display:block;width:100%;min-height:250px}:host ::ng-deep .mat-mdc-form-field{margin:0}\n"] }]
2356
+ ], changeDetection: ChangeDetectionStrategy.OnPush, 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"] }]
2216
2357
  }], propDecorators: { pageNumber: [{
2217
2358
  type: Input
2218
2359
  }], pageSize: [{
@@ -2613,7 +2754,7 @@ class TaonDraggableButtonPanelComponent {
2613
2754
  return transitions[current].includes(next);
2614
2755
  }
2615
2756
  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 }); }
2616
- 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-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: i2$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: i4$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2757
+ 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 }); }
2617
2758
  }
2618
2759
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: TaonDraggableButtonPanelComponent, decorators: [{
2619
2760
  type: Component,
@@ -2624,7 +2765,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImpor
2624
2765
  MatButtonModule,
2625
2766
  MatIconModule,
2626
2767
  MatTooltipModule,
2627
- ], 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-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"] }]
2768
+ ], 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"] }]
2628
2769
  }], ctorParameters: () => [{ type: i1.Router }], propDecorators: { drag: [{
2629
2770
  type: ViewChild,
2630
2771
  args: [CdkDrag]