@memberjunction/ng-dashboard-viewer 5.42.0 → 5.44.0

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 (46) hide show
  1. package/dist/lib/config-panels/artifact-config-panel.component.d.ts +0 -1
  2. package/dist/lib/config-panels/artifact-config-panel.component.d.ts.map +1 -1
  3. package/dist/lib/config-panels/artifact-config-panel.component.js +49 -63
  4. package/dist/lib/config-panels/artifact-config-panel.component.js.map +1 -1
  5. package/dist/lib/config-panels/query-config-panel.component.d.ts +0 -1
  6. package/dist/lib/config-panels/query-config-panel.component.d.ts.map +1 -1
  7. package/dist/lib/config-panels/query-config-panel.component.js +110 -124
  8. package/dist/lib/config-panels/query-config-panel.component.js.map +1 -1
  9. package/dist/lib/config-panels/view-config-panel.component.d.ts +0 -2
  10. package/dist/lib/config-panels/view-config-panel.component.d.ts.map +1 -1
  11. package/dist/lib/config-panels/view-config-panel.component.js +142 -171
  12. package/dist/lib/config-panels/view-config-panel.component.js.map +1 -1
  13. package/dist/lib/config-panels/weburl-config-panel.component.js +2 -2
  14. package/dist/lib/dashboard-browser/dashboard-browser.component.d.ts +4 -0
  15. package/dist/lib/dashboard-browser/dashboard-browser.component.d.ts.map +1 -1
  16. package/dist/lib/dashboard-browser/dashboard-browser.component.js +205 -272
  17. package/dist/lib/dashboard-browser/dashboard-browser.component.js.map +1 -1
  18. package/dist/lib/dashboard-viewer/dashboard-viewer.component.d.ts +46 -1
  19. package/dist/lib/dashboard-viewer/dashboard-viewer.component.d.ts.map +1 -1
  20. package/dist/lib/dashboard-viewer/dashboard-viewer.component.js +229 -49
  21. package/dist/lib/dashboard-viewer/dashboard-viewer.component.js.map +1 -1
  22. package/dist/lib/dashboard-viewer.module.d.ts +2 -1
  23. package/dist/lib/dashboard-viewer.module.d.ts.map +1 -1
  24. package/dist/lib/dashboard-viewer.module.js +11 -3
  25. package/dist/lib/dashboard-viewer.module.js.map +1 -1
  26. package/dist/lib/parts/artifact-part.component.d.ts.map +1 -1
  27. package/dist/lib/parts/artifact-part.component.js +25 -32
  28. package/dist/lib/parts/artifact-part.component.js.map +1 -1
  29. package/dist/lib/parts/query-part.component.d.ts.map +1 -1
  30. package/dist/lib/parts/query-part.component.js +25 -32
  31. package/dist/lib/parts/query-part.component.js.map +1 -1
  32. package/dist/lib/parts/view-part.component.d.ts.map +1 -1
  33. package/dist/lib/parts/view-part.component.js +25 -32
  34. package/dist/lib/parts/view-part.component.js.map +1 -1
  35. package/dist/lib/parts/weburl-part.component.d.ts.map +1 -1
  36. package/dist/lib/parts/weburl-part.component.js +46 -52
  37. package/dist/lib/parts/weburl-part.component.js.map +1 -1
  38. package/package.json +13 -11
  39. package/dist/__tests__/exports.test.d.ts +0 -2
  40. package/dist/__tests__/exports.test.d.ts.map +0 -1
  41. package/dist/__tests__/exports.test.js +0 -17
  42. package/dist/__tests__/exports.test.js.map +0 -1
  43. package/dist/__tests__/index.test.d.ts +0 -2
  44. package/dist/__tests__/index.test.d.ts.map +0 -1
  45. package/dist/__tests__/index.test.js +0 -7
  46. package/dist/__tests__/index.test.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard-viewer.component.d.ts","sourceRoot":"","sources":["../../../src/lib/dashboard-viewer/dashboard-viewer.component.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dashboard-viewer.component.d.ts","sourceRoot":"","sources":["../../../src/lib/dashboard-viewer/dashboard-viewer.component.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { Component, Input, Output, EventEmitter, ViewChild, createComponent, ViewEncapsulation } from '@angular/core';
2
2
  import { Subject } from 'rxjs';
3
3
  import { takeUntil } from 'rxjs/operators';
4
- import { MJGlobal, UUIDsEqual } from '@memberjunction/global';
4
+ import { MJGlobal, UUIDsEqual, EscapeHTML } from '@memberjunction/global';
5
5
  import { BaseAngularComponent } from '@memberjunction/ng-base-types';
6
6
  import { DashboardEngine } from '@memberjunction/core-entities';
7
7
  import { createDefaultDashboardConfig, generatePanelId, extractPanelsFromLayout, findPanelInLayout } from '../models/dashboard-types';
@@ -9,7 +9,8 @@ import { GoldenLayoutWrapperService } from '../services/golden-layout-wrapper.se
9
9
  import { BaseDashboardPart } from '../parts/base-dashboard-part';
10
10
  import * as i0 from "@angular/core";
11
11
  import * as i1 from "@memberjunction/ng-shared-generic";
12
- import * as i2 from "../breadcrumb/dashboard-breadcrumb.component";
12
+ import * as i2 from "@memberjunction/ng-ui-components";
13
+ import * as i3 from "../breadcrumb/dashboard-breadcrumb.component";
13
14
  const _c0 = ["layoutContainer"];
14
15
  function DashboardViewerComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
15
16
  const _r1 = i0.ɵɵgetCurrentView();
@@ -106,31 +107,16 @@ function DashboardViewerComponent_Conditional_3_Template(rf, ctx) { if (rf & 1)
106
107
  i0.ɵɵelement(1, "mj-loading", 26);
107
108
  i0.ɵɵelementEnd();
108
109
  } }
109
- function DashboardViewerComponent_Conditional_6_Conditional_7_Template(rf, ctx) { if (rf & 1) {
110
- const _r7 = i0.ɵɵgetCurrentView();
111
- i0.ɵɵelementStart(0, "button", 30);
112
- i0.ɵɵlistener("click", function DashboardViewerComponent_Conditional_6_Conditional_7_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r7); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.onAddPanelClick()); });
113
- i0.ɵɵelement(1, "i", 22);
114
- i0.ɵɵtext(2, " Add Your First Part ");
115
- i0.ɵɵelementEnd();
116
- } }
117
110
  function DashboardViewerComponent_Conditional_6_Template(rf, ctx) { if (rf & 1) {
118
- i0.ɵɵelementStart(0, "div", 6)(1, "div", 27);
119
- i0.ɵɵelement(2, "i", 28);
120
- i0.ɵɵelementEnd();
121
- i0.ɵɵelementStart(3, "h3");
122
- i0.ɵɵtext(4, "No parts configured");
123
- i0.ɵɵelementEnd();
124
- i0.ɵɵelementStart(5, "p");
125
- i0.ɵɵtext(6, "This dashboard has no parts yet. Add your first part to start visualizing your data.");
126
- i0.ɵɵelementEnd();
127
- i0.ɵɵconditionalCreate(7, DashboardViewerComponent_Conditional_6_Conditional_7_Template, 3, 0, "button", 29);
111
+ const _r7 = i0.ɵɵgetCurrentView();
112
+ i0.ɵɵelementStart(0, "mj-empty-state", 27);
113
+ i0.ɵɵlistener("Action", function DashboardViewerComponent_Conditional_6_Template_mj_empty_state_Action_0_listener() { i0.ɵɵrestoreView(_r7); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onAddPanelClick()); });
128
114
  i0.ɵɵelementEnd();
129
115
  } if (rf & 2) {
130
116
  const ctx_r1 = i0.ɵɵnextContext();
131
- i0.ɵɵadvance(7);
132
- i0.ɵɵconditional(ctx_r1.isEditing ? 7 : -1);
117
+ i0.ɵɵproperty("ActionText", ctx_r1.isEditing ? "Add Your First Part" : "");
133
118
  } }
119
+ const LAYOUT_CONTAINER_SIZE_TIMEOUT_MS = 10_000;
134
120
  /**
135
121
  * Main dashboard viewer component.
136
122
  * Renders a configurable dashboard with draggable/resizable panels using Golden Layout.
@@ -238,6 +224,12 @@ export class DashboardViewerComponent extends BaseAngularComponent {
238
224
  breadcrumbNavigate = new EventEmitter();
239
225
  /** Emitted when user clicks "Open in Tab" button */
240
226
  openInTab = new EventEmitter();
227
+ /** Emitted as the Golden Layout viewer moves through dashboard/layout readiness states. */
228
+ layoutLifecycle = new EventEmitter();
229
+ /** Emitted once Golden Layout has initialized with a non-zero container and updated its size. */
230
+ layoutReady = new EventEmitter();
231
+ /** Emitted when layout initialization is deferred because the container cannot be initialized yet. */
232
+ layoutDeferred = new EventEmitter();
241
233
  // ========================================
242
234
  // View Children
243
235
  // ========================================
@@ -261,6 +253,14 @@ export class DashboardViewerComponent extends BaseAngularComponent {
261
253
  _glService = null;
262
254
  /** Promise that resolves when part types are loaded - used to ensure layout waits for part types */
263
255
  _partTypesLoaded = null;
256
+ _layoutReadyPromise = Promise.resolve();
257
+ _resolveLayoutReady = null;
258
+ _rejectLayoutReady = null;
259
+ _layoutInitGeneration = 0;
260
+ _layoutReadyTimer = null;
261
+ _resolveDeferredLayoutInit = null;
262
+ _rejectDeferredLayoutInit = null;
263
+ _deferredLayoutInitTimer = null;
264
264
  // ========================================
265
265
  // Constructor
266
266
  // ========================================
@@ -279,6 +279,11 @@ export class DashboardViewerComponent extends BaseAngularComponent {
279
279
  ngOnDestroy() {
280
280
  this._destroy$.next();
281
281
  this._destroy$.complete();
282
+ const generation = this._layoutInitGeneration;
283
+ this.resolveLayoutReady(generation);
284
+ this._layoutInitGeneration++;
285
+ this.clearLayoutReadyTimer();
286
+ this.cancelDeferredLayoutInit();
282
287
  this.destroyLayout();
283
288
  }
284
289
  // ========================================
@@ -384,6 +389,13 @@ export class DashboardViewerComponent extends BaseAngularComponent {
384
389
  await this.initializeLayout();
385
390
  }
386
391
  }
392
+ /**
393
+ * Resolves when the current dashboard's Golden Layout instance has initialized
394
+ * against a non-zero container and completed its first size update.
395
+ */
396
+ waitForLayoutReady() {
397
+ return this._layoutReadyPromise;
398
+ }
387
399
  /**
388
400
  * Get the current configuration
389
401
  */
@@ -531,15 +543,28 @@ export class DashboardViewerComponent extends BaseAngularComponent {
531
543
  async onDashboardChanged() {
532
544
  if (!this._dashboard)
533
545
  return;
534
- // Parse or create config
535
- this.config = this.parseOrCreateConfig();
536
- // Wait for part types to be loaded before initializing layout
537
- // This ensures partTypes array is populated when createPanelComponent is called
538
- if (this._partTypesLoaded) {
539
- await this._partTypesLoaded;
546
+ const generation = this.startLayoutReadyCycle();
547
+ try {
548
+ // Parse or create config
549
+ this.config = this.parseOrCreateConfig();
550
+ // Wait for part types to be loaded before initializing layout
551
+ // This ensures partTypes array is populated when createPanelComponent is called
552
+ if (this._partTypesLoaded) {
553
+ this.emitLayoutLifecycle('pending-parts');
554
+ await this._partTypesLoaded;
555
+ }
556
+ if (generation !== this._layoutInitGeneration) {
557
+ return;
558
+ }
559
+ // Initialize layout
560
+ await this.initializeLayout(generation);
561
+ }
562
+ catch (err) {
563
+ const error = err instanceof Error ? err : new Error(String(err));
564
+ this.emitLayoutLifecycle('error', undefined, error);
565
+ this.rejectLayoutReady(error, generation);
566
+ this.error.emit({ message: 'Failed to initialize dashboard layout', error });
540
567
  }
541
- // Initialize layout
542
- this.initializeLayout();
543
568
  }
544
569
  parseOrCreateConfig() {
545
570
  if (!this._dashboard?.UIConfigDetails) {
@@ -562,34 +587,183 @@ export class DashboardViewerComponent extends BaseAngularComponent {
562
587
  // ========================================
563
588
  // Private Methods - Layout
564
589
  // ========================================
565
- initializeLayout() {
590
+ async initializeLayout(generation = this._layoutInitGeneration) {
566
591
  if (!this.config || !this.layoutContainer?.nativeElement) {
567
592
  return;
568
593
  }
594
+ const el = this.layoutContainer.nativeElement;
595
+ const rect = el.getBoundingClientRect();
596
+ if (rect.width === 0 || rect.height === 0) {
597
+ // The container has no size yet — typically because Explorer is reattaching
598
+ // this cached resource component on browser back/forward and the pane hasn't
599
+ // been laid out. GoldenLayout never fires its 'show' event for a zero-size
600
+ // container, so each panel's lazy content factory never runs and the panel is
601
+ // stuck on "Loading…" forever (the React component inside never even mounts).
602
+ // Defer init until the container actually has a size, then build the layout.
603
+ const reason = 'layout container has zero size';
604
+ this.emitLayoutLifecycle('waiting-for-size', reason);
605
+ this.layoutDeferred.emit({ reason });
606
+ await this.waitForLayoutContainerSize(el, generation);
607
+ if (generation !== this._layoutInitGeneration) {
608
+ return;
609
+ }
610
+ }
611
+ // We're proceeding with a real init — cancel any pending deferred attempt.
612
+ this.cancelDeferredLayoutInit();
613
+ this.emitLayoutLifecycle('initializing');
569
614
  // Destroy existing layout
570
615
  this.destroyLayout();
571
616
  // Create new Golden Layout service
572
617
  this._glService = new GoldenLayoutWrapperService();
573
618
  // Subscribe to layout events
574
619
  this.subscribeToLayoutEvents();
620
+ const pendingPanelCreations = [];
575
621
  // Panel factory - called by GL when it binds a component
576
622
  // The panel comes directly from GL's componentState (single source of truth)
577
623
  const panelFactory = (panel, container) => {
578
- this.createPanelComponent(panel, container);
624
+ const creation = this.createPanelComponent(panel, container);
625
+ pendingPanelCreations.push(creation.catch(error => {
626
+ console.error('[DashboardViewer] Failed to create panel component:', error);
627
+ }));
579
628
  };
580
629
  // Initialize with saved layout (or null for empty dashboard)
581
630
  // Golden Layout's native ResolvedLayoutConfig is the source of truth
582
631
  // Panel data is embedded in each component's componentState
583
632
  this._glService.initialize(this.layoutContainer.nativeElement, this.config.layout, panelFactory, this.isEditing);
633
+ if (pendingPanelCreations.length > 0) {
634
+ await Promise.all(pendingPanelCreations);
635
+ }
584
636
  // After GL.initialize() completes, all components from the saved layout
585
637
  // have been synchronously bound via the panelFactory callback.
586
638
  // However, Angular needs time to complete change detection and render
587
639
  // the dynamic components. A single delayed updateSize() ensures GL
588
640
  // recalculates dimensions after Angular has finished rendering.
589
- setTimeout(() => {
590
- this._glService?.updateSize();
591
- this.cdr.detectChanges();
592
- }, 100);
641
+ await new Promise(resolve => {
642
+ this.clearLayoutReadyTimer();
643
+ this._layoutReadyTimer = setTimeout(() => {
644
+ this._layoutReadyTimer = null;
645
+ resolve();
646
+ }, 100);
647
+ });
648
+ if (generation !== this._layoutInitGeneration) {
649
+ return;
650
+ }
651
+ this._glService?.updateSize();
652
+ this.cdr.detectChanges();
653
+ this.emitLayoutLifecycle('ready');
654
+ this.layoutReady.emit();
655
+ this.resolveLayoutReady(generation);
656
+ }
657
+ /** ResizeObserver used to wait for a zero-size container to gain a size before init. */
658
+ _layoutSizeObserver = null;
659
+ /**
660
+ * Wait (via ResizeObserver) until the layout container has a non-zero size, then
661
+ * (re)initialize. Used when initializeLayout() is called while the container is
662
+ * still 0×0 — e.g. during a cached-component reattach on browser back/forward —
663
+ * where GoldenLayout would otherwise bind panels that never receive a 'show' event.
664
+ */
665
+ waitForLayoutContainerSize(el, generation) {
666
+ this.cancelDeferredLayoutInit();
667
+ return new Promise((resolve, reject) => {
668
+ this._resolveDeferredLayoutInit = resolve;
669
+ this._rejectDeferredLayoutInit = reject;
670
+ const ro = new ResizeObserver(() => {
671
+ if (generation !== this._layoutInitGeneration) {
672
+ this.cancelDeferredLayoutInit();
673
+ return;
674
+ }
675
+ const r = el.getBoundingClientRect();
676
+ if (r.width > 0 && r.height > 0) {
677
+ this.cancelDeferredLayoutInit();
678
+ }
679
+ });
680
+ const r = el.getBoundingClientRect();
681
+ if (r.width > 0 && r.height > 0) {
682
+ this._resolveDeferredLayoutInit = null;
683
+ this._rejectDeferredLayoutInit = null;
684
+ resolve();
685
+ return;
686
+ }
687
+ this._deferredLayoutInitTimer = setTimeout(() => {
688
+ const error = new Error(`Dashboard layout container stayed at zero size for ${LAYOUT_CONTAINER_SIZE_TIMEOUT_MS}ms`);
689
+ this.failDeferredLayoutInit(error);
690
+ }, LAYOUT_CONTAINER_SIZE_TIMEOUT_MS);
691
+ ro.observe(el);
692
+ this._layoutSizeObserver = ro;
693
+ });
694
+ }
695
+ cancelDeferredLayoutInit() {
696
+ this.clearDeferredLayoutInitTimer();
697
+ if (this._layoutSizeObserver) {
698
+ this._layoutSizeObserver.disconnect();
699
+ this._layoutSizeObserver = null;
700
+ }
701
+ if (this._resolveDeferredLayoutInit) {
702
+ this._resolveDeferredLayoutInit();
703
+ this._resolveDeferredLayoutInit = null;
704
+ }
705
+ this._rejectDeferredLayoutInit = null;
706
+ }
707
+ failDeferredLayoutInit(error) {
708
+ this.clearDeferredLayoutInitTimer();
709
+ if (this._layoutSizeObserver) {
710
+ this._layoutSizeObserver.disconnect();
711
+ this._layoutSizeObserver = null;
712
+ }
713
+ if (this._rejectDeferredLayoutInit) {
714
+ this._rejectDeferredLayoutInit(error);
715
+ this._rejectDeferredLayoutInit = null;
716
+ }
717
+ this._resolveDeferredLayoutInit = null;
718
+ }
719
+ clearDeferredLayoutInitTimer() {
720
+ if (this._deferredLayoutInitTimer) {
721
+ clearTimeout(this._deferredLayoutInitTimer);
722
+ this._deferredLayoutInitTimer = null;
723
+ }
724
+ }
725
+ startLayoutReadyCycle() {
726
+ const generation = ++this._layoutInitGeneration;
727
+ this.clearLayoutReadyTimer();
728
+ this.cancelDeferredLayoutInit();
729
+ this._resolveLayoutReady?.();
730
+ this._layoutReadyPromise = new Promise((resolve, reject) => {
731
+ this._resolveLayoutReady = resolve;
732
+ this._rejectLayoutReady = reject;
733
+ });
734
+ this._layoutReadyPromise.catch(() => undefined);
735
+ this.emitLayoutLifecycle('pending-dashboard');
736
+ return generation;
737
+ }
738
+ resolveLayoutReady(generation) {
739
+ if (generation !== this._layoutInitGeneration) {
740
+ return;
741
+ }
742
+ this._resolveLayoutReady?.();
743
+ this._resolveLayoutReady = null;
744
+ this._rejectLayoutReady = null;
745
+ }
746
+ rejectLayoutReady(error, generation) {
747
+ if (generation !== this._layoutInitGeneration) {
748
+ return;
749
+ }
750
+ this._rejectLayoutReady?.(error);
751
+ this._resolveLayoutReady = null;
752
+ this._rejectLayoutReady = null;
753
+ }
754
+ clearLayoutReadyTimer() {
755
+ if (this._layoutReadyTimer) {
756
+ clearTimeout(this._layoutReadyTimer);
757
+ this._layoutReadyTimer = null;
758
+ }
759
+ }
760
+ emitLayoutLifecycle(state, reason, error) {
761
+ this.layoutLifecycle.emit({
762
+ state,
763
+ dashboardId: this._dashboard?.ID,
764
+ reason,
765
+ error,
766
+ });
593
767
  }
594
768
  /** Flag to prevent panel removal during layout reinit */
595
769
  _isReinitializing = false;
@@ -754,8 +928,8 @@ export class DashboardViewerComponent extends BaseAngularComponent {
754
928
  const titleSection = document.createElement('div');
755
929
  titleSection.style.cssText = 'display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0;';
756
930
  titleSection.innerHTML = `
757
- <i class="${panel.icon || 'fa-solid fa-puzzle-piece'}" style="color: var(--mj-brand-primary); font-size: 14px;"></i>
758
- <span style="font-weight: 500; font-size: 14px; color: var(--mj-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${panel.title}</span>
931
+ <i class="${EscapeHTML(panel.icon || 'fa-solid fa-puzzle-piece')}" style="color: var(--mj-brand-primary); font-size: 14px;"></i>
932
+ <span style="font-weight: 500; font-size: 14px; color: var(--mj-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${EscapeHTML(panel.title)}</span>
759
933
  `;
760
934
  header.appendChild(titleSection);
761
935
  // Action buttons (only in edit mode)
@@ -895,9 +1069,9 @@ export class DashboardViewerComponent extends BaseAngularComponent {
895
1069
  <i class="fa-solid fa-table" style="font-size: 20px; color: var(--mj-brand-primary);"></i>
896
1070
  <div>
897
1071
  <div style="font-weight: 500; color: var(--mj-text-primary); font-size: 14px;">Entity View</div>
898
- <div style="font-size: 12px; color: var(--mj-text-secondary);">${entityName || 'View ' + viewInfo}</div>
1072
+ <div style="font-size: 12px; color: var(--mj-text-secondary);">${EscapeHTML(entityName || 'View ' + viewInfo)}</div>
899
1073
  </div>
900
- <span style="margin-left: auto; padding: 4px 10px; background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent); color: var(--mj-brand-primary); border-radius: 12px; font-size: 11px; font-weight: 500;">${displayMode}</span>
1074
+ <span style="margin-left: auto; padding: 4px 10px; background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent); color: var(--mj-brand-primary); border-radius: 12px; font-size: 11px; font-weight: 500;">${EscapeHTML(displayMode)}</span>
901
1075
  </div>
902
1076
  </div>
903
1077
  <div style="flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; color: var(--mj-text-muted); padding: 24px;">
@@ -930,7 +1104,7 @@ export class DashboardViewerComponent extends BaseAngularComponent {
930
1104
  <i class="fa-solid fa-database" style="font-size: 20px; color: var(--mj-brand-primary);"></i>
931
1105
  <div>
932
1106
  <div style="font-weight: 500; color: var(--mj-text-primary); font-size: 14px;">Query Results</div>
933
- <div style="font-size: 12px; color: var(--mj-text-secondary);">${queryInfo}</div>
1107
+ <div style="font-size: 12px; color: var(--mj-text-secondary);">${EscapeHTML(queryInfo)}</div>
934
1108
  </div>
935
1109
  <span style="margin-left: auto; padding: 4px 10px; background: color-mix(in srgb, var(--mj-status-success) 10%, transparent); color: var(--mj-status-success); border-radius: 12px; font-size: 11px; font-weight: 500;">${autoRefreshSeconds > 0 ? 'Refresh: ' + autoRefreshSeconds + 's' : 'Manual refresh'}</span>
936
1110
  </div>
@@ -965,9 +1139,9 @@ export class DashboardViewerComponent extends BaseAngularComponent {
965
1139
  <i class="fa-solid fa-cube" style="font-size: 20px; color: var(--mj-brand-primary);"></i>
966
1140
  <div>
967
1141
  <div style="font-weight: 500; color: var(--mj-text-primary); font-size: 14px;">Artifact</div>
968
- <div style="font-size: 12px; color: var(--mj-text-secondary);">ID: ${artifactInfo}</div>
1142
+ <div style="font-size: 12px; color: var(--mj-text-secondary);">ID: ${EscapeHTML(artifactInfo)}</div>
969
1143
  </div>
970
- <span style="margin-left: auto; padding: 4px 10px; background: color-mix(in srgb, var(--mj-status-error) 10%, transparent); color: var(--mj-status-error); border-radius: 12px; font-size: 11px; font-weight: 500;">${versionInfo}</span>
1144
+ <span style="margin-left: auto; padding: 4px 10px; background: color-mix(in srgb, var(--mj-status-error) 10%, transparent); color: var(--mj-status-error); border-radius: 12px; font-size: 11px; font-weight: 500;">${EscapeHTML(versionInfo)}</span>
971
1145
  </div>
972
1146
  </div>
973
1147
  <div style="flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; color: var(--mj-text-muted); padding: 24px;">
@@ -983,7 +1157,7 @@ export class DashboardViewerComponent extends BaseAngularComponent {
983
1157
  container.innerHTML = `
984
1158
  <div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: var(--mj-text-secondary); text-align: center; padding: 24px;">
985
1159
  <i class="fa-solid fa-puzzle-piece" style="font-size: 48px; color: var(--mj-text-muted); margin-bottom: 16px;"></i>
986
- <h4 style="margin: 0 0 8px 0; color: var(--mj-text-primary);">${partTypeName} Part</h4>
1160
+ <h4 style="margin: 0 0 8px 0; color: var(--mj-text-primary);">${EscapeHTML(partTypeName)} Part</h4>
987
1161
  <p style="margin: 0; font-size: 13px;">This part type is not yet fully implemented.</p>
988
1162
  </div>
989
1163
  `;
@@ -1059,13 +1233,13 @@ export class DashboardViewerComponent extends BaseAngularComponent {
1059
1233
  } if (rf & 2) {
1060
1234
  let _t;
1061
1235
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.layoutContainer = _t.first);
1062
- } }, inputs: { dashboard: "dashboard", dashboardId: "dashboardId", isEditing: "isEditing", showToolbar: "showToolbar", autoSave: "autoSave", showBreadcrumb: "showBreadcrumb", showOpenInTabButton: "showOpenInTabButton", showEditButton: "showEditButton", Categories: "Categories" }, outputs: { configChanged: "configChanged", navigationRequested: "navigationRequested", panelInteraction: "panelInteraction", dashboardSaved: "dashboardSaved", error: "error", editModeChanged: "editModeChanged", breadcrumbNavigate: "breadcrumbNavigate", openInTab: "openInTab" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 7, vars: 8, consts: [["layoutContainer", ""], [1, "dashboard-viewer"], ["Size", "large", "RootIcon", "fa-solid fa-gauge-high", "RootLabel", "Dashboards", 3, "Categories", "CurrentCategoryId", "CurrentDashboard", "ShowDashboardName", "AllowDragDrop"], [1, "dashboard-toolbar"], [1, "loading-overlay"], [1, "layout-container"], [1, "empty-state"], ["Size", "large", "RootIcon", "fa-solid fa-gauge-high", "RootLabel", "Dashboards", 3, "Navigate", "Categories", "CurrentCategoryId", "CurrentDashboard", "ShowDashboardName", "AllowDragDrop"], [1, "toolbar-left"], [1, "dashboard-title"], [1, "toolbar-center"], [1, "unsaved-indicator"], [1, "toolbar-right"], ["title", "Open in its own tab", 1, "toolbar-button"], [1, "toolbar-button"], [1, "toolbar-button", 3, "active"], [1, "toolbar-button", "primary"], [1, "fa-solid", "fa-chart-line"], [1, "fa-solid", "fa-circle"], ["title", "Open in its own tab", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-up-right-from-square"], [1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-plus"], [1, "fa-solid", "fa-edit"], [1, "toolbar-button", "primary", 3, "click"], [1, "fa-solid", "fa-save"], ["text", "Loading dashboard..."], [1, "empty-icon"], [1, "fa-solid", "fa-layer-group"], [1, "add-part-button"], [1, "add-part-button", 3, "click"]], template: function DashboardViewerComponent_Template(rf, ctx) { if (rf & 1) {
1236
+ } }, inputs: { dashboard: "dashboard", dashboardId: "dashboardId", isEditing: "isEditing", showToolbar: "showToolbar", autoSave: "autoSave", showBreadcrumb: "showBreadcrumb", showOpenInTabButton: "showOpenInTabButton", showEditButton: "showEditButton", Categories: "Categories" }, outputs: { configChanged: "configChanged", navigationRequested: "navigationRequested", panelInteraction: "panelInteraction", dashboardSaved: "dashboardSaved", error: "error", editModeChanged: "editModeChanged", breadcrumbNavigate: "breadcrumbNavigate", openInTab: "openInTab", layoutLifecycle: "layoutLifecycle", layoutReady: "layoutReady", layoutDeferred: "layoutDeferred" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 7, vars: 8, consts: [["layoutContainer", ""], [1, "dashboard-viewer"], ["Size", "large", "RootIcon", "fa-solid fa-gauge-high", "RootLabel", "Dashboards", 3, "Categories", "CurrentCategoryId", "CurrentDashboard", "ShowDashboardName", "AllowDragDrop"], [1, "dashboard-toolbar"], [1, "loading-overlay"], [1, "layout-container"], ["Icon", "fa-solid fa-layer-group", "Title", "No parts configured", "Message", "This dashboard has no parts yet. Add your first part to start visualizing your data.", "ActionIcon", "fa-solid fa-plus", 1, "dashboard-empty", 3, "ActionText"], ["Size", "large", "RootIcon", "fa-solid fa-gauge-high", "RootLabel", "Dashboards", 3, "Navigate", "Categories", "CurrentCategoryId", "CurrentDashboard", "ShowDashboardName", "AllowDragDrop"], [1, "toolbar-left"], [1, "dashboard-title"], [1, "toolbar-center"], [1, "unsaved-indicator"], [1, "toolbar-right"], ["title", "Open in its own tab", 1, "toolbar-button"], [1, "toolbar-button"], [1, "toolbar-button", 3, "active"], [1, "toolbar-button", "primary"], [1, "fa-solid", "fa-chart-line"], [1, "fa-solid", "fa-circle"], ["title", "Open in its own tab", 1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-up-right-from-square"], [1, "toolbar-button", 3, "click"], [1, "fa-solid", "fa-plus"], [1, "fa-solid", "fa-edit"], [1, "toolbar-button", "primary", 3, "click"], [1, "fa-solid", "fa-save"], ["text", "Loading dashboard..."], ["Icon", "fa-solid fa-layer-group", "Title", "No parts configured", "Message", "This dashboard has no parts yet. Add your first part to start visualizing your data.", "ActionIcon", "fa-solid fa-plus", 1, "dashboard-empty", 3, "Action", "ActionText"]], template: function DashboardViewerComponent_Template(rf, ctx) { if (rf & 1) {
1063
1237
  i0.ɵɵelementStart(0, "div", 1);
1064
1238
  i0.ɵɵconditionalCreate(1, DashboardViewerComponent_Conditional_1_Template, 1, 5, "mj-dashboard-breadcrumb", 2);
1065
1239
  i0.ɵɵconditionalCreate(2, DashboardViewerComponent_Conditional_2_Template, 10, 6, "div", 3);
1066
1240
  i0.ɵɵconditionalCreate(3, DashboardViewerComponent_Conditional_3_Template, 2, 0, "div", 4);
1067
1241
  i0.ɵɵelement(4, "div", 5, 0);
1068
- i0.ɵɵconditionalCreate(6, DashboardViewerComponent_Conditional_6_Template, 8, 1, "div", 6);
1242
+ i0.ɵɵconditionalCreate(6, DashboardViewerComponent_Conditional_6_Template, 1, 1, "mj-empty-state", 6);
1069
1243
  i0.ɵɵelementEnd();
1070
1244
  } if (rf & 2) {
1071
1245
  i0.ɵɵclassProp("editing", ctx.isEditing)("has-toolbar", ctx.shouldShowToolbar);
@@ -1077,11 +1251,11 @@ export class DashboardViewerComponent extends BaseAngularComponent {
1077
1251
  i0.ɵɵconditional(ctx.isLoading ? 3 : -1);
1078
1252
  i0.ɵɵadvance(3);
1079
1253
  i0.ɵɵconditional(!ctx.hasPanels && !ctx.isLoading ? 6 : -1);
1080
- } }, dependencies: [i1.LoadingComponent, i2.DashboardBreadcrumbComponent], styles: ["/**\n * Dashboard Viewer Component Styles\n *\n * IMPORTANT: For Golden Layout tabs to display correctly, you must import\n * Golden Layout's CSS in your application's global styles:\n *\n * @import 'golden-layout/dist/css/goldenlayout-base.css';\n *\n * Or add to your angular.json styles array:\n * \"node_modules/golden-layout/dist/css/goldenlayout-base.css\"\n */\n\n:host {\n display: block;\n width: 100%;\n height: 100%;\n position: relative;\n}\n\n.dashboard-viewer {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n background: var(--dashboard-bg, var(--mj-bg-surface-card));\n}\n\n.dashboard-viewer.editing .layout-container {\n outline: 2px dashed var(--dashboard-edit-outline, var(--mj-brand-primary));\n outline-offset: -2px;\n}\n\n/* ========================================\n Toolbar\n ======================================== */\n\n.dashboard-toolbar {\n flex: 0 0 48px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 16px;\n background: var(--dashboard-toolbar-bg, var(--mj-bg-surface));\n border-bottom: 1px solid var(--dashboard-border, var(--mj-border-default));\n box-sizing: border-box;\n z-index: 10;\n}\n\n.toolbar-left,\n.toolbar-center,\n.toolbar-right {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.toolbar-left {\n flex: 1;\n}\n\n.toolbar-center {\n flex: 0 0 auto;\n}\n\n.toolbar-right {\n flex: 1;\n justify-content: flex-end;\n}\n\n.dashboard-title {\n font-size: 16px;\n font-weight: 500;\n color: var(--dashboard-title-color, var(--mj-text-primary));\n}\n\n.dashboard-title i {\n margin-right: 8px;\n color: var(--dashboard-title-icon, var(--mj-brand-primary));\n}\n\n.unsaved-indicator {\n font-size: 12px;\n color: var(--dashboard-unsaved-color, var(--mj-status-warning));\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.unsaved-indicator i {\n font-size: 8px;\n}\n\n.toolbar-button {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border: 1px solid var(--dashboard-button-border, var(--mj-border-strong));\n border-radius: 4px;\n background: var(--dashboard-button-bg, var(--mj-bg-surface));\n color: var(--dashboard-button-color, var(--mj-text-primary));\n font-size: 13px;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.toolbar-button:hover {\n background: var(--dashboard-button-hover-bg, var(--mj-bg-surface-card));\n border-color: var(--dashboard-button-hover-border, var(--mj-border-strong));\n}\n\n.toolbar-button.active {\n background: var(--dashboard-button-active-bg, color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface)));\n border-color: var(--dashboard-button-active-border, var(--mj-brand-primary));\n color: var(--dashboard-button-active-color, var(--mj-brand-primary));\n}\n\n.toolbar-button.primary {\n background: var(--dashboard-button-primary-bg, var(--mj-brand-primary));\n border-color: var(--dashboard-button-primary-border, var(--mj-brand-primary-hover));\n color: var(--dashboard-button-primary-color, var(--mj-text-inverse));\n}\n\n.toolbar-button.primary:hover {\n background: var(--dashboard-button-primary-hover-bg, var(--mj-brand-primary-hover));\n}\n\n.toolbar-button i {\n font-size: 14px;\n}\n\n/* ========================================\n Layout Container\n ======================================== */\n\n/* Layout container fills remaining space in the flex column */\n.layout-container {\n flex: 1;\n position: relative;\n overflow: hidden;\n min-height: 0;\n}\n\n/* ========================================\n Dashboard Panel Content (our wrapper)\n ======================================== */\n\n/* Dashboard panel wrapper (header + content) */\n:host ::ng-deep .dashboard-part-wrapper {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface);\n}\n\n/* Dashboard part header */\n:host ::ng-deep .dashboard-part-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 12px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n min-height: 40px;\n flex-shrink: 0;\n}\n\n/* Dashboard part content */\n:host ::ng-deep .dashboard-part-content {\n flex: 1;\n overflow: auto;\n min-height: 0;\n}\n\n/* ========================================\n Loading Overlay\n ======================================== */\n\n.loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--mj-bg-surface) 90%, transparent);\n z-index: 100;\n}\n\n/* ========================================\n Empty State\n ======================================== */\n\n.empty-state {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n text-align: center;\n color: var(--dashboard-empty-color, var(--mj-text-secondary));\n max-width: 400px;\n padding: 40px;\n}\n\n.empty-state .empty-icon {\n width: 80px;\n height: 80px;\n border-radius: 50%;\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto 24px;\n}\n\n.empty-state .empty-icon i {\n font-size: 36px;\n color: var(--mj-brand-primary);\n}\n\n.empty-state h3 {\n margin: 0 0 12px 0;\n font-size: 20px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.empty-state p {\n margin: 0 0 24px 0;\n font-size: 14px;\n color: var(--dashboard-empty-text, var(--mj-text-secondary));\n line-height: 1.5;\n}\n\n.add-part-button {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 12px 24px;\n border: none;\n border-radius: 6px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n.add-part-button:hover {\n transform: translateY(-1px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 40%, transparent);\n}\n\n.add-part-button i {\n font-size: 14px;\n}\n\n/* ========================================\n Panel Error State\n ======================================== */\n\n:host ::ng-deep .panel-error {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n padding: 24px;\n text-align: center;\n color: var(--dashboard-error-color, var(--mj-status-error));\n background: var(--dashboard-error-bg, color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface)));\n}\n\n:host ::ng-deep .panel-error i {\n font-size: 32px;\n margin-bottom: 12px;\n}\n\n:host ::ng-deep .panel-error span {\n font-size: 14px;\n}\n\n/* ========================================\n Golden Layout Overrides\n These require ViewEncapsulation.None to work on dynamic GL elements\n ======================================== */\n\n/* Override shell's hide-tab-bar rule that hides GL headers when dashboard is inside shell */\n/* The shell hides .lm_header when mj-tab-container has hide-tab-bar class */\n/* We need to ensure OUR Golden Layout headers are always visible */\nmj-dashboard-viewer .lm_header {\n display: block !important;\n}\n\n/* Ensure GL root container fills available space */\nmj-dashboard-viewer .lm_goldenlayout {\n width: 100% !important;\n height: 100% !important;\n}\n\n/* Ensure the root layout item fills space */\nmj-dashboard-viewer .lm_root {\n width: 100% !important;\n height: 100% !important;\n}\n\n/* Ensure proper box-sizing for all GL layout elements */\nmj-dashboard-viewer .lm_item,\nmj-dashboard-viewer .lm_content,\nmj-dashboard-viewer .lm_stack,\nmj-dashboard-viewer .lm_row,\nmj-dashboard-viewer .lm_column {\n box-sizing: border-box !important;\n}\n\n/* Ensure layout items don't overflow */\nmj-dashboard-viewer .lm_item {\n overflow: hidden !important;\n}\n\n/* Fix for .lm_items - the content container inside stacks */\nmj-dashboard-viewer .lm_items {\n width: 100% !important;\n height: calc(100% - 38px) !important; /* Account for header height (38px) */\n box-sizing: border-box !important;\n position: relative !important;\n}\n\n/* When tabs are maximized, no header visible */\nmj-dashboard-viewer .lm_stack.lm_maximised > .lm_items {\n height: 100% !important;\n}\n\n/* Target the ComponentItem div inside lm_items */\nmj-dashboard-viewer .lm_items > div {\n width: 100% !important;\n height: 100% !important;\n box-sizing: border-box !important;\n flex-direction: column !important;\n}\n\n/* Only apply flex display to the active/visible tab content div */\nmj-dashboard-viewer .lm_items > div:not([style*=\"display: none\"]) {\n display: flex !important;\n}\n\n/* Clearfix for float-based row layout */\nmj-dashboard-viewer .lm_row::after {\n content: \"\" !important;\n display: table !important;\n clear: both !important;\n}\n\n/* Force content children to respect parent bounds */\nmj-dashboard-viewer .lm_content > * {\n max-width: 100% !important;\n width: 100% !important;\n}\n\nmj-dashboard-viewer .lm_content {\n background: var(--mj-bg-surface) !important;\n display: flex !important;\n flex-direction: column !important;\n height: 100% !important;\n width: 100% !important;\n}\n\nmj-dashboard-viewer .lm_item_container {\n background: var(--mj-bg-surface) !important;\n}\n\n/* Tab header styling */\nmj-dashboard-viewer .lm_header {\n height: 38px !important;\n padding-top: 2px !important;\n padding-left: 4px !important;\n background: var(--mj-bg-surface-card) !important;\n border-bottom: 1px solid var(--mj-border-default) !important;\n overflow: visible !important;\n box-sizing: border-box !important;\n}\n\nmj-dashboard-viewer .lm_tabs {\n height: 36px !important;\n}\n\n/* Hide Golden Layout window controls (popout, maximize) */\nmj-dashboard-viewer .lm_controls {\n display: none !important;\n}\n\n/* Tab styling */\nmj-dashboard-viewer .lm_header .lm_tab {\n padding: 0 16px 0 28px !important; /* Extra left padding for icon */\n font-size: 13px !important;\n height: 35px !important;\n line-height: 35px !important;\n box-sizing: border-box !important;\n cursor: pointer !important;\n user-select: none !important;\n background: transparent !important;\n border: none !important;\n border-bottom: 1px solid var(--mj-border-default) !important;\n transition: all 0.15s ease !important;\n position: relative !important;\n z-index: 1 !important;\n margin-right: 1px !important;\n}\n\nmj-dashboard-viewer .lm_header .lm_tab:hover {\n background: var(--mj-bg-surface-sunken) !important;\n}\n\nmj-dashboard-viewer .lm_header .lm_tab.lm_active {\n background: var(--mj-bg-surface) !important;\n height: 36px !important;\n margin-bottom: -1px !important;\n margin-right: 0 !important;\n border: 1px solid var(--mj-border-default) !important;\n border-bottom-color: var(--mj-bg-surface) !important;\n border-radius: 4px 4px 0 0 !important;\n z-index: 2 !important;\n}\n\nmj-dashboard-viewer .lm_title {\n cursor: pointer !important;\n user-select: none !important;\n}\n\n/* Panel icon styling - icon is direct child of .lm_tab to preserve GL's drag/drop */\n/* Positioned absolutely in the left padding area so it doesn't affect GL's title structure */\nmj-dashboard-viewer .lm_header .lm_tab .panel-icon {\n position: absolute !important;\n left: 10px !important;\n top: 50% !important;\n transform: translateY(-30%) !important;\n font-family: \"Font Awesome 6 Free\", \"Font Awesome 6 Pro\", \"Font Awesome 5 Free\", \"Font Awesome 5 Pro\", FontAwesome !important;\n font-weight: 900 !important; /* Required for solid icons */\n font-size: 12px !important;\n color: var(--mj-brand-primary) !important;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\nmj-dashboard-viewer .lm_close_tab {\n position: absolute !important;\n right: 4px !important;\n top: 50% !important;\n transform: translateY(-50%) !important;\n width: 16px !important;\n height: 16px !important;\n cursor: pointer !important;\n opacity: 0 !important;\n transition: all 0.15s ease !important;\n}\n\nmj-dashboard-viewer .lm_header .lm_tab:hover .lm_close_tab {\n opacity: 0.7 !important;\n}\n\nmj-dashboard-viewer .lm_close_tab:hover {\n opacity: 1 !important;\n color: var(--mj-status-error) !important;\n}\n\n/* Add padding for close button when editing */\nmj-dashboard-viewer .dashboard-viewer.editing .lm_header .lm_tab {\n padding-right: 24px !important;\n}\n\n/* ========================================\n View Mode (Not Editing) - Lock Layout\n ======================================== */\n\n/* Hide close buttons on tabs when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_close_tab {\n display: none !important;\n}\n\n/* Remove extra padding for close button when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_header .lm_tab {\n padding-right: 16px !important;\n}\n\n/* Disable splitter dragging when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_splitter {\n pointer-events: none !important;\n cursor: default !important;\n}\n\n/* Hide splitter drag handle visual when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_splitter .lm_drag_handle {\n display: none !important;\n}\n\n/* Disable tab dragging cursor when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_header .lm_tab {\n cursor: default !important;\n}\n\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_title {\n cursor: default !important;\n}\n"], encapsulation: 2 });
1254
+ } }, dependencies: [i1.LoadingComponent, i2.MJEmptyStateComponent, i3.DashboardBreadcrumbComponent], styles: ["/**\n * Dashboard Viewer Component Styles\n *\n * IMPORTANT: For Golden Layout tabs to display correctly, you must import\n * Golden Layout's CSS in your application's global styles:\n *\n * @import 'golden-layout/dist/css/goldenlayout-base.css';\n *\n * Or add to your angular.json styles array:\n * \"node_modules/golden-layout/dist/css/goldenlayout-base.css\"\n */\n\n:host {\n display: block;\n width: 100%;\n height: 100%;\n position: relative;\n}\n\n.dashboard-viewer {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n background: var(--dashboard-bg, var(--mj-bg-surface-card));\n}\n\n.dashboard-viewer.editing .layout-container {\n outline: 2px dashed var(--dashboard-edit-outline, var(--mj-brand-primary));\n outline-offset: -2px;\n}\n\n/* ========================================\n Toolbar\n ======================================== */\n\n.dashboard-toolbar {\n flex: 0 0 48px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 16px;\n background: var(--dashboard-toolbar-bg, var(--mj-bg-surface));\n border-bottom: 1px solid var(--dashboard-border, var(--mj-border-default));\n box-sizing: border-box;\n z-index: 10;\n}\n\n.toolbar-left,\n.toolbar-center,\n.toolbar-right {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.toolbar-left {\n flex: 1;\n}\n\n.toolbar-center {\n flex: 0 0 auto;\n}\n\n.toolbar-right {\n flex: 1;\n justify-content: flex-end;\n}\n\n.dashboard-title {\n font-size: 16px;\n font-weight: 500;\n color: var(--dashboard-title-color, var(--mj-text-primary));\n}\n\n.dashboard-title i {\n margin-right: 8px;\n color: var(--dashboard-title-icon, var(--mj-brand-primary));\n}\n\n.unsaved-indicator {\n font-size: 12px;\n color: var(--dashboard-unsaved-color, var(--mj-status-warning));\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.unsaved-indicator i {\n font-size: 8px;\n}\n\n.toolbar-button {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border: 1px solid var(--dashboard-button-border, var(--mj-border-strong));\n border-radius: 4px;\n background: var(--dashboard-button-bg, var(--mj-bg-surface));\n color: var(--dashboard-button-color, var(--mj-text-primary));\n font-size: 13px;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.toolbar-button:hover {\n background: var(--dashboard-button-hover-bg, var(--mj-bg-surface-card));\n border-color: var(--dashboard-button-hover-border, var(--mj-border-strong));\n}\n\n.toolbar-button.active {\n background: var(--dashboard-button-active-bg, color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface)));\n border-color: var(--dashboard-button-active-border, var(--mj-brand-primary));\n color: var(--dashboard-button-active-color, var(--mj-brand-primary));\n}\n\n.toolbar-button.primary {\n background: var(--dashboard-button-primary-bg, var(--mj-brand-primary));\n border-color: var(--dashboard-button-primary-border, var(--mj-brand-primary-hover));\n color: var(--dashboard-button-primary-color, var(--mj-text-inverse));\n}\n\n.toolbar-button.primary:hover {\n background: var(--dashboard-button-primary-hover-bg, var(--mj-brand-primary-hover));\n}\n\n.toolbar-button i {\n font-size: 14px;\n}\n\n/* ========================================\n Layout Container\n ======================================== */\n\n/* Layout container fills remaining space in the flex column */\n.layout-container {\n flex: 1;\n position: relative;\n overflow: hidden;\n min-height: 0;\n}\n\n/* ========================================\n Dashboard Panel Content (our wrapper)\n ======================================== */\n\n/* Dashboard panel wrapper (header + content) */\n:host ::ng-deep .dashboard-part-wrapper {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface);\n}\n\n/* Dashboard part header */\n:host ::ng-deep .dashboard-part-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 12px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n min-height: 40px;\n flex-shrink: 0;\n}\n\n/* Dashboard part content */\n:host ::ng-deep .dashboard-part-content {\n flex: 1;\n overflow: auto;\n min-height: 0;\n}\n\n/* ========================================\n Loading Overlay\n ======================================== */\n\n.loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--mj-bg-surface) 90%, transparent);\n z-index: 100;\n}\n\n/* ========================================\n Empty State\n ======================================== */\n\n/* Empty state centered in the dashboard pane (mj-empty-state centers its own\n content; absolute positioning keeps it overlaid on the layout container). */\n.dashboard-empty {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n max-width: 400px;\n}\n\n/* ========================================\n Panel Error State\n ======================================== */\n\n:host ::ng-deep .panel-error {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n padding: 24px;\n text-align: center;\n color: var(--dashboard-error-color, var(--mj-status-error));\n background: var(--dashboard-error-bg, color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface)));\n}\n\n:host ::ng-deep .panel-error i {\n font-size: 32px;\n margin-bottom: 12px;\n}\n\n:host ::ng-deep .panel-error span {\n font-size: 14px;\n}\n\n/* ========================================\n Golden Layout Overrides\n These require ViewEncapsulation.None to work on dynamic GL elements\n ======================================== */\n\n/* Override shell's hide-tab-bar rule that hides GL headers when dashboard is inside shell */\n/* The shell hides .lm_header when mj-tab-container has hide-tab-bar class */\n/* We need to ensure OUR Golden Layout headers are always visible */\nmj-dashboard-viewer .lm_header {\n display: block !important;\n}\n\n/* Ensure GL root container fills available space */\nmj-dashboard-viewer .lm_goldenlayout {\n width: 100% !important;\n height: 100% !important;\n}\n\n/* Ensure the root layout item fills space */\nmj-dashboard-viewer .lm_root {\n width: 100% !important;\n height: 100% !important;\n}\n\n/* Ensure proper box-sizing for all GL layout elements */\nmj-dashboard-viewer .lm_item,\nmj-dashboard-viewer .lm_content,\nmj-dashboard-viewer .lm_stack,\nmj-dashboard-viewer .lm_row,\nmj-dashboard-viewer .lm_column {\n box-sizing: border-box !important;\n}\n\n/* Ensure layout items don't overflow */\nmj-dashboard-viewer .lm_item {\n overflow: hidden !important;\n}\n\n/* Fix for .lm_items - the content container inside stacks */\nmj-dashboard-viewer .lm_items {\n width: 100% !important;\n height: calc(100% - 38px) !important; /* Account for header height (38px) */\n box-sizing: border-box !important;\n position: relative !important;\n}\n\n/* When tabs are maximized, no header visible */\nmj-dashboard-viewer .lm_stack.lm_maximised > .lm_items {\n height: 100% !important;\n}\n\n/* Target the ComponentItem div inside lm_items */\nmj-dashboard-viewer .lm_items > div {\n width: 100% !important;\n height: 100% !important;\n box-sizing: border-box !important;\n flex-direction: column !important;\n}\n\n/* Only apply flex display to the active/visible tab content div */\nmj-dashboard-viewer .lm_items > div:not([style*=\"display: none\"]) {\n display: flex !important;\n}\n\n/* Clearfix for float-based row layout */\nmj-dashboard-viewer .lm_row::after {\n content: \"\" !important;\n display: table !important;\n clear: both !important;\n}\n\n/* Force content children to respect parent bounds */\nmj-dashboard-viewer .lm_content > * {\n max-width: 100% !important;\n width: 100% !important;\n}\n\nmj-dashboard-viewer .lm_content {\n background: var(--mj-bg-surface) !important;\n display: flex !important;\n flex-direction: column !important;\n height: 100% !important;\n width: 100% !important;\n}\n\nmj-dashboard-viewer .lm_item_container {\n background: var(--mj-bg-surface) !important;\n}\n\n/* Tab header styling */\nmj-dashboard-viewer .lm_header {\n height: 38px !important;\n padding-top: 2px !important;\n padding-left: 4px !important;\n background: var(--mj-bg-surface-card) !important;\n border-bottom: 1px solid var(--mj-border-default) !important;\n overflow: visible !important;\n box-sizing: border-box !important;\n}\n\nmj-dashboard-viewer .lm_tabs {\n height: 36px !important;\n}\n\n/* Hide Golden Layout window controls (popout, maximize) */\nmj-dashboard-viewer .lm_controls {\n display: none !important;\n}\n\n/* Tab styling */\nmj-dashboard-viewer .lm_header .lm_tab {\n padding: 0 16px 0 28px !important; /* Extra left padding for icon */\n font-size: 13px !important;\n height: 35px !important;\n line-height: 35px !important;\n box-sizing: border-box !important;\n cursor: pointer !important;\n user-select: none !important;\n background: transparent !important;\n border: none !important;\n border-bottom: 1px solid var(--mj-border-default) !important;\n transition: all 0.15s ease !important;\n position: relative !important;\n z-index: 1 !important;\n margin-right: 1px !important;\n}\n\nmj-dashboard-viewer .lm_header .lm_tab:hover {\n background: var(--mj-bg-surface-sunken) !important;\n}\n\nmj-dashboard-viewer .lm_header .lm_tab.lm_active {\n background: var(--mj-bg-surface) !important;\n height: 36px !important;\n margin-bottom: -1px !important;\n margin-right: 0 !important;\n border: 1px solid var(--mj-border-default) !important;\n border-bottom-color: var(--mj-bg-surface) !important;\n border-radius: 4px 4px 0 0 !important;\n z-index: 2 !important;\n}\n\nmj-dashboard-viewer .lm_title {\n cursor: pointer !important;\n user-select: none !important;\n}\n\n/* Panel icon styling - icon is direct child of .lm_tab to preserve GL's drag/drop */\n/* Positioned absolutely in the left padding area so it doesn't affect GL's title structure */\nmj-dashboard-viewer .lm_header .lm_tab .panel-icon {\n position: absolute !important;\n left: 10px !important;\n top: 50% !important;\n transform: translateY(-30%) !important;\n font-family: \"Font Awesome 6 Free\", \"Font Awesome 6 Pro\", \"Font Awesome 5 Free\", \"Font Awesome 5 Pro\", FontAwesome !important;\n font-weight: 900 !important; /* Required for solid icons */\n font-size: 12px !important;\n color: var(--mj-brand-primary) !important;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\nmj-dashboard-viewer .lm_close_tab {\n position: absolute !important;\n right: 4px !important;\n top: 50% !important;\n transform: translateY(-50%) !important;\n width: 16px !important;\n height: 16px !important;\n cursor: pointer !important;\n opacity: 0 !important;\n transition: all 0.15s ease !important;\n}\n\nmj-dashboard-viewer .lm_header .lm_tab:hover .lm_close_tab {\n opacity: 0.7 !important;\n}\n\nmj-dashboard-viewer .lm_close_tab:hover {\n opacity: 1 !important;\n color: var(--mj-status-error) !important;\n}\n\n/* Add padding for close button when editing */\nmj-dashboard-viewer .dashboard-viewer.editing .lm_header .lm_tab {\n padding-right: 24px !important;\n}\n\n/* ========================================\n View Mode (Not Editing) - Lock Layout\n ======================================== */\n\n/* Hide close buttons on tabs when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_close_tab {\n display: none !important;\n}\n\n/* Remove extra padding for close button when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_header .lm_tab {\n padding-right: 16px !important;\n}\n\n/* Disable splitter dragging when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_splitter {\n pointer-events: none !important;\n cursor: default !important;\n}\n\n/* Hide splitter drag handle visual when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_splitter .lm_drag_handle {\n display: none !important;\n}\n\n/* Disable tab dragging cursor when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_header .lm_tab {\n cursor: default !important;\n}\n\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_title {\n cursor: default !important;\n}\n"], encapsulation: 2 });
1081
1255
  }
1082
1256
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(DashboardViewerComponent, [{
1083
1257
  type: Component,
1084
- args: [{ standalone: false, selector: 'mj-dashboard-viewer', encapsulation: ViewEncapsulation.None, template: "<!-- Dashboard Viewer Component -->\n<div class=\"dashboard-viewer\" [class.editing]=\"isEditing\" [class.has-toolbar]=\"shouldShowToolbar\">\n <!-- Breadcrumb Navigation (hidden in edit mode) -->\n @if (showBreadcrumb && !isEditing && dashboard) {\n <mj-dashboard-breadcrumb\n [Categories]=\"Categories\"\n [CurrentCategoryId]=\"dashboard.CategoryID\"\n [CurrentDashboard]=\"dashboard\"\n [ShowDashboardName]=\"true\"\n [AllowDragDrop]=\"false\"\n Size=\"large\"\n RootIcon=\"fa-solid fa-gauge-high\"\n RootLabel=\"Dashboards\"\n (Navigate)=\"onBreadcrumbNavigate($event)\">\n </mj-dashboard-breadcrumb>\n }\n\n <!-- Toolbar (auto-hides when all elements are disabled) -->\n @if (shouldShowToolbar) {\n <div class=\"dashboard-toolbar\">\n <div class=\"toolbar-left\">\n @if (dashboard && (isEditing || !showBreadcrumb)) {\n <span class=\"dashboard-title\">\n <i class=\"fa-solid fa-chart-line\"></i>\n {{ dashboard.Name }}\n </span>\n }\n </div>\n <div class=\"toolbar-center\">\n @if (hasUnsavedChanges && isEditing) {\n <span class=\"unsaved-indicator\">\n <i class=\"fa-solid fa-circle\"></i>\n Unsaved changes\n </span>\n }\n </div>\n <div class=\"toolbar-right\">\n <!-- Open in New Tab button (when embedded) -->\n @if (showOpenInTabButton && !isEditing) {\n <button\n class=\"toolbar-button\"\n (click)=\"onOpenInTabClick()\"\n title=\"Open in its own tab\">\n <i class=\"fa-solid fa-up-right-from-square\"></i>\n Open in Tab\n </button>\n }\n @if (isEditing) {\n <button\n class=\"toolbar-button\"\n (click)=\"onAddPanelClick()\">\n <i class=\"fa-solid fa-plus\"></i>\n Add Part\n </button>\n }\n @if (showEditButton) {\n <button\n class=\"toolbar-button\"\n [class.active]=\"isEditing\"\n (click)=\"toggleEditMode()\">\n <i class=\"fa-solid fa-edit\"></i>\n {{ isEditing ? 'Editing' : 'Edit' }}\n </button>\n }\n @if (isEditing && hasUnsavedChanges) {\n <button\n class=\"toolbar-button primary\"\n (click)=\"save()\">\n <i class=\"fa-solid fa-save\"></i>\n Save\n </button>\n }\n </div>\n </div>\n }\n\n <!-- Loading Overlay -->\n @if (isLoading) {\n <div class=\"loading-overlay\">\n <mj-loading text=\"Loading dashboard...\"></mj-loading>\n </div>\n }\n\n <!-- Layout Container -->\n <div class=\"layout-container\" #layoutContainer>\n <!-- Golden Layout will render panels here -->\n </div>\n\n <!-- Empty State -->\n @if (!hasPanels && !isLoading) {\n <div class=\"empty-state\">\n <div class=\"empty-icon\">\n <i class=\"fa-solid fa-layer-group\"></i>\n </div>\n <h3>No parts configured</h3>\n <p>This dashboard has no parts yet. Add your first part to start visualizing your data.</p>\n @if (isEditing) {\n <button class=\"add-part-button\" (click)=\"onAddPanelClick()\">\n <i class=\"fa-solid fa-plus\"></i>\n Add Your First Part\n </button>\n }\n </div>\n }\n</div>\n", styles: ["/**\n * Dashboard Viewer Component Styles\n *\n * IMPORTANT: For Golden Layout tabs to display correctly, you must import\n * Golden Layout's CSS in your application's global styles:\n *\n * @import 'golden-layout/dist/css/goldenlayout-base.css';\n *\n * Or add to your angular.json styles array:\n * \"node_modules/golden-layout/dist/css/goldenlayout-base.css\"\n */\n\n:host {\n display: block;\n width: 100%;\n height: 100%;\n position: relative;\n}\n\n.dashboard-viewer {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n background: var(--dashboard-bg, var(--mj-bg-surface-card));\n}\n\n.dashboard-viewer.editing .layout-container {\n outline: 2px dashed var(--dashboard-edit-outline, var(--mj-brand-primary));\n outline-offset: -2px;\n}\n\n/* ========================================\n Toolbar\n ======================================== */\n\n.dashboard-toolbar {\n flex: 0 0 48px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 16px;\n background: var(--dashboard-toolbar-bg, var(--mj-bg-surface));\n border-bottom: 1px solid var(--dashboard-border, var(--mj-border-default));\n box-sizing: border-box;\n z-index: 10;\n}\n\n.toolbar-left,\n.toolbar-center,\n.toolbar-right {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.toolbar-left {\n flex: 1;\n}\n\n.toolbar-center {\n flex: 0 0 auto;\n}\n\n.toolbar-right {\n flex: 1;\n justify-content: flex-end;\n}\n\n.dashboard-title {\n font-size: 16px;\n font-weight: 500;\n color: var(--dashboard-title-color, var(--mj-text-primary));\n}\n\n.dashboard-title i {\n margin-right: 8px;\n color: var(--dashboard-title-icon, var(--mj-brand-primary));\n}\n\n.unsaved-indicator {\n font-size: 12px;\n color: var(--dashboard-unsaved-color, var(--mj-status-warning));\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.unsaved-indicator i {\n font-size: 8px;\n}\n\n.toolbar-button {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border: 1px solid var(--dashboard-button-border, var(--mj-border-strong));\n border-radius: 4px;\n background: var(--dashboard-button-bg, var(--mj-bg-surface));\n color: var(--dashboard-button-color, var(--mj-text-primary));\n font-size: 13px;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.toolbar-button:hover {\n background: var(--dashboard-button-hover-bg, var(--mj-bg-surface-card));\n border-color: var(--dashboard-button-hover-border, var(--mj-border-strong));\n}\n\n.toolbar-button.active {\n background: var(--dashboard-button-active-bg, color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface)));\n border-color: var(--dashboard-button-active-border, var(--mj-brand-primary));\n color: var(--dashboard-button-active-color, var(--mj-brand-primary));\n}\n\n.toolbar-button.primary {\n background: var(--dashboard-button-primary-bg, var(--mj-brand-primary));\n border-color: var(--dashboard-button-primary-border, var(--mj-brand-primary-hover));\n color: var(--dashboard-button-primary-color, var(--mj-text-inverse));\n}\n\n.toolbar-button.primary:hover {\n background: var(--dashboard-button-primary-hover-bg, var(--mj-brand-primary-hover));\n}\n\n.toolbar-button i {\n font-size: 14px;\n}\n\n/* ========================================\n Layout Container\n ======================================== */\n\n/* Layout container fills remaining space in the flex column */\n.layout-container {\n flex: 1;\n position: relative;\n overflow: hidden;\n min-height: 0;\n}\n\n/* ========================================\n Dashboard Panel Content (our wrapper)\n ======================================== */\n\n/* Dashboard panel wrapper (header + content) */\n:host ::ng-deep .dashboard-part-wrapper {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface);\n}\n\n/* Dashboard part header */\n:host ::ng-deep .dashboard-part-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 12px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n min-height: 40px;\n flex-shrink: 0;\n}\n\n/* Dashboard part content */\n:host ::ng-deep .dashboard-part-content {\n flex: 1;\n overflow: auto;\n min-height: 0;\n}\n\n/* ========================================\n Loading Overlay\n ======================================== */\n\n.loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--mj-bg-surface) 90%, transparent);\n z-index: 100;\n}\n\n/* ========================================\n Empty State\n ======================================== */\n\n.empty-state {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n text-align: center;\n color: var(--dashboard-empty-color, var(--mj-text-secondary));\n max-width: 400px;\n padding: 40px;\n}\n\n.empty-state .empty-icon {\n width: 80px;\n height: 80px;\n border-radius: 50%;\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto 24px;\n}\n\n.empty-state .empty-icon i {\n font-size: 36px;\n color: var(--mj-brand-primary);\n}\n\n.empty-state h3 {\n margin: 0 0 12px 0;\n font-size: 20px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.empty-state p {\n margin: 0 0 24px 0;\n font-size: 14px;\n color: var(--dashboard-empty-text, var(--mj-text-secondary));\n line-height: 1.5;\n}\n\n.add-part-button {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 12px 24px;\n border: none;\n border-radius: 6px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n.add-part-button:hover {\n transform: translateY(-1px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 40%, transparent);\n}\n\n.add-part-button i {\n font-size: 14px;\n}\n\n/* ========================================\n Panel Error State\n ======================================== */\n\n:host ::ng-deep .panel-error {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n padding: 24px;\n text-align: center;\n color: var(--dashboard-error-color, var(--mj-status-error));\n background: var(--dashboard-error-bg, color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface)));\n}\n\n:host ::ng-deep .panel-error i {\n font-size: 32px;\n margin-bottom: 12px;\n}\n\n:host ::ng-deep .panel-error span {\n font-size: 14px;\n}\n\n/* ========================================\n Golden Layout Overrides\n These require ViewEncapsulation.None to work on dynamic GL elements\n ======================================== */\n\n/* Override shell's hide-tab-bar rule that hides GL headers when dashboard is inside shell */\n/* The shell hides .lm_header when mj-tab-container has hide-tab-bar class */\n/* We need to ensure OUR Golden Layout headers are always visible */\nmj-dashboard-viewer .lm_header {\n display: block !important;\n}\n\n/* Ensure GL root container fills available space */\nmj-dashboard-viewer .lm_goldenlayout {\n width: 100% !important;\n height: 100% !important;\n}\n\n/* Ensure the root layout item fills space */\nmj-dashboard-viewer .lm_root {\n width: 100% !important;\n height: 100% !important;\n}\n\n/* Ensure proper box-sizing for all GL layout elements */\nmj-dashboard-viewer .lm_item,\nmj-dashboard-viewer .lm_content,\nmj-dashboard-viewer .lm_stack,\nmj-dashboard-viewer .lm_row,\nmj-dashboard-viewer .lm_column {\n box-sizing: border-box !important;\n}\n\n/* Ensure layout items don't overflow */\nmj-dashboard-viewer .lm_item {\n overflow: hidden !important;\n}\n\n/* Fix for .lm_items - the content container inside stacks */\nmj-dashboard-viewer .lm_items {\n width: 100% !important;\n height: calc(100% - 38px) !important; /* Account for header height (38px) */\n box-sizing: border-box !important;\n position: relative !important;\n}\n\n/* When tabs are maximized, no header visible */\nmj-dashboard-viewer .lm_stack.lm_maximised > .lm_items {\n height: 100% !important;\n}\n\n/* Target the ComponentItem div inside lm_items */\nmj-dashboard-viewer .lm_items > div {\n width: 100% !important;\n height: 100% !important;\n box-sizing: border-box !important;\n flex-direction: column !important;\n}\n\n/* Only apply flex display to the active/visible tab content div */\nmj-dashboard-viewer .lm_items > div:not([style*=\"display: none\"]) {\n display: flex !important;\n}\n\n/* Clearfix for float-based row layout */\nmj-dashboard-viewer .lm_row::after {\n content: \"\" !important;\n display: table !important;\n clear: both !important;\n}\n\n/* Force content children to respect parent bounds */\nmj-dashboard-viewer .lm_content > * {\n max-width: 100% !important;\n width: 100% !important;\n}\n\nmj-dashboard-viewer .lm_content {\n background: var(--mj-bg-surface) !important;\n display: flex !important;\n flex-direction: column !important;\n height: 100% !important;\n width: 100% !important;\n}\n\nmj-dashboard-viewer .lm_item_container {\n background: var(--mj-bg-surface) !important;\n}\n\n/* Tab header styling */\nmj-dashboard-viewer .lm_header {\n height: 38px !important;\n padding-top: 2px !important;\n padding-left: 4px !important;\n background: var(--mj-bg-surface-card) !important;\n border-bottom: 1px solid var(--mj-border-default) !important;\n overflow: visible !important;\n box-sizing: border-box !important;\n}\n\nmj-dashboard-viewer .lm_tabs {\n height: 36px !important;\n}\n\n/* Hide Golden Layout window controls (popout, maximize) */\nmj-dashboard-viewer .lm_controls {\n display: none !important;\n}\n\n/* Tab styling */\nmj-dashboard-viewer .lm_header .lm_tab {\n padding: 0 16px 0 28px !important; /* Extra left padding for icon */\n font-size: 13px !important;\n height: 35px !important;\n line-height: 35px !important;\n box-sizing: border-box !important;\n cursor: pointer !important;\n user-select: none !important;\n background: transparent !important;\n border: none !important;\n border-bottom: 1px solid var(--mj-border-default) !important;\n transition: all 0.15s ease !important;\n position: relative !important;\n z-index: 1 !important;\n margin-right: 1px !important;\n}\n\nmj-dashboard-viewer .lm_header .lm_tab:hover {\n background: var(--mj-bg-surface-sunken) !important;\n}\n\nmj-dashboard-viewer .lm_header .lm_tab.lm_active {\n background: var(--mj-bg-surface) !important;\n height: 36px !important;\n margin-bottom: -1px !important;\n margin-right: 0 !important;\n border: 1px solid var(--mj-border-default) !important;\n border-bottom-color: var(--mj-bg-surface) !important;\n border-radius: 4px 4px 0 0 !important;\n z-index: 2 !important;\n}\n\nmj-dashboard-viewer .lm_title {\n cursor: pointer !important;\n user-select: none !important;\n}\n\n/* Panel icon styling - icon is direct child of .lm_tab to preserve GL's drag/drop */\n/* Positioned absolutely in the left padding area so it doesn't affect GL's title structure */\nmj-dashboard-viewer .lm_header .lm_tab .panel-icon {\n position: absolute !important;\n left: 10px !important;\n top: 50% !important;\n transform: translateY(-30%) !important;\n font-family: \"Font Awesome 6 Free\", \"Font Awesome 6 Pro\", \"Font Awesome 5 Free\", \"Font Awesome 5 Pro\", FontAwesome !important;\n font-weight: 900 !important; /* Required for solid icons */\n font-size: 12px !important;\n color: var(--mj-brand-primary) !important;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\nmj-dashboard-viewer .lm_close_tab {\n position: absolute !important;\n right: 4px !important;\n top: 50% !important;\n transform: translateY(-50%) !important;\n width: 16px !important;\n height: 16px !important;\n cursor: pointer !important;\n opacity: 0 !important;\n transition: all 0.15s ease !important;\n}\n\nmj-dashboard-viewer .lm_header .lm_tab:hover .lm_close_tab {\n opacity: 0.7 !important;\n}\n\nmj-dashboard-viewer .lm_close_tab:hover {\n opacity: 1 !important;\n color: var(--mj-status-error) !important;\n}\n\n/* Add padding for close button when editing */\nmj-dashboard-viewer .dashboard-viewer.editing .lm_header .lm_tab {\n padding-right: 24px !important;\n}\n\n/* ========================================\n View Mode (Not Editing) - Lock Layout\n ======================================== */\n\n/* Hide close buttons on tabs when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_close_tab {\n display: none !important;\n}\n\n/* Remove extra padding for close button when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_header .lm_tab {\n padding-right: 16px !important;\n}\n\n/* Disable splitter dragging when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_splitter {\n pointer-events: none !important;\n cursor: default !important;\n}\n\n/* Hide splitter drag handle visual when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_splitter .lm_drag_handle {\n display: none !important;\n}\n\n/* Disable tab dragging cursor when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_header .lm_tab {\n cursor: default !important;\n}\n\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_title {\n cursor: default !important;\n}\n"] }]
1258
+ args: [{ standalone: false, selector: 'mj-dashboard-viewer', encapsulation: ViewEncapsulation.None, template: "<!-- Dashboard Viewer Component -->\n<div class=\"dashboard-viewer\" [class.editing]=\"isEditing\" [class.has-toolbar]=\"shouldShowToolbar\">\n <!-- Breadcrumb Navigation (hidden in edit mode) -->\n @if (showBreadcrumb && !isEditing && dashboard) {\n <mj-dashboard-breadcrumb\n [Categories]=\"Categories\"\n [CurrentCategoryId]=\"dashboard.CategoryID\"\n [CurrentDashboard]=\"dashboard\"\n [ShowDashboardName]=\"true\"\n [AllowDragDrop]=\"false\"\n Size=\"large\"\n RootIcon=\"fa-solid fa-gauge-high\"\n RootLabel=\"Dashboards\"\n (Navigate)=\"onBreadcrumbNavigate($event)\">\n </mj-dashboard-breadcrumb>\n }\n\n <!-- Toolbar (auto-hides when all elements are disabled) -->\n @if (shouldShowToolbar) {\n <div class=\"dashboard-toolbar\">\n <div class=\"toolbar-left\">\n @if (dashboard && (isEditing || !showBreadcrumb)) {\n <span class=\"dashboard-title\">\n <i class=\"fa-solid fa-chart-line\"></i>\n {{ dashboard.Name }}\n </span>\n }\n </div>\n <div class=\"toolbar-center\">\n @if (hasUnsavedChanges && isEditing) {\n <span class=\"unsaved-indicator\">\n <i class=\"fa-solid fa-circle\"></i>\n Unsaved changes\n </span>\n }\n </div>\n <div class=\"toolbar-right\">\n <!-- Open in New Tab button (when embedded) -->\n @if (showOpenInTabButton && !isEditing) {\n <button\n class=\"toolbar-button\"\n (click)=\"onOpenInTabClick()\"\n title=\"Open in its own tab\">\n <i class=\"fa-solid fa-up-right-from-square\"></i>\n Open in Tab\n </button>\n }\n @if (isEditing) {\n <button\n class=\"toolbar-button\"\n (click)=\"onAddPanelClick()\">\n <i class=\"fa-solid fa-plus\"></i>\n Add Part\n </button>\n }\n @if (showEditButton) {\n <button\n class=\"toolbar-button\"\n [class.active]=\"isEditing\"\n (click)=\"toggleEditMode()\">\n <i class=\"fa-solid fa-edit\"></i>\n {{ isEditing ? 'Editing' : 'Edit' }}\n </button>\n }\n @if (isEditing && hasUnsavedChanges) {\n <button\n class=\"toolbar-button primary\"\n (click)=\"save()\">\n <i class=\"fa-solid fa-save\"></i>\n Save\n </button>\n }\n </div>\n </div>\n }\n\n <!-- Loading Overlay -->\n @if (isLoading) {\n <div class=\"loading-overlay\">\n <mj-loading text=\"Loading dashboard...\"></mj-loading>\n </div>\n }\n\n <!-- Layout Container -->\n <div class=\"layout-container\" #layoutContainer>\n <!-- Golden Layout will render panels here -->\n </div>\n\n <!-- Empty State -->\n @if (!hasPanels && !isLoading) {\n <mj-empty-state\n class=\"dashboard-empty\"\n Icon=\"fa-solid fa-layer-group\"\n Title=\"No parts configured\"\n Message=\"This dashboard has no parts yet. Add your first part to start visualizing your data.\"\n [ActionText]=\"isEditing ? 'Add Your First Part' : ''\"\n ActionIcon=\"fa-solid fa-plus\"\n (Action)=\"onAddPanelClick()\" />\n }\n</div>\n", styles: ["/**\n * Dashboard Viewer Component Styles\n *\n * IMPORTANT: For Golden Layout tabs to display correctly, you must import\n * Golden Layout's CSS in your application's global styles:\n *\n * @import 'golden-layout/dist/css/goldenlayout-base.css';\n *\n * Or add to your angular.json styles array:\n * \"node_modules/golden-layout/dist/css/goldenlayout-base.css\"\n */\n\n:host {\n display: block;\n width: 100%;\n height: 100%;\n position: relative;\n}\n\n.dashboard-viewer {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n background: var(--dashboard-bg, var(--mj-bg-surface-card));\n}\n\n.dashboard-viewer.editing .layout-container {\n outline: 2px dashed var(--dashboard-edit-outline, var(--mj-brand-primary));\n outline-offset: -2px;\n}\n\n/* ========================================\n Toolbar\n ======================================== */\n\n.dashboard-toolbar {\n flex: 0 0 48px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 16px;\n background: var(--dashboard-toolbar-bg, var(--mj-bg-surface));\n border-bottom: 1px solid var(--dashboard-border, var(--mj-border-default));\n box-sizing: border-box;\n z-index: 10;\n}\n\n.toolbar-left,\n.toolbar-center,\n.toolbar-right {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.toolbar-left {\n flex: 1;\n}\n\n.toolbar-center {\n flex: 0 0 auto;\n}\n\n.toolbar-right {\n flex: 1;\n justify-content: flex-end;\n}\n\n.dashboard-title {\n font-size: 16px;\n font-weight: 500;\n color: var(--dashboard-title-color, var(--mj-text-primary));\n}\n\n.dashboard-title i {\n margin-right: 8px;\n color: var(--dashboard-title-icon, var(--mj-brand-primary));\n}\n\n.unsaved-indicator {\n font-size: 12px;\n color: var(--dashboard-unsaved-color, var(--mj-status-warning));\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.unsaved-indicator i {\n font-size: 8px;\n}\n\n.toolbar-button {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border: 1px solid var(--dashboard-button-border, var(--mj-border-strong));\n border-radius: 4px;\n background: var(--dashboard-button-bg, var(--mj-bg-surface));\n color: var(--dashboard-button-color, var(--mj-text-primary));\n font-size: 13px;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.toolbar-button:hover {\n background: var(--dashboard-button-hover-bg, var(--mj-bg-surface-card));\n border-color: var(--dashboard-button-hover-border, var(--mj-border-strong));\n}\n\n.toolbar-button.active {\n background: var(--dashboard-button-active-bg, color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface)));\n border-color: var(--dashboard-button-active-border, var(--mj-brand-primary));\n color: var(--dashboard-button-active-color, var(--mj-brand-primary));\n}\n\n.toolbar-button.primary {\n background: var(--dashboard-button-primary-bg, var(--mj-brand-primary));\n border-color: var(--dashboard-button-primary-border, var(--mj-brand-primary-hover));\n color: var(--dashboard-button-primary-color, var(--mj-text-inverse));\n}\n\n.toolbar-button.primary:hover {\n background: var(--dashboard-button-primary-hover-bg, var(--mj-brand-primary-hover));\n}\n\n.toolbar-button i {\n font-size: 14px;\n}\n\n/* ========================================\n Layout Container\n ======================================== */\n\n/* Layout container fills remaining space in the flex column */\n.layout-container {\n flex: 1;\n position: relative;\n overflow: hidden;\n min-height: 0;\n}\n\n/* ========================================\n Dashboard Panel Content (our wrapper)\n ======================================== */\n\n/* Dashboard panel wrapper (header + content) */\n:host ::ng-deep .dashboard-part-wrapper {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface);\n}\n\n/* Dashboard part header */\n:host ::ng-deep .dashboard-part-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 12px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n min-height: 40px;\n flex-shrink: 0;\n}\n\n/* Dashboard part content */\n:host ::ng-deep .dashboard-part-content {\n flex: 1;\n overflow: auto;\n min-height: 0;\n}\n\n/* ========================================\n Loading Overlay\n ======================================== */\n\n.loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--mj-bg-surface) 90%, transparent);\n z-index: 100;\n}\n\n/* ========================================\n Empty State\n ======================================== */\n\n/* Empty state centered in the dashboard pane (mj-empty-state centers its own\n content; absolute positioning keeps it overlaid on the layout container). */\n.dashboard-empty {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n max-width: 400px;\n}\n\n/* ========================================\n Panel Error State\n ======================================== */\n\n:host ::ng-deep .panel-error {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n padding: 24px;\n text-align: center;\n color: var(--dashboard-error-color, var(--mj-status-error));\n background: var(--dashboard-error-bg, color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface)));\n}\n\n:host ::ng-deep .panel-error i {\n font-size: 32px;\n margin-bottom: 12px;\n}\n\n:host ::ng-deep .panel-error span {\n font-size: 14px;\n}\n\n/* ========================================\n Golden Layout Overrides\n These require ViewEncapsulation.None to work on dynamic GL elements\n ======================================== */\n\n/* Override shell's hide-tab-bar rule that hides GL headers when dashboard is inside shell */\n/* The shell hides .lm_header when mj-tab-container has hide-tab-bar class */\n/* We need to ensure OUR Golden Layout headers are always visible */\nmj-dashboard-viewer .lm_header {\n display: block !important;\n}\n\n/* Ensure GL root container fills available space */\nmj-dashboard-viewer .lm_goldenlayout {\n width: 100% !important;\n height: 100% !important;\n}\n\n/* Ensure the root layout item fills space */\nmj-dashboard-viewer .lm_root {\n width: 100% !important;\n height: 100% !important;\n}\n\n/* Ensure proper box-sizing for all GL layout elements */\nmj-dashboard-viewer .lm_item,\nmj-dashboard-viewer .lm_content,\nmj-dashboard-viewer .lm_stack,\nmj-dashboard-viewer .lm_row,\nmj-dashboard-viewer .lm_column {\n box-sizing: border-box !important;\n}\n\n/* Ensure layout items don't overflow */\nmj-dashboard-viewer .lm_item {\n overflow: hidden !important;\n}\n\n/* Fix for .lm_items - the content container inside stacks */\nmj-dashboard-viewer .lm_items {\n width: 100% !important;\n height: calc(100% - 38px) !important; /* Account for header height (38px) */\n box-sizing: border-box !important;\n position: relative !important;\n}\n\n/* When tabs are maximized, no header visible */\nmj-dashboard-viewer .lm_stack.lm_maximised > .lm_items {\n height: 100% !important;\n}\n\n/* Target the ComponentItem div inside lm_items */\nmj-dashboard-viewer .lm_items > div {\n width: 100% !important;\n height: 100% !important;\n box-sizing: border-box !important;\n flex-direction: column !important;\n}\n\n/* Only apply flex display to the active/visible tab content div */\nmj-dashboard-viewer .lm_items > div:not([style*=\"display: none\"]) {\n display: flex !important;\n}\n\n/* Clearfix for float-based row layout */\nmj-dashboard-viewer .lm_row::after {\n content: \"\" !important;\n display: table !important;\n clear: both !important;\n}\n\n/* Force content children to respect parent bounds */\nmj-dashboard-viewer .lm_content > * {\n max-width: 100% !important;\n width: 100% !important;\n}\n\nmj-dashboard-viewer .lm_content {\n background: var(--mj-bg-surface) !important;\n display: flex !important;\n flex-direction: column !important;\n height: 100% !important;\n width: 100% !important;\n}\n\nmj-dashboard-viewer .lm_item_container {\n background: var(--mj-bg-surface) !important;\n}\n\n/* Tab header styling */\nmj-dashboard-viewer .lm_header {\n height: 38px !important;\n padding-top: 2px !important;\n padding-left: 4px !important;\n background: var(--mj-bg-surface-card) !important;\n border-bottom: 1px solid var(--mj-border-default) !important;\n overflow: visible !important;\n box-sizing: border-box !important;\n}\n\nmj-dashboard-viewer .lm_tabs {\n height: 36px !important;\n}\n\n/* Hide Golden Layout window controls (popout, maximize) */\nmj-dashboard-viewer .lm_controls {\n display: none !important;\n}\n\n/* Tab styling */\nmj-dashboard-viewer .lm_header .lm_tab {\n padding: 0 16px 0 28px !important; /* Extra left padding for icon */\n font-size: 13px !important;\n height: 35px !important;\n line-height: 35px !important;\n box-sizing: border-box !important;\n cursor: pointer !important;\n user-select: none !important;\n background: transparent !important;\n border: none !important;\n border-bottom: 1px solid var(--mj-border-default) !important;\n transition: all 0.15s ease !important;\n position: relative !important;\n z-index: 1 !important;\n margin-right: 1px !important;\n}\n\nmj-dashboard-viewer .lm_header .lm_tab:hover {\n background: var(--mj-bg-surface-sunken) !important;\n}\n\nmj-dashboard-viewer .lm_header .lm_tab.lm_active {\n background: var(--mj-bg-surface) !important;\n height: 36px !important;\n margin-bottom: -1px !important;\n margin-right: 0 !important;\n border: 1px solid var(--mj-border-default) !important;\n border-bottom-color: var(--mj-bg-surface) !important;\n border-radius: 4px 4px 0 0 !important;\n z-index: 2 !important;\n}\n\nmj-dashboard-viewer .lm_title {\n cursor: pointer !important;\n user-select: none !important;\n}\n\n/* Panel icon styling - icon is direct child of .lm_tab to preserve GL's drag/drop */\n/* Positioned absolutely in the left padding area so it doesn't affect GL's title structure */\nmj-dashboard-viewer .lm_header .lm_tab .panel-icon {\n position: absolute !important;\n left: 10px !important;\n top: 50% !important;\n transform: translateY(-30%) !important;\n font-family: \"Font Awesome 6 Free\", \"Font Awesome 6 Pro\", \"Font Awesome 5 Free\", \"Font Awesome 5 Pro\", FontAwesome !important;\n font-weight: 900 !important; /* Required for solid icons */\n font-size: 12px !important;\n color: var(--mj-brand-primary) !important;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\nmj-dashboard-viewer .lm_close_tab {\n position: absolute !important;\n right: 4px !important;\n top: 50% !important;\n transform: translateY(-50%) !important;\n width: 16px !important;\n height: 16px !important;\n cursor: pointer !important;\n opacity: 0 !important;\n transition: all 0.15s ease !important;\n}\n\nmj-dashboard-viewer .lm_header .lm_tab:hover .lm_close_tab {\n opacity: 0.7 !important;\n}\n\nmj-dashboard-viewer .lm_close_tab:hover {\n opacity: 1 !important;\n color: var(--mj-status-error) !important;\n}\n\n/* Add padding for close button when editing */\nmj-dashboard-viewer .dashboard-viewer.editing .lm_header .lm_tab {\n padding-right: 24px !important;\n}\n\n/* ========================================\n View Mode (Not Editing) - Lock Layout\n ======================================== */\n\n/* Hide close buttons on tabs when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_close_tab {\n display: none !important;\n}\n\n/* Remove extra padding for close button when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_header .lm_tab {\n padding-right: 16px !important;\n}\n\n/* Disable splitter dragging when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_splitter {\n pointer-events: none !important;\n cursor: default !important;\n}\n\n/* Hide splitter drag handle visual when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_splitter .lm_drag_handle {\n display: none !important;\n}\n\n/* Disable tab dragging cursor when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_header .lm_tab {\n cursor: default !important;\n}\n\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_title {\n cursor: default !important;\n}\n"] }]
1085
1259
  }], () => [{ type: i0.ChangeDetectorRef }, { type: i0.ApplicationRef }, { type: i0.Injector }, { type: i0.EnvironmentInjector }], { dashboard: [{
1086
1260
  type: Input
1087
1261
  }], dashboardId: [{
@@ -1116,9 +1290,15 @@ export class DashboardViewerComponent extends BaseAngularComponent {
1116
1290
  type: Output
1117
1291
  }], openInTab: [{
1118
1292
  type: Output
1293
+ }], layoutLifecycle: [{
1294
+ type: Output
1295
+ }], layoutReady: [{
1296
+ type: Output
1297
+ }], layoutDeferred: [{
1298
+ type: Output
1119
1299
  }], layoutContainer: [{
1120
1300
  type: ViewChild,
1121
1301
  args: ['layoutContainer', { static: true }]
1122
1302
  }] }); })();
1123
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(DashboardViewerComponent, { className: "DashboardViewerComponent", filePath: "src/lib/dashboard-viewer/dashboard-viewer.component.ts", lineNumber: 56 }); })();
1303
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(DashboardViewerComponent, { className: "DashboardViewerComponent", filePath: "src/lib/dashboard-viewer/dashboard-viewer.component.ts", lineNumber: 73 }); })();
1124
1304
  //# sourceMappingURL=dashboard-viewer.component.js.map