@memberjunction/ng-dashboard-viewer 6.2.0-edge.2 → 6.2.0-edge.4

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 (99) hide show
  1. package/README.md +165 -17
  2. package/dist/lib/config-dialogs/confirm-dialog.component.d.ts +3 -4
  3. package/dist/lib/config-dialogs/confirm-dialog.component.d.ts.map +1 -1
  4. package/dist/lib/config-dialogs/confirm-dialog.component.js +3 -2
  5. package/dist/lib/config-dialogs/confirm-dialog.component.js.map +1 -1
  6. package/dist/lib/config-panels/artifact-config-panel.component.d.ts.map +1 -1
  7. package/dist/lib/config-panels/artifact-config-panel.component.js +59 -50
  8. package/dist/lib/config-panels/artifact-config-panel.component.js.map +1 -1
  9. package/dist/lib/config-panels/base-config-panel.d.ts +8 -3
  10. package/dist/lib/config-panels/base-config-panel.d.ts.map +1 -1
  11. package/dist/lib/config-panels/base-config-panel.js +10 -3
  12. package/dist/lib/config-panels/base-config-panel.js.map +1 -1
  13. package/dist/lib/config-panels/query-config-panel.component.js +124 -99
  14. package/dist/lib/config-panels/query-config-panel.component.js.map +1 -1
  15. package/dist/lib/config-panels/view-config-panel.component.js +111 -103
  16. package/dist/lib/config-panels/view-config-panel.component.js.map +1 -1
  17. package/dist/lib/config-panels/weburl-config-panel.component.js +89 -80
  18. package/dist/lib/config-panels/weburl-config-panel.component.js.map +1 -1
  19. package/dist/lib/dashboard-browser/dashboard-browser.component.d.ts +79 -28
  20. package/dist/lib/dashboard-browser/dashboard-browser.component.d.ts.map +1 -1
  21. package/dist/lib/dashboard-browser/dashboard-browser.component.js +446 -447
  22. package/dist/lib/dashboard-browser/dashboard-browser.component.js.map +1 -1
  23. package/dist/lib/dashboard-card/dashboard-card.component.d.ts +69 -0
  24. package/dist/lib/dashboard-card/dashboard-card.component.d.ts.map +1 -0
  25. package/dist/lib/dashboard-card/dashboard-card.component.js +281 -0
  26. package/dist/lib/dashboard-card/dashboard-card.component.js.map +1 -0
  27. package/dist/lib/dashboard-card/dashboard-card.helpers.d.ts +18 -0
  28. package/dist/lib/dashboard-card/dashboard-card.helpers.d.ts.map +1 -0
  29. package/dist/lib/dashboard-card/dashboard-card.helpers.js +43 -0
  30. package/dist/lib/dashboard-card/dashboard-card.helpers.js.map +1 -0
  31. package/dist/lib/dashboard-card/layout-preview-cache.d.ts +15 -0
  32. package/dist/lib/dashboard-card/layout-preview-cache.d.ts.map +1 -0
  33. package/dist/lib/dashboard-card/layout-preview-cache.js +22 -0
  34. package/dist/lib/dashboard-card/layout-preview-cache.js.map +1 -0
  35. package/dist/lib/dashboard-viewer/dashboard-viewer.component.d.ts +69 -12
  36. package/dist/lib/dashboard-viewer/dashboard-viewer.component.d.ts.map +1 -1
  37. package/dist/lib/dashboard-viewer/dashboard-viewer.component.js +173 -145
  38. package/dist/lib/dashboard-viewer/dashboard-viewer.component.js.map +1 -1
  39. package/dist/lib/dashboard-viewer.module.d.ts +24 -20
  40. package/dist/lib/dashboard-viewer.module.d.ts.map +1 -1
  41. package/dist/lib/dashboard-viewer.module.js +42 -5
  42. package/dist/lib/dashboard-viewer.module.js.map +1 -1
  43. package/dist/lib/dialogs/add-panel-dialog/add-panel-dialog.component.d.ts +7 -12
  44. package/dist/lib/dialogs/add-panel-dialog/add-panel-dialog.component.d.ts.map +1 -1
  45. package/dist/lib/dialogs/add-panel-dialog/add-panel-dialog.component.js +3 -1
  46. package/dist/lib/dialogs/add-panel-dialog/add-panel-dialog.component.js.map +1 -1
  47. package/dist/lib/dialogs/dashboard-name-dialog/dashboard-name-dialog.component.d.ts +54 -0
  48. package/dist/lib/dialogs/dashboard-name-dialog/dashboard-name-dialog.component.d.ts.map +1 -0
  49. package/dist/lib/dialogs/dashboard-name-dialog/dashboard-name-dialog.component.js +156 -0
  50. package/dist/lib/dialogs/dashboard-name-dialog/dashboard-name-dialog.component.js.map +1 -0
  51. package/dist/lib/dialogs/edit-part-dialog/edit-part-dialog.component.d.ts +5 -11
  52. package/dist/lib/dialogs/edit-part-dialog/edit-part-dialog.component.d.ts.map +1 -1
  53. package/dist/lib/dialogs/edit-part-dialog/edit-part-dialog.component.js +3 -1
  54. package/dist/lib/dialogs/edit-part-dialog/edit-part-dialog.component.js.map +1 -1
  55. package/dist/lib/dialogs/part-dialog/dashboard-part-dialog.component.d.ts +147 -0
  56. package/dist/lib/dialogs/part-dialog/dashboard-part-dialog.component.d.ts.map +1 -0
  57. package/dist/lib/dialogs/part-dialog/dashboard-part-dialog.component.js +536 -0
  58. package/dist/lib/dialogs/part-dialog/dashboard-part-dialog.component.js.map +1 -0
  59. package/dist/lib/layout-preview/dashboard-layout-preview-node.component.d.ts +20 -0
  60. package/dist/lib/layout-preview/dashboard-layout-preview-node.component.d.ts.map +1 -0
  61. package/dist/lib/layout-preview/dashboard-layout-preview-node.component.js +80 -0
  62. package/dist/lib/layout-preview/dashboard-layout-preview-node.component.js.map +1 -0
  63. package/dist/lib/layout-preview/dashboard-layout-preview.component.d.ts +28 -0
  64. package/dist/lib/layout-preview/dashboard-layout-preview.component.d.ts.map +1 -0
  65. package/dist/lib/layout-preview/dashboard-layout-preview.component.js +62 -0
  66. package/dist/lib/layout-preview/dashboard-layout-preview.component.js.map +1 -0
  67. package/dist/lib/layout-preview/dashboard-layout-preview.d.ts +33 -0
  68. package/dist/lib/layout-preview/dashboard-layout-preview.d.ts.map +1 -0
  69. package/dist/lib/layout-preview/dashboard-layout-preview.js +138 -0
  70. package/dist/lib/layout-preview/dashboard-layout-preview.js.map +1 -0
  71. package/dist/lib/models/dashboard-layout-editor.d.ts +78 -0
  72. package/dist/lib/models/dashboard-layout-editor.d.ts.map +1 -0
  73. package/dist/lib/models/dashboard-layout-editor.js +374 -0
  74. package/dist/lib/models/dashboard-layout-editor.js.map +1 -0
  75. package/dist/lib/models/remove-part-confirm.d.ts +8 -0
  76. package/dist/lib/models/remove-part-confirm.d.ts.map +1 -0
  77. package/dist/lib/models/remove-part-confirm.js +23 -0
  78. package/dist/lib/models/remove-part-confirm.js.map +1 -0
  79. package/dist/lib/parts/artifact-part.component.js +2 -2
  80. package/dist/lib/parts/artifact-part.component.js.map +1 -1
  81. package/dist/lib/parts/query-part.component.js +2 -2
  82. package/dist/lib/parts/query-part.component.js.map +1 -1
  83. package/dist/lib/parts/view-part.component.js +2 -2
  84. package/dist/lib/parts/view-part.component.js.map +1 -1
  85. package/dist/lib/parts/weburl-part.component.js +2 -2
  86. package/dist/lib/parts/weburl-part.component.js.map +1 -1
  87. package/dist/lib/services/golden-layout-wrapper.service.d.ts +40 -0
  88. package/dist/lib/services/golden-layout-wrapper.service.d.ts.map +1 -1
  89. package/dist/lib/services/golden-layout-wrapper.service.js +117 -9
  90. package/dist/lib/services/golden-layout-wrapper.service.js.map +1 -1
  91. package/dist/lib/services/stack-part-actions.d.ts +11 -0
  92. package/dist/lib/services/stack-part-actions.d.ts.map +1 -0
  93. package/dist/lib/services/stack-part-actions.js +41 -0
  94. package/dist/lib/services/stack-part-actions.js.map +1 -0
  95. package/dist/public-api.d.ts +9 -0
  96. package/dist/public-api.d.ts.map +1 -1
  97. package/dist/public-api.js +13 -0
  98. package/dist/public-api.js.map +1 -1
  99. package/package.json +13 -13
@@ -0,0 +1,536 @@
1
+ import { Component, Input, Output, EventEmitter, ChangeDetectorRef, ViewChild, ViewContainerRef, inject } from '@angular/core';
2
+ import { MJGlobal, UUIDsEqual } from '@memberjunction/global';
3
+ import { BaseAngularComponent } from '@memberjunction/ng-base-types';
4
+ import { BaseConfigPanel } from '../../config-panels/base-config-panel';
5
+ import * as i0 from "@angular/core";
6
+ import * as i1 from "@angular/forms";
7
+ import * as i2 from "@memberjunction/ng-shared-generic";
8
+ import * as i3 from "@memberjunction/ng-ui-components";
9
+ const _c0 = ["configPanelContainer"];
10
+ const _forTrack0 = ($index, $item) => $item.ID;
11
+ function DashboardPartDialogComponent_Conditional_0_For_7_Template(rf, ctx) { if (rf & 1) {
12
+ const _r3 = i0.ɵɵgetCurrentView();
13
+ i0.ɵɵelementStart(0, "button", 11);
14
+ i0.ɵɵlistener("click", function DashboardPartDialogComponent_Conditional_0_For_7_Template_button_click_0_listener() { const partType_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.SelectPartType(partType_r4)); });
15
+ i0.ɵɵelement(1, "i", 9);
16
+ i0.ɵɵelementStart(2, "span", 12);
17
+ i0.ɵɵtext(3);
18
+ i0.ɵɵelementEnd();
19
+ i0.ɵɵelementStart(4, "span", 13);
20
+ i0.ɵɵtext(5);
21
+ i0.ɵɵelementEnd()();
22
+ } if (rf & 2) {
23
+ const partType_r4 = ctx.$implicit;
24
+ const ctx_r1 = i0.ɵɵnextContext(2);
25
+ i0.ɵɵclassProp("part-dialog__type--selected", ctx_r1.IsSelected(partType_r4));
26
+ i0.ɵɵproperty("disabled", ctx_r1.Mode === "edit" && !ctx_r1.IsSelected(partType_r4));
27
+ i0.ɵɵattribute("aria-pressed", ctx_r1.IsSelected(partType_r4))("data-autofocus", ctx_r1.Mode === "add" && ctx_r1.IsSelected(partType_r4) ? "" : null);
28
+ i0.ɵɵadvance();
29
+ i0.ɵɵclassMap(partType_r4.Icon || "fa-solid fa-puzzle-piece");
30
+ i0.ɵɵadvance(2);
31
+ i0.ɵɵtextInterpolate(ctx_r1.TileLabel(partType_r4));
32
+ i0.ɵɵadvance(2);
33
+ i0.ɵɵtextInterpolate(ctx_r1.TileDescription(partType_r4));
34
+ } }
35
+ function DashboardPartDialogComponent_Conditional_0_Conditional_8_Template(rf, ctx) { if (rf & 1) {
36
+ i0.ɵɵelement(0, "mj-empty-state", 7);
37
+ } }
38
+ function DashboardPartDialogComponent_Conditional_0_Conditional_9_Conditional_0_Conditional_3_Template(rf, ctx) { if (rf & 1) {
39
+ i0.ɵɵelement(0, "mj-loading", 18);
40
+ } }
41
+ function DashboardPartDialogComponent_Conditional_0_Conditional_9_Conditional_0_Conditional_4_Template(rf, ctx) { if (rf & 1) {
42
+ i0.ɵɵelementStart(0, "p", 19);
43
+ i0.ɵɵtext(1);
44
+ i0.ɵɵelementEnd();
45
+ } if (rf & 2) {
46
+ const ctx_r1 = i0.ɵɵnextContext(4);
47
+ i0.ɵɵadvance();
48
+ i0.ɵɵtextInterpolate(ctx_r1.LoadError);
49
+ } }
50
+ function DashboardPartDialogComponent_Conditional_0_Conditional_9_Conditional_0_For_8_Template(rf, ctx) { if (rf & 1) {
51
+ i0.ɵɵelementStart(0, "p", 20);
52
+ i0.ɵɵelement(1, "i", 21);
53
+ i0.ɵɵtext(2);
54
+ i0.ɵɵelementEnd();
55
+ } if (rf & 2) {
56
+ const error_r6 = ctx.$implicit;
57
+ i0.ɵɵadvance(2);
58
+ i0.ɵɵtextInterpolate1(" ", error_r6);
59
+ } }
60
+ function DashboardPartDialogComponent_Conditional_0_Conditional_9_Conditional_0_Template(rf, ctx) { if (rf & 1) {
61
+ i0.ɵɵelementStart(0, "section")(1, "div", 17);
62
+ i0.ɵɵtext(2, "Source");
63
+ i0.ɵɵelementEnd();
64
+ i0.ɵɵconditionalCreate(3, DashboardPartDialogComponent_Conditional_0_Conditional_9_Conditional_0_Conditional_3_Template, 1, 0, "mj-loading", 18);
65
+ i0.ɵɵconditionalCreate(4, DashboardPartDialogComponent_Conditional_0_Conditional_9_Conditional_0_Conditional_4_Template, 2, 1, "p", 19);
66
+ i0.ɵɵelementContainer(5, null, 0);
67
+ i0.ɵɵrepeaterCreate(7, DashboardPartDialogComponent_Conditional_0_Conditional_9_Conditional_0_For_8_Template, 3, 1, "p", 20, i0.ɵɵrepeaterTrackByIndex);
68
+ i0.ɵɵelementEnd();
69
+ } if (rf & 2) {
70
+ const ctx_r1 = i0.ɵɵnextContext(3);
71
+ i0.ɵɵadvance(3);
72
+ i0.ɵɵconditional(ctx_r1.IsLoadingPanel ? 3 : -1);
73
+ i0.ɵɵadvance();
74
+ i0.ɵɵconditional(ctx_r1.LoadError ? 4 : -1);
75
+ i0.ɵɵadvance(3);
76
+ i0.ɵɵrepeater(ctx_r1.Errors);
77
+ } }
78
+ function DashboardPartDialogComponent_Conditional_0_Conditional_9_Conditional_5_For_9_Template(rf, ctx) { if (rf & 1) {
79
+ i0.ɵɵelementStart(0, "option", 25);
80
+ i0.ɵɵtext(1);
81
+ i0.ɵɵelementEnd();
82
+ } if (rf & 2) {
83
+ const seconds_r8 = ctx.$implicit;
84
+ i0.ɵɵproperty("ngValue", seconds_r8);
85
+ i0.ɵɵadvance();
86
+ i0.ɵɵtextInterpolate(seconds_r8);
87
+ } }
88
+ function DashboardPartDialogComponent_Conditional_0_Conditional_9_Conditional_5_Template(rf, ctx) { if (rf & 1) {
89
+ const _r7 = i0.ɵɵgetCurrentView();
90
+ i0.ɵɵelementStart(0, "section", 16)(1, "label", 22)(2, "input", 23);
91
+ i0.ɵɵtwoWayListener("ngModelChange", function DashboardPartDialogComponent_Conditional_0_Conditional_9_Conditional_5_Template_input_ngModelChange_2_listener($event) { i0.ɵɵrestoreView(_r7); const ctx_r1 = i0.ɵɵnextContext(3); i0.ɵɵtwoWayBindingSet(ctx_r1.ShowParameterControls, $event) || (ctx_r1.ShowParameterControls = $event); return i0.ɵɵresetView($event); });
92
+ i0.ɵɵelementEnd();
93
+ i0.ɵɵtext(3, " Show parameter controls ");
94
+ i0.ɵɵelementEnd();
95
+ i0.ɵɵelementStart(4, "label", 22)(5, "input", 23);
96
+ i0.ɵɵtwoWayListener("ngModelChange", function DashboardPartDialogComponent_Conditional_0_Conditional_9_Conditional_5_Template_input_ngModelChange_5_listener($event) { i0.ɵɵrestoreView(_r7); const ctx_r1 = i0.ɵɵnextContext(3); i0.ɵɵtwoWayBindingSet(ctx_r1.AutoRefreshEnabled, $event) || (ctx_r1.AutoRefreshEnabled = $event); return i0.ɵɵresetView($event); });
97
+ i0.ɵɵelementEnd();
98
+ i0.ɵɵtext(6, " Refresh automatically every ");
99
+ i0.ɵɵelementStart(7, "select", 24);
100
+ i0.ɵɵtwoWayListener("ngModelChange", function DashboardPartDialogComponent_Conditional_0_Conditional_9_Conditional_5_Template_select_ngModelChange_7_listener($event) { i0.ɵɵrestoreView(_r7); const ctx_r1 = i0.ɵɵnextContext(3); i0.ɵɵtwoWayBindingSet(ctx_r1.AutoRefreshSeconds, $event) || (ctx_r1.AutoRefreshSeconds = $event); return i0.ɵɵresetView($event); });
101
+ i0.ɵɵrepeaterCreate(8, DashboardPartDialogComponent_Conditional_0_Conditional_9_Conditional_5_For_9_Template, 2, 2, "option", 25, i0.ɵɵrepeaterTrackByIdentity);
102
+ i0.ɵɵelementEnd();
103
+ i0.ɵɵtext(10, " seconds ");
104
+ i0.ɵɵelementEnd()();
105
+ } if (rf & 2) {
106
+ const ctx_r1 = i0.ɵɵnextContext(3);
107
+ i0.ɵɵadvance(2);
108
+ i0.ɵɵtwoWayProperty("ngModel", ctx_r1.ShowParameterControls);
109
+ i0.ɵɵadvance(3);
110
+ i0.ɵɵtwoWayProperty("ngModel", ctx_r1.AutoRefreshEnabled);
111
+ i0.ɵɵadvance(2);
112
+ i0.ɵɵtwoWayProperty("ngModel", ctx_r1.AutoRefreshSeconds);
113
+ i0.ɵɵproperty("disabled", !ctx_r1.AutoRefreshEnabled);
114
+ i0.ɵɵadvance();
115
+ i0.ɵɵrepeater(ctx_r1.RefreshChoices);
116
+ } }
117
+ function DashboardPartDialogComponent_Conditional_0_Conditional_9_Template(rf, ctx) { if (rf & 1) {
118
+ const _r5 = i0.ɵɵgetCurrentView();
119
+ i0.ɵɵconditionalCreate(0, DashboardPartDialogComponent_Conditional_0_Conditional_9_Conditional_0_Template, 9, 2, "section");
120
+ i0.ɵɵelementStart(1, "section")(2, "label", 14);
121
+ i0.ɵɵtext(3, "Title");
122
+ i0.ɵɵelementEnd();
123
+ i0.ɵɵelementStart(4, "input", 15);
124
+ i0.ɵɵtwoWayListener("ngModelChange", function DashboardPartDialogComponent_Conditional_0_Conditional_9_Template_input_ngModelChange_4_listener($event) { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(2); i0.ɵɵtwoWayBindingSet(ctx_r1.Title, $event) || (ctx_r1.Title = $event); return i0.ɵɵresetView($event); });
125
+ i0.ɵɵelementEnd()();
126
+ i0.ɵɵconditionalCreate(5, DashboardPartDialogComponent_Conditional_0_Conditional_9_Conditional_5_Template, 11, 4, "section", 16);
127
+ } if (rf & 2) {
128
+ const ctx_r1 = i0.ɵɵnextContext(2);
129
+ i0.ɵɵconditional(ctx_r1.HasSettingsPanel ? 0 : -1);
130
+ i0.ɵɵadvance(4);
131
+ i0.ɵɵtwoWayProperty("ngModel", ctx_r1.Title);
132
+ i0.ɵɵproperty("placeholder", ctx_r1.TitlePlaceholder);
133
+ i0.ɵɵadvance();
134
+ i0.ɵɵconditional(ctx_r1.IsQuery ? 5 : -1);
135
+ } }
136
+ function DashboardPartDialogComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
137
+ const _r1 = i0.ɵɵgetCurrentView();
138
+ i0.ɵɵelementStart(0, "mj-dialog", 2);
139
+ i0.ɵɵlistener("Close", function DashboardPartDialogComponent_Conditional_0_Template_mj_dialog_Close_0_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.Cancel()); });
140
+ i0.ɵɵelementStart(1, "div", 3)(2, "section")(3, "div", 4);
141
+ i0.ɵɵtext(4, "Part type");
142
+ i0.ɵɵelementEnd();
143
+ i0.ɵɵelementStart(5, "div", 5);
144
+ i0.ɵɵrepeaterCreate(6, DashboardPartDialogComponent_Conditional_0_For_7_Template, 6, 9, "button", 6, _forTrack0);
145
+ i0.ɵɵelementEnd();
146
+ i0.ɵɵconditionalCreate(8, DashboardPartDialogComponent_Conditional_0_Conditional_8_Template, 1, 0, "mj-empty-state", 7);
147
+ i0.ɵɵelementEnd();
148
+ i0.ɵɵconditionalCreate(9, DashboardPartDialogComponent_Conditional_0_Conditional_9_Template, 6, 4);
149
+ i0.ɵɵelementEnd();
150
+ i0.ɵɵelementStart(10, "mj-dialog-actions")(11, "button", 8);
151
+ i0.ɵɵlistener("click", function DashboardPartDialogComponent_Conditional_0_Template_button_click_11_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.Submit()); });
152
+ i0.ɵɵelement(12, "i", 9);
153
+ i0.ɵɵtext(13);
154
+ i0.ɵɵelementEnd();
155
+ i0.ɵɵelementStart(14, "button", 10);
156
+ i0.ɵɵlistener("click", function DashboardPartDialogComponent_Conditional_0_Template_button_click_14_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.Cancel()); });
157
+ i0.ɵɵtext(15, "Cancel");
158
+ i0.ɵɵelementEnd()()();
159
+ } if (rf & 2) {
160
+ const ctx_r1 = i0.ɵɵnextContext();
161
+ i0.ɵɵproperty("Visible", true)("Title", ctx_r1.DialogTitle);
162
+ i0.ɵɵadvance(6);
163
+ i0.ɵɵrepeater(ctx_r1.TypeTiles);
164
+ i0.ɵɵadvance(2);
165
+ i0.ɵɵconditional(ctx_r1.TypeTiles.length === 0 ? 8 : -1);
166
+ i0.ɵɵadvance();
167
+ i0.ɵɵconditional(ctx_r1.SelectedPartType ? 9 : -1);
168
+ i0.ɵɵadvance(2);
169
+ i0.ɵɵproperty("disabled", !ctx_r1.CanSubmit);
170
+ i0.ɵɵadvance();
171
+ i0.ɵɵclassMap(ctx_r1.SubmitIcon);
172
+ i0.ɵɵadvance();
173
+ i0.ɵɵtextInterpolate1(" ", ctx_r1.SubmitLabel, " ");
174
+ } }
175
+ /** The refresh intervals a query part offers, in seconds; the dashboard assistant accepts the same ones. */
176
+ export const QUERY_REFRESH_SECONDS = [30, 60, 300, 600];
177
+ /** The refresh interval a query part gets when the user turns automatic refresh on. */
178
+ const DEFAULT_QUERY_REFRESH_SECONDS = 60;
179
+ /** The icon of a part whose part type has none. */
180
+ const DEFAULT_PART_ICON = 'fa-solid fa-puzzle-piece';
181
+ /** Short tile copy for the built-in part types, by part type Name. Other types show their metadata Name and Description. */
182
+ const PART_TYPE_TILE_COPY = {
183
+ View: { Label: 'View', Description: 'A saved view, or all records of an entity' },
184
+ Query: { Label: 'Query', Description: 'A saved query, with its parameters' },
185
+ Artifact: { Label: 'Artifact', Description: 'A report, chart or component from Chat' },
186
+ WebURL: { Label: 'Web page', Description: 'An embedded page' },
187
+ };
188
+ /**
189
+ * The dialog that adds a part to a dashboard or edits one, on `mj-dialog`.
190
+ *
191
+ * It shows the part type tiles, the part type's settings panel as the Source section, the title
192
+ * and, for a query, the parameter-controls and auto refresh options. The settings panel is the
193
+ * `BaseConfigPanel` that ClassFactory registers under the part type's `ConfigDialogClass`; the
194
+ * dialog passes it `Provider` and hides the panel's own copies of the fields it shows itself.
195
+ * In edit mode the part's type cannot change, so the other tiles are disabled.
196
+ *
197
+ * The host opens and closes it, for example with `@if` around `[Visible]="true"`.
198
+ */
199
+ export class DashboardPartDialogComponent extends BaseAngularComponent {
200
+ cdr = inject(ChangeDetectorRef);
201
+ /**
202
+ * Whether the dialog shows. Opening reads Mode, Panel and PartTypes after the current input
203
+ * pass, because inputs arrive in no fixed order. Closing removes the settings panel.
204
+ */
205
+ set Visible(value) {
206
+ const wasVisible = this._visible;
207
+ this._visible = value;
208
+ if (value && !wasVisible) {
209
+ const openId = ++this.openCount;
210
+ Promise.resolve().then(() => this.open(openId));
211
+ }
212
+ else if (!value && wasVisible) {
213
+ this.close();
214
+ }
215
+ }
216
+ get Visible() {
217
+ return this._visible;
218
+ }
219
+ _visible = false;
220
+ /** Whether the dialog adds a new part or edits {@link Panel}. */
221
+ Mode = 'add';
222
+ /** The part types to offer. The tiles show the active ones, and the edited part's type, by SortOrder. */
223
+ PartTypes = [];
224
+ /** The part to edit in edit mode. Its part type is the one in {@link PartTypes} with its `partTypeId`. */
225
+ Panel = null;
226
+ /** Emitted on Add part or Apply with the part type, config, title and icon of the part. */
227
+ Saved = new EventEmitter();
228
+ /** Emitted on Cancel, and when the user closes the dialog with Escape, the backdrop or the close button. */
229
+ Cancelled = new EventEmitter();
230
+ /** Where the settings panel of the chosen part type renders. */
231
+ configPanelContainer;
232
+ /** The chosen part type. */
233
+ SelectedPartType = null;
234
+ /** The part's title: empty for a new part, the part's title in edit mode. An empty title means the source name. */
235
+ Title = '';
236
+ /** The name of the chosen source, from the settings panel. A part type without one shows its tile label. */
237
+ SourceName = '';
238
+ /** Query option: show the query's parameter controls on the part. */
239
+ ShowParameterControls = true;
240
+ /** Query option: refresh the part on a timer. */
241
+ AutoRefreshEnabled = false;
242
+ /** Query option: the refresh interval, in seconds. */
243
+ AutoRefreshSeconds = DEFAULT_QUERY_REFRESH_SECONDS;
244
+ /** True while the settings panel of the chosen part type loads. */
245
+ IsLoadingPanel = false;
246
+ /** Why the settings panel could not load, when it could not. */
247
+ LoadError = null;
248
+ /** The settings panel's validation errors from the last Add part or Apply. */
249
+ Errors = [];
250
+ configPanelRef = null;
251
+ configChangedSubscription = null;
252
+ /** The last result the settings panel reported. */
253
+ latestResult = null;
254
+ refreshChoices = QUERY_REFRESH_SECONDS;
255
+ /** Counts opens, so an open that a close or a newer open overtook does nothing. */
256
+ openCount = 0;
257
+ /** Counts settings panel loads, so a load for a part type the user has left is dropped. */
258
+ loadCount = 0;
259
+ destroyed = false;
260
+ /** The dialog title. */
261
+ get DialogTitle() {
262
+ return this.Mode === 'edit' ? 'Edit part' : 'Add a part';
263
+ }
264
+ /** The label of the confirm button. */
265
+ get SubmitLabel() {
266
+ return this.Mode === 'edit' ? 'Apply' : 'Add part';
267
+ }
268
+ /** The icon of the confirm button. */
269
+ get SubmitIcon() {
270
+ return this.Mode === 'edit' ? 'fa-solid fa-check' : 'fa-solid fa-plus';
271
+ }
272
+ /** The placeholder of the title field: the title the part gets when the field is empty. */
273
+ get TitlePlaceholder() {
274
+ return `Default: ${this.SourceName}`;
275
+ }
276
+ /** Whether the chosen part type is Query, which has the query options. */
277
+ get IsQuery() {
278
+ return this.SelectedPartType?.Name === 'Query';
279
+ }
280
+ /** Whether the chosen part type has a settings panel, which fills the Source section. */
281
+ get HasSettingsPanel() {
282
+ return this.configDialogClassOf(this.SelectedPartType) != null;
283
+ }
284
+ /** The part type tiles: the active part types, and the edited part's type, by SortOrder. */
285
+ get TypeTiles() {
286
+ const editedTypeId = this.Mode === 'edit' ? this.Panel?.partTypeId ?? null : null;
287
+ return this.PartTypes
288
+ .filter(pt => pt.IsActive !== false || (editedTypeId != null && UUIDsEqual(pt.ID, editedTypeId)))
289
+ .sort((a, b) => (a.SortOrder ?? 0) - (b.SortOrder ?? 0));
290
+ }
291
+ /** The refresh intervals to offer: {@link QUERY_REFRESH_SECONDS}, plus the edited part's interval when it is another one. */
292
+ get RefreshChoices() {
293
+ return this.refreshChoices;
294
+ }
295
+ /**
296
+ * Whether Add part or Apply is enabled. Apply is enabled as soon as the part type is shown.
297
+ * Add part waits until the settings panel reports a valid source.
298
+ */
299
+ get CanSubmit() {
300
+ if (!this.SelectedPartType || this.IsLoadingPanel)
301
+ return false;
302
+ return this.Mode === 'edit' || this.configPanelRef == null || this.latestResult?.isValid === true;
303
+ }
304
+ /** Whether a tile's part type is the chosen one. */
305
+ IsSelected(partType) {
306
+ return this.SelectedPartType != null && UUIDsEqual(partType.ID, this.SelectedPartType.ID);
307
+ }
308
+ /** The label on a part type tile. */
309
+ TileLabel(partType) {
310
+ return PART_TYPE_TILE_COPY[partType.Name]?.Label ?? partType.Name;
311
+ }
312
+ /** The description on a part type tile. */
313
+ TileDescription(partType) {
314
+ return PART_TYPE_TILE_COPY[partType.Name]?.Description ?? partType.Description ?? '';
315
+ }
316
+ /** Chooses a part type in add mode and loads its settings panel. */
317
+ SelectPartType(partType) {
318
+ if (this.Mode === 'edit' || this.IsSelected(partType))
319
+ return;
320
+ this.selectType(partType);
321
+ }
322
+ /** Takes a change from the settings panel: its latest result and the name of its source. */
323
+ OnConfigChanged(result) {
324
+ this.latestResult = result;
325
+ const panel = this.configPanelRef?.instance;
326
+ if (panel) {
327
+ this.setSourceName(panel.getDefaultTitle());
328
+ }
329
+ this.Errors = [];
330
+ this.cdr.markForCheck();
331
+ }
332
+ /**
333
+ * Emits {@link Saved}, or shows the settings panel's errors when its source is not valid.
334
+ * The config is the settings panel's config. Without a panel it names only the part type
335
+ * (add mode) or is the part's current config (edit mode). A query part also gets the query options.
336
+ */
337
+ Submit() {
338
+ const partType = this.SelectedPartType;
339
+ if (!partType || !this.CanSubmit)
340
+ return;
341
+ const panel = this.configPanelRef?.instance ?? null;
342
+ let config;
343
+ let icon;
344
+ if (panel) {
345
+ const result = panel.getResult();
346
+ if (!result.isValid) {
347
+ this.latestResult = result;
348
+ this.Errors = result.errors;
349
+ this.cdr.markForCheck();
350
+ return;
351
+ }
352
+ this.setSourceName(panel.getDefaultTitle());
353
+ config = { ...result.config };
354
+ icon = result.icon;
355
+ }
356
+ else if (this.Mode === 'edit' && this.Panel) {
357
+ config = { ...this.Panel.config };
358
+ icon = this.Panel.icon;
359
+ }
360
+ else {
361
+ config = { type: partType.Name };
362
+ }
363
+ if (this.IsQuery) {
364
+ config['showParameterControls'] = this.ShowParameterControls;
365
+ config['autoRefreshSeconds'] = this.AutoRefreshEnabled ? this.AutoRefreshSeconds : 0;
366
+ }
367
+ this.Saved.emit({
368
+ PartType: partType,
369
+ Config: config,
370
+ Title: this.Title.trim() || this.SourceName,
371
+ Icon: icon || partType.Icon || DEFAULT_PART_ICON
372
+ });
373
+ }
374
+ /** Emits {@link Cancelled}. */
375
+ Cancel() {
376
+ this.Cancelled.emit();
377
+ }
378
+ ngOnDestroy() {
379
+ this.destroyed = true;
380
+ this.loadCount++;
381
+ this.destroyConfigPanel();
382
+ }
383
+ /** Starts from the inputs: a new part with the first tile chosen, or the edited part. */
384
+ open(openId) {
385
+ if (this.destroyed || !this._visible || openId !== this.openCount)
386
+ return;
387
+ const editing = this.Mode === 'edit';
388
+ this.Title = editing ? this.Panel?.title ?? '' : '';
389
+ this.readQueryOptions(editing ? this.Panel?.config ?? null : null);
390
+ this.selectType(editing ? this.findEditedPartType() : this.TypeTiles[0] ?? null);
391
+ }
392
+ /** Removes the settings panel and drops a load that is still running. */
393
+ close() {
394
+ this.loadCount++;
395
+ this.destroyConfigPanel();
396
+ this.SelectedPartType = null;
397
+ this.IsLoadingPanel = false;
398
+ this.LoadError = null;
399
+ this.Errors = [];
400
+ this.latestResult = null;
401
+ }
402
+ /** The part type of the edited part, from {@link PartTypes}. */
403
+ findEditedPartType() {
404
+ const partTypeId = this.Panel?.partTypeId;
405
+ if (!partTypeId)
406
+ return null;
407
+ return this.PartTypes.find(pt => UUIDsEqual(pt.ID, partTypeId)) ?? null;
408
+ }
409
+ /** Reads the query options from a part's config. A new part (no config) gets the defaults. */
410
+ readQueryOptions(config) {
411
+ const showParameterControls = config?.['showParameterControls'];
412
+ const seconds = config?.['autoRefreshSeconds'];
413
+ const refreshSeconds = typeof seconds === 'number' && seconds > 0 ? seconds : 0;
414
+ this.ShowParameterControls = typeof showParameterControls === 'boolean' ? showParameterControls : true;
415
+ this.AutoRefreshEnabled = refreshSeconds > 0;
416
+ this.AutoRefreshSeconds = refreshSeconds > 0 ? refreshSeconds : DEFAULT_QUERY_REFRESH_SECONDS;
417
+ this.refreshChoices = refreshSeconds > 0 && !QUERY_REFRESH_SECONDS.some(choice => choice === refreshSeconds)
418
+ ? [...QUERY_REFRESH_SECONDS, refreshSeconds].sort((a, b) => a - b)
419
+ : QUERY_REFRESH_SECONDS;
420
+ }
421
+ /** Makes a part type the chosen one and starts to load its settings panel. */
422
+ selectType(partType) {
423
+ const loadId = ++this.loadCount;
424
+ this.destroyConfigPanel();
425
+ this.SelectedPartType = partType;
426
+ this.SourceName = partType ? this.TileLabel(partType) : '';
427
+ this.latestResult = null;
428
+ this.Errors = [];
429
+ this.LoadError = null;
430
+ const className = this.configDialogClassOf(partType);
431
+ this.IsLoadingPanel = className != null;
432
+ // Renders the Source section now, so its container exists when the panel class arrives.
433
+ this.cdr.detectChanges();
434
+ if (partType && className) {
435
+ void this.loadConfigPanel(partType, className, loadId);
436
+ }
437
+ }
438
+ /**
439
+ * Creates the part type's settings panel in the Source section. A load that a newer load or a
440
+ * close overtook is dropped. When the panel cannot be created, the dialog says why and still
441
+ * submits: with a config that names only the part type (add mode) or the part's current config (edit mode).
442
+ */
443
+ async loadConfigPanel(partType, className, loadId) {
444
+ try {
445
+ const registration = await MJGlobal.Instance.ClassFactory.GetRegistrationAsync(BaseConfigPanel, className);
446
+ if (loadId !== this.loadCount)
447
+ return;
448
+ const container = this.configPanelContainer;
449
+ if (!registration || !container) {
450
+ this.failPanelLoad(registration ? `The settings panel "${className}" could not be shown.` : `The settings panel "${className}" is not registered.`);
451
+ return;
452
+ }
453
+ container.clear();
454
+ const ref = container.createComponent(registration.SubClass);
455
+ this.configPanelRef = ref;
456
+ const panel = ref.instance;
457
+ panel.Provider = this.Provider;
458
+ panel.ShowCommonFields = false;
459
+ panel.partType = partType;
460
+ if (this.Mode === 'edit' && this.Panel) {
461
+ panel.panel = this.Panel;
462
+ panel.config = this.Panel.config;
463
+ }
464
+ this.configChangedSubscription = panel.configChanged.subscribe(result => this.OnConfigChanged(result));
465
+ this.SourceName = panel.getDefaultTitle();
466
+ this.IsLoadingPanel = false;
467
+ this.cdr.detectChanges();
468
+ }
469
+ catch (error) {
470
+ if (loadId !== this.loadCount)
471
+ return;
472
+ this.failPanelLoad(`The settings panel "${className}" could not load (${error instanceof Error ? error.message : String(error)}).`);
473
+ }
474
+ }
475
+ /** Shows why the settings panel could not load, and what Add part or Apply does without it. */
476
+ failPanelLoad(reason) {
477
+ this.destroyConfigPanel();
478
+ this.IsLoadingPanel = false;
479
+ const fallback = this.Mode === 'edit' ? 'Apply keeps the part\'s current settings.' : 'The part is added with default settings.';
480
+ this.LoadError = `${reason} ${fallback}`;
481
+ this.cdr.detectChanges();
482
+ }
483
+ /**
484
+ * Takes a new source name. A title that is the old source name follows the new one, as the
485
+ * settings panels' own title fields do; a title the user changed stays.
486
+ */
487
+ setSourceName(sourceName) {
488
+ const title = this.Title.trim();
489
+ if (title !== '' && title === this.SourceName) {
490
+ this.Title = sourceName;
491
+ }
492
+ this.SourceName = sourceName;
493
+ }
494
+ /** The registered class name of a part type's settings panel, if it has one. */
495
+ configDialogClassOf(partType) {
496
+ return partType?.ConfigDialogClass?.trim() || null;
497
+ }
498
+ destroyConfigPanel() {
499
+ this.configChangedSubscription?.unsubscribe();
500
+ this.configChangedSubscription = null;
501
+ this.configPanelRef?.destroy();
502
+ this.configPanelRef = null;
503
+ }
504
+ static ɵfac = /*@__PURE__*/ (() => { let ɵDashboardPartDialogComponent_BaseFactory; return function DashboardPartDialogComponent_Factory(__ngFactoryType__) { return (ɵDashboardPartDialogComponent_BaseFactory || (ɵDashboardPartDialogComponent_BaseFactory = i0.ɵɵgetInheritedFactory(DashboardPartDialogComponent)))(__ngFactoryType__ || DashboardPartDialogComponent); }; })();
505
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: DashboardPartDialogComponent, selectors: [["mj-dashboard-part-dialog"]], viewQuery: function DashboardPartDialogComponent_Query(rf, ctx) { if (rf & 1) {
506
+ i0.ɵɵviewQuery(_c0, 5, ViewContainerRef);
507
+ } if (rf & 2) {
508
+ let _t;
509
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.configPanelContainer = _t.first);
510
+ } }, inputs: { Visible: "Visible", Mode: "Mode", PartTypes: "PartTypes", Panel: "Panel" }, outputs: { Saved: "Saved", Cancelled: "Cancelled" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 1, vars: 1, consts: [["configPanelContainer", ""], ["Size", "md", 3, "Visible", "Title"], ["Size", "md", 3, "Close", "Visible", "Title"], [1, "part-dialog"], ["id", "part-dialog-type-label", 1, "part-dialog__label"], ["role", "group", "aria-labelledby", "part-dialog-type-label", 1, "part-dialog__types"], ["type", "button", 1, "part-dialog__type", 3, "part-dialog__type--selected", "disabled"], ["Size", "compact", "Variant", "warning", "Title", "No part types available", "Message", "Dashboard part types have not been set up in this system."], ["mjButton", "", "Variant", "primary", "type", "button", 3, "click", "disabled"], ["aria-hidden", "true"], ["mjButton", "", "Variant", "secondary", "type", "button", 3, "click"], ["type", "button", 1, "part-dialog__type", 3, "click", "disabled"], [1, "part-dialog__type-label"], [1, "part-dialog__type-desc"], ["for", "part-dialog-title", 1, "part-dialog__label"], ["id", "part-dialog-title", "type", "text", 1, "mj-input", 3, "ngModelChange", "ngModel", "placeholder"], [1, "part-dialog__options"], [1, "part-dialog__label"], ["Text", "Loading...", "Size", "small"], [1, "part-dialog__warning"], [1, "part-dialog__error"], ["aria-hidden", "true", 1, "fa-solid", "fa-circle-exclamation"], [1, "part-dialog__option"], ["type", "checkbox", 1, "mj-checkbox", 3, "ngModelChange", "ngModel"], ["aria-label", "Refresh interval in seconds", 1, "mj-input", "part-dialog__refresh", 3, "ngModelChange", "ngModel", "disabled"], [3, "ngValue"]], template: function DashboardPartDialogComponent_Template(rf, ctx) { if (rf & 1) {
511
+ i0.ɵɵconditionalCreate(0, DashboardPartDialogComponent_Conditional_0_Template, 16, 8, "mj-dialog", 1);
512
+ } if (rf & 2) {
513
+ i0.ɵɵconditional(ctx.Visible ? 0 : -1);
514
+ } }, dependencies: [i1.NgSelectOption, i1.ɵNgSelectMultipleOption, i1.DefaultValueAccessor, i1.CheckboxControlValueAccessor, i1.SelectControlValueAccessor, i1.NgControlStatus, i1.NgModel, i2.LoadingComponent, i3.MJEmptyStateComponent, i3.MJDialogComponent, i3.MJDialogActionsComponent, i3.MJButtonDirective], styles: [".part-dialog[_ngcontent-%COMP%] { display: flex; flex-direction: column; gap: var(--mj-space-4); }\n.part-dialog__label[_ngcontent-%COMP%] { display: block; margin-bottom: 6px; font-size: var(--mj-text-xs); font-weight: var(--mj-font-semibold); color: var(--mj-text-secondary); }\n.part-dialog__types[_ngcontent-%COMP%] { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--mj-space-2); }\n.part-dialog__type[_ngcontent-%COMP%] {\n display: flex; flex-direction: column; gap: 4px; padding: 10px; text-align: left;\n font: inherit; color: inherit; cursor: pointer;\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default); border-radius: var(--mj-radius-md);\n transition: border-color var(--mj-transition-fast), background-color var(--mj-transition-fast);\n}\n.part-dialog__type[_ngcontent-%COMP%]:hover:not(:disabled) { border-color: var(--mj-border-strong); }\n.part-dialog__type[_ngcontent-%COMP%]:focus-visible { outline: none; box-shadow: var(--mj-focus-ring); }\n.part-dialog__type[_ngcontent-%COMP%]:disabled { cursor: not-allowed; opacity: 0.5; }\n.part-dialog__type[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { font-size: 16px; color: var(--mj-text-secondary); }\n.part-dialog__type-label[_ngcontent-%COMP%] { font-size: 13px; font-weight: var(--mj-font-bold); }\n.part-dialog__type-desc[_ngcontent-%COMP%] { font-size: 11px; color: var(--mj-text-muted); }\n.part-dialog__type--selected[_ngcontent-%COMP%] { border-color: var(--mj-brand-primary); background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface)); }\n.part-dialog__type--selected[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { color: var(--mj-brand-primary); }\n.part-dialog__options[_ngcontent-%COMP%] { display: flex; flex-direction: column; gap: var(--mj-space-2); }\n.part-dialog__option[_ngcontent-%COMP%] { display: flex; align-items: center; gap: var(--mj-space-2); font-size: 13px; }\n.part-dialog__refresh[_ngcontent-%COMP%] { display: inline-block; width: auto; min-height: 32px; padding: 4px 8px; }\n.part-dialog__error[_ngcontent-%COMP%] { margin: var(--mj-space-2) 0 0; font-size: 13px; color: var(--mj-status-error); }\n.part-dialog__warning[_ngcontent-%COMP%] { margin: 0 0 var(--mj-space-2); font-size: 13px; color: var(--mj-status-warning-text); }\n@media (max-width: 900px) { .part-dialog__types[_ngcontent-%COMP%] { grid-template-columns: repeat(2, minmax(0, 1fr)); } }"] });
515
+ }
516
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(DashboardPartDialogComponent, [{
517
+ type: Component,
518
+ args: [{ standalone: false, selector: 'mj-dashboard-part-dialog', template: "@if (Visible) {\n <mj-dialog [Visible]=\"true\" Size=\"md\" [Title]=\"DialogTitle\" (Close)=\"Cancel()\">\n <div class=\"part-dialog\">\n <section>\n <div class=\"part-dialog__label\" id=\"part-dialog-type-label\">Part type</div>\n <div class=\"part-dialog__types\" role=\"group\" aria-labelledby=\"part-dialog-type-label\">\n @for (partType of TypeTiles; track partType.ID) {\n <button type=\"button\" class=\"part-dialog__type\"\n [class.part-dialog__type--selected]=\"IsSelected(partType)\"\n [attr.aria-pressed]=\"IsSelected(partType)\"\n [attr.data-autofocus]=\"Mode === 'add' && IsSelected(partType) ? '' : null\"\n [disabled]=\"Mode === 'edit' && !IsSelected(partType)\"\n (click)=\"SelectPartType(partType)\">\n <i [class]=\"partType.Icon || 'fa-solid fa-puzzle-piece'\" aria-hidden=\"true\"></i>\n <span class=\"part-dialog__type-label\">{{ TileLabel(partType) }}</span>\n <span class=\"part-dialog__type-desc\">{{ TileDescription(partType) }}</span>\n </button>\n }\n </div>\n @if (TypeTiles.length === 0) {\n <mj-empty-state Size=\"compact\" Variant=\"warning\" Title=\"No part types available\"\n Message=\"Dashboard part types have not been set up in this system.\" />\n }\n </section>\n\n @if (SelectedPartType) {\n @if (HasSettingsPanel) {\n <section>\n <div class=\"part-dialog__label\">Source</div>\n @if (IsLoadingPanel) {\n <mj-loading Text=\"Loading...\" Size=\"small\"></mj-loading>\n }\n @if (LoadError) {\n <p class=\"part-dialog__warning\">{{ LoadError }}</p>\n }\n <ng-container #configPanelContainer></ng-container>\n @for (error of Errors; track $index) {\n <p class=\"part-dialog__error\"><i class=\"fa-solid fa-circle-exclamation\" aria-hidden=\"true\"></i> {{ error }}</p>\n }\n </section>\n }\n\n <section>\n <label class=\"part-dialog__label\" for=\"part-dialog-title\">Title</label>\n <input id=\"part-dialog-title\" class=\"mj-input\" type=\"text\" [(ngModel)]=\"Title\" [placeholder]=\"TitlePlaceholder\">\n </section>\n\n @if (IsQuery) {\n <section class=\"part-dialog__options\">\n <label class=\"part-dialog__option\">\n <input type=\"checkbox\" class=\"mj-checkbox\" [(ngModel)]=\"ShowParameterControls\"> Show parameter controls\n </label>\n <label class=\"part-dialog__option\">\n <input type=\"checkbox\" class=\"mj-checkbox\" [(ngModel)]=\"AutoRefreshEnabled\"> Refresh automatically every\n <select class=\"mj-input part-dialog__refresh\" [(ngModel)]=\"AutoRefreshSeconds\" [disabled]=\"!AutoRefreshEnabled\" aria-label=\"Refresh interval in seconds\">\n @for (seconds of RefreshChoices; track seconds) {\n <option [ngValue]=\"seconds\">{{ seconds }}</option>\n }\n </select>\n seconds\n </label>\n </section>\n }\n }\n </div>\n <mj-dialog-actions>\n <button mjButton Variant=\"primary\" type=\"button\" [disabled]=\"!CanSubmit\" (click)=\"Submit()\">\n <i [class]=\"SubmitIcon\" aria-hidden=\"true\"></i> {{ SubmitLabel }}\n </button>\n <button mjButton Variant=\"secondary\" type=\"button\" (click)=\"Cancel()\">Cancel</button>\n </mj-dialog-actions>\n </mj-dialog>\n}\n", styles: [".part-dialog { display: flex; flex-direction: column; gap: var(--mj-space-4); }\n.part-dialog__label { display: block; margin-bottom: 6px; font-size: var(--mj-text-xs); font-weight: var(--mj-font-semibold); color: var(--mj-text-secondary); }\n.part-dialog__types { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--mj-space-2); }\n.part-dialog__type {\n display: flex; flex-direction: column; gap: 4px; padding: 10px; text-align: left;\n font: inherit; color: inherit; cursor: pointer;\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default); border-radius: var(--mj-radius-md);\n transition: border-color var(--mj-transition-fast), background-color var(--mj-transition-fast);\n}\n.part-dialog__type:hover:not(:disabled) { border-color: var(--mj-border-strong); }\n.part-dialog__type:focus-visible { outline: none; box-shadow: var(--mj-focus-ring); }\n.part-dialog__type:disabled { cursor: not-allowed; opacity: 0.5; }\n.part-dialog__type i { font-size: 16px; color: var(--mj-text-secondary); }\n.part-dialog__type-label { font-size: 13px; font-weight: var(--mj-font-bold); }\n.part-dialog__type-desc { font-size: 11px; color: var(--mj-text-muted); }\n.part-dialog__type--selected { border-color: var(--mj-brand-primary); background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface)); }\n.part-dialog__type--selected i { color: var(--mj-brand-primary); }\n.part-dialog__options { display: flex; flex-direction: column; gap: var(--mj-space-2); }\n.part-dialog__option { display: flex; align-items: center; gap: var(--mj-space-2); font-size: 13px; }\n.part-dialog__refresh { display: inline-block; width: auto; min-height: 32px; padding: 4px 8px; }\n.part-dialog__error { margin: var(--mj-space-2) 0 0; font-size: 13px; color: var(--mj-status-error); }\n.part-dialog__warning { margin: 0 0 var(--mj-space-2); font-size: 13px; color: var(--mj-status-warning-text); }\n@media (max-width: 900px) { .part-dialog__types { grid-template-columns: repeat(2, minmax(0, 1fr)); } }\n"] }]
519
+ }], null, { Visible: [{
520
+ type: Input
521
+ }], Mode: [{
522
+ type: Input
523
+ }], PartTypes: [{
524
+ type: Input
525
+ }], Panel: [{
526
+ type: Input
527
+ }], Saved: [{
528
+ type: Output
529
+ }], Cancelled: [{
530
+ type: Output
531
+ }], configPanelContainer: [{
532
+ type: ViewChild,
533
+ args: ['configPanelContainer', { read: ViewContainerRef }]
534
+ }] }); })();
535
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(DashboardPartDialogComponent, { className: "DashboardPartDialogComponent", filePath: "src/lib/dialogs/part-dialog/dashboard-part-dialog.component.ts", lineNumber: 71 }); })();
536
+ //# sourceMappingURL=dashboard-part-dialog.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-part-dialog.component.js","sourceRoot":"","sources":["../../../../src/lib/dialogs/part-dialog/dashboard-part-dialog.component.ts","../../../../src/lib/dialogs/part-dialog/dashboard-part-dialog.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n Input,\n Output,\n EventEmitter,\n ChangeDetectorRef,\n ViewChild,\n ViewContainerRef,\n ComponentRef,\n OnDestroy,\n inject\n} from '@angular/core';\nimport { Subscription } from 'rxjs';\nimport { MJGlobal, UUIDsEqual } from '@memberjunction/global';\nimport { MJDashboardPartTypeEntity } from '@memberjunction/core-entities';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { PanelConfig, DashboardPanel } from '../../models/dashboard-types';\nimport { BaseConfigPanel, ConfigPanelResult } from '../../config-panels/base-config-panel';\n\n/** Whether the part dialog adds a new part or edits an existing one. */\nexport type DashboardPartDialogMode = 'add' | 'edit';\n\n/** What the part dialog gives back on Add part or Apply. */\nexport interface DashboardPartDialogResult {\n PartType: MJDashboardPartTypeEntity;\n Config: PanelConfig;\n Title: string;\n Icon?: string;\n}\n\n/** The refresh intervals a query part offers, in seconds; the dashboard assistant accepts the same ones. */\nexport const QUERY_REFRESH_SECONDS = [30, 60, 300, 600] as const;\n\n/** The refresh interval a query part gets when the user turns automatic refresh on. */\nconst DEFAULT_QUERY_REFRESH_SECONDS = 60;\n\n/** The icon of a part whose part type has none. */\nconst DEFAULT_PART_ICON = 'fa-solid fa-puzzle-piece';\n\n/** The label and description on a part type tile. */\ninterface PartTypeTileCopy {\n Label: string;\n Description: string;\n}\n\n/** Short tile copy for the built-in part types, by part type Name. Other types show their metadata Name and Description. */\nconst PART_TYPE_TILE_COPY: Readonly<Record<string, PartTypeTileCopy | undefined>> = {\n View: { Label: 'View', Description: 'A saved view, or all records of an entity' },\n Query: { Label: 'Query', Description: 'A saved query, with its parameters' },\n Artifact: { Label: 'Artifact', Description: 'A report, chart or component from Chat' },\n WebURL: { Label: 'Web page', Description: 'An embedded page' },\n};\n\n/**\n * The dialog that adds a part to a dashboard or edits one, on `mj-dialog`.\n *\n * It shows the part type tiles, the part type's settings panel as the Source section, the title\n * and, for a query, the parameter-controls and auto refresh options. The settings panel is the\n * `BaseConfigPanel` that ClassFactory registers under the part type's `ConfigDialogClass`; the\n * dialog passes it `Provider` and hides the panel's own copies of the fields it shows itself.\n * In edit mode the part's type cannot change, so the other tiles are disabled.\n *\n * The host opens and closes it, for example with `@if` around `[Visible]=\"true\"`.\n */\n@Component({\n standalone: false,\n selector: 'mj-dashboard-part-dialog',\n templateUrl: './dashboard-part-dialog.component.html',\n styleUrls: ['./dashboard-part-dialog.component.css']\n})\nexport class DashboardPartDialogComponent extends BaseAngularComponent implements OnDestroy {\n private readonly cdr = inject(ChangeDetectorRef);\n\n /**\n * Whether the dialog shows. Opening reads Mode, Panel and PartTypes after the current input\n * pass, because inputs arrive in no fixed order. Closing removes the settings panel.\n */\n @Input()\n set Visible(value: boolean) {\n const wasVisible = this._visible;\n this._visible = value;\n if (value && !wasVisible) {\n const openId = ++this.openCount;\n Promise.resolve().then(() => this.open(openId));\n } else if (!value && wasVisible) {\n this.close();\n }\n }\n get Visible(): boolean {\n return this._visible;\n }\n private _visible = false;\n\n /** Whether the dialog adds a new part or edits {@link Panel}. */\n @Input() Mode: DashboardPartDialogMode = 'add';\n\n /** The part types to offer. The tiles show the active ones, and the edited part's type, by SortOrder. */\n @Input() PartTypes: MJDashboardPartTypeEntity[] = [];\n\n /** The part to edit in edit mode. Its part type is the one in {@link PartTypes} with its `partTypeId`. */\n @Input() Panel: DashboardPanel | null = null;\n\n /** Emitted on Add part or Apply with the part type, config, title and icon of the part. */\n @Output() Saved = new EventEmitter<DashboardPartDialogResult>();\n\n /** Emitted on Cancel, and when the user closes the dialog with Escape, the backdrop or the close button. */\n @Output() Cancelled = new EventEmitter<void>();\n\n /** Where the settings panel of the chosen part type renders. */\n @ViewChild('configPanelContainer', { read: ViewContainerRef })\n private configPanelContainer?: ViewContainerRef;\n\n /** The chosen part type. */\n public SelectedPartType: MJDashboardPartTypeEntity | null = null;\n\n /** The part's title: empty for a new part, the part's title in edit mode. An empty title means the source name. */\n public Title = '';\n\n /** The name of the chosen source, from the settings panel. A part type without one shows its tile label. */\n public SourceName = '';\n\n /** Query option: show the query's parameter controls on the part. */\n public ShowParameterControls = true;\n\n /** Query option: refresh the part on a timer. */\n public AutoRefreshEnabled = false;\n\n /** Query option: the refresh interval, in seconds. */\n public AutoRefreshSeconds = DEFAULT_QUERY_REFRESH_SECONDS;\n\n /** True while the settings panel of the chosen part type loads. */\n public IsLoadingPanel = false;\n\n /** Why the settings panel could not load, when it could not. */\n public LoadError: string | null = null;\n\n /** The settings panel's validation errors from the last Add part or Apply. */\n public Errors: string[] = [];\n\n private configPanelRef: ComponentRef<BaseConfigPanel> | null = null;\n private configChangedSubscription: Subscription | null = null;\n /** The last result the settings panel reported. */\n private latestResult: ConfigPanelResult | null = null;\n private refreshChoices: readonly number[] = QUERY_REFRESH_SECONDS;\n /** Counts opens, so an open that a close or a newer open overtook does nothing. */\n private openCount = 0;\n /** Counts settings panel loads, so a load for a part type the user has left is dropped. */\n private loadCount = 0;\n private destroyed = false;\n\n /** The dialog title. */\n get DialogTitle(): string {\n return this.Mode === 'edit' ? 'Edit part' : 'Add a part';\n }\n\n /** The label of the confirm button. */\n get SubmitLabel(): string {\n return this.Mode === 'edit' ? 'Apply' : 'Add part';\n }\n\n /** The icon of the confirm button. */\n get SubmitIcon(): string {\n return this.Mode === 'edit' ? 'fa-solid fa-check' : 'fa-solid fa-plus';\n }\n\n /** The placeholder of the title field: the title the part gets when the field is empty. */\n get TitlePlaceholder(): string {\n return `Default: ${this.SourceName}`;\n }\n\n /** Whether the chosen part type is Query, which has the query options. */\n get IsQuery(): boolean {\n return this.SelectedPartType?.Name === 'Query';\n }\n\n /** Whether the chosen part type has a settings panel, which fills the Source section. */\n get HasSettingsPanel(): boolean {\n return this.configDialogClassOf(this.SelectedPartType) != null;\n }\n\n /** The part type tiles: the active part types, and the edited part's type, by SortOrder. */\n get TypeTiles(): MJDashboardPartTypeEntity[] {\n const editedTypeId = this.Mode === 'edit' ? this.Panel?.partTypeId ?? null : null;\n return this.PartTypes\n .filter(pt => pt.IsActive !== false || (editedTypeId != null && UUIDsEqual(pt.ID, editedTypeId)))\n .sort((a, b) => (a.SortOrder ?? 0) - (b.SortOrder ?? 0));\n }\n\n /** The refresh intervals to offer: {@link QUERY_REFRESH_SECONDS}, plus the edited part's interval when it is another one. */\n get RefreshChoices(): readonly number[] {\n return this.refreshChoices;\n }\n\n /**\n * Whether Add part or Apply is enabled. Apply is enabled as soon as the part type is shown.\n * Add part waits until the settings panel reports a valid source.\n */\n get CanSubmit(): boolean {\n if (!this.SelectedPartType || this.IsLoadingPanel) return false;\n return this.Mode === 'edit' || this.configPanelRef == null || this.latestResult?.isValid === true;\n }\n\n /** Whether a tile's part type is the chosen one. */\n public IsSelected(partType: MJDashboardPartTypeEntity): boolean {\n return this.SelectedPartType != null && UUIDsEqual(partType.ID, this.SelectedPartType.ID);\n }\n\n /** The label on a part type tile. */\n public TileLabel(partType: MJDashboardPartTypeEntity): string {\n return PART_TYPE_TILE_COPY[partType.Name]?.Label ?? partType.Name;\n }\n\n /** The description on a part type tile. */\n public TileDescription(partType: MJDashboardPartTypeEntity): string {\n return PART_TYPE_TILE_COPY[partType.Name]?.Description ?? partType.Description ?? '';\n }\n\n /** Chooses a part type in add mode and loads its settings panel. */\n public SelectPartType(partType: MJDashboardPartTypeEntity): void {\n if (this.Mode === 'edit' || this.IsSelected(partType)) return;\n this.selectType(partType);\n }\n\n /** Takes a change from the settings panel: its latest result and the name of its source. */\n public OnConfigChanged(result: ConfigPanelResult): void {\n this.latestResult = result;\n const panel = this.configPanelRef?.instance;\n if (panel) {\n this.setSourceName(panel.getDefaultTitle());\n }\n this.Errors = [];\n this.cdr.markForCheck();\n }\n\n /**\n * Emits {@link Saved}, or shows the settings panel's errors when its source is not valid.\n * The config is the settings panel's config. Without a panel it names only the part type\n * (add mode) or is the part's current config (edit mode). A query part also gets the query options.\n */\n public Submit(): void {\n const partType = this.SelectedPartType;\n if (!partType || !this.CanSubmit) return;\n\n const panel = this.configPanelRef?.instance ?? null;\n let config: PanelConfig;\n let icon: string | undefined;\n if (panel) {\n const result = panel.getResult();\n if (!result.isValid) {\n this.latestResult = result;\n this.Errors = result.errors;\n this.cdr.markForCheck();\n return;\n }\n this.setSourceName(panel.getDefaultTitle());\n config = { ...result.config };\n icon = result.icon;\n } else if (this.Mode === 'edit' && this.Panel) {\n config = { ...this.Panel.config };\n icon = this.Panel.icon;\n } else {\n config = { type: partType.Name };\n }\n\n if (this.IsQuery) {\n config['showParameterControls'] = this.ShowParameterControls;\n config['autoRefreshSeconds'] = this.AutoRefreshEnabled ? this.AutoRefreshSeconds : 0;\n }\n\n this.Saved.emit({\n PartType: partType,\n Config: config,\n Title: this.Title.trim() || this.SourceName,\n Icon: icon || partType.Icon || DEFAULT_PART_ICON\n });\n }\n\n /** Emits {@link Cancelled}. */\n public Cancel(): void {\n this.Cancelled.emit();\n }\n\n ngOnDestroy(): void {\n this.destroyed = true;\n this.loadCount++;\n this.destroyConfigPanel();\n }\n\n /** Starts from the inputs: a new part with the first tile chosen, or the edited part. */\n private open(openId: number): void {\n if (this.destroyed || !this._visible || openId !== this.openCount) return;\n const editing = this.Mode === 'edit';\n this.Title = editing ? this.Panel?.title ?? '' : '';\n this.readQueryOptions(editing ? this.Panel?.config ?? null : null);\n this.selectType(editing ? this.findEditedPartType() : this.TypeTiles[0] ?? null);\n }\n\n /** Removes the settings panel and drops a load that is still running. */\n private close(): void {\n this.loadCount++;\n this.destroyConfigPanel();\n this.SelectedPartType = null;\n this.IsLoadingPanel = false;\n this.LoadError = null;\n this.Errors = [];\n this.latestResult = null;\n }\n\n /** The part type of the edited part, from {@link PartTypes}. */\n private findEditedPartType(): MJDashboardPartTypeEntity | null {\n const partTypeId = this.Panel?.partTypeId;\n if (!partTypeId) return null;\n return this.PartTypes.find(pt => UUIDsEqual(pt.ID, partTypeId)) ?? null;\n }\n\n /** Reads the query options from a part's config. A new part (no config) gets the defaults. */\n private readQueryOptions(config: PanelConfig | null): void {\n const showParameterControls = config?.['showParameterControls'];\n const seconds = config?.['autoRefreshSeconds'];\n const refreshSeconds = typeof seconds === 'number' && seconds > 0 ? seconds : 0;\n this.ShowParameterControls = typeof showParameterControls === 'boolean' ? showParameterControls : true;\n this.AutoRefreshEnabled = refreshSeconds > 0;\n this.AutoRefreshSeconds = refreshSeconds > 0 ? refreshSeconds : DEFAULT_QUERY_REFRESH_SECONDS;\n this.refreshChoices = refreshSeconds > 0 && !QUERY_REFRESH_SECONDS.some(choice => choice === refreshSeconds)\n ? [...QUERY_REFRESH_SECONDS, refreshSeconds].sort((a, b) => a - b)\n : QUERY_REFRESH_SECONDS;\n }\n\n /** Makes a part type the chosen one and starts to load its settings panel. */\n private selectType(partType: MJDashboardPartTypeEntity | null): void {\n const loadId = ++this.loadCount;\n this.destroyConfigPanel();\n this.SelectedPartType = partType;\n this.SourceName = partType ? this.TileLabel(partType) : '';\n this.latestResult = null;\n this.Errors = [];\n this.LoadError = null;\n const className = this.configDialogClassOf(partType);\n this.IsLoadingPanel = className != null;\n // Renders the Source section now, so its container exists when the panel class arrives.\n this.cdr.detectChanges();\n if (partType && className) {\n void this.loadConfigPanel(partType, className, loadId);\n }\n }\n\n /**\n * Creates the part type's settings panel in the Source section. A load that a newer load or a\n * close overtook is dropped. When the panel cannot be created, the dialog says why and still\n * submits: with a config that names only the part type (add mode) or the part's current config (edit mode).\n */\n private async loadConfigPanel(partType: MJDashboardPartTypeEntity, className: string, loadId: number): Promise<void> {\n try {\n const registration = await MJGlobal.Instance.ClassFactory.GetRegistrationAsync(BaseConfigPanel, className);\n if (loadId !== this.loadCount) return;\n const container = this.configPanelContainer;\n if (!registration || !container) {\n this.failPanelLoad(registration ? `The settings panel \"${className}\" could not be shown.` : `The settings panel \"${className}\" is not registered.`);\n return;\n }\n\n container.clear();\n const ref = container.createComponent<BaseConfigPanel>(registration.SubClass);\n this.configPanelRef = ref;\n const panel = ref.instance;\n panel.Provider = this.Provider;\n panel.ShowCommonFields = false;\n panel.partType = partType;\n if (this.Mode === 'edit' && this.Panel) {\n panel.panel = this.Panel;\n panel.config = this.Panel.config;\n }\n this.configChangedSubscription = panel.configChanged.subscribe(result => this.OnConfigChanged(result));\n this.SourceName = panel.getDefaultTitle();\n this.IsLoadingPanel = false;\n this.cdr.detectChanges();\n } catch (error) {\n if (loadId !== this.loadCount) return;\n this.failPanelLoad(`The settings panel \"${className}\" could not load (${error instanceof Error ? error.message : String(error)}).`);\n }\n }\n\n /** Shows why the settings panel could not load, and what Add part or Apply does without it. */\n private failPanelLoad(reason: string): void {\n this.destroyConfigPanel();\n this.IsLoadingPanel = false;\n const fallback = this.Mode === 'edit' ? 'Apply keeps the part\\'s current settings.' : 'The part is added with default settings.';\n this.LoadError = `${reason} ${fallback}`;\n this.cdr.detectChanges();\n }\n\n /**\n * Takes a new source name. A title that is the old source name follows the new one, as the\n * settings panels' own title fields do; a title the user changed stays.\n */\n private setSourceName(sourceName: string): void {\n const title = this.Title.trim();\n if (title !== '' && title === this.SourceName) {\n this.Title = sourceName;\n }\n this.SourceName = sourceName;\n }\n\n /** The registered class name of a part type's settings panel, if it has one. */\n private configDialogClassOf(partType: MJDashboardPartTypeEntity | null): string | null {\n return partType?.ConfigDialogClass?.trim() || null;\n }\n\n private destroyConfigPanel(): void {\n this.configChangedSubscription?.unsubscribe();\n this.configChangedSubscription = null;\n this.configPanelRef?.destroy();\n this.configPanelRef = null;\n }\n}\n","@if (Visible) {\n <mj-dialog [Visible]=\"true\" Size=\"md\" [Title]=\"DialogTitle\" (Close)=\"Cancel()\">\n <div class=\"part-dialog\">\n <section>\n <div class=\"part-dialog__label\" id=\"part-dialog-type-label\">Part type</div>\n <div class=\"part-dialog__types\" role=\"group\" aria-labelledby=\"part-dialog-type-label\">\n @for (partType of TypeTiles; track partType.ID) {\n <button type=\"button\" class=\"part-dialog__type\"\n [class.part-dialog__type--selected]=\"IsSelected(partType)\"\n [attr.aria-pressed]=\"IsSelected(partType)\"\n [attr.data-autofocus]=\"Mode === 'add' && IsSelected(partType) ? '' : null\"\n [disabled]=\"Mode === 'edit' && !IsSelected(partType)\"\n (click)=\"SelectPartType(partType)\">\n <i [class]=\"partType.Icon || 'fa-solid fa-puzzle-piece'\" aria-hidden=\"true\"></i>\n <span class=\"part-dialog__type-label\">{{ TileLabel(partType) }}</span>\n <span class=\"part-dialog__type-desc\">{{ TileDescription(partType) }}</span>\n </button>\n }\n </div>\n @if (TypeTiles.length === 0) {\n <mj-empty-state Size=\"compact\" Variant=\"warning\" Title=\"No part types available\"\n Message=\"Dashboard part types have not been set up in this system.\" />\n }\n </section>\n\n @if (SelectedPartType) {\n @if (HasSettingsPanel) {\n <section>\n <div class=\"part-dialog__label\">Source</div>\n @if (IsLoadingPanel) {\n <mj-loading Text=\"Loading...\" Size=\"small\"></mj-loading>\n }\n @if (LoadError) {\n <p class=\"part-dialog__warning\">{{ LoadError }}</p>\n }\n <ng-container #configPanelContainer></ng-container>\n @for (error of Errors; track $index) {\n <p class=\"part-dialog__error\"><i class=\"fa-solid fa-circle-exclamation\" aria-hidden=\"true\"></i> {{ error }}</p>\n }\n </section>\n }\n\n <section>\n <label class=\"part-dialog__label\" for=\"part-dialog-title\">Title</label>\n <input id=\"part-dialog-title\" class=\"mj-input\" type=\"text\" [(ngModel)]=\"Title\" [placeholder]=\"TitlePlaceholder\">\n </section>\n\n @if (IsQuery) {\n <section class=\"part-dialog__options\">\n <label class=\"part-dialog__option\">\n <input type=\"checkbox\" class=\"mj-checkbox\" [(ngModel)]=\"ShowParameterControls\"> Show parameter controls\n </label>\n <label class=\"part-dialog__option\">\n <input type=\"checkbox\" class=\"mj-checkbox\" [(ngModel)]=\"AutoRefreshEnabled\"> Refresh automatically every\n <select class=\"mj-input part-dialog__refresh\" [(ngModel)]=\"AutoRefreshSeconds\" [disabled]=\"!AutoRefreshEnabled\" aria-label=\"Refresh interval in seconds\">\n @for (seconds of RefreshChoices; track seconds) {\n <option [ngValue]=\"seconds\">{{ seconds }}</option>\n }\n </select>\n seconds\n </label>\n </section>\n }\n }\n </div>\n <mj-dialog-actions>\n <button mjButton Variant=\"primary\" type=\"button\" [disabled]=\"!CanSubmit\" (click)=\"Submit()\">\n <i [class]=\"SubmitIcon\" aria-hidden=\"true\"></i> {{ SubmitLabel }}\n </button>\n <button mjButton Variant=\"secondary\" type=\"button\" (click)=\"Cancel()\">Cancel</button>\n </mj-dialog-actions>\n </mj-dialog>\n}\n"]}