@memberjunction/ng-dashboard-viewer 6.1.4 → 6.2.0-edge.1
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.
- package/dist/lib/config-dialogs/confirm-dialog.component.d.ts +58 -8
- package/dist/lib/config-dialogs/confirm-dialog.component.d.ts.map +1 -1
- package/dist/lib/config-dialogs/confirm-dialog.component.js +119 -17
- package/dist/lib/config-dialogs/confirm-dialog.component.js.map +1 -1
- package/dist/lib/config-panels/artifact-config-panel.component.d.ts +63 -11
- package/dist/lib/config-panels/artifact-config-panel.component.d.ts.map +1 -1
- package/dist/lib/config-panels/artifact-config-panel.component.js +152 -48
- package/dist/lib/config-panels/artifact-config-panel.component.js.map +1 -1
- package/dist/lib/config-panels/query-config-panel.component.d.ts +57 -9
- package/dist/lib/config-panels/query-config-panel.component.d.ts.map +1 -1
- package/dist/lib/config-panels/query-config-panel.component.js +142 -46
- package/dist/lib/config-panels/query-config-panel.component.js.map +1 -1
- package/dist/lib/config-panels/view-config-panel.component.d.ts +69 -11
- package/dist/lib/config-panels/view-config-panel.component.d.ts.map +1 -1
- package/dist/lib/config-panels/view-config-panel.component.js +165 -49
- package/dist/lib/config-panels/view-config-panel.component.js.map +1 -1
- package/dist/lib/config-panels/weburl-config-panel.component.d.ts +42 -6
- package/dist/lib/config-panels/weburl-config-panel.component.d.ts.map +1 -1
- package/dist/lib/config-panels/weburl-config-panel.component.js +113 -41
- package/dist/lib/config-panels/weburl-config-panel.component.js.map +1 -1
- package/dist/lib/dashboard-browser/dashboard-browser.component.d.ts.map +1 -1
- package/dist/lib/dashboard-browser/dashboard-browser.component.js +7 -4
- package/dist/lib/dashboard-browser/dashboard-browser.component.js.map +1 -1
- package/dist/lib/dashboard-viewer/dashboard-viewer.component.d.ts +163 -12
- package/dist/lib/dashboard-viewer/dashboard-viewer.component.d.ts.map +1 -1
- package/dist/lib/dashboard-viewer/dashboard-viewer.component.js +332 -78
- package/dist/lib/dashboard-viewer/dashboard-viewer.component.js.map +1 -1
- package/dist/lib/dialogs/add-panel-dialog/add-panel-dialog.component.d.ts +63 -9
- package/dist/lib/dialogs/add-panel-dialog/add-panel-dialog.component.d.ts.map +1 -1
- package/dist/lib/dialogs/add-panel-dialog/add-panel-dialog.component.js +172 -66
- package/dist/lib/dialogs/add-panel-dialog/add-panel-dialog.component.js.map +1 -1
- package/dist/lib/dialogs/edit-part-dialog/edit-part-dialog.component.d.ts +11 -1
- package/dist/lib/dialogs/edit-part-dialog/edit-part-dialog.component.d.ts.map +1 -1
- package/dist/lib/dialogs/edit-part-dialog/edit-part-dialog.component.js +30 -10
- package/dist/lib/dialogs/edit-part-dialog/edit-part-dialog.component.js.map +1 -1
- package/dist/lib/models/dashboard-types.d.ts +8 -0
- package/dist/lib/models/dashboard-types.d.ts.map +1 -1
- package/dist/lib/models/dashboard-types.js +21 -5
- package/dist/lib/models/dashboard-types.js.map +1 -1
- package/dist/lib/parts/artifact-part.component.d.ts +63 -7
- package/dist/lib/parts/artifact-part.component.d.ts.map +1 -1
- package/dist/lib/parts/artifact-part.component.js +98 -22
- package/dist/lib/parts/artifact-part.component.js.map +1 -1
- package/dist/lib/parts/query-part.component.d.ts +26 -4
- package/dist/lib/parts/query-part.component.d.ts.map +1 -1
- package/dist/lib/parts/query-part.component.js +62 -18
- package/dist/lib/parts/query-part.component.js.map +1 -1
- package/dist/lib/parts/view-part.component.d.ts +21 -3
- package/dist/lib/parts/view-part.component.d.ts.map +1 -1
- package/dist/lib/parts/view-part.component.js +55 -19
- package/dist/lib/parts/view-part.component.js.map +1 -1
- package/dist/lib/parts/weburl-part.component.d.ts +29 -5
- package/dist/lib/parts/weburl-part.component.d.ts.map +1 -1
- package/dist/lib/parts/weburl-part.component.js +63 -15
- package/dist/lib/parts/weburl-part.component.js.map +1 -1
- package/dist/lib/services/golden-layout-wrapper.service.d.ts +65 -5
- package/dist/lib/services/golden-layout-wrapper.service.d.ts.map +1 -1
- package/dist/lib/services/golden-layout-wrapper.service.js +135 -27
- package/dist/lib/services/golden-layout-wrapper.service.js.map +1 -1
- package/package.json +13 -13
|
@@ -4,7 +4,7 @@ import { takeUntil } from 'rxjs/operators';
|
|
|
4
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
|
-
import {
|
|
7
|
+
import { CreateDefaultDashboardConfig, GeneratePanelId, ExtractPanelsFromLayout, FindPanelInLayout } from '../models/dashboard-types';
|
|
8
8
|
import { GoldenLayoutWrapperService } from '../services/golden-layout-wrapper.service';
|
|
9
9
|
import { BaseDashboardPart } from '../parts/base-dashboard-part';
|
|
10
10
|
import * as i0 from "@angular/core";
|
|
@@ -135,30 +135,46 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
135
135
|
_dashboard = null;
|
|
136
136
|
_dashboardId = null;
|
|
137
137
|
/** The dashboard entity to display */
|
|
138
|
-
set
|
|
138
|
+
set Dashboard(value) {
|
|
139
139
|
const previous = this._dashboard;
|
|
140
140
|
this._dashboard = value;
|
|
141
141
|
if (value && value !== previous) {
|
|
142
142
|
this.onDashboardChanged();
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
|
-
get
|
|
145
|
+
get Dashboard() {
|
|
146
146
|
return this._dashboard;
|
|
147
147
|
}
|
|
148
|
+
/** @deprecated Use {@link Dashboard}. */
|
|
149
|
+
get dashboard() {
|
|
150
|
+
return this.Dashboard;
|
|
151
|
+
}
|
|
152
|
+
/** @deprecated Use {@link Dashboard}. */
|
|
153
|
+
set dashboard(value) {
|
|
154
|
+
this.Dashboard = value;
|
|
155
|
+
}
|
|
148
156
|
/** Alternative: Load dashboard by ID */
|
|
149
|
-
set
|
|
157
|
+
set DashboardId(value) {
|
|
150
158
|
const previous = this._dashboardId;
|
|
151
159
|
this._dashboardId = value;
|
|
152
160
|
if (value && value !== previous) {
|
|
153
161
|
this.loadDashboardById(value);
|
|
154
162
|
}
|
|
155
163
|
}
|
|
156
|
-
get
|
|
164
|
+
get DashboardId() {
|
|
157
165
|
return this._dashboardId;
|
|
158
166
|
}
|
|
167
|
+
/** @deprecated Use {@link DashboardId}. */
|
|
168
|
+
get dashboardId() {
|
|
169
|
+
return this.DashboardId;
|
|
170
|
+
}
|
|
171
|
+
/** @deprecated Use {@link DashboardId}. */
|
|
172
|
+
set dashboardId(value) {
|
|
173
|
+
this.DashboardId = value;
|
|
174
|
+
}
|
|
159
175
|
/** Whether the dashboard is in edit mode */
|
|
160
176
|
_isEditing = false;
|
|
161
|
-
set
|
|
177
|
+
set IsEditing(value) {
|
|
162
178
|
const previous = this._isEditing;
|
|
163
179
|
this._isEditing = value;
|
|
164
180
|
// When isEditing changes (and layout exists), reinitialize to apply GL settings
|
|
@@ -167,43 +183,95 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
167
183
|
this.updatePanelEditModes();
|
|
168
184
|
}
|
|
169
185
|
}
|
|
170
|
-
get
|
|
186
|
+
get IsEditing() {
|
|
171
187
|
return this._isEditing;
|
|
172
188
|
}
|
|
189
|
+
/** @deprecated Use {@link IsEditing}. */
|
|
190
|
+
get isEditing() {
|
|
191
|
+
return this.IsEditing;
|
|
192
|
+
}
|
|
193
|
+
/** @deprecated Use {@link IsEditing}. */
|
|
194
|
+
set isEditing(value) {
|
|
195
|
+
this.IsEditing = value;
|
|
196
|
+
}
|
|
173
197
|
/** Whether to show the toolbar */
|
|
174
198
|
_showToolbar = true;
|
|
175
|
-
set
|
|
199
|
+
set ShowToolbar(value) {
|
|
176
200
|
this._showToolbar = value;
|
|
177
201
|
}
|
|
178
|
-
get
|
|
202
|
+
get ShowToolbar() {
|
|
179
203
|
return this._showToolbar;
|
|
180
204
|
}
|
|
205
|
+
/** @deprecated Use {@link ShowToolbar}. */
|
|
206
|
+
get showToolbar() {
|
|
207
|
+
return this.ShowToolbar;
|
|
208
|
+
}
|
|
209
|
+
/** @deprecated Use {@link ShowToolbar}. */
|
|
210
|
+
set showToolbar(value) {
|
|
211
|
+
this.ShowToolbar = value;
|
|
212
|
+
}
|
|
181
213
|
/** Whether to auto-save layout changes */
|
|
182
214
|
_autoSave = false;
|
|
183
|
-
set
|
|
215
|
+
set AutoSave(value) {
|
|
184
216
|
this._autoSave = value;
|
|
185
217
|
}
|
|
186
|
-
get
|
|
218
|
+
get AutoSave() {
|
|
187
219
|
return this._autoSave;
|
|
188
220
|
}
|
|
221
|
+
/** @deprecated Use {@link AutoSave}. */
|
|
222
|
+
get autoSave() {
|
|
223
|
+
return this.AutoSave;
|
|
224
|
+
}
|
|
225
|
+
/** @deprecated Use {@link AutoSave}. */
|
|
226
|
+
set autoSave(value) {
|
|
227
|
+
this.AutoSave = value;
|
|
228
|
+
}
|
|
189
229
|
/** Whether to show the breadcrumb navigation */
|
|
190
|
-
|
|
230
|
+
ShowBreadcrumb = true;
|
|
231
|
+
/** @deprecated Use {@link ShowBreadcrumb}. */
|
|
232
|
+
set showBreadcrumb(value) {
|
|
233
|
+
this.ShowBreadcrumb = value;
|
|
234
|
+
}
|
|
235
|
+
/** @deprecated Use {@link ShowBreadcrumb}. */
|
|
236
|
+
get showBreadcrumb() {
|
|
237
|
+
return this.ShowBreadcrumb;
|
|
238
|
+
}
|
|
191
239
|
/** Whether to show the "Open in Tab" button (for embedded dashboards) */
|
|
192
|
-
|
|
240
|
+
ShowOpenInTabButton = false;
|
|
241
|
+
/** @deprecated Use {@link ShowOpenInTabButton}. */
|
|
242
|
+
set showOpenInTabButton(value) {
|
|
243
|
+
this.ShowOpenInTabButton = value;
|
|
244
|
+
}
|
|
245
|
+
/** @deprecated Use {@link ShowOpenInTabButton}. */
|
|
246
|
+
get showOpenInTabButton() {
|
|
247
|
+
return this.ShowOpenInTabButton;
|
|
248
|
+
}
|
|
193
249
|
/** Whether to show the Edit button */
|
|
194
|
-
|
|
250
|
+
ShowEditButton = true;
|
|
251
|
+
/** @deprecated Use {@link ShowEditButton}. */
|
|
252
|
+
set showEditButton(value) {
|
|
253
|
+
this.ShowEditButton = value;
|
|
254
|
+
}
|
|
255
|
+
/** @deprecated Use {@link ShowEditButton}. */
|
|
256
|
+
get showEditButton() {
|
|
257
|
+
return this.ShowEditButton;
|
|
258
|
+
}
|
|
195
259
|
/** All categories for breadcrumb path resolution */
|
|
196
260
|
Categories = [];
|
|
197
261
|
/**
|
|
198
262
|
* Computed: Should the toolbar be visible?
|
|
199
263
|
* Auto-hides when showToolbar=false OR when all toolbar elements are disabled
|
|
200
264
|
*/
|
|
201
|
-
get
|
|
265
|
+
get ShouldShowToolbar() {
|
|
202
266
|
if (!this._showToolbar) {
|
|
203
267
|
return false;
|
|
204
268
|
}
|
|
205
269
|
// If all elements are hidden, hide the toolbar entirely
|
|
206
|
-
return this.
|
|
270
|
+
return this.ShowBreadcrumb || this.ShowOpenInTabButton || this.ShowEditButton;
|
|
271
|
+
}
|
|
272
|
+
/** @deprecated Use {@link ShouldShowToolbar}. */
|
|
273
|
+
get shouldShowToolbar() {
|
|
274
|
+
return this.ShouldShowToolbar;
|
|
207
275
|
}
|
|
208
276
|
// ========================================
|
|
209
277
|
// Outputs
|
|
@@ -211,42 +279,142 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
211
279
|
/** Emitted when dashboard configuration changes */
|
|
212
280
|
configChanged = new EventEmitter();
|
|
213
281
|
/** Emitted when a panel requests navigation to another resource */
|
|
214
|
-
|
|
282
|
+
NavigationRequested = new EventEmitter();
|
|
283
|
+
/**
|
|
284
|
+
* @deprecated Use {@link NavigationRequested}.
|
|
285
|
+
*
|
|
286
|
+
* The same emitter under the old binding name, so a template still binding
|
|
287
|
+
* (navigationRequested) keeps working. Must stay AFTER NavigationRequested: class fields
|
|
288
|
+
* initialise in order, and the other way round this captures undefined.
|
|
289
|
+
*/
|
|
290
|
+
navigationRequested = this.NavigationRequested;
|
|
215
291
|
/** Emitted when a panel interaction occurs */
|
|
216
|
-
|
|
292
|
+
PanelInteraction = new EventEmitter();
|
|
293
|
+
/**
|
|
294
|
+
* @deprecated Use {@link PanelInteraction}.
|
|
295
|
+
*
|
|
296
|
+
* The same emitter under the old binding name, so a template still binding
|
|
297
|
+
* (panelInteraction) keeps working. Must stay AFTER PanelInteraction: class fields
|
|
298
|
+
* initialise in order, and the other way round this captures undefined.
|
|
299
|
+
*/
|
|
300
|
+
panelInteraction = this.PanelInteraction;
|
|
217
301
|
/** Emitted when the dashboard is saved */
|
|
218
|
-
|
|
302
|
+
DashboardSaved = new EventEmitter();
|
|
303
|
+
/**
|
|
304
|
+
* @deprecated Use {@link DashboardSaved}.
|
|
305
|
+
*
|
|
306
|
+
* The same emitter under the old binding name, so a template still binding
|
|
307
|
+
* (dashboardSaved) keeps working. Must stay AFTER DashboardSaved: class fields
|
|
308
|
+
* initialise in order, and the other way round this captures undefined.
|
|
309
|
+
*/
|
|
310
|
+
dashboardSaved = this.DashboardSaved;
|
|
219
311
|
/** Emitted when an error occurs */
|
|
220
312
|
error = new EventEmitter();
|
|
221
313
|
/** Emitted when edit mode changes */
|
|
222
|
-
|
|
314
|
+
EditModeChanged = new EventEmitter();
|
|
315
|
+
/**
|
|
316
|
+
* @deprecated Use {@link EditModeChanged}.
|
|
317
|
+
*
|
|
318
|
+
* The same emitter under the old binding name, so a template still binding
|
|
319
|
+
* (editModeChanged) keeps working. Must stay AFTER EditModeChanged: class fields
|
|
320
|
+
* initialise in order, and the other way round this captures undefined.
|
|
321
|
+
*/
|
|
322
|
+
editModeChanged = this.EditModeChanged;
|
|
223
323
|
/** Emitted when user navigates via breadcrumb */
|
|
224
|
-
|
|
324
|
+
BreadcrumbNavigate = new EventEmitter();
|
|
325
|
+
/**
|
|
326
|
+
* @deprecated Use {@link BreadcrumbNavigate}.
|
|
327
|
+
*
|
|
328
|
+
* The same emitter under the old binding name, so a template still binding
|
|
329
|
+
* (breadcrumbNavigate) keeps working. Must stay AFTER BreadcrumbNavigate: class fields
|
|
330
|
+
* initialise in order, and the other way round this captures undefined.
|
|
331
|
+
*/
|
|
332
|
+
breadcrumbNavigate = this.BreadcrumbNavigate;
|
|
225
333
|
/** Emitted when user clicks "Open in Tab" button */
|
|
226
|
-
|
|
334
|
+
OpenInTab = new EventEmitter();
|
|
335
|
+
/**
|
|
336
|
+
* @deprecated Use {@link OpenInTab}.
|
|
337
|
+
*
|
|
338
|
+
* The same emitter under the old binding name, so a template still binding
|
|
339
|
+
* (openInTab) keeps working. Must stay AFTER OpenInTab: class fields
|
|
340
|
+
* initialise in order, and the other way round this captures undefined.
|
|
341
|
+
*/
|
|
342
|
+
openInTab = this.OpenInTab;
|
|
227
343
|
/** Emitted as the Golden Layout viewer moves through dashboard/layout readiness states. */
|
|
228
|
-
|
|
344
|
+
LayoutLifecycle = new EventEmitter();
|
|
345
|
+
/**
|
|
346
|
+
* @deprecated Use {@link LayoutLifecycle}.
|
|
347
|
+
*
|
|
348
|
+
* The same emitter under the old binding name, so a template still binding
|
|
349
|
+
* (layoutLifecycle) keeps working. Must stay AFTER LayoutLifecycle: class fields
|
|
350
|
+
* initialise in order, and the other way round this captures undefined.
|
|
351
|
+
*/
|
|
352
|
+
layoutLifecycle = this.LayoutLifecycle;
|
|
229
353
|
/** Emitted once Golden Layout has initialized with a non-zero container and updated its size. */
|
|
230
|
-
|
|
354
|
+
LayoutReady = new EventEmitter();
|
|
355
|
+
/**
|
|
356
|
+
* @deprecated Use {@link LayoutReady}.
|
|
357
|
+
*
|
|
358
|
+
* The same emitter under the old binding name, so a template still binding
|
|
359
|
+
* (layoutReady) keeps working. Must stay AFTER LayoutReady: class fields
|
|
360
|
+
* initialise in order, and the other way round this captures undefined.
|
|
361
|
+
*/
|
|
362
|
+
layoutReady = this.LayoutReady;
|
|
231
363
|
/** Emitted when layout initialization is deferred because the container cannot be initialized yet. */
|
|
232
|
-
|
|
364
|
+
LayoutDeferred = new EventEmitter();
|
|
365
|
+
/**
|
|
366
|
+
* @deprecated Use {@link LayoutDeferred}.
|
|
367
|
+
*
|
|
368
|
+
* The same emitter under the old binding name, so a template still binding
|
|
369
|
+
* (layoutDeferred) keeps working. Must stay AFTER LayoutDeferred: class fields
|
|
370
|
+
* initialise in order, and the other way round this captures undefined.
|
|
371
|
+
*/
|
|
372
|
+
layoutDeferred = this.LayoutDeferred;
|
|
233
373
|
// ========================================
|
|
234
374
|
// View Children
|
|
235
375
|
// ========================================
|
|
236
|
-
|
|
376
|
+
LayoutContainer;
|
|
377
|
+
/** @deprecated Use {@link LayoutContainer}. */
|
|
378
|
+
get layoutContainer() {
|
|
379
|
+
return this.LayoutContainer;
|
|
380
|
+
}
|
|
381
|
+
/** @deprecated Use {@link LayoutContainer}. */
|
|
382
|
+
set layoutContainer(value) {
|
|
383
|
+
this.LayoutContainer = value;
|
|
384
|
+
}
|
|
237
385
|
// ========================================
|
|
238
386
|
// State
|
|
239
387
|
// ========================================
|
|
240
388
|
isLoading = false;
|
|
241
389
|
config = null;
|
|
242
|
-
|
|
243
|
-
|
|
390
|
+
PartTypes = [];
|
|
391
|
+
/** @deprecated Use {@link PartTypes}. */
|
|
392
|
+
get partTypes() {
|
|
393
|
+
return this.PartTypes;
|
|
394
|
+
}
|
|
395
|
+
/** @deprecated Use {@link PartTypes}. */
|
|
396
|
+
set partTypes(value) {
|
|
397
|
+
this.PartTypes = value;
|
|
398
|
+
}
|
|
399
|
+
HasUnsavedChanges = false;
|
|
400
|
+
/** @deprecated Use {@link HasUnsavedChanges}. */
|
|
401
|
+
get hasUnsavedChanges() {
|
|
402
|
+
return this.HasUnsavedChanges;
|
|
403
|
+
}
|
|
404
|
+
/** @deprecated Use {@link HasUnsavedChanges}. */
|
|
405
|
+
set hasUnsavedChanges(value) {
|
|
406
|
+
this.HasUnsavedChanges = value;
|
|
407
|
+
}
|
|
244
408
|
/**
|
|
245
409
|
* Helper to check if layout has any panels (for template use).
|
|
246
410
|
* Panels are stored in componentState within the layout tree.
|
|
247
411
|
*/
|
|
412
|
+
get HasPanels() {
|
|
413
|
+
return ExtractPanelsFromLayout(this.config?.layout ?? null).length > 0;
|
|
414
|
+
}
|
|
415
|
+
/** @deprecated Use {@link HasPanels}. */
|
|
248
416
|
get hasPanels() {
|
|
249
|
-
return
|
|
417
|
+
return this.HasPanels;
|
|
250
418
|
}
|
|
251
419
|
_destroy$ = new Subject();
|
|
252
420
|
_panelComponents = new Map();
|
|
@@ -292,26 +460,30 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
292
460
|
/**
|
|
293
461
|
* Toggle edit mode
|
|
294
462
|
*/
|
|
295
|
-
|
|
296
|
-
this.
|
|
297
|
-
this.
|
|
463
|
+
ToggleEditMode() {
|
|
464
|
+
this.IsEditing = !this.IsEditing;
|
|
465
|
+
this.EditModeChanged.emit(this.IsEditing);
|
|
298
466
|
this.updatePanelEditModes();
|
|
299
467
|
}
|
|
468
|
+
/** @deprecated Use {@link ToggleEditMode}. */
|
|
469
|
+
toggleEditMode() {
|
|
470
|
+
return this.ToggleEditMode();
|
|
471
|
+
}
|
|
300
472
|
/**
|
|
301
473
|
* Add a new panel to the dashboard.
|
|
302
474
|
* The panel is stored in GL's componentState - no separate panels array.
|
|
303
475
|
*/
|
|
304
|
-
async
|
|
476
|
+
async AddPanel(partTypeId, panelConfig, title, icon, location) {
|
|
305
477
|
if (!this.config || !this._glService) {
|
|
306
478
|
return;
|
|
307
479
|
}
|
|
308
|
-
const partType = this.
|
|
480
|
+
const partType = this.PartTypes.find(pt => UUIDsEqual(pt.ID, partTypeId));
|
|
309
481
|
if (!partType) {
|
|
310
482
|
this.error.emit({ message: `Unknown panel type: ${partTypeId}` });
|
|
311
483
|
return;
|
|
312
484
|
}
|
|
313
485
|
const panel = {
|
|
314
|
-
id:
|
|
486
|
+
id: GeneratePanelId(),
|
|
315
487
|
partTypeId,
|
|
316
488
|
title,
|
|
317
489
|
icon: icon || partType.Icon || 'fa-solid fa-window-maximize',
|
|
@@ -327,11 +499,15 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
327
499
|
}
|
|
328
500
|
this.markDirty();
|
|
329
501
|
}
|
|
502
|
+
/** @deprecated Use {@link AddPanel}. */
|
|
503
|
+
async addPanel(partTypeId, panelConfig, title, icon, location) {
|
|
504
|
+
return this.AddPanel(partTypeId, panelConfig, title, icon, location);
|
|
505
|
+
}
|
|
330
506
|
/**
|
|
331
507
|
* Remove a panel from the dashboard.
|
|
332
508
|
* Panels live in GL's componentState, so removing from GL removes the panel.
|
|
333
509
|
*/
|
|
334
|
-
|
|
510
|
+
RemovePanel(panelId) {
|
|
335
511
|
if (!this.config || !this._glService)
|
|
336
512
|
return;
|
|
337
513
|
// Remove from layout (panel data is in componentState)
|
|
@@ -345,6 +521,10 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
345
521
|
this.destroyPanelComponent(panelId);
|
|
346
522
|
this.markDirty();
|
|
347
523
|
}
|
|
524
|
+
/** @deprecated Use {@link RemovePanel}. */
|
|
525
|
+
removePanel(panelId) {
|
|
526
|
+
return this.RemovePanel(panelId);
|
|
527
|
+
}
|
|
348
528
|
/**
|
|
349
529
|
* Save the current dashboard configuration
|
|
350
530
|
*/
|
|
@@ -365,8 +545,8 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
365
545
|
this._dashboard.UIConfigDetails = JSON.stringify(this.config);
|
|
366
546
|
const saved = await this._dashboard.Save();
|
|
367
547
|
if (saved) {
|
|
368
|
-
this.
|
|
369
|
-
this.
|
|
548
|
+
this.HasUnsavedChanges = false;
|
|
549
|
+
this.DashboardSaved.emit(this._dashboard);
|
|
370
550
|
}
|
|
371
551
|
return saved;
|
|
372
552
|
}
|
|
@@ -383,19 +563,27 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
383
563
|
/**
|
|
384
564
|
* Refresh all panels
|
|
385
565
|
*/
|
|
386
|
-
async
|
|
566
|
+
async RefreshAllPanels() {
|
|
387
567
|
// For placeholder implementation, reinitialize the layout
|
|
388
568
|
if (this._glService) {
|
|
389
569
|
await this.initializeLayout();
|
|
390
570
|
}
|
|
391
571
|
}
|
|
572
|
+
/** @deprecated Use {@link RefreshAllPanels}. */
|
|
573
|
+
async refreshAllPanels() {
|
|
574
|
+
return this.RefreshAllPanels();
|
|
575
|
+
}
|
|
392
576
|
/**
|
|
393
577
|
* Resolves when the current dashboard's Golden Layout instance has initialized
|
|
394
578
|
* against a non-zero container and completed its first size update.
|
|
395
579
|
*/
|
|
396
|
-
|
|
580
|
+
WaitForLayoutReady() {
|
|
397
581
|
return this._layoutReadyPromise;
|
|
398
582
|
}
|
|
583
|
+
/** @deprecated Use {@link WaitForLayoutReady}. */
|
|
584
|
+
waitForLayoutReady() {
|
|
585
|
+
return this.WaitForLayoutReady();
|
|
586
|
+
}
|
|
399
587
|
/**
|
|
400
588
|
* Get the current configuration
|
|
401
589
|
*/
|
|
@@ -405,31 +593,43 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
405
593
|
/**
|
|
406
594
|
* Get available part types
|
|
407
595
|
*/
|
|
596
|
+
GetPartTypes() {
|
|
597
|
+
return this.PartTypes;
|
|
598
|
+
}
|
|
599
|
+
/** @deprecated Use {@link GetPartTypes}. */
|
|
408
600
|
getPartTypes() {
|
|
409
|
-
return this.
|
|
601
|
+
return this.GetPartTypes();
|
|
410
602
|
}
|
|
411
603
|
/**
|
|
412
604
|
* Get a panel by ID.
|
|
413
605
|
* Extracts panel from the layout's componentState (single source of truth).
|
|
414
606
|
*/
|
|
607
|
+
GetPanel(panelId) {
|
|
608
|
+
return FindPanelInLayout(this.config?.layout ?? null, panelId);
|
|
609
|
+
}
|
|
610
|
+
/** @deprecated Use {@link GetPanel}. */
|
|
415
611
|
getPanel(panelId) {
|
|
416
|
-
return
|
|
612
|
+
return this.GetPanel(panelId);
|
|
417
613
|
}
|
|
418
614
|
/**
|
|
419
615
|
* Get the part type for a panel
|
|
420
616
|
*/
|
|
421
|
-
|
|
422
|
-
const panel = this.
|
|
617
|
+
GetPartTypeForPanel(panelId) {
|
|
618
|
+
const panel = this.GetPanel(panelId);
|
|
423
619
|
if (!panel)
|
|
424
620
|
return null;
|
|
425
|
-
return this.
|
|
621
|
+
return this.PartTypes.find(pt => UUIDsEqual(pt.ID, panel.partTypeId)) ?? null;
|
|
622
|
+
}
|
|
623
|
+
/** @deprecated Use {@link GetPartTypeForPanel}. */
|
|
624
|
+
getPartTypeForPanel(panelId) {
|
|
625
|
+
return this.GetPartTypeForPanel(panelId);
|
|
426
626
|
}
|
|
427
627
|
/**
|
|
428
628
|
* Update a panel's configuration.
|
|
429
629
|
* Since panels live in componentState within the layout, we need to
|
|
430
630
|
* update the layout tree directly or reinitialize with updated data.
|
|
431
631
|
*/
|
|
432
|
-
|
|
632
|
+
UpdatePanelConfig(panelId, newConfig, title, icon) {
|
|
433
633
|
if (!this.config || !this._glService)
|
|
434
634
|
return;
|
|
435
635
|
// Get current layout which contains all panel data
|
|
@@ -443,6 +643,10 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
443
643
|
// Reinitialize layout to reflect the updated panel
|
|
444
644
|
this.initializeLayout();
|
|
445
645
|
}
|
|
646
|
+
/** @deprecated Use {@link UpdatePanelConfig}. */
|
|
647
|
+
updatePanelConfig(panelId, newConfig, title, icon) {
|
|
648
|
+
return this.UpdatePanelConfig(panelId, newConfig, title, icon);
|
|
649
|
+
}
|
|
446
650
|
/**
|
|
447
651
|
* Recursively update a panel's config within the layout tree
|
|
448
652
|
*/
|
|
@@ -478,31 +682,43 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
478
682
|
/**
|
|
479
683
|
* Handle add panel button click - emits event for parent to show dialog
|
|
480
684
|
*/
|
|
481
|
-
|
|
685
|
+
OnAddPanelClick() {
|
|
482
686
|
// Emit interaction event for parent to handle
|
|
483
687
|
// Parent should show AddPanelDialog and call addPanel() with result
|
|
484
|
-
this.
|
|
688
|
+
this.PanelInteraction.emit({
|
|
485
689
|
panelId: '',
|
|
486
690
|
interactionType: 'custom',
|
|
487
|
-
payload: { action: 'add-panel-requested', partTypes: this.
|
|
691
|
+
payload: { action: 'add-panel-requested', partTypes: this.PartTypes }
|
|
488
692
|
});
|
|
489
693
|
}
|
|
694
|
+
/** @deprecated Use {@link OnAddPanelClick}. */
|
|
695
|
+
onAddPanelClick() {
|
|
696
|
+
return this.OnAddPanelClick();
|
|
697
|
+
}
|
|
490
698
|
/**
|
|
491
699
|
* Handle "Open in Tab" button click - emits event for parent to open dashboard in its own tab
|
|
492
700
|
*/
|
|
493
|
-
|
|
701
|
+
OnOpenInTabClick() {
|
|
494
702
|
if (this._dashboard) {
|
|
495
|
-
this.
|
|
703
|
+
this.OpenInTab.emit({
|
|
496
704
|
dashboardId: this._dashboard.ID,
|
|
497
705
|
dashboardName: this._dashboard.Name
|
|
498
706
|
});
|
|
499
707
|
}
|
|
500
708
|
}
|
|
709
|
+
/** @deprecated Use {@link OnOpenInTabClick}. */
|
|
710
|
+
onOpenInTabClick() {
|
|
711
|
+
return this.OnOpenInTabClick();
|
|
712
|
+
}
|
|
501
713
|
/**
|
|
502
714
|
* Handle breadcrumb navigation
|
|
503
715
|
*/
|
|
716
|
+
OnBreadcrumbNavigate(event) {
|
|
717
|
+
this.BreadcrumbNavigate.emit(event);
|
|
718
|
+
}
|
|
719
|
+
/** @deprecated Use {@link OnBreadcrumbNavigate}. */
|
|
504
720
|
onBreadcrumbNavigate(event) {
|
|
505
|
-
this.
|
|
721
|
+
return this.OnBreadcrumbNavigate(event);
|
|
506
722
|
}
|
|
507
723
|
// ========================================
|
|
508
724
|
// Private Methods - Initialization
|
|
@@ -510,7 +726,7 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
510
726
|
async loadPartTypes() {
|
|
511
727
|
try {
|
|
512
728
|
await DashboardEngine.Instance.Config(false);
|
|
513
|
-
this.
|
|
729
|
+
this.PartTypes = DashboardEngine.Instance.DashboardPartTypes;
|
|
514
730
|
}
|
|
515
731
|
catch (err) {
|
|
516
732
|
console.error('Failed to load dashboard part types:', err);
|
|
@@ -568,7 +784,7 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
568
784
|
}
|
|
569
785
|
parseOrCreateConfig() {
|
|
570
786
|
if (!this._dashboard?.UIConfigDetails) {
|
|
571
|
-
return
|
|
787
|
+
return CreateDefaultDashboardConfig();
|
|
572
788
|
}
|
|
573
789
|
try {
|
|
574
790
|
const parsed = JSON.parse(this._dashboard.UIConfigDetails);
|
|
@@ -578,20 +794,20 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
578
794
|
}
|
|
579
795
|
// Invalid format, return default
|
|
580
796
|
console.warn('[DashboardViewer] Invalid config format, using default');
|
|
581
|
-
return
|
|
797
|
+
return CreateDefaultDashboardConfig();
|
|
582
798
|
}
|
|
583
799
|
catch {
|
|
584
|
-
return
|
|
800
|
+
return CreateDefaultDashboardConfig();
|
|
585
801
|
}
|
|
586
802
|
}
|
|
587
803
|
// ========================================
|
|
588
804
|
// Private Methods - Layout
|
|
589
805
|
// ========================================
|
|
590
806
|
async initializeLayout(generation = this._layoutInitGeneration) {
|
|
591
|
-
if (!this.config || !this.
|
|
807
|
+
if (!this.config || !this.LayoutContainer?.nativeElement) {
|
|
592
808
|
return;
|
|
593
809
|
}
|
|
594
|
-
const el = this.
|
|
810
|
+
const el = this.LayoutContainer.nativeElement;
|
|
595
811
|
const rect = el.getBoundingClientRect();
|
|
596
812
|
if (rect.width === 0 || rect.height === 0) {
|
|
597
813
|
// The container has no size yet — typically because Explorer is reattaching
|
|
@@ -602,7 +818,7 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
602
818
|
// Defer init until the container actually has a size, then build the layout.
|
|
603
819
|
const reason = 'layout container has zero size';
|
|
604
820
|
this.emitLayoutLifecycle('waiting-for-size', reason);
|
|
605
|
-
this.
|
|
821
|
+
this.LayoutDeferred.emit({ reason });
|
|
606
822
|
await this.waitForLayoutContainerSize(el, generation);
|
|
607
823
|
if (generation !== this._layoutInitGeneration) {
|
|
608
824
|
return;
|
|
@@ -629,7 +845,7 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
629
845
|
// Initialize with saved layout (or null for empty dashboard)
|
|
630
846
|
// Golden Layout's native ResolvedLayoutConfig is the source of truth
|
|
631
847
|
// Panel data is embedded in each component's componentState
|
|
632
|
-
this._glService.initialize(this.
|
|
848
|
+
this._glService.initialize(this.LayoutContainer.nativeElement, this.config.layout, panelFactory, this.IsEditing);
|
|
633
849
|
if (pendingPanelCreations.length > 0) {
|
|
634
850
|
await Promise.all(pendingPanelCreations);
|
|
635
851
|
}
|
|
@@ -651,7 +867,7 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
651
867
|
this._glService?.updateSize();
|
|
652
868
|
this.cdr.detectChanges();
|
|
653
869
|
this.emitLayoutLifecycle('ready');
|
|
654
|
-
this.
|
|
870
|
+
this.LayoutReady.emit();
|
|
655
871
|
this.resolveLayoutReady(generation);
|
|
656
872
|
}
|
|
657
873
|
/** ResizeObserver used to wait for a zero-size container to gain a size before init. */
|
|
@@ -758,7 +974,7 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
758
974
|
}
|
|
759
975
|
}
|
|
760
976
|
emitLayoutLifecycle(state, reason, error) {
|
|
761
|
-
this.
|
|
977
|
+
this.LayoutLifecycle.emit({
|
|
762
978
|
state,
|
|
763
979
|
dashboardId: this._dashboard?.ID,
|
|
764
980
|
reason,
|
|
@@ -840,13 +1056,13 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
840
1056
|
* Panel comes directly from GL's componentState - no lookup needed.
|
|
841
1057
|
*/
|
|
842
1058
|
async createPanelComponent(panel, container) {
|
|
843
|
-
const partType = this.
|
|
1059
|
+
const partType = this.PartTypes.find(pt => UUIDsEqual(pt.ID, panel.partTypeId));
|
|
844
1060
|
// Create the panel wrapper with header and content
|
|
845
1061
|
const wrapper = document.createElement('div');
|
|
846
1062
|
wrapper.className = 'dashboard-part-wrapper';
|
|
847
1063
|
wrapper.style.cssText = 'display: flex; flex-direction: column; height: 100%; background: var(--mj-bg-surface);';
|
|
848
1064
|
// Only show header in edit mode - GL tabs already display the title in view mode
|
|
849
|
-
if (this.
|
|
1065
|
+
if (this.IsEditing) {
|
|
850
1066
|
const header = this.createPartHeader(panel, panel.id);
|
|
851
1067
|
wrapper.appendChild(header);
|
|
852
1068
|
}
|
|
@@ -890,7 +1106,7 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
890
1106
|
const instance = componentRef.instance;
|
|
891
1107
|
instance.Panel = panel;
|
|
892
1108
|
instance.PartType = partType;
|
|
893
|
-
instance.IsEditing = this.
|
|
1109
|
+
instance.IsEditing = this.IsEditing;
|
|
894
1110
|
// Subscribe to events
|
|
895
1111
|
instance.ConfigureRequested.subscribe(() => {
|
|
896
1112
|
this.onConfigurePart(panel.id);
|
|
@@ -899,7 +1115,7 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
899
1115
|
this.onRemovePart(panel.id);
|
|
900
1116
|
});
|
|
901
1117
|
instance.NavigationRequested.subscribe((event) => {
|
|
902
|
-
this.
|
|
1118
|
+
this.NavigationRequested.emit(event);
|
|
903
1119
|
});
|
|
904
1120
|
// Attach component to DOM
|
|
905
1121
|
container.appendChild(componentRef.location.nativeElement);
|
|
@@ -933,7 +1149,7 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
933
1149
|
`;
|
|
934
1150
|
header.appendChild(titleSection);
|
|
935
1151
|
// Action buttons (only in edit mode)
|
|
936
|
-
if (this.
|
|
1152
|
+
if (this.IsEditing) {
|
|
937
1153
|
const actions = document.createElement('div');
|
|
938
1154
|
actions.style.cssText = 'display: flex; gap: 4px;';
|
|
939
1155
|
// Configure button
|
|
@@ -1164,7 +1380,7 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
1164
1380
|
}
|
|
1165
1381
|
onConfigurePart(panelId) {
|
|
1166
1382
|
// Emit event for parent to handle configuration
|
|
1167
|
-
this.
|
|
1383
|
+
this.PanelInteraction.emit({
|
|
1168
1384
|
panelId,
|
|
1169
1385
|
interactionType: 'custom',
|
|
1170
1386
|
payload: { action: 'configure-part-requested' }
|
|
@@ -1172,8 +1388,8 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
1172
1388
|
}
|
|
1173
1389
|
onRemovePart(panelId) {
|
|
1174
1390
|
// Emit event for parent to show confirmation dialog
|
|
1175
|
-
const panel =
|
|
1176
|
-
this.
|
|
1391
|
+
const panel = FindPanelInLayout(this.config?.layout ?? null, panelId);
|
|
1392
|
+
this.PanelInteraction.emit({
|
|
1177
1393
|
panelId,
|
|
1178
1394
|
interactionType: 'custom',
|
|
1179
1395
|
payload: {
|
|
@@ -1185,8 +1401,12 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
1185
1401
|
/**
|
|
1186
1402
|
* Confirm removal of a panel (called by parent after confirmation dialog)
|
|
1187
1403
|
*/
|
|
1404
|
+
ConfirmRemovePanel(panelId) {
|
|
1405
|
+
this.RemovePanel(panelId);
|
|
1406
|
+
}
|
|
1407
|
+
/** @deprecated Use {@link ConfirmRemovePanel}. */
|
|
1188
1408
|
confirmRemovePanel(panelId) {
|
|
1189
|
-
this.
|
|
1409
|
+
return this.ConfirmRemovePanel(panelId);
|
|
1190
1410
|
}
|
|
1191
1411
|
destroyPanelComponent(panelId) {
|
|
1192
1412
|
const entry = this._panelComponents.get(panelId);
|
|
@@ -1203,7 +1423,7 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
1203
1423
|
// Update IsEditing on all dynamic components
|
|
1204
1424
|
this._panelComponents.forEach((entry) => {
|
|
1205
1425
|
if (entry.componentRef) {
|
|
1206
|
-
entry.componentRef.instance.IsEditing = this.
|
|
1426
|
+
entry.componentRef.instance.IsEditing = this.IsEditing;
|
|
1207
1427
|
}
|
|
1208
1428
|
});
|
|
1209
1429
|
// Save current layout before reinitializing (preserves user's arrangement)
|
|
@@ -1222,8 +1442,8 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
1222
1442
|
// Private Methods - State
|
|
1223
1443
|
// ========================================
|
|
1224
1444
|
markDirty() {
|
|
1225
|
-
this.
|
|
1226
|
-
if (this.
|
|
1445
|
+
this.HasUnsavedChanges = true;
|
|
1446
|
+
if (this.AutoSave && this.config) {
|
|
1227
1447
|
this.save();
|
|
1228
1448
|
}
|
|
1229
1449
|
}
|
|
@@ -1232,8 +1452,8 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
1232
1452
|
i0.ɵɵviewQuery(_c0, 7);
|
|
1233
1453
|
} if (rf & 2) {
|
|
1234
1454
|
let _t;
|
|
1235
|
-
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.
|
|
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) {
|
|
1455
|
+
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.LayoutContainer = _t.first);
|
|
1456
|
+
} }, inputs: { Dashboard: "Dashboard", dashboard: "dashboard", DashboardId: "DashboardId", dashboardId: "dashboardId", IsEditing: "IsEditing", isEditing: "isEditing", ShowToolbar: "ShowToolbar", showToolbar: "showToolbar", AutoSave: "AutoSave", autoSave: "autoSave", ShowBreadcrumb: "ShowBreadcrumb", showBreadcrumb: "showBreadcrumb", ShowOpenInTabButton: "ShowOpenInTabButton", showOpenInTabButton: "showOpenInTabButton", ShowEditButton: "ShowEditButton", showEditButton: "showEditButton", Categories: "Categories" }, outputs: { configChanged: "configChanged", NavigationRequested: "NavigationRequested", navigationRequested: "navigationRequested", PanelInteraction: "PanelInteraction", panelInteraction: "panelInteraction", DashboardSaved: "DashboardSaved", dashboardSaved: "dashboardSaved", error: "error", EditModeChanged: "EditModeChanged", editModeChanged: "editModeChanged", BreadcrumbNavigate: "BreadcrumbNavigate", breadcrumbNavigate: "breadcrumbNavigate", OpenInTab: "OpenInTab", openInTab: "openInTab", LayoutLifecycle: "LayoutLifecycle", layoutLifecycle: "layoutLifecycle", LayoutReady: "LayoutReady", layoutReady: "layoutReady", LayoutDeferred: "LayoutDeferred", 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) {
|
|
1237
1457
|
i0.ɵɵelementStart(0, "div", 1);
|
|
1238
1458
|
i0.ɵɵconditionalCreate(1, DashboardViewerComponent_Conditional_1_Template, 1, 5, "mj-dashboard-breadcrumb", 2);
|
|
1239
1459
|
i0.ɵɵconditionalCreate(2, DashboardViewerComponent_Conditional_2_Template, 10, 6, "div", 3);
|
|
@@ -1256,47 +1476,81 @@ export class DashboardViewerComponent extends BaseAngularComponent {
|
|
|
1256
1476
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(DashboardViewerComponent, [{
|
|
1257
1477
|
type: Component,
|
|
1258
1478
|
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"] }]
|
|
1259
|
-
}], () => [{ type: i0.ChangeDetectorRef }, { type: i0.ApplicationRef }, { type: i0.Injector }, { type: i0.EnvironmentInjector }], {
|
|
1479
|
+
}], () => [{ type: i0.ChangeDetectorRef }, { type: i0.ApplicationRef }, { type: i0.Injector }, { type: i0.EnvironmentInjector }], { Dashboard: [{
|
|
1480
|
+
type: Input
|
|
1481
|
+
}], dashboard: [{
|
|
1482
|
+
type: Input
|
|
1483
|
+
}], DashboardId: [{
|
|
1260
1484
|
type: Input
|
|
1261
1485
|
}], dashboardId: [{
|
|
1262
1486
|
type: Input
|
|
1487
|
+
}], IsEditing: [{
|
|
1488
|
+
type: Input
|
|
1263
1489
|
}], isEditing: [{
|
|
1264
1490
|
type: Input
|
|
1491
|
+
}], ShowToolbar: [{
|
|
1492
|
+
type: Input
|
|
1265
1493
|
}], showToolbar: [{
|
|
1266
1494
|
type: Input
|
|
1495
|
+
}], AutoSave: [{
|
|
1496
|
+
type: Input
|
|
1267
1497
|
}], autoSave: [{
|
|
1268
1498
|
type: Input
|
|
1499
|
+
}], ShowBreadcrumb: [{
|
|
1500
|
+
type: Input
|
|
1269
1501
|
}], showBreadcrumb: [{
|
|
1270
1502
|
type: Input
|
|
1503
|
+
}], ShowOpenInTabButton: [{
|
|
1504
|
+
type: Input
|
|
1271
1505
|
}], showOpenInTabButton: [{
|
|
1272
1506
|
type: Input
|
|
1507
|
+
}], ShowEditButton: [{
|
|
1508
|
+
type: Input
|
|
1273
1509
|
}], showEditButton: [{
|
|
1274
1510
|
type: Input
|
|
1275
1511
|
}], Categories: [{
|
|
1276
1512
|
type: Input
|
|
1277
1513
|
}], configChanged: [{
|
|
1278
1514
|
type: Output
|
|
1515
|
+
}], NavigationRequested: [{
|
|
1516
|
+
type: Output
|
|
1279
1517
|
}], navigationRequested: [{
|
|
1280
1518
|
type: Output
|
|
1519
|
+
}], PanelInteraction: [{
|
|
1520
|
+
type: Output
|
|
1281
1521
|
}], panelInteraction: [{
|
|
1282
1522
|
type: Output
|
|
1523
|
+
}], DashboardSaved: [{
|
|
1524
|
+
type: Output
|
|
1283
1525
|
}], dashboardSaved: [{
|
|
1284
1526
|
type: Output
|
|
1285
1527
|
}], error: [{
|
|
1286
1528
|
type: Output
|
|
1529
|
+
}], EditModeChanged: [{
|
|
1530
|
+
type: Output
|
|
1287
1531
|
}], editModeChanged: [{
|
|
1288
1532
|
type: Output
|
|
1533
|
+
}], BreadcrumbNavigate: [{
|
|
1534
|
+
type: Output
|
|
1289
1535
|
}], breadcrumbNavigate: [{
|
|
1290
1536
|
type: Output
|
|
1537
|
+
}], OpenInTab: [{
|
|
1538
|
+
type: Output
|
|
1291
1539
|
}], openInTab: [{
|
|
1292
1540
|
type: Output
|
|
1541
|
+
}], LayoutLifecycle: [{
|
|
1542
|
+
type: Output
|
|
1293
1543
|
}], layoutLifecycle: [{
|
|
1294
1544
|
type: Output
|
|
1545
|
+
}], LayoutReady: [{
|
|
1546
|
+
type: Output
|
|
1295
1547
|
}], layoutReady: [{
|
|
1296
1548
|
type: Output
|
|
1549
|
+
}], LayoutDeferred: [{
|
|
1550
|
+
type: Output
|
|
1297
1551
|
}], layoutDeferred: [{
|
|
1298
1552
|
type: Output
|
|
1299
|
-
}],
|
|
1553
|
+
}], LayoutContainer: [{
|
|
1300
1554
|
type: ViewChild,
|
|
1301
1555
|
args: ['layoutContainer', { static: true }]
|
|
1302
1556
|
}] }); })();
|